@appkit/llamacpp-cli 1.12.0 → 1.13.0
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 +294 -168
- package/dist/cli.js +35 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/launch/claude.d.ts +6 -0
- package/dist/commands/launch/claude.d.ts.map +1 -0
- package/dist/commands/launch/claude.js +277 -0
- package/dist/commands/launch/claude.js.map +1 -0
- package/dist/lib/integration-checker.d.ts +26 -0
- package/dist/lib/integration-checker.d.ts.map +1 -0
- package/dist/lib/integration-checker.js +77 -0
- package/dist/lib/integration-checker.js.map +1 -0
- package/dist/lib/router-manager.d.ts +4 -0
- package/dist/lib/router-manager.d.ts.map +1 -1
- package/dist/lib/router-manager.js +10 -0
- package/dist/lib/router-manager.js.map +1 -1
- package/dist/lib/router-server.d.ts +13 -0
- package/dist/lib/router-server.d.ts.map +1 -1
- package/dist/lib/router-server.js +267 -7
- package/dist/lib/router-server.js.map +1 -1
- package/dist/types/integration-config.d.ts +28 -0
- package/dist/types/integration-config.d.ts.map +1 -0
- package/dist/types/integration-config.js +3 -0
- package/dist/types/integration-config.js.map +1 -0
- package/package.json +10 -2
- package/web/dist/assets/index-Bin89Lwr.css +1 -0
- package/web/dist/assets/index-CVmonw3T.js +17 -0
- package/web/{index.html → dist/index.html} +2 -1
- package/.versionrc.json +0 -16
- package/CHANGELOG.md +0 -213
- package/docs/images/.gitkeep +0 -1
- package/docs/images/web-ui-servers.png +0 -0
- package/src/cli.ts +0 -523
- package/src/commands/admin/config.ts +0 -121
- package/src/commands/admin/logs.ts +0 -91
- package/src/commands/admin/restart.ts +0 -26
- package/src/commands/admin/start.ts +0 -27
- package/src/commands/admin/status.ts +0 -84
- package/src/commands/admin/stop.ts +0 -16
- package/src/commands/config-global.ts +0 -38
- package/src/commands/config.ts +0 -323
- package/src/commands/create.ts +0 -183
- package/src/commands/delete.ts +0 -74
- package/src/commands/list.ts +0 -37
- package/src/commands/logs-all.ts +0 -251
- package/src/commands/logs.ts +0 -345
- package/src/commands/monitor.ts +0 -110
- package/src/commands/ps.ts +0 -84
- package/src/commands/pull.ts +0 -44
- package/src/commands/rm.ts +0 -107
- package/src/commands/router/config.ts +0 -116
- package/src/commands/router/logs.ts +0 -256
- package/src/commands/router/restart.ts +0 -36
- package/src/commands/router/start.ts +0 -60
- package/src/commands/router/status.ts +0 -119
- package/src/commands/router/stop.ts +0 -33
- package/src/commands/run.ts +0 -233
- package/src/commands/search.ts +0 -107
- package/src/commands/server-show.ts +0 -161
- package/src/commands/show.ts +0 -207
- package/src/commands/start.ts +0 -101
- package/src/commands/stop.ts +0 -39
- package/src/commands/tui.ts +0 -25
- package/src/lib/admin-manager.ts +0 -435
- package/src/lib/admin-server.ts +0 -1243
- package/src/lib/config-generator.ts +0 -130
- package/src/lib/download-job-manager.ts +0 -213
- package/src/lib/history-manager.ts +0 -172
- package/src/lib/launchctl-manager.ts +0 -225
- package/src/lib/metrics-aggregator.ts +0 -257
- package/src/lib/model-downloader.ts +0 -328
- package/src/lib/model-scanner.ts +0 -157
- package/src/lib/model-search.ts +0 -114
- package/src/lib/models-dir-setup.ts +0 -46
- package/src/lib/port-manager.ts +0 -80
- package/src/lib/router-logger.ts +0 -201
- package/src/lib/router-manager.ts +0 -414
- package/src/lib/router-server.ts +0 -538
- package/src/lib/state-manager.ts +0 -206
- package/src/lib/status-checker.ts +0 -113
- package/src/lib/system-collector.ts +0 -315
- package/src/tui/ConfigApp.ts +0 -1085
- package/src/tui/HistoricalMonitorApp.ts +0 -587
- package/src/tui/ModelsApp.ts +0 -368
- package/src/tui/MonitorApp.ts +0 -386
- package/src/tui/MultiServerMonitorApp.ts +0 -1833
- package/src/tui/RootNavigator.ts +0 -74
- package/src/tui/SearchApp.ts +0 -511
- package/src/tui/SplashScreen.ts +0 -149
- package/src/types/admin-config.ts +0 -25
- package/src/types/global-config.ts +0 -26
- package/src/types/history-types.ts +0 -39
- package/src/types/model-info.ts +0 -8
- package/src/types/monitor-types.ts +0 -162
- package/src/types/router-config.ts +0 -25
- package/src/types/server-config.ts +0 -46
- package/src/utils/downsample-utils.ts +0 -128
- package/src/utils/file-utils.ts +0 -146
- package/src/utils/format-utils.ts +0 -98
- package/src/utils/log-parser.ts +0 -284
- package/src/utils/log-utils.ts +0 -178
- package/src/utils/process-utils.ts +0 -316
- package/src/utils/prompt-utils.ts +0 -47
- package/test-load.sh +0 -100
- package/tsconfig.json +0 -20
- package/web/eslint.config.js +0 -23
- package/web/llamacpp-web-dist.tar.gz +0 -0
- package/web/package-lock.json +0 -4017
- package/web/package.json +0 -38
- package/web/postcss.config.js +0 -6
- package/web/src/App.css +0 -42
- package/web/src/App.tsx +0 -86
- package/web/src/assets/react.svg +0 -1
- package/web/src/components/ApiKeyPrompt.tsx +0 -71
- package/web/src/components/CreateServerModal.tsx +0 -372
- package/web/src/components/DownloadProgress.tsx +0 -123
- package/web/src/components/Nav.tsx +0 -89
- package/web/src/components/RouterConfigModal.tsx +0 -240
- package/web/src/components/SearchModal.tsx +0 -306
- package/web/src/components/ServerConfigModal.tsx +0 -291
- package/web/src/hooks/useApi.ts +0 -259
- package/web/src/index.css +0 -42
- package/web/src/lib/api.ts +0 -226
- package/web/src/main.tsx +0 -10
- package/web/src/pages/Dashboard.tsx +0 -103
- package/web/src/pages/Models.tsx +0 -258
- package/web/src/pages/Router.tsx +0 -270
- package/web/src/pages/RouterLogs.tsx +0 -201
- package/web/src/pages/ServerLogs.tsx +0 -553
- package/web/src/pages/Servers.tsx +0 -358
- package/web/src/types/api.ts +0 -140
- package/web/tailwind.config.js +0 -31
- package/web/tsconfig.app.json +0 -28
- package/web/tsconfig.json +0 -7
- package/web/tsconfig.node.json +0 -26
- package/web/vite.config.ts +0 -25
- /package/web/{public → dist}/vite.svg +0 -0
package/src/tui/ModelsApp.ts
DELETED
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
import blessed from 'blessed';
|
|
2
|
-
import { modelScanner } from '../lib/model-scanner.js';
|
|
3
|
-
import { stateManager } from '../lib/state-manager.js';
|
|
4
|
-
import { launchctlManager } from '../lib/launchctl-manager.js';
|
|
5
|
-
import { ModelInfo } from '../types/model-info.js';
|
|
6
|
-
import { ServerConfig } from '../types/server-config.js';
|
|
7
|
-
import { formatBytes, formatDateShort } from '../utils/format-utils.js';
|
|
8
|
-
import * as fs from 'fs/promises';
|
|
9
|
-
import { createSearchUI } from './SearchApp.js';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Models management TUI
|
|
13
|
-
* Display installed models and allow deletion
|
|
14
|
-
*/
|
|
15
|
-
export async function createModelsUI(
|
|
16
|
-
screen: blessed.Widgets.Screen,
|
|
17
|
-
onBack: () => void,
|
|
18
|
-
onSearch: () => void
|
|
19
|
-
): Promise<void> {
|
|
20
|
-
let models: ModelInfo[] = [];
|
|
21
|
-
let selectedIndex = 0;
|
|
22
|
-
let isLoading = false;
|
|
23
|
-
|
|
24
|
-
// Create content box
|
|
25
|
-
const contentBox = blessed.box({
|
|
26
|
-
top: 0,
|
|
27
|
-
left: 0,
|
|
28
|
-
width: '100%',
|
|
29
|
-
height: '100%',
|
|
30
|
-
tags: true,
|
|
31
|
-
scrollable: true,
|
|
32
|
-
alwaysScroll: true,
|
|
33
|
-
keys: true,
|
|
34
|
-
vi: true,
|
|
35
|
-
mouse: true,
|
|
36
|
-
scrollbar: {
|
|
37
|
-
ch: '█',
|
|
38
|
-
style: {
|
|
39
|
-
fg: 'blue',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
screen.append(contentBox);
|
|
44
|
-
|
|
45
|
-
// Render models view
|
|
46
|
-
async function render() {
|
|
47
|
-
const termWidth = (screen.width as number) || 80;
|
|
48
|
-
const divider = '─'.repeat(termWidth - 2);
|
|
49
|
-
let content = '';
|
|
50
|
-
|
|
51
|
-
// Header
|
|
52
|
-
content += '{bold}{blue-fg}═══ Models Management{/blue-fg}{/bold}\n\n';
|
|
53
|
-
|
|
54
|
-
if (isLoading) {
|
|
55
|
-
content += '{cyan-fg}⏳ Loading models...{/cyan-fg}\n';
|
|
56
|
-
contentBox.setContent(content);
|
|
57
|
-
screen.render();
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (models.length === 0) {
|
|
62
|
-
content += '{yellow-fg}No models found{/yellow-fg}\n\n';
|
|
63
|
-
content += '{dim}Models directory: ' + await stateManager.getModelsDirectory() + '{/dim}\n';
|
|
64
|
-
content += '{dim}Download models: Press [S] to search HuggingFace{/dim}\n';
|
|
65
|
-
content += '\n' + divider + '\n';
|
|
66
|
-
content += `{gray-fg}[S]earch [ESC] Back [Q]uit{/gray-fg}`;
|
|
67
|
-
contentBox.setContent(content);
|
|
68
|
-
screen.render();
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// System info
|
|
73
|
-
const totalSize = models.reduce((sum, m) => sum + m.size, 0);
|
|
74
|
-
content += `{bold}Total: ${models.length} models{/bold} - ${formatBytes(totalSize)}\n`;
|
|
75
|
-
content += divider + '\n';
|
|
76
|
-
|
|
77
|
-
// Get all servers to check dependencies
|
|
78
|
-
const allServers = await stateManager.getAllServers();
|
|
79
|
-
|
|
80
|
-
// Table header
|
|
81
|
-
content += '{bold} │ Model File │ Size │ Modified │ Servers{/bold}\n';
|
|
82
|
-
content += divider + '\n';
|
|
83
|
-
|
|
84
|
-
// Model rows
|
|
85
|
-
for (let i = 0; i < models.length; i++) {
|
|
86
|
-
const model = models[i];
|
|
87
|
-
const isSelected = i === selectedIndex;
|
|
88
|
-
|
|
89
|
-
// Count servers using this model
|
|
90
|
-
const serversUsingModel = allServers.filter(s => s.modelPath === model.path);
|
|
91
|
-
const serverCount = serversUsingModel.length;
|
|
92
|
-
|
|
93
|
-
// Selection indicator
|
|
94
|
-
const indicator = isSelected ? '►' : ' ';
|
|
95
|
-
|
|
96
|
-
// Model filename (truncate if too long)
|
|
97
|
-
const maxFilenameLen = 46;
|
|
98
|
-
let filename = model.filename;
|
|
99
|
-
if (filename.length > maxFilenameLen) {
|
|
100
|
-
filename = filename.substring(0, maxFilenameLen - 3) + '...';
|
|
101
|
-
}
|
|
102
|
-
filename = filename.padEnd(maxFilenameLen);
|
|
103
|
-
|
|
104
|
-
// Size
|
|
105
|
-
const size = model.sizeFormatted.padStart(11);
|
|
106
|
-
|
|
107
|
-
// Modified date
|
|
108
|
-
const modified = formatDateShort(model.modified).padStart(11);
|
|
109
|
-
|
|
110
|
-
// Servers count with color coding
|
|
111
|
-
let serversText = '';
|
|
112
|
-
let serversTextPlain = '';
|
|
113
|
-
if (serverCount === 0) {
|
|
114
|
-
serversText = '{green-fg}0 servers{/green-fg}';
|
|
115
|
-
serversTextPlain = '0 servers';
|
|
116
|
-
} else {
|
|
117
|
-
const runningCount = serversUsingModel.filter(s => s.status === 'running').length;
|
|
118
|
-
if (runningCount > 0) {
|
|
119
|
-
serversText = `{yellow-fg}${serverCount} (${runningCount} running){/yellow-fg}`;
|
|
120
|
-
serversTextPlain = `${serverCount} (${runningCount} running)`;
|
|
121
|
-
} else {
|
|
122
|
-
serversText = `{gray-fg}${serverCount} stopped{/gray-fg}`;
|
|
123
|
-
serversTextPlain = `${serverCount} stopped`;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Build row content
|
|
128
|
-
let rowContent = '';
|
|
129
|
-
if (isSelected) {
|
|
130
|
-
// Selected row: cyan background with bright white text
|
|
131
|
-
rowContent = `{cyan-bg}{15-fg}${indicator} │ ${filename} │ ${size} │ ${modified} │ ${serversTextPlain}{/15-fg}{/cyan-bg}`;
|
|
132
|
-
} else {
|
|
133
|
-
// Normal row: with colored server text
|
|
134
|
-
rowContent = `${indicator} │ ${filename} │ ${size} │ ${modified} │ ${serversText}`;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
content += rowContent + '\n';
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Footer
|
|
141
|
-
content += '\n' + divider + '\n';
|
|
142
|
-
content += '{gray-fg}[↑/↓] Navigate [D]elete [S]earch [R]efresh [ESC] Back [Q]uit{/gray-fg}';
|
|
143
|
-
|
|
144
|
-
contentBox.setContent(content);
|
|
145
|
-
screen.render();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Load models
|
|
149
|
-
async function loadModels() {
|
|
150
|
-
isLoading = true;
|
|
151
|
-
await render();
|
|
152
|
-
|
|
153
|
-
models = await modelScanner.scanModels();
|
|
154
|
-
selectedIndex = Math.min(selectedIndex, Math.max(0, models.length - 1));
|
|
155
|
-
|
|
156
|
-
isLoading = false;
|
|
157
|
-
await render();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Delete selected model
|
|
161
|
-
async function deleteModel() {
|
|
162
|
-
if (models.length === 0) return;
|
|
163
|
-
|
|
164
|
-
const model = models[selectedIndex];
|
|
165
|
-
const allServers = await stateManager.getAllServers();
|
|
166
|
-
const serversUsingModel = allServers.filter(s => s.modelPath === model.path);
|
|
167
|
-
|
|
168
|
-
// Show confirmation dialog
|
|
169
|
-
const confirmBox = blessed.message({
|
|
170
|
-
parent: screen,
|
|
171
|
-
top: 'center',
|
|
172
|
-
left: 'center',
|
|
173
|
-
width: '60%',
|
|
174
|
-
height: 'shrink',
|
|
175
|
-
border: { type: 'line' },
|
|
176
|
-
style: {
|
|
177
|
-
border: { fg: 'red' },
|
|
178
|
-
fg: 'white',
|
|
179
|
-
},
|
|
180
|
-
tags: true,
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
let confirmText = `{bold}Delete model: ${model.filename}?{/bold}\n\n`;
|
|
184
|
-
confirmText += `Size: ${model.sizeFormatted}\n\n`;
|
|
185
|
-
|
|
186
|
-
if (serversUsingModel.length > 0) {
|
|
187
|
-
confirmText += `{yellow-fg}⚠️ This model has ${serversUsingModel.length} server(s) configured:{/yellow-fg}\n`;
|
|
188
|
-
for (const server of serversUsingModel) {
|
|
189
|
-
const statusColor = server.status === 'running' ? 'green-fg' : 'gray-fg';
|
|
190
|
-
confirmText += ` - ${server.id} ({${statusColor}}${server.status}{/${statusColor}})\n`;
|
|
191
|
-
}
|
|
192
|
-
confirmText += `\n{yellow-fg}These servers will be deleted before removing the model.{/yellow-fg}\n\n`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
confirmText += `Type 'yes' to confirm:\n\n\n\n`; // Extra lines for input box space
|
|
196
|
-
|
|
197
|
-
// Create input box for confirmation
|
|
198
|
-
const inputBox = blessed.textbox({
|
|
199
|
-
parent: confirmBox,
|
|
200
|
-
bottom: 1,
|
|
201
|
-
left: 2,
|
|
202
|
-
right: 2,
|
|
203
|
-
height: 3,
|
|
204
|
-
inputOnFocus: true,
|
|
205
|
-
border: { type: 'line' },
|
|
206
|
-
style: {
|
|
207
|
-
border: { fg: 'cyan' },
|
|
208
|
-
focus: { border: { fg: 'green' } },
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
confirmBox.setContent(confirmText);
|
|
213
|
-
screen.append(confirmBox);
|
|
214
|
-
confirmBox.focus();
|
|
215
|
-
inputBox.focus();
|
|
216
|
-
screen.render();
|
|
217
|
-
|
|
218
|
-
inputBox.on('submit', async (value: string) => {
|
|
219
|
-
screen.remove(confirmBox);
|
|
220
|
-
|
|
221
|
-
if (value.toLowerCase() !== 'yes') {
|
|
222
|
-
await render();
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Show deleting message
|
|
227
|
-
isLoading = true;
|
|
228
|
-
contentBox.setContent('{cyan-fg}⏳ Deleting model...{/cyan-fg}');
|
|
229
|
-
screen.render();
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
// Delete all servers using this model
|
|
233
|
-
for (const server of serversUsingModel) {
|
|
234
|
-
// Unload service (stops and removes from launchd)
|
|
235
|
-
try {
|
|
236
|
-
await launchctlManager.unloadService(server.plistPath);
|
|
237
|
-
if (server.status === 'running') {
|
|
238
|
-
await launchctlManager.waitForServiceStop(server.label, 5000);
|
|
239
|
-
}
|
|
240
|
-
} catch (error) {
|
|
241
|
-
// Continue even if unload fails
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Delete plist
|
|
245
|
-
await launchctlManager.deletePlist(server.plistPath);
|
|
246
|
-
|
|
247
|
-
// Delete server config
|
|
248
|
-
await stateManager.deleteServerConfig(server.id);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Delete model file
|
|
252
|
-
await fs.unlink(model.path);
|
|
253
|
-
|
|
254
|
-
// Reload models
|
|
255
|
-
await loadModels();
|
|
256
|
-
} catch (error) {
|
|
257
|
-
// Show error
|
|
258
|
-
const errorBox = blessed.message({
|
|
259
|
-
parent: screen,
|
|
260
|
-
top: 'center',
|
|
261
|
-
left: 'center',
|
|
262
|
-
width: '60%',
|
|
263
|
-
height: 'shrink',
|
|
264
|
-
border: { type: 'line' },
|
|
265
|
-
style: {
|
|
266
|
-
border: { fg: 'red' },
|
|
267
|
-
fg: 'red',
|
|
268
|
-
},
|
|
269
|
-
tags: true,
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
273
|
-
errorBox.display(`{bold}Delete failed{/bold}\n\n${errorMsg}\n\nPress any key to continue`, () => {
|
|
274
|
-
screen.remove(errorBox);
|
|
275
|
-
isLoading = false;
|
|
276
|
-
render();
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
inputBox.on('cancel', () => {
|
|
282
|
-
screen.remove(confirmBox);
|
|
283
|
-
render();
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
inputBox.key(['escape'], () => {
|
|
287
|
-
screen.remove(confirmBox);
|
|
288
|
-
render();
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Store key handler references for cleanup
|
|
293
|
-
const keyHandlers = {
|
|
294
|
-
up: () => {
|
|
295
|
-
if (models.length === 0) return;
|
|
296
|
-
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
297
|
-
render();
|
|
298
|
-
},
|
|
299
|
-
down: () => {
|
|
300
|
-
if (models.length === 0) return;
|
|
301
|
-
selectedIndex = Math.min(models.length - 1, selectedIndex + 1);
|
|
302
|
-
render();
|
|
303
|
-
},
|
|
304
|
-
delete: () => {
|
|
305
|
-
deleteModel();
|
|
306
|
-
},
|
|
307
|
-
search: async () => {
|
|
308
|
-
// Cleanup current handlers before switching views
|
|
309
|
-
cleanup();
|
|
310
|
-
|
|
311
|
-
// Open search view
|
|
312
|
-
await createSearchUI(screen, async () => {
|
|
313
|
-
// onBack callback - return to models view
|
|
314
|
-
// Re-register handlers
|
|
315
|
-
registerHandlers();
|
|
316
|
-
screen.append(contentBox);
|
|
317
|
-
await loadModels();
|
|
318
|
-
});
|
|
319
|
-
},
|
|
320
|
-
refresh: () => {
|
|
321
|
-
loadModels();
|
|
322
|
-
},
|
|
323
|
-
escape: async () => {
|
|
324
|
-
cleanup();
|
|
325
|
-
await onBack();
|
|
326
|
-
},
|
|
327
|
-
quit: () => {
|
|
328
|
-
screen.destroy();
|
|
329
|
-
process.exit(0);
|
|
330
|
-
},
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
// Cleanup function to unregister all handlers
|
|
334
|
-
function cleanup() {
|
|
335
|
-
screen.unkey('up', keyHandlers.up);
|
|
336
|
-
screen.unkey('k', keyHandlers.up);
|
|
337
|
-
screen.unkey('down', keyHandlers.down);
|
|
338
|
-
screen.unkey('j', keyHandlers.down);
|
|
339
|
-
screen.unkey('d', keyHandlers.delete);
|
|
340
|
-
screen.unkey('D', keyHandlers.delete);
|
|
341
|
-
screen.unkey('s', keyHandlers.search);
|
|
342
|
-
screen.unkey('S', keyHandlers.search);
|
|
343
|
-
screen.unkey('r', keyHandlers.refresh);
|
|
344
|
-
screen.unkey('R', keyHandlers.refresh);
|
|
345
|
-
screen.unkey('escape', keyHandlers.escape);
|
|
346
|
-
screen.unkey('q', keyHandlers.quit);
|
|
347
|
-
screen.unkey('Q', keyHandlers.quit);
|
|
348
|
-
screen.unkey('C-c', keyHandlers.quit);
|
|
349
|
-
screen.remove(contentBox);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Register key handlers
|
|
353
|
-
function registerHandlers() {
|
|
354
|
-
screen.key(['up', 'k'], keyHandlers.up);
|
|
355
|
-
screen.key(['down', 'j'], keyHandlers.down);
|
|
356
|
-
screen.key(['d', 'D'], keyHandlers.delete);
|
|
357
|
-
screen.key(['s', 'S'], keyHandlers.search);
|
|
358
|
-
screen.key(['r', 'R'], keyHandlers.refresh);
|
|
359
|
-
screen.key(['escape'], keyHandlers.escape);
|
|
360
|
-
screen.key(['q', 'Q', 'C-c'], keyHandlers.quit);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Register initial handlers
|
|
364
|
-
registerHandlers();
|
|
365
|
-
|
|
366
|
-
// Initial load
|
|
367
|
-
await loadModels();
|
|
368
|
-
}
|