spawn-term 3.1.5 → 3.2.1
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/dist/cjs/components/App.js +136 -6
- package/dist/cjs/components/App.js.map +1 -1
- package/dist/cjs/components/ExpandedOutput.js +5 -2
- package/dist/cjs/components/ExpandedOutput.js.map +1 -1
- package/dist/cjs/components/FullscreenOverlay.js +88 -0
- package/dist/cjs/components/FullscreenOverlay.js.map +1 -0
- package/dist/cjs/src/components/FullscreenOverlay.d.ts +9 -0
- package/dist/cjs/src/state/processStore.d.ts +19 -0
- package/dist/cjs/state/processStore.js +119 -2
- package/dist/cjs/state/processStore.js.map +1 -1
- package/dist/esm/components/App.js +134 -6
- package/dist/esm/components/App.js.map +1 -1
- package/dist/esm/components/ExpandedOutput.js +5 -2
- package/dist/esm/components/ExpandedOutput.js.map +1 -1
- package/dist/esm/components/FullscreenOverlay.js +74 -0
- package/dist/esm/components/FullscreenOverlay.js.map +1 -0
- package/dist/esm/src/components/FullscreenOverlay.d.ts +9 -0
- package/dist/esm/src/state/processStore.d.ts +19 -0
- package/dist/esm/state/processStore.js +103 -2
- package/dist/esm/state/processStore.js.map +1 -1
- package/package.json +1 -1
|
@@ -27,15 +27,18 @@ export default /*#__PURE__*/ memo(function ExpandedOutput({ lines, scrollOffset,
|
|
|
27
27
|
line.text
|
|
28
28
|
]
|
|
29
29
|
}, scrollOffset + i)),
|
|
30
|
-
hasMore
|
|
30
|
+
hasMore ? /*#__PURE__*/ _jsxs(Text, {
|
|
31
31
|
dimColor: true,
|
|
32
32
|
children: [
|
|
33
33
|
"│ [+",
|
|
34
34
|
remaining,
|
|
35
35
|
" more, Tab/⇧Tab page, ",
|
|
36
36
|
isMac ? '⌥↑/↓' : 'g/G',
|
|
37
|
-
" top/bottom]"
|
|
37
|
+
" top/bottom, f fullscreen]"
|
|
38
38
|
]
|
|
39
|
+
}) : /*#__PURE__*/ _jsx(Text, {
|
|
40
|
+
dimColor: true,
|
|
41
|
+
children: "│ [f fullscreen]"
|
|
39
42
|
})
|
|
40
43
|
]
|
|
41
44
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ExpandedOutput.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { Line } from '../types.ts';\n\nconst isMac = process.platform === 'darwin';\n\ntype Props = {\n lines: Line[];\n scrollOffset: number;\n maxVisible?: number;\n};\n\nexport default memo(function ExpandedOutput({ lines, scrollOffset, maxVisible = EXPANDED_MAX_VISIBLE_LINES }: Props) {\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const hasMore = lines.length > scrollOffset + maxVisible;\n const remaining = lines.length - scrollOffset - maxVisible;\n\n if (lines.length === 0) {\n return (\n <Box paddingLeft={2}>\n <Text dimColor>│ (no output)</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n {visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>│ {line.text}</Text>\n ))}\n {hasMore
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ExpandedOutput.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { Line } from '../types.ts';\n\nconst isMac = process.platform === 'darwin';\n\ntype Props = {\n lines: Line[];\n scrollOffset: number;\n maxVisible?: number;\n};\n\nexport default memo(function ExpandedOutput({ lines, scrollOffset, maxVisible = EXPANDED_MAX_VISIBLE_LINES }: Props) {\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const hasMore = lines.length > scrollOffset + maxVisible;\n const remaining = lines.length - scrollOffset - maxVisible;\n\n if (lines.length === 0) {\n return (\n <Box paddingLeft={2}>\n <Text dimColor>│ (no output)</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n {visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>│ {line.text}</Text>\n ))}\n {hasMore ? (\n <Text dimColor>\n │ [+{remaining} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom, f fullscreen]\n </Text>\n ) : (\n <Text dimColor>│ [f fullscreen]</Text>\n )}\n </Box>\n );\n});\n"],"names":["Box","Text","memo","EXPANDED_MAX_VISIBLE_LINES","isMac","process","platform","ExpandedOutput","lines","scrollOffset","maxVisible","visibleLines","slice","hasMore","length","remaining","paddingLeft","dimColor","flexDirection","map","line","i","text"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,QAAQ,MAAM;AAChC,SAASC,IAAI,QAAQ,QAAQ;AAC7B,SAASC,0BAA0B,QAAQ,kBAAkB;AAG7D,MAAMC,QAAQC,QAAQC,QAAQ,KAAK;AAQnC,6BAAeJ,KAAK,SAASK,eAAe,EAAEC,KAAK,EAAEC,YAAY,EAAEC,aAAaP,0BAA0B,EAAS;IACjH,MAAMQ,eAAeH,MAAMI,KAAK,CAACH,cAAcA,eAAeC;IAC9D,MAAMG,UAAUL,MAAMM,MAAM,GAAGL,eAAeC;IAC9C,MAAMK,YAAYP,MAAMM,MAAM,GAAGL,eAAeC;IAEhD,IAAIF,MAAMM,MAAM,KAAK,GAAG;QACtB,qBACE,KAACd;YAAIgB,aAAa;sBAChB,cAAA,KAACf;gBAAKgB,QAAQ;0BAAC;;;IAGrB;IAEA,qBACE,MAACjB;QAAIkB,eAAc;QAASF,aAAa;;YACtCL,aAAaQ,GAAG,CAAC,CAACC,MAAMC,IACvB,iHAAiH;8BACjH,MAACpB;;wBAA4B;wBAAGmB,KAAKE,IAAI;;mBAA9Bb,eAAeY;YAE3BR,wBACC,MAACZ;gBAAKgB,QAAQ;;oBAAC;oBACRF;oBAAU;oBAAuBX,QAAQ,SAAS;oBAAM;;+BAG/D,KAACH;gBAAKgB,QAAQ;0BAAC;;;;AAIvB,GAAG"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useStdout } from 'ink';
|
|
3
|
+
import { memo, useEffect } from 'react';
|
|
4
|
+
const isMac = process.platform === 'darwin';
|
|
5
|
+
// ANSI escape codes for alternate screen buffer
|
|
6
|
+
const ENTER_ALT_SCREEN = '\x1b[?1049h';
|
|
7
|
+
const EXIT_ALT_SCREEN = '\x1b[?1049l';
|
|
8
|
+
const CLEAR_SCREEN = '\x1b[2J';
|
|
9
|
+
const CURSOR_HOME = '\x1b[H';
|
|
10
|
+
export default /*#__PURE__*/ memo(function FullscreenOverlay({ title, lines, scrollOffset }) {
|
|
11
|
+
const { stdout } = useStdout();
|
|
12
|
+
const terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
|
|
13
|
+
// Reserve lines for header (title + divider) and footer (scroll hint)
|
|
14
|
+
const headerLines = 2;
|
|
15
|
+
const footerLines = 1;
|
|
16
|
+
const maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);
|
|
17
|
+
// Enter alternate screen on mount, exit on unmount
|
|
18
|
+
useEffect(()=>{
|
|
19
|
+
if (stdout) {
|
|
20
|
+
stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME);
|
|
21
|
+
}
|
|
22
|
+
return ()=>{
|
|
23
|
+
if (stdout) {
|
|
24
|
+
stdout.write(EXIT_ALT_SCREEN);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}, [
|
|
28
|
+
stdout
|
|
29
|
+
]);
|
|
30
|
+
const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);
|
|
31
|
+
const totalLines = lines.length;
|
|
32
|
+
const currentLine = scrollOffset + 1;
|
|
33
|
+
const endLine = Math.min(scrollOffset + maxVisible, totalLines);
|
|
34
|
+
return /*#__PURE__*/ _jsxs(Box, {
|
|
35
|
+
flexDirection: "column",
|
|
36
|
+
height: terminalHeight,
|
|
37
|
+
children: [
|
|
38
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
39
|
+
bold: true,
|
|
40
|
+
color: "cyan",
|
|
41
|
+
children: title
|
|
42
|
+
}),
|
|
43
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
44
|
+
dimColor: true,
|
|
45
|
+
children: '─'.repeat(Math.min(80, (stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80))
|
|
46
|
+
}),
|
|
47
|
+
/*#__PURE__*/ _jsx(Box, {
|
|
48
|
+
flexDirection: "column",
|
|
49
|
+
flexGrow: 1,
|
|
50
|
+
children: lines.length === 0 ? /*#__PURE__*/ _jsx(Text, {
|
|
51
|
+
dimColor: true,
|
|
52
|
+
children: "(no output)"
|
|
53
|
+
}) : visibleLines.map((line, i)=>// biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view
|
|
54
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
55
|
+
children: line.text
|
|
56
|
+
}, scrollOffset + i))
|
|
57
|
+
}),
|
|
58
|
+
/*#__PURE__*/ _jsxs(Text, {
|
|
59
|
+
dimColor: true,
|
|
60
|
+
children: [
|
|
61
|
+
"Lines ",
|
|
62
|
+
currentLine,
|
|
63
|
+
"-",
|
|
64
|
+
endLine,
|
|
65
|
+
" of ",
|
|
66
|
+
totalLines,
|
|
67
|
+
" | j/k scroll | Tab/⇧Tab page | ",
|
|
68
|
+
isMac ? '⌥↑/↓' : 'g/G',
|
|
69
|
+
" top/bottom | q exit"
|
|
70
|
+
]
|
|
71
|
+
})
|
|
72
|
+
]
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/FullscreenOverlay.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo, useEffect } from 'react';\nimport type { Line } from '../types.ts';\n\nconst isMac = process.platform === 'darwin';\n\n// ANSI escape codes for alternate screen buffer\nconst ENTER_ALT_SCREEN = '\\x1b[?1049h';\nconst EXIT_ALT_SCREEN = '\\x1b[?1049l';\nconst CLEAR_SCREEN = '\\x1b[2J';\nconst CURSOR_HOME = '\\x1b[H';\n\ntype Props = {\n title: string;\n lines: Line[];\n scrollOffset: number;\n onExit: () => void;\n};\n\nexport default memo(function FullscreenOverlay({ title, lines, scrollOffset }: Props) {\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Reserve lines for header (title + divider) and footer (scroll hint)\n const headerLines = 2;\n const footerLines = 1;\n const maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);\n\n // Enter alternate screen on mount, exit on unmount\n useEffect(() => {\n if (stdout) {\n stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME);\n }\n return () => {\n if (stdout) {\n stdout.write(EXIT_ALT_SCREEN);\n }\n };\n }, [stdout]);\n\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const totalLines = lines.length;\n const currentLine = scrollOffset + 1;\n const endLine = Math.min(scrollOffset + maxVisible, totalLines);\n\n return (\n <Box flexDirection=\"column\" height={terminalHeight}>\n {/* Header */}\n <Text bold color=\"cyan\">\n {title}\n </Text>\n <Text dimColor>{'─'.repeat(Math.min(80, stdout?.columns || 80))}</Text>\n\n {/* Content */}\n <Box flexDirection=\"column\" flexGrow={1}>\n {lines.length === 0 ? (\n <Text dimColor>(no output)</Text>\n ) : (\n visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>{line.text}</Text>\n ))\n )}\n </Box>\n\n {/* Footer */}\n <Text dimColor>\n Lines {currentLine}-{endLine} of {totalLines} | j/k scroll | Tab/⇧Tab page | {isMac ? '⌥↑/↓' : 'g/G'} top/bottom | q exit\n </Text>\n </Box>\n );\n});\n"],"names":["Box","Text","useStdout","memo","useEffect","isMac","process","platform","ENTER_ALT_SCREEN","EXIT_ALT_SCREEN","CLEAR_SCREEN","CURSOR_HOME","FullscreenOverlay","title","lines","scrollOffset","stdout","terminalHeight","rows","headerLines","footerLines","maxVisible","Math","max","write","visibleLines","slice","totalLines","length","currentLine","endLine","min","flexDirection","height","bold","color","dimColor","repeat","columns","flexGrow","map","line","i","text"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,EAAEC,SAAS,QAAQ,MAAM;AAC3C,SAASC,IAAI,EAAEC,SAAS,QAAQ,QAAQ;AAGxC,MAAMC,QAAQC,QAAQC,QAAQ,KAAK;AAEnC,gDAAgD;AAChD,MAAMC,mBAAmB;AACzB,MAAMC,kBAAkB;AACxB,MAAMC,eAAe;AACrB,MAAMC,cAAc;AASpB,6BAAeR,KAAK,SAASS,kBAAkB,EAAEC,KAAK,EAAEC,KAAK,EAAEC,YAAY,EAAS;IAClF,MAAM,EAAEC,MAAM,EAAE,GAAGd;IACnB,MAAMe,iBAAiBD,CAAAA,mBAAAA,6BAAAA,OAAQE,IAAI,KAAI;IAEvC,sEAAsE;IACtE,MAAMC,cAAc;IACpB,MAAMC,cAAc;IACpB,MAAMC,aAAaC,KAAKC,GAAG,CAAC,GAAGN,iBAAiBE,cAAcC;IAE9D,mDAAmD;IACnDhB,UAAU;QACR,IAAIY,QAAQ;YACVA,OAAOQ,KAAK,CAAChB,mBAAmBE,eAAeC;QACjD;QACA,OAAO;YACL,IAAIK,QAAQ;gBACVA,OAAOQ,KAAK,CAACf;YACf;QACF;IACF,GAAG;QAACO;KAAO;IAEX,MAAMS,eAAeX,MAAMY,KAAK,CAACX,cAAcA,eAAeM;IAC9D,MAAMM,aAAab,MAAMc,MAAM;IAC/B,MAAMC,cAAcd,eAAe;IACnC,MAAMe,UAAUR,KAAKS,GAAG,CAAChB,eAAeM,YAAYM;IAEpD,qBACE,MAAC3B;QAAIgC,eAAc;QAASC,QAAQhB;;0BAElC,KAAChB;gBAAKiC,IAAI;gBAACC,OAAM;0BACdtB;;0BAEH,KAACZ;gBAAKmC,QAAQ;0BAAE,IAAIC,MAAM,CAACf,KAAKS,GAAG,CAAC,IAAIf,CAAAA,mBAAAA,6BAAAA,OAAQsB,OAAO,KAAI;;0BAG3D,KAACtC;gBAAIgC,eAAc;gBAASO,UAAU;0BACnCzB,MAAMc,MAAM,KAAK,kBAChB,KAAC3B;oBAAKmC,QAAQ;8BAAC;qBAEfX,aAAae,GAAG,CAAC,CAACC,MAAMC,IACtB,iHAAiH;kCACjH,KAACzC;kCAA6BwC,KAAKE,IAAI;uBAA5B5B,eAAe2B;;0BAMhC,MAACzC;gBAAKmC,QAAQ;;oBAAC;oBACNP;oBAAY;oBAAEC;oBAAQ;oBAAKH;oBAAW;oBAAiCtB,QAAQ,SAAS;oBAAM;;;;;AAI7G,GAAG"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ChildProcess, Line, SessionOptions } from '../types.js';
|
|
2
2
|
type Listener = () => void;
|
|
3
3
|
type Mode = 'normal' | 'interactive';
|
|
4
|
+
type FilterMode = 'all' | 'running' | 'finished' | 'failed';
|
|
4
5
|
export declare class ProcessStore {
|
|
5
6
|
private processes;
|
|
6
7
|
private completedIds;
|
|
@@ -8,6 +9,10 @@ export declare class ProcessStore {
|
|
|
8
9
|
private mode;
|
|
9
10
|
private expandedId;
|
|
10
11
|
private errorFooterExpanded;
|
|
12
|
+
private filterMode;
|
|
13
|
+
private searchTerm;
|
|
14
|
+
private isSearching;
|
|
15
|
+
private isFullscreen;
|
|
11
16
|
private header;
|
|
12
17
|
private showStatusBar;
|
|
13
18
|
private isInteractive;
|
|
@@ -42,6 +47,11 @@ export declare class ProcessStore {
|
|
|
42
47
|
getListScrollOffset: () => number;
|
|
43
48
|
getErrorFooterExpanded: () => boolean;
|
|
44
49
|
getBufferVersion: () => number;
|
|
50
|
+
getFilterMode: () => FilterMode;
|
|
51
|
+
getSearchTerm: () => string;
|
|
52
|
+
getIsSearching: () => boolean;
|
|
53
|
+
getIsFullscreen: () => boolean;
|
|
54
|
+
getFilteredProcesses: () => ChildProcess[];
|
|
45
55
|
getScrollOffset: () => number;
|
|
46
56
|
getHeader: () => string | undefined;
|
|
47
57
|
getShowStatusBar: () => boolean;
|
|
@@ -49,6 +59,15 @@ export declare class ProcessStore {
|
|
|
49
59
|
isAllComplete: () => boolean;
|
|
50
60
|
setMode(mode: Mode): void;
|
|
51
61
|
getSelectedProcess(): ChildProcess | undefined;
|
|
62
|
+
cycleFilterNext(): void;
|
|
63
|
+
cycleFilterPrev(): void;
|
|
64
|
+
startSearch(): void;
|
|
65
|
+
updateSearchTerm(term: string): void;
|
|
66
|
+
cancelSearch(): void;
|
|
67
|
+
confirmSearch(): void;
|
|
68
|
+
clearSearch(): void;
|
|
69
|
+
enterFullscreen(): void;
|
|
70
|
+
exitFullscreen(): void;
|
|
52
71
|
toggleErrorFooter(): void;
|
|
53
72
|
expandErrorFooter(): void;
|
|
54
73
|
selectNext(visibleCount?: number): void;
|
|
@@ -2,6 +2,12 @@ import { arrayFind } from '../compat.js';
|
|
|
2
2
|
import { DEFAULT_COLUMN_WIDTH } from '../constants.js';
|
|
3
3
|
import { LineType } from '../types.js';
|
|
4
4
|
import { createNavigator } from './Navigator.js';
|
|
5
|
+
const FILTER_CYCLE = [
|
|
6
|
+
'all',
|
|
7
|
+
'running',
|
|
8
|
+
'finished',
|
|
9
|
+
'failed'
|
|
10
|
+
];
|
|
5
11
|
export class ProcessStore {
|
|
6
12
|
getErrorLines() {
|
|
7
13
|
return this.getFailedProcesses().map((p)=>({
|
|
@@ -88,7 +94,67 @@ export class ProcessStore {
|
|
|
88
94
|
this.notify();
|
|
89
95
|
}
|
|
90
96
|
getSelectedProcess() {
|
|
91
|
-
return this.
|
|
97
|
+
return this.getFilteredProcesses()[this.listNav.position];
|
|
98
|
+
}
|
|
99
|
+
// Filter mode cycling (left/right arrows)
|
|
100
|
+
cycleFilterNext() {
|
|
101
|
+
const currentIndex = FILTER_CYCLE.indexOf(this.filterMode);
|
|
102
|
+
this.filterMode = FILTER_CYCLE[(currentIndex + 1) % FILTER_CYCLE.length];
|
|
103
|
+
// Reset selection when filter changes
|
|
104
|
+
this.listNav.toStart();
|
|
105
|
+
// Collapse any expanded process when filter changes
|
|
106
|
+
this.expandedId = null;
|
|
107
|
+
this.notify();
|
|
108
|
+
}
|
|
109
|
+
cycleFilterPrev() {
|
|
110
|
+
const currentIndex = FILTER_CYCLE.indexOf(this.filterMode);
|
|
111
|
+
this.filterMode = FILTER_CYCLE[(currentIndex - 1 + FILTER_CYCLE.length) % FILTER_CYCLE.length];
|
|
112
|
+
// Reset selection when filter changes
|
|
113
|
+
this.listNav.toStart();
|
|
114
|
+
// Collapse any expanded process when filter changes
|
|
115
|
+
this.expandedId = null;
|
|
116
|
+
this.notify();
|
|
117
|
+
}
|
|
118
|
+
// Search mode
|
|
119
|
+
startSearch() {
|
|
120
|
+
this.isSearching = true;
|
|
121
|
+
this.searchTerm = '';
|
|
122
|
+
this.notify();
|
|
123
|
+
}
|
|
124
|
+
updateSearchTerm(term) {
|
|
125
|
+
this.searchTerm = term;
|
|
126
|
+
// Reset selection when search changes
|
|
127
|
+
this.listNav.toStart();
|
|
128
|
+
this.notify();
|
|
129
|
+
}
|
|
130
|
+
cancelSearch() {
|
|
131
|
+
this.isSearching = false;
|
|
132
|
+
this.searchTerm = '';
|
|
133
|
+
// Reset selection
|
|
134
|
+
this.listNav.toStart();
|
|
135
|
+
this.notify();
|
|
136
|
+
}
|
|
137
|
+
confirmSearch() {
|
|
138
|
+
this.isSearching = false;
|
|
139
|
+
// Keep searchTerm applied, reset selection to first match
|
|
140
|
+
this.listNav.toStart();
|
|
141
|
+
this.notify();
|
|
142
|
+
}
|
|
143
|
+
clearSearch() {
|
|
144
|
+
this.searchTerm = '';
|
|
145
|
+
this.listNav.toStart();
|
|
146
|
+
this.notify();
|
|
147
|
+
}
|
|
148
|
+
// Fullscreen mode (alternate screen buffer)
|
|
149
|
+
enterFullscreen() {
|
|
150
|
+
if (this.expandedId) {
|
|
151
|
+
this.isFullscreen = true;
|
|
152
|
+
this.notify();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
exitFullscreen() {
|
|
156
|
+
this.isFullscreen = false;
|
|
157
|
+
this.notify();
|
|
92
158
|
}
|
|
93
159
|
// Error footer methods (for non-interactive mode)
|
|
94
160
|
toggleErrorFooter() {
|
|
@@ -247,6 +313,10 @@ export class ProcessStore {
|
|
|
247
313
|
this.listNav.reset();
|
|
248
314
|
this.expandedId = null;
|
|
249
315
|
this.errorFooterExpanded = false;
|
|
316
|
+
this.filterMode = 'all';
|
|
317
|
+
this.searchTerm = '';
|
|
318
|
+
this.isSearching = false;
|
|
319
|
+
this.isFullscreen = false;
|
|
250
320
|
this.header = undefined;
|
|
251
321
|
}
|
|
252
322
|
// === INFRASTRUCTURE ===
|
|
@@ -264,6 +334,10 @@ export class ProcessStore {
|
|
|
264
334
|
this.mode = 'normal';
|
|
265
335
|
this.expandedId = null;
|
|
266
336
|
this.errorFooterExpanded = false; // For non-interactive error footer
|
|
337
|
+
this.filterMode = 'all';
|
|
338
|
+
this.searchTerm = '';
|
|
339
|
+
this.isSearching = false;
|
|
340
|
+
this.isFullscreen = false;
|
|
267
341
|
this.showStatusBar = false;
|
|
268
342
|
this.isInteractive = false;
|
|
269
343
|
// === INFRASTRUCTURE ===
|
|
@@ -306,6 +380,32 @@ export class ProcessStore {
|
|
|
306
380
|
this.getListScrollOffset = ()=>this.listNav.viewportOffset;
|
|
307
381
|
this.getErrorFooterExpanded = ()=>this.errorFooterExpanded;
|
|
308
382
|
this.getBufferVersion = ()=>this.bufferVersion;
|
|
383
|
+
this.getFilterMode = ()=>this.filterMode;
|
|
384
|
+
this.getSearchTerm = ()=>this.searchTerm;
|
|
385
|
+
this.getIsSearching = ()=>this.isSearching;
|
|
386
|
+
this.getIsFullscreen = ()=>this.isFullscreen;
|
|
387
|
+
// Get processes filtered by current filter mode and search term
|
|
388
|
+
this.getFilteredProcesses = ()=>{
|
|
389
|
+
let filtered = this.processes;
|
|
390
|
+
// Apply filter mode
|
|
391
|
+
switch(this.filterMode){
|
|
392
|
+
case 'running':
|
|
393
|
+
filtered = filtered.filter((p)=>p.state === 'running');
|
|
394
|
+
break;
|
|
395
|
+
case 'finished':
|
|
396
|
+
filtered = filtered.filter((p)=>p.state !== 'running');
|
|
397
|
+
break;
|
|
398
|
+
case 'failed':
|
|
399
|
+
filtered = filtered.filter((p)=>p.state === 'error');
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
// Apply search term
|
|
403
|
+
if (this.searchTerm) {
|
|
404
|
+
const term = this.searchTerm.toLowerCase();
|
|
405
|
+
filtered = filtered.filter((p)=>p.title.toLowerCase().includes(term) || p.group && p.group.toLowerCase().includes(term));
|
|
406
|
+
}
|
|
407
|
+
return filtered;
|
|
408
|
+
};
|
|
309
409
|
// Get scroll offset for expanded process (or 0 if none)
|
|
310
410
|
this.getScrollOffset = ()=>{
|
|
311
411
|
var _process_scrollNav;
|
|
@@ -327,8 +427,9 @@ export class ProcessStore {
|
|
|
327
427
|
var _options_interactive;
|
|
328
428
|
this.isInteractive = (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false;
|
|
329
429
|
// Create list navigator with wrap-around behavior
|
|
430
|
+
// Uses filtered processes count so selection works correctly with filters
|
|
330
431
|
this.listNav = createNavigator({
|
|
331
|
-
getLength: ()=>this.
|
|
432
|
+
getLength: ()=>this.getFilteredProcesses().length,
|
|
332
433
|
wrap: true,
|
|
333
434
|
onMove: ()=>this.notify()
|
|
334
435
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/state/processStore.ts"],"sourcesContent":["import { arrayFind } from '../compat.ts';\nimport { DEFAULT_COLUMN_WIDTH } from '../constants.ts';\nimport type { ChildProcess, Line, SessionOptions } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport { createNavigator, type Navigator } from './Navigator.ts';\n\ntype Listener = () => void;\ntype Mode = 'normal' | 'interactive';\n\nexport class ProcessStore {\n // === DATA: Process collection ===\n private processes: ChildProcess[] = [];\n private completedIds: string[] = []; // Track completion order\n\n // === NAVIGATION: List cursor ===\n private listNav: Navigator;\n\n // === VIEW STATE ===\n private mode: Mode = 'normal';\n private expandedId: string | null = null;\n private errorFooterExpanded = false; // For non-interactive error footer\n\n // === SESSION CONFIG (immutable after construction) ===\n private header: string | undefined;\n private showStatusBar = false;\n private isInteractive = false;\n\n // === INFRASTRUCTURE ===\n private listeners = new Set<Listener>();\n private shouldExit = false;\n private exitCallback: (() => void) | null = null;\n private bufferVersion = 0; // Increments on every notify() to trigger re-renders\n\n constructor(options: SessionOptions = {}) {\n this.header = options.header;\n this.showStatusBar = options.showStatusBar ?? false;\n this.isInteractive = options.interactive ?? false;\n\n // Create list navigator with wrap-around behavior\n this.listNav = createNavigator({\n getLength: () => this.processes.length,\n wrap: true,\n onMove: () => this.notify(),\n });\n }\n\n // === SUBSCRIPTION API (useSyncExternalStore) ===\n\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n getSnapshot = (): ChildProcess[] => this.processes;\n\n // === DATA: Queries ===\n\n getRunningProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'running');\n };\n\n getCompletedProcesses = (): ChildProcess[] => {\n // Return in completion order\n return this.completedIds.map((id) => arrayFind(this.processes, (p) => p.id === id)).filter((p): p is ChildProcess => p !== undefined);\n };\n\n getFailedProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'error');\n };\n\n // Counts\n getRunningCount = (): number => this.processes.filter((p) => p.state === 'running').length;\n getMaxGroupLength = (): number => {\n if (this.processes.length === 0) return DEFAULT_COLUMN_WIDTH;\n return Math.max(...this.processes.map((p) => (p.group || p.title).length));\n };\n getDoneCount = (): number => this.processes.filter((p) => p.state !== 'running').length;\n getErrorCount = (): number => this.processes.filter((p) => p.state === 'error').length;\n getErrorLineCount = (): number => {\n return this.processes.filter((p) => p.state === 'error').reduce((total, p) => total + this.getProcessLineCount(p.id), 0);\n };\n\n getErrorLines(): Array<{ processName: string; lines: Line[] }> {\n return this.getFailedProcesses().map((p) => ({\n processName: p.group || p.title,\n lines: this.getProcessLines(p.id),\n }));\n }\n\n // === DATA: Mutations ===\n\n addProcess(process: ChildProcess): void {\n // Create scroll navigator for this process\n const processWithNav: ChildProcess = {\n ...process,\n scrollNav: createNavigator({\n getLength: () => this.getProcessLineCount(processWithNav.id),\n wrap: false,\n onMove: () => this.notify(),\n }),\n };\n this.processes = [...this.processes, processWithNav];\n this.notify();\n }\n\n updateProcess(id: string, update: Partial<ChildProcess>): void {\n const oldProcess = arrayFind(this.processes, (p) => p.id === id);\n const wasRunning = oldProcess?.state === 'running';\n const isNowComplete = update.state && update.state !== 'running';\n\n this.processes = this.processes.map((p) => (p.id === id ? { ...p, ...update } : p));\n\n // Track completion order\n if (wasRunning && isNowComplete && !this.completedIds.includes(id)) {\n this.completedIds = [...this.completedIds, id];\n }\n\n // Auto-expand error footer when all complete with errors (non-interactive only)\n if (!this.isInteractive && this.isAllComplete() && this.getErrorCount() > 0) {\n this.errorFooterExpanded = true;\n }\n\n this.notify();\n }\n\n appendLines(id: string, newLines: Line[]): void {\n const process = arrayFind(this.processes, (p) => p.id === id);\n if (process) {\n this.updateProcess(id, { lines: process.lines.concat(newLines) });\n }\n }\n\n getProcess(id: string): ChildProcess | undefined {\n return arrayFind(this.processes, (p) => p.id === id);\n }\n\n // Get rendered lines from terminal buffer or fallback to lines array\n getProcessLines(id: string): Line[] {\n const process = this.getProcess(id);\n if (!process) return [];\n if (process.terminalBuffer) {\n return process.terminalBuffer.getLines().map((text) => ({\n type: LineType.stdout,\n text,\n }));\n }\n return process.lines;\n }\n\n // Get line count from terminal buffer or lines array\n getProcessLineCount(id: string): number {\n const process = this.getProcess(id);\n if (!process) return 0;\n if (process.terminalBuffer) {\n return process.terminalBuffer.lineCount;\n }\n return process.lines.length;\n }\n\n // === VIEW STATE: Getters ===\n\n getMode = (): Mode => this.mode;\n getSelectedIndex = (): number => this.listNav.position;\n getExpandedId = (): string | null => this.expandedId;\n getListScrollOffset = (): number => this.listNav.viewportOffset;\n getErrorFooterExpanded = (): boolean => this.errorFooterExpanded;\n getBufferVersion = (): number => this.bufferVersion;\n\n // Get scroll offset for expanded process (or 0 if none)\n getScrollOffset = (): number => {\n if (!this.expandedId) return 0;\n const process = this.getProcess(this.expandedId);\n return process?.scrollNav?.position ?? 0;\n };\n\n // Session-level getters (set at session creation, immutable)\n getHeader = (): string | undefined => this.header;\n getShowStatusBar = (): boolean => this.showStatusBar;\n getIsInteractive = (): boolean => this.isInteractive;\n isAllComplete = (): boolean => this.processes.length > 0 && this.processes.every((p) => p.state !== 'running');\n\n // === VIEW STATE: Mutations ===\n\n setMode(mode: Mode): void {\n this.mode = mode;\n if (mode === 'interactive') {\n this.listNav.setPosition(0);\n }\n this.notify();\n }\n\n getSelectedProcess(): ChildProcess | undefined {\n return this.processes[this.listNav.position];\n }\n\n // Error footer methods (for non-interactive mode)\n toggleErrorFooter(): void {\n this.errorFooterExpanded = !this.errorFooterExpanded;\n this.notify();\n }\n\n expandErrorFooter(): void {\n if (!this.errorFooterExpanded) {\n this.errorFooterExpanded = true;\n this.notify();\n }\n }\n\n // === NAVIGATION: List (delegates to listNav) ===\n\n selectNext(visibleCount?: number): void {\n this.listNav.down();\n if (visibleCount) {\n this.listNav.ensureVisible(visibleCount);\n }\n }\n\n selectPrev(visibleCount?: number): void {\n this.listNav.up();\n if (visibleCount) {\n this.listNav.ensureVisible(visibleCount);\n }\n }\n\n selectPageDown(pageSize: number, visibleCount?: number): void {\n this.listNav.pageDown(pageSize, visibleCount);\n }\n\n selectPageUp(pageSize: number, visibleCount?: number): void {\n this.listNav.pageUp(pageSize, visibleCount);\n }\n\n selectFirst(visibleCount?: number): void {\n this.listNav.toStart();\n if (visibleCount) {\n this.listNav.ensureVisible(visibleCount);\n }\n }\n\n selectLast(visibleCount?: number): void {\n this.listNav.toEnd();\n if (visibleCount) {\n this.listNav.ensureVisible(visibleCount);\n }\n }\n\n clampListViewport(visibleCount: number): void {\n const changed = this.listNav.clampViewport(visibleCount);\n if (changed) {\n this.notify();\n }\n }\n\n // === NAVIGATION: Expanded content (delegates to process.scrollNav) ===\n\n private getExpandedNav(): { nav: Navigator; id: string } | undefined {\n if (!this.expandedId) return undefined;\n const nav = this.getProcess(this.expandedId)?.scrollNav;\n if (!nav) return undefined;\n return { nav, id: this.expandedId };\n }\n\n scrollDown(maxVisible: number): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n const lineCount = this.getProcessLineCount(expanded.id);\n const maxOffset = Math.max(0, lineCount - maxVisible);\n\n // Only scroll if not at bottom\n if (expanded.nav.position < maxOffset) {\n expanded.nav.down();\n }\n }\n\n scrollUp(): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n if (expanded.nav.position > 0) {\n expanded.nav.up();\n }\n }\n\n scrollPageDown(pageSize: number): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n const lineCount = this.getProcessLineCount(expanded.id);\n const maxOffset = Math.max(0, lineCount - pageSize);\n\n // Clamp to max offset\n const newPosition = Math.min(expanded.nav.position + pageSize, maxOffset);\n expanded.nav.setPosition(newPosition);\n this.notify();\n }\n\n scrollPageUp(pageSize: number): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n const newPosition = Math.max(0, expanded.nav.position - pageSize);\n expanded.nav.setPosition(newPosition);\n this.notify();\n }\n\n scrollToTop(): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n expanded.nav.toStart();\n }\n\n scrollToBottom(maxVisible: number): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n const lineCount = this.getProcessLineCount(expanded.id);\n const newPosition = Math.max(0, lineCount - maxVisible);\n expanded.nav.setPosition(newPosition);\n this.notify();\n }\n\n // === EXPANSION ===\n\n toggleExpand(visibleCountWhenExpanded?: number, visibleCountWhenCollapsed?: number): void {\n const selected = this.getSelectedProcess();\n if (!selected) return;\n\n if (this.expandedId === selected.id) {\n // Collapse (keep scroll position for later)\n this.expandedId = null;\n // Adjust viewport to avoid empty space at bottom\n if (visibleCountWhenCollapsed) {\n this.listNav.clampViewport(visibleCountWhenCollapsed);\n }\n } else {\n // Expand (scroll position is preserved in process.scrollNav)\n this.expandedId = selected.id;\n // Adjust list scroll to keep expanded process visible\n if (visibleCountWhenExpanded) {\n this.listNav.ensureVisible(visibleCountWhenExpanded);\n }\n }\n this.notify();\n }\n\n collapse(visibleCountWhenCollapsed?: number): void {\n // Collapse but keep scroll position in process\n this.expandedId = null;\n // Adjust viewport to avoid empty space at bottom\n if (visibleCountWhenCollapsed) {\n this.listNav.clampViewport(visibleCountWhenCollapsed);\n }\n this.notify();\n }\n\n // === EXIT ===\n\n signalExit(callback: () => void): void {\n this.shouldExit = true;\n this.exitCallback = callback;\n this.notify();\n }\n\n getShouldExit = (): boolean => this.shouldExit;\n getExitCallback = (): (() => void) | null => this.exitCallback;\n\n // === RESET ===\n\n reset(): void {\n // Dispose terminal buffers before clearing\n for (const process of this.processes) {\n process.terminalBuffer?.dispose();\n }\n this.processes = [];\n this.completedIds = [];\n this.shouldExit = false;\n this.exitCallback = null;\n this.mode = 'normal';\n this.listNav.reset();\n this.expandedId = null;\n this.errorFooterExpanded = false;\n this.header = undefined;\n }\n\n // === INFRASTRUCTURE ===\n\n notify(): void {\n this.bufferVersion++;\n this.listeners.forEach((l) => {\n l();\n });\n }\n}\n\n// Note: No global singleton - session creates its own store instance\n"],"names":["arrayFind","DEFAULT_COLUMN_WIDTH","LineType","createNavigator","ProcessStore","getErrorLines","getFailedProcesses","map","p","processName","group","title","lines","getProcessLines","id","addProcess","process","processWithNav","scrollNav","getLength","getProcessLineCount","wrap","onMove","notify","processes","updateProcess","update","oldProcess","wasRunning","state","isNowComplete","completedIds","includes","isInteractive","isAllComplete","getErrorCount","errorFooterExpanded","appendLines","newLines","concat","getProcess","terminalBuffer","getLines","text","type","stdout","lineCount","length","setMode","mode","listNav","setPosition","getSelectedProcess","position","toggleErrorFooter","expandErrorFooter","selectNext","visibleCount","down","ensureVisible","selectPrev","up","selectPageDown","pageSize","pageDown","selectPageUp","pageUp","selectFirst","toStart","selectLast","toEnd","clampListViewport","changed","clampViewport","getExpandedNav","expandedId","undefined","nav","scrollDown","maxVisible","expanded","maxOffset","Math","max","scrollUp","scrollPageDown","newPosition","min","scrollPageUp","scrollToTop","scrollToBottom","toggleExpand","visibleCountWhenExpanded","visibleCountWhenCollapsed","selected","collapse","signalExit","callback","shouldExit","exitCallback","reset","dispose","header","bufferVersion","listeners","forEach","l","options","showStatusBar","Set","subscribe","listener","add","delete","getSnapshot","getRunningProcesses","filter","getCompletedProcesses","getRunningCount","getMaxGroupLength","getDoneCount","getErrorLineCount","reduce","total","getMode","getSelectedIndex","getExpandedId","getListScrollOffset","viewportOffset","getErrorFooterExpanded","getBufferVersion","getScrollOffset","getHeader","getShowStatusBar","getIsInteractive","every","getShouldExit","getExitCallback","interactive"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/state/processStore.ts"],"sourcesContent":["import { arrayFind } from '../compat.ts';\nimport { DEFAULT_COLUMN_WIDTH } from '../constants.ts';\nimport type { ChildProcess, Line, SessionOptions } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport { createNavigator, type Navigator } from './Navigator.ts';\n\ntype Listener = () => void;\ntype Mode = 'normal' | 'interactive';\ntype FilterMode = 'all' | 'running' | 'finished' | 'failed';\n\nconst FILTER_CYCLE: FilterMode[] = ['all', 'running', 'finished', 'failed'];\n\nexport class ProcessStore {\n // === DATA: Process collection ===\n private processes: ChildProcess[] = [];\n private completedIds: string[] = []; // Track completion order\n\n // === NAVIGATION: List cursor ===\n private listNav: Navigator;\n\n // === VIEW STATE ===\n private mode: Mode = 'normal';\n private expandedId: string | null = null;\n private errorFooterExpanded = false; // For non-interactive error footer\n private filterMode: FilterMode = 'all';\n private searchTerm = '';\n private isSearching = false;\n private isFullscreen = false;\n\n // === SESSION CONFIG (immutable after construction) ===\n private header: string | undefined;\n private showStatusBar = false;\n private isInteractive = false;\n\n // === INFRASTRUCTURE ===\n private listeners = new Set<Listener>();\n private shouldExit = false;\n private exitCallback: (() => void) | null = null;\n private bufferVersion = 0; // Increments on every notify() to trigger re-renders\n\n constructor(options: SessionOptions = {}) {\n this.header = options.header;\n this.showStatusBar = options.showStatusBar ?? false;\n this.isInteractive = options.interactive ?? false;\n\n // Create list navigator with wrap-around behavior\n // Uses filtered processes count so selection works correctly with filters\n this.listNav = createNavigator({\n getLength: () => this.getFilteredProcesses().length,\n wrap: true,\n onMove: () => this.notify(),\n });\n }\n\n // === SUBSCRIPTION API (useSyncExternalStore) ===\n\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n getSnapshot = (): ChildProcess[] => this.processes;\n\n // === DATA: Queries ===\n\n getRunningProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'running');\n };\n\n getCompletedProcesses = (): ChildProcess[] => {\n // Return in completion order\n return this.completedIds.map((id) => arrayFind(this.processes, (p) => p.id === id)).filter((p): p is ChildProcess => p !== undefined);\n };\n\n getFailedProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'error');\n };\n\n // Counts\n getRunningCount = (): number => this.processes.filter((p) => p.state === 'running').length;\n getMaxGroupLength = (): number => {\n if (this.processes.length === 0) return DEFAULT_COLUMN_WIDTH;\n return Math.max(...this.processes.map((p) => (p.group || p.title).length));\n };\n getDoneCount = (): number => this.processes.filter((p) => p.state !== 'running').length;\n getErrorCount = (): number => this.processes.filter((p) => p.state === 'error').length;\n getErrorLineCount = (): number => {\n return this.processes.filter((p) => p.state === 'error').reduce((total, p) => total + this.getProcessLineCount(p.id), 0);\n };\n\n getErrorLines(): Array<{ processName: string; lines: Line[] }> {\n return this.getFailedProcesses().map((p) => ({\n processName: p.group || p.title,\n lines: this.getProcessLines(p.id),\n }));\n }\n\n // === DATA: Mutations ===\n\n addProcess(process: ChildProcess): void {\n // Create scroll navigator for this process\n const processWithNav: ChildProcess = {\n ...process,\n scrollNav: createNavigator({\n getLength: () => this.getProcessLineCount(processWithNav.id),\n wrap: false,\n onMove: () => this.notify(),\n }),\n };\n this.processes = [...this.processes, processWithNav];\n this.notify();\n }\n\n updateProcess(id: string, update: Partial<ChildProcess>): void {\n const oldProcess = arrayFind(this.processes, (p) => p.id === id);\n const wasRunning = oldProcess?.state === 'running';\n const isNowComplete = update.state && update.state !== 'running';\n\n this.processes = this.processes.map((p) => (p.id === id ? { ...p, ...update } : p));\n\n // Track completion order\n if (wasRunning && isNowComplete && !this.completedIds.includes(id)) {\n this.completedIds = [...this.completedIds, id];\n }\n\n // Auto-expand error footer when all complete with errors (non-interactive only)\n if (!this.isInteractive && this.isAllComplete() && this.getErrorCount() > 0) {\n this.errorFooterExpanded = true;\n }\n\n this.notify();\n }\n\n appendLines(id: string, newLines: Line[]): void {\n const process = arrayFind(this.processes, (p) => p.id === id);\n if (process) {\n this.updateProcess(id, { lines: process.lines.concat(newLines) });\n }\n }\n\n getProcess(id: string): ChildProcess | undefined {\n return arrayFind(this.processes, (p) => p.id === id);\n }\n\n // Get rendered lines from terminal buffer or fallback to lines array\n getProcessLines(id: string): Line[] {\n const process = this.getProcess(id);\n if (!process) return [];\n if (process.terminalBuffer) {\n return process.terminalBuffer.getLines().map((text) => ({\n type: LineType.stdout,\n text,\n }));\n }\n return process.lines;\n }\n\n // Get line count from terminal buffer or lines array\n getProcessLineCount(id: string): number {\n const process = this.getProcess(id);\n if (!process) return 0;\n if (process.terminalBuffer) {\n return process.terminalBuffer.lineCount;\n }\n return process.lines.length;\n }\n\n // === VIEW STATE: Getters ===\n\n getMode = (): Mode => this.mode;\n getSelectedIndex = (): number => this.listNav.position;\n getExpandedId = (): string | null => this.expandedId;\n getListScrollOffset = (): number => this.listNav.viewportOffset;\n getErrorFooterExpanded = (): boolean => this.errorFooterExpanded;\n getBufferVersion = (): number => this.bufferVersion;\n getFilterMode = (): FilterMode => this.filterMode;\n getSearchTerm = (): string => this.searchTerm;\n getIsSearching = (): boolean => this.isSearching;\n getIsFullscreen = (): boolean => this.isFullscreen;\n\n // Get processes filtered by current filter mode and search term\n getFilteredProcesses = (): ChildProcess[] => {\n let filtered = this.processes;\n\n // Apply filter mode\n switch (this.filterMode) {\n case 'running':\n filtered = filtered.filter((p) => p.state === 'running');\n break;\n case 'finished':\n filtered = filtered.filter((p) => p.state !== 'running');\n break;\n case 'failed':\n filtered = filtered.filter((p) => p.state === 'error');\n break;\n }\n\n // Apply search term\n if (this.searchTerm) {\n const term = this.searchTerm.toLowerCase();\n filtered = filtered.filter((p) => p.title.toLowerCase().includes(term) || (p.group && p.group.toLowerCase().includes(term)));\n }\n\n return filtered;\n };\n\n // Get scroll offset for expanded process (or 0 if none)\n getScrollOffset = (): number => {\n if (!this.expandedId) return 0;\n const process = this.getProcess(this.expandedId);\n return process?.scrollNav?.position ?? 0;\n };\n\n // Session-level getters (set at session creation, immutable)\n getHeader = (): string | undefined => this.header;\n getShowStatusBar = (): boolean => this.showStatusBar;\n getIsInteractive = (): boolean => this.isInteractive;\n isAllComplete = (): boolean => this.processes.length > 0 && this.processes.every((p) => p.state !== 'running');\n\n // === VIEW STATE: Mutations ===\n\n setMode(mode: Mode): void {\n this.mode = mode;\n if (mode === 'interactive') {\n this.listNav.setPosition(0);\n }\n this.notify();\n }\n\n getSelectedProcess(): ChildProcess | undefined {\n return this.getFilteredProcesses()[this.listNav.position];\n }\n\n // Filter mode cycling (left/right arrows)\n cycleFilterNext(): void {\n const currentIndex = FILTER_CYCLE.indexOf(this.filterMode);\n this.filterMode = FILTER_CYCLE[(currentIndex + 1) % FILTER_CYCLE.length] as FilterMode;\n // Reset selection when filter changes\n this.listNav.toStart();\n // Collapse any expanded process when filter changes\n this.expandedId = null;\n this.notify();\n }\n\n cycleFilterPrev(): void {\n const currentIndex = FILTER_CYCLE.indexOf(this.filterMode);\n this.filterMode = FILTER_CYCLE[(currentIndex - 1 + FILTER_CYCLE.length) % FILTER_CYCLE.length] as FilterMode;\n // Reset selection when filter changes\n this.listNav.toStart();\n // Collapse any expanded process when filter changes\n this.expandedId = null;\n this.notify();\n }\n\n // Search mode\n startSearch(): void {\n this.isSearching = true;\n this.searchTerm = '';\n this.notify();\n }\n\n updateSearchTerm(term: string): void {\n this.searchTerm = term;\n // Reset selection when search changes\n this.listNav.toStart();\n this.notify();\n }\n\n cancelSearch(): void {\n this.isSearching = false;\n this.searchTerm = '';\n // Reset selection\n this.listNav.toStart();\n this.notify();\n }\n\n confirmSearch(): void {\n this.isSearching = false;\n // Keep searchTerm applied, reset selection to first match\n this.listNav.toStart();\n this.notify();\n }\n\n clearSearch(): void {\n this.searchTerm = '';\n this.listNav.toStart();\n this.notify();\n }\n\n // Fullscreen mode (alternate screen buffer)\n enterFullscreen(): void {\n if (this.expandedId) {\n this.isFullscreen = true;\n this.notify();\n }\n }\n\n exitFullscreen(): void {\n this.isFullscreen = false;\n this.notify();\n }\n\n // Error footer methods (for non-interactive mode)\n toggleErrorFooter(): void {\n this.errorFooterExpanded = !this.errorFooterExpanded;\n this.notify();\n }\n\n expandErrorFooter(): void {\n if (!this.errorFooterExpanded) {\n this.errorFooterExpanded = true;\n this.notify();\n }\n }\n\n // === NAVIGATION: List (delegates to listNav) ===\n\n selectNext(visibleCount?: number): void {\n this.listNav.down();\n if (visibleCount) {\n this.listNav.ensureVisible(visibleCount);\n }\n }\n\n selectPrev(visibleCount?: number): void {\n this.listNav.up();\n if (visibleCount) {\n this.listNav.ensureVisible(visibleCount);\n }\n }\n\n selectPageDown(pageSize: number, visibleCount?: number): void {\n this.listNav.pageDown(pageSize, visibleCount);\n }\n\n selectPageUp(pageSize: number, visibleCount?: number): void {\n this.listNav.pageUp(pageSize, visibleCount);\n }\n\n selectFirst(visibleCount?: number): void {\n this.listNav.toStart();\n if (visibleCount) {\n this.listNav.ensureVisible(visibleCount);\n }\n }\n\n selectLast(visibleCount?: number): void {\n this.listNav.toEnd();\n if (visibleCount) {\n this.listNav.ensureVisible(visibleCount);\n }\n }\n\n clampListViewport(visibleCount: number): void {\n const changed = this.listNav.clampViewport(visibleCount);\n if (changed) {\n this.notify();\n }\n }\n\n // === NAVIGATION: Expanded content (delegates to process.scrollNav) ===\n\n private getExpandedNav(): { nav: Navigator; id: string } | undefined {\n if (!this.expandedId) return undefined;\n const nav = this.getProcess(this.expandedId)?.scrollNav;\n if (!nav) return undefined;\n return { nav, id: this.expandedId };\n }\n\n scrollDown(maxVisible: number): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n const lineCount = this.getProcessLineCount(expanded.id);\n const maxOffset = Math.max(0, lineCount - maxVisible);\n\n // Only scroll if not at bottom\n if (expanded.nav.position < maxOffset) {\n expanded.nav.down();\n }\n }\n\n scrollUp(): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n if (expanded.nav.position > 0) {\n expanded.nav.up();\n }\n }\n\n scrollPageDown(pageSize: number): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n const lineCount = this.getProcessLineCount(expanded.id);\n const maxOffset = Math.max(0, lineCount - pageSize);\n\n // Clamp to max offset\n const newPosition = Math.min(expanded.nav.position + pageSize, maxOffset);\n expanded.nav.setPosition(newPosition);\n this.notify();\n }\n\n scrollPageUp(pageSize: number): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n const newPosition = Math.max(0, expanded.nav.position - pageSize);\n expanded.nav.setPosition(newPosition);\n this.notify();\n }\n\n scrollToTop(): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n expanded.nav.toStart();\n }\n\n scrollToBottom(maxVisible: number): void {\n const expanded = this.getExpandedNav();\n if (!expanded) return;\n\n const lineCount = this.getProcessLineCount(expanded.id);\n const newPosition = Math.max(0, lineCount - maxVisible);\n expanded.nav.setPosition(newPosition);\n this.notify();\n }\n\n // === EXPANSION ===\n\n toggleExpand(visibleCountWhenExpanded?: number, visibleCountWhenCollapsed?: number): void {\n const selected = this.getSelectedProcess();\n if (!selected) return;\n\n if (this.expandedId === selected.id) {\n // Collapse (keep scroll position for later)\n this.expandedId = null;\n // Adjust viewport to avoid empty space at bottom\n if (visibleCountWhenCollapsed) {\n this.listNav.clampViewport(visibleCountWhenCollapsed);\n }\n } else {\n // Expand (scroll position is preserved in process.scrollNav)\n this.expandedId = selected.id;\n // Adjust list scroll to keep expanded process visible\n if (visibleCountWhenExpanded) {\n this.listNav.ensureVisible(visibleCountWhenExpanded);\n }\n }\n this.notify();\n }\n\n collapse(visibleCountWhenCollapsed?: number): void {\n // Collapse but keep scroll position in process\n this.expandedId = null;\n // Adjust viewport to avoid empty space at bottom\n if (visibleCountWhenCollapsed) {\n this.listNav.clampViewport(visibleCountWhenCollapsed);\n }\n this.notify();\n }\n\n // === EXIT ===\n\n signalExit(callback: () => void): void {\n this.shouldExit = true;\n this.exitCallback = callback;\n this.notify();\n }\n\n getShouldExit = (): boolean => this.shouldExit;\n getExitCallback = (): (() => void) | null => this.exitCallback;\n\n // === RESET ===\n\n reset(): void {\n // Dispose terminal buffers before clearing\n for (const process of this.processes) {\n process.terminalBuffer?.dispose();\n }\n this.processes = [];\n this.completedIds = [];\n this.shouldExit = false;\n this.exitCallback = null;\n this.mode = 'normal';\n this.listNav.reset();\n this.expandedId = null;\n this.errorFooterExpanded = false;\n this.filterMode = 'all';\n this.searchTerm = '';\n this.isSearching = false;\n this.isFullscreen = false;\n this.header = undefined;\n }\n\n // === INFRASTRUCTURE ===\n\n notify(): void {\n this.bufferVersion++;\n this.listeners.forEach((l) => {\n l();\n });\n }\n}\n\n// Note: No global singleton - session creates its own store instance\n"],"names":["arrayFind","DEFAULT_COLUMN_WIDTH","LineType","createNavigator","FILTER_CYCLE","ProcessStore","getErrorLines","getFailedProcesses","map","p","processName","group","title","lines","getProcessLines","id","addProcess","process","processWithNav","scrollNav","getLength","getProcessLineCount","wrap","onMove","notify","processes","updateProcess","update","oldProcess","wasRunning","state","isNowComplete","completedIds","includes","isInteractive","isAllComplete","getErrorCount","errorFooterExpanded","appendLines","newLines","concat","getProcess","terminalBuffer","getLines","text","type","stdout","lineCount","length","setMode","mode","listNav","setPosition","getSelectedProcess","getFilteredProcesses","position","cycleFilterNext","currentIndex","indexOf","filterMode","toStart","expandedId","cycleFilterPrev","startSearch","isSearching","searchTerm","updateSearchTerm","term","cancelSearch","confirmSearch","clearSearch","enterFullscreen","isFullscreen","exitFullscreen","toggleErrorFooter","expandErrorFooter","selectNext","visibleCount","down","ensureVisible","selectPrev","up","selectPageDown","pageSize","pageDown","selectPageUp","pageUp","selectFirst","selectLast","toEnd","clampListViewport","changed","clampViewport","getExpandedNav","undefined","nav","scrollDown","maxVisible","expanded","maxOffset","Math","max","scrollUp","scrollPageDown","newPosition","min","scrollPageUp","scrollToTop","scrollToBottom","toggleExpand","visibleCountWhenExpanded","visibleCountWhenCollapsed","selected","collapse","signalExit","callback","shouldExit","exitCallback","reset","dispose","header","bufferVersion","listeners","forEach","l","options","showStatusBar","Set","subscribe","listener","add","delete","getSnapshot","getRunningProcesses","filter","getCompletedProcesses","getRunningCount","getMaxGroupLength","getDoneCount","getErrorLineCount","reduce","total","getMode","getSelectedIndex","getExpandedId","getListScrollOffset","viewportOffset","getErrorFooterExpanded","getBufferVersion","getFilterMode","getSearchTerm","getIsSearching","getIsFullscreen","filtered","toLowerCase","getScrollOffset","getHeader","getShowStatusBar","getIsInteractive","every","getShouldExit","getExitCallback","interactive"],"mappings":"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"}
|