project-compass 2.8.2 → 2.9.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/package.json +1 -1
- package/src/cli.js +104 -62
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -78,9 +78,7 @@ function useScanner(rootPath) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function buildDetailCommands(project, config) {
|
|
81
|
-
if (!project)
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
81
|
+
if (!project) return [];
|
|
84
82
|
const builtins = Object.entries(project.commands || {}).map(([key, command]) => ({
|
|
85
83
|
label: command.label || key,
|
|
86
84
|
command: command.command,
|
|
@@ -177,6 +175,11 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
177
175
|
const [customMode, setCustomMode] = useState(false);
|
|
178
176
|
const [customInput, setCustomInput] = useState('');
|
|
179
177
|
const [customCursor, setCustomCursor] = useState(0);
|
|
178
|
+
const [renameMode, setRenameMode] = useState(false);
|
|
179
|
+
const [renameInput, setRenameInput] = useState('');
|
|
180
|
+
const [renameCursor, setRenameCursor] = useState(0);
|
|
181
|
+
const [quitConfirm, setQuitConfirm] = useState(false);
|
|
182
|
+
const [showArtBoard, setShowArtBoard] = useState(true);
|
|
180
183
|
const [config, setConfig] = useState(() => loadConfig());
|
|
181
184
|
const [showHelpCards, setShowHelpCards] = useState(false);
|
|
182
185
|
const [showStructureGuide, setShowStructureGuide] = useState(false);
|
|
@@ -189,6 +192,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
189
192
|
|
|
190
193
|
const activeTask = useMemo(() => tasks.find(t => t.id === activeTaskId), [tasks, activeTaskId]);
|
|
191
194
|
const running = activeTask?.status === 'running';
|
|
195
|
+
const hasRunningTasks = useMemo(() => tasks.some(t => t.status === 'running'), [tasks]);
|
|
192
196
|
|
|
193
197
|
const addLogToTask = useCallback((taskId, line) => {
|
|
194
198
|
setTasks(prev => prev.map(t => {
|
|
@@ -200,11 +204,11 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
200
204
|
}));
|
|
201
205
|
}, []);
|
|
202
206
|
|
|
203
|
-
const
|
|
204
|
-
const detailedIndexed = useMemo(() => detailCommands.map((command, index) => ({
|
|
207
|
+
const detailedIndexed = useMemo(() => buildDetailCommands(selectedProject, config).map((command, index) => ({
|
|
205
208
|
...command,
|
|
206
209
|
shortcut: `${index + 1}`
|
|
207
|
-
})), [
|
|
210
|
+
})), [selectedProject, config]);
|
|
211
|
+
|
|
208
212
|
const detailShortcutMap = useMemo(() => {
|
|
209
213
|
const map = new Map();
|
|
210
214
|
detailedIndexed.forEach((cmd) => map.set(cmd.shortcut, cmd));
|
|
@@ -245,52 +249,27 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
245
249
|
setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'finished'} : t));
|
|
246
250
|
addLogToTask(taskId, kleur.green(`✓ ${commandLabel} finished`));
|
|
247
251
|
} catch (error) {
|
|
248
|
-
|
|
249
|
-
|
|
252
|
+
if (error.isCanceled) {
|
|
253
|
+
setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'killed'} : t));
|
|
254
|
+
addLogToTask(taskId, kleur.yellow(`! Task killed by user`));
|
|
255
|
+
} else {
|
|
256
|
+
setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'failed'} : t));
|
|
257
|
+
addLogToTask(taskId, kleur.red(`✗ ${commandLabel} failed: ${error.shortMessage || error.message}`));
|
|
258
|
+
}
|
|
250
259
|
} finally {
|
|
251
260
|
runningProcessMap.current.delete(taskId);
|
|
252
261
|
}
|
|
253
262
|
}, [addLogToTask, selectedProject]);
|
|
254
263
|
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
customCommands: {
|
|
263
|
-
...prev.customCommands,
|
|
264
|
-
[projectKey]: [...existing, {label, command: commandTokens}]
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
saveConfig(nextConfig);
|
|
268
|
-
return nextConfig;
|
|
269
|
-
});
|
|
270
|
-
}, [selectedProject]);
|
|
271
|
-
|
|
272
|
-
const handleCustomSubmit = useCallback(() => {
|
|
273
|
-
const raw = customInput.trim();
|
|
274
|
-
if (!selectedProject || !raw) {
|
|
275
|
-
setCustomMode(false);
|
|
276
|
-
setCustomInput('');
|
|
277
|
-
setCustomCursor(0);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
const [labelPart, commandPart] = raw.split('|');
|
|
281
|
-
const commandTokens = (commandPart || labelPart).trim().split(/\s+/).filter(Boolean);
|
|
282
|
-
if (!commandTokens.length) {
|
|
283
|
-
setCustomMode(false);
|
|
284
|
-
setCustomInput('');
|
|
285
|
-
setCustomCursor(0);
|
|
286
|
-
return;
|
|
264
|
+
const handleKillTask = useCallback((taskId) => {
|
|
265
|
+
const proc = runningProcessMap.current.get(taskId);
|
|
266
|
+
if (proc) {
|
|
267
|
+
proc.kill('SIGINT');
|
|
268
|
+
} else {
|
|
269
|
+
setTasks(prev => prev.filter(t => t.id !== taskId));
|
|
270
|
+
if (activeTaskId === taskId) setActiveTaskId(null);
|
|
287
271
|
}
|
|
288
|
-
|
|
289
|
-
handleAddCustomCommand(label, commandTokens);
|
|
290
|
-
setCustomMode(false);
|
|
291
|
-
setCustomInput('');
|
|
292
|
-
setCustomCursor(0);
|
|
293
|
-
}, [customInput, selectedProject, handleAddCustomCommand]);
|
|
272
|
+
}, [activeTaskId]);
|
|
294
273
|
|
|
295
274
|
const exportLogs = useCallback(() => {
|
|
296
275
|
if (!activeTask || !activeTask.logs.length) return;
|
|
@@ -304,8 +283,32 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
304
283
|
}, [activeTask, activeTaskId, addLogToTask]);
|
|
305
284
|
|
|
306
285
|
useInput((input, key) => {
|
|
286
|
+
if (quitConfirm) {
|
|
287
|
+
if (input?.toLowerCase() === 'y') exit();
|
|
288
|
+
if (input?.toLowerCase() === 'n' || key.escape) setQuitConfirm(false);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
307
292
|
if (customMode) {
|
|
308
|
-
if (key.return) {
|
|
293
|
+
if (key.return) {
|
|
294
|
+
const raw = customInput.trim();
|
|
295
|
+
if (selectedProject && raw) {
|
|
296
|
+
const [labelPart, commandPart] = raw.split('|');
|
|
297
|
+
const commandTokens = (commandPart || labelPart).trim().split(/\s+/).filter(Boolean);
|
|
298
|
+
if (commandTokens.length) {
|
|
299
|
+
const label = commandPart ? labelPart.trim() : `Custom ${selectedProject.name}`;
|
|
300
|
+
setConfig((prev) => {
|
|
301
|
+
const projectKey = selectedProject.path;
|
|
302
|
+
const existing = prev.customCommands?.[projectKey] || [];
|
|
303
|
+
const nextConfig = { ...prev, customCommands: { ...prev.customCommands, [projectKey]: [...existing, {label, command: commandTokens}] } };
|
|
304
|
+
saveConfig(nextConfig);
|
|
305
|
+
return nextConfig;
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
setCustomMode(false); setCustomInput(''); setCustomCursor(0);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
309
312
|
if (key.escape) { setCustomMode(false); setCustomInput(''); setCustomCursor(0); return; }
|
|
310
313
|
if (key.backspace || key.delete) {
|
|
311
314
|
if (customCursor > 0) {
|
|
@@ -323,6 +326,29 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
323
326
|
return;
|
|
324
327
|
}
|
|
325
328
|
|
|
329
|
+
if (renameMode) {
|
|
330
|
+
if (key.return) {
|
|
331
|
+
setTasks(prev => prev.map(t => t.id === activeTaskId ? {...t, name: renameInput} : t));
|
|
332
|
+
setRenameMode(false); setRenameInput(''); setRenameCursor(0);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (key.escape) { setRenameMode(false); setRenameInput(''); setRenameCursor(0); return; }
|
|
336
|
+
if (key.backspace || key.delete) {
|
|
337
|
+
if (renameCursor > 0) {
|
|
338
|
+
setRenameInput((prev) => prev.slice(0, renameCursor - 1) + prev.slice(renameCursor));
|
|
339
|
+
setRenameCursor(c => Math.max(0, c - 1));
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (key.leftArrow) { setRenameCursor(c => Math.max(0, c - 1)); return; }
|
|
344
|
+
if (key.rightArrow) { setRenameCursor(c => Math.min(renameInput.length, c + 1)); return; }
|
|
345
|
+
if (input) {
|
|
346
|
+
setRenameInput((prev) => prev.slice(0, renameCursor) + input + prev.slice(renameCursor));
|
|
347
|
+
setRenameCursor(c => c + input.length);
|
|
348
|
+
}
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
326
352
|
const normalizedInput = input?.toLowerCase();
|
|
327
353
|
const shiftCombo = (char) => key.shift && normalizedInput === char;
|
|
328
354
|
|
|
@@ -332,6 +358,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
332
358
|
if (shiftCombo('x')) { setTasks(prev => prev.map(t => t.id === activeTaskId ? {...t, logs: []} : t)); setLogOffset(0); return; }
|
|
333
359
|
if (shiftCombo('e')) { exportLogs(); return; }
|
|
334
360
|
if (shiftCombo('d')) { setActiveTaskId(null); return; }
|
|
361
|
+
if (shiftCombo('b')) { setShowArtBoard(prev => !prev); return; }
|
|
335
362
|
|
|
336
363
|
if (shiftCombo('t')) {
|
|
337
364
|
setMainView((prev) => {
|
|
@@ -354,6 +381,11 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
354
381
|
if (tasks.length > 0) {
|
|
355
382
|
if (key.upArrow) { setActiveTaskId(prev => tasks[(tasks.findIndex(t => t.id === prev) - 1 + tasks.length) % tasks.length]?.id); return; }
|
|
356
383
|
if (key.downArrow) { setActiveTaskId(prev => tasks[(tasks.findIndex(t => t.id === prev) + 1) % tasks.length]?.id); return; }
|
|
384
|
+
if (shiftCombo('k') && activeTaskId) {
|
|
385
|
+
handleKillTask(activeTaskId);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (shiftCombo('r') && activeTaskId) { setRenameMode(true); setRenameInput(activeTask.name); setRenameCursor(activeTask.name.length); return; }
|
|
357
389
|
}
|
|
358
390
|
if (key.return) { setMainView('navigator'); return; }
|
|
359
391
|
return;
|
|
@@ -392,7 +424,10 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
392
424
|
setViewMode((prev) => (prev === 'detail' ? 'list' : 'detail'));
|
|
393
425
|
return;
|
|
394
426
|
}
|
|
395
|
-
if (shiftCombo('q')) {
|
|
427
|
+
if (shiftCombo('q')) {
|
|
428
|
+
if (hasRunningTasks) setQuitConfirm(true); else exit();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
396
431
|
if (shiftCombo('c') && viewMode === 'detail' && selectedProject) { setCustomMode(true); setCustomInput(''); setCustomCursor(0); return; }
|
|
397
432
|
|
|
398
433
|
const actionKey = normalizedInput && ACTION_MAP[normalizedInput];
|
|
@@ -408,6 +443,10 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
408
443
|
|
|
409
444
|
const projectCountLabel = `${projects.length} project${projects.length === 1 ? '' : 's'}`;
|
|
410
445
|
|
|
446
|
+
if (quitConfirm) {
|
|
447
|
+
return create(Box, {flexDirection: 'column', borderStyle: 'round', borderColor: 'red', padding: 1}, create(Text, {bold: true, color: 'red'}, '⚠️ Confirm Exit'), create(Text, null, `There are ${tasks.filter(t=>t.status==='running').length} tasks still running in the background.`), create(Text, null, 'Are you sure you want to quit and stop all processes?'), create(Text, {marginTop: 1}, kleur.bold('Y') + ' to Quit, ' + kleur.bold('N') + ' to Cancel'));
|
|
448
|
+
}
|
|
449
|
+
|
|
411
450
|
if (mainView === 'studio') return create(Studio);
|
|
412
451
|
|
|
413
452
|
if (mainView === 'tasks') {
|
|
@@ -415,14 +454,16 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
415
454
|
Box,
|
|
416
455
|
{flexDirection: 'column', borderStyle: 'round', borderColor: 'yellow', padding: 1},
|
|
417
456
|
create(Text, {bold: true, color: 'yellow'}, '🛰️ Task Manager | Background Processes'),
|
|
418
|
-
create(Text, {dimColor: true, marginBottom: 1}, '
|
|
457
|
+
create(Text, {dimColor: true, marginBottom: 1}, 'Up/Down: focus, Shift+K: Kill/Delete, Shift+R: Rename'),
|
|
419
458
|
...tasks.map(t => create(
|
|
420
459
|
Box,
|
|
421
|
-
{key: t.id, marginBottom: 0},
|
|
422
|
-
|
|
460
|
+
{key: t.id, marginBottom: 0, flexDirection: 'column'},
|
|
461
|
+
t.id === activeTaskId && renameMode
|
|
462
|
+
? create(Box, {flexDirection: 'row'}, create(Text, {color: 'cyan'}, '→ Rename to: '), create(CursorText, {value: renameInput, cursorIndex: renameCursor}))
|
|
463
|
+
: create(Text, {color: t.id === activeTaskId ? 'cyan' : 'white', bold: t.id === activeTaskId}, `${t.id === activeTaskId ? '→' : ' '} [${t.status.toUpperCase()}] ${t.name}`)
|
|
423
464
|
)),
|
|
424
465
|
!tasks.length && create(Text, {dimColor: true}, 'No active or background tasks.'),
|
|
425
|
-
create(Text, {marginTop: 1, dimColor: true}, 'Press Enter or Shift+T to return to Navigator
|
|
466
|
+
create(Text, {marginTop: 1, dimColor: true}, 'Press Enter or Shift+T to return to Navigator.')
|
|
426
467
|
);
|
|
427
468
|
}
|
|
428
469
|
|
|
@@ -486,10 +527,6 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
486
527
|
detailContent.push(create(Text, {key: 'e-h', dimColor: true}, 'Press Enter on a project to reveal details.'));
|
|
487
528
|
}
|
|
488
529
|
|
|
489
|
-
if (customMode) {
|
|
490
|
-
detailContent.push(create(Box, {key: 'ci-box', flexDirection: 'row'}, create(Text, {color: 'cyan'}, 'Type label|cmd (Enter: save, Esc: cancel): '), create(CursorText, {value: customInput, cursorIndex: customCursor})));
|
|
491
|
-
}
|
|
492
|
-
|
|
493
530
|
const artTileNodes = [
|
|
494
531
|
{label: 'Pulse', detail: projectCountLabel, accent: 'magenta', icon: '●', subtext: `Workspace · ${path.basename(rootPath) || rootPath}`},
|
|
495
532
|
{label: 'Focus', detail: selectedProject?.name || 'Selection', accent: 'cyan', icon: '◆', subtext: `${selectedProject?.type || 'Stack'}`},
|
|
@@ -500,11 +537,11 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
500
537
|
create(Text, {dimColor: true}, tile.subtext)
|
|
501
538
|
));
|
|
502
539
|
|
|
503
|
-
const artBoard = create(Box, {flexDirection: 'column', marginTop: 1, borderStyle: 'round', borderColor: 'gray', padding: 1},
|
|
540
|
+
const artBoard = showArtBoard ? create(Box, {flexDirection: 'column', marginTop: 1, borderStyle: 'round', borderColor: 'gray', padding: 1},
|
|
504
541
|
create(Box, {flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {color: 'magenta', bold: true}, 'Art-coded build atlas'), create(Text, {dimColor: true}, 'press ? for overlay help')),
|
|
505
542
|
create(Box, {flexDirection: 'row', marginTop: 1}, ...ART_CHARS.map((char, i) => create(Text, {key: i, color: ART_COLORS[i % ART_COLORS.length]}, char.repeat(2)))),
|
|
506
543
|
create(Box, {flexDirection: 'row', marginTop: 1}, ...artTileNodes)
|
|
507
|
-
);
|
|
544
|
+
) : null;
|
|
508
545
|
|
|
509
546
|
const logs = activeTask?.logs || [];
|
|
510
547
|
const logWindowStart = Math.max(0, logs.length - OUTPUT_WINDOW_SIZE - logOffset);
|
|
@@ -515,7 +552,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
515
552
|
const helpCards = [
|
|
516
553
|
{label: 'Navigation', color: 'magenta', body: ['↑ / ↓ move focus, Enter: details', 'Shift+↑ / ↓ scroll output', 'Shift+H toggle help cards', 'Shift+D detach from task']},
|
|
517
554
|
{label: 'Commands', color: 'cyan', body: ['B / T / R build/test/run', '1-9 run detail commands', 'Shift+L rerun last command', 'Shift+X clear / Shift+E export']},
|
|
518
|
-
{label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A
|
|
555
|
+
{label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+B art', 'Shift+C custom / Shift+Q quit']}
|
|
519
556
|
];
|
|
520
557
|
|
|
521
558
|
const toggleHint = showHelpCards ? 'Shift+H hide help' : 'Shift+H show help';
|
|
@@ -533,11 +570,11 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
533
570
|
create(Box, {flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {bold: true, color: 'yellow'}, `Output: ${activeTask?.name || 'None'}`), create(Text, {dimColor: true}, logOffset ? `Scrolled ${logOffset} lines` : 'Live log view')),
|
|
534
571
|
create(Box, {flexDirection: 'column', borderStyle: 'round', borderColor: 'yellow', padding: 1, minHeight: OUTPUT_WINDOW_HEIGHT, maxHeight: OUTPUT_WINDOW_HEIGHT, height: OUTPUT_WINDOW_HEIGHT, overflow: 'hidden'}, ...logNodes),
|
|
535
572
|
create(Box, {marginTop: 1, flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {dimColor: true}, running ? 'Type to feed stdin; Enter: submit, Ctrl+C: abort.' : 'Run a command or press Shift+T to switch tasks.'), create(Text, {dimColor: true}, `${toggleHint}, Shift+S: Structure Guide`)),
|
|
536
|
-
create(Box, {marginTop: 1, flexDirection: 'row', borderStyle: 'round', borderColor: running ? 'green' : 'gray', paddingX: 1}, create(Text, {bold: true, color: 'green'}, running ? ' Stdin buffer ' : ' Input ready '), create(Box, {marginLeft: 1}, create(CursorText, {value: stdinBuffer || (running ? '' : 'Start a command to feed stdin'), cursorIndex: stdinCursor, active: running})))
|
|
573
|
+
create(Box, {marginTop: 1, flexDirection: 'row', borderStyle: 'round', borderColor: running ? 'green' : 'gray', paddingX: 1}, create(Text, {bold: true, color: running ? 'green' : 'white'}, running ? ' Stdin buffer ' : ' Input ready '), create(Box, {marginLeft: 1}, create(CursorText, {value: stdinBuffer || (running ? '' : 'Start a command to feed stdin'), cursorIndex: stdinCursor, active: running})))
|
|
537
574
|
),
|
|
538
575
|
showHelpCards && create(Box, {marginTop: 1, flexDirection: 'row', justifyContent: 'space-between', flexWrap: 'wrap'}, ...helpCards.map((card, idx) => create(Box, {key: card.label, flexGrow: 1, flexBasis: 0, minWidth: HELP_CARD_MIN_WIDTH, marginRight: idx < 2 ? 1 : 0, marginBottom: 1, borderStyle: 'round', borderColor: card.color, padding: 1, flexDirection: 'column'}, create(Text, {color: card.color, bold: true, marginBottom: 1}, card.label), ...card.body.map((line, lidx) => create(Text, {key: lidx, dimColor: card.color === 'yellow'}, line))))),
|
|
539
576
|
showStructureGuide && create(Box, {flexDirection: 'column', borderStyle: 'round', borderColor: 'blue', marginTop: 1, padding: 1}, create(Text, {color: 'cyan', bold: true}, 'Structure guide · press Shift+S to hide'), ...SCHEMA_GUIDE.map(e => create(Text, {key: e.type, dimColor: true}, `• ${e.icon} ${e.label}: ${e.files.join(', ')}`))),
|
|
540
|
-
showHelp && create(Box, {flexDirection: 'column', borderStyle: 'double', borderColor: 'cyan', marginTop: 1, padding: 1}, create(Text, {color: 'cyan', bold: true}, 'Help overlay'), create(Text, null, 'Shift+↑/↓ scrolls logs; Shift+X clears; Shift+E exports; Shift+A Studio; Shift+T Tasks; Shift+D Detach.'))
|
|
577
|
+
showHelp && create(Box, {flexDirection: 'column', borderStyle: 'double', borderColor: 'cyan', marginTop: 1, padding: 1}, create(Text, {color: 'cyan', bold: true}, 'Help overlay'), create(Text, null, 'Shift+↑/↓ scrolls logs; Shift+X clears; Shift+E exports; Shift+A Studio; Shift+T Tasks; Shift+D Detach; Shift+B Toggle Art.'))
|
|
541
578
|
);
|
|
542
579
|
}
|
|
543
580
|
|
|
@@ -574,10 +611,15 @@ async function main() {
|
|
|
574
611
|
console.log(' Shift+A Switch to Omni-Studio (Environment Health)');
|
|
575
612
|
console.log(' Shift+T Open Orbit Task Manager (Manage background processes)');
|
|
576
613
|
console.log(' Shift+D Detach from active task (Keep it running in background)');
|
|
614
|
+
console.log(' Shift+B Toggle Art Board visibility');
|
|
577
615
|
console.log(' Shift+X Clear active task output log');
|
|
578
616
|
console.log(' Shift+E Export current logs to a .txt file');
|
|
579
617
|
console.log(' Shift+↑ / ↓ Scroll the output logs');
|
|
580
|
-
console.log(' Shift+Q Quit application');
|
|
618
|
+
console.log(' Shift+Q Quit application (with confirmation if tasks run)');
|
|
619
|
+
console.log('');
|
|
620
|
+
console.log(kleur.bold('Task Manager (Shift+T):'));
|
|
621
|
+
console.log(' Shift+K Kill active/selected task');
|
|
622
|
+
console.log(' Shift+R Rename selected task');
|
|
581
623
|
console.log('');
|
|
582
624
|
console.log(kleur.bold('Execution shortcuts:'));
|
|
583
625
|
console.log(' B / T / R Quick run: Build / Test / Run');
|