project-compass 2.9.0 → 2.9.2

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +46 -21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-compass",
3
- "version": "2.9.0",
3
+ "version": "2.9.2",
4
4
  "description": "Ink-based project explorer that detects local repos and lets you build/test/run them without memorizing commands.",
5
5
  "main": "src/cli.js",
6
6
  "type": "module",
package/src/cli.js CHANGED
@@ -9,7 +9,6 @@ import {discoverProjects, SCHEMA_GUIDE, checkBinary} from './projectDetection.js
9
9
  import {CONFIG_PATH, ensureConfigDir} from './configPaths.js';
10
10
 
11
11
  const create = React.createElement;
12
- const DEFAULT_CONFIG = {customCommands: {}};
13
12
  const ART_CHARS = ['▁', '▃', '▄', '▅', '▇'];
14
13
  const ART_COLORS = ['magenta', 'blue', 'cyan', 'yellow', 'red'];
15
14
  const OUTPUT_WINDOW_SIZE = 8;
@@ -38,18 +37,17 @@ function loadConfig() {
38
37
  const payload = fs.readFileSync(CONFIG_PATH, 'utf-8');
39
38
  const parsed = JSON.parse(payload || '{}');
40
39
  return {
41
- ...DEFAULT_CONFIG,
40
+ customCommands: {},
41
+ showArtBoard: true,
42
+ showHelpCards: false,
43
+ showStructureGuide: false,
42
44
  ...parsed,
43
- customCommands: {
44
- ...DEFAULT_CONFIG.customCommands,
45
- ...(parsed.customCommands || {})
46
- }
47
45
  };
48
46
  }
49
47
  } catch (error) {
50
48
  console.error(`Ignoring corrupt config: ${error.message}`);
51
49
  }
52
- return {...DEFAULT_CONFIG};
50
+ return {customCommands: {}, showArtBoard: true, showHelpCards: false, showStructureGuide: false};
53
51
  }
54
52
 
55
53
  function useScanner(rootPath) {
@@ -179,10 +177,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
179
177
  const [renameInput, setRenameInput] = useState('');
180
178
  const [renameCursor, setRenameCursor] = useState(0);
181
179
  const [quitConfirm, setQuitConfirm] = useState(false);
182
- const [showArtBoard, setShowArtBoard] = useState(true);
183
180
  const [config, setConfig] = useState(() => loadConfig());
184
- const [showHelpCards, setShowHelpCards] = useState(false);
185
- const [showStructureGuide, setShowStructureGuide] = useState(false);
186
181
  const [stdinBuffer, setStdinBuffer] = useState('');
187
182
  const [stdinCursor, setStdinCursor] = useState(0);
188
183
  const [showHelp, setShowHelp] = useState(false);
@@ -215,6 +210,13 @@ function Compass({rootPath, initialView = 'navigator'}) {
215
210
  return map;
216
211
  }, [detailedIndexed]);
217
212
 
213
+ const killAllTasks = useCallback(() => {
214
+ runningProcessMap.current.forEach((proc) => {
215
+ try { proc.kill('SIGINT'); } catch { /* ignore */ }
216
+ });
217
+ runningProcessMap.current.clear();
218
+ }, []);
219
+
218
220
  const runProjectCommand = useCallback(async (commandMeta, targetProject = selectedProject) => {
219
221
  const project = targetProject || selectedProject;
220
222
  if (!project) return;
@@ -249,7 +251,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
249
251
  setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'finished'} : t));
250
252
  addLogToTask(taskId, kleur.green(`✓ ${commandLabel} finished`));
251
253
  } catch (error) {
252
- if (error.isCanceled) {
254
+ if (error.isCanceled || error.killed) {
253
255
  setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'killed'} : t));
254
256
  addLogToTask(taskId, kleur.yellow(`! Task killed by user`));
