spawn-term 1.1.8 → 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 (73) hide show
  1. package/dist/cjs/components/App.js +159 -9
  2. package/dist/cjs/components/App.js.map +1 -1
  3. package/dist/cjs/components/CompactProcessLine.js +162 -0
  4. package/dist/cjs/components/CompactProcessLine.js.map +1 -0
  5. package/dist/cjs/components/Divider.js +24 -0
  6. package/dist/cjs/components/Divider.js.map +1 -0
  7. package/dist/cjs/components/ErrorDetailModal.js +115 -0
  8. package/dist/cjs/components/ErrorDetailModal.js.map +1 -0
  9. package/dist/cjs/components/ErrorListModal.js +135 -0
  10. package/dist/cjs/components/ErrorListModal.js.map +1 -0
  11. package/dist/cjs/components/ExpandedOutput.js +55 -0
  12. package/dist/cjs/components/ExpandedOutput.js.map +1 -0
  13. package/dist/cjs/components/StatusBar.js +104 -0
  14. package/dist/cjs/components/StatusBar.js.map +1 -0
  15. package/dist/cjs/constants.js +42 -0
  16. package/dist/cjs/constants.js.map +1 -0
  17. package/dist/cjs/createApp.js +7 -2
  18. package/dist/cjs/createApp.js.map +1 -1
  19. package/dist/cjs/lib/addLines.js +58 -4
  20. package/dist/cjs/lib/addLines.js.map +1 -1
  21. package/dist/cjs/lib/format.js +21 -0
  22. package/dist/cjs/lib/format.js.map +1 -0
  23. package/dist/cjs/src/components/CompactProcessLine.d.ts +7 -0
  24. package/dist/cjs/src/components/Divider.d.ts +2 -0
  25. package/dist/cjs/src/components/ErrorDetailModal.d.ts +8 -0
  26. package/dist/cjs/src/components/ErrorListModal.d.ts +8 -0
  27. package/dist/cjs/src/components/ExpandedOutput.d.ts +8 -0
  28. package/dist/cjs/src/components/StatusBar.d.ts +8 -0
  29. package/dist/cjs/src/constants.d.ts +7 -0
  30. package/dist/cjs/src/lib/addLines.d.ts +6 -1
  31. package/dist/cjs/src/lib/format.d.ts +2 -0
  32. package/dist/cjs/src/state/processStore.d.ts +36 -0
  33. package/dist/cjs/src/types.d.ts +6 -0
  34. package/dist/cjs/state/processStore.js +202 -0
  35. package/dist/cjs/state/processStore.js.map +1 -1
  36. package/dist/cjs/types.js.map +1 -1
  37. package/dist/esm/components/App.js +159 -9
  38. package/dist/esm/components/App.js.map +1 -1
  39. package/dist/esm/components/CompactProcessLine.js +143 -0
  40. package/dist/esm/components/CompactProcessLine.js.map +1 -0
  41. package/dist/esm/components/Divider.js +13 -0
  42. package/dist/esm/components/Divider.js.map +1 -0
  43. package/dist/esm/components/ErrorDetailModal.js +99 -0
  44. package/dist/esm/components/ErrorDetailModal.js.map +1 -0
  45. package/dist/esm/components/ErrorListModal.js +116 -0
  46. package/dist/esm/components/ErrorListModal.js.map +1 -0
  47. package/dist/esm/components/ExpandedOutput.js +41 -0
  48. package/dist/esm/components/ExpandedOutput.js.map +1 -0
  49. package/dist/esm/components/StatusBar.js +87 -0
  50. package/dist/esm/components/StatusBar.js.map +1 -0
  51. package/dist/esm/constants.js +11 -0
  52. package/dist/esm/constants.js.map +1 -0
  53. package/dist/esm/createApp.js +7 -2
  54. package/dist/esm/createApp.js.map +1 -1
  55. package/dist/esm/lib/addLines.js +32 -5
  56. package/dist/esm/lib/addLines.js.map +1 -1
  57. package/dist/esm/lib/format.js +10 -0
  58. package/dist/esm/lib/format.js.map +1 -0
  59. package/dist/esm/src/components/CompactProcessLine.d.ts +7 -0
  60. package/dist/esm/src/components/Divider.d.ts +2 -0
  61. package/dist/esm/src/components/ErrorDetailModal.d.ts +8 -0
  62. package/dist/esm/src/components/ErrorListModal.d.ts +8 -0
  63. package/dist/esm/src/components/ExpandedOutput.d.ts +8 -0
  64. package/dist/esm/src/components/StatusBar.d.ts +8 -0
  65. package/dist/esm/src/constants.d.ts +7 -0
  66. package/dist/esm/src/lib/addLines.d.ts +6 -1
  67. package/dist/esm/src/lib/format.d.ts +2 -0
  68. package/dist/esm/src/state/processStore.d.ts +36 -0
  69. package/dist/esm/src/types.d.ts +6 -0
  70. package/dist/esm/state/processStore.js +146 -0
  71. package/dist/esm/state/processStore.js.map +1 -1
  72. package/dist/esm/types.js.map +1 -1
  73. package/package.json +1 -1
