spawn-term 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/cjs/components/App.js +79 -16
  2. package/dist/cjs/components/App.js.map +1 -1
  3. package/dist/cjs/components/CompactProcessLine.js +12 -3
  4. package/dist/cjs/components/CompactProcessLine.js.map +1 -1
  5. package/dist/cjs/components/ExpandedOutput.js +55 -0
  6. package/dist/cjs/components/ExpandedOutput.js.map +1 -0
  7. package/dist/cjs/constants.js +42 -0
  8. package/dist/cjs/constants.js.map +1 -0
  9. package/dist/cjs/createApp.js +11 -11
  10. package/dist/cjs/createApp.js.map +1 -1
  11. package/dist/cjs/lib/addLines.js +58 -4
  12. package/dist/cjs/lib/addLines.js.map +1 -1
  13. package/dist/cjs/lib/format.js +21 -0
  14. package/dist/cjs/lib/format.js.map +1 -0
  15. package/dist/cjs/src/components/CompactProcessLine.d.ts +1 -0
  16. package/dist/cjs/src/components/ExpandedOutput.d.ts +8 -0
  17. package/dist/cjs/src/constants.d.ts +7 -0
  18. package/dist/cjs/src/lib/addLines.d.ts +6 -1
  19. package/dist/cjs/src/lib/format.d.ts +2 -0
  20. package/dist/cjs/src/state/processStore.d.ts +20 -1
  21. package/dist/cjs/src/types.d.ts +6 -0
  22. package/dist/cjs/state/processStore.js +105 -1
  23. package/dist/cjs/state/processStore.js.map +1 -1
  24. package/dist/cjs/types.js.map +1 -1
  25. package/dist/esm/components/App.js +79 -14
  26. package/dist/esm/components/App.js.map +1 -1
  27. package/dist/esm/components/CompactProcessLine.js +12 -3
  28. package/dist/esm/components/CompactProcessLine.js.map +1 -1
  29. package/dist/esm/components/ExpandedOutput.js +41 -0
  30. package/dist/esm/components/ExpandedOutput.js.map +1 -0
  31. package/dist/esm/constants.js +11 -0
  32. package/dist/esm/constants.js.map +1 -0
  33. package/dist/esm/createApp.js +11 -11
  34. package/dist/esm/createApp.js.map +1 -1
  35. package/dist/esm/lib/addLines.js +32 -5
  36. package/dist/esm/lib/addLines.js.map +1 -1
  37. package/dist/esm/lib/format.js +10 -0
  38. package/dist/esm/lib/format.js.map +1 -0
  39. package/dist/esm/src/components/CompactProcessLine.d.ts +1 -0
  40. package/dist/esm/src/components/ExpandedOutput.d.ts +8 -0
  41. package/dist/esm/src/constants.d.ts +7 -0
  42. package/dist/esm/src/lib/addLines.d.ts +6 -1
  43. package/dist/esm/src/lib/format.d.ts +2 -0
  44. package/dist/esm/src/state/processStore.d.ts +20 -1
  45. package/dist/esm/src/types.d.ts +6 -0
  46. package/dist/esm/state/processStore.js +82 -1
  47. package/dist/esm/state/processStore.js.map +1 -1
  48. package/dist/esm/types.js.map +1 -1
  49. package/package.json +1 -1
@@ -11,11 +11,13 @@ Object.defineProperty(exports, "default", {
11
11
  var _jsxruntime = require("react/jsx-runtime");
12
12
  var _ink = require("ink");
13
13
  var _react = require("react");
14
+ var _constantsts = require("../constants.js");
14
15
  var _processStorets = require("../state/processStore.js");
15
16
  var _CompactProcessLinets = /*#__PURE__*/ _interop_require_default(require("./CompactProcessLine.js"));
16
17
  var _Dividerts = /*#__PURE__*/ _interop_require_default(require("./Divider.js"));
17
18
  var _ErrorDetailModalts = /*#__PURE__*/ _interop_require_default(require("./ErrorDetailModal.js"));
18
19
  var _ErrorListModalts = /*#__PURE__*/ _interop_require_default(require("./ErrorListModal.js"));
20
+ var _ExpandedOutputts = /*#__PURE__*/ _interop_require_default(require("./ExpandedOutput.js"));
19
21
  var _StatusBarts = /*#__PURE__*/ _interop_require_default(require("./StatusBar.js"));
20
22
  function _interop_require_default(obj) {
21
23
  return obj && obj.__esModule ? obj : {
@@ -29,15 +31,20 @@ function App() {
29
31
  var processes = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getSnapshot);
30
32
  var shouldExit = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getShouldExit);
31
33
  var mode = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getMode);
34
+ var selectedIndex = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getSelectedIndex);
32
35
  var selectedErrorIndex = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getSelectedErrorIndex);
36
+ var expandedId = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getExpandedId);
37
+ var scrollOffset = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getScrollOffset);
33
38
  // Derived state
34
- var completedProcesses = _processStorets.processStore.getCompletedProcesses();
35
- var runningProcesses = _processStorets.processStore.getRunningProcesses();
36
39
  var failedProcesses = _processStorets.processStore.getFailedProcesses();
37
40
  var runningCount = _processStorets.processStore.getRunningCount();
38
41
  var doneCount = _processStorets.processStore.getDoneCount();
39
42
  var errorCount = _processStorets.processStore.getErrorCount();
40
43
  var errorLineCount = _processStorets.processStore.getErrorLineCount();
44
+ var header = _processStorets.processStore.getHeader();
45
+ var showStatusBar = _processStorets.processStore.getShowStatusBar();
46
+ var isInteractive = _processStorets.processStore.getIsInteractive();
47
+ var isAllComplete = _processStorets.processStore.isAllComplete();
41
48
  // Handle exit signal
