skill-search 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +381 -0
- package/bin/skill.js +348 -0
- package/bin/tui.js +33 -0
- package/package.json +53 -0
- package/scripts/package-lock.json +6 -0
- package/scripts/setup-env.bat +58 -0
- package/scripts/test-scan.js +42 -0
- package/src/actions.js +216 -0
- package/src/api.js +306 -0
- package/src/cache.js +107 -0
- package/src/config.js +220 -0
- package/src/fallback-index.json +6 -0
- package/src/interactive.js +23 -0
- package/src/localCrawler.js +204 -0
- package/src/matcher.js +170 -0
- package/src/store.js +156 -0
- package/src/syncer.js +226 -0
- package/src/theme.js +191 -0
- package/src/tui/ActionModal.js +209 -0
- package/src/tui/AddDelView.js +212 -0
- package/src/tui/App.js +739 -0
- package/src/tui/AsciiHeader.js +35 -0
- package/src/tui/CommandPalette.js +64 -0
- package/src/tui/ConfigView.js +168 -0
- package/src/tui/DetailView.js +139 -0
- package/src/tui/DualPane.js +114 -0
- package/src/tui/PrimaryView.js +163 -0
- package/src/tui/SearchBox.js +26 -0
- package/src/tui/SearchView.js +121 -0
- package/src/tui/SkillList.js +102 -0
- package/src/tui/SyncView.js +143 -0
- package/src/tui/ThemeView.js +116 -0
- package/src/utils.js +83 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
|
|
2
|
+
const React = require('react');
|
|
3
|
+
const { useState, useEffect } = React;
|
|
4
|
+
const { Box, Text, useInput } = require('ink');
|
|
5
|
+
const TextInput = require('ink-text-input').default;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Action Modal
|
|
9
|
+
* Handles Menu, Input, and Confirmation states
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Action Modal
|
|
14
|
+
* Handles Menu, Input, and Confirmation states
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Define standard background color for the modal
|
|
18
|
+
const BG_COLOR = 'black'; // 'gray' background often renders as light gray which ruins contrast with white text.
|
|
19
|
+
// However, user ASKED for gray. standard 'gray' is often bright.
|
|
20
|
+
// safe 'dark gray' is usually not a standard named color in simple ANSI.
|
|
21
|
+
// Let's try 'gray' but user might mean a "Dialog Grey".
|
|
22
|
+
// Actually, widely compatible "Gray" is often the bright black.
|
|
23
|
+
// Let's use 'gray' and adapt text to 'black' if needed?
|
|
24
|
+
// No, typically 'white' on 'gray' is readable.
|
|
25
|
+
// Let's try using hex for a nice dark grey if possible or 'gray'.
|
|
26
|
+
// But to be safe with standard colors, let's try 'gray' and assume standard terminal behavior.
|
|
27
|
+
// WAIT: If I use 'gray' bg, white text might be hard to read. Black text is safer.
|
|
28
|
+
// Let's try `BG_COLOR = 'gray'` and change default text to 'black'.
|
|
29
|
+
|
|
30
|
+
const MODAL_BG = '#333333'; // Dark Gray Hex
|
|
31
|
+
|
|
32
|
+
function ActionModal({ item, pane, level, onAction, onClose }) {
|
|
33
|
+
// view: 'menu' | 'input' | 'confirm' | 'processing' | 'result'
|
|
34
|
+
const [view, setView] = useState('menu');
|
|
35
|
+
const [selectedIdx, setSelectedIdx] = useState(0);
|
|
36
|
+
const [inputValue, setInputValue] = useState('');
|
|
37
|
+
const [message, setMessage] = useState('');
|
|
38
|
+
const [currentAction, setCurrentAction] = useState(null);
|
|
39
|
+
|
|
40
|
+
// Define options based on pane
|
|
41
|
+
const options = pane === 'local'
|
|
42
|
+
? ['Open', 'Sync', 'Move', 'Delete']
|
|
43
|
+
: ['Open', 'Sync'];
|
|
44
|
+
|
|
45
|
+
useInput((input, key) => {
|
|
46
|
+
if (view === 'processing') return;
|
|
47
|
+
|
|
48
|
+
if (key.escape) {
|
|
49
|
+
onClose();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (view === 'menu') {
|
|
54
|
+
if (key.upArrow) setSelectedIdx(Math.max(0, selectedIdx - 1));
|
|
55
|
+
if (key.downArrow) setSelectedIdx(Math.min(options.length - 1, selectedIdx + 1));
|
|
56
|
+
if (key.return) {
|
|
57
|
+
const action = options[selectedIdx].toLowerCase();
|
|
58
|
+
handleMenuSelection(action);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (view === 'confirm') {
|
|
62
|
+
if (key.return || input === 'y' || input === 'Y') {
|
|
63
|
+
// Confirmed
|
|
64
|
+
executeAction(currentAction);
|
|
65
|
+
} else if (input === 'n' || input === 'N') {
|
|
66
|
+
// Cancelled
|
|
67
|
+
onClose();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (view === 'input') {
|
|
71
|
+
if (key.return) {
|
|
72
|
+
executeAction(currentAction, inputValue);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else if (view === 'result') {
|
|
76
|
+
if (key.return) {
|
|
77
|
+
onClose();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const handleMenuSelection = (action) => {
|
|
83
|
+
setCurrentAction(action);
|
|
84
|
+
|
|
85
|
+
if (action === 'open') {
|
|
86
|
+
onAction('open', item); // Immediate
|
|
87
|
+
onClose(); // Close modal after opening
|
|
88
|
+
}
|
|
89
|
+
else if (action === 'sync') {
|
|
90
|
+
// Immediate sync (or confirm? Requirement: "Move, Delete need confirmation". Sync doesn't explicitly say so, but usually safe)
|
|
91
|
+
// Re-read: "Move, Delete 操作时都需要二次确认" -> Sync doesn't.
|
|
92
|
+
executeAction('sync');
|
|
93
|
+
}
|
|
94
|
+
else if (action === 'move') {
|
|
95
|
+
setView('input');
|
|
96
|
+
setMessage('Enter target path (e.g. D:\\MySkills):');
|
|
97
|
+
}
|
|
98
|
+
else if (action === 'delete') {
|
|
99
|
+
setView('confirm');
|
|
100
|
+
setMessage(`Are you sure you want to DELETE this ${level === 1 ? 'PROVIDER group' : 'skill'}?\nThis cannot be undone.`);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const executeAction = async (action, param) => {
|
|
105
|
+
setView('processing');
|
|
106
|
+
try {
|
|
107
|
+
const result = await onAction(action, item, param);
|
|
108
|
+
if (result.success) {
|
|
109
|
+
setMessage('✅ Success!');
|
|
110
|
+
} else {
|
|
111
|
+
setMessage(`❌ Error: ${result.error}`);
|
|
112
|
+
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
setMessage(`❌ Error: ${e.message}`);
|
|
115
|
+
}
|
|
116
|
+
setView('result');
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Helper for fixed-width lines with background color
|
|
120
|
+
const MODAL_WIDTH = 46; // Internal width (50 - 2 border - 2 margin safety)
|
|
121
|
+
|
|
122
|
+
const Line = ({ text = '', align = 'left', color = 'white', bg = MODAL_BG, bold = false, underline = false }) => {
|
|
123
|
+
let content = text;
|
|
124
|
+
const len = content.length;
|
|
125
|
+
const spa = MODAL_WIDTH - len;
|
|
126
|
+
|
|
127
|
+
if (spa > 0) {
|
|
128
|
+
if (align === 'center') {
|
|
129
|
+
const left = Math.floor(spa / 2);
|
|
130
|
+
const right = spa - left;
|
|
131
|
+
content = ' '.repeat(left) + content + ' '.repeat(right);
|
|
132
|
+
} else {
|
|
133
|
+
content = content + ' '.repeat(spa);
|
|
134
|
+
}
|
|
135
|
+
} else if (spa < 0) {
|
|
136
|
+
content = content.slice(0, MODAL_WIDTH);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Add side padding (1 char each side) - CRITICAL: backgroundColor only works on Text, not Box
|
|
140
|
+
return React.createElement(Text, { color, backgroundColor: bg, bold, underline }, ' ' + content + ' ');
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const EmptyLine = () => React.createElement(Line, { text: '' });
|
|
144
|
+
|
|
145
|
+
return React.createElement(Box, {
|
|
146
|
+
position: 'absolute',
|
|
147
|
+
width: 50,
|
|
148
|
+
borderStyle: 'double',
|
|
149
|
+
borderColor: 'cyan',
|
|
150
|
+
flexDirection: 'column',
|
|
151
|
+
alignItems: 'center'
|
|
152
|
+
},
|
|
153
|
+
// Header
|
|
154
|
+
React.createElement(EmptyLine),
|
|
155
|
+
React.createElement(Line, { text: `Action: ${item.name || item.id}`, align: 'center', bold: true, color: 'white' }),
|
|
156
|
+
React.createElement(Line, { text: `${pane.toUpperCase()} - Level ${level}`, align: 'center', color: 'gray' }),
|
|
157
|
+
React.createElement(EmptyLine),
|
|
158
|
+
|
|
159
|
+
// Views
|
|
160
|
+
view === 'menu' && React.createElement(Box, { flexDirection: 'column', key: 'menu' },
|
|
161
|
+
options.map((opt, i) =>
|
|
162
|
+
React.createElement(Line, {
|
|
163
|
+
key: opt,
|
|
164
|
+
text: i === selectedIdx ? `● ${opt}` : ` ${opt}`,
|
|
165
|
+
color: i === selectedIdx ? 'black' : 'white',
|
|
166
|
+
bg: i === selectedIdx ? 'green' : MODAL_BG,
|
|
167
|
+
bold: i === selectedIdx
|
|
168
|
+
})
|
|
169
|
+
)
|
|
170
|
+
),
|
|
171
|
+
|
|
172
|
+
view === 'input' && React.createElement(Box, { flexDirection: 'column', key: 'input' },
|
|
173
|
+
React.createElement(Line, { text: message, color: 'white' }),
|
|
174
|
+
React.createElement(Box, { width: MODAL_WIDTH + 2, backgroundColor: MODAL_BG, paddingX: 1 },
|
|
175
|
+
React.createElement(Text, { backgroundColor: MODAL_BG, color: 'white' }, '> '),
|
|
176
|
+
React.createElement(TextInput, {
|
|
177
|
+
value: inputValue,
|
|
178
|
+
onChange: setInputValue,
|
|
179
|
+
onSubmit: () => executeAction(currentAction, inputValue)
|
|
180
|
+
})
|
|
181
|
+
)
|
|
182
|
+
),
|
|
183
|
+
|
|
184
|
+
view === 'confirm' && React.createElement(Box, { flexDirection: 'column', alignItems: 'center', key: 'confirm' },
|
|
185
|
+
message.split('\n').map((msgLine, i) =>
|
|
186
|
+
React.createElement(Line, { key: i, text: msgLine, color: 'white', bg: 'red', align: 'center', bold: true })
|
|
187
|
+
),
|
|
188
|
+
React.createElement(EmptyLine),
|
|
189
|
+
React.createElement(Line, { text: 'Press Enter or Y to confirm', align: 'center', color: 'white' }),
|
|
190
|
+
React.createElement(Line, { text: 'N or Esc to cancel', align: 'center', color: 'gray' })
|
|
191
|
+
),
|
|
192
|
+
|
|
193
|
+
view === 'processing' && React.createElement(Box, { key: 'processing' },
|
|
194
|
+
React.createElement(Line, { text: '⏳ Processing...', color: 'yellow', align: 'center' })
|
|
195
|
+
),
|
|
196
|
+
|
|
197
|
+
view === 'result' && React.createElement(Box, { flexDirection: 'column', key: 'result' },
|
|
198
|
+
message.split('\n').map((msgLine, i) =>
|
|
199
|
+
React.createElement(Line, { key: i, text: msgLine, align: 'center', color: 'white' })
|
|
200
|
+
),
|
|
201
|
+
React.createElement(EmptyLine),
|
|
202
|
+
React.createElement(Line, { text: 'Press Enter to close', color: 'gray', align: 'center' })
|
|
203
|
+
),
|
|
204
|
+
|
|
205
|
+
React.createElement(EmptyLine)
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = ActionModal;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// src/tui/AddDelView.js - Add/Delete Custom Paths View Component
|
|
2
|
+
|
|
3
|
+
const React = require('react');
|
|
4
|
+
const { useState, useEffect } = React;
|
|
5
|
+
const { Box, Text, useInput } = require('ink');
|
|
6
|
+
const TextInput = require('ink-text-input').default;
|
|
7
|
+
const config = require('../config');
|
|
8
|
+
const { listSkills } = require('../matcher');
|
|
9
|
+
|
|
10
|
+
function AddDelView({ onBack, onRefresh }) {
|
|
11
|
+
const [mode, setMode] = useState('menu'); // 'menu', 'add', 'delete'
|
|
12
|
+
const [customPaths, setCustomPaths] = useState([]);
|
|
13
|
+
const [selectedIdx, setSelectedIdx] = useState(0);
|
|
14
|
+
const [inputValue, setInputValue] = useState('');
|
|
15
|
+
const [statusMsg, setStatusMsg] = useState('');
|
|
16
|
+
|
|
17
|
+
// Load custom paths on mount
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
refreshPaths();
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
const refreshPaths = () => {
|
|
23
|
+
const paths = config.getCustomPaths();
|
|
24
|
+
setCustomPaths(paths);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const menuOptions = [
|
|
28
|
+
{ key: 'add', label: 'Add Path', desc: 'Add a new custom skill path' },
|
|
29
|
+
{ key: 'delete', label: 'Delete Path', desc: 'Remove an existing custom path' },
|
|
30
|
+
{ key: 'back', label: 'Back', desc: 'Return to main menu' }
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
useInput((input, key) => {
|
|
34
|
+
if (mode === 'menu') {
|
|
35
|
+
if (key.escape) {
|
|
36
|
+
onBack();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (key.upArrow) {
|
|
40
|
+
setSelectedIdx(Math.max(0, selectedIdx - 1));
|
|
41
|
+
} else if (key.downArrow) {
|
|
42
|
+
setSelectedIdx(Math.min(menuOptions.length - 1, selectedIdx + 1));
|
|
43
|
+
} else if (key.return) {
|
|
44
|
+
const selected = menuOptions[selectedIdx];
|
|
45
|
+
if (selected.key === 'back') {
|
|
46
|
+
onBack();
|
|
47
|
+
} else if (selected.key === 'add') {
|
|
48
|
+
setMode('add');
|
|
49
|
+
setInputValue('');
|
|
50
|
+
setStatusMsg('');
|
|
51
|
+
} else if (selected.key === 'delete') {
|
|
52
|
+
if (customPaths.length === 0) {
|
|
53
|
+
setStatusMsg('No custom paths to delete');
|
|
54
|
+
} else {
|
|
55
|
+
setMode('delete');
|
|
56
|
+
setSelectedIdx(0);
|
|
57
|
+
setStatusMsg('');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} else if (mode === 'add') {
|
|
62
|
+
if (key.escape) {
|
|
63
|
+
setMode('menu');
|
|
64
|
+
setSelectedIdx(0);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (key.return) {
|
|
68
|
+
const trimmed = inputValue.trim();
|
|
69
|
+
if (trimmed) {
|
|
70
|
+
if (config.addCustomPath(trimmed)) {
|
|
71
|
+
setStatusMsg(`✅ Added: ${trimmed}`);
|
|
72
|
+
refreshPaths();
|
|
73
|
+
if (onRefresh) onRefresh();
|
|
74
|
+
} else {
|
|
75
|
+
setStatusMsg(`⚠️ Path already exists: ${trimmed}`);
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
setStatusMsg('⚠️ Please enter a valid path');
|
|
79
|
+
}
|
|
80
|
+
setInputValue('');
|
|
81
|
+
}
|
|
82
|
+
} else if (mode === 'delete') {
|
|
83
|
+
if (key.escape) {
|
|
84
|
+
setMode('menu');
|
|
85
|
+
setSelectedIdx(0);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (key.upArrow) {
|
|
89
|
+
setSelectedIdx(Math.max(0, selectedIdx - 1));
|
|
90
|
+
} else if (key.downArrow) {
|
|
91
|
+
setSelectedIdx(Math.min(customPaths.length - 1, selectedIdx + 1));
|
|
92
|
+
} else if (key.return && customPaths.length > 0) {
|
|
93
|
+
const pathToDelete = customPaths[selectedIdx];
|
|
94
|
+
if (config.removeCustomPath(pathToDelete)) {
|
|
95
|
+
setStatusMsg(`✅ Removed: ${pathToDelete}`);
|
|
96
|
+
refreshPaths();
|
|
97
|
+
if (onRefresh) onRefresh();
|
|
98
|
+
// Adjust selected index if needed
|
|
99
|
+
if (selectedIdx >= customPaths.length - 1) {
|
|
100
|
+
setSelectedIdx(Math.max(0, customPaths.length - 2));
|
|
101
|
+
}
|
|
102
|
+
// If no more paths, go back to menu
|
|
103
|
+
if (customPaths.length <= 1) {
|
|
104
|
+
setMode('menu');
|
|
105
|
+
setSelectedIdx(0);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Get display name for path (last 2 folder levels)
|
|
113
|
+
const getDisplayPath = (p) => {
|
|
114
|
+
const parts = p.replace(/\\/g, '/').split('/').filter(Boolean);
|
|
115
|
+
if (parts.length >= 2) {
|
|
116
|
+
return parts.slice(-2).join('/');
|
|
117
|
+
}
|
|
118
|
+
return parts[parts.length - 1] || p;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return React.createElement(Box, { flexDirection: 'column' },
|
|
122
|
+
React.createElement(Box, { marginBottom: 1 },
|
|
123
|
+
React.createElement(Text, { bold: true, color: 'cyan' }, '📂 Custom Paths Manager')
|
|
124
|
+
),
|
|
125
|
+
|
|
126
|
+
// Current custom paths display
|
|
127
|
+
React.createElement(Box, { flexDirection: 'column', marginBottom: 1 },
|
|
128
|
+
React.createElement(Text, { color: 'gray' }, ` Currently tracked paths: ${customPaths.length}`),
|
|
129
|
+
customPaths.length > 0 && mode === 'menu' && customPaths.map((p, i) =>
|
|
130
|
+
React.createElement(Text, { key: i, color: 'gray', dimColor: true }, ` • ${getDisplayPath(p)}`)
|
|
131
|
+
)
|
|
132
|
+
),
|
|
133
|
+
|
|
134
|
+
// Menu mode
|
|
135
|
+
mode === 'menu' && React.createElement(Box, { flexDirection: 'column', marginBottom: 1 },
|
|
136
|
+
React.createElement(Text, { color: 'gray', marginBottom: 1 }, 'Select an action:'),
|
|
137
|
+
menuOptions.map((opt, idx) => {
|
|
138
|
+
const isSelected = idx === selectedIdx;
|
|
139
|
+
return React.createElement(Box, { key: opt.key },
|
|
140
|
+
React.createElement(Text, {
|
|
141
|
+
color: isSelected ? 'cyan' : 'white',
|
|
142
|
+
inverse: isSelected
|
|
143
|
+
}, isSelected ? '▶ ' : ' '),
|
|
144
|
+
React.createElement(Text, {
|
|
145
|
+
color: isSelected ? 'cyan' : 'white',
|
|
146
|
+
bold: isSelected
|
|
147
|
+
}, opt.label),
|
|
148
|
+
React.createElement(Text, { color: 'gray' }, ` - ${opt.desc}`)
|
|
149
|
+
);
|
|
150
|
+
})
|
|
151
|
+
),
|
|
152
|
+
|
|
153
|
+
// Add mode
|
|
154
|
+
mode === 'add' && React.createElement(Box, { flexDirection: 'column', marginBottom: 1 },
|
|
155
|
+
React.createElement(Text, { color: 'yellow', marginBottom: 1 }, '📁 Enter absolute path to skill folder:'),
|
|
156
|
+
React.createElement(Box, {
|
|
157
|
+
borderStyle: 'single',
|
|
158
|
+
borderColor: 'cyan',
|
|
159
|
+
paddingX: 1,
|
|
160
|
+
width: '80%'
|
|
161
|
+
},
|
|
162
|
+
React.createElement(TextInput, {
|
|
163
|
+
value: inputValue,
|
|
164
|
+
onChange: setInputValue,
|
|
165
|
+
placeholder: 'C:\\Users\\...\\skills or /home/.../skills',
|
|
166
|
+
focus: true,
|
|
167
|
+
showCursor: true
|
|
168
|
+
})
|
|
169
|
+
),
|
|
170
|
+
React.createElement(Text, { color: 'gray', marginTop: 1 }, 'Press Enter to add, Esc to cancel')
|
|
171
|
+
),
|
|
172
|
+
|
|
173
|
+
// Delete mode
|
|
174
|
+
mode === 'delete' && React.createElement(Box, { flexDirection: 'column', marginBottom: 1 },
|
|
175
|
+
React.createElement(Text, { color: 'yellow', marginBottom: 1 }, '🗑️ Select path to delete:'),
|
|
176
|
+
customPaths.map((p, idx) => {
|
|
177
|
+
const isSelected = idx === selectedIdx;
|
|
178
|
+
return React.createElement(Box, { key: idx },
|
|
179
|
+
React.createElement(Text, {
|
|
180
|
+
color: isSelected ? 'red' : 'white',
|
|
181
|
+
inverse: isSelected
|
|
182
|
+
}, isSelected ? '▶ ' : ' '),
|
|
183
|
+
React.createElement(Text, {
|
|
184
|
+
color: isSelected ? 'red' : 'white',
|
|
185
|
+
bold: isSelected
|
|
186
|
+
}, getDisplayPath(p)),
|
|
187
|
+
React.createElement(Text, { color: 'gray', dimColor: true }, ` (${p})`)
|
|
188
|
+
);
|
|
189
|
+
}),
|
|
190
|
+
React.createElement(Text, { color: 'gray', marginTop: 1 }, 'Press Enter to delete, Esc to cancel')
|
|
191
|
+
),
|
|
192
|
+
|
|
193
|
+
// Status message
|
|
194
|
+
statusMsg !== '' && React.createElement(Box, { marginTop: 1 },
|
|
195
|
+
React.createElement(Text, {
|
|
196
|
+
color: statusMsg.startsWith('✅') ? 'green' :
|
|
197
|
+
statusMsg.startsWith('⚠️') ? 'yellow' :
|
|
198
|
+
statusMsg.startsWith('❌') ? 'red' : 'gray'
|
|
199
|
+
}, statusMsg)
|
|
200
|
+
),
|
|
201
|
+
|
|
202
|
+
React.createElement(Box, { marginTop: 1 },
|
|
203
|
+
React.createElement(Text, { color: 'gray', dimColor: true },
|
|
204
|
+
mode === 'menu' ? 'Up/Down Navigate │ Enter Select │ Esc Back' :
|
|
205
|
+
mode === 'add' ? 'Enter Add │ Esc Cancel' :
|
|
206
|
+
'Up/Down Navigate │ Enter Delete │ Esc Cancel'
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports = AddDelView;
|