spawn-term 2.1.1 → 3.0.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.
- package/dist/cjs/components/App.js +62 -46
- package/dist/cjs/components/App.js.map +1 -1
- package/dist/cjs/components/CompactProcessLine.js +3 -2
- package/dist/cjs/components/CompactProcessLine.js.map +1 -1
- package/dist/cjs/index-cjs.js +3 -3
- package/dist/cjs/index-cjs.js.map +1 -1
- package/dist/cjs/index-esm.js +4 -4
- package/dist/cjs/index-esm.js.map +1 -1
- package/dist/cjs/session.js +311 -0
- package/dist/cjs/session.js.map +1 -0
- package/dist/cjs/src/components/App.d.ts +6 -1
- package/dist/cjs/src/index-cjs.d.ts +3 -3
- package/dist/cjs/src/index-esm.d.ts +3 -3
- package/dist/cjs/src/session.d.ts +7 -0
- package/dist/cjs/src/state/StoreContext.d.ts +3 -0
- package/dist/cjs/src/state/processStore.d.ts +6 -4
- package/dist/cjs/src/types.d.ts +5 -6
- package/dist/cjs/state/StoreContext.js +28 -0
- package/dist/cjs/state/StoreContext.js.map +1 -0
- package/dist/cjs/state/processStore.js +15 -17
- package/dist/cjs/state/processStore.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/components/App.js +59 -45
- package/dist/esm/components/App.js.map +1 -1
- package/dist/esm/components/CompactProcessLine.js +3 -2
- package/dist/esm/components/CompactProcessLine.js.map +1 -1
- package/dist/esm/index-cjs.js +1 -1
- package/dist/esm/index-cjs.js.map +1 -1
- package/dist/esm/index-esm.js +2 -2
- package/dist/esm/index-esm.js.map +1 -1
- package/dist/esm/session.js +218 -0
- package/dist/esm/session.js.map +1 -0
- package/dist/esm/src/components/App.d.ts +6 -1
- package/dist/esm/src/index-cjs.d.ts +3 -3
- package/dist/esm/src/index-esm.d.ts +3 -3
- package/dist/esm/src/session.d.ts +7 -0
- package/dist/esm/src/state/StoreContext.d.ts +3 -0
- package/dist/esm/src/state/processStore.d.ts +6 -4
- package/dist/esm/src/types.d.ts +5 -6
- package/dist/esm/state/StoreContext.js +9 -0
- package/dist/esm/state/StoreContext.js.map +1 -0
- package/dist/esm/state/processStore.js +13 -13
- package/dist/esm/state/processStore.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/cjs/createApp.js +0 -58
- package/dist/cjs/createApp.js.map +0 -1
- package/dist/cjs/spawnTerminal.js +0 -75
- package/dist/cjs/spawnTerminal.js.map +0 -1
- package/dist/cjs/src/createApp.d.ts +0 -6
- package/dist/cjs/src/spawnTerminal.d.ts +0 -2
- package/dist/cjs/src/worker.d.ts +0 -2
- package/dist/cjs/worker.js +0 -236
- package/dist/cjs/worker.js.map +0 -1
- package/dist/esm/createApp.js +0 -42
- package/dist/esm/createApp.js.map +0 -1
- package/dist/esm/spawnTerminal.js +0 -19
- package/dist/esm/spawnTerminal.js.map +0 -1
- package/dist/esm/src/createApp.d.ts +0 -6
- package/dist/esm/src/spawnTerminal.d.ts +0 -2
- package/dist/esm/src/worker.d.ts +0 -2
- package/dist/esm/worker.js +0 -175
- package/dist/esm/worker.js.map +0 -1
|
@@ -2,35 +2,35 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Box, Text, useApp, useInput, useStdin } from 'ink';
|
|
3
3
|
import { useEffect, useSyncExternalStore } from 'react';
|
|
4
4
|
import { EXPANDED_MAX_VISIBLE_LINES } from '../constants.js';
|
|
5
|
-
import {
|
|
5
|
+
import { StoreContext } from '../state/StoreContext.js';
|
|
6
6
|
import CompactProcessLine from './CompactProcessLine.js';
|
|
7
7
|
import Divider from './Divider.js';
|
|
8
8
|
import ErrorDetailModal from './ErrorDetailModal.js';
|
|
9
9
|
import ErrorListModal from './ErrorListModal.js';
|
|
10
10
|
import ExpandedOutput from './ExpandedOutput.js';
|
|
11
11
|
import StatusBar from './StatusBar.js';
|
|
12
|
-
|
|
12
|
+
function AppContent({ store }) {
|
|
13
13
|
const { exit } = useApp();
|
|
14
14
|
const { isRawModeSupported } = useStdin();
|
|
15
15
|
// Subscribe to store state
|
|
16
|
-
const processes = useSyncExternalStore(
|
|
17
|
-
const shouldExit = useSyncExternalStore(
|
|
18
|
-
const mode = useSyncExternalStore(
|
|
19
|
-
const selectedIndex = useSyncExternalStore(
|
|
20
|
-
const selectedErrorIndex = useSyncExternalStore(
|
|
21
|
-
const expandedId = useSyncExternalStore(
|
|
22
|
-
const scrollOffset = useSyncExternalStore(
|
|
16
|
+
const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
17
|
+
const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);
|
|
18
|
+
const mode = useSyncExternalStore(store.subscribe, store.getMode);
|
|
19
|
+
const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);
|
|
20
|
+
const selectedErrorIndex = useSyncExternalStore(store.subscribe, store.getSelectedErrorIndex);
|
|
21
|
+
const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);
|
|
22
|
+
const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);
|
|
23
23
|
// Subscribed state that triggers re-renders
|
|
24
|
-
const header = useSyncExternalStore(
|
|
25
|
-
const showStatusBar = useSyncExternalStore(
|
|
26
|
-
const isInteractive = useSyncExternalStore(
|
|
24
|
+
const header = useSyncExternalStore(store.subscribe, store.getHeader);
|
|
25
|
+
const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);
|
|
26
|
+
const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);
|
|
27
27
|
// Derived state (computed from processes which is already subscribed)
|
|
28
|
-
const failedProcesses =
|
|
29
|
-
const runningCount =
|
|
30
|
-
const doneCount =
|
|
31
|
-
const errorCount =
|
|
32
|
-
const errorLineCount =
|
|
33
|
-
const isAllComplete =
|
|
28
|
+
const failedProcesses = store.getFailedProcesses();
|
|
29
|
+
const runningCount = store.getRunningCount();
|
|
30
|
+
const doneCount = store.getDoneCount();
|
|
31
|
+
const errorCount = store.getErrorCount();
|
|
32
|
+
const errorLineCount = store.getErrorLineCount();
|
|
33
|
+
const isAllComplete = store.isAllComplete();
|
|
34
34
|
// Handle exit signal
|
|
35
35
|
useEffect(()=>{
|
|
36
36
|
if (shouldExit) {
|
|
@@ -43,72 +43,73 @@ export default function App() {
|
|
|
43
43
|
// Auto-enter interactive mode when all complete and interactive flag is set
|
|
44
44
|
useEffect(()=>{
|
|
45
45
|
if (isAllComplete && isInteractive && mode === 'normal') {
|
|
46
|
-
|
|
46
|
+
store.setMode('interactive');
|
|
47
47
|
}
|
|
48
48
|
}, [
|
|
49
49
|
isAllComplete,
|
|
50
50
|
isInteractive,
|
|
51
|
-
mode
|
|
51
|
+
mode,
|
|
52
|
+
store
|
|
52
53
|
]);
|
|
53
54
|
// Keyboard handling (only active when raw mode is supported)
|
|
54
55
|
useInput((input, key)=>{
|
|
55
56
|
if (mode === 'normal') {
|
|
56
57
|
if (input === 'e' && errorCount > 0) {
|
|
57
|
-
|
|
58
|
+
store.setMode('errorList');
|
|
58
59
|
}
|
|
59
60
|
} else if (mode === 'interactive') {
|
|
60
61
|
if (input === 'q' || key.escape) {
|
|
61
62
|
if (expandedId) {
|
|
62
|
-
|
|
63
|
+
store.collapse();
|
|
63
64
|
} else {
|
|
64
|
-
|
|
65
|
+
store.signalExit(()=>{});
|
|
65
66
|
}
|
|
66
67
|
} else if (key.return) {
|
|
67
|
-
|
|
68
|
+
store.toggleExpand();
|
|
68
69
|
} else if (key.downArrow) {
|
|
69
70
|
if (expandedId) {
|
|
70
|
-
|
|
71
|
+
store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);
|
|
71
72
|
} else {
|
|
72
|
-
|
|
73
|
+
store.selectNext();
|
|
73
74
|
}
|
|
74
75
|
} else if (key.upArrow) {
|
|
75
76
|
if (expandedId) {
|
|
76
|
-
|
|
77
|
+
store.scrollUp();
|
|
77
78
|
} else {
|
|
78
|
-
|
|
79
|
+
store.selectPrev();
|
|
79
80
|
}
|
|
80
81
|
} else if (input === 'j') {
|
|
81
82
|
if (expandedId) {
|
|
82
|
-
|
|
83
|
+
store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);
|
|
83
84
|
} else {
|
|
84
|
-
|
|
85
|
+
store.selectNext();
|
|
85
86
|
}
|
|
86
87
|
} else if (input === 'k') {
|
|
87
88
|
if (expandedId) {
|
|
88
|
-
|
|
89
|
+
store.scrollUp();
|
|
89
90
|
} else {
|
|
90
|
-
|
|
91
|
+
store.selectPrev();
|
|
91
92
|
}
|
|
92
93
|
} else if (input === 'e' && errorCount > 0) {
|
|
93
|
-
|
|
94
|
+
store.setMode('errorList');
|
|
94
95
|
}
|
|
95
96
|
} else if (mode === 'errorList') {
|
|
96
97
|
if (key.escape) {
|
|
97
|
-
|
|
98
|
+
store.setMode(isInteractive ? 'interactive' : 'normal');
|
|
98
99
|
} else if (key.downArrow) {
|
|
99
|
-
|
|
100
|
+
store.selectNextError();
|
|
100
101
|
} else if (key.upArrow) {
|
|
101
|
-
|
|
102
|
+
store.selectPrevError();
|
|
102
103
|
} else if (key.return) {
|
|
103
|
-
|
|
104
|
+
store.setMode('errorDetail');
|
|
104
105
|
}
|
|
105
106
|
} else if (mode === 'errorDetail') {
|
|
106
107
|
if (key.escape) {
|
|
107
|
-
|
|
108
|
+
store.setMode('errorList');
|
|
108
109
|
} else if (key.downArrow) {
|
|
109
|
-
|
|
110
|
+
store.selectNextError();
|
|
110
111
|
} else if (key.upArrow) {
|
|
111
|
-
|
|
112
|
+
store.selectPrevError();
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
}, {
|
|
@@ -124,7 +125,7 @@ export default function App() {
|
|
|
124
125
|
}
|
|
125
126
|
// Error detail modal
|
|
126
127
|
if (mode === 'errorDetail') {
|
|
127
|
-
const selectedError =
|
|
128
|
+
const selectedError = store.getSelectedError();
|
|
128
129
|
if (selectedError) {
|
|
129
130
|
return /*#__PURE__*/ _jsx(ErrorDetailModal, {
|
|
130
131
|
error: selectedError,
|
|
@@ -133,17 +134,21 @@ export default function App() {
|
|
|
133
134
|
});
|
|
134
135
|
}
|
|
135
136
|
// Fallback if no error selected
|
|
136
|
-
|
|
137
|
+
store.setMode('errorList');
|
|
137
138
|
}
|
|
138
139
|
// Normal/Interactive view - render in original registration order
|
|
139
140
|
const showSelection = mode === 'interactive';
|
|
140
141
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
141
142
|
flexDirection: "column",
|
|
142
143
|
children: [
|
|
143
|
-
/*#__PURE__*/
|
|
144
|
-
children:
|
|
144
|
+
header && /*#__PURE__*/ _jsxs(_Fragment, {
|
|
145
|
+
children: [
|
|
146
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
147
|
+
children: header
|
|
148
|
+
}),
|
|
149
|
+
/*#__PURE__*/ _jsx(Divider, {})
|
|
150
|
+
]
|
|
145
151
|
}),
|
|
146
|
-
/*#__PURE__*/ _jsx(Divider, {}),
|
|
147
152
|
processes.map((item, index)=>/*#__PURE__*/ _jsxs(Box, {
|
|
148
153
|
flexDirection: "column",
|
|
149
154
|
children: [
|
|
@@ -171,3 +176,12 @@ export default function App() {
|
|
|
171
176
|
]
|
|
172
177
|
});
|
|
173
178
|
}
|
|
179
|
+
// Wrapper component that provides store context
|
|
180
|
+
export default function App({ store }) {
|
|
181
|
+
return /*#__PURE__*/ _jsx(StoreContext.Provider, {
|
|
182
|
+
value: store,
|
|
183
|
+
children: /*#__PURE__*/ _jsx(AppContent, {
|
|
184
|
+
store: store
|
|
185
|
+
})
|
|
186
|
+
});
|
|
187
|
+
}
|
|
@@ -1 +1 @@
|
|
|
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":["Box","Text","useApp","useInput","useStdin","useEffect","useSyncExternalStore","EXPANDED_MAX_VISIBLE_LINES","processStore","CompactProcessLine","Divider","ErrorDetailModal","ErrorListModal","ExpandedOutput","StatusBar","App","exit","isRawModeSupported","processes","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","setMode","input","key","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","selectNext","upArrow","scrollUp","selectPrev","selectNextError","selectPrevError","isActive","errors","totalErrorLines","selectedError","getSelectedError","error","currentIndex","totalErrors","length","showSelection","flexDirection","map","item","index","isSelected","id","lines","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 type { ProcessStore } from '../state/processStore.ts';\nimport { StoreContext } from '../state/StoreContext.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorDetailModal from './ErrorDetailModal.ts';\nimport ErrorListModal from './ErrorListModal.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\n\ninterface AppProps {\n store: ProcessStore;\n}\n\nfunction AppContent({ store }: AppProps): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n\n // Subscribe to store state\n const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);\n const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);\n const mode = useSyncExternalStore(store.subscribe, store.getMode);\n const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);\n const selectedErrorIndex = useSyncExternalStore(store.subscribe, store.getSelectedErrorIndex);\n const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);\n const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);\n\n // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(store.subscribe, store.getHeader);\n const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);\n const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);\n\n // Derived state (computed from processes which is already subscribed)\n const failedProcesses = store.getFailedProcesses();\n const runningCount = store.getRunningCount();\n const doneCount = store.getDoneCount();\n const errorCount = store.getErrorCount();\n const errorLineCount = store.getErrorLineCount();\n const isAllComplete = store.isAllComplete();\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode when all complete and interactive flag is set\n useEffect(() => {\n if (isAllComplete && isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isAllComplete, isInteractive, mode, store]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n if (mode === 'normal') {\n if (input === 'e' && errorCount > 0) {\n store.setMode('errorList');\n }\n } else if (mode === 'interactive') {\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse();\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand();\n } else if (key.downArrow) {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext();\n }\n } else if (key.upArrow) {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev();\n }\n } else if (input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext();\n }\n } else if (input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev();\n }\n } else if (input === 'e' && errorCount > 0) {\n store.setMode('errorList');\n }\n } else if (mode === 'errorList') {\n if (key.escape) {\n store.setMode(isInteractive ? 'interactive' : 'normal');\n } else if (key.downArrow) {\n store.selectNextError();\n } else if (key.upArrow) {\n store.selectPrevError();\n } else if (key.return) {\n store.setMode('errorDetail');\n }\n } else if (mode === 'errorDetail') {\n if (key.escape) {\n store.setMode('errorList');\n } else if (key.downArrow) {\n store.selectNextError();\n } else if (key.upArrow) {\n store.selectPrevError();\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Error list modal\n if (mode === 'errorList') {\n return <ErrorListModal errors={failedProcesses} selectedIndex={selectedErrorIndex} totalErrorLines={errorLineCount} />;\n }\n\n // Error detail modal\n if (mode === 'errorDetail') {\n const selectedError = store.getSelectedError();\n if (selectedError) {\n return <ErrorDetailModal error={selectedError} currentIndex={selectedErrorIndex} totalErrors={failedProcesses.length} />;\n }\n // Fallback if no error selected\n store.setMode('errorList');\n }\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n return (\n <Box flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* All processes in registration order */}\n {processes.map((item, index) => (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && index === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={item.lines} scrollOffset={scrollOffset} />}\n </Box>\n ))}\n\n {/* Status bar */}\n {showStatusBar && processes.length > 0 && (\n <>\n <Divider />\n <StatusBar running={runningCount} done={doneCount} errors={errorCount} errorLines={errorLineCount} />\n </>\n )}\n </Box>\n );\n}\n\n// Wrapper component that provides store context\nexport default function App({ store }: AppProps): React.JSX.Element {\n return (\n <StoreContext.Provider value={store}>\n <AppContent store={store} />\n </StoreContext.Provider>\n );\n}\n"],"names":["Box","Text","useApp","useInput","useStdin","useEffect","useSyncExternalStore","EXPANDED_MAX_VISIBLE_LINES","StoreContext","CompactProcessLine","Divider","ErrorDetailModal","ErrorListModal","ExpandedOutput","StatusBar","AppContent","store","exit","isRawModeSupported","processes","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","setMode","input","key","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","selectNext","upArrow","scrollUp","selectPrev","selectNextError","selectPrevError","isActive","errors","totalErrorLines","selectedError","getSelectedError","error","currentIndex","totalErrors","length","showSelection","flexDirection","map","item","index","isSelected","id","lines","running","done","errorLines","App","Provider","value"],"mappings":"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"}
|
|
@@ -31,7 +31,7 @@ import { Box, Text, useStdout } from 'ink';
|
|
|
31
31
|
import { memo, useMemo } from 'react';
|
|
32
32
|
import figures from '../lib/figures.js';
|
|
33
33
|
import { calculateColumnWidth } from '../lib/format.js';
|
|
34
|
-
import {
|
|
34
|
+
import { useStore } from '../state/StoreContext.js';
|
|
35
35
|
import { LineType } from '../types.js';
|
|
36
36
|
import Spinner from './Spinner.js';
|
|
37
37
|
// From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2
|
|
@@ -66,6 +66,7 @@ function getErrorCount(lines) {
|
|
|
66
66
|
return lines.filter((line)=>line.type === LineType.stderr).length;
|
|
67
67
|
}
|
|
68
68
|
export default /*#__PURE__*/ memo(function CompactProcessLine({ item, isSelected = false }) {
|
|
69
|
+
const store = useStore();
|
|
69
70
|
const { stdout } = useStdout();
|
|
70
71
|
const terminalWidth = (stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80;
|
|
71
72
|
const { group, title, state, lines } = item;
|
|
@@ -74,7 +75,7 @@ export default /*#__PURE__*/ memo(function CompactProcessLine({ item, isSelected
|
|
|
74
75
|
const displayName = group || title;
|
|
75
76
|
// Calculate widths - use dynamic column width based on longest name
|
|
76
77
|
const iconWidth = 2; // icon + space
|
|
77
|
-
const maxGroupLength =
|
|
78
|
+
const maxGroupLength = store.getMaxGroupLength();
|
|
78
79
|
const nameColumnWidth = calculateColumnWidth('max', terminalWidth, maxGroupLength);
|
|
79
80
|
const gap = 1; // space between name and status
|
|
80
81
|
const statusWidth = terminalWidth - iconWidth - nameColumnWidth - gap;
|
|
@@ -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 { calculateColumnWidth } from '../lib/format.ts';\nimport {
|
|
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 { useStore } from '../state/StoreContext.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 store = useStore();\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 = store.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","useStore","LineType","Spinner","SPINNER","interval","frames","truncate","str","maxLength","length","slice","getLastOutputLine","lines","i","text","getErrorCount","filter","line","type","stderr","CompactProcessLine","item","isSelected","store","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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"}
|
package/dist/esm/index-cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/index-cjs.ts"],"sourcesContent":["export { default as figures } from './lib/figures.ts';\nexport { default as formatArguments } from './lib/formatArguments.ts';\nexport * from './types.ts';\n\nimport type {
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/index-cjs.ts"],"sourcesContent":["export { default as figures } from './lib/figures.ts';\nexport { default as formatArguments } from './lib/formatArguments.ts';\nexport * from './types.ts';\n\nimport type { createSession as createSessionType, Session } from './session.ts';\nexport type { Session };\nexport const createSession = undefined as typeof createSessionType;\n"],"names":["default","figures","formatArguments","createSession","undefined"],"mappings":"AAAA,SAASA,WAAWC,OAAO,QAAQ,mBAAmB;AACtD,SAASD,WAAWE,eAAe,QAAQ,2BAA2B;AACtE,cAAc,aAAa;AAI3B,OAAO,MAAMC,gBAAgBC,UAAsC"}
|
package/dist/esm/index-esm.js
CHANGED
|
@@ -2,5 +2,5 @@ export { default as figures } from './lib/figures.js';
|
|
|
2
2
|
export { default as formatArguments } from './lib/formatArguments.js';
|
|
3
3
|
export * from './types.js';
|
|
4
4
|
const major = +process.versions.node.split('.')[0];
|
|
5
|
-
import {
|
|
6
|
-
export
|
|
5
|
+
import { createSession as createSessionImpl } from './session.js';
|
|
6
|
+
export const createSession = major > 18 ? createSessionImpl : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/index-esm.ts"],"sourcesContent":["export { default as figures } from './lib/figures.ts';\nexport { default as formatArguments } from './lib/formatArguments.ts';\nexport * from './types.ts';\n\nconst major = +process.versions.node.split('.')[0];\n\nimport {
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/index-esm.ts"],"sourcesContent":["export { default as figures } from './lib/figures.ts';\nexport { default as formatArguments } from './lib/formatArguments.ts';\nexport * from './types.ts';\n\nconst major = +process.versions.node.split('.')[0];\n\nimport { createSession as createSessionImpl, type Session } from './session.ts';\nexport type { Session };\nexport const createSession = major > 18 ? createSessionImpl : (undefined as typeof createSessionImpl);\n"],"names":["default","figures","formatArguments","major","process","versions","node","split","createSession","createSessionImpl","undefined"],"mappings":"AAAA,SAASA,WAAWC,OAAO,QAAQ,mBAAmB;AACtD,SAASD,WAAWE,eAAe,QAAQ,2BAA2B;AACtE,cAAc,aAAa;AAE3B,MAAMC,QAAQ,CAACC,QAAQC,QAAQ,CAACC,IAAI,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE;AAElD,SAASC,iBAAiBC,iBAAiB,QAAsB,eAAe;AAEhF,OAAO,MAAMD,gBAAgBL,QAAQ,KAAKM,oBAAqBC,UAAuC"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
function _object_without_properties(source, excluded) {
|
|
2
|
+
if (source == null) return {};
|
|
3
|
+
var target = _object_without_properties_loose(source, excluded);
|
|
4
|
+
var key, i;
|
|
5
|
+
if (Object.getOwnPropertySymbols) {
|
|
6
|
+
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
|
|
7
|
+
for(i = 0; i < sourceSymbolKeys.length; i++){
|
|
8
|
+
key = sourceSymbolKeys[i];
|
|
9
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
10
|
+
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
|
|
11
|
+
target[key] = source[key];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
}
|
|
16
|
+
function _object_without_properties_loose(source, excluded) {
|
|
17
|
+
if (source == null) return {};
|
|
18
|
+
var target = {};
|
|
19
|
+
var sourceKeys = Object.keys(source);
|
|
20
|
+
var key, i;
|
|
21
|
+
for(i = 0; i < sourceKeys.length; i++){
|
|
22
|
+
key = sourceKeys[i];
|
|
23
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
24
|
+
target[key] = source[key];
|
|
25
|
+
}
|
|
26
|
+
return target;
|
|
27
|
+
}
|
|
28
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
29
|
+
import spawn, { crossSpawn } from 'cross-spawn-cb';
|
|
30
|
+
import crypto from 'crypto';
|
|
31
|
+
import { render } from 'ink';
|
|
32
|
+
import oo from 'on-one';
|
|
33
|
+
import Queue from 'queue-cb';
|
|
34
|
+
import App from './components/App.js';
|
|
35
|
+
import { DEFAULT_MAX_FPS } from './constants.js';
|
|
36
|
+
import addLines from './lib/addLines.js';
|
|
37
|
+
import concatWritable from './lib/concatWritable.js';
|
|
38
|
+
import formatArguments from './lib/formatArguments.js';
|
|
39
|
+
import { ProcessStore } from './state/processStore.js';
|
|
40
|
+
import { LineType } from './types.js';
|
|
41
|
+
class SessionImpl {
|
|
42
|
+
spawn(command, args, spawnOptions, options, callback) {
|
|
43
|
+
if (this.closed) {
|
|
44
|
+
throw new Error('Session is closed');
|
|
45
|
+
}
|
|
46
|
+
const { encoding, stdio } = spawnOptions, csOptions = _object_without_properties(spawnOptions, [
|
|
47
|
+
"encoding",
|
|
48
|
+
"stdio"
|
|
49
|
+
]);
|
|
50
|
+
if (stdio === 'inherit') {
|
|
51
|
+
this.runningCount++;
|
|
52
|
+
const id = crypto.randomUUID();
|
|
53
|
+
this.store.addProcess({
|
|
54
|
+
id,
|
|
55
|
+
title: [
|
|
56
|
+
command
|
|
57
|
+
].concat(formatArguments(args)).join(' '),
|
|
58
|
+
state: 'running',
|
|
59
|
+
lines: [],
|
|
60
|
+
group: options.group,
|
|
61
|
+
expanded: options.expanded
|
|
62
|
+
});
|
|
63
|
+
const cp = crossSpawn(command, args, csOptions);
|
|
64
|
+
const outputs = {
|
|
65
|
+
stdout: null,
|
|
66
|
+
stderr: null
|
|
67
|
+
};
|
|
68
|
+
const queue = new Queue();
|
|
69
|
+
if (cp.stdout) {
|
|
70
|
+
outputs.stdout = addLines((lines)=>{
|
|
71
|
+
this.store.appendLines(id, lines.map((text)=>({
|
|
72
|
+
type: LineType.stdout,
|
|
73
|
+
text
|
|
74
|
+
})));
|
|
75
|
+
});
|
|
76
|
+
queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), [
|
|
77
|
+
'error',
|
|
78
|
+
'end',
|
|
79
|
+
'close',
|
|
80
|
+
'finish'
|
|
81
|
+
]));
|
|
82
|
+
}
|
|
83
|
+
if (cp.stderr) {
|
|
84
|
+
outputs.stderr = addLines((lines)=>{
|
|
85
|
+
this.store.appendLines(id, lines.map((text)=>({
|
|
86
|
+
type: LineType.stderr,
|
|
87
|
+
text
|
|
88
|
+
})));
|
|
89
|
+
});
|
|
90
|
+
queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), [
|
|
91
|
+
'error',
|
|
92
|
+
'end',
|
|
93
|
+
'close',
|
|
94
|
+
'finish'
|
|
95
|
+
]));
|
|
96
|
+
}
|
|
97
|
+
queue.defer(spawn.worker.bind(null, cp, csOptions));
|
|
98
|
+
queue.await((err)=>{
|
|
99
|
+
const res = err ? err : {};
|
|
100
|
+
res.stdout = outputs.stdout ? outputs.stdout.output : null;
|
|
101
|
+
res.stderr = outputs.stderr ? outputs.stderr.output : null;
|
|
102
|
+
res.output = [
|
|
103
|
+
res.stdout,
|
|
104
|
+
res.stderr,
|
|
105
|
+
null
|
|
106
|
+
];
|
|
107
|
+
this.store.updateProcess(id, {
|
|
108
|
+
state: err ? 'error' : 'success'
|
|
109
|
+
});
|
|
110
|
+
this.onProcessComplete();
|
|
111
|
+
err ? callback(err) : callback(null, res);
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
// Non-inherit mode: collect output but don't display in UI
|
|
115
|
+
const cp = crossSpawn(command, args, csOptions);
|
|
116
|
+
const outputs = {
|
|
117
|
+
stdout: null,
|
|
118
|
+
stderr: null
|
|
119
|
+
};
|
|
120
|
+
const queue = new Queue();
|
|
121
|
+
if (cp.stdout) {
|
|
122
|
+
outputs.stdout = concatWritable((output)=>{
|
|
123
|
+
outputs.stdout.output = output.toString(encoding || 'utf8');
|
|
124
|
+
});
|
|
125
|
+
queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), [
|
|
126
|
+
'error',
|
|
127
|
+
'end',
|
|
128
|
+
'close',
|
|
129
|
+
'finish'
|
|
130
|
+
]));
|
|
131
|
+
}
|
|
132
|
+
if (cp.stderr) {
|
|
133
|
+
outputs.stderr = concatWritable((output)=>{
|
|
134
|
+
outputs.stderr.output = output.toString(encoding || 'utf8');
|
|
135
|
+
});
|
|
136
|
+
queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), [
|
|
137
|
+
'error',
|
|
138
|
+
'end',
|
|
139
|
+
'close',
|
|
140
|
+
'finish'
|
|
141
|
+
]));
|
|
142
|
+
}
|
|
143
|
+
queue.defer(spawn.worker.bind(null, cp, csOptions));
|
|
144
|
+
queue.await((err)=>{
|
|
145
|
+
const res = err ? err : {};
|
|
146
|
+
res.stdout = outputs.stdout ? outputs.stdout.output : null;
|
|
147
|
+
res.stderr = outputs.stderr ? outputs.stderr.output : null;
|
|
148
|
+
res.output = [
|
|
149
|
+
res.stdout,
|
|
150
|
+
res.stderr,
|
|
151
|
+
null
|
|
152
|
+
];
|
|
153
|
+
err ? callback(err) : callback(null, res);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
close() {
|
|
158
|
+
if (this.closed) return;
|
|
159
|
+
this.closed = true;
|
|
160
|
+
this.cleanup();
|
|
161
|
+
}
|
|
162
|
+
waitAndClose(callback) {
|
|
163
|
+
if (this.closed) {
|
|
164
|
+
callback === null || callback === void 0 ? void 0 : callback();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (this.runningCount === 0) {
|
|
168
|
+
this.close();
|
|
169
|
+
callback === null || callback === void 0 ? void 0 : callback();
|
|
170
|
+
} else {
|
|
171
|
+
if (callback) this.waitCallbacks.push(callback);
|
|
172
|
+
// Will close when runningCount hits 0
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
onProcessComplete() {
|
|
176
|
+
this.runningCount--;
|
|
177
|
+
if (this.runningCount === 0 && this.waitCallbacks.length > 0) {
|
|
178
|
+
this.close();
|
|
179
|
+
for (const cb of this.waitCallbacks)cb();
|
|
180
|
+
this.waitCallbacks = [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
cleanup() {
|
|
184
|
+
// Signal exit to React component
|
|
185
|
+
this.store.signalExit(()=>{
|
|
186
|
+
this.store.reset();
|
|
187
|
+
process.stdout.write('\x1b[?25h'); // show cursor
|
|
188
|
+
});
|
|
189
|
+
// Wait for Ink to finish
|
|
190
|
+
if (this.inkApp) {
|
|
191
|
+
this.inkApp.waitUntilExit().then(()=>{
|
|
192
|
+
const cb = this.store.getExitCallback();
|
|
193
|
+
cb === null || cb === void 0 ? void 0 : cb();
|
|
194
|
+
}).catch(()=>{
|
|
195
|
+
const cb = this.store.getExitCallback();
|
|
196
|
+
cb === null || cb === void 0 ? void 0 : cb();
|
|
197
|
+
});
|
|
198
|
+
this.inkApp = null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
constructor(options = {}){
|
|
202
|
+
this.inkApp = null;
|
|
203
|
+
this.runningCount = 0;
|
|
204
|
+
this.closed = false;
|
|
205
|
+
this.waitCallbacks = [];
|
|
206
|
+
this.store = new ProcessStore(options);
|
|
207
|
+
// Render Ink app immediately
|
|
208
|
+
this.inkApp = render(/*#__PURE__*/ _jsx(App, {
|
|
209
|
+
store: this.store
|
|
210
|
+
}), {
|
|
211
|
+
incrementalRendering: true,
|
|
212
|
+
maxFps: DEFAULT_MAX_FPS
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
export function createSession(options = {}) {
|
|
217
|
+
return new SessionImpl(options);
|
|
218
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/session.tsx"],"sourcesContent":["import spawn, { crossSpawn, type SpawnResult } from 'cross-spawn-cb';\nimport crypto from 'crypto';\nimport { render } from 'ink';\nimport oo from 'on-one';\nimport Queue from 'queue-cb';\n\nimport App from './components/App.ts';\nimport { DEFAULT_MAX_FPS } from './constants.ts';\nimport addLines from './lib/addLines.ts';\nimport concatWritable from './lib/concatWritable.ts';\nimport formatArguments from './lib/formatArguments.ts';\nimport { ProcessStore } from './state/processStore.ts';\nimport type { ProcessOptions, SessionOptions, SpawnError, SpawnOptions, TerminalCallback } from './types.ts';\nimport { LineType } from './types.ts';\n\nexport interface Session {\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void;\n close(): void;\n waitAndClose(callback?: () => void): void;\n}\n\nclass SessionImpl implements Session {\n private store: ProcessStore;\n private inkApp: ReturnType<typeof render> | null = null;\n private runningCount = 0;\n private closed = false;\n private waitCallbacks: (() => void)[] = [];\n\n constructor(options: SessionOptions = {}) {\n this.store = new ProcessStore(options);\n\n // Render Ink app immediately\n this.inkApp = render(<App store={this.store} />, {\n incrementalRendering: true,\n maxFps: DEFAULT_MAX_FPS,\n });\n }\n\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void {\n if (this.closed) {\n throw new Error('Session is closed');\n }\n\n const { encoding, stdio, ...csOptions } = spawnOptions;\n\n if (stdio === 'inherit') {\n this.runningCount++;\n const id = crypto.randomUUID();\n this.store.addProcess({\n id,\n title: [command].concat(formatArguments(args)).join(' '),\n state: 'running',\n lines: [],\n group: options.group,\n expanded: options.expanded,\n });\n\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof addLines> | null, stderr: null as ReturnType<typeof addLines> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = addLines((lines) => {\n this.store.appendLines(\n id,\n lines.map((text) => ({ type: LineType.stdout, text }))\n );\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = addLines((lines) => {\n this.store.appendLines(\n id,\n lines.map((text) => ({ type: LineType.stderr, text }))\n );\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n this.store.updateProcess(id, { state: err ? 'error' : 'success' });\n\n this.onProcessComplete();\n err ? callback(err) : callback(null, res);\n });\n } else {\n // Non-inherit mode: collect output but don't display in UI\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof concatWritable> | null, stderr: null as ReturnType<typeof concatWritable> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = concatWritable((output) => {\n (outputs.stdout as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = concatWritable((output) => {\n (outputs.stderr as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n err ? callback(err) : callback(null, res);\n });\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup();\n }\n\n waitAndClose(callback?: () => void): void {\n if (this.closed) {\n callback?.();\n return;\n }\n\n if (this.runningCount === 0) {\n this.close();\n callback?.();\n } else {\n if (callback) this.waitCallbacks.push(callback);\n // Will close when runningCount hits 0\n }\n }\n\n private onProcessComplete(): void {\n this.runningCount--;\n if (this.runningCount === 0 && this.waitCallbacks.length > 0) {\n this.close();\n for (const cb of this.waitCallbacks) cb();\n this.waitCallbacks = [];\n }\n }\n\n private cleanup(): void {\n // Signal exit to React component\n this.store.signalExit(() => {\n this.store.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n });\n\n // Wait for Ink to finish\n if (this.inkApp) {\n this.inkApp\n .waitUntilExit()\n .then(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n })\n .catch(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n });\n this.inkApp = null;\n }\n }\n}\n\nexport function createSession(options: SessionOptions = {}): Session {\n return new SessionImpl(options);\n}\n"],"names":["spawn","crossSpawn","crypto","render","oo","Queue","App","DEFAULT_MAX_FPS","addLines","concatWritable","formatArguments","ProcessStore","LineType","SessionImpl","command","args","spawnOptions","options","callback","closed","Error","encoding","stdio","csOptions","runningCount","id","randomUUID","store","addProcess","title","concat","join","state","lines","group","expanded","cp","outputs","stdout","stderr","queue","appendLines","map","text","type","defer","bind","pipe","worker","await","err","res","output","updateProcess","onProcessComplete","toString","close","cleanup","waitAndClose","waitCallbacks","push","length","cb","signalExit","reset","process","write","inkApp","waitUntilExit","then","getExitCallback","catch","incrementalRendering","maxFps","createSession"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { default as figures } from './lib/figures.js';
|
|
2
2
|
export { default as formatArguments } from './lib/formatArguments.js';
|
|
3
3
|
export * from './types.js';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
export
|
|
4
|
+
import type { createSession as createSessionType, Session } from './session.js';
|
|
5
|
+
export type { Session };
|
|
6
|
+
export declare const createSession: typeof createSessionType;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { default as figures } from './lib/figures.js';
|
|
2
2
|
export { default as formatArguments } from './lib/formatArguments.js';
|
|
3
3
|
export * from './types.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export
|
|
4
|
+
import { createSession as createSessionImpl, type Session } from './session.js';
|
|
5
|
+
export type { Session };
|
|
6
|
+
export declare const createSession: typeof createSessionImpl;
|