42
49
  (0, _react.useEffect)(function() {
43
50
  if (shouldExit) {
@@ -47,15 +54,61 @@ function App() {
47
54
  shouldExit,
48
55
  exit
49
56
  ]);
57
+ // Auto-enter interactive mode when all complete and interactive flag is set
58
+ (0, _react.useEffect)(function() {
59
+ if (isAllComplete && isInteractive && mode === 'normal') {
60
+ _processStorets.processStore.setMode('interactive');
61
+ }
62
+ }, [
63
+ isAllComplete,
64
+ isInteractive,
65
+ mode
66
+ ]);
50
67
  // Keyboard handling (only active when raw mode is supported)
51
68
  (0, _ink.useInput)(function(input, key) {
52
69
  if (mode === 'normal') {
53
70
  if (input === 'e' && errorCount > 0) {
54
71
  _processStorets.processStore.setMode('errorList');
55
72
  }
73
+ } else if (mode === 'interactive') {
74
+ if (input === 'q' || key.escape) {
75
+ if (expandedId) {
76
+ _processStorets.processStore.collapse();
77
+ } else {
78
+ _processStorets.processStore.signalExit(function() {});
79
+ }
80
+ } else if (key.return) {
81
+ _processStorets.processStore.toggleExpand();
82
+ } else if (key.downArrow) {
83
+ if (expandedId) {
84
+ _processStorets.processStore.scrollDown(_constantsts.EXPANDED_MAX_VISIBLE_LINES);
85
+ } else {
86
+ _processStorets.processStore.selectNext();
87
+ }
88
+ } else if (key.upArrow) {
89
+ if (expandedId) {
90
+ _processStorets.processStore.scrollUp();
91
+ } else {
92
+ _processStorets.processStore.selectPrev();
93
+ }
94
+ } else if (input === 'j') {
95
+ if (expandedId) {
96
+ _processStorets.processStore.scrollDown(_constantsts.EXPANDED_MAX_VISIBLE_LINES);
97
+ } else {
98
+ _processStorets.processStore.selectNext();
99
+ }
100
+ } else if (input === 'k') {
101
+ if (expandedId) {
102
+ _processStorets.processStore.scrollUp();
103
+ } else {
104
+ _processStorets.processStore.selectPrev();
105
+ }
106
+ } else if (input === 'e' && errorCount > 0) {
107
+ _processStorets.processStore.setMode('errorList');
108
+ }
56
109
  } else if (mode === 'errorList') {
57
110
  if (key.escape) {
58
- _processStorets.processStore.setMode('normal');
111
+ _processStorets.processStore.setMode(isInteractive ? 'interactive' : 'normal');
59
112
  } else if (key.downArrow) {
60
113
  _processStorets.processStore.selectNextError();
61
114
  } else if (key.upArrow) {
@@ -96,25 +149,35 @@ function App() {
96
149
  // Fallback if no error selected
97
150
  _processStorets.processStore.setMode('errorList');
98
151
  }
99
- // Normal view
152
+ // Normal/Interactive view - render in original registration order
153
+ var showSelection = mode === 'interactive';
100
154
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
101
155
  flexDirection: "column",
102
156
  children: [
103
- /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Static, {
104
- items: completedProcesses,
105
- children: function(item) {
106
- return /*#__PURE__*/ (0, _jsxruntime.jsx)(_CompactProcessLinets.default, {
107
- item: item
108
- }, item.id);
109
- }
157
+ header && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
158
+ children: [
159
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
160
+ children: header
161
+ }),
162
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {})
163
+ ]
110
164
  }),
111
- completedProcesses.length > 0 && runningProcesses.length > 0 && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {}),
112
- runningProcesses.map(function(item) {
113
- return /*#__PURE__*/ (0, _jsxruntime.jsx)(_CompactProcessLinets.default, {
114
- item: item
165
+ processes.map(function(item, index) {
166
+ return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
167
+ flexDirection: "column",
168
+ children: [
169
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_CompactProcessLinets.default, {
170
+ item: item,
171
+ isSelected: showSelection && index === selectedIndex
172
+ }),
173
+ expandedId === item.id && /*#__PURE__*/ (0, _jsxruntime.jsx)(_ExpandedOutputts.default, {
174
+ lines: item.lines,
175
+ scrollOffset: scrollOffset
176
+ })
177
+ ]
115
178
  }, item.id);
116
179
  }),
117
- processes.length > 0 && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
180
+ showStatusBar && processes.length > 0 && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
118
181
  children: [
119
182
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {}),
120
183
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_StatusBarts.default, {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Static, useApp, useInput, useStdin } from 'ink';\nimport { useEffect, useSyncExternalStore } from 'react';\nimport { processStore } from '../state/processStore.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorDetailModal from './ErrorDetailModal.ts';\nimport ErrorListModal from './ErrorListModal.ts';\nimport StatusBar from './StatusBar.ts';\n\nexport default function App(): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n\n // Subscribe to store state\n const processes = useSyncExternalStore(processStore.subscribe, processStore.getSnapshot);\n const shouldExit = useSyncExternalStore(processStore.subscribe, processStore.getShouldExit);\n const mode = useSyncExternalStore(processStore.subscribe, processStore.getMode);\n const selectedErrorIndex = useSyncExternalStore(processStore.subscribe, processStore.getSelectedErrorIndex);\n\n // Derived state\n const completedProcesses = processStore.getCompletedProcesses();\n const runningProcesses = processStore.getRunningProcesses();\n const failedProcesses = processStore.getFailedProcesses();\n const runningCount = processStore.getRunningCount();\n const doneCount = processStore.getDoneCount();\n const errorCount = processStore.getErrorCount();\n const errorLineCount = processStore.getErrorLineCount();\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\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 processStore.setMode('errorList');\n }\n } else if (mode === 'errorList') {\n if (key.escape) {\n processStore.setMode('normal');\n } else if (key.downArrow) {\n processStore.selectNextError();\n } else if (key.upArrow) {\n processStore.selectPrevError();\n } else if (key.return) {\n processStore.setMode('errorDetail');\n }\n } else if (mode === 'errorDetail') {\n if (key.escape) {\n processStore.setMode('errorList');\n } else if (key.downArrow) {\n processStore.selectNextError();\n } else if (key.upArrow) {\n processStore.selectPrevError();\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\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 = processStore.getSelectedError();\n if (selectedError) {\n return <ErrorDetailModal error={selectedError} currentIndex={selectedErrorIndex} totalErrors={failedProcesses.length} />;\n }\n // Fallback if no error selected\n processStore.setMode('errorList');\n }\n\n // Normal view\n return (\n <Box flexDirection=\"column\">\n {/* Static area - completed processes (completion order) */}\n <Static items={completedProcesses}>{(item) => <CompactProcessLine key={item.id} item={item} />}</Static>\n\n {/* Divider between completed and running */}\n {completedProcesses.length > 0 && runningProcesses.length > 0 && <Divider />}\n\n {/* Dynamic area - running processes */}\n {runningProcesses.map((item) => (\n <CompactProcessLine key={item.id} item={item} />\n ))}\n\n {/* Status bar */}\n {processes.length > 0 && (\n <>\n <Divider />\n <StatusBar running={runningCount} done={doneCount} errors={errorCount} errorLines={errorLineCount} />\n </>\n )}\n </Box>\n );\n}\n"],"names":["App","exit","useApp","isRawModeSupported","useStdin","processes","useSyncExternalStore","processStore","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedErrorIndex","getSelectedErrorIndex","completedProcesses","getCompletedProcesses","runningProcesses","getRunningProcesses","failedProcesses","getFailedProcesses","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","useEffect","useInput","input","key","setMode","escape","downArrow","selectNextError","upArrow","selectPrevError","return","isActive","ErrorListModal","errors","selectedIndex","totalErrorLines","selectedError","getSelectedError","ErrorDetailModal","error","currentIndex","totalErrors","length","Box","flexDirection","Static","items","item","CompactProcessLine","id","Divider","map","StatusBar","running","done","errorLines"],"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 } from 'ink';\nimport { useEffect, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport { processStore } from '../state/processStore.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\nexport default function App(): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n\n // Subscribe to store state\n const processes = useSyncExternalStore(processStore.subscribe, processStore.getSnapshot);\n const shouldExit = useSyncExternalStore(processStore.subscribe, processStore.getShouldExit);\n const mode = useSyncExternalStore(processStore.subscribe, processStore.getMode);\n const selectedIndex = useSyncExternalStore(processStore.subscribe, processStore.getSelectedIndex);\n const selectedErrorIndex = useSyncExternalStore(processStore.subscribe, processStore.getSelectedErrorIndex);\n const expandedId = useSyncExternalStore(processStore.subscribe, processStore.getExpandedId);\n const scrollOffset = useSyncExternalStore(processStore.subscribe, processStore.getScrollOffset);\n\n // Derived state\n const failedProcesses = processStore.getFailedProcesses();\n const runningCount = processStore.getRunningCount();\n const doneCount = processStore.getDoneCount();\n const errorCount = processStore.getErrorCount();\n const errorLineCount = processStore.getErrorLineCount();\n const header = processStore.getHeader();\n const showStatusBar = processStore.getShowStatusBar();\n const isInteractive = processStore.getIsInteractive();\n const isAllComplete = processStore.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 processStore.setMode('interactive');\n }\n }, [isAllComplete, isInteractive, mode]);\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 processStore.setMode('errorList');\n }\n } else if (mode === 'interactive') {\n if (input === 'q' || key.escape) {\n if (expandedId) {\n processStore.collapse();\n } else {\n processStore.signalExit(() => {});\n }\n } else if (key.return) {\n processStore.toggleExpand();\n } else if (key.downArrow) {\n if (expandedId) {\n processStore.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n processStore.selectNext();\n }\n } else if (key.upArrow) {\n if (expandedId) {\n processStore.scrollUp();\n } else {\n processStore.selectPrev();\n }\n } else if (input === 'j') {\n if (expandedId) {\n processStore.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n processStore.selectNext();\n }\n } else if (input === 'k') {\n if (expandedId) {\n processStore.scrollUp();\n } else {\n processStore.selectPrev();\n }\n } else if (input === 'e' && errorCount > 0) {\n processStore.setMode('errorList');\n }\n } else if (mode === 'errorList') {\n if (key.escape) {\n processStore.setMode(isInteractive ? 'interactive' : 'normal');\n } else if (key.downArrow) {\n processStore.selectNextError();\n } else if (key.upArrow) {\n processStore.selectPrevError();\n } else if (key.return) {\n processStore.setMode('errorDetail');\n }\n } else if (mode === 'errorDetail') {\n if (key.escape) {\n processStore.setMode('errorList');\n } else if (key.downArrow) {\n processStore.selectNextError();\n } else if (key.upArrow) {\n processStore.selectPrevError();\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\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 = processStore.getSelectedError();\n if (selectedError) {\n return <ErrorDetailModal error={selectedError} currentIndex={selectedErrorIndex} totalErrors={failedProcesses.length} />;\n }\n // Fallback if no error selected\n processStore.setMode('errorList');\n }\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n return (\n <Box flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* All processes in registration order */}\n {processes.map((item, index) => (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && index === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={item.lines} scrollOffset={scrollOffset} />}\n </Box>\n ))}\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"],"names":["App","exit","useApp","isRawModeSupported","useStdin","processes","useSyncExternalStore","processStore","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","selectedErrorIndex","getSelectedErrorIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","failedProcesses","getFailedProcesses","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","isAllComplete","useEffect","setMode","useInput","input","key","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","EXPANDED_MAX_VISIBLE_LINES","selectNext","upArrow","scrollUp","selectPrev","selectNextError","selectPrevError","isActive","ErrorListModal","errors","totalErrorLines","selectedError","getSelectedError","ErrorDetailModal","error","currentIndex","totalErrors","length","showSelection","Box","flexDirection","Text","Divider","map","item","index","CompactProcessLine","isSelected","id","ExpandedOutput","lines","StatusBar","running","done","errorLines"],"mappings":"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"}
@@ -12,6 +12,8 @@ var _jsxruntime = require("react/jsx-runtime");
12
12
  var _ink = require("ink");
13
13
  var _react = require("react");
14
14
  var _figurests = /*#__PURE__*/ _interop_require_default(require("../lib/figures.js"));
15
+ var _formatts = require("../lib/format.js");
16
+ var _processStorets = require("../state/processStore.js");
15
17
  var _typests = require("../types.js");
16
18
  var _Spinnerts = /*#__PURE__*/ _interop_require_default(require("./Spinner.js"));
17
19
  function _define_property(obj, key, value) {
@@ -81,15 +83,17 @@ function getErrorCount(lines) {
81
83
  }).length;
82
84
  }
83
85
  var _default = /*#__PURE__*/ (0, _react.memo)(function CompactProcessLine(param) {
84
- var item = param.item;
86
+ var item = param.item, _param_isSelected = param.isSelected, isSelected = _param_isSelected === void 0 ? false : _param_isSelected;
85
87
  var stdout = (0, _ink.useStdout)().stdout;
86
88
  var terminalWidth = (stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80;
87
89
  var group = item.group, title = item.title, state = item.state, lines = item.lines;
90
+ var selectionIndicator = isSelected ? _figurests.default.pointer : ' ';
88
91
  // Display name: prefer group, fall back to title
89
92
  var displayName = group || title;
90
- // Calculate widths - use fixed-width columns for alignment
93
+ // Calculate widths - use dynamic column width based on longest name
91
94
  var iconWidth = 2; // icon + space
92
- var nameColumnWidth = 15; // fixed width for name column
95
+ var maxGroupLength = _processStorets.processStore.getMaxGroupLength();
96
+ var nameColumnWidth = (0, _formatts.calculateColumnWidth)('max', terminalWidth, maxGroupLength);
93
97
  var gap = 1; // space between name and status
94
98
  var statusWidth = terminalWidth - iconWidth - nameColumnWidth - gap;
95
99
  // Truncate name if needed and pad to column width
@@ -133,11 +137,16 @@ var _default = /*#__PURE__*/ (0, _react.memo)(function CompactProcessLine(param)
133
137
  var statusColor = state === 'error' ? 'red' : 'gray';
134
138
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
135
139
  children: [
140
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
141
+ color: isSelected ? 'cyan' : undefined,
142
+ children: selectionIndicator
143
+ }),
136
144
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Box, {
137
145
  width: iconWidth,
138
146
  children: icon
139
147
  }),
140
148
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
149
+ inverse: isSelected,
141
150
  children: truncatedName
142
151
  }),
143
152
  statusText && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/CompactProcessLine.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo, useMemo } from 'react';\nimport figures from '../lib/figures.ts';\nimport type { ChildProcess, Line } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport Spinner from './Spinner.ts';\n\n// From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2\nconst SPINNER = {\n interval: 80,\n frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],\n};\n\ntype Props = {\n item: ChildProcess;\n};\n\nfunction truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return `${str.slice(0, maxLength - 1)}…`;\n}\n\nfunction getLastOutputLine(lines: Line[]): string {\n for (let i = lines.length - 1; i >= 0; i--) {\n if (lines[i].text.length > 0) {\n return lines[i].text;\n }\n }\n return '';\n}\n\nfunction getErrorCount(lines: Line[]): number {\n return lines.filter((line) => line.type === LineType.stderr).length;\n}\n\nexport default memo(function CompactProcessLine({ item }: Props) {\n const { stdout } = useStdout();\n const terminalWidth = stdout?.columns || 80;\n\n const { group, title, state, lines } = item;\n\n // Display name: prefer group, fall back to title\n const displayName = group || title;\n\n // Calculate widths - use fixed-width columns for alignment\n const iconWidth = 2; // icon + space\n const nameColumnWidth = 15; // fixed width for name column\n const gap = 1; // space between name and status\n const statusWidth = terminalWidth - iconWidth - nameColumnWidth - gap;\n\n // Truncate name if needed and pad to column width\n const truncatedName = truncate(displayName, nameColumnWidth).padEnd(nameColumnWidth);\n\n // Status text based on state\n const statusText = useMemo(() => {\n if (state === 'running') {\n const lastLine = getLastOutputLine(lines);\n return lastLine ? truncate(lastLine, statusWidth) : '';\n }\n if (state === 'error') {\n const errorCount = getErrorCount(lines);\n return errorCount > 0 ? `${errorCount} error${errorCount > 1 ? 's' : ''}` : 'failed';\n }\n return ''; // success - no status text\n }, [state, lines, statusWidth]);\n\n // Icon based on state\n const icon = useMemo(() => {\n switch (state) {\n case 'running':\n return <Spinner {...SPINNER} />;\n case 'success':\n return <Text color=\"green\">{figures.tick}</Text>;\n case 'error':\n return <Text color=\"red\">{figures.cross}</Text>;\n }\n }, [state]);\n\n // Status text color\n const statusColor = state === 'error' ? 'red' : 'gray';\n\n return (\n <Box>\n <Box width={iconWidth}>{icon}</Box>\n <Text>{truncatedName}</Text>\n {statusText && <Text color={statusColor}> {statusText}</Text>}\n </Box>\n );\n});\n"],"names":["SPINNER","interval","frames","truncate","str","maxLength","length","slice","getLastOutputLine","lines","i","text","getErrorCount","filter","line","type","LineType","stderr","memo","CompactProcessLine","item","stdout","useStdout","terminalWidth","columns","group","title","state","displayName","iconWidth","nameColumnWidth","gap","statusWidth","truncatedName","padEnd","statusText","useMemo","lastLine","errorCount","icon","Spinner","Text","color","figures","tick","cross","statusColor","Box","width"],"mappings":";;;;+BAmCA;;;eAAA;;;;mBAnCqC;qBACP;gEACV;uBAEK;gEACL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEpB,oHAAoH;AACpH,IAAMA,UAAU;IACdC,UAAU;IACVC,QAAQ;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;KAAI;AAC5D;AAMA,SAASC,SAASC,GAAW,EAAEC,SAAiB;IAC9C,IAAID,IAAIE,MAAM,IAAID,WAAW,OAAOD;IACpC,OAAO,AAAC,GAA8B,OAA5BA,IAAIG,KAAK,CAAC,GAAGF,YAAY,IAAG;AACxC;AAEA,SAASG,kBAAkBC,KAAa;IACtC,IAAK,IAAIC,IAAID,MAAMH,MAAM,GAAG,GAAGI,KAAK,GAAGA,IAAK;QAC1C,IAAID,KAAK,CAACC,EAAE,CAACC,IAAI,CAACL,MAAM,GAAG,GAAG;YAC5B,OAAOG,KAAK,CAACC,EAAE,CAACC,IAAI;QACtB;IACF;IACA,OAAO;AACT;AAEA,SAASC,cAAcH,KAAa;IAClC,OAAOA,MAAMI,MAAM,CAAC,SAACC;eAASA,KAAKC,IAAI,KAAKC,iBAAQ,CAACC,MAAM;OAAEX,MAAM;AACrE;IAEA,yBAAeY,IAAAA,WAAI,EAAC,SAASC,mBAAmB,KAAe;QAAf,AAAEC,OAAF,MAAEA;IAChD,IAAM,AAAEC,SAAWC,IAAAA,cAAS,IAApBD;IACR,IAAME,gBAAgBF,CAAAA,mBAAAA,6BAAAA,OAAQG,OAAO,KAAI;IAEzC,IAAQC,QAA+BL,KAA/BK,OAAOC,QAAwBN,KAAxBM,OAAOC,QAAiBP,KAAjBO,OAAOlB,QAAUW,KAAVX;IAE7B,iDAAiD;IACjD,IAAMmB,cAAcH,SAASC;IAE7B,2DAA2D;IAC3D,IAAMG,YAAY,GAAG,eAAe;IACpC,IAAMC,kBAAkB,IAAI,8BAA8B;IAC1D,IAAMC,MAAM,GAAG,gCAAgC;IAC/C,IAAMC,cAAcT,gBAAgBM,YAAYC,kBAAkBC;IAElE,kDAAkD;IAClD,IAAME,gBAAgB9B,SAASyB,aAAaE,iBAAiBI,MAAM,CAACJ;IAEpE,6BAA6B;IAC7B,IAAMK,aAAaC,IAAAA,cAAO,EAAC;QACzB,IAAIT,UAAU,WAAW;YACvB,IAAMU,WAAW7B,kBAAkBC;YACnC,OAAO4B,WAAWlC,SAASkC,UAAUL,eAAe;QACtD;QACA,IAAIL,UAAU,SAAS;YACrB,IAAMW,aAAa1B,cAAcH;YACjC,OAAO6B,aAAa,IAAI,AAAC,GAAqBA,OAAnBA,YAAW,UAAkC,OAA1BA,aAAa,IAAI,MAAM,MAAO;QAC9E;QACA,OAAO,IAAI,2BAA2B;IACxC,GAAG;QAACX;QAAOlB;QAAOuB;KAAY;IAE9B,sBAAsB;IACtB,IAAMO,OAAOH,IAAAA,cAAO,EAAC;QACnB,OAAQT;YACN,KAAK;gBACH,qBAAO,qBAACa,kBAAO,qBAAKxC;YACtB,KAAK;gBACH,qBAAO,qBAACyC,SAAI;oBAACC,OAAM;8BAASC,kBAAO,CAACC,IAAI;;YAC1C,KAAK;gBACH,qBAAO,qBAACH,SAAI;oBAACC,OAAM;8BAAOC,kBAAO,CAACE,KAAK;;QAC3C;IACF,GAAG;QAAClB;KAAM;IAEV,oBAAoB;IACpB,IAAMmB,cAAcnB,UAAU,UAAU,QAAQ;IAEhD,qBACE,sBAACoB,QAAG;;0BACF,qBAACA,QAAG;gBAACC,OAAOnB;0BAAYU;;0BACxB,qBAACE,SAAI;0BAAER;;YACNE,4BAAc,sBAACM,SAAI;gBAACC,OAAOI;;oBAAa;oBAAEX;;;;;AAGjD"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/CompactProcessLine.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo, useMemo } from 'react';\nimport figures from '../lib/figures.ts';\nimport { calculateColumnWidth } from '../lib/format.ts';\nimport { processStore } from '../state/processStore.ts';\nimport type { ChildProcess, Line } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport Spinner from './Spinner.ts';\n\n// From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2\nconst SPINNER = {\n interval: 80,\n frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],\n};\n\ntype Props = {\n item: ChildProcess;\n isSelected?: boolean;\n};\n\nfunction truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return `${str.slice(0, maxLength - 1)}…`;\n}\n\nfunction getLastOutputLine(lines: Line[]): string {\n for (let i = lines.length - 1; i >= 0; i--) {\n if (lines[i].text.length > 0) {\n return lines[i].text;\n }\n }\n return '';\n}\n\nfunction getErrorCount(lines: Line[]): number {\n return lines.filter((line) => line.type === LineType.stderr).length;\n}\n\nexport default memo(function CompactProcessLine({ item, isSelected = false }: Props) {\n const { stdout } = useStdout();\n const terminalWidth = stdout?.columns || 80;\n\n const { group, title, state, lines } = item;\n const selectionIndicator = isSelected ? figures.pointer : ' ';\n\n // Display name: prefer group, fall back to title\n const displayName = group || title;\n\n // Calculate widths - use dynamic column width based on longest name\n const iconWidth = 2; // icon + space\n const maxGroupLength = processStore.getMaxGroupLength();\n const nameColumnWidth = calculateColumnWidth('max', terminalWidth, maxGroupLength);\n const gap = 1; // space between name and status\n const statusWidth = terminalWidth - iconWidth - nameColumnWidth - gap;\n\n // Truncate name if needed and pad to column width\n const truncatedName = truncate(displayName, nameColumnWidth).padEnd(nameColumnWidth);\n\n // Status text based on state\n const statusText = useMemo(() => {\n if (state === 'running') {\n const lastLine = getLastOutputLine(lines);\n return lastLine ? truncate(lastLine, statusWidth) : '';\n }\n if (state === 'error') {\n const errorCount = getErrorCount(lines);\n return errorCount > 0 ? `${errorCount} error${errorCount > 1 ? 's' : ''}` : 'failed';\n }\n return ''; // success - no status text\n }, [state, lines, statusWidth]);\n\n // Icon based on state\n const icon = useMemo(() => {\n switch (state) {\n case 'running':\n return <Spinner {...SPINNER} />;\n case 'success':\n return <Text color=\"green\">{figures.tick}</Text>;\n case 'error':\n return <Text color=\"red\">{figures.cross}</Text>;\n }\n }, [state]);\n\n // Status text color\n const statusColor = state === 'error' ? 'red' : 'gray';\n\n return (\n <Box>\n <Text color={isSelected ? 'cyan' : undefined}>{selectionIndicator}</Text>\n <Box width={iconWidth}>{icon}</Box>\n <Text inverse={isSelected}>{truncatedName}</Text>\n {statusText && <Text color={statusColor}> {statusText}</Text>}\n </Box>\n );\n});\n"],"names":["SPINNER","interval","frames","truncate","str","maxLength","length","slice","getLastOutputLine","lines","i","text","getErrorCount","filter","line","type","LineType","stderr","memo","CompactProcessLine","item","isSelected","stdout","useStdout","terminalWidth","columns","group","title","state","selectionIndicator","figures","pointer","displayName","iconWidth","maxGroupLength","processStore","getMaxGroupLength","nameColumnWidth","calculateColumnWidth","gap","statusWidth","truncatedName","padEnd","statusText","useMemo","lastLine","errorCount","icon","Spinner","Text","color","tick","cross","statusColor","Box","undefined","width","inverse"],"mappings":"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"}
@@ -0,0 +1,55 @@
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 _constantsts = require("../constants.js");
15
+ var _typests = require("../types.js");
16
+ var _default = /*#__PURE__*/ (0, _react.memo)(function ExpandedOutput(param) {
17
+ var lines = param.lines, scrollOffset = param.scrollOffset, _param_maxVisible = param.maxVisible, maxVisible = _param_maxVisible === void 0 ? _constantsts.EXPANDED_MAX_VISIBLE_LINES : _param_maxVisible;
18
+ var visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);
19
+ var hasMore = lines.length > scrollOffset + maxVisible;
20
+ var remaining = lines.length - scrollOffset - maxVisible;
21
+ if (lines.length === 0) {
22
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Box, {
23
+ paddingLeft: 2,
24
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
25
+ dimColor: true,
26
+ children: "│ (no output)"
27
+ })
28
+ });
29
+ }
30
+ return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
31
+ flexDirection: "column",
32
+ paddingLeft: 2,
33
+ children: [
34
+ visibleLines.map(function(line, i) {
35
+ return(// biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view
36
+ /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
37
+ color: line.type === _typests.LineType.stderr ? 'red' : undefined,
38
+ children: [
39
+ "│ ",
40
+ line.text
41
+ ]
42
+ }, scrollOffset + i));
43
+ }),
44
+ hasMore && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
45
+ dimColor: true,
46
+ children: [
47
+ "│ [+",
48
+ remaining,
49
+ " more, j/k to scroll]"
50
+ ]
51
+ })
52
+ ]
53
+ });
54
+ });
55
+ /* 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/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"}
@@ -0,0 +1,42 @@
1
+ // Column width defaults
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ function _export(target, all) {
7
+ for(var name in all)Object.defineProperty(target, name, {
8
+ enumerable: true,
9
+ get: Object.getOwnPropertyDescriptor(all, name).get
10
+ });
11
+ }
12
+ _export(exports, {
13
+ get BATCH_MAX_LINES () {
14
+ return BATCH_MAX_LINES;
15
+ },
16
+ get BATCH_MAX_WAIT_MS () {
17
+ return BATCH_MAX_WAIT_MS;
18
+ },
19
+ get DEFAULT_COLUMN_WIDTH () {
20
+ return DEFAULT_COLUMN_WIDTH;
21
+ },
22
+ get DEFAULT_MAX_FPS () {
23
+ return DEFAULT_MAX_FPS;
24
+ },
25
+ get EXPANDED_MAX_VISIBLE_LINES () {
26
+ return EXPANDED_MAX_VISIBLE_LINES;
27
+ },
28
+ get FALLBACK_COLUMN_WIDTH () {
29
+ return FALLBACK_COLUMN_WIDTH;
30
+ },
31
+ get MAX_COLUMN_WIDTH_PERCENT () {
32
+ return MAX_COLUMN_WIDTH_PERCENT;
33
+ }
34
+ });
35
+ var DEFAULT_COLUMN_WIDTH = 15;
36
+ var MAX_COLUMN_WIDTH_PERCENT = 0.4; // 40% of terminal width
37
+ var FALLBACK_COLUMN_WIDTH = 25;
38
+ var BATCH_MAX_LINES = 20;
39
+ var BATCH_MAX_WAIT_MS = 50;
40
+ var DEFAULT_MAX_FPS = 20;
41
+ var EXPANDED_MAX_VISIBLE_LINES = 10;
42
+ /* 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/constants.ts"],"sourcesContent":["// Column width defaults\nexport const DEFAULT_COLUMN_WIDTH = 15;\nexport const MAX_COLUMN_WIDTH_PERCENT = 0.4; // 40% of terminal width\nexport const FALLBACK_COLUMN_WIDTH = 25;\n\n// Batching defaults\nexport const BATCH_MAX_LINES = 20;\nexport const BATCH_MAX_WAIT_MS = 50;\n\n// Rendering\nexport const DEFAULT_MAX_FPS = 20;\n\n// Expansion\nexport const EXPANDED_MAX_VISIBLE_LINES = 10;\n"],"names":["BATCH_MAX_LINES","BATCH_MAX_WAIT_MS","DEFAULT_COLUMN_WIDTH","DEFAULT_MAX_FPS","EXPANDED_MAX_VISIBLE_LINES","FALLBACK_COLUMN_WIDTH","MAX_COLUMN_WIDTH_PERCENT"],"mappings":"AAAA,wBAAwB;;;;;;;;;;;;QAMXA;eAAAA;;QACAC;eAAAA;;QANAC;eAAAA;;QASAC;eAAAA;;QAGAC;eAAAA;;QAVAC;eAAAA;;QADAC;eAAAA;;;AADN,IAAMJ,uBAAuB;AAC7B,IAAMI,2BAA2B,KAAK,wBAAwB;AAC9D,IAAMD,wBAAwB;AAG9B,IAAML,kBAAkB;AACxB,IAAMC,oBAAoB;AAG1B,IAAME,kBAAkB;AAGxB,IAAMC,6BAA6B"}
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "default", {
11
11
  var _jsxruntime = require("react/jsx-runtime");
12
12
  var _ink = require("ink");
13
13
  var _Appts = /*#__PURE__*/ _interop_require_default(require("./components/App.js"));
