spawn-term 3.0.4 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/cjs/components/App.js +10 -47
  2. package/dist/cjs/components/App.js.map +1 -1
  3. package/dist/cjs/components/ErrorFooter.js +120 -0
  4. package/dist/cjs/components/ErrorFooter.js.map +1 -0
  5. package/dist/cjs/components/ExpandedOutput.js +0 -2
  6. package/dist/cjs/components/ExpandedOutput.js.map +1 -1
  7. package/dist/cjs/components/StatusBar.js +22 -33
  8. package/dist/cjs/components/StatusBar.js.map +1 -1
  9. package/dist/cjs/src/components/ErrorFooter.d.ts +11 -0
  10. package/dist/cjs/src/state/processStore.d.ts +9 -6
  11. package/dist/cjs/state/processStore.js +23 -20
  12. package/dist/cjs/state/processStore.js.map +1 -1
  13. package/dist/esm/components/App.js +10 -47
  14. package/dist/esm/components/App.js.map +1 -1
  15. package/dist/esm/components/ErrorFooter.js +95 -0
  16. package/dist/esm/components/ErrorFooter.js.map +1 -0
  17. package/dist/esm/components/ExpandedOutput.js +0 -2
  18. package/dist/esm/components/ExpandedOutput.js.map +1 -1
  19. package/dist/esm/components/StatusBar.js +22 -33
  20. package/dist/esm/components/StatusBar.js.map +1 -1
  21. package/dist/esm/src/components/ErrorFooter.d.ts +11 -0
  22. package/dist/esm/src/state/processStore.d.ts +9 -6
  23. package/dist/esm/state/processStore.js +19 -18
  24. package/dist/esm/state/processStore.js.map +1 -1
  25. package/package.json +1 -1
  26. package/dist/cjs/components/ErrorDetailModal.js +0 -115
  27. package/dist/cjs/components/ErrorDetailModal.js.map +0 -1
  28. package/dist/cjs/components/ErrorListModal.js +0 -135
  29. package/dist/cjs/components/ErrorListModal.js.map +0 -1
  30. package/dist/cjs/src/components/ErrorDetailModal.d.ts +0 -8
  31. package/dist/cjs/src/components/ErrorListModal.d.ts +0 -8
  32. package/dist/esm/components/ErrorDetailModal.js +0 -99
  33. package/dist/esm/components/ErrorDetailModal.js.map +0 -1
  34. package/dist/esm/components/ErrorListModal.js +0 -116
  35. package/dist/esm/components/ErrorListModal.js.map +0 -1
  36. package/dist/esm/src/components/ErrorDetailModal.d.ts +0 -8
  37. package/dist/esm/src/components/ErrorListModal.d.ts +0 -8
@@ -16,8 +16,7 @@ var _constantsts = require("../constants.js");
16
16
  var _StoreContextts = require("../state/StoreContext.js");
17
17
  var _CompactProcessLinets = /*#__PURE__*/ _interop_require_default(require("./CompactProcessLine.js"));
18
18
  var _Dividerts = /*#__PURE__*/ _interop_require_default(require("./Divider.js"));
19
- var _ErrorDetailModalts = /*#__PURE__*/ _interop_require_default(require("./ErrorDetailModal.js"));
20
- var _ErrorListModalts = /*#__PURE__*/ _interop_require_default(require("./ErrorListModal.js"));
19
+ var _ErrorFooterts = /*#__PURE__*/ _interop_require_default(require("./ErrorFooter.js"));
21
20
  var _ExpandedOutputts = /*#__PURE__*/ _interop_require_default(require("./ExpandedOutput.js"));
22
21
  var _StatusBarts = /*#__PURE__*/ _interop_require_default(require("./StatusBar.js"));
