spawn-term 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/cjs/components/App.js +77 -17
  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 +10 -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 +77 -15
  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 +10 -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,21 @@ 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);
33
- // Derived state
34
- var completedProcesses = _processStorets.processStore.getCompletedProcesses();
35
- var runningProcesses = _processStorets.processStore.getRunningProcesses();
36
+ var expandedId = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getExpandedId);
37
+ var scrollOffset = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getScrollOffset);
38
+ // Subscribed state that triggers re-renders
39
+ var header = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getHeader);
40
+ var showStatusBar = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getShowStatusBar);
41
+ var isInteractive = (0, _react.useSyncExternalStore)(_processStorets.processStore.subscribe, _processStorets.processStore.getIsInteractive);
42
+ // Derived state (computed from processes which is already subscribed)
36
43
  var failedProcesses = _processStorets.processStore.getFailedProcesses();
37
44
  var runningCount = _processStorets.processStore.getRunningCount();
38
45
  var doneCount = _processStorets.processStore.getDoneCount();
39
46
  var errorCount = _processStorets.processStore.getErrorCount();
40
47
  var errorLineCount = _processStorets.processStore.getErrorLineCount();
48
+ var isAllComplete = _processStorets.processStore.isAllComplete();
41
49
  // Handle exit signal
42
50
  (0, _react.useEffect)(function() {
43
51
  if (shouldExit) {
@@ -47,15 +55,61 @@ function App() {
47
55
  shouldExit,
48
56
  exit
49
57
  ]);
58
+ // Auto-enter interactive mode when all complete and interactive flag is set
59
+ (0, _react.useEffect)(function() {
60
+ if (isAllComplete && isInteractive && mode === 'normal') {
61
+ _processStorets.processStore.setMode('interactive');
62
+ }
63
+ }, [
64
+ isAllComplete,
65
+ isInteractive,
66
+ mode
67
+ ]);
50
68
  // Keyboard handling (only active when raw mode is supported)
51
69
  (0, _ink.useInput)(function(input, key) {
52
70
  if (mode === 'normal') {
53
71
  if (input === 'e' && errorCount > 0) {
54
72
  _processStorets.processStore.setMode('errorList');
55
73
  }
74
+ } else if (mode === 'interactive') {
75
+ if (input === 'q' || key.escape) {
76
+ if (expandedId) {
77
+ _processStorets.processStore.collapse();
78
+ } else {
79
+ _processStorets.processStore.signalExit(function() {});
80
+ }
81
+ } else if (key.return) {
82
+ _processStorets.processStore.toggleExpand();
83
+ } else if (key.downArrow) {
84
+ if (expandedId) {
85
+ _processStorets.processStore.scrollDown(_constantsts.EXPANDED_MAX_VISIBLE_LINES);
86
+ } else {
87
+ _processStorets.processStore.selectNext();
88
+ }
89
+ } else if (key.upArrow) {
90
+ if (expandedId) {
91
+ _processStorets.processStore.scrollUp();
92
+ } else {
93
+ _processStorets.processStore.selectPrev();
94
+ }
95
+ } else if (input === 'j') {
96
+ if (expandedId) {
97
+ _processStorets.processStore.scrollDown(_constantsts.EXPANDED_MAX_VISIBLE_LINES);
98
+ } else {
99
+ _processStorets.processStore.selectNext();
100
+ }
101
+ } else if (input === 'k') {
102
+ if (expandedId) {
103
+ _processStorets.processStore.scrollUp();
104
+ } else {
105
+ _processStorets.processStore.selectPrev();
106
+ }
107
+ } else if (input === 'e' && errorCount > 0) {
108
+ _processStorets.processStore.setMode('errorList');
109
+ }
56
110
  } else if (mode === 'errorList') {
57
111
  if (key.escape) {
58
- _processStorets.processStore.setMode('normal');
112
+ _processStorets.processStore.setMode(isInteractive ? 'interactive' : 'normal');
59
113
  } else if (key.downArrow) {
60
114
  _processStorets.processStore.selectNextError();
61
115
  } else if (key.upArrow) {
@@ -96,25 +150,31 @@ function App() {
96
150
  // Fallback if no error selected
97
151
  _processStorets.processStore.setMode('errorList');
98
152
  }
99
- // Normal view
153
+ // Normal/Interactive view - render in original registration order
154
+ var showSelection = mode === 'interactive';
100
155
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
101
156
  flexDirection: "column",
102
157
  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
- }
158
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
159
+ children: header || '(loading...)'
110
160
  }),
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
161
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {}),
162
+ processes.map(function(item, index) {
163
+ return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
164
+ flexDirection: "column",
165
+ children: [
166
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_CompactProcessLinets.default, {
167
+ item: item,
168
+ isSelected: showSelection && index === selectedIndex
169
+ }),
170
+ expandedId === item.id && /*#__PURE__*/ (0, _jsxruntime.jsx)(_ExpandedOutputts.default, {
171
+ lines: item.lines,
172
+ scrollOffset: scrollOffset
173
+ })
174
+ ]
115
175
  }, item.id);
116
176
  }),