14
+ var _constantsts = require("./constants.js");
14
15
  var _processStorets = require("./state/processStore.js");
15
16
  function _interop_require_default(obj) {
16
17
  return obj && obj.__esModule ? obj : {
@@ -24,7 +25,11 @@ function createApp() {
24
25
  retain: function retain() {
25
26
  if (++refCount > 1) return _processStorets.processStore;
26
27
  // Render once - React handles all subsequent updates via useSyncExternalStore
27
- inkApp = (0, _ink.render)(/*#__PURE__*/ (0, _jsxruntime.jsx)(_Appts.default, {}));
28
+ // Enable incremental rendering to only rewrite changed lines (reduces flicker)
29
+ inkApp = (0, _ink.render)(/*#__PURE__*/ (0, _jsxruntime.jsx)(_Appts.default, {}), {
30
+ incrementalRendering: true,
31
+ maxFps: _constantsts.DEFAULT_MAX_FPS
32
+ });
28
33
  return _processStorets.processStore;
29
34
  },
30
35
  release: function release(callback) {
@@ -33,16 +38,11 @@ function createApp() {
33
38
  return;
34
39
  }
35
40
  if (!inkApp) throw new Error('Expecting inkApp');
36
- // Defer signalExit to allow React's reconciliation to complete fully
37
- // Using setImmediate ensures we run after I/O callbacks and microtasks,
38
- // preventing the Static component from outputting the last item twice
39
- // (the second notify() from signalExit can race with Static's useLayoutEffect)
40
- setImmediate(function() {
41
- _processStorets.processStore.signalExit(function() {
42
- _processStorets.processStore.reset();
43
- process.stdout.write('\x1b[?25h'); // show cursor
44
- callback();
45
- });
41
+ // Signal exit to React component
42
+ _processStorets.processStore.signalExit(function() {
43
+ _processStorets.processStore.reset();
44
+ process.stdout.write('\x1b[?25h'); // show cursor
45
+ callback();
46
46
  });
47
47
  // Wait for Ink to finish, then call the callback
48
48
  inkApp.waitUntilExit().then(function() {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/createApp.tsx"],"sourcesContent":["import { render } from 'ink';\nimport App from './components/App.ts';\nimport { type ProcessStore, processStore } from './state/processStore.ts';\n\nexport type ReleaseCallback = () => void;\n\nexport default function createApp() {\n let refCount = 0;\n let inkApp: ReturnType<typeof render> | null = null;\n\n return {\n retain(): ProcessStore {\n if (++refCount > 1) return processStore;\n\n // Render once - React handles all subsequent updates via useSyncExternalStore\n inkApp = render(<App />);\n return processStore;\n },\n\n release(callback: ReleaseCallback): void {\n if (--refCount > 0) {\n callback();\n return;\n }\n if (!inkApp) throw new Error('Expecting inkApp');\n\n // Defer signalExit to allow React's reconciliation to complete fully\n // Using setImmediate ensures we run after I/O callbacks and microtasks,\n // preventing the Static component from outputting the last item twice\n // (the second notify() from signalExit can race with Static's useLayoutEffect)\n setImmediate(() => {\n processStore.signalExit(() => {\n processStore.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n callback();\n });\n });\n\n // Wait for Ink to finish, then call the callback\n inkApp\n .waitUntilExit()\n .then(() => {\n const cb = processStore.getExitCallback();\n cb?.();\n })\n .catch(() => {\n const cb = processStore.getExitCallback();\n cb?.();\n });\n\n inkApp = null;\n },\n };\n}\n"],"names":["createApp","refCount","inkApp","retain","processStore","render","App","release","callback","Error","setImmediate","signalExit","reset","process","stdout","write","waitUntilExit","then","cb","getExitCallback","catch"],"mappings":";;;;+BAMA;;;eAAwBA;;;;mBAND;4DACP;8BACgC;;;;;;AAIjC,SAASA;IACtB,IAAIC,WAAW;IACf,IAAIC,SAA2C;IAE/C,OAAO;QACLC,QAAAA,SAAAA;YACE,IAAI,EAAEF,WAAW,GAAG,OAAOG,4BAAY;YAEvC,8EAA8E;YAC9EF,SAASG,IAAAA,WAAM,gBAAC,qBAACC,cAAG;YACpB,OAAOF,4BAAY;QACrB;QAEAG,SAAAA,SAAAA,QAAQC,QAAyB;YAC/B,IAAI,EAAEP,WAAW,GAAG;gBAClBO;gBACA;YACF;YACA,IAAI,CAACN,QAAQ,MAAM,IAAIO,MAAM;YAE7B,qEAAqE;YACrE,wEAAwE;YACxE,sEAAsE;YACtE,+EAA+E;YAC/EC,aAAa;gBACXN,4BAAY,CAACO,UAAU,CAAC;oBACtBP,4BAAY,CAACQ,KAAK;oBAClBC,QAAQC,MAAM,CAACC,KAAK,CAAC,cAAc,cAAc;oBACjDP;gBACF;YACF;YAEA,iDAAiD;YACjDN,OACGc,aAAa,GACbC,IAAI,CAAC;gBACJ,IAAMC,KAAKd,4BAAY,CAACe,eAAe;gBACvCD,eAAAA,yBAAAA;YACF,GACCE,KAAK,CAAC;gBACL,IAAMF,KAAKd,4BAAY,CAACe,eAAe;gBACvCD,eAAAA,yBAAAA;YACF;YAEFhB,SAAS;QACX;IACF;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/createApp.tsx"],"sourcesContent":["import { render } from 'ink';\nimport App from './components/App.ts';\nimport { DEFAULT_MAX_FPS } from './constants.ts';\nimport { type ProcessStore, processStore } from './state/processStore.ts';\n\nexport type ReleaseCallback = () => void;\n\nexport default function createApp() {\n let refCount = 0;\n let inkApp: ReturnType<typeof render> | null = null;\n\n return {\n retain(): ProcessStore {\n if (++refCount > 1) return processStore;\n\n // Render once - React handles all subsequent updates via useSyncExternalStore\n // Enable incremental rendering to only rewrite changed lines (reduces flicker)\n inkApp = render(<App />, {\n incrementalRendering: true,\n maxFps: DEFAULT_MAX_FPS,\n });\n return processStore;\n },\n\n release(callback: ReleaseCallback): void {\n if (--refCount > 0) {\n callback();\n return;\n }\n if (!inkApp) throw new Error('Expecting inkApp');\n\n // Signal exit to React component\n processStore.signalExit(() => {\n processStore.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n callback();\n });\n\n // Wait for Ink to finish, then call the callback\n inkApp\n .waitUntilExit()\n .then(() => {\n const cb = processStore.getExitCallback();\n cb?.();\n })\n .catch(() => {\n const cb = processStore.getExitCallback();\n cb?.();\n });\n\n inkApp = null;\n },\n };\n}\n"],"names":["createApp","refCount","inkApp","retain","processStore","render","App","incrementalRendering","maxFps","DEFAULT_MAX_FPS","release","callback","Error","signalExit","reset","process","stdout","write","waitUntilExit","then","cb","getExitCallback","catch"],"mappings":";;;;+BAOA;;;eAAwBA;;;;mBAPD;4DACP;2BACgB;8BACgB;;;;;;AAIjC,SAASA;IACtB,IAAIC,WAAW;IACf,IAAIC,SAA2C;IAE/C,OAAO;QACLC,QAAAA,SAAAA;YACE,IAAI,EAAEF,WAAW,GAAG,OAAOG,4BAAY;YAEvC,8EAA8E;YAC9E,+EAA+E;YAC/EF,SAASG,IAAAA,WAAM,gBAAC,qBAACC,cAAG,OAAK;gBACvBC,sBAAsB;gBACtBC,QAAQC,4BAAe;YACzB;YACA,OAAOL,4BAAY;QACrB;QAEAM,SAAAA,SAAAA,QAAQC,QAAyB;YAC/B,IAAI,EAAEV,WAAW,GAAG;gBAClBU;gBACA;YACF;YACA,IAAI,CAACT,QAAQ,MAAM,IAAIU,MAAM;YAE7B,iCAAiC;YACjCR,4BAAY,CAACS,UAAU,CAAC;gBACtBT,4BAAY,CAACU,KAAK;gBAClBC,QAAQC,MAAM,CAACC,KAAK,CAAC,cAAc,cAAc;gBACjDN;YACF;YAEA,iDAAiD;YACjDT,OACGgB,aAAa,GACbC,IAAI,CAAC;gBACJ,IAAMC,KAAKhB,4BAAY,CAACiB,eAAe;gBACvCD,eAAAA,yBAAAA;YACF,GACCE,KAAK,CAAC;gBACL,IAAMF,KAAKhB,4BAAY,CAACiB,eAAe;gBACvCD,eAAAA,yBAAAA;YACF;YAEFlB,SAAS;QACX;IACF;AACF"}
@@ -9,22 +9,76 @@ Object.defineProperty(exports, "default", {
9
9
  }
10
10
  });
11
11
  var _stream = require("stream");
12
+ var _constantsts = require("../constants.js");
13
+ function _array_like_to_array(arr, len) {
14
+ if (len == null || len > arr.length) len = arr.length;
15
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
16
+ return arr2;
17
+ }
18
+ function _array_without_holes(arr) {
19
+ if (Array.isArray(arr)) return _array_like_to_array(arr);
20
+ }
21
+ function _iterable_to_array(iter) {
22
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
23
+ }
24
+ function _non_iterable_spread() {
25
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
26
+ }
27
+ function _to_consumable_array(arr) {
28
+ return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
29
+ }
30
+ function _unsupported_iterable_to_array(o, minLen) {
31
+ if (!o) return;
32
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
33
+ var n = Object.prototype.toString.call(o).slice(8, -1);
34
+ if (n === "Object" && o.constructor) n = o.constructor.name;
35
+ if (n === "Map" || n === "Set") return Array.from(n);
36
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
37
+ }
12
38
  var REGEX_NEW_LINE = /\r?\n|\r/g;
13
39
  function addLines(fn) {
40
+ var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
41
+ var _options_maxLines = options.maxLines, maxLines = _options_maxLines === void 0 ? _constantsts.BATCH_MAX_LINES : _options_maxLines, _options_maxWait = options.maxWait, maxWait = _options_maxWait === void 0 ? _constantsts.BATCH_MAX_WAIT_MS : _options_maxWait;
14
42
  var last = '';
43
+ var lineBuffer = [];
44
+ var flushTimer = null;
45
+ var flush = function() {
46
+ if (flushTimer) {
47
+ clearTimeout(flushTimer);
48
+ flushTimer = null;
49
+ }
50
+ if (lineBuffer.length > 0) {
51
+ fn(lineBuffer);
52
+ lineBuffer = [];
53
+ }
54
+ };
55
+ var scheduleFlush = function() {
56
+ if (!flushTimer) {
57
+ flushTimer = setTimeout(flush, maxWait);
58
+ }
59
+ };
15
60
  var stream = new _stream.Writable({
16
61
  write: function write(chunk, _enc, callback) {
17
62
  var more = last + chunk.toString('utf8');
18
63
  var lines = more.split(REGEX_NEW_LINE);
19
64
  last = lines.pop();
20
- if (lines.length > 0) fn(lines);
65
+ if (lines.length > 0) {
66
+ var _lineBuffer;
67
+ (_lineBuffer = lineBuffer).push.apply(_lineBuffer, _to_consumable_array(lines));
68
+ // Flush immediately if buffer is large enough
69
+ if (lineBuffer.length >= maxLines) {
70
+ flush();
71
+ } else {
72
+ scheduleFlush();
73
+ }
74
+ }
21
75
  callback();
22
76
  }
23
77
  });
24
78
  stream.on('finish', function() {
25
- if (last.length) fn([
26
- last
27
- ]);
79
+ // Flush any remaining buffered lines
80
+ if (last.length) lineBuffer.push(last);
81
+ flush();
28
82
  last = '';
29
83
  });
30
84
  return stream;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/lib/addLines.ts"],"sourcesContent":["import { Writable } from 'stream';\n\nconst REGEX_NEW_LINE = /\\r?\\n|\\r/g;\n\nexport type Callback = (lines: string[]) => undefined;\n\nexport default function addLines(fn: Callback): Writable {\n let last = '';\n\n const stream = new Writable({\n write(chunk, _enc, callback) {\n const more = last + chunk.toString('utf8');\n const lines = more.split(REGEX_NEW_LINE);\n last = lines.pop();\n if (lines.length > 0) fn(lines);\n callback();\n },\n });\n stream.on('finish', () => {\n if (last.length) fn([last]);\n last = '';\n });\n return stream;\n}\n"],"names":["addLines","REGEX_NEW_LINE","fn","last","stream","Writable","write","chunk","_enc","callback","more","toString","lines","split","pop","length","on"],"mappings":";;;;+BAMA;;;eAAwBA;;;sBANC;AAEzB,IAAMC,iBAAiB;AAIR,SAASD,SAASE,EAAY;IAC3C,IAAIC,OAAO;IAEX,IAAMC,SAAS,IAAIC,gBAAQ,CAAC;QAC1BC,OAAAA,SAAAA,MAAMC,KAAK,EAAEC,IAAI,EAAEC,QAAQ;YACzB,IAAMC,OAAOP,OAAOI,MAAMI,QAAQ,CAAC;YACnC,IAAMC,QAAQF,KAAKG,KAAK,CAACZ;YACzBE,OAAOS,MAAME,GAAG;YAChB,IAAIF,MAAMG,MAAM,GAAG,GAAGb,GAAGU;YACzBH;QACF;IACF;IACAL,OAAOY,EAAE,CAAC,UAAU;QAClB,IAAIb,KAAKY,MAAM,EAAEb,GAAG;YAACC;SAAK;QAC1BA,OAAO;IACT;IACA,OAAOC;AACT"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/lib/addLines.ts"],"sourcesContent":["import { Writable } from 'stream';\nimport { BATCH_MAX_LINES, BATCH_MAX_WAIT_MS } from '../constants.ts';\n\nconst REGEX_NEW_LINE = /\\r?\\n|\\r/g;\n\nexport type Callback = (lines: string[]) => undefined;\n\ninterface BatchOptions {\n maxLines?: number;\n maxWait?: number;\n}\n\nexport default function addLines(fn: Callback, options: BatchOptions = {}): Writable {\n const { maxLines = BATCH_MAX_LINES, maxWait = BATCH_MAX_WAIT_MS } = options;\n\n let last = '';\n let lineBuffer: string[] = [];\n let flushTimer: NodeJS.Timeout | null = null;\n\n const flush = () => {\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n if (lineBuffer.length > 0) {\n fn(lineBuffer);\n lineBuffer = [];\n }\n };\n\n const scheduleFlush = () => {\n if (!flushTimer) {\n flushTimer = setTimeout(flush, maxWait);\n }\n };\n\n const stream = new Writable({\n write(chunk, _enc, callback) {\n const more = last + chunk.toString('utf8');\n const lines = more.split(REGEX_NEW_LINE);\n last = lines.pop();\n\n if (lines.length > 0) {\n lineBuffer.push(...lines);\n\n // Flush immediately if buffer is large enough\n if (lineBuffer.length >= maxLines) {\n flush();\n } else {\n scheduleFlush();\n }\n }\n callback();\n },\n });\n\n stream.on('finish', () => {\n // Flush any remaining buffered lines\n if (last.length) lineBuffer.push(last);\n flush();\n last = '';\n });\n\n return stream;\n}\n"],"names":["addLines","REGEX_NEW_LINE","fn","options","maxLines","BATCH_MAX_LINES","maxWait","BATCH_MAX_WAIT_MS","last","lineBuffer","flushTimer","flush","clearTimeout","length","scheduleFlush","setTimeout","stream","Writable","write","chunk","_enc","callback","more","toString","lines","split","pop","push","on"],"mappings":";;;;+BAYA;;;eAAwBA;;;sBAZC;2BAC0B;;;;;;;;;;;;;;;;;;;;;;;;;;AAEnD,IAAMC,iBAAiB;AASR,SAASD,SAASE,EAAY;QAAEC,UAAAA,iEAAwB,CAAC;IACtE,wBAAoEA,QAA5DC,UAAAA,0CAAWC,4BAAe,yCAAkCF,QAAhCG,SAAAA,wCAAUC,8BAAiB;IAE/D,IAAIC,OAAO;IACX,IAAIC,aAAuB,EAAE;IAC7B,IAAIC,aAAoC;IAExC,IAAMC,QAAQ;QACZ,IAAID,YAAY;YACdE,aAAaF;YACbA,aAAa;QACf;QACA,IAAID,WAAWI,MAAM,GAAG,GAAG;YACzBX,GAAGO;YACHA,aAAa,EAAE;QACjB;IACF;IAEA,IAAMK,gBAAgB;QACpB,IAAI,CAACJ,YAAY;YACfA,aAAaK,WAAWJ,OAAOL;QACjC;IACF;IAEA,IAAMU,SAAS,IAAIC,gBAAQ,CAAC;QAC1BC,OAAAA,SAAAA,MAAMC,KAAK,EAAEC,IAAI,EAAEC,QAAQ;YACzB,IAAMC,OAAOd,OAAOW,MAAMI,QAAQ,CAAC;YACnC,IAAMC,QAAQF,KAAKG,KAAK,CAACxB;YACzBO,OAAOgB,MAAME,GAAG;YAEhB,IAAIF,MAAMX,MAAM,GAAG,GAAG;oBACpBJ;gBAAAA,CAAAA,cAAAA,YAAWkB,IAAI,OAAflB,aAAgB,qBAAGe;gBAEnB,8CAA8C;gBAC9C,IAAIf,WAAWI,MAAM,IAAIT,UAAU;oBACjCO;gBACF,OAAO;oBACLG;gBACF;YACF;YACAO;QACF;IACF;IAEAL,OAAOY,EAAE,CAAC,UAAU;QAClB,qCAAqC;QACrC,IAAIpB,KAAKK,MAAM,EAAEJ,WAAWkB,IAAI,CAACnB;QACjCG;QACAH,OAAO;IACT;IAEA,OAAOQ;AACT"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "calculateColumnWidth", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return calculateColumnWidth;
9
+ }
10
+ });
11
+ var _constantsts = require("../constants.js");
12
+ function calculateColumnWidth(groupWidth, terminalWidth, maxGroupLength) {
13
+ if (typeof groupWidth === 'number') return groupWidth;
14
+ if (groupWidth === 'max') return Math.min(maxGroupLength, Math.floor(terminalWidth * _constantsts.MAX_COLUMN_WIDTH_PERCENT));
15
+ if (typeof groupWidth === 'string' && groupWidth.endsWith('%')) {
16
+ var pct = parseInt(groupWidth, 10) / 100;
17
+ return Math.floor(terminalWidth * pct);
18
+ }
19
+ return _constantsts.FALLBACK_COLUMN_WIDTH;
20
+ }
21
+ /* 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/lib/format.ts"],"sourcesContent":["import { FALLBACK_COLUMN_WIDTH, MAX_COLUMN_WIDTH_PERCENT } from '../constants.ts';\n\nexport type GroupWidth = number | `${number}%` | 'max';\n\nexport function calculateColumnWidth(groupWidth: GroupWidth, terminalWidth: number, maxGroupLength: number): number {\n if (typeof groupWidth === 'number') return groupWidth;\n if (groupWidth === 'max') return Math.min(maxGroupLength, Math.floor(terminalWidth * MAX_COLUMN_WIDTH_PERCENT));\n if (typeof groupWidth === 'string' && groupWidth.endsWith('%')) {\n const pct = parseInt(groupWidth, 10) / 100;\n return Math.floor(terminalWidth * pct);\n }\n return FALLBACK_COLUMN_WIDTH;\n}\n"],"names":["calculateColumnWidth","groupWidth","terminalWidth","maxGroupLength","Math","min","floor","MAX_COLUMN_WIDTH_PERCENT","endsWith","pct","parseInt","FALLBACK_COLUMN_WIDTH"],"mappings":";;;;+BAIgBA;;;eAAAA;;;2BAJgD;AAIzD,SAASA,qBAAqBC,UAAsB,EAAEC,aAAqB,EAAEC,cAAsB;IACxG,IAAI,OAAOF,eAAe,UAAU,OAAOA;IAC3C,IAAIA,eAAe,OAAO,OAAOG,KAAKC,GAAG,CAACF,gBAAgBC,KAAKE,KAAK,CAACJ,gBAAgBK,qCAAwB;IAC7G,IAAI,OAAON,eAAe,YAAYA,WAAWO,QAAQ,CAAC,MAAM;QAC9D,IAAMC,MAAMC,SAAST,YAAY,MAAM;QACvC,OAAOG,KAAKE,KAAK,CAACJ,gBAAgBO;IACpC;IACA,OAAOE,kCAAqB;AAC9B"}
@@ -1,6 +1,7 @@
1
1
  import type { ChildProcess } from '../types.js';
2
2
  type Props = {
3
3
  item: ChildProcess;
4
+ isSelected?: boolean;
4
5
  };
5
6
  declare const _default: import("react").NamedExoticComponent<Props>;
6
7
  export default _default;
@@ -0,0 +1,8 @@
1
+ import type { Line } from '../types.js';
2
+ type Props = {
3
+ lines: Line[];
4
+ scrollOffset: number;
5
+ maxVisible?: number;
6
+ };
7
+ declare const _default: import("react").NamedExoticComponent<Props>;
8
+ export default _default;
@@ -0,0 +1,7 @@
1
+ export declare const DEFAULT_COLUMN_WIDTH = 15;
2
+ export declare const MAX_COLUMN_WIDTH_PERCENT = 0.4;
3
+ export declare const FALLBACK_COLUMN_WIDTH = 25;
4
+ export declare const BATCH_MAX_LINES = 20;
5
+ export declare const BATCH_MAX_WAIT_MS = 50;
6
+ export declare const DEFAULT_MAX_FPS = 20;
7
+ export declare const EXPANDED_MAX_VISIBLE_LINES = 10;
@@ -1,3 +1,8 @@
1
1
  import { Writable } from 'stream';
2
2
  export type Callback = (lines: string[]) => undefined;
3
- export default function addLines(fn: Callback): Writable;
3
+ interface BatchOptions {
4
+ maxLines?: number;
5
+ maxWait?: number;
6
+ }
7
+ export default function addLines(fn: Callback, options?: BatchOptions): Writable;
8
+ export {};
@@ -0,0 +1,2 @@
1
+ export type GroupWidth = number | `${number}%` | 'max';
2
+ export declare function calculateColumnWidth(groupWidth: GroupWidth, terminalWidth: number, maxGroupLength: number): number;