@@ -0,0 +1,143 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _object_spread(target) {
15
+ for(var i = 1; i < arguments.length; i++){
16
+ var source = arguments[i] != null ? arguments[i] : {};
17
+ var ownKeys = Object.keys(source);
18
+ if (typeof Object.getOwnPropertySymbols === "function") {
19
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
20
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
21
+ }));
22
+ }
23
+ ownKeys.forEach(function(key) {
24
+ _define_property(target, key, source[key]);
25
+ });
26
+ }
27
+ return target;
28
+ }
29
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
30
+ import { Box, Text, useStdout } from 'ink';
31
+ import { memo, useMemo } from 'react';
32
+ import figures from '../lib/figures.js';
33
+ import { calculateColumnWidth } from '../lib/format.js';
34
+ import { processStore } from '../state/processStore.js';
35
+ import { LineType } from '../types.js';
36
+ import Spinner from './Spinner.js';
37
+ // From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2
38
+ const SPINNER = {
39
+ interval: 80,
40
+ frames: [
41
+ '⠋',
42
+ '⠙',
43
+ '⠹',
44
+ '⠸',
45
+ '⠼',
46
+ '⠴',
47
+ '⠦',
48
+ '⠧',
49
+ '⠇',
50
+ '⠏'
51
+ ]
52
+ };
53
+ function truncate(str, maxLength) {
54
+ if (str.length <= maxLength) return str;
55
+ return `${str.slice(0, maxLength - 1)}…`;
56
+ }
57
+ function getLastOutputLine(lines) {
58
+ for(let i = lines.length - 1; i >= 0; i--){
59
+ if (lines[i].text.length > 0) {
60
+ return lines[i].text;
61
+ }
62
+ }
63
+ return '';
64
+ }
65
+ function getErrorCount(lines) {
66
+ return lines.filter((line)=>line.type === LineType.stderr).length;
67
+ }
68
+ export default /*#__PURE__*/ memo(function CompactProcessLine({ item, isSelected = false }) {
69
+ const { stdout } = useStdout();
70
+ const terminalWidth = (stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80;
71
+ const { group, title, state, lines } = item;
72
+ const selectionIndicator = isSelected ? figures.pointer : ' ';
73
+ // Display name: prefer group, fall back to title
74
+ const displayName = group || title;
75
+ // Calculate widths - use dynamic column width based on longest name
76
+ const iconWidth = 2; // icon + space
77
+ const maxGroupLength = processStore.getMaxGroupLength();
78
+ const nameColumnWidth = calculateColumnWidth('max', terminalWidth, maxGroupLength);
79
+ const gap = 1; // space between name and status
80
+ const statusWidth = terminalWidth - iconWidth - nameColumnWidth - gap;
81
+ // Truncate name if needed and pad to column width
82
+ const truncatedName = truncate(displayName, nameColumnWidth).padEnd(nameColumnWidth);
83
+ // Status text based on state
84
+ const statusText = useMemo(()=>{
85
+ if (state === 'running') {
86
+ const lastLine = getLastOutputLine(lines);
87
+ return lastLine ? truncate(lastLine, statusWidth) : '';
88
+ }
89
+ if (state === 'error') {
90
+ const errorCount = getErrorCount(lines);
91
+ return errorCount > 0 ? `${errorCount} error${errorCount > 1 ? 's' : ''}` : 'failed';
92
+ }
93
+ return ''; // success - no status text
94
+ }, [
95
+ state,
96
+ lines,
97
+ statusWidth
98
+ ]);
99
+ // Icon based on state
100
+ const icon = useMemo(()=>{
101
+ switch(state){
102
+ case 'running':
103
+ return /*#__PURE__*/ _jsx(Spinner, _object_spread({}, SPINNER));
104
+ case 'success':
105
+ return /*#__PURE__*/ _jsx(Text, {
106
+ color: "green",
107
+ children: figures.tick
108
+ });
109
+ case 'error':
110
+ return /*#__PURE__*/ _jsx(Text, {
111
+ color: "red",
112
+ children: figures.cross
113
+ });
114
+ }
115
+ }, [
116
+ state
117
+ ]);
118
+ // Status text color
119
+ const statusColor = state === 'error' ? 'red' : 'gray';
120
+ return /*#__PURE__*/ _jsxs(Box, {
121
+ children: [
122
+ /*#__PURE__*/ _jsx(Text, {
123
+ color: isSelected ? 'cyan' : undefined,
124
+ children: selectionIndicator
125
+ }),
126
+ /*#__PURE__*/ _jsx(Box, {
127
+ width: iconWidth,
128
+ children: icon
129
+ }),
130
+ /*#__PURE__*/ _jsx(Text, {
131
+ inverse: isSelected,
132
+ children: truncatedName
133
+ }),
134
+ statusText && /*#__PURE__*/ _jsxs(Text, {
135
+ color: statusColor,
136
+ children: [
137
+ " ",
138
+ statusText
139
+ ]
140
+ })
141
+ ]
142
+ });
143
+ });
@@ -0,0 +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 { 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":["Box","Text","useStdout","memo","useMemo","figures","calculateColumnWidth","processStore","LineType","Spinner","SPINNER","interval","frames","truncate","str","maxLength","length","slice","getLastOutputLine","lines","i","text","getErrorCount","filter","line","type","stderr","CompactProcessLine","item","isSelected","stdout","terminalWidth","columns","group","title","state","selectionIndicator","pointer","displayName","iconWidth","maxGroupLength","getMaxGroupLength","nameColumnWidth","gap","statusWidth","truncatedName","padEnd","statusText","lastLine","errorCount","icon","color","tick","cross","statusColor","undefined","width","inverse"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text, useStdout } from 'ink';
3
+ import { memo } from 'react';
4
+ export default /*#__PURE__*/ memo(function Divider() {
5
+ const { stdout } = useStdout();
6
+ const width = (stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80;
7
+ return /*#__PURE__*/ _jsx(Box, {
8
+ children: /*#__PURE__*/ _jsx(Text, {
9
+ dimColor: true,
10
+ children: '─'.repeat(width)
11
+ })
12
+ });
13
+ });
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/Divider.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo } from 'react';\n\nexport default memo(function Divider() {\n const { stdout } = useStdout();\n const width = stdout?.columns || 80;\n\n return (\n <Box>\n <Text dimColor>{'─'.repeat(width)}</Text>\n </Box>\n );\n});\n"],"names":["Box","Text","useStdout","memo","Divider","stdout","width","columns","dimColor","repeat"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,EAAEC,SAAS,QAAQ,MAAM;AAC3C,SAASC,IAAI,QAAQ,QAAQ;AAE7B,6BAAeA,KAAK,SAASC;IAC3B,MAAM,EAAEC,MAAM,EAAE,GAAGH;IACnB,MAAMI,QAAQD,CAAAA,mBAAAA,6BAAAA,OAAQE,OAAO,KAAI;IAEjC,qBACE,KAACP;kBACC,cAAA,KAACC;YAAKO,QAAQ;sBAAE,IAAIC,MAAM,CAACH;;;AAGjC,GAAG"}
@@ -0,0 +1,99 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useStdout } from 'ink';
3
+ import { memo } from 'react';
4
+ import { LineType } from '../types.js';
5
+ export default /*#__PURE__*/ memo(function ErrorDetailModal({ error, currentIndex, totalErrors }) {
6
+ const { stdout } = useStdout();
7
+ const width = (stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80;
8
+ const borderH = '─'.repeat(width - 2);
9
+ const title = ` Error Detail (${currentIndex + 1}/${totalErrors}) `;
10
+ const titleBorder = `┌${title}${borderH.slice(title.length + 1)}┐`;
11
+ const name = error.group || error.title;
12
+ const stderrLines = error.lines.filter((l)=>l.type === LineType.stderr);
13
+ return /*#__PURE__*/ _jsxs(Box, {
14
+ flexDirection: "column",
15
+ children: [
16
+ /*#__PURE__*/ _jsx(Text, {
17
+ children: titleBorder
18
+ }),
19
+ /*#__PURE__*/ _jsx(Box, {
20
+ paddingX: 1,
21
+ children: /*#__PURE__*/ _jsx(Text, {
22
+ bold: true,
23
+ children: name
24
+ })
25
+ }),
26
+ /*#__PURE__*/ _jsxs(Box, {
27
+ paddingX: 1,
28
+ children: [
29
+ /*#__PURE__*/ _jsx(Text, {
30
+ dimColor: true,
31
+ children: "Command: "
32
+ }),
33
+ /*#__PURE__*/ _jsx(Text, {
34
+ children: error.title
35
+ })
36
+ ]
37
+ }),
38
+ /*#__PURE__*/ _jsx(Box, {
39
+ paddingX: 1,
40
+ children: /*#__PURE__*/ _jsx(Text, {
41
+ dimColor: true,
42
+ children: '─'.repeat(width - 4)
43
+ })
44
+ }),
45
+ /*#__PURE__*/ _jsx(Box, {
46
+ paddingX: 1,
47
+ children: /*#__PURE__*/ _jsx(Text, {
48
+ dimColor: true,
49
+ children: "stderr:"
50
+ })
51
+ }),
52
+ /*#__PURE__*/ _jsx(Box, {
53
+ flexDirection: "column",
54
+ paddingX: 2,
55
+ children: stderrLines.length === 0 ? /*#__PURE__*/ _jsx(Text, {
56
+ dimColor: true,
57
+ children: "(no stderr output)"
58
+ }) : stderrLines.map((line, index)=>/*#__PURE__*/ _jsx(Text, {
59
+ color: "red",
60
+ children: line.text
61
+ }, `stderr-${index}-${line.text.slice(0, 20)}`))
62
+ }),
63
+ /*#__PURE__*/ _jsx(Box, {
64
+ paddingX: 1,
65
+ children: /*#__PURE__*/ _jsx(Text, {
66
+ children: " "
67
+ })
68
+ }),
69
+ /*#__PURE__*/ _jsxs(Text, {
70
+ children: [
71
+ "├",
72
+ borderH,
73
+ "┤"
74
+ ]
75
+ }),
76
+ /*#__PURE__*/ _jsxs(Box, {
77
+ paddingX: 1,
78
+ justifyContent: "space-between",
79
+ children: [
80
+ /*#__PURE__*/ _jsx(Text, {
81
+ dimColor: true,
82
+ children: "[↑↓] prev/next error"
83
+ }),
84
+ /*#__PURE__*/ _jsx(Text, {
85
+ dimColor: true,
86
+ children: "[Esc] back to list"
87
+ })
88
+ ]
89
+ }),
90
+ /*#__PURE__*/ _jsxs(Text, {
91
+ children: [
92
+ "└",
93
+ borderH,
94
+ "┘"
95
+ ]
96
+ })
97
+ ]
98
+ });
99
+ });
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ErrorDetailModal.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo } from 'react';\nimport type { ChildProcess } from '../types.ts';\nimport { LineType } from '../types.ts';\n\ntype Props = {\n error: ChildProcess;\n currentIndex: number;\n totalErrors: number;\n};\n\nexport default memo(function ErrorDetailModal({ error, currentIndex, totalErrors }: Props) {\n const { stdout } = useStdout();\n const width = stdout?.columns || 80;\n\n const borderH = '─'.repeat(width - 2);\n const title = ` Error Detail (${currentIndex + 1}/${totalErrors}) `;\n const titleBorder = `┌${title}${borderH.slice(title.length + 1)}┐`;\n\n const name = error.group || error.title;\n const stderrLines = error.lines.filter((l) => l.type === LineType.stderr);\n\n return (\n <Box flexDirection=\"column\">\n {/* Top border with title */}\n <Text>{titleBorder}</Text>\n\n {/* Process name */}\n <Box paddingX={1}>\n <Text bold>{name}</Text>\n </Box>\n\n {/* Command */}\n <Box paddingX={1}>\n <Text dimColor>Command: </Text>\n <Text>{error.title}</Text>\n </Box>\n\n {/* Separator */}\n <Box paddingX={1}>\n <Text dimColor>{'─'.repeat(width - 4)}</Text>\n </Box>\n\n {/* Error output */}\n <Box paddingX={1}>\n <Text dimColor>stderr:</Text>\n </Box>\n\n {/* Error lines in a box */}\n <Box flexDirection=\"column\" paddingX={2}>\n {stderrLines.length === 0 ? (\n <Text dimColor>(no stderr output)</Text>\n ) : (\n stderrLines.map((line, index) => (\n <Text key={`stderr-${index}-${line.text.slice(0, 20)}`} color=\"red\">\n {line.text}\n </Text>\n ))\n )}\n </Box>\n\n {/* Empty line for padding */}\n <Box paddingX={1}>\n <Text> </Text>\n </Box>\n\n {/* Bottom border with hints */}\n <Text>├{borderH}┤</Text>\n <Box paddingX={1} justifyContent=\"space-between\">\n <Text dimColor>[↑↓] prev/next error</Text>\n <Text dimColor>[Esc] back to list</Text>\n </Box>\n <Text>└{borderH}┘</Text>\n </Box>\n );\n});\n"],"names":["Box","Text","useStdout","memo","LineType","ErrorDetailModal","error","currentIndex","totalErrors","stdout","width","columns","borderH","repeat","title","titleBorder","slice","length","name","group","stderrLines","lines","filter","l","type","stderr","flexDirection","paddingX","bold","dimColor","map","line","index","color","text","justifyContent"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,EAAEC,SAAS,QAAQ,MAAM;AAC3C,SAASC,IAAI,QAAQ,QAAQ;AAE7B,SAASC,QAAQ,QAAQ,cAAc;AAQvC,6BAAeD,KAAK,SAASE,iBAAiB,EAAEC,KAAK,EAAEC,YAAY,EAAEC,WAAW,EAAS;IACvF,MAAM,EAAEC,MAAM,EAAE,GAAGP;IACnB,MAAMQ,QAAQD,CAAAA,mBAAAA,6BAAAA,OAAQE,OAAO,KAAI;IAEjC,MAAMC,UAAU,IAAIC,MAAM,CAACH,QAAQ;IACnC,MAAMI,QAAQ,CAAC,eAAe,EAAEP,eAAe,EAAE,CAAC,EAAEC,YAAY,EAAE,CAAC;IACnE,MAAMO,cAAc,CAAC,CAAC,EAAED,QAAQF,QAAQI,KAAK,CAACF,MAAMG,MAAM,GAAG,GAAG,CAAC,CAAC;IAElE,MAAMC,OAAOZ,MAAMa,KAAK,IAAIb,MAAMQ,KAAK;IACvC,MAAMM,cAAcd,MAAMe,KAAK,CAACC,MAAM,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKpB,SAASqB,MAAM;IAExE,qBACE,MAACzB;QAAI0B,eAAc;;0BAEjB,KAACzB;0BAAMc;;0BAGP,KAACf;gBAAI2B,UAAU;0BACb,cAAA,KAAC1B;oBAAK2B,IAAI;8BAAEV;;;0BAId,MAAClB;gBAAI2B,UAAU;;kCACb,KAAC1B;wBAAK4B,QAAQ;kCAAC;;kCACf,KAAC5B;kCAAMK,MAAMQ,KAAK;;;;0BAIpB,KAACd;gBAAI2B,UAAU;0BACb,cAAA,KAAC1B;oBAAK4B,QAAQ;8BAAE,IAAIhB,MAAM,CAACH,QAAQ;;;0BAIrC,KAACV;gBAAI2B,UAAU;0BACb,cAAA,KAAC1B;oBAAK4B,QAAQ;8BAAC;;;0BAIjB,KAAC7B;gBAAI0B,eAAc;gBAASC,UAAU;0BACnCP,YAAYH,MAAM,KAAK,kBACtB,KAAChB;oBAAK4B,QAAQ;8BAAC;qBAEfT,YAAYU,GAAG,CAAC,CAACC,MAAMC,sBACrB,KAAC/B;wBAAuDgC,OAAM;kCAC3DF,KAAKG,IAAI;uBADD,CAAC,OAAO,EAAEF,MAAM,CAAC,EAAED,KAAKG,IAAI,CAAClB,KAAK,CAAC,GAAG,KAAK;;0BAQ5D,KAAChB;gBAAI2B,UAAU;0BACb,cAAA,KAAC1B;8BAAK;;;0BAIR,MAACA;;oBAAK;oBAAEW;oBAAQ;;;0BAChB,MAACZ;gBAAI2B,UAAU;gBAAGQ,gBAAe;;kCAC/B,KAAClC;wBAAK4B,QAAQ;kCAAC;;kCACf,KAAC5B;wBAAK4B,QAAQ;kCAAC;;;;0BAEjB,MAAC5B;;oBAAK;oBAAEW;oBAAQ;;;;;AAGtB,GAAG"}
@@ -0,0 +1,116 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useStdout } from 'ink';
3
+ import { memo } from 'react';
4
+ import figures from '../lib/figures.js';
5
+ import { LineType } from '../types.js';
6
+ function truncate(str, maxLength) {
7
+ if (str.length <= maxLength) return str;
8
+ return `${str.slice(0, maxLength - 1)}…`;
9
+ }
10
+ function getErrorLineCount(process) {
11
+ return process.lines.filter((l)=>l.type === LineType.stderr).length;
12
+ }
13
+ export default /*#__PURE__*/ memo(function ErrorListModal({ errors, selectedIndex, totalErrorLines }) {
14
+ const { stdout } = useStdout();
15
+ const width = (stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80;
16
+ const innerWidth = width - 4; // 2 chars padding each side
17
+ const borderH = '─'.repeat(width - 2);
18
+ const title = ' Errors ';
19
+ const titleBorder = `┌${title}${borderH.slice(title.length + 1)}┐`;
20
+ return /*#__PURE__*/ _jsxs(Box, {
21
+ flexDirection: "column",
22
+ children: [
23
+ /*#__PURE__*/ _jsx(Text, {
24
+ children: titleBorder
25
+ }),
26
+ /*#__PURE__*/ _jsx(Box, {
27
+ paddingX: 1,
28
+ children: /*#__PURE__*/ _jsxs(Text, {
29
+ children: [
30
+ errors.length,
31
+ " process",
32
+ errors.length !== 1 ? 'es' : '',
33
+ " failed (",
34
+ totalErrorLines,
35
+ " error line",
36
+ totalErrorLines !== 1 ? 's' : '',
37
+ " total)"
38
+ ]
39
+ })
40
+ }),
41
+ /*#__PURE__*/ _jsx(Box, {
42
+ paddingX: 1,
43
+ children: /*#__PURE__*/ _jsx(Text, {
44
+ children: " "
45
+ })
46
+ }),
47
+ errors.map((error, index)=>{
48
+ const isSelected = index === selectedIndex;
49
+ const indicator = isSelected ? figures.pointer : ' ';
50
+ const name = error.group || error.title;
51
+ const lineCount = getErrorLineCount(error);
52
+ const lineText = `${lineCount} line${lineCount !== 1 ? 's' : ''}`;
53
+ // Calculate available space for name
54
+ const prefixLen = 3; // indicator + space + space
55
+ const suffixLen = lineText.length + 2; // space + lineText
56
+ const maxNameLen = innerWidth - prefixLen - suffixLen;
57
+ const truncatedName = truncate(name, maxNameLen);
58
+ return /*#__PURE__*/ _jsxs(Box, {
59
+ paddingX: 1,
60
+ children: [
61
+ /*#__PURE__*/ _jsxs(Text, {
62
+ color: isSelected ? 'cyan' : undefined,
63
+ bold: isSelected,
64
+ children: [
65
+ indicator,
66
+ " ",
67
+ truncatedName
68
+ ]
69
+ }),
70
+ /*#__PURE__*/ _jsx(Box, {
71
+ flexGrow: 1
72
+ }),
73
+ /*#__PURE__*/ _jsx(Text, {
74
+ dimColor: true,
75
+ children: lineText
76
+ })
77
+ ]
78
+ }, error.id);
79
+ }),
80
+ /*#__PURE__*/ _jsx(Box, {
81
+ paddingX: 1,
82
+ children: /*#__PURE__*/ _jsx(Text, {
83
+ children: " "
84
+ })
85
+ }),
86
+ /*#__PURE__*/ _jsxs(Text, {
87
+ children: [
88
+ "├",
89
+ borderH,
90
+ "┤"
91
+ ]
92
+ }),
93
+ /*#__PURE__*/ _jsxs(Box, {
94
+ paddingX: 1,
95
+ justifyContent: "space-between",
96
+ children: [
97
+ /*#__PURE__*/ _jsx(Text, {
98
+ dimColor: true,
99
+ children: "[↑↓] navigate [Enter] view details"
100
+ }),
101
+ /*#__PURE__*/ _jsx(Text, {
102
+ dimColor: true,
103
+ children: "[Esc] close"
104
+ })
105
+ ]
106
+ }),
107
+ /*#__PURE__*/ _jsxs(Text, {
108
+ children: [
109
+ "└",
110
+ borderH,
111
+ "┘"
112
+ ]
113
+ })
114
+ ]
115
+ });
116
+ });
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ErrorListModal.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo } from 'react';\nimport figures from '../lib/figures.ts';\nimport type { ChildProcess } from '../types.ts';\nimport { LineType } from '../types.ts';\n\ntype Props = {\n errors: ChildProcess[];\n selectedIndex: number;\n totalErrorLines: number;\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 getErrorLineCount(process: ChildProcess): number {\n return process.lines.filter((l) => l.type === LineType.stderr).length;\n}\n\nexport default memo(function ErrorListModal({ errors, selectedIndex, totalErrorLines }: Props) {\n const { stdout } = useStdout();\n const width = stdout?.columns || 80;\n const innerWidth = width - 4; // 2 chars padding each side\n\n const borderH = '─'.repeat(width - 2);\n const title = ' Errors ';\n const titleBorder = `┌${title}${borderH.slice(title.length + 1)}┐`;\n\n return (\n <Box flexDirection=\"column\">\n {/* Top border with title */}\n <Text>{titleBorder}</Text>\n\n {/* Summary */}\n <Box paddingX={1}>\n <Text>\n {errors.length} process{errors.length !== 1 ? 'es' : ''} failed ({totalErrorLines} error line\n {totalErrorLines !== 1 ? 's' : ''} total)\n </Text>\n </Box>\n\n {/* Empty line */}\n <Box paddingX={1}>\n <Text> </Text>\n </Box>\n\n {/* Error list */}\n {errors.map((error, index) => {\n const isSelected = index === selectedIndex;\n const indicator = isSelected ? figures.pointer : ' ';\n const name = error.group || error.title;\n const lineCount = getErrorLineCount(error);\n const lineText = `${lineCount} line${lineCount !== 1 ? 's' : ''}`;\n\n // Calculate available space for name\n const prefixLen = 3; // indicator + space + space\n const suffixLen = lineText.length + 2; // space + lineText\n const maxNameLen = innerWidth - prefixLen - suffixLen;\n const truncatedName = truncate(name, maxNameLen);\n\n return (\n <Box key={error.id} paddingX={1}>\n <Text color={isSelected ? 'cyan' : undefined} bold={isSelected}>\n {indicator} {truncatedName}\n </Text>\n <Box flexGrow={1} />\n <Text dimColor>{lineText}</Text>\n </Box>\n );\n })}\n\n {/* Empty line for padding */}\n <Box paddingX={1}>\n <Text> </Text>\n </Box>\n\n {/* Bottom border with hints */}\n <Text>├{borderH}┤</Text>\n <Box paddingX={1} justifyContent=\"space-between\">\n <Text dimColor>[↑↓] navigate [Enter] view details</Text>\n <Text dimColor>[Esc] close</Text>\n </Box>\n <Text>└{borderH}┘</Text>\n </Box>\n );\n});\n"],"names":["Box","Text","useStdout","memo","figures","LineType","truncate","str","maxLength","length","slice","getErrorLineCount","process","lines","filter","l","type","stderr","ErrorListModal","errors","selectedIndex","totalErrorLines","stdout","width","columns","innerWidth","borderH","repeat","title","titleBorder","flexDirection","paddingX","map","error","index","isSelected","indicator","pointer","name","group","lineCount","lineText","prefixLen","suffixLen","maxNameLen","truncatedName","color","undefined","bold","flexGrow","dimColor","id","justifyContent"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,EAAEC,SAAS,QAAQ,MAAM;AAC3C,SAASC,IAAI,QAAQ,QAAQ;AAC7B,OAAOC,aAAa,oBAAoB;AAExC,SAASC,QAAQ,QAAQ,cAAc;AAQvC,SAASC,SAASC,GAAW,EAAEC,SAAiB;IAC9C,IAAID,IAAIE,MAAM,IAAID,WAAW,OAAOD;IACpC,OAAO,GAAGA,IAAIG,KAAK,CAAC,GAAGF,YAAY,GAAG,CAAC,CAAC;AAC1C;AAEA,SAASG,kBAAkBC,OAAqB;IAC9C,OAAOA,QAAQC,KAAK,CAACC,MAAM,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKX,SAASY,MAAM,EAAER,MAAM;AACvE;AAEA,6BAAeN,KAAK,SAASe,eAAe,EAAEC,MAAM,EAAEC,aAAa,EAAEC,eAAe,EAAS;IAC3F,MAAM,EAAEC,MAAM,EAAE,GAAGpB;IACnB,MAAMqB,QAAQD,CAAAA,mBAAAA,6BAAAA,OAAQE,OAAO,KAAI;IACjC,MAAMC,aAAaF,QAAQ,GAAG,4BAA4B;IAE1D,MAAMG,UAAU,IAAIC,MAAM,CAACJ,QAAQ;IACnC,MAAMK,QAAQ;IACd,MAAMC,cAAc,CAAC,CAAC,EAAED,QAAQF,QAAQhB,KAAK,CAACkB,MAAMnB,MAAM,GAAG,GAAG,CAAC,CAAC;IAElE,qBACE,MAACT;QAAI8B,eAAc;;0BAEjB,KAAC7B;0BAAM4B;;0BAGP,KAAC7B;gBAAI+B,UAAU;0BACb,cAAA,MAAC9B;;wBACEkB,OAAOV,MAAM;wBAAC;wBAASU,OAAOV,MAAM,KAAK,IAAI,OAAO;wBAAG;wBAAUY;wBAAgB;wBACjFA,oBAAoB,IAAI,MAAM;wBAAG;;;;0BAKtC,KAACrB;gBAAI+B,UAAU;0BACb,cAAA,KAAC9B;8BAAK;;;YAIPkB,OAAOa,GAAG,CAAC,CAACC,OAAOC;gBAClB,MAAMC,aAAaD,UAAUd;gBAC7B,MAAMgB,YAAYD,aAAa/B,QAAQiC,OAAO,GAAG;gBACjD,MAAMC,OAAOL,MAAMM,KAAK,IAAIN,MAAML,KAAK;gBACvC,MAAMY,YAAY7B,kBAAkBsB;gBACpC,MAAMQ,WAAW,GAAGD,UAAU,KAAK,EAAEA,cAAc,IAAI,MAAM,IAAI;gBAEjE,qCAAqC;gBACrC,MAAME,YAAY,GAAG,4BAA4B;gBACjD,MAAMC,YAAYF,SAAShC,MAAM,GAAG,GAAG,mBAAmB;gBAC1D,MAAMmC,aAAanB,aAAaiB,YAAYC;gBAC5C,MAAME,gBAAgBvC,SAASgC,MAAMM;gBAErC,qBACE,MAAC5C;oBAAmB+B,UAAU;;sCAC5B,MAAC9B;4BAAK6C,OAAOX,aAAa,SAASY;4BAAWC,MAAMb;;gCACjDC;gCAAU;gCAAES;;;sCAEf,KAAC7C;4BAAIiD,UAAU;;sCACf,KAAChD;4BAAKiD,QAAQ;sCAAET;;;mBALRR,MAAMkB,EAAE;YAQtB;0BAGA,KAACnD;gBAAI+B,UAAU;0BACb,cAAA,KAAC9B;8BAAK;;;0BAIR,MAACA;;oBAAK;oBAAEyB;oBAAQ;;;0BAChB,MAAC1B;gBAAI+B,UAAU;gBAAGqB,gBAAe;;kCAC/B,KAACnD;wBAAKiD,QAAQ;kCAAC;;kCACf,KAACjD;wBAAKiD,QAAQ;kCAAC;;;;0BAEjB,MAACjD;;oBAAK;oBAAEyB;oBAAQ;;;;;AAGtB,GAAG"}
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { memo } from 'react';
4
+ import { EXPANDED_MAX_VISIBLE_LINES } from '../constants.js';
5
+ import { LineType } from '../types.js';
6
+ export default /*#__PURE__*/ memo(function ExpandedOutput({ lines, scrollOffset, maxVisible = EXPANDED_MAX_VISIBLE_LINES }) {
7
+ const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);
8
+ const hasMore = lines.length > scrollOffset + maxVisible;
9
+ const remaining = lines.length - scrollOffset - maxVisible;
10
+ if (lines.length === 0) {
11
+ return /*#__PURE__*/ _jsx(Box, {
12
+ paddingLeft: 2,
13
+ children: /*#__PURE__*/ _jsx(Text, {
14
+ dimColor: true,
15
+ children: "│ (no output)"
16
+ })
17
+ });
18
+ }
19
+ return /*#__PURE__*/ _jsxs(Box, {
20
+ flexDirection: "column",
21
+ paddingLeft: 2,
22
+ children: [
23
+ visibleLines.map((line, i)=>// biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view
24
+ /*#__PURE__*/ _jsxs(Text, {
25
+ color: line.type === LineType.stderr ? 'red' : undefined,
26
+ children: [
27
+ "│ ",
28
+ line.text
29
+ ]
30
+ }, scrollOffset + i)),
31
+ hasMore && /*#__PURE__*/ _jsxs(Text, {
32
+ dimColor: true,
33
+ children: [
34
+ "│ [+",
35
+ remaining,
36
+ " more, j/k to scroll]"
37
+ ]
38
+ })
39
+ ]
40
+ });
41
+ });
@@ -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":["Box","Text","memo","EXPANDED_MAX_VISIBLE_LINES","LineType","ExpandedOutput","lines","scrollOffset","maxVisible","visibleLines","slice","hasMore","length","remaining","paddingLeft","dimColor","flexDirection","map","line","i","color","type","stderr","undefined","text"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,QAAQ,MAAM;AAChC,SAASC,IAAI,QAAQ,QAAQ;AAC7B,SAASC,0BAA0B,QAAQ,kBAAkB;AAE7D,SAASC,QAAQ,QAAQ,cAAc;AAQvC,6BAAeF,KAAK,SAASG,eAAe,EAAEC,KAAK,EAAEC,YAAY,EAAEC,aAAaL,0BAA0B,EAAS;IACjH,MAAMM,eAAeH,MAAMI,KAAK,CAACH,cAAcA,eAAeC;IAC9D,MAAMG,UAAUL,MAAMM,MAAM,GAAGL,eAAeC;IAC9C,MAAMK,YAAYP,MAAMM,MAAM,GAAGL,eAAeC;IAEhD,IAAIF,MAAMM,MAAM,KAAK,GAAG;QACtB,qBACE,KAACZ;YAAIc,aAAa;sBAChB,cAAA,KAACb;gBAAKc,QAAQ;0BAAC;;;IAGrB;IAEA,qBACE,MAACf;QAAIgB,eAAc;QAASF,aAAa;;YACtCL,aAAaQ,GAAG,CAAC,CAACC,MAAMC,IACvB,iHAAiH;8BACjH,MAAClB;oBAA4BmB,OAAOF,KAAKG,IAAI,KAAKjB,SAASkB,MAAM,GAAG,QAAQC;;wBAAW;wBAClFL,KAAKM,IAAI;;mBADHjB,eAAeY;YAI3BR,yBAAW,MAACV;gBAAKc,QAAQ;;oBAAC;oBAAKF;oBAAU;;;;;AAGhD,GAAG"}
@@ -0,0 +1,87 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _object_spread(target) {
15
+ for(var i = 1; i < arguments.length; i++){
16
+ var source = arguments[i] != null ? arguments[i] : {};
17
+ var ownKeys = Object.keys(source);
18
+ if (typeof Object.getOwnPropertySymbols === "function") {
19
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
20
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
21
+ }));
22
+ }
23
+ ownKeys.forEach(function(key) {
24
+ _define_property(target, key, source[key]);
25
+ });
26
+ }
27
+ return target;
28
+ }
29
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
30
+ import { Box, Text } from 'ink';
31
+ import { memo } from 'react';
32
+ import figures from '../lib/figures.js';
33
+ import Spinner from './Spinner.js';
34
+ // From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2
35
+ const SPINNER = {
36
+ interval: 80,
37
+ frames: [
38
+ '⠋',
39
+ '⠙',
40
+ '⠹',
41
+ '⠸',
42
+ '⠼',
43
+ '⠴',
44
+ '⠦',
45
+ '⠧',
46
+ '⠇',
47
+ '⠏'
48
+ ]
49
+ };
50
+ export default /*#__PURE__*/ memo(function StatusBar({ running, done, errors, errorLines }) {
51
+ return /*#__PURE__*/ _jsxs(Box, {
52
+ justifyContent: "space-between",
53
+ children: [
54
+ /*#__PURE__*/ _jsx(Box, {
55
+ children: /*#__PURE__*/ _jsxs(Text, {
56
+ children: [
57
+ running > 0 ? /*#__PURE__*/ _jsx(Spinner, _object_spread({}, SPINNER)) : /*#__PURE__*/ _jsx(Text, {
58
+ color: "green",
59
+ children: figures.tick
60
+ }),
61
+ ` Running: ${running} `,
62
+ /*#__PURE__*/ _jsx(Text, {
63
+ color: "green",
64
+ children: figures.tick
65
+ }),
66
+ ` Done: ${done} `,
67
+ /*#__PURE__*/ _jsx(Text, {
68
+ color: "red",
69
+ children: figures.cross
70
+ }),
71
+ ` Errors: ${errors}`,
72
+ errorLines > 0 && /*#__PURE__*/ _jsx(Text, {
73
+ dimColor: true,
74
+ children: ` (${errorLines} lines)`
75
+ })
76
+ ]
77
+ })
78
+ }),
79
+ errors > 0 && /*#__PURE__*/ _jsx(Box, {
80
+ children: /*#__PURE__*/ _jsx(Text, {
81
+ dimColor: true,
82
+ children: "[e]rrors"
83
+ })
84
+ })
85
+ ]
86
+ });
87
+ });
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/StatusBar.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport figures from '../lib/figures.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 running: number;\n done: number;\n errors: number;\n errorLines: number;\n};\n\nexport default memo(function StatusBar({ running, done, errors, errorLines }: Props) {\n return (\n <Box justifyContent=\"space-between\">\n <Box>\n <Text>\n {running > 0 ? <Spinner {...SPINNER} /> : <Text color=\"green\">{figures.tick}</Text>}\n {` Running: ${running} `}\n <Text color=\"green\">{figures.tick}</Text>\n {` Done: ${done} `}\n <Text color=\"red\">{figures.cross}</Text>\n {` Errors: ${errors}`}\n {errorLines > 0 && <Text dimColor>{` (${errorLines} lines)`}</Text>}\n </Text>\n </Box>\n {errors > 0 && (\n <Box>\n <Text dimColor>[e]rrors</Text>\n </Box>\n )}\n </Box>\n );\n});\n"],"names":["Box","Text","memo","figures","Spinner","SPINNER","interval","frames","StatusBar","running","done","errors","errorLines","justifyContent","color","tick","cross","dimColor"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAASA,GAAG,EAAEC,IAAI,QAAQ,MAAM;AAChC,SAASC,IAAI,QAAQ,QAAQ;AAC7B,OAAOC,aAAa,oBAAoB;AACxC,OAAOC,aAAa,eAAe;AAEnC,oHAAoH;AACpH,MAAMC,UAAU;IACdC,UAAU;IACVC,QAAQ;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;KAAI;AAC5D;AASA,6BAAeL,KAAK,SAASM,UAAU,EAAEC,OAAO,EAAEC,IAAI,EAAEC,MAAM,EAAEC,UAAU,EAAS;IACjF,qBACE,MAACZ;QAAIa,gBAAe;;0BAClB,KAACb;0BACC,cAAA,MAACC;;wBACEQ,UAAU,kBAAI,KAACL,4BAAYC,0BAAc,KAACJ;4BAAKa,OAAM;sCAASX,QAAQY,IAAI;;wBAC1E,CAAC,UAAU,EAAEN,QAAQ,EAAE,CAAC;sCACzB,KAACR;4BAAKa,OAAM;sCAASX,QAAQY,IAAI;;wBAChC,CAAC,OAAO,EAAEL,KAAK,EAAE,CAAC;sCACnB,KAACT;4BAAKa,OAAM;sCAAOX,QAAQa,KAAK;;wBAC/B,CAAC,SAAS,EAAEL,QAAQ;wBACpBC,aAAa,mBAAK,KAACX;4BAAKgB,QAAQ;sCAAE,CAAC,EAAE,EAAEL,WAAW,OAAO,CAAC;;;;;YAG9DD,SAAS,mBACR,KAACX;0BACC,cAAA,KAACC;oBAAKgB,QAAQ;8BAAC;;;;;AAKzB,GAAG"}
@@ -0,0 +1,11 @@
1
+ // Column width defaults
2
+ export const DEFAULT_COLUMN_WIDTH = 15;
3
+ export const MAX_COLUMN_WIDTH_PERCENT = 0.4; // 40% of terminal width
4
+ export const FALLBACK_COLUMN_WIDTH = 25;
5
+ // Batching defaults
6
+ export const BATCH_MAX_LINES = 20;
7
+ export const BATCH_MAX_WAIT_MS = 50;
8
+ // Rendering
9
+ export const DEFAULT_MAX_FPS = 20;
10
+ // Expansion
11
+ export const EXPANDED_MAX_VISIBLE_LINES = 10;
@@ -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":["DEFAULT_COLUMN_WIDTH","MAX_COLUMN_WIDTH_PERCENT","FALLBACK_COLUMN_WIDTH","BATCH_MAX_LINES","BATCH_MAX_WAIT_MS","DEFAULT_MAX_FPS","EXPANDED_MAX_VISIBLE_LINES"],"mappings":"AAAA,wBAAwB;AACxB,OAAO,MAAMA,uBAAuB,GAAG;AACvC,OAAO,MAAMC,2BAA2B,IAAI,CAAC,wBAAwB;AACrE,OAAO,MAAMC,wBAAwB,GAAG;AAExC,oBAAoB;AACpB,OAAO,MAAMC,kBAAkB,GAAG;AAClC,OAAO,MAAMC,oBAAoB,GAAG;AAEpC,YAAY;AACZ,OAAO,MAAMC,kBAAkB,GAAG;AAElC,YAAY;AACZ,OAAO,MAAMC,6BAA6B,GAAG"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { render } from 'ink';
3
3
  import App from './components/App.js';
