spawn-term 3.1.5 → 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.
@@ -32,7 +32,7 @@ function AppContent(param) {
32
32
  var stdout = (0, _ink.useStdout)().stdout;
33
33
  var terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
34
34
  // Subscribe to store state
35
- var processes = (0, _react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
35
+ var allProcesses = (0, _react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
36
36
  var shouldExit = (0, _react.useSyncExternalStore)(store.subscribe, store.getShouldExit);
37
37
  var mode = (0, _react.useSyncExternalStore)(store.subscribe, store.getMode);
38
38
  var selectedIndex = (0, _react.useSyncExternalStore)(store.subscribe, store.getSelectedIndex);
@@ -40,26 +40,39 @@ function AppContent(param) {
40
40
  var scrollOffset = (0, _react.useSyncExternalStore)(store.subscribe, store.getScrollOffset);
41
41
  var listScrollOffset = (0, _react.useSyncExternalStore)(store.subscribe, store.getListScrollOffset);
42
42
  var errorFooterExpanded = (0, _react.useSyncExternalStore)(store.subscribe, store.getErrorFooterExpanded);
43
+ var filterMode = (0, _react.useSyncExternalStore)(store.subscribe, store.getFilterMode);
44
+ var searchTerm = (0, _react.useSyncExternalStore)(store.subscribe, store.getSearchTerm);
45
+ var isSearching = (0, _react.useSyncExternalStore)(store.subscribe, store.getIsSearching);
43
46
  // Subscribe to buffer version to trigger re-renders when terminal buffer content changes
44
47
  var _bufferVersion = (0, _react.useSyncExternalStore)(store.subscribe, store.getBufferVersion);
48
+ // Use filtered processes for display
49
+ var processes = store.getFilteredProcesses();
45
50
  // Subscribed state that triggers re-renders
46
51
  var header = (0, _react.useSyncExternalStore)(store.subscribe, store.getHeader);
47
52
  var showStatusBar = (0, _react.useSyncExternalStore)(store.subscribe, store.getShowStatusBar);
48
53
  var isInteractive = (0, _react.useSyncExternalStore)(store.subscribe, store.getIsInteractive);
49
54
  // Calculate visible process count (reserve lines for header, divider, status bar, expanded output)
50
55
  // When a process is expanded, reserve space for the expanded output to prevent terminal scrolling
51
- // In interactive mode without expansion, reserve space for potential list scroll hint
56
+ // In interactive mode without expansion, reserve space for filter bar and list scroll hint
52
57
  var expandedHeight = expandedId ? _constantsts.EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint
58
+ var filterBarHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for filter/search bar
53
59
  var listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint
54
- var reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + listHintHeight;
60
+ var reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + filterBarHeight + listHintHeight;
55
61
  var visibleProcessCount = Math.max(1, terminalHeight - reservedLines);
56
- // Derived state (computed from processes which is already subscribed)
62
+ // Derived state (computed from allProcesses - total counts regardless of filter)
57
63
  var runningCount = store.getRunningCount();
58
64
  var doneCount = store.getDoneCount();
59
65
  var errorCount = store.getErrorCount();
60
66
  var errorLineCount = store.getErrorLineCount();
61
67
  var _isAllComplete = store.isAllComplete();
62
68
  var errorLines = store.getErrorLines();
69
+ // Filter mode display labels
70
+ var filterLabels = {
71
+ all: 'All',
72
+ running: 'Running',
73
+ finished: 'Finished',
74
+ failed: 'Failed'
75
+ };
63
76
  // Handle exit signal
64
77
  (0, _react.useEffect)(function() {
65
78
  if (shouldExit) {
@@ -94,6 +107,19 @@ function AppContent(param) {
94
107
  ]);
95
108
  // Keyboard handling (only active when raw mode is supported)
96
109
  (0, _ink.useInput)(function(input, key) {
110
+ // Search mode input handling
111
+ if (isSearching) {
112
+ if (key.escape) {
113
+ store.cancelSearch();
114
+ } else if (key.return) {
115
+ store.confirmSearch();
116
+ } else if (key.backspace || key.delete) {
117
+ store.updateSearchTerm(searchTerm.slice(0, -1));
118
+ } else if (input && !key.ctrl && !key.meta) {
119
+ store.updateSearchTerm(searchTerm + input);
120
+ }
121
+ return;
122
+ }
97
123
  if (mode === 'normal') {
98
124
  // In non-interactive mode, 'e' toggles error footer
99
125
  if (input === 'e' && errorCount > 0) {
@@ -103,15 +129,26 @@ function AppContent(param) {
103
129
  // Pre-calculate visible counts for expand/collapse transitions
104
130
  var baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);
105
131
  var visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - _constantsts.EXPANDED_MAX_VISIBLE_LINES - 1);
106
- var visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 1); // -1 for list hint
132
+ var visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint
107
133
  if (input === 'q' || key.escape) {
108
134
  if (expandedId) {
109
135
  store.collapse(visibleWhenCollapsed);
136
+ } else if (searchTerm) {
137
+ // Clear search first before exiting
138
+ store.clearSearch();
110
139
  } else {
111
140
  store.signalExit(function() {});
112
141
  }
113
142
  } else if (key.return) {
114
143
  store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);
144
+ // Filter cycling - left/right arrows
145
+ } else if (key.rightArrow && !expandedId) {
146
+ store.cycleFilterNext();
147
+ } else if (key.leftArrow && !expandedId) {
148
+ store.cycleFilterPrev();
149
+ // Search - '/' to start search
150
+ } else if (input === '/' && !expandedId) {
151
+ store.startSearch();
115
152
  // Jump to top - Option+↑ (detected as meta), vim: g
116
153
  // Must check meta+arrow BEFORE plain arrow
117
154
  } else if (key.meta && key.upArrow || input === 'g') {
@@ -174,7 +211,7 @@ function AppContent(param) {
174
211
  var showSelection = mode === 'interactive';
175
212
  // Force full re-render when layout structure changes
176
213
  // Note: scrollOffset is NOT included - scrolling within expansion doesn't change structure
177
- var layoutKey = "".concat(listScrollOffset, "-").concat(expandedId, "-").concat(errorCount, "-").concat(errorFooterExpanded);
214
+ var layoutKey = "".concat(listScrollOffset, "-").concat(expandedId, "-").concat(errorCount, "-").concat(errorFooterExpanded, "-").concat(filterMode, "-").concat(searchTerm, "-").concat(isSearching);
178
215
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
179
216
  flexDirection: "column",
180
217
  children: [
@@ -186,6 +223,60 @@ function AppContent(param) {
186
223
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {})
187
224
  ]
188
225
  }),
226
+ mode === 'interactive' && !expandedId && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
227
+ children: [
228
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
229
+ dimColor: true,
230
+ children: "◀ "
231
+ }),
232
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
233
+ color: filterMode === 'running' ? 'yellow' : filterMode === 'failed' ? 'red' : filterMode === 'finished' ? 'green' : 'cyan',
234
+ bold: true,
235
+ children: filterLabels[filterMode]
236
+ }),
237
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
238
+ dimColor: true,
239
+ children: " ▶"
240
+ }),
241
+ isSearching ? /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
242
+ children: [
243
+ ' ',
244
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
245
+ dimColor: true,
246
+ children: "/"
247
+ }),
248
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
249
+ children: searchTerm
250
+ }),
251
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
252
+ dimColor: true,
253
+ children: "▋"
254
+ })
255
+ ]
256
+ }) : searchTerm ? /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
257
+ dimColor: true,
258
+ children: [
259
+ ' "',
260
+ searchTerm,
261
+ '"'
262
+ ]
263
+ }) : /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
264
+ dimColor: true,
265
+ children: " (/ search)"
266
+ }),
267
+ processes.length !== allProcesses.length && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
268
+ dimColor: true,
269
+ children: [
270
+ ' ',
271
+ "[",
272
+ processes.length,
273
+ "/",
274
+ allProcesses.length,
275
+ "]"
276
+ ]
277
+ })
278
+ ]
279
+ }),
189
280
  /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