117
- processes.length > 0 && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
177
+ showStatusBar && processes.length > 0 && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
118
178
  children: [
119
179
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {}),
120
180
  /*#__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 // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(processStore.subscribe, processStore.getHeader);\n const showStatusBar = useSyncExternalStore(processStore.subscribe, processStore.getShowStatusBar);\n const isInteractive = useSyncExternalStore(processStore.subscribe, processStore.getIsInteractive);\n\n // Derived state (computed from processes which is already subscribed)\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 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 - always render a line */}\n <Text>{header || '(loading...)'}</Text>\n <Divider />\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","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","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","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":";;;;+BAsCA;;;eAAA;;;;mBAtCqC;qBACP;gEACV;wBACiB;8BACR;uBAEJ;gEACL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEpB,oHAAoH;AACpH,IAAMA,UAAU;IACdC,UAAU;IACVC,QAAQ;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;KAAI;AAC5D;AAOA,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,KAAmC;QAAjCC,OAAF,MAAEA,0BAAF,MAAQC,YAAAA,4CAAa;IACnE,IAAM,AAAEC,SAAWC,IAAAA,cAAS,IAApBD;IACR,IAAME,gBAAgBF,CAAAA,mBAAAA,6BAAAA,OAAQG,OAAO,KAAI;IAEzC,IAAQC,QAA+BN,KAA/BM,OAAOC,QAAwBP,KAAxBO,OAAOC,QAAiBR,KAAjBQ,OAAOnB,QAAUW,KAAVX;IAC7B,IAAMoB,qBAAqBR,aAAaS,kBAAO,CAACC,OAAO,GAAG;IAE1D,iDAAiD;IACjD,IAAMC,cAAcN,SAASC;IAE7B,oEAAoE;IACpE,IAAMM,YAAY,GAAG,eAAe;IACpC,IAAMC,iBAAiBC,4BAAY,CAACC,iBAAiB;IACrD,IAAMC,kBAAkBC,IAAAA,8BAAoB,EAAC,OAAOd,eAAeU;IACnE,IAAMK,MAAM,GAAG,gCAAgC;IAC/C,IAAMC,cAAchB,gBAAgBS,YAAYI,kBAAkBE;IAElE,kDAAkD;IAClD,IAAME,gBAAgBtC,SAAS6B,aAAaK,iBAAiBK,MAAM,CAACL;IAEpE,6BAA6B;IAC7B,IAAMM,aAAaC,IAAAA,cAAO,EAAC;QACzB,IAAIhB,UAAU,WAAW;YACvB,IAAMiB,WAAWrC,kBAAkBC;YACnC,OAAOoC,WAAW1C,SAAS0C,UAAUL,eAAe;QACtD;QACA,IAAIZ,UAAU,SAAS;YACrB,IAAMkB,aAAalC,cAAcH;YACjC,OAAOqC,aAAa,IAAI,AAAC,GAAqBA,OAAnBA,YAAW,UAAkC,OAA1BA,aAAa,IAAI,MAAM,MAAO;QAC9E;QACA,OAAO,IAAI,2BAA2B;IACxC,GAAG;QAAClB;QAAOnB;QAAO+B;KAAY;IAE9B,sBAAsB;IACtB,IAAMO,OAAOH,IAAAA,cAAO,EAAC;QACnB,OAAQhB;YACN,KAAK;gBACH,qBAAO,qBAACoB,kBAAO,qBAAKhD;YACtB,KAAK;gBACH,qBAAO,qBAACiD,SAAI;oBAACC,OAAM;8BAASpB,kBAAO,CAACqB,IAAI;;YAC1C,KAAK;gBACH,qBAAO,qBAACF,SAAI;oBAACC,OAAM;8BAAOpB,kBAAO,CAACsB,KAAK;;QAC3C;IACF,GAAG;QAACxB;KAAM;IAEV,oBAAoB;IACpB,IAAMyB,cAAczB,UAAU,UAAU,QAAQ;IAEhD,qBACE,sBAAC0B,QAAG;;0BACF,qBAACL,SAAI;gBAACC,OAAO7B,aAAa,SAASkC;0BAAY1B;;0BAC/C,qBAACyB,QAAG;gBAACE,OAAOvB;0BAAYc;;0BACxB,qBAACE,SAAI;gBAACQ,SAASpC;0BAAaoB;;YAC3BE,4BAAc,sBAACM,SAAI;gBAACC,OAAOG;;oBAAa;oBAAEV;;;;;AAGjD"}
@@ -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,10 @@ 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
+ inkApp = (0, _ink.render)(/*#__PURE__*/ (0, _jsxruntime.jsx)(_Appts.default, {}), {
29
+ incrementalRendering: false,
30
+ maxFps: _constantsts.DEFAULT_MAX_FPS
31
+ });
28
32
  return _processStorets.processStore;
29
33
  },
30
34
  release: function release(callback) {
@@ -33,16 +37,11 @@ function createApp() {
33
37
  return;
34
38
  }
35
39
  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
- });
40
+ // Signal exit to React component
41
+ _processStorets.processStore.signalExit(function() {
42
+ _processStorets.processStore.reset();
43
+ process.stdout.write('\x1b[?25h'); // show cursor
44
+ callback();
46
45
  });
47
46
  // Wait for Ink to finish, then call the callback
48
47
  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 inkApp = render(<App />, {\n incrementalRendering: false,\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;YAC9EF,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;