4
+ import { DEFAULT_MAX_FPS } from './constants.js';
4
5
  import { processStore } from './state/processStore.js';
5
6
  export default function createApp() {
6
7
  let refCount = 0;
@@ -9,7 +10,11 @@ export default function createApp() {
9
10
  retain () {
10
11
  if (++refCount > 1) return processStore;
11
12
  // Render once - React handles all subsequent updates via useSyncExternalStore
12
- inkApp = render(/*#__PURE__*/ _jsx(App, {}));
13
+ // Enable incremental rendering to only rewrite changed lines (reduces flicker)
14
+ inkApp = render(/*#__PURE__*/ _jsx(App, {}), {
15
+ incrementalRendering: true,
16
+ maxFps: DEFAULT_MAX_FPS
17
+ });
13
18
  return processStore;
14
19
  },
15
20
  release (callback) {
@@ -18,7 +23,7 @@ export default function createApp() {
18
23
  return;
19
24
  }
20
25
  if (!inkApp) throw new Error('Expecting inkApp');
21
- // Signal exit to React component, provide callback for after cleanup
26
+ // Signal exit to React component
22
27
  processStore.signalExit(()=>{
23
28
  processStore.reset();
24
29
  process.stdout.write('\x1b[?25h'); // show cursor
@@ -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 // Signal exit to React component, provide callback for after cleanup\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":["render","App","processStore","createApp","refCount","inkApp","retain","release","callback","Error","signalExit","reset","process","stdout","write","waitUntilExit","then","cb","getExitCallback","catch"],"mappings":";AAAA,SAASA,MAAM,QAAQ,MAAM;AAC7B,OAAOC,SAAS,sBAAsB;AACtC,SAA4BC,YAAY,QAAQ,0BAA0B;AAI1E,eAAe,SAASC;IACtB,IAAIC,WAAW;IACf,IAAIC,SAA2C;IAE/C,OAAO;QACLC;YACE,IAAI,EAAEF,WAAW,GAAG,OAAOF;YAE3B,8EAA8E;YAC9EG,SAASL,qBAAO,KAACC;YACjB,OAAOC;QACT;QAEAK,SAAQC,QAAyB;YAC/B,IAAI,EAAEJ,WAAW,GAAG;gBAClBI;gBACA;YACF;YACA,IAAI,CAACH,QAAQ,MAAM,IAAII,MAAM;YAE7B,qEAAqE;YACrEP,aAAaQ,UAAU,CAAC;gBACtBR,aAAaS,KAAK;gBAClBC,QAAQC,MAAM,CAACC,KAAK,CAAC,cAAc,cAAc;gBACjDN;YACF;YAEA,iDAAiD;YACjDH,OACGU,aAAa,GACbC,IAAI,CAAC;gBACJ,MAAMC,KAAKf,aAAagB,eAAe;gBACvCD,eAAAA,yBAAAA;YACF,GACCE,KAAK,CAAC;gBACL,MAAMF,KAAKf,aAAagB,eAAe;gBACvCD,eAAAA,yBAAAA;YACF;YAEFZ,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":["render","App","DEFAULT_MAX_FPS","processStore","createApp","refCount","inkApp","retain","incrementalRendering","maxFps","release","callback","Error","signalExit","reset","process","stdout","write","waitUntilExit","then","cb","getExitCallback","catch"],"mappings":";AAAA,SAASA,MAAM,QAAQ,MAAM;AAC7B,OAAOC,SAAS,sBAAsB;AACtC,SAASC,eAAe,QAAQ,iBAAiB;AACjD,SAA4BC,YAAY,QAAQ,0BAA0B;AAI1E,eAAe,SAASC;IACtB,IAAIC,WAAW;IACf,IAAIC,SAA2C;IAE/C,OAAO;QACLC;YACE,IAAI,EAAEF,WAAW,GAAG,OAAOF;YAE3B,8EAA8E;YAC9E,+EAA+E;YAC/EG,SAASN,qBAAO,KAACC,UAAQ;gBACvBO,sBAAsB;gBACtBC,QAAQP;YACV;YACA,OAAOC;QACT;QAEAO,SAAQC,QAAyB;YAC/B,IAAI,EAAEN,WAAW,GAAG;gBAClBM;gBACA;YACF;YACA,IAAI,CAACL,QAAQ,MAAM,IAAIM,MAAM;YAE7B,iCAAiC;YACjCT,aAAaU,UAAU,CAAC;gBACtBV,aAAaW,KAAK;gBAClBC,QAAQC,MAAM,CAACC,KAAK,CAAC,cAAc,cAAc;gBACjDN;YACF;YAEA,iDAAiD;YACjDL,OACGY,aAAa,GACbC,IAAI,CAAC;gBACJ,MAAMC,KAAKjB,aAAakB,eAAe;gBACvCD,eAAAA,yBAAAA;YACF,GACCE,KAAK,CAAC;gBACL,MAAMF,KAAKjB,aAAakB,eAAe;gBACvCD,eAAAA,yBAAAA;YACF;YAEFd,SAAS;QACX;IACF;AACF"}