190
281
  flexDirection: "column",
191
282
  children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';\nimport { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { ProcessStore } from '../state/processStore.ts';\nimport { StoreContext } from '../state/StoreContext.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorFooter from './ErrorFooter.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\n\nconst isMac = process.platform === 'darwin';\n\ninterface AppProps {\n store: ProcessStore;\n}\n\nfunction AppContent({ store }: AppProps): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Subscribe to store state\n const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);\n const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);\n const mode = useSyncExternalStore(store.subscribe, store.getMode);\n const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);\n const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);\n const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);\n const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);\n const errorFooterExpanded = useSyncExternalStore(store.subscribe, store.getErrorFooterExpanded);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(store.subscribe, store.getHeader);\n const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);\n const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);\n\n // Calculate visible process count (reserve lines for header, divider, status bar, expanded output)\n // When a process is expanded, reserve space for the expanded output to prevent terminal scrolling\n // In interactive mode without expansion, reserve space for potential list scroll hint\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + listHintHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from processes which is already subscribed)\n const runningCount = store.getRunningCount();\n const doneCount = store.getDoneCount();\n const errorCount = store.getErrorCount();\n const errorLineCount = store.getErrorLineCount();\n const _isAllComplete = store.isAllComplete();\n const errorLines = store.getErrorLines();\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode immediately when interactive flag is set\n // This allows selecting and viewing logs of running processes\n useEffect(() => {\n if (isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isInteractive, mode, store]);\n\n // Clamp viewport when collapsing to avoid empty space\n // This runs after render with correct visibleProcessCount\n useEffect(() => {\n if (mode === 'interactive' && !expandedId) {\n store.clampListViewport(visibleProcessCount);\n }\n }, [mode, expandedId, visibleProcessCount, store]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n if (mode === 'normal') {\n // In non-interactive mode, 'e' toggles error footer\n if (input === 'e' && errorCount > 0) {\n store.toggleErrorFooter();\n }\n } else if (mode === 'interactive') {\n // Pre-calculate visible counts for expand/collapse transitions\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 1); // -1 for list hint\n\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse(visibleWhenCollapsed);\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\n // Jump to top - Option+↑ (detected as meta), vim: g\n // Must check meta+arrow BEFORE plain arrow\n } else if ((key.meta && key.upArrow) || input === 'g') {\n if (expandedId) {\n store.scrollToTop();\n } else {\n store.selectFirst(visibleProcessCount);\n }\n // Jump to bottom - Option+↓ (detected as meta), vim: G\n } else if ((key.meta && key.downArrow) || input === 'G') {\n if (expandedId) {\n store.scrollToBottom(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectLast(visibleProcessCount);\n }\n // Page scrolling - Tab/Shift+Tab (use same page size as expanded view)\n } else if (key.tab && key.shift) {\n if (expandedId) {\n store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageUp(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n } else if (key.tab && !key.shift) {\n if (expandedId) {\n store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageDown(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n // Line scrolling - arrows and vim j/k\n } else if (key.downArrow || input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow || input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Slice processes to visible viewport in interactive mode\n const visibleProcesses = useMemo(() => {\n if (mode === 'interactive') {\n return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);\n }\n return processes;\n }, [processes, mode, listScrollOffset, visibleProcessCount]);\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n\n // Force full re-render when layout structure changes\n // Note: scrollOffset is NOT included - scrolling within expansion doesn't change structure\n const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}`;\n\n return (\n <Box key={layoutKey} flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Visible processes */}\n <Box flexDirection=\"column\">\n {visibleProcesses.map((item) => {\n const originalIndex = processes.indexOf(item);\n return (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && originalIndex === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={store.getProcessLines(item.id)} scrollOffset={scrollOffset} />}\n </Box>\n );\n })}\n {/* List scroll hint (interactive mode without expansion) */}\n {mode === 'interactive' && !expandedId && processes.length > visibleProcessCount && (\n <Text dimColor>\n [+{processes.length - visibleProcessCount} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom]\n </Text>\n )}\n </Box>\n\n {/* Status bar */}\n {showStatusBar && processes.length > 0 && (\n <>\n <Divider />\n <StatusBar running={runningCount} done={doneCount} errors={errorCount} errorLines={errorLineCount} />\n </>\n )}\n\n {/* Error footer (non-interactive mode only) */}\n {!isInteractive && errorCount > 0 && <ErrorFooter errors={errorLines} isExpanded={errorFooterExpanded} />}\n </Box>\n );\n}\n\n// Wrapper component that provides store context\nexport default function App({ store }: AppProps): React.JSX.Element {\n return (\n <StoreContext.Provider value={store}>\n <AppContent store={store} />\n </StoreContext.Provider>\n );\n}\n"],"names":["App","isMac","process","platform","AppContent","store","exit","useApp","isRawModeSupported","useStdin","stdout","useStdout","terminalHeight","rows","processes","useSyncExternalStore","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","_bufferVersion","getBufferVersion","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","EXPANDED_MAX_VISIBLE_LINES","listHintHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","useEffect","setMode","clampListViewport","useInput","input","key","toggleErrorFooter","baseReserved","visibleWhenExpanded","visibleWhenCollapsed","escape","collapse","signalExit","return","toggleExpand","meta","upArrow","scrollToTop","selectFirst","downArrow","scrollToBottom","selectLast","tab","shift","scrollPageUp","selectPageUp","scrollPageDown","selectPageDown","scrollDown","selectNext","scrollUp","selectPrev","isActive","visibleProcesses","useMemo","slice","showSelection","layoutKey","Box","flexDirection","Text","Divider","map","item","originalIndex","indexOf","CompactProcessLine","isSelected","id","ExpandedOutput","lines","getProcessLines","length","dimColor","StatusBar","running","done","errors","ErrorFooter","isExpanded","StoreContext","Provider","value"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';\nimport { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { ProcessStore } from '../state/processStore.ts';\nimport { StoreContext } from '../state/StoreContext.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorFooter from './ErrorFooter.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\n\nconst isMac = process.platform === 'darwin';\n\ninterface AppProps {\n store: ProcessStore;\n}\n\nfunction AppContent({ store }: AppProps): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Subscribe to store state\n const allProcesses = useSyncExternalStore(store.subscribe, store.getSnapshot);\n const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);\n const mode = useSyncExternalStore(store.subscribe, store.getMode);\n const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);\n const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);\n const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);\n const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);\n const errorFooterExpanded = useSyncExternalStore(store.subscribe, store.getErrorFooterExpanded);\n const filterMode = useSyncExternalStore(store.subscribe, store.getFilterMode);\n const searchTerm = useSyncExternalStore(store.subscribe, store.getSearchTerm);\n const isSearching = useSyncExternalStore(store.subscribe, store.getIsSearching);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Use filtered processes for display\n const processes = store.getFilteredProcesses();\n\n // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(store.subscribe, store.getHeader);\n const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);\n const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);\n\n // Calculate visible process count (reserve lines for header, divider, status bar, expanded output)\n // When a process is expanded, reserve space for the expanded output to prevent terminal scrolling\n // In interactive mode without expansion, reserve space for filter bar and list scroll hint\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const filterBarHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for filter/search bar\n const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + filterBarHeight + listHintHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from allProcesses - total counts regardless of filter)\n const runningCount = store.getRunningCount();\n const doneCount = store.getDoneCount();\n const errorCount = store.getErrorCount();\n const errorLineCount = store.getErrorLineCount();\n const _isAllComplete = store.isAllComplete();\n const errorLines = store.getErrorLines();\n\n // Filter mode display labels\n const filterLabels: Record<string, string> = {\n all: 'All',\n running: 'Running',\n finished: 'Finished',\n failed: 'Failed',\n };\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode immediately when interactive flag is set\n // This allows selecting and viewing logs of running processes\n useEffect(() => {\n if (isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isInteractive, mode, store]);\n\n // Clamp viewport when collapsing to avoid empty space\n // This runs after render with correct visibleProcessCount\n useEffect(() => {\n if (mode === 'interactive' && !expandedId) {\n store.clampListViewport(visibleProcessCount);\n }\n }, [mode, expandedId, visibleProcessCount, store]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n // Search mode input handling\n if (isSearching) {\n if (key.escape) {\n store.cancelSearch();\n } else if (key.return) {\n store.confirmSearch();\n } else if (key.backspace || key.delete) {\n store.updateSearchTerm(searchTerm.slice(0, -1));\n } else if (input && !key.ctrl && !key.meta) {\n store.updateSearchTerm(searchTerm + input);\n }\n return;\n }\n\n if (mode === 'normal') {\n // In non-interactive mode, 'e' toggles error footer\n if (input === 'e' && errorCount > 0) {\n store.toggleErrorFooter();\n }\n } else if (mode === 'interactive') {\n // Pre-calculate visible counts for expand/collapse transitions\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint\n\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse(visibleWhenCollapsed);\n } else if (searchTerm) {\n // Clear search first before exiting\n store.clearSearch();\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\n // Filter cycling - left/right arrows\n } else if (key.rightArrow && !expandedId) {\n store.cycleFilterNext();\n } else if (key.leftArrow && !expandedId) {\n store.cycleFilterPrev();\n // Search - '/' to start search\n } else if (input === '/' && !expandedId) {\n store.startSearch();\n // Jump to top - Option+↑ (detected as meta), vim: g\n // Must check meta+arrow BEFORE plain arrow\n } else if ((key.meta && key.upArrow) || input === 'g') {\n if (expandedId) {\n store.scrollToTop();\n } else {\n store.selectFirst(visibleProcessCount);\n }\n // Jump to bottom - Option+↓ (detected as meta), vim: G\n } else if ((key.meta && key.downArrow) || input === 'G') {\n if (expandedId) {\n store.scrollToBottom(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectLast(visibleProcessCount);\n }\n // Page scrolling - Tab/Shift+Tab (use same page size as expanded view)\n } else if (key.tab && key.shift) {\n if (expandedId) {\n store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageUp(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n } else if (key.tab && !key.shift) {\n if (expandedId) {\n store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageDown(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n // Line scrolling - arrows and vim j/k\n } else if (key.downArrow || input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow || input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Slice processes to visible viewport in interactive mode\n const visibleProcesses = useMemo(() => {\n if (mode === 'interactive') {\n return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);\n }\n return processes;\n }, [processes, mode, listScrollOffset, visibleProcessCount]);\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n\n // Force full re-render when layout structure changes\n // Note: scrollOffset is NOT included - scrolling within expansion doesn't change structure\n const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}-${filterMode}-${searchTerm}-${isSearching}`;\n\n return (\n <Box key={layoutKey} flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Filter/Search bar (interactive mode only) */}\n {mode === 'interactive' && !expandedId && (\n <Box>\n <Text dimColor>◀ </Text>\n <Text color={filterMode === 'running' ? 'yellow' : filterMode === 'failed' ? 'red' : filterMode === 'finished' ? 'green' : 'cyan'} bold>\n {filterLabels[filterMode]}\n </Text>\n <Text dimColor> ▶</Text>\n {isSearching ? (\n <Text>\n {' '}\n <Text dimColor>/</Text>\n <Text>{searchTerm}</Text>\n <Text dimColor>▋</Text>\n </Text>\n ) : searchTerm ? (\n <Text dimColor> \"{searchTerm}\"</Text>\n ) : (\n <Text dimColor> (/ search)</Text>\n )}\n {processes.length !== allProcesses.length && (\n <Text dimColor>\n {' '}\n [{processes.length}/{allProcesses.length}]\n </Text>\n )}\n </Box>\n )}\n\n {/* Visible processes */}\n <Box flexDirection=\"column\">\n {visibleProcesses.map((item) => {\n const originalIndex = processes.indexOf(item);\n return (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && originalIndex === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={store.getProcessLines(item.id)} scrollOffset={scrollOffset} />}\n </Box>\n );\n })}\n {/* List scroll hint (interactive mode without expansion) */}\n {mode === 'interactive' && !expandedId && processes.length > visibleProcessCount && (\n <Text dimColor>\n [+{processes.length - visibleProcessCount} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom]\n </Text>\n )}\n </Box>\n\n {/* Status bar */}\n {showStatusBar && processes.length > 0 && (\n <>\n <Divider />\n <StatusBar running={runningCount} done={doneCount} errors={errorCount} errorLines={errorLineCount} />\n </>\n )}\n\n {/* Error footer (non-interactive mode only) */}\n {!isInteractive && errorCount > 0 && <ErrorFooter errors={errorLines} isExpanded={errorFooterExpanded} />}\n </Box>\n );\n}\n\n// Wrapper component that provides store context\nexport default function App({ store }: AppProps): React.JSX.Element {\n return (\n <StoreContext.Provider value={store}>\n <AppContent store={store} />\n </StoreContext.Provider>\n );\n}\n"],"names":["App","isMac","process","platform","AppContent","store","exit","useApp","isRawModeSupported","useStdin","stdout","useStdout","terminalHeight","rows","allProcesses","useSyncExternalStore","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","filterMode","getFilterMode","searchTerm","getSearchTerm","isSearching","getIsSearching","_bufferVersion","getBufferVersion","processes","getFilteredProcesses","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","EXPANDED_MAX_VISIBLE_LINES","filterBarHeight","listHintHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","filterLabels","all","running","finished","failed","useEffect","setMode","clampListViewport","useInput","input","key","escape","cancelSearch","return","confirmSearch","backspace","delete","updateSearchTerm","slice","ctrl","meta","toggleErrorFooter","baseReserved","visibleWhenExpanded","visibleWhenCollapsed","collapse","clearSearch","signalExit","toggleExpand","rightArrow","cycleFilterNext","leftArrow","cycleFilterPrev","startSearch","upArrow","scrollToTop","selectFirst","downArrow","scrollToBottom","selectLast","tab","shift","scrollPageUp","selectPageUp","scrollPageDown","selectPageDown","scrollDown","selectNext","scrollUp","selectPrev","isActive","visibleProcesses","useMemo","showSelection","layoutKey","Box","flexDirection","Text","Divider","dimColor","color","bold","length","map","item","originalIndex","indexOf","CompactProcessLine","isSelected","id","ExpandedOutput","lines","getProcessLines","StatusBar","done","errors","ErrorFooter","isExpanded","StoreContext","Provider","value"],"mappings":"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"}
@@ -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,9 @@ 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;
11
15
  private header;
12
16
  private showStatusBar;
13
17
  private isInteractive;
@@ -42,6 +46,10 @@ export declare class ProcessStore {
42
46
  getListScrollOffset: () => number;
43
47
  getErrorFooterExpanded: () => boolean;
44
48
  getBufferVersion: () => number;
49
+ getFilterMode: () => FilterMode;
50
+ getSearchTerm: () => string;
51
+ getIsSearching: () => boolean;
52
+ getFilteredProcesses: () => ChildProcess[];
45
53
  getScrollOffset: () => number;
46
54
  getHeader: () => string | undefined;
47
55
  getShowStatusBar: () => boolean;
@@ -49,6 +57,13 @@ export declare class ProcessStore {
49
57
  isAllComplete: () => boolean;
50
58
  setMode(mode: Mode): void;
51
59
  getSelectedProcess(): ChildProcess | undefined;
60
+ cycleFilterNext(): void;
61
+ cycleFilterPrev(): void;
62
+ startSearch(): void;
63
+ updateSearchTerm(term: string): void;
64
+ cancelSearch(): void;
65
+ confirmSearch(): void;
66
+ clearSearch(): void;
52
67
  toggleErrorFooter(): void;
53
68
  expandErrorFooter(): void;
54
69
  selectNext(visibleCount?: number): void;
@@ -94,6 +94,12 @@ function _unsupported_iterable_to_array(o, minLen) {
94
94
  if (n === "Map" || n === "Set") return Array.from(n);
95
95
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
96
96
  }
97
+ var FILTER_CYCLE = [
98
+ 'all',
99
+ 'running',
100
+ 'finished',
101
+ 'failed'
102
+ ];
97
103
  var ProcessStore = /*#__PURE__*/ function() {
98
104
  "use strict";
99
105
  function ProcessStore() {
@@ -107,6 +113,9 @@ var ProcessStore = /*#__PURE__*/ function() {
107
113
  this.mode = 'normal';
108
114
  this.expandedId = null;
109
115
  this.errorFooterExpanded = false; // For non-interactive error footer
116
+ this.filterMode = 'all';
117
+ this.searchTerm = '';
118
+ this.isSearching = false;
110
119
  this.showStatusBar = false;
111
120
  this.isInteractive = false;
112
121
  // === INFRASTRUCTURE ===
@@ -194,6 +203,45 @@ var ProcessStore = /*#__PURE__*/ function() {
194
203
  this.getBufferVersion = function() {
195
204
  return _this.bufferVersion;
196
205
  };
206
+ this.getFilterMode = function() {
207
+ return _this.filterMode;
208
+ };
209
+ this.getSearchTerm = function() {
210
+ return _this.searchTerm;
211
+ };
212
+ this.getIsSearching = function() {
213
+ return _this.isSearching;
214
+ };
215
+ // Get processes filtered by current filter mode and search term
216
+ this.getFilteredProcesses = function() {
217
+ var filtered = _this.processes;
218
+ // Apply filter mode
219
+ switch(_this.filterMode){
220
+ case 'running':
221
+ filtered = filtered.filter(function(p) {
222
+ return p.state === 'running';
223
+ });
224
+ break;
225
+ case 'finished':
226
+ filtered = filtered.filter(function(p) {
227
+ return p.state !== 'running';
228
+ });
229
+ break;
230
+ case 'failed':
231
+ filtered = filtered.filter(function(p) {
232
+ return p.state === 'error';
233
+ });
234
+ break;
235
+ }
236
+ // Apply search term
237
+ if (_this.searchTerm) {
238
+ var term = _this.searchTerm.toLowerCase();
239
+ filtered = filtered.filter(function(p) {
240
+ return p.title.toLowerCase().includes(term) || p.group && p.group.toLowerCase().includes(term);
241
+ });
242
+ }
243
+ return filtered;
244
+ };
197
245
  // Get scroll offset for expanded process (or 0 if none)
198
246
  this.getScrollOffset = function() {
199
247
  var _process_scrollNav;
@@ -229,9 +277,10 @@ var ProcessStore = /*#__PURE__*/ function() {
229
277
  var _options_interactive;
230
278
  this.isInteractive = (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false;
231
279
  // Create list navigator with wrap-around behavior
280
+ // Uses filtered processes count so selection works correctly with filters
232
281
  this.listNav = (0, _Navigatorts.createNavigator)({
233
282
  getLength: function() {
234
- return _this.processes.length;
283
+ return _this.getFilteredProcesses().length;
235
284
  },
236
285
  wrap: true,
237
286
  onMove: function() {
@@ -337,7 +386,56 @@ var ProcessStore = /*#__PURE__*/ function() {
337
386
  this.notify();
338
387
  };
339
388
  _proto.getSelectedProcess = function getSelectedProcess() {
340
- return this.processes[this.listNav.position];
389
+ return this.getFilteredProcesses()[this.listNav.position];
390
+ };
391
+ // Filter mode cycling (left/right arrows)
392
+ _proto.cycleFilterNext = function cycleFilterNext() {
393
+ var currentIndex = FILTER_CYCLE.indexOf(this.filterMode);
394
+ this.filterMode = FILTER_CYCLE[(currentIndex + 1) % FILTER_CYCLE.length];
395
+ // Reset selection when filter changes
396
+ this.listNav.toStart();
397
+ // Collapse any expanded process when filter changes
398
+ this.expandedId = null;
399
+ this.notify();
400
+ };
401
+ _proto.cycleFilterPrev = function cycleFilterPrev() {
402
+ var currentIndex = FILTER_CYCLE.indexOf(this.filterMode);
403
+ this.filterMode = FILTER_CYCLE[(currentIndex - 1 + FILTER_CYCLE.length) % FILTER_CYCLE.length];
404
+ // Reset selection when filter changes
405
+ this.listNav.toStart();
406
+ // Collapse any expanded process when filter changes
407
+ this.expandedId = null;
408
+ this.notify();
409
+ };
410
+ // Search mode
411
+ _proto.startSearch = function startSearch() {
412
+ this.isSearching = true;
413
+ this.searchTerm = '';
414
+ this.notify();
415
+ };
416
+ _proto.updateSearchTerm = function updateSearchTerm(term) {
417
+ this.searchTerm = term;
418
+ // Reset selection when search changes
419
+ this.listNav.toStart();
420
+ this.notify();
421
+ };
422
+ _proto.cancelSearch = function cancelSearch() {
423
+ this.isSearching = false;
424
+ this.searchTerm = '';
425
+ // Reset selection
426
+ this.listNav.toStart();
427
+ this.notify();
428
+ };
429
+ _proto.confirmSearch = function confirmSearch() {
430
+ this.isSearching = false;
431
+ // Keep searchTerm applied, reset selection to first match
432
+ this.listNav.toStart();
433
+ this.notify();
434
+ };
435
+ _proto.clearSearch = function clearSearch() {
436
+ this.searchTerm = '';
437
+ this.listNav.toStart();
438
+ this.notify();
341
439
  };
342
440
  // Error footer methods (for non-interactive mode)
343
441
  _proto.toggleErrorFooter = function toggleErrorFooter() {
@@ -513,6 +611,9 @@ var ProcessStore = /*#__PURE__*/ function() {
513
611
  this.listNav.reset();
514
612
  this.expandedId = null;
515
613
  this.errorFooterExpanded = false;
614
+ this.filterMode = 'all';
615
+ this.searchTerm = '';
616
+ this.isSearching = false;
516
617
  this.header = undefined;
517
618
  };
518
619
  // === INFRASTRUCTURE ===
@@ -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":["ProcessStore","options","processes","completedIds","mode","expandedId","errorFooterExpanded","showStatusBar","isInteractive","listeners","Set","shouldExit","exitCallback","bufferVersion","subscribe","listener","add","delete","getSnapshot","getRunningProcesses","filter","p","state","getCompletedProcesses","map","id","arrayFind","undefined","getFailedProcesses","getRunningCount","length","getMaxGroupLength","Math","DEFAULT_COLUMN_WIDTH","max","group","title","getDoneCount","getErrorCount","getErrorLineCount","reduce","total","getProcessLineCount","getMode","getSelectedIndex","listNav","position","getExpandedId","getListScrollOffset","viewportOffset","getErrorFooterExpanded","getBufferVersion","getScrollOffset","process","getProcess","scrollNav","getHeader","header","getShowStatusBar","getIsInteractive","isAllComplete","every","getShouldExit","getExitCallback","interactive","createNavigator","getLength","wrap","onMove","notify","getErrorLines","processName","lines","getProcessLines","addProcess","processWithNav","updateProcess","update","oldProcess","wasRunning","isNowComplete","includes","appendLines","newLines","concat","terminalBuffer","getLines","text","type","LineType","stdout","lineCount","setMode","setPosition","getSelectedProcess","toggleErrorFooter","expandErrorFooter","selectNext","visibleCount","down","ensureVisible","selectPrev","up","selectPageDown","pageSize","pageDown","selectPageUp","pageUp","selectFirst","toStart","selectLast","toEnd","clampListViewport","changed","clampViewport","getExpandedNav","nav","scrollDown","maxVisible","expanded","maxOffset","scrollUp","scrollPageDown","newPosition","min","scrollPageUp","scrollToTop","scrollToBottom","toggleExpand","visibleCountWhenExpanded","visibleCountWhenCollapsed","selected","collapse","signalExit","callback","reset","dispose","forEach","l"],"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\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\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 // 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.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":["ProcessStore","FILTER_CYCLE","options","processes","completedIds","mode","expandedId","errorFooterExpanded","filterMode","searchTerm","isSearching","showStatusBar","isInteractive","listeners","Set","shouldExit","exitCallback","bufferVersion","subscribe","listener","add","delete","getSnapshot","getRunningProcesses","filter","p","state","getCompletedProcesses","map","id","arrayFind","undefined","getFailedProcesses","getRunningCount","length","getMaxGroupLength","Math","DEFAULT_COLUMN_WIDTH","max","group","title","getDoneCount","getErrorCount","getErrorLineCount","reduce","total","getProcessLineCount","getMode","getSelectedIndex","listNav","position","getExpandedId","getListScrollOffset","viewportOffset","getErrorFooterExpanded","getBufferVersion","getFilterMode","getSearchTerm","getIsSearching","getFilteredProcesses","filtered","term","toLowerCase","includes","getScrollOffset","process","getProcess","scrollNav","getHeader","header","getShowStatusBar","getIsInteractive","isAllComplete","every","getShouldExit","getExitCallback","interactive","createNavigator","getLength","wrap","onMove","notify","getErrorLines","processName","lines","getProcessLines","addProcess","processWithNav","updateProcess","update","oldProcess","wasRunning","isNowComplete","appendLines","newLines","concat","terminalBuffer","getLines","text","type","LineType","stdout","lineCount","setMode","setPosition","getSelectedProcess","cycleFilterNext","currentIndex","indexOf","toStart","cycleFilterPrev","startSearch","updateSearchTerm","cancelSearch","confirmSearch","clearSearch","toggleErrorFooter","expandErrorFooter","selectNext","visibleCount","down","ensureVisible","selectPrev","up","selectPageDown","pageSize","pageDown","selectPageUp","pageUp","selectFirst","selectLast","toEnd","clampListViewport","changed","clampViewport","getExpandedNav","nav","scrollDown","maxVisible","expanded","maxOffset","scrollUp","scrollPageDown","newPosition","min","scrollPageUp","scrollToTop","scrollToBottom","toggleExpand","visibleCountWhenExpanded","visibleCountWhenCollapsed","selected","collapse","signalExit","callback","reset","dispose","forEach","l"],"mappings":"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"}
@@ -15,7 +15,7 @@ function AppContent({ store }) {
15
15
  const { stdout } = useStdout();
16
16
  const terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
17
17
  // Subscribe to store state
18
- const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);
18
+ const allProcesses = useSyncExternalStore(store.subscribe, store.getSnapshot);
19
19
  const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);
20
20
  const mode = useSyncExternalStore(store.subscribe, store.getMode);
21
21
  const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);
@@ -23,26 +23,39 @@ function AppContent({ store }) {
23
23
  const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);
24
24
  const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);
25
25
  const errorFooterExpanded = useSyncExternalStore(store.subscribe, store.getErrorFooterExpanded);
26
+ const filterMode = useSyncExternalStore(store.subscribe, store.getFilterMode);
27
+ const searchTerm = useSyncExternalStore(store.subscribe, store.getSearchTerm);
28
+ const isSearching = useSyncExternalStore(store.subscribe, store.getIsSearching);
26
29
  // Subscribe to buffer version to trigger re-renders when terminal buffer content changes
27
30
  const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);
31
+ // Use filtered processes for display
32
+ const processes = store.getFilteredProcesses();
28
33
  // Subscribed state that triggers re-renders
29
34
  const header = useSyncExternalStore(store.subscribe, store.getHeader);
30
35
  const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);
31
36
  const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);
32
37
  // Calculate visible process count (reserve lines for header, divider, status bar, expanded output)
33
38
  // When a process is expanded, reserve space for the expanded output to prevent terminal scrolling
34
- // In interactive mode without expansion, reserve space for potential list scroll hint
39
+ // In interactive mode without expansion, reserve space for filter bar and list scroll hint
35
40
  const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint
41
+ const filterBarHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for filter/search bar
36
42
  const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint
37
- const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + listHintHeight;
43
+ const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + filterBarHeight + listHintHeight;
38
44
  const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);
39
- // Derived state (computed from processes which is already subscribed)
45
+ // Derived state (computed from allProcesses - total counts regardless of filter)
40
46
  const runningCount = store.getRunningCount();
41
47
  const doneCount = store.getDoneCount();
42
48
  const errorCount = store.getErrorCount();
43
49
  const errorLineCount = store.getErrorLineCount();
44
50
  const _isAllComplete = store.isAllComplete();
45
51
  const errorLines = store.getErrorLines();
52
+ // Filter mode display labels
53
+ const filterLabels = {
54
+ all: 'All',
55
+ running: 'Running',
56
+ finished: 'Finished',
57
+ failed: 'Failed'
58
+ };
46
59
  // Handle exit signal
47
60
  useEffect(()=>{
48
61
  if (shouldExit) {
@@ -77,6 +90,19 @@ function AppContent({ store }) {
77
90
  ]);
78
91
  // Keyboard handling (only active when raw mode is supported)
79
92
  useInput((input, key)=>{
93
+ // Search mode input handling
94
+ if (isSearching) {
95
+ if (key.escape) {
96
+ store.cancelSearch();
97
+ } else if (key.return) {
98
+ store.confirmSearch();
99
+ } else if (key.backspace || key.delete) {
100
+ store.updateSearchTerm(searchTerm.slice(0, -1));
101
+ } else if (input && !key.ctrl && !key.meta) {
102
+ store.updateSearchTerm(searchTerm + input);
103
+ }
104
+ return;
105
+ }
80
106
  if (mode === 'normal') {
81
107
  // In non-interactive mode, 'e' toggles error footer
82
108
  if (input === 'e' && errorCount > 0) {
@@ -86,15 +112,26 @@ function AppContent({ store }) {
86
112
  // Pre-calculate visible counts for expand/collapse transitions
87
113
  const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);
88
114
  const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);
89
- const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 1); // -1 for list hint
115
+ const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint
90
116
  if (input === 'q' || key.escape) {
91
117
  if (expandedId) {
92
118
  store.collapse(visibleWhenCollapsed);
119
+ } else if (searchTerm) {
120
+ // Clear search first before exiting
121
+ store.clearSearch();
93
122
  } else {
94
123
  store.signalExit(()=>{});
95
124
  }
96
125
  } else if (key.return) {
97
126
  store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);
127
+ // Filter cycling - left/right arrows
128
+ } else if (key.rightArrow && !expandedId) {
129
+ store.cycleFilterNext();
130
+ } else if (key.leftArrow && !expandedId) {
131
+ store.cycleFilterPrev();
132
+ // Search - '/' to start search
133
+ } else if (input === '/' && !expandedId) {
134
+ store.startSearch();
98
135
  // Jump to top - Option+↑ (detected as meta), vim: g
99
136
  // Must check meta+arrow BEFORE plain arrow
100
137
  } else if (key.meta && key.upArrow || input === 'g') {
@@ -157,7 +194,7 @@ function AppContent({ store }) {
157
194
  const showSelection = mode === 'interactive';
158
195
  // Force full re-render when layout structure changes
159
196
  // Note: scrollOffset is NOT included - scrolling within expansion doesn't change structure
160
- const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}`;
197
+ const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}-${filterMode}-${searchTerm}-${isSearching}`;
161
198
  return /*#__PURE__*/ _jsxs(Box, {
162
199
  flexDirection: "column",
163
200
  children: [
@@ -169,6 +206,60 @@ function AppContent({ store }) {
169
206
  /*#__PURE__*/ _jsx(Divider, {})
170
207
  ]
171
208
  }),
209
+ mode === 'interactive' && !expandedId && /*#__PURE__*/ _jsxs(Box, {
210
+ children: [
211
+ /*#__PURE__*/ _jsx(Text, {
212
+ dimColor: true,
213
+ children: "◀ "
214
+ }),
215
+ /*#__PURE__*/ _jsx(Text, {
216
+ color: filterMode === 'running' ? 'yellow' : filterMode === 'failed' ? 'red' : filterMode === 'finished' ? 'green' : 'cyan',
217
+ bold: true,
218
+ children: filterLabels[filterMode]
219
+ }),
220
+ /*#__PURE__*/ _jsx(Text, {
221
+ dimColor: true,
222
+ children: " ▶"
223
+ }),
224
+ isSearching ? /*#__PURE__*/ _jsxs(Text, {
225
+ children: [
226
+ ' ',
227
+ /*#__PURE__*/ _jsx(Text, {
228
+ dimColor: true,
229
+ children: "/"
230
+ }),
231
+ /*#__PURE__*/ _jsx(Text, {
232
+ children: searchTerm
233
+ }),
234
+ /*#__PURE__*/ _jsx(Text, {
235
+ dimColor: true,
236
+ children: "▋"
237
+ })
238
+ ]
239
+ }) : searchTerm ? /*#__PURE__*/ _jsxs(Text, {
240
+ dimColor: true,
241
+ children: [
242
+ ' "',
243
+ searchTerm,
244
+ '"'
245
+ ]
246
+ }) : /*#__PURE__*/ _jsx(Text, {
247
+ dimColor: true,
248
+ children: " (/ search)"
249
+ }),
250
+ processes.length !== allProcesses.length && /*#__PURE__*/ _jsxs(Text, {
251
+ dimColor: true,
252
+ children: [
253
+ ' ',
254
+ "[",
255
+ processes.length,
256
+ "/",
257
+ allProcesses.length,
258
+ "]"
259
+ ]
260
+ })
261
+ ]
262
+ }),
172
263
  /*#__PURE__*/ _jsxs(Box, {
173
264
  flexDirection: "column",
174
265
  children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';\nimport { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { ProcessStore } from '../state/processStore.ts';\nimport { StoreContext } from '../state/StoreContext.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorFooter from './ErrorFooter.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\n\nconst isMac = process.platform === 'darwin';\n\ninterface AppProps {\n store: ProcessStore;\n}\n\nfunction AppContent({ store }: AppProps): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Subscribe to store state\n const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);\n const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);\n const mode = useSyncExternalStore(store.subscribe, store.getMode);\n const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);\n const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);\n const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);\n const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);\n const errorFooterExpanded = useSyncExternalStore(store.subscribe, store.getErrorFooterExpanded);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(store.subscribe, store.getHeader);\n const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);\n const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);\n\n // Calculate visible process count (reserve lines for header, divider, status bar, expanded output)\n // When a process is expanded, reserve space for the expanded output to prevent terminal scrolling\n // In interactive mode without expansion, reserve space for potential list scroll hint\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + listHintHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from processes which is already subscribed)\n const runningCount = store.getRunningCount();\n const doneCount = store.getDoneCount();\n const errorCount = store.getErrorCount();\n const errorLineCount = store.getErrorLineCount();\n const _isAllComplete = store.isAllComplete();\n const errorLines = store.getErrorLines();\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode immediately when interactive flag is set\n // This allows selecting and viewing logs of running processes\n useEffect(() => {\n if (isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isInteractive, mode, store]);\n\n // Clamp viewport when collapsing to avoid empty space\n // This runs after render with correct visibleProcessCount\n useEffect(() => {\n if (mode === 'interactive' && !expandedId) {\n store.clampListViewport(visibleProcessCount);\n }\n }, [mode, expandedId, visibleProcessCount, store]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n if (mode === 'normal') {\n // In non-interactive mode, 'e' toggles error footer\n if (input === 'e' && errorCount > 0) {\n store.toggleErrorFooter();\n }\n } else if (mode === 'interactive') {\n // Pre-calculate visible counts for expand/collapse transitions\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 1); // -1 for list hint\n\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse(visibleWhenCollapsed);\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\n // Jump to top - Option+↑ (detected as meta), vim: g\n // Must check meta+arrow BEFORE plain arrow\n } else if ((key.meta && key.upArrow) || input === 'g') {\n if (expandedId) {\n store.scrollToTop();\n } else {\n store.selectFirst(visibleProcessCount);\n }\n // Jump to bottom - Option+↓ (detected as meta), vim: G\n } else if ((key.meta && key.downArrow) || input === 'G') {\n if (expandedId) {\n store.scrollToBottom(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectLast(visibleProcessCount);\n }\n // Page scrolling - Tab/Shift+Tab (use same page size as expanded view)\n } else if (key.tab && key.shift) {\n if (expandedId) {\n store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageUp(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n } else if (key.tab && !key.shift) {\n if (expandedId) {\n store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageDown(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n // Line scrolling - arrows and vim j/k\n } else if (key.downArrow || input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow || input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Slice processes to visible viewport in interactive mode\n const visibleProcesses = useMemo(() => {\n if (mode === 'interactive') {\n return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);\n }\n return processes;\n }, [processes, mode, listScrollOffset, visibleProcessCount]);\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n\n // Force full re-render when layout structure changes\n // Note: scrollOffset is NOT included - scrolling within expansion doesn't change structure\n const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}`;\n\n return (\n <Box key={layoutKey} flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Visible processes */}\n <Box flexDirection=\"column\">\n {visibleProcesses.map((item) => {\n const originalIndex = processes.indexOf(item);\n return (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && originalIndex === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={store.getProcessLines(item.id)} scrollOffset={scrollOffset} />}\n </Box>\n );\n })}\n {/* List scroll hint (interactive mode without expansion) */}\n {mode === 'interactive' && !expandedId && processes.length > visibleProcessCount && (\n <Text dimColor>\n [+{processes.length - visibleProcessCount} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom]\n </Text>\n )}\n </Box>\n\n {/* Status bar */}\n {showStatusBar && processes.length > 0 && (\n <>\n <Divider />\n <StatusBar running={runningCount} done={doneCount} errors={errorCount} errorLines={errorLineCount} />\n </>\n )}\n\n {/* Error footer (non-interactive mode only) */}\n {!isInteractive && errorCount > 0 && <ErrorFooter errors={errorLines} isExpanded={errorFooterExpanded} />}\n </Box>\n );\n}\n\n// Wrapper component that provides store context\nexport default function App({ store }: AppProps): React.JSX.Element {\n return (\n <StoreContext.Provider value={store}>\n <AppContent store={store} />\n </StoreContext.Provider>\n );\n}\n"],"names":["Box","Text","useApp","useInput","useStdin","useStdout","useEffect","useMemo","useSyncExternalStore","EXPANDED_MAX_VISIBLE_LINES","StoreContext","CompactProcessLine","Divider","ErrorFooter","ExpandedOutput","StatusBar","isMac","process","platform","AppContent","store","exit","isRawModeSupported","stdout","terminalHeight","rows","processes","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","_bufferVersion","getBufferVersion","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","listHintHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","setMode","clampListViewport","input","key","toggleErrorFooter","baseReserved","visibleWhenExpanded","visibleWhenCollapsed","escape","collapse","signalExit","return","toggleExpand","meta","upArrow","scrollToTop","selectFirst","downArrow","scrollToBottom","selectLast","tab","shift","scrollPageUp","selectPageUp","scrollPageDown","selectPageDown","scrollDown","selectNext","scrollUp","selectPrev","isActive","visibleProcesses","slice","showSelection","layoutKey","flexDirection","map","item","originalIndex","indexOf","isSelected","id","lines","getProcessLines","length","dimColor","running","done","errors","isExpanded","App","Provider","value"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';\nimport { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { ProcessStore } from '../state/processStore.ts';\nimport { StoreContext } from '../state/StoreContext.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorFooter from './ErrorFooter.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\n\nconst isMac = process.platform === 'darwin';\n\ninterface AppProps {\n store: ProcessStore;\n}\n\nfunction AppContent({ store }: AppProps): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Subscribe to store state\n const allProcesses = useSyncExternalStore(store.subscribe, store.getSnapshot);\n const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);\n const mode = useSyncExternalStore(store.subscribe, store.getMode);\n const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);\n const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);\n const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);\n const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);\n const errorFooterExpanded = useSyncExternalStore(store.subscribe, store.getErrorFooterExpanded);\n const filterMode = useSyncExternalStore(store.subscribe, store.getFilterMode);\n const searchTerm = useSyncExternalStore(store.subscribe, store.getSearchTerm);\n const isSearching = useSyncExternalStore(store.subscribe, store.getIsSearching);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Use filtered processes for display\n const processes = store.getFilteredProcesses();\n\n // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(store.subscribe, store.getHeader);\n const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);\n const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);\n\n // Calculate visible process count (reserve lines for header, divider, status bar, expanded output)\n // When a process is expanded, reserve space for the expanded output to prevent terminal scrolling\n // In interactive mode without expansion, reserve space for filter bar and list scroll hint\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const filterBarHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for filter/search bar\n const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + filterBarHeight + listHintHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from allProcesses - total counts regardless of filter)\n const runningCount = store.getRunningCount();\n const doneCount = store.getDoneCount();\n const errorCount = store.getErrorCount();\n const errorLineCount = store.getErrorLineCount();\n const _isAllComplete = store.isAllComplete();\n const errorLines = store.getErrorLines();\n\n // Filter mode display labels\n const filterLabels: Record<string, string> = {\n all: 'All',\n running: 'Running',\n finished: 'Finished',\n failed: 'Failed',\n };\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode immediately when interactive flag is set\n // This allows selecting and viewing logs of running processes\n useEffect(() => {\n if (isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isInteractive, mode, store]);\n\n // Clamp viewport when collapsing to avoid empty space\n // This runs after render with correct visibleProcessCount\n useEffect(() => {\n if (mode === 'interactive' && !expandedId) {\n store.clampListViewport(visibleProcessCount);\n }\n }, [mode, expandedId, visibleProcessCount, store]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n // Search mode input handling\n if (isSearching) {\n if (key.escape) {\n store.cancelSearch();\n } else if (key.return) {\n store.confirmSearch();\n } else if (key.backspace || key.delete) {\n store.updateSearchTerm(searchTerm.slice(0, -1));\n } else if (input && !key.ctrl && !key.meta) {\n store.updateSearchTerm(searchTerm + input);\n }\n return;\n }\n\n if (mode === 'normal') {\n // In non-interactive mode, 'e' toggles error footer\n if (input === 'e' && errorCount > 0) {\n store.toggleErrorFooter();\n }\n } else if (mode === 'interactive') {\n // Pre-calculate visible counts for expand/collapse transitions\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint\n\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse(visibleWhenCollapsed);\n } else if (searchTerm) {\n // Clear search first before exiting\n store.clearSearch();\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\n // Filter cycling - left/right arrows\n } else if (key.rightArrow && !expandedId) {\n store.cycleFilterNext();\n } else if (key.leftArrow && !expandedId) {\n store.cycleFilterPrev();\n // Search - '/' to start search\n } else if (input === '/' && !expandedId) {\n store.startSearch();\n // Jump to top - Option+↑ (detected as meta), vim: g\n // Must check meta+arrow BEFORE plain arrow\n } else if ((key.meta && key.upArrow) || input === 'g') {\n if (expandedId) {\n store.scrollToTop();\n } else {\n store.selectFirst(visibleProcessCount);\n }\n // Jump to bottom - Option+↓ (detected as meta), vim: G\n } else if ((key.meta && key.downArrow) || input === 'G') {\n if (expandedId) {\n store.scrollToBottom(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectLast(visibleProcessCount);\n }\n // Page scrolling - Tab/Shift+Tab (use same page size as expanded view)\n } else if (key.tab && key.shift) {\n if (expandedId) {\n store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageUp(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n } else if (key.tab && !key.shift) {\n if (expandedId) {\n store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageDown(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n // Line scrolling - arrows and vim j/k\n } else if (key.downArrow || input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow || input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Slice processes to visible viewport in interactive mode\n const visibleProcesses = useMemo(() => {\n if (mode === 'interactive') {\n return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);\n }\n return processes;\n }, [processes, mode, listScrollOffset, visibleProcessCount]);\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n\n // Force full re-render when layout structure changes\n // Note: scrollOffset is NOT included - scrolling within expansion doesn't change structure\n const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}-${filterMode}-${searchTerm}-${isSearching}`;\n\n return (\n <Box key={layoutKey} flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Filter/Search bar (interactive mode only) */}\n {mode === 'interactive' && !expandedId && (\n <Box>\n <Text dimColor>◀ </Text>\n <Text color={filterMode === 'running' ? 'yellow' : filterMode === 'failed' ? 'red' : filterMode === 'finished' ? 'green' : 'cyan'} bold>\n {filterLabels[filterMode]}\n </Text>\n <Text dimColor> ▶</Text>\n {isSearching ? (\n <Text>\n {' '}\n <Text dimColor>/</Text>\n <Text>{searchTerm}</Text>\n <Text dimColor>▋</Text>\n </Text>\n ) : searchTerm ? (\n <Text dimColor> \"{searchTerm}\"</Text>\n ) : (\n <Text dimColor> (/ search)</Text>\n )}\n {processes.length !== allProcesses.length && (\n <Text dimColor>\n {' '}\n [{processes.length}/{allProcesses.length}]\n </Text>\n )}\n </Box>\n )}\n\n {/* Visible processes */}\n <Box flexDirection=\"column\">\n {visibleProcesses.map((item) => {\n const originalIndex = processes.indexOf(item);\n return (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && originalIndex === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={store.getProcessLines(item.id)} scrollOffset={scrollOffset} />}\n </Box>\n );\n })}\n {/* List scroll hint (interactive mode without expansion) */}\n {mode === 'interactive' && !expandedId && processes.length > visibleProcessCount && (\n <Text dimColor>\n [+{processes.length - visibleProcessCount} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom]\n </Text>\n )}\n </Box>\n\n {/* Status bar */}\n {showStatusBar && processes.length > 0 && (\n <>\n <Divider />\n <StatusBar running={runningCount} done={doneCount} errors={errorCount} errorLines={errorLineCount} />\n </>\n )}\n\n {/* Error footer (non-interactive mode only) */}\n {!isInteractive && errorCount > 0 && <ErrorFooter errors={errorLines} isExpanded={errorFooterExpanded} />}\n </Box>\n );\n}\n\n// Wrapper component that provides store context\nexport default function App({ store }: AppProps): React.JSX.Element {\n return (\n <StoreContext.Provider value={store}>\n <AppContent store={store} />\n </StoreContext.Provider>\n );\n}\n"],"names":["Box","Text","useApp","useInput","useStdin","useStdout","useEffect","useMemo","useSyncExternalStore","EXPANDED_MAX_VISIBLE_LINES","StoreContext","CompactProcessLine","Divider","ErrorFooter","ExpandedOutput","StatusBar","isMac","process","platform","AppContent","store","exit","isRawModeSupported","stdout","terminalHeight","rows","allProcesses","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","filterMode","getFilterMode","searchTerm","getSearchTerm","isSearching","getIsSearching","_bufferVersion","getBufferVersion","processes","getFilteredProcesses","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","filterBarHeight","listHintHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","filterLabels","all","running","finished","failed","setMode","clampListViewport","input","key","escape","cancelSearch","return","confirmSearch","backspace","delete","updateSearchTerm","slice","ctrl","meta","toggleErrorFooter","baseReserved","visibleWhenExpanded","visibleWhenCollapsed","collapse","clearSearch","signalExit","toggleExpand","rightArrow","cycleFilterNext","leftArrow","cycleFilterPrev","startSearch","upArrow","scrollToTop","selectFirst","downArrow","scrollToBottom","selectLast","tab","shift","scrollPageUp","selectPageUp","scrollPageDown","selectPageDown","scrollDown","selectNext","scrollUp","selectPrev","isActive","visibleProcesses","showSelection","layoutKey","flexDirection","dimColor","color","bold","length","map","item","originalIndex","indexOf","isSelected","id","lines","getProcessLines","done","errors","isExpanded","App","Provider","value"],"mappings":"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"}
@@ -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,9 @@ 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;
11
15
  private header;
12
16
  private showStatusBar;
13
17
  private isInteractive;
@@ -42,6 +46,10 @@ export declare class ProcessStore {
42
46
  getListScrollOffset: () => number;
43
47
  getErrorFooterExpanded: () => boolean;
44
48
  getBufferVersion: () => number;
49
+ getFilterMode: () => FilterMode;
50
+ getSearchTerm: () => string;
51
+ getIsSearching: () => boolean;
52
+ getFilteredProcesses: () => ChildProcess[];
45
53
  getScrollOffset: () => number;
46
54
  getHeader: () => string | undefined;
47
55
  getShowStatusBar: () => boolean;
@@ -49,6 +57,13 @@ export declare class ProcessStore {
49
57
  isAllComplete: () => boolean;
50
58
  setMode(mode: Mode): void;
51
59
  getSelectedProcess(): ChildProcess | undefined;
60
+ cycleFilterNext(): void;
61
+ cycleFilterPrev(): void;
62
+ startSearch(): void;
63
+ updateSearchTerm(term: string): void;
64
+ cancelSearch(): void;
65
+ confirmSearch(): void;
66
+ clearSearch(): void;
52
67
  toggleErrorFooter(): void;
53
68
  expandErrorFooter(): void;
54
69
  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,56 @@ export class ProcessStore {
88
94
  this.notify();
89
95
  }
90
96
  getSelectedProcess() {
91
- return this.processes[this.listNav.position];
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();
92
147
  }
93
148
  // Error footer methods (for non-interactive mode)
94
149
  toggleErrorFooter() {
@@ -247,6 +302,9 @@ export class ProcessStore {
247
302
  this.listNav.reset();
248
303
  this.expandedId = null;
249
304
  this.errorFooterExpanded = false;
305
+ this.filterMode = 'all';
306
+ this.searchTerm = '';
307
+ this.isSearching = false;
250
308
  this.header = undefined;
251
309
  }
252
310
  // === INFRASTRUCTURE ===
@@ -264,6 +322,9 @@ export class ProcessStore {
264
322
  this.mode = 'normal';
265
323
  this.expandedId = null;
266
324
  this.errorFooterExpanded = false; // For non-interactive error footer
325
+ this.filterMode = 'all';
326
+ this.searchTerm = '';
327
+ this.isSearching = false;
267
328
  this.showStatusBar = false;
268
329
  this.isInteractive = false;
269
330
  // === INFRASTRUCTURE ===
@@ -306,6 +367,31 @@ export class ProcessStore {
306
367
  this.getListScrollOffset = ()=>this.listNav.viewportOffset;
307
368
  this.getErrorFooterExpanded = ()=>this.errorFooterExpanded;
308
369
  this.getBufferVersion = ()=>this.bufferVersion;
370
+ this.getFilterMode = ()=>this.filterMode;
371
+ this.getSearchTerm = ()=>this.searchTerm;
372
+ this.getIsSearching = ()=>this.isSearching;
373
+ // Get processes filtered by current filter mode and search term
374
+ this.getFilteredProcesses = ()=>{
375
+ let filtered = this.processes;
376
+ // Apply filter mode
377
+ switch(this.filterMode){
378
+ case 'running':
379
+ filtered = filtered.filter((p)=>p.state === 'running');
380
+ break;
381
+ case 'finished':
382
+ filtered = filtered.filter((p)=>p.state !== 'running');
383
+ break;
384
+ case 'failed':
385
+ filtered = filtered.filter((p)=>p.state === 'error');
386
+ break;
387
+ }
388
+ // Apply search term
389
+ if (this.searchTerm) {
390
+ const term = this.searchTerm.toLowerCase();
391
+ filtered = filtered.filter((p)=>p.title.toLowerCase().includes(term) || p.group && p.group.toLowerCase().includes(term));
392
+ }
393
+ return filtered;
394
+ };
309
395
  // Get scroll offset for expanded process (or 0 if none)
310
396
  this.getScrollOffset = ()=>{
311
397
  var _process_scrollNav;
@@ -327,8 +413,9 @@ export class ProcessStore {
327
413
  var _options_interactive;
328
414
  this.isInteractive = (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false;
329
415
  // Create list navigator with wrap-around behavior
416
+ // Uses filtered processes count so selection works correctly with filters
330
417
  this.listNav = createNavigator({
331
- getLength: ()=>this.processes.length,
418
+ getLength: ()=>this.getFilteredProcesses().length,
332
419
  wrap: true,
333
420
  onMove: ()=>this.notify()
334
421
  });
@@ -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\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\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 // 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.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","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","filtered","toLowerCase","getScrollOffset","getHeader","getShowStatusBar","getIsInteractive","every","getShouldExit","getExitCallback","interactive"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spawn-term",
3
- "version": "3.1.5",
3
+ "version": "3.2.0",
4
4
  "description": "Formats spawn with for terminal grouping",
5
5
  "keywords": [
6
6
  "spawn",