255
257
  } else {
@@ -284,8 +286,8 @@ function Compass({rootPath, initialView = 'navigator'}) {
284
286
 
285
287
  useInput((input, key) => {
286
288
  if (quitConfirm) {
287
- if (input?.toLowerCase() === 'y') exit();
288
- if (input?.toLowerCase() === 'n' || key.escape) setQuitConfirm(false);
289
+ if (input?.toLowerCase() === 'y') { killAllTasks(); exit(); return; }
290
+ if (input?.toLowerCase() === 'n' || key.escape) { setQuitConfirm(false); return; }
289
291
  return;
290
292
  }
291
293
 
@@ -352,13 +354,34 @@ function Compass({rootPath, initialView = 'navigator'}) {
352
354
  const normalizedInput = input?.toLowerCase();
353
355
  const shiftCombo = (char) => key.shift && normalizedInput === char;
354
356
 
355
- if (shiftCombo('h')) { setShowHelpCards((prev) => !prev); return; }
356
- if (shiftCombo('s')) { setShowStructureGuide((prev) => !prev); return; }
357
+ if (shiftCombo('h')) {
358
+ setConfig(prev => {
359
+ const next = {...prev, showHelpCards: !prev.showHelpCards};
360
+ saveConfig(next);
361
+ return next;
362
+ });
363
+ return;
364
+ }
365
+ if (shiftCombo('s')) {
366
+ setConfig(prev => {
367
+ const next = {...prev, showStructureGuide: !prev.showStructureGuide};
368
+ saveConfig(next);
369
+ return next;
370
+ });
371
+ return;
372
+ }
357
373
  if (shiftCombo('a')) { setMainView((prev) => (prev === 'navigator' ? 'studio' : 'navigator')); return; }
358
374
  if (shiftCombo('x')) { setTasks(prev => prev.map(t => t.id === activeTaskId ? {...t, logs: []} : t)); setLogOffset(0); return; }
359
375
  if (shiftCombo('e')) { exportLogs(); return; }
360
376
  if (shiftCombo('d')) { setActiveTaskId(null); return; }
361
- if (shiftCombo('b')) { setShowArtBoard(prev => !prev); return; }
377
+ if (shiftCombo('b')) {
378
+ setConfig(prev => {
379
+ const next = {...prev, showArtBoard: !prev.showArtBoard};
380
+ saveConfig(next);
381
+ return next;
382
+ });
383
+ return;
384
+ }
362
385
 
363
386
  if (shiftCombo('t')) {
364
387
  setMainView((prev) => {
@@ -537,7 +560,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
537
560
  create(Text, {dimColor: true}, tile.subtext)
538
561
  ));
539
562
 
540
- const artBoard = showArtBoard ? create(Box, {flexDirection: 'column', marginTop: 1, borderStyle: 'round', borderColor: 'gray', padding: 1},
563
+ const artBoard = config.showArtBoard ? create(Box, {flexDirection: 'column', marginTop: 1, borderStyle: 'round', borderColor: 'gray', padding: 1},
541
564
  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')),
542
565
  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)))),
543
566
  create(Box, {flexDirection: 'row', marginTop: 1}, ...artTileNodes)
@@ -552,10 +575,10 @@ function Compass({rootPath, initialView = 'navigator'}) {
552
575
  const helpCards = [
553
576
  {label: 'Navigation', color: 'magenta', body: ['↑ / ↓ move focus, Enter: details', 'Shift+↑ / ↓ scroll output', 'Shift+H toggle help cards', 'Shift+D detach from task']},
554
577
  {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']},
555
- {label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+B art', 'Shift+C custom / Shift+Q quit']}
578
+ {label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+B art board', 'Shift+S structure / Shift+Q quit']}
556
579
  ];
557
580
 
558
- const toggleHint = showHelpCards ? 'Shift+H hide help' : 'Shift+H show help';
581
+ const toggleHint = config.showHelpCards ? 'Shift+H hide help' : 'Shift+H show help';
559
582
  return create(Box, {flexDirection: 'column', padding: 1},
560
583
  create(Box, {justifyContent: 'space-between'},
561
584
  create(Box, {flexDirection: 'column'}, create(Text, {color: 'magenta', bold: true}, 'Project Compass'), create(Text, {dimColor: true}, `${projectCountLabel} detected in ${rootPath}`)),
@@ -572,8 +595,8 @@ function Compass({rootPath, initialView = 'navigator'}) {
572
595
  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`)),
573
596
  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})))
574
597
  ),
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))))),
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(', ')}`))),
598
+ config.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))))),
599
+ config.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(', ')}`))),
577
600
  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.'))
578
601
  );
579
602
  }
@@ -612,6 +635,8 @@ async function main() {
612
635
  console.log(' Shift+T Open Orbit Task Manager (Manage background processes)');
613
636
  console.log(' Shift+D Detach from active task (Keep it running in background)');
614
637
  console.log(' Shift+B Toggle Art Board visibility');
638
+ console.log(' Shift+H Toggle Help Cards visibility');
639
+ console.log(' Shift+S Toggle Structure Guide visibility');
615
640
  console.log(' Shift+X Clear active task output log');
616
641
  console.log(' Shift+E Export current logs to a .txt file');
617
642
  console.log(' Shift+↑ / ↓ Scroll the output logs');