project-compass 3.0.1 → 3.2.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 +64 -45
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
|
2
|
+
import React, {useCallback, useEffect, useMemo, useRef, useState, memo} from 'react';
|
|
3
3
|
import {render, Box, Text, useApp, useInput} from 'ink';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import fs from 'fs';
|
|
@@ -90,7 +90,7 @@ function buildDetailCommands(project, config) {
|
|
|
90
90
|
return [...builtins, ...custom];
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
const Studio = memo(() => {
|
|
94
94
|
const [runtimes, setRuntimes] = useState([]);
|
|
95
95
|
const [loading, setLoading] = useState(true);
|
|
96
96
|
|
|
@@ -145,7 +145,7 @@ function Studio() {
|
|
|
145
145
|
create(Text, {dimColor: true}, 'Press Shift+A to return to Navigator.')
|
|
146
146
|
)
|
|
147
147
|
);
|
|
148
|
-
}
|
|
148
|
+
});
|
|
149
149
|
|
|
150
150
|
function CursorText({value, cursorIndex, active = true}) {
|
|
151
151
|
const before = value.slice(0, cursorIndex);
|
|
@@ -161,6 +161,32 @@ function CursorText({value, cursorIndex, active = true}) {
|
|
|
161
161
|
);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
const OutputPanel = memo(({activeTask, logOffset}) => {
|
|
165
|
+
const logs = activeTask?.logs || [];
|
|
166
|
+
const logWindowStart = Math.max(0, logs.length - OUTPUT_WINDOW_SIZE - logOffset);
|
|
167
|
+
const logWindowEnd = Math.max(0, logs.length - logOffset);
|
|
168
|
+
const visibleLogs = logs.slice(logWindowStart, logWindowEnd);
|
|
169
|
+
|
|
170
|
+
const logNodes = visibleLogs.length
|
|
171
|
+
? visibleLogs.map((line, i) => create(Text, {key: i}, line))
|
|
172
|
+
: [create(Text, {key: 'empty', dimColor: true}, 'Select a task or run a command to see logs.')];
|
|
173
|
+
|
|
174
|
+
return create(
|
|
175
|
+
Box,
|
|
176
|
+
{
|
|
177
|
+
flexDirection: 'column',
|
|
178
|
+
borderStyle: 'round',
|
|
179
|
+
borderColor: 'yellow',
|
|
180
|
+
padding: 1,
|
|
181
|
+
minHeight: OUTPUT_WINDOW_HEIGHT,
|
|
182
|
+
maxHeight: OUTPUT_WINDOW_HEIGHT,
|
|
183
|
+
height: OUTPUT_WINDOW_HEIGHT,
|
|
184
|
+
overflow: 'hidden'
|
|
185
|
+
},
|
|
186
|
+
...logNodes
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
164
190
|
function Compass({rootPath, initialView = 'navigator'}) {
|
|
165
191
|
const {exit} = useApp();
|
|
166
192
|
const {projects, loading, error} = useScanner(rootPath);
|
|
@@ -494,67 +520,66 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
494
520
|
);
|
|
495
521
|
}
|
|
496
522
|
|
|
497
|
-
const projectRows =
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
projects.forEach((project, index) => {
|
|
523
|
+
const projectRows = useMemo(() => {
|
|
524
|
+
if (loading) return [create(Text, {key: 'scanning', dimColor: true}, 'Scanning projects…')];
|
|
525
|
+
if (error) return [create(Text, {key: 'error', color: 'red'}, `Unable to scan: ${error}`)];
|
|
526
|
+
if (projects.length === 0) return [create(Text, {key: 'empty', dimColor: true}, 'No recognizable project manifests found.')];
|
|
527
|
+
|
|
528
|
+
return projects.map((project, index) => {
|
|
504
529
|
const isSelected = index === selectedIndex;
|
|
505
530
|
const frameworkBadges = (project.frameworks || []).map((frame) => `${frame.icon} ${frame.name}`).join(', ');
|
|
506
531
|
const hasMissingRuntime = project.missingBinaries && project.missingBinaries.length > 0;
|
|
507
|
-
|
|
532
|
+
return create(
|
|
533
|
+
Box,
|
|
534
|
+
{key: project.id, flexDirection: 'column', marginBottom: 1, padding: 1},
|
|
508
535
|
create(
|
|
509
536
|
Box,
|
|
510
|
-
{
|
|
511
|
-
create(
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
),
|
|
517
|
-
create(Text, {dimColor: true}, ` ${project.type} · ${path.relative(rootPath, project.path) || '.'}`),
|
|
518
|
-
frameworkBadges && create(Text, {dimColor: true}, ` ${frameworkBadges}`)
|
|
519
|
-
)
|
|
537
|
+
{flexDirection: 'row'},
|
|
538
|
+
create(Text, {color: isSelected ? 'cyan' : 'white', bold: isSelected}, `${project.icon} ${project.name}`),
|
|
539
|
+
hasMissingRuntime && create(Text, {color: 'red', bold: true}, ' ⚠️ Runtime missing')
|
|
540
|
+
),
|
|
541
|
+
create(Text, {dimColor: true}, ` ${project.type} · ${path.relative(rootPath, project.path) || '.'}`),
|
|
542
|
+
frameworkBadges && create(Text, {dimColor: true}, ` ${frameworkBadges}`)
|
|
520
543
|
);
|
|
521
544
|
});
|
|
522
|
-
}
|
|
545
|
+
}, [loading, error, projects, selectedIndex, rootPath]);
|
|
523
546
|
|
|
524
|
-
const detailContent =
|
|
525
|
-
|
|
526
|
-
|
|
547
|
+
const detailContent = useMemo(() => {
|
|
548
|
+
if (viewMode !== 'detail' || !selectedProject) {
|
|
549
|
+
return [create(Text, {key: 'e-h', dimColor: true}, 'Press Enter on a project to reveal details.')];
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const content = [
|
|
527
553
|
create(Box, {key: 'title-row', flexDirection: 'row'},
|
|
528
554
|
create(Text, {color: 'cyan', bold: true}, `${selectedProject.icon} ${selectedProject.name}`),
|
|
529
555
|
selectedProject.missingBinaries && selectedProject.missingBinaries.length > 0 && create(Text, {color: 'red', bold: true}, ' ⚠️ MISSING RUNTIME')
|
|
530
556
|
),
|
|
531
557
|
create(Text, {key: 'manifest', dimColor: true}, `${selectedProject.type} · ${selectedProject.manifest || 'detected manifest'}`),
|
|
532
558
|
create(Text, {key: 'loc', dimColor: true}, `Location: ${path.relative(rootPath, selectedProject.path) || '.'}`)
|
|
533
|
-
|
|
534
|
-
if (selectedProject.description)
|
|
559
|
+
];
|
|
560
|
+
if (selectedProject.description) content.push(create(Text, {key: 'desc'}, selectedProject.description));
|
|
535
561
|
const frameworks = (selectedProject.frameworks || []).map((lib) => `${lib.icon} ${lib.name}`).join(', ');
|
|
536
|
-
if (frameworks)
|
|
562
|
+
if (frameworks) content.push(create(Text, {key: 'frames', dimColor: true}, `Frameworks: ${frameworks}`));
|
|
537
563
|
|
|
538
564
|
if (selectedProject.missingBinaries && selectedProject.missingBinaries.length > 0) {
|
|
539
|
-
|
|
565
|
+
content.push(
|
|
540
566
|
create(Text, {key: 'm-t', color: 'red', bold: true, marginTop: 1}, 'MISSING BINARIES:'),
|
|
541
567
|
create(Text, {key: 'm-l', color: 'red'}, `Please install: ${selectedProject.missingBinaries.join(', ')}`)
|
|
542
568
|
);
|
|
543
569
|
}
|
|
544
570
|
|
|
545
|
-
|
|
571
|
+
content.push(create(Text, {key: 'cmd-header', bold: true, marginTop: 1}, 'Commands'));
|
|
546
572
|
detailedIndexed.forEach((command) => {
|
|
547
|
-
|
|
573
|
+
content.push(
|
|
548
574
|
create(Text, {key: `d-${command.shortcut}`}, `${command.shortcut}. ${command.label} ${command.source === 'custom' ? kleur.magenta('(custom)') : command.source === 'framework' ? kleur.cyan('(framework)') : ''}`),
|
|
549
575
|
create(Text, {key: `dl-${command.shortcut}`, dimColor: true}, ` ↳ ${command.command.join(' ')}`)
|
|
550
576
|
);
|
|
551
577
|
});
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
578
|
+
content.push(create(Text, {key: 'h-l', dimColor: true, marginTop: 1}, 'Press Shift+C → label|cmd to save custom actions, Enter to close detail view.'));
|
|
579
|
+
return content;
|
|
580
|
+
}, [viewMode, selectedProject, rootPath, detailedIndexed]);
|
|
556
581
|
|
|
557
|
-
const artTileNodes = [
|
|
582
|
+
const artTileNodes = useMemo(() => [
|
|
558
583
|
{label: 'Pulse', detail: projectCountLabel, accent: 'magenta', icon: '●', subtext: `Workspace · ${path.basename(rootPath) || rootPath}`},
|
|
559
584
|
{label: 'Focus', detail: selectedProject?.name || 'Selection', accent: 'cyan', icon: '◆', subtext: `${selectedProject?.type || 'Stack'}`},
|
|
560
585
|
{label: 'Orbit', detail: `${tasks.length} active tasks`, accent: 'yellow', icon: '■', subtext: running ? 'Busy streaming...' : 'Idle'}
|
|
@@ -562,7 +587,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
562
587
|
create(Text, {color: tile.accent, bold: true}, `${tile.icon} ${tile.label}`),
|
|
563
588
|
create(Text, {bold: true}, tile.detail),
|
|
564
589
|
create(Text, {dimColor: true}, tile.subtext)
|
|
565
|
-
));
|
|
590
|
+
)), [projectCountLabel, rootPath, selectedProject, tasks.length, running]);
|
|
566
591
|
|
|
567
592
|
const artBoard = config.showArtBoard ? create(Box, {flexDirection: 'column', marginTop: 1, borderStyle: 'round', borderColor: 'gray', padding: 1},
|
|
568
593
|
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')),
|
|
@@ -570,16 +595,10 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
570
595
|
create(Box, {flexDirection: 'row', marginTop: 1}, ...artTileNodes)
|
|
571
596
|
) : null;
|
|
572
597
|
|
|
573
|
-
const logs = activeTask?.logs || [];
|
|
574
|
-
const logWindowStart = Math.max(0, logs.length - OUTPUT_WINDOW_SIZE - logOffset);
|
|
575
|
-
const logWindowEnd = Math.max(0, logs.length - logOffset);
|
|
576
|
-
const visibleLogs = logs.slice(logWindowStart, logWindowEnd);
|
|
577
|
-
const logNodes = visibleLogs.length ? visibleLogs.map((line, i) => create(Text, {key: i}, line)) : [create(Text, {dimColor: true}, 'Select a task or run a command to see logs.')];
|
|
578
|
-
|
|
579
598
|
const helpCards = [
|
|
580
599
|
{label: 'Navigation', color: 'magenta', body: ['↑ / ↓ move focus, Enter: details', 'Shift+↑ / ↓ scroll output', 'Shift+H toggle help cards', 'Shift+D detach from task']},
|
|
581
600
|
{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']},
|
|
582
|
-
{label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+B art board', 'Shift+
|
|
601
|
+
{label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+B art board', 'Shift+C custom / Shift+Q quit']}
|
|
583
602
|
];
|
|
584
603
|
|
|
585
604
|
return create(Box, {flexDirection: 'column', padding: 1},
|
|
@@ -597,7 +616,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
597
616
|
),
|
|
598
617
|
create(Box, {marginTop: 1, flexDirection: 'column'},
|
|
599
618
|
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')),
|
|
600
|
-
create(
|
|
619
|
+
create(OutputPanel, {activeTask, logOffset}),
|
|
601
620
|
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`)),
|
|
602
621
|
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})))
|
|
603
622
|
),
|