23
22
  function _interop_require_default(obj) {
@@ -36,10 +35,10 @@ function AppContent(param) {
36
35
  var shouldExit = (0, _react.useSyncExternalStore)(store.subscribe, store.getShouldExit);
37
36
  var mode = (0, _react.useSyncExternalStore)(store.subscribe, store.getMode);
38
37
  var selectedIndex = (0, _react.useSyncExternalStore)(store.subscribe, store.getSelectedIndex);
39
- var selectedErrorIndex = (0, _react.useSyncExternalStore)(store.subscribe, store.getSelectedErrorIndex);
40
38
  var expandedId = (0, _react.useSyncExternalStore)(store.subscribe, store.getExpandedId);
41
39
  var scrollOffset = (0, _react.useSyncExternalStore)(store.subscribe, store.getScrollOffset);
42
40
  var listScrollOffset = (0, _react.useSyncExternalStore)(store.subscribe, store.getListScrollOffset);
41
+ var errorFooterExpanded = (0, _react.useSyncExternalStore)(store.subscribe, store.getErrorFooterExpanded);
43
42
  // Subscribed state that triggers re-renders
44
43
  var header = (0, _react.useSyncExternalStore)(store.subscribe, store.getHeader);
45
44
  var showStatusBar = (0, _react.useSyncExternalStore)(store.subscribe, store.getShowStatusBar);
@@ -48,12 +47,12 @@ function AppContent(param) {
48
47
  var reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0);
49
48
  var visibleProcessCount = Math.max(1, terminalHeight - reservedLines);
50
49
  // Derived state (computed from processes which is already subscribed)
51
- var failedProcesses = store.getFailedProcesses();
52
50
  var runningCount = store.getRunningCount();
53
51
  var doneCount = store.getDoneCount();
54
52
  var errorCount = store.getErrorCount();
55
53
  var errorLineCount = store.getErrorLineCount();
56
54
  var isAllComplete = store.isAllComplete();
55
+ var errorLines = store.getErrorLines();
57
56
  // Handle exit signal
58
57
  (0, _react.useEffect)(function() {
59
58
  if (shouldExit) {
@@ -77,8 +76,9 @@ function AppContent(param) {
77
76
  // Keyboard handling (only active when raw mode is supported)
78
77
  (0, _ink.useInput)(function(input, key) {
79
78
  if (mode === 'normal') {
79
+ // In non-interactive mode, 'e' toggles error footer
80
80
  if (input === 'e' && errorCount > 0) {
81
- store.setMode('errorList');
81
+ store.toggleErrorFooter();
82
82
  }
83
83
  } else if (mode === 'interactive') {
84
84
  if (input === 'q' || key.escape) {
@@ -113,32 +113,12 @@ function AppContent(param) {
113
113
  } else {
114
114
  store.selectPrev(visibleProcessCount);
115
115
  }
116
- } else if (input === 'e' && errorCount > 0) {
117
- store.setMode('errorList');
118
- }
119
- } else if (mode === 'errorList') {
120
- if (key.escape) {
121
- store.setMode(isInteractive ? 'interactive' : 'normal');
122
- } else if (key.downArrow) {
123
- store.selectNextError();
124
- } else if (key.upArrow) {
125
- store.selectPrevError();
126
- } else if (key.return) {
127
- store.setMode('errorDetail');
128
- }
129
- } else if (mode === 'errorDetail') {
130
- if (key.escape) {
131
- store.setMode('errorList');
132
- } else if (key.downArrow) {
133
- store.selectNextError();
134
- } else if (key.upArrow) {
135
- store.selectPrevError();
136
116
  }
137
117
  }
138
118
  }, {
139
119
  isActive: isRawModeSupported === true
140
120
  });
141
- // Slice processes to visible viewport in interactive mode (must be before early returns)
121
+ // Slice processes to visible viewport in interactive mode
142
122
  var visibleProcesses = (0, _react.useMemo)(function() {
143
123
  if (mode === 'interactive') {
144
124
  return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);
@@ -150,27 +130,6 @@ function AppContent(param) {
150
130
  listScrollOffset,
151
131
  visibleProcessCount
152
132
  ]);
153
- // Error list modal
154
- if (mode === 'errorList') {
155
- return /*#__PURE__*/ (0, _jsxruntime.jsx)(_ErrorListModalts.default, {
156
- errors: failedProcesses,
157
- selectedIndex: selectedErrorIndex,
158
- totalErrorLines: errorLineCount
159
- });
160
- }
161
- // Error detail modal
162
- if (mode === 'errorDetail') {
163
- var selectedError = store.getSelectedError();
164
- if (selectedError) {
165
- return /*#__PURE__*/ (0, _jsxruntime.jsx)(_ErrorDetailModalts.default, {
166
- error: selectedError,
167
- currentIndex: selectedErrorIndex,
168
- totalErrors: failedProcesses.length
169
- });
170
- }
171
- // Fallback if no error selected
172
- store.setMode('errorList');
173
- }
174
133
  // Normal/Interactive view - render in original registration order
175
134
  var showSelection = mode === 'interactive';
176
135
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
@@ -213,6 +172,10 @@ function AppContent(param) {
213
172
  errorLines: errorLineCount
214
173
  })
215
174
  ]
175
+ }),
176
+ !isInteractive && errorCount > 0 && /*#__PURE__*/ (0, _jsxruntime.jsx)(_ErrorFooterts.default, {
177
+ errors: errorLines,
178
+ isExpanded: errorFooterExpanded
216
179
  })
217
180
  ]
218
181
  });
