project-compass 2.6.0 → 2.7.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +71 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-compass",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
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
@@ -152,6 +152,27 @@ function Studio() {
152
152
  );
153
153
  }
154
154
 
155
+ function CursorText({value, cursorIndex, active = true}) {
156
+ const [visible, setVisible] = useState(true);
157
+ useEffect(() => {
158
+ if (!active) return;
159
+ const interval = setInterval(() => setVisible(v => !v), 500);
160
+ return () => clearInterval(interval);
161
+ }, [active]);
162
+
163
+ const before = value.slice(0, cursorIndex);
164
+ const charAt = value[cursorIndex] || ' ';
165
+ const after = value.slice(cursorIndex + 1);
166
+
167
+ return create(
168
+ Text,
169
+ null,
170
+ before,
171
+ active && visible ? create(Text, {backgroundColor: 'white', color: 'black'}, charAt) : charAt,
172
+ after
173
+ );
174
+ }
175
+
155
176
  function Compass({rootPath, initialView = 'navigator'}) {
156
177
  const {exit} = useApp();
157
178
  const {projects, loading, error} = useScanner(rootPath);
@@ -164,10 +185,12 @@ function Compass({rootPath, initialView = 'navigator'}) {
164
185
  const [lastAction, setLastAction] = useState(null);
165
186
  const [customMode, setCustomMode] = useState(false);
166
187
  const [customInput, setCustomInput] = useState('');
188
+ const [customCursor, setCustomCursor] = useState(0);
167
189
  const [config, setConfig] = useState(() => loadConfig());
168
190
  const [showHelpCards, setShowHelpCards] = useState(false);
169
191
  const [showStructureGuide, setShowStructureGuide] = useState(false);
170
192
  const [stdinBuffer, setStdinBuffer] = useState('');
193
+ const [stdinCursor, setStdinCursor] = useState(0);
171
194
  const [showHelp, setShowHelp] = useState(false);
172
195
  const [recentRuns, setRecentRuns] = useState([]);
173
196
  const selectedProject = projects[selectedIndex] || null;
@@ -241,6 +264,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
241
264
  } finally {
242
265
  setRunning(false);
243
266
  setStdinBuffer('');
267
+ setStdinCursor(0);
244
268
  runningProcessRef.current = null;
245
269
  }
246
270
  }, [addLog, running, selectedProject]);
@@ -276,6 +300,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
276
300
  addLog(kleur.gray('Canceled custom command (empty).'));
277
301
  setCustomMode(false);
278
302
  setCustomInput('');
303
+ setCustomCursor(0);
279
304
  return;
280
305
  }
281
306
  const [labelPart, commandPart] = raw.split('|');
@@ -284,12 +309,14 @@ function Compass({rootPath, initialView = 'navigator'}) {
284
309
  addLog(kleur.red('Custom command needs at least one token.'));
285
310
  setCustomMode(false);
286
311
  setCustomInput('');
312
+ setCustomCursor(0);
287
313
  return;
288
314
  }
289
315
  const label = commandPart ? labelPart.trim() : `Custom ${selectedProject.name}`;
290
316
  handleAddCustomCommand(label || 'Custom', commandTokens);
291
317
  setCustomMode(false);
292
318
  setCustomInput('');
319
+ setCustomCursor(0);
293
320
  }, [customInput, selectedProject, handleAddCustomCommand, addLog]);
294
321
 
295
322
  const exportLogs = useCallback(() => {
@@ -314,14 +341,27 @@ function Compass({rootPath, initialView = 'navigator'}) {
314
341
  if (key.escape) {
315
342
  setCustomMode(false);
316
343
  setCustomInput('');
344
+ setCustomCursor(0);
345
+ return;
346
+ }
347
+ if (key.backspace || key.delete) {
348
+ if (customCursor > 0) {
349
+ setCustomInput((prev) => prev.slice(0, customCursor - 1) + prev.slice(customCursor));
350
+ setCustomCursor(c => Math.max(0, c - 1));
351
+ }
352
+ return;
353
+ }
354
+ if (key.leftArrow) {
355
+ setCustomCursor(c => Math.max(0, c - 1));
317
356
  return;
318
357
  }
319
- if (key.backspace) {
320
- setCustomInput((prev) => prev.slice(0, -1));
358
+ if (key.rightArrow) {
359
+ setCustomCursor(c => Math.min(customInput.length, c + 1));
321
360
  return;
322
361
  }
323
362
  if (input) {
324
- setCustomInput((prev) => prev + input);
363
+ setCustomInput((prev) => prev.slice(0, customCursor) + input + prev.slice(customCursor));
364
+ setCustomCursor(c => c + input.length);
325
365
  }
326
366
  return;
327
367
  }
@@ -363,21 +403,33 @@ function Compass({rootPath, initialView = 'navigator'}) {
363
403
  if (key.ctrl && input === 'c') {
364
404
  runningProcessRef.current.kill('SIGINT');
365
405
  setStdinBuffer('');
406
+ setStdinCursor(0);
366
407
  return;
367
408
  }
368
409
  if (key.return) {
369
- runningProcessRef.current.stdin?.write('\n');
410
+ runningProcessRef.current.stdin?.write(stdinBuffer + '\n');
370
411
  setStdinBuffer('');
412
+ setStdinCursor(0);
413
+ return;
414
+ }
415
+ if (key.backspace || key.delete) {
416
+ if (stdinCursor > 0) {
417
+ setStdinBuffer(prev => prev.slice(0, stdinCursor - 1) + prev.slice(stdinCursor));
418
+ setStdinCursor(c => Math.max(0, c - 1));
419
+ }
420
+ return;
421
+ }
422
+ if (key.leftArrow) {
423
+ setStdinCursor(c => Math.max(0, c - 1));
371
424
  return;
372
425
  }
373
- if (key.backspace) {
374
- runningProcessRef.current.stdin?.write('\x08');
375
- setStdinBuffer((prev) => prev.slice(0, -1));
426
+ if (key.rightArrow) {
427
+ setStdinCursor(c => Math.min(stdinBuffer.length, c + 1));
376
428
  return;
377
429
  }
378
430
  if (input) {
379
- runningProcessRef.current.stdin?.write(input);
380
- setStdinBuffer((prev) => prev + input);
431
+ setStdinBuffer(prev => prev.slice(0, stdinCursor) + input + prev.slice(stdinCursor));
432
+ setStdinCursor(c => c + input.length);
381
433
  }
382
434
  return;
383
435
  }
@@ -422,6 +474,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
422
474
  if (shiftCombo('c') && viewMode === 'detail' && selectedProject) {
423
475
  setCustomMode(true);
424
476
  setCustomInput('');
477
+ setCustomCursor(0);
425
478
  return;
426
479
  }
427
480
  const actionKey = normalizedInput && ACTION_MAP[normalizedInput];
@@ -529,7 +582,14 @@ function Compass({rootPath, initialView = 'navigator'}) {
529
582
  }
530
583
 
531
584
  if (customMode) {
532
- detailContent.push(create(Text, {key: 'custom-input', color: 'cyan'}, `Type label|cmd (Enter to save, Esc to cancel): ${customInput}`));
585
+ detailContent.push(
586
+ create(
587
+ Box,
588
+ {key: 'custom-input-box', flexDirection: 'row'},
589
+ create(Text, {color: 'cyan'}, 'Type label|cmd (Enter: save, Esc: cancel): '),
590
+ create(CursorText, {value: customInput, cursorIndex: customCursor})
591
+ )
592
+ );
533
593
  }
534
594
 
535
595
  const artTileNodes = useMemo(() => {
@@ -818,7 +878,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
818
878
  paddingX: 1
819
879
  },
820
880
  create(Text, {bold: true, color: running ? 'green' : 'white'}, running ? ' Stdin buffer ' : ' Input ready '),
821
- create(Text, {dimColor: true, marginLeft: 1}, running ? (stdinBuffer || '(type to send)') : 'Start a command to feed stdin')
881
+ create(Box, {marginLeft: 1}, create(CursorText, {value: stdinBuffer || (running ? '' : 'Start a command to feed stdin'), cursorIndex: stdinCursor, active: running}))
822
882
  )
823
883
  ),
824
884
  helpSection,