@@ -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 ErrorDetailModal from './ErrorDetailModal.ts';\nimport ErrorListModal from './ErrorListModal.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\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 selectedErrorIndex = useSyncExternalStore(store.subscribe, store.getSelectedErrorIndex);\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\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)\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from processes which is already subscribed)\n const failedProcesses = store.getFailedProcesses();\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\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode when all complete and interactive flag is set\n useEffect(() => {\n if (isAllComplete && isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isAllComplete, isInteractive, mode, store]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n if (mode === 'normal') {\n if (input === 'e' && errorCount > 0) {\n store.setMode('errorList');\n }\n } else if (mode === 'interactive') {\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse();\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand();\n } else if (key.downArrow) {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow) {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n } else if (input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n } else if (input === 'e' && errorCount > 0) {\n store.setMode('errorList');\n }\n } else if (mode === 'errorList') {\n if (key.escape) {\n store.setMode(isInteractive ? 'interactive' : 'normal');\n } else if (key.downArrow) {\n store.selectNextError();\n } else if (key.upArrow) {\n store.selectPrevError();\n } else if (key.return) {\n store.setMode('errorDetail');\n }\n } else if (mode === 'errorDetail') {\n if (key.escape) {\n store.setMode('errorList');\n } else if (key.downArrow) {\n store.selectNextError();\n } else if (key.upArrow) {\n store.selectPrevError();\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Slice processes to visible viewport in interactive mode (must be before early returns)\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 // Error list modal\n if (mode === 'errorList') {\n return <ErrorListModal errors={failedProcesses} selectedIndex={selectedErrorIndex} totalErrorLines={errorLineCount} />;\n }\n\n // Error detail modal\n if (mode === 'errorDetail') {\n const selectedError = store.getSelectedError();\n if (selectedError) {\n return <ErrorDetailModal error={selectedError} currentIndex={selectedErrorIndex} totalErrors={failedProcesses.length} />;\n }\n // Fallback if no error selected\n store.setMode('errorList');\n }\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n\n return (\n <Box flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Visible processes - key forces clean re-render on scroll */}\n <Box key={`processes-${listScrollOffset}`} 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={item.lines} scrollOffset={scrollOffset} />}\n </Box>\n );\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 </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","AppContent","store","exit","useApp","isRawModeSupported","useStdin","stdout","useStdout","terminalHeight","rows","processes","useSyncExternalStore","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","selectedErrorIndex","getSelectedErrorIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","reservedLines","visibleProcessCount","Math","max","failedProcesses","getFailedProcesses","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","isAllComplete","useEffect","setMode","useInput","input","key","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","EXPANDED_MAX_VISIBLE_LINES","selectNext","upArrow","scrollUp","selectPrev","selectNextError","selectPrevError","isActive","visibleProcesses","useMemo","slice","ErrorListModal","errors","totalErrorLines","selectedError","getSelectedError","ErrorDetailModal","error","currentIndex","totalErrors","length","showSelection","Box","flexDirection","Text","Divider","map","item","originalIndex","indexOf","CompactProcessLine","isSelected","id","ExpandedOutput","lines","StatusBar","running","done","errorLines","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\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\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)\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\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 when all complete and interactive flag is set\n useEffect(() => {\n if (isAllComplete && isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isAllComplete, isInteractive, mode, 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 if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse();\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand();\n } else if (key.downArrow) {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow) {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n } else if (input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (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 return (\n <Box flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Visible processes - key forces clean re-render on scroll */}\n <Box key={`processes-${listScrollOffset}`} 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={item.lines} scrollOffset={scrollOffset} />}\n </Box>\n );\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","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","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","isAllComplete","errorLines","getErrorLines","useEffect","setMode","useInput","input","key","toggleErrorFooter","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","EXPANDED_MAX_VISIBLE_LINES","selectNext","upArrow","scrollUp","selectPrev","isActive","visibleProcesses","useMemo","slice","showSelection","Box","flexDirection","Text","Divider","map","item","originalIndex","indexOf","CompactProcessLine","isSelected","id","ExpandedOutput","lines","length","StatusBar","running","done","errors","ErrorFooter","isExpanded","StoreContext","Provider","value"],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "default", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return _default;
9
+ }
10
+ });
11
+ var _jsxruntime = require("react/jsx-runtime");
12
+ var _ink = require("ink");
13
+ var _react = require("react");
14
+ var _typests = require("../types.js");
15
+ var _Dividerts = /*#__PURE__*/ _interop_require_default(require("./Divider.js"));
16
+ function _interop_require_default(obj) {
17
+ return obj && obj.__esModule ? obj : {
18
+ default: obj
19
+ };
20
+ }
21
+ var _default = /*#__PURE__*/ (0, _react.memo)(function ErrorFooter(param) {
22
+ var errors = param.errors, isExpanded = param.isExpanded;
23
+ // Calculate totals for collapsed summary
24
+ var totalLines = errors.reduce(function(sum, e) {
25
+ return sum + e.lines.filter(function(l) {
26
+ return l.type === _typests.LineType.stderr;
27
+ }).length;
28
+ }, 0);
29
+ var totalProcesses = errors.length;
30
+ if (totalProcesses === 0) {
31
+ return null;
32
+ }
33
+ var processText = totalProcesses === 1 ? 'process' : 'processes';
34
+ if (!isExpanded) {
35
+ // Collapsed view - single summary line
36
+ var summary = totalLines > 0 ? "".concat(totalLines, " error line").concat(totalLines === 1 ? '' : 's', " in ").concat(totalProcesses, " ").concat(processText) : "".concat(totalProcesses, " failed ").concat(processText);
37
+ return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
38
+ children: [
39
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {}),
40
+ /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
41
+ children: [
42
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
43
+ color: "red",
44
+ children: '\u25b8'
45
+ }),
46
+ " ".concat(summary, " "),
47
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
48
+ dimColor: true,
49
+ children: "[e]"
50
+ })
51
+ ]
52
+ })
53
+ ]
54
+ });
55
+ }
56
+ // Expanded view - show all error lines (or just process names if no stderr)
57
+ return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
58
+ children: [
59
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {}),
60
+ /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
61
+ children: [
62
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
63
+ color: "red",
64
+ children: '\u25be'
65
+ }),
66
+ ' Errors ',
67
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
68
+ dimColor: true,
69
+ children: "[e]"
70
+ })
71
+ ]
72
+ }),
73
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Box, {
74
+ flexDirection: "column",
75
+ children: errors.map(function(errorGroup) {
76
+ var stderrLines = errorGroup.lines.filter(function(line) {
77
+ return line.type === _typests.LineType.stderr;
78
+ });
79
+ if (stderrLines.length === 0) {
80
+ // No stderr output - just show process name
81
+ return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
82
+ children: [
83
+ /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
84
+ dimColor: true,
85
+ children: [
86
+ "[",
87
+ errorGroup.processName,
88
+ "]"
89
+ ]
90
+ }),
91
+ " ",
92
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
93
+ color: "red",
94
+ children: "(failed)"
95
+ })
96
+ ]
97
+ }, errorGroup.processName);
98
+ }
99
+ return stderrLines.map(function(line, index) {
100
+ return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
101
+ children: [
102
+ /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
103
+ dimColor: true,
104
+ children: [
105
+ "[",
106
+ errorGroup.processName,
107
+ "]"
108
+ ]
109
+ }),
110
+ " ",
111
+ line.text
112
+ ]
113
+ }, "".concat(errorGroup.processName, "-").concat(index));
114
+ });
115
+ })
116
+ })
117
+ ]
118
+ });
119
+ });
120
+ /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ErrorFooter.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport type { Line } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport Divider from './Divider.ts';\n\ntype ErrorGroup = {\n processName: string;\n lines: Line[];\n};\n\ntype Props = {\n errors: ErrorGroup[];\n isExpanded: boolean;\n};\n\nexport default memo(function ErrorFooter({ errors, isExpanded }: Props) {\n // Calculate totals for collapsed summary\n const totalLines = errors.reduce((sum, e) => sum + e.lines.filter((l) => l.type === LineType.stderr).length, 0);\n const totalProcesses = errors.length;\n\n if (totalProcesses === 0) {\n return null;\n }\n\n const processText = totalProcesses === 1 ? 'process' : 'processes';\n\n if (!isExpanded) {\n // Collapsed view - single summary line\n const summary = totalLines > 0 ? `${totalLines} error line${totalLines === 1 ? '' : 's'} in ${totalProcesses} ${processText}` : `${totalProcesses} failed ${processText}`;\n return (\n <>\n <Divider />\n <Text>\n <Text color=\"red\">{'\\u25b8'}</Text>\n {` ${summary} `}\n <Text dimColor>[e]</Text>\n </Text>\n </>\n );\n }\n\n // Expanded view - show all error lines (or just process names if no stderr)\n return (\n <>\n <Divider />\n <Text>\n <Text color=\"red\">{'\\u25be'}</Text>\n {' Errors '}\n <Text dimColor>[e]</Text>\n </Text>\n <Box flexDirection=\"column\">\n {errors.map((errorGroup) => {\n const stderrLines = errorGroup.lines.filter((line) => line.type === LineType.stderr);\n if (stderrLines.length === 0) {\n // No stderr output - just show process name\n return (\n <Text key={errorGroup.processName}>\n <Text dimColor>[{errorGroup.processName}]</Text> <Text color=\"red\">(failed)</Text>\n </Text>\n );\n }\n return stderrLines.map((line, index) => (\n <Text key={`${errorGroup.processName}-${index}`}>\n <Text dimColor>[{errorGroup.processName}]</Text> {line.text}\n </Text>\n ));\n })}\n </Box>\n </>\n );\n});\n"],"names":["memo","ErrorFooter","errors","isExpanded","totalLines","reduce","sum","e","lines","filter","l","type","LineType","stderr","length","totalProcesses","processText","summary","Divider","Text","color","dimColor","Box","flexDirection","map","errorGroup","stderrLines","line","processName","index","text"],"mappings":";;;;+BAgBA;;;eAAA;;;;mBAhB0B;qBACL;uBAEI;gEACL;;;;;;IAYpB,yBAAeA,IAAAA,WAAI,EAAC,SAASC,YAAY,KAA6B;QAA3BC,SAAF,MAAEA,QAAQC,aAAV,MAAUA;IACjD,yCAAyC;IACzC,IAAMC,aAAaF,OAAOG,MAAM,CAAC,SAACC,KAAKC;eAAMD,MAAMC,EAAEC,KAAK,CAACC,MAAM,CAAC,SAACC;mBAAMA,EAAEC,IAAI,KAAKC,iBAAQ,CAACC,MAAM;WAAEC,MAAM;OAAE;IAC7G,IAAMC,iBAAiBb,OAAOY,MAAM;IAEpC,IAAIC,mBAAmB,GAAG;QACxB,OAAO;IACT;IAEA,IAAMC,cAAcD,mBAAmB,IAAI,YAAY;IAEvD,IAAI,CAACZ,YAAY;QACf,uCAAuC;QACvC,IAAMc,UAAUb,aAAa,IAAI,AAAC,GAA0BA,OAAxBA,YAAW,eAA+CW,OAAlCX,eAAe,IAAI,KAAK,KAAI,QAAwBY,OAAlBD,gBAAe,KAAe,OAAZC,eAAgB,AAAC,GAA2BA,OAAzBD,gBAAe,YAAsB,OAAZC;QAC5J,qBACE;;8BACE,qBAACE,kBAAO;8BACR,sBAACC,SAAI;;sCACH,qBAACA,SAAI;4BAACC,OAAM;sCAAO;;wBACjB,IAAW,OAARH,SAAQ;sCACb,qBAACE,SAAI;4BAACE,QAAQ;sCAAC;;;;;;IAIvB;IAEA,4EAA4E;IAC5E,qBACE;;0BACE,qBAACH,kBAAO;0BACR,sBAACC,SAAI;;kCACH,qBAACA,SAAI;wBAACC,OAAM;kCAAO;;oBAClB;kCACD,qBAACD,SAAI;wBAACE,QAAQ;kCAAC;;;;0BAEjB,qBAACC,QAAG;gBAACC,eAAc;0BAChBrB,OAAOsB,GAAG,CAAC,SAACC;oBACX,IAAMC,cAAcD,WAAWjB,KAAK,CAACC,MAAM,CAAC,SAACkB;+BAASA,KAAKhB,IAAI,KAAKC,iBAAQ,CAACC,MAAM;;oBACnF,IAAIa,YAAYZ,MAAM,KAAK,GAAG;wBAC5B,4CAA4C;wBAC5C,qBACE,sBAACK,SAAI;;8CACH,sBAACA,SAAI;oCAACE,QAAQ;;wCAAC;wCAAEI,WAAWG,WAAW;wCAAC;;;gCAAQ;8CAAC,qBAACT,SAAI;oCAACC,OAAM;8CAAM;;;2BAD1DK,WAAWG,WAAW;oBAIrC;oBACA,OAAOF,YAAYF,GAAG,CAAC,SAACG,MAAME;6CAC5B,sBAACV,SAAI;;8CACH,sBAACA,SAAI;oCAACE,QAAQ;;wCAAC;wCAAEI,WAAWG,WAAW;wCAAC;;;gCAAQ;gCAAED,KAAKG,IAAI;;2BADlD,AAAC,GAA4BD,OAA1BJ,WAAWG,WAAW,EAAC,KAAS,OAANC;;gBAI5C;;;;AAIR"}
@@ -12,7 +12,6 @@ var _jsxruntime = require("react/jsx-runtime");
12
12
  var _ink = require("ink");
13
13
  var _react = require("react");
14
14
  var _constantsts = require("../constants.js");
15
- var _typests = require("../types.js");
16
15
  var _default = /*#__PURE__*/ (0, _react.memo)(function ExpandedOutput(param) {
17
16
  var lines = param.lines, scrollOffset = param.scrollOffset, _param_maxVisible = param.maxVisible, maxVisible = _param_maxVisible === void 0 ? _constantsts.EXPANDED_MAX_VISIBLE_LINES : _param_maxVisible;
18
17
  var visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);
@@ -34,7 +33,6 @@ var _default = /*#__PURE__*/ (0, _react.memo)(function ExpandedOutput(param) {
34
33
  visibleLines.map(function(line, i) {
35
34
  return(// biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view
36
35
  /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
37
- color: line.type === _typests.LineType.stderr ? 'red' : undefined,
38
36
  children: [
39
37
  "│ ",
40
38
  line.text
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ExpandedOutput.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { Line } from '../types.ts';\nimport { LineType } from '../types.ts';\n\ntype Props = {\n lines: Line[];\n scrollOffset: number;\n maxVisible?: number;\n};\n\nexport default memo(function ExpandedOutput({ lines, scrollOffset, maxVisible = EXPANDED_MAX_VISIBLE_LINES }: Props) {\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const hasMore = lines.length > scrollOffset + maxVisible;\n const remaining = lines.length - scrollOffset - maxVisible;\n\n if (lines.length === 0) {\n return (\n <Box paddingLeft={2}>\n <Text dimColor>│ (no output)</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n {visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i} color={line.type === LineType.stderr ? 'red' : undefined}>\n │ {line.text}\n </Text>\n ))}\n {hasMore && <Text dimColor>│ [+{remaining} more, j/k to scroll]</Text>}\n </Box>\n );\n});\n"],"names":["memo","ExpandedOutput","lines","scrollOffset","maxVisible","EXPANDED_MAX_VISIBLE_LINES","visibleLines","slice","hasMore","length","remaining","Box","paddingLeft","Text","dimColor","flexDirection","map","line","i","color","type","LineType","stderr","undefined","text"],"mappings":";;;;+BAYA;;;eAAA;;;;mBAZ0B;qBACL;2BACsB;uBAElB;IAQzB,yBAAeA,IAAAA,WAAI,EAAC,SAASC,eAAe,KAAuE;QAArEC,QAAF,MAAEA,OAAOC,eAAT,MAASA,kCAAT,MAAuBC,YAAAA,4CAAaC,uCAA0B;IACxG,IAAMC,eAAeJ,MAAMK,KAAK,CAACJ,cAAcA,eAAeC;IAC9D,IAAMI,UAAUN,MAAMO,MAAM,GAAGN,eAAeC;IAC9C,IAAMM,YAAYR,MAAMO,MAAM,GAAGN,eAAeC;IAEhD,IAAIF,MAAMO,MAAM,KAAK,GAAG;QACtB,qBACE,qBAACE,QAAG;YAACC,aAAa;sBAChB,cAAA,qBAACC,SAAI;gBAACC,QAAQ;0BAAC;;;IAGrB;IAEA,qBACE,sBAACH,QAAG;QAACI,eAAc;QAASH,aAAa;;YACtCN,aAAaU,GAAG,CAAC,SAACC,MAAMC;uBACvB,iHAAiH;8BACjH,sBAACL,SAAI;oBAAwBM,OAAOF,KAAKG,IAAI,KAAKC,iBAAQ,CAACC,MAAM,GAAG,QAAQC;;wBAAW;wBAClFN,KAAKO,IAAI;;mBADHrB,eAAee;;YAI3BV,yBAAW,sBAACK,SAAI;gBAACC,QAAQ;;oBAAC;oBAAKJ;oBAAU;;;;;AAGhD"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ExpandedOutput.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { Line } from '../types.ts';\n\ntype Props = {\n lines: Line[];\n scrollOffset: number;\n maxVisible?: number;\n};\n\nexport default memo(function ExpandedOutput({ lines, scrollOffset, maxVisible = EXPANDED_MAX_VISIBLE_LINES }: Props) {\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const hasMore = lines.length > scrollOffset + maxVisible;\n const remaining = lines.length - scrollOffset - maxVisible;\n\n if (lines.length === 0) {\n return (\n <Box paddingLeft={2}>\n <Text dimColor>│ (no output)</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n {visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>│ {line.text}</Text>\n ))}\n {hasMore && <Text dimColor>│ [+{remaining} more, j/k to scroll]</Text>}\n </Box>\n );\n});\n"],"names":["memo","ExpandedOutput","lines","scrollOffset","maxVisible","EXPANDED_MAX_VISIBLE_LINES","visibleLines","slice","hasMore","length","remaining","Box","paddingLeft","Text","dimColor","flexDirection","map","line","i","text"],"mappings":";;;;+BAWA;;;eAAA;;;;mBAX0B;qBACL;2BACsB;IAS3C,yBAAeA,IAAAA,WAAI,EAAC,SAASC,eAAe,KAAuE;QAArEC,QAAF,MAAEA,OAAOC,eAAT,MAASA,kCAAT,MAAuBC,YAAAA,4CAAaC,uCAA0B;IACxG,IAAMC,eAAeJ,MAAMK,KAAK,CAACJ,cAAcA,eAAeC;IAC9D,IAAMI,UAAUN,MAAMO,MAAM,GAAGN,eAAeC;IAC9C,IAAMM,YAAYR,MAAMO,MAAM,GAAGN,eAAeC;IAEhD,IAAIF,MAAMO,MAAM,KAAK,GAAG;QACtB,qBACE,qBAACE,QAAG;YAACC,aAAa;sBAChB,cAAA,qBAACC,SAAI;gBAACC,QAAQ;0BAAC;;;IAGrB;IAEA,qBACE,sBAACH,QAAG;QAACI,eAAc;QAASH,aAAa;;YACtCN,aAAaU,GAAG,CAAC,SAACC,MAAMC;uBACvB,iHAAiH;8BACjH,sBAACL,SAAI;;wBAAwB;wBAAGI,KAAKE,IAAI;;mBAA9BhB,eAAee;;YAE3BV,yBAAW,sBAACK,SAAI;gBAACC,QAAQ;;oBAAC;oBAAKJ;oBAAU;;;;;AAGhD"}
@@ -49,41 +49,30 @@ function _object_spread(target) {
49
49
  }
50
50
  var _default = /*#__PURE__*/ (0, _react.memo)(function StatusBar(param) {
51
51
  var running = param.running, done = param.done, errors = param.errors, errorLines = param.errorLines;
52
- return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
53
- justifyContent: "space-between",
54
- children: [
55
- /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Box, {
56
- children: /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
57
- children: [
58
- running > 0 ? /*#__PURE__*/ (0, _jsxruntime.jsx)(_Spinnerts.default, _object_spread({}, _constantsts.SPINNER)) : /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
59
- color: "green",
60
- children: _figurests.default.tick
61
- }),
62
- " Running: ".concat(running, " | "),
63
- /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
64
- color: "green",
65
- children: _figurests.default.tick
66
- }),
67
- " Done: ".concat(done, " | "),
68
- /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
69
- color: "red",
70
- children: _figurests.default.cross
71
- }),
72
- " Errors: ".concat(errors),
73
- errorLines > 0 && /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
74
- dimColor: true,
75
- children: " (".concat(errorLines, " lines)")
76
- })
77
- ]
78
- })
79
- }),
80
- errors > 0 && /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Box, {
81
- children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
52
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Box, {
53
+ children: /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
54
+ children: [
55
+ running > 0 ? /*#__PURE__*/ (0, _jsxruntime.jsx)(_Spinnerts.default, _object_spread({}, _constantsts.SPINNER)) : /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
56
+ color: "green",
57
+ children: _figurests.default.tick
58
+ }),
59
+ " Running: ".concat(running, " | "),
60
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
61
+ color: "green",
62
+ children: _figurests.default.tick
63
+ }),
64
+ " Done: ".concat(done, " | "),
65
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
66
+ color: "red",
67
+ children: _figurests.default.cross
68
+ }),
69
+ " Errors: ".concat(errors),
70
+ errorLines > 0 && /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
82
71
  dimColor: true,
83
- children: "[e]rrors"
72
+ children: " (".concat(errorLines, " lines)")
84
73
  })
85
- })
86
- ]
74
+ ]
75
+ })
87
76
  });
88
77
  });
89
78
  /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/StatusBar.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport { SPINNER } from '../constants.ts';\nimport figures from '../lib/figures.ts';\nimport Spinner from './Spinner.ts';\n\ntype Props = {\n running: number;\n done: number;\n errors: number;\n errorLines: number;\n};\n\nexport default memo(function StatusBar({ running, done, errors, errorLines }: Props) {\n return (\n <Box justifyContent=\"space-between\">\n <Box>\n <Text>\n {running > 0 ? <Spinner {...SPINNER} /> : <Text color=\"green\">{figures.tick}</Text>}\n {` Running: ${running} | `}\n <Text color=\"green\">{figures.tick}</Text>\n {` Done: ${done} | `}\n <Text color=\"red\">{figures.cross}</Text>\n {` Errors: ${errors}`}\n {errorLines > 0 && <Text dimColor>{` (${errorLines} lines)`}</Text>}\n </Text>\n </Box>\n {errors > 0 && (\n <Box>\n <Text dimColor>[e]rrors</Text>\n </Box>\n )}\n </Box>\n );\n});\n"],"names":["memo","StatusBar","running","done","errors","errorLines","Box","justifyContent","Text","Spinner","SPINNER","color","figures","tick","cross","dimColor"],"mappings":";;;;+BAaA;;;eAAA;;;;mBAb0B;qBACL;2BACG;gEACJ;gEACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IASpB,yBAAeA,IAAAA,WAAI,EAAC,SAASC,UAAU,KAA4C;QAA1CC,UAAF,MAAEA,SAASC,OAAX,MAAWA,MAAMC,SAAjB,MAAiBA,QAAQC,aAAzB,MAAyBA;IAC9D,qBACE,sBAACC,QAAG;QAACC,gBAAe;;0BAClB,qBAACD,QAAG;0BACF,cAAA,sBAACE,SAAI;;wBACFN,UAAU,kBAAI,qBAACO,kBAAO,qBAAKC,oBAAO,mBAAO,qBAACF,SAAI;4BAACG,OAAM;sCAASC,kBAAO,CAACC,IAAI;;wBACzE,aAAoB,OAARX,SAAQ;sCACtB,qBAACM,SAAI;4BAACG,OAAM;sCAASC,kBAAO,CAACC,IAAI;;wBAC/B,UAAc,OAALV,MAAK;sCAChB,qBAACK,SAAI;4BAACG,OAAM;sCAAOC,kBAAO,CAACE,KAAK;;wBAC9B,YAAkB,OAAPV;wBACZC,aAAa,mBAAK,qBAACG,SAAI;4BAACO,QAAQ;sCAAE,AAAC,KAAe,OAAXV,YAAW;;;;;YAGtDD,SAAS,mBACR,qBAACE,QAAG;0BACF,cAAA,qBAACE,SAAI;oBAACO,QAAQ;8BAAC;;;;;AAKzB"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/StatusBar.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport { SPINNER } from '../constants.ts';\nimport figures from '../lib/figures.ts';\nimport Spinner from './Spinner.ts';\n\ntype Props = {\n running: number;\n done: number;\n errors: number;\n errorLines: number;\n};\n\nexport default memo(function StatusBar({ running, done, errors, errorLines }: Props) {\n return (\n <Box>\n <Text>\n {running > 0 ? <Spinner {...SPINNER} /> : <Text color=\"green\">{figures.tick}</Text>}\n {` Running: ${running} | `}\n <Text color=\"green\">{figures.tick}</Text>\n {` Done: ${done} | `}\n <Text color=\"red\">{figures.cross}</Text>\n {` Errors: ${errors}`}\n {errorLines > 0 && <Text dimColor>{` (${errorLines} lines)`}</Text>}\n </Text>\n </Box>\n );\n});\n"],"names":["memo","StatusBar","running","done","errors","errorLines","Box","Text","Spinner","SPINNER","color","figures","tick","cross","dimColor"],"mappings":";;;;+BAaA;;;eAAA;;;;mBAb0B;qBACL;2BACG;gEACJ;gEACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IASpB,yBAAeA,IAAAA,WAAI,EAAC,SAASC,UAAU,KAA4C;QAA1CC,UAAF,MAAEA,SAASC,OAAX,MAAWA,MAAMC,SAAjB,MAAiBA,QAAQC,aAAzB,MAAyBA;IAC9D,qBACE,qBAACC,QAAG;kBACF,cAAA,sBAACC,SAAI;;gBACFL,UAAU,kBAAI,qBAACM,kBAAO,qBAAKC,oBAAO,mBAAO,qBAACF,SAAI;oBAACG,OAAM;8BAASC,kBAAO,CAACC,IAAI;;gBACzE,aAAoB,OAARV,SAAQ;8BACtB,qBAACK,SAAI;oBAACG,OAAM;8BAASC,kBAAO,CAACC,IAAI;;gBAC/B,UAAc,OAALT,MAAK;8BAChB,qBAACI,SAAI;oBAACG,OAAM;8BAAOC,kBAAO,CAACE,KAAK;;gBAC9B,YAAkB,OAAPT;gBACZC,aAAa,mBAAK,qBAACE,SAAI;oBAACO,QAAQ;8BAAE,AAAC,KAAe,OAAXT,YAAW;;;;;AAI3D"}
@@ -0,0 +1,11 @@
1
+ import type { Line } from '../types.js';
2
+ type ErrorGroup = {
3
+ processName: string;
4
+ lines: Line[];
5
+ };
6
+ type Props = {
7
+ errors: ErrorGroup[];
8
+ isExpanded: boolean;
9
+ };
10
+ declare const _default: import("react").NamedExoticComponent<Props>;
11
+ export default _default;
@@ -1,6 +1,6 @@
1
1
  import type { ChildProcess, Line, SessionOptions } from '../types.js';
2
2
  type Listener = () => void;
3
- type Mode = 'normal' | 'interactive' | 'errorList' | 'errorDetail';
3
+ type Mode = 'normal' | 'interactive';
4
4
  export declare class ProcessStore {
5
5
  private processes;
6
6
  private completedIds;
@@ -9,10 +9,10 @@ export declare class ProcessStore {
9
9
  private exitCallback;
10
10
  private mode;
11
11
  private selectedIndex;
12
- private selectedErrorIndex;
13
12
  private expandedId;
14
13
  private scrollOffset;
15
14
  private listScrollOffset;
15
+ private errorFooterExpanded;
16
16
  private header;
17
17
  private showStatusBar;
18
18
  private isInteractive;
@@ -29,10 +29,10 @@ export declare class ProcessStore {
29
29
  getErrorLineCount: () => number;
30
30
  getMode: () => Mode;
31
31
  getSelectedIndex: () => number;
32
- getSelectedErrorIndex: () => number;
33
32
  getExpandedId: () => string | null;
34
33
  getScrollOffset: () => number;
35
34
  getListScrollOffset: () => number;
35
+ getErrorFooterExpanded: () => boolean;
36
36
  getHeader: () => string | undefined;
37
37
  getShowStatusBar: () => boolean;
38
38
  getIsInteractive: () => boolean;
@@ -46,9 +46,12 @@ export declare class ProcessStore {
46
46
  selectPrev(visibleCount?: number): void;
47
47
  private adjustListScroll;
48
48
  getSelectedProcess(): ChildProcess | undefined;
49
- selectNextError(): void;
50
- selectPrevError(): void;
51
- getSelectedError(): ChildProcess | undefined;
49
+ toggleErrorFooter(): void;
50
+ expandErrorFooter(): void;
51
+ getErrorLines(): Array<{
52
+ processName: string;
53
+ lines: Line[];
54
+ }>;
52
55
  toggleExpand(): void;
53
56
  collapse(): void;
54
57
  scrollDown(maxVisible: number): void;
@@ -82,10 +82,10 @@ var ProcessStore = /*#__PURE__*/ function() {
82
82
  // UI state
83
83
  this.mode = 'normal';
84
84
  this.selectedIndex = 0;
85
- this.selectedErrorIndex = 0;
86
85
  this.expandedId = null;
87
86
  this.scrollOffset = 0;
88
87
  this.listScrollOffset = 0; // Viewport offset for process list
88
+ this.errorFooterExpanded = false; // For non-interactive error footer
89
89
  this.showStatusBar = false;
90
90
  this.isInteractive = false;
91
91
  // useSyncExternalStore API
@@ -158,9 +158,6 @@ var ProcessStore = /*#__PURE__*/ function() {
158
158
  this.getSelectedIndex = function() {
159
159
  return _this.selectedIndex;
160
160
  };
161
- this.getSelectedErrorIndex = function() {
162
- return _this.selectedErrorIndex;
163
- };
164
161
  this.getExpandedId = function() {
165
162
  return _this.expandedId;
166
163
  };
@@ -170,6 +167,9 @@ var ProcessStore = /*#__PURE__*/ function() {
170
167
  this.getListScrollOffset = function() {
171
168
  return _this.listScrollOffset;
172
169
  };
170
+ this.getErrorFooterExpanded = function() {
171
+ return _this.errorFooterExpanded;
172
+ };
173
173
  // Session-level getters (set at session creation, immutable)
174
174
  this.getHeader = function() {
175
175
  return _this.header;
@@ -220,6 +220,10 @@ var ProcessStore = /*#__PURE__*/ function() {
220
220
  id
221
221
  ]);
222
222
  }
223
+ // Auto-expand error footer when all complete with errors (non-interactive only)
224
+ if (!this.isInteractive && this.isAllComplete() && this.getErrorCount() > 0) {
225
+ this.errorFooterExpanded = true;
226
+ }
223
227
  this.notify();
224
228
  };
225
229
  _proto.appendLines = function appendLines(id, newLines) {
@@ -242,8 +246,6 @@ var ProcessStore = /*#__PURE__*/ function() {
242
246
  this.mode = mode;
243
247
  if (mode === 'interactive') {
244
248
  this.selectedIndex = 0;
245
- } else if (mode === 'errorList') {
246
- this.selectedErrorIndex = 0;
247
249
  }
248
250
  this.notify();
249
251
  };
@@ -276,23 +278,24 @@ var ProcessStore = /*#__PURE__*/ function() {
276
278
  _proto.getSelectedProcess = function getSelectedProcess() {
277
279
  return this.processes[this.selectedIndex];
278
280
  };
279
- _proto.selectNextError = function selectNextError() {
280
- var failed = this.getFailedProcesses();
281
- if (failed.length > 0) {
282
- this.selectedErrorIndex = (this.selectedErrorIndex + 1) % failed.length;
283
- this.notify();
284
- }
281
+ // Error footer methods (for non-interactive mode)
282
+ _proto.toggleErrorFooter = function toggleErrorFooter() {
283
+ this.errorFooterExpanded = !this.errorFooterExpanded;
284
+ this.notify();
285
285
  };
286
- _proto.selectPrevError = function selectPrevError() {
287
- var failed = this.getFailedProcesses();
288
- if (failed.length > 0) {
289
- this.selectedErrorIndex = (this.selectedErrorIndex - 1 + failed.length) % failed.length;
286
+ _proto.expandErrorFooter = function expandErrorFooter() {
287
+ if (!this.errorFooterExpanded) {
288
+ this.errorFooterExpanded = true;
290
289
  this.notify();
291
290
  }
292
291
  };
293
- _proto.getSelectedError = function getSelectedError() {
294
- var failed = this.getFailedProcesses();
295
- return failed[this.selectedErrorIndex];
292
+ _proto.getErrorLines = function getErrorLines() {
293
+ return this.getFailedProcesses().map(function(p) {
294
+ return {
295
+ processName: p.group || p.title,
296
+ lines: p.lines
297
+ };
298
+ });
296
299
  };
297
300
  // Expansion methods
298
301
  _proto.toggleExpand = function toggleExpand() {
@@ -344,10 +347,10 @@ var ProcessStore = /*#__PURE__*/ function() {
344
347
  this.exitCallback = null;
345
348
  this.mode = 'normal';
346
349
  this.selectedIndex = 0;
347
- this.selectedErrorIndex = 0;
348
350
  this.expandedId = null;
349
351
  this.scrollOffset = 0;
350
352
  this.listScrollOffset = 0;
353
+ this.errorFooterExpanded = false;
351
354
  this.header = undefined;
352
355
  };
353
356
  _proto.notify = function notify() {