spawn-term 3.0.0 → 3.0.2
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 +25 -6
- package/dist/cjs/components/App.js.map +1 -1
- package/dist/cjs/components/ChildProcess.js +2 -17
- package/dist/cjs/components/ChildProcess.js.map +1 -1
- package/dist/cjs/components/CompactProcessLine.js +27 -36
- package/dist/cjs/components/CompactProcessLine.js.map +1 -1
- package/dist/cjs/components/StatusBar.js +4 -19
- package/dist/cjs/components/StatusBar.js.map +1 -1
- package/dist/cjs/constants.js +18 -0
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/lib/clipText.js +56 -0
- package/dist/cjs/lib/clipText.js.map +1 -0
- package/dist/cjs/session.js +43 -10
- package/dist/cjs/session.js.map +1 -1
- package/dist/cjs/src/constants.d.ts +4 -0
- package/dist/cjs/src/lib/clipText.d.ts +9 -0
- package/dist/cjs/src/state/processStore.d.ts +5 -2
- package/dist/cjs/state/processStore.js +20 -2
- package/dist/cjs/state/processStore.js.map +1 -1
- package/dist/esm/components/App.js +30 -9
- package/dist/esm/components/App.js.map +1 -1
- package/dist/esm/components/ChildProcess.js +1 -16
- package/dist/esm/components/ChildProcess.js.map +1 -1
- package/dist/esm/components/CompactProcessLine.js +26 -35
- package/dist/esm/components/CompactProcessLine.js.map +1 -1
- package/dist/esm/components/StatusBar.js +3 -18
- package/dist/esm/components/StatusBar.js.map +1 -1
- package/dist/esm/constants.js +16 -0
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/lib/clipText.js +37 -0
- package/dist/esm/lib/clipText.js.map +1 -0
- package/dist/esm/session.js +38 -8
- package/dist/esm/session.js.map +1 -1
- package/dist/esm/src/constants.d.ts +4 -0
- package/dist/esm/src/lib/clipText.d.ts +9 -0
- package/dist/esm/src/state/processStore.d.ts +5 -2
- package/dist/esm/state/processStore.js +18 -2
- package/dist/esm/state/processStore.js.map +1 -1
- package/package.json +1 -1
|
@@ -29,31 +29,13 @@ function _object_spread(target) {
|
|
|
29
29
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
30
30
|
import { Box, Text, useStdout } from 'ink';
|
|
31
31
|
import { memo, useMemo } from 'react';
|
|
32
|
+
import { SPINNER } from '../constants.js';
|
|
33
|
+
import { clipText } from '../lib/clipText.js';
|
|
32
34
|
import figures from '../lib/figures.js';
|
|
33
35
|
import { calculateColumnWidth } from '../lib/format.js';
|
|
34
36
|
import { useStore } from '../state/StoreContext.js';
|
|
35
37
|
import { LineType } from '../types.js';
|
|
36
38
|
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
39
|
function getLastOutputLine(lines) {
|
|
58
40
|
for(let i = lines.length - 1; i >= 0; i--){
|
|
59
41
|
if (lines[i].text.length > 0) {
|
|
@@ -74,22 +56,24 @@ export default /*#__PURE__*/ memo(function CompactProcessLine({ item, isSelected
|
|
|
74
56
|
// Display name: prefer group, fall back to title
|
|
75
57
|
const displayName = group || title;
|
|
76
58
|
// Calculate widths - use dynamic column width based on longest name
|
|
59
|
+
const selectionWidth = 1; // selection indicator
|
|
77
60
|
const iconWidth = 2; // icon + space
|
|
78
61
|
const maxGroupLength = store.getMaxGroupLength();
|
|
79
62
|
const nameColumnWidth = calculateColumnWidth('max', terminalWidth, maxGroupLength);
|
|
80
63
|
const gap = 1; // space between name and status
|
|
81
|
-
const statusWidth = terminalWidth - iconWidth - nameColumnWidth - gap;
|
|
82
|
-
//
|
|
83
|
-
const
|
|
84
|
-
// Status text based on state
|
|
64
|
+
const statusWidth = Math.max(0, terminalWidth - selectionWidth - iconWidth - nameColumnWidth - gap);
|
|
65
|
+
// Clip name to column width and pad
|
|
66
|
+
const clippedName = clipText(displayName, nameColumnWidth).padEnd(nameColumnWidth);
|
|
67
|
+
// Status text based on state - clip to available width
|
|
85
68
|
const statusText = useMemo(()=>{
|
|
86
69
|
if (state === 'running') {
|
|
87
70
|
const lastLine = getLastOutputLine(lines);
|
|
88
|
-
return lastLine ?
|
|
71
|
+
return lastLine ? clipText(lastLine, statusWidth) : '';
|
|
89
72
|
}
|
|
90
73
|
if (state === 'error') {
|
|
91
74
|
const errorCount = getErrorCount(lines);
|
|
92
|
-
|
|
75
|
+
const text = errorCount > 0 ? `${errorCount} error${errorCount > 1 ? 's' : ''}` : 'failed';
|
|
76
|
+
return clipText(text, statusWidth);
|
|
93
77
|
}
|
|
94
78
|
return ''; // success - no status text
|
|
95
79
|
}, [
|
|
@@ -119,6 +103,7 @@ export default /*#__PURE__*/ memo(function CompactProcessLine({ item, isSelected
|
|
|
119
103
|
// Status text color
|
|
120
104
|
const statusColor = state === 'error' ? 'red' : 'gray';
|
|
121
105
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
106
|
+
width: terminalWidth,
|
|
122
107
|
children: [
|
|
123
108
|
/*#__PURE__*/ _jsx(Text, {
|
|
124
109
|
color: isSelected ? 'cyan' : undefined,
|
|
@@ -128,16 +113,22 @@ export default /*#__PURE__*/ memo(function CompactProcessLine({ item, isSelected
|
|
|
128
113
|
width: iconWidth,
|
|
129
114
|
children: icon
|
|
130
115
|
}),
|
|
131
|
-
/*#__PURE__*/ _jsx(
|
|
132
|
-
|
|
133
|
-
children:
|
|
116
|
+
/*#__PURE__*/ _jsx(Box, {
|
|
117
|
+
width: nameColumnWidth,
|
|
118
|
+
children: /*#__PURE__*/ _jsx(Text, {
|
|
119
|
+
inverse: isSelected,
|
|
120
|
+
children: clippedName
|
|
121
|
+
})
|
|
134
122
|
}),
|
|
135
|
-
statusText && /*#__PURE__*/
|
|
136
|
-
|
|
137
|
-
children:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
123
|
+
statusWidth > 0 && statusText && /*#__PURE__*/ _jsx(Box, {
|
|
124
|
+
width: statusWidth + gap,
|
|
125
|
+
children: /*#__PURE__*/ _jsxs(Text, {
|
|
126
|
+
color: statusColor,
|
|
127
|
+
children: [
|
|
128
|
+
" ",
|
|
129
|
+
statusText
|
|
130
|
+
]
|
|
131
|
+
})
|
|
141
132
|
})
|
|
142
133
|
]
|
|
143
134
|
});
|
|
@@ -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 { useStore } from '../state/StoreContext.ts';\nimport type { ChildProcess, Line } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport Spinner from './Spinner.ts';\n\
|
|
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 { SPINNER } from '../constants.ts';\nimport { clipText } from '../lib/clipText.ts';\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\ntype Props = {\n item: ChildProcess;\n isSelected?: boolean;\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 selectionWidth = 1; // selection indicator\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 = Math.max(0, terminalWidth - selectionWidth - iconWidth - nameColumnWidth - gap);\n\n // Clip name to column width and pad\n const clippedName = clipText(displayName, nameColumnWidth).padEnd(nameColumnWidth);\n\n // Status text based on state - clip to available width\n const statusText = useMemo(() => {\n if (state === 'running') {\n const lastLine = getLastOutputLine(lines);\n return lastLine ? clipText(lastLine, statusWidth) : '';\n }\n if (state === 'error') {\n const errorCount = getErrorCount(lines);\n const text = errorCount > 0 ? `${errorCount} error${errorCount > 1 ? 's' : ''}` : 'failed';\n return clipText(text, statusWidth);\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 width={terminalWidth}>\n <Text color={isSelected ? 'cyan' : undefined}>{selectionIndicator}</Text>\n <Box width={iconWidth}>{icon}</Box>\n <Box width={nameColumnWidth}>\n <Text inverse={isSelected}>{clippedName}</Text>\n </Box>\n {statusWidth > 0 && statusText && (\n <Box width={statusWidth + gap}>\n <Text color={statusColor}> {statusText}</Text>\n </Box>\n )}\n </Box>\n );\n});\n"],"names":["Box","Text","useStdout","memo","useMemo","SPINNER","clipText","figures","calculateColumnWidth","useStore","LineType","Spinner","getLastOutputLine","lines","i","length","text","getErrorCount","filter","line","type","stderr","CompactProcessLine","item","isSelected","store","stdout","terminalWidth","columns","group","title","state","selectionIndicator","pointer","displayName","selectionWidth","iconWidth","maxGroupLength","getMaxGroupLength","nameColumnWidth","gap","statusWidth","Math","max","clippedName","padEnd","statusText","lastLine","errorCount","icon","color","tick","cross","statusColor","width","undefined","inverse"],"mappings":"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"}
|
|
@@ -29,24 +29,9 @@ function _object_spread(target) {
|
|
|
29
29
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
30
30
|
import { Box, Text } from 'ink';
|
|
31
31
|
import { memo } from 'react';
|
|
32
|
+
import { SPINNER } from '../constants.js';
|
|
32
33
|
import figures from '../lib/figures.js';
|
|
33
34
|
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
35
|
export default /*#__PURE__*/ memo(function StatusBar({ running, done, errors, errorLines }) {
|
|
51
36
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
52
37
|
justifyContent: "space-between",
|
|
@@ -58,12 +43,12 @@ export default /*#__PURE__*/ memo(function StatusBar({ running, done, errors, er
|
|
|
58
43
|
color: "green",
|
|
59
44
|
children: figures.tick
|
|
60
45
|
}),
|
|
61
|
-
` Running: ${running} `,
|
|
46
|
+
` Running: ${running} | `,
|
|
62
47
|
/*#__PURE__*/ _jsx(Text, {
|
|
63
48
|
color: "green",
|
|
64
49
|
children: figures.tick
|
|
65
50
|
}),
|
|
66
|
-
` Done: ${done} `,
|
|
51
|
+
` Done: ${done} | `,
|
|
67
52
|
/*#__PURE__*/ _jsx(Text, {
|
|
68
53
|
color: "red",
|
|
69
54
|
children: figures.cross
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/StatusBar.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport figures from '../lib/figures.ts';\nimport Spinner from './Spinner.ts';\n\
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/StatusBar.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport { SPINNER } from '../constants.ts';\nimport figures from '../lib/figures.ts';\nimport Spinner from './Spinner.ts';\n\ntype Props = {\n running: number;\n done: number;\n errors: number;\n errorLines: number;\n};\n\nexport default memo(function StatusBar({ running, done, errors, errorLines }: Props) {\n return (\n <Box justifyContent=\"space-between\">\n <Box>\n <Text>\n {running > 0 ? <Spinner {...SPINNER} /> : <Text color=\"green\">{figures.tick}</Text>}\n {` Running: ${running} | `}\n <Text color=\"green\">{figures.tick}</Text>\n {` Done: ${done} | `}\n <Text color=\"red\">{figures.cross}</Text>\n {` Errors: ${errors}`}\n {errorLines > 0 && <Text dimColor>{` (${errorLines} lines)`}</Text>}\n </Text>\n </Box>\n {errors > 0 && (\n <Box>\n <Text dimColor>[e]rrors</Text>\n </Box>\n )}\n </Box>\n );\n});\n"],"names":["Box","Text","memo","SPINNER","figures","Spinner","StatusBar","running","done","errors","errorLines","justifyContent","color","tick","cross","dimColor"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAASA,GAAG,EAAEC,IAAI,QAAQ,MAAM;AAChC,SAASC,IAAI,QAAQ,QAAQ;AAC7B,SAASC,OAAO,QAAQ,kBAAkB;AAC1C,OAAOC,aAAa,oBAAoB;AACxC,OAAOC,aAAa,eAAe;AASnC,6BAAeH,KAAK,SAASI,UAAU,EAAEC,OAAO,EAAEC,IAAI,EAAEC,MAAM,EAAEC,UAAU,EAAS;IACjF,qBACE,MAACV;QAAIW,gBAAe;;0BAClB,KAACX;0BACC,cAAA,MAACC;;wBACEM,UAAU,kBAAI,KAACF,4BAAYF,0BAAc,KAACF;4BAAKW,OAAM;sCAASR,QAAQS,IAAI;;wBAC1E,CAAC,UAAU,EAAEN,QAAQ,IAAI,CAAC;sCAC3B,KAACN;4BAAKW,OAAM;sCAASR,QAAQS,IAAI;;wBAChC,CAAC,OAAO,EAAEL,KAAK,IAAI,CAAC;sCACrB,KAACP;4BAAKW,OAAM;sCAAOR,QAAQU,KAAK;;wBAC/B,CAAC,SAAS,EAAEL,QAAQ;wBACpBC,aAAa,mBAAK,KAACT;4BAAKc,QAAQ;sCAAE,CAAC,EAAE,EAAEL,WAAW,OAAO,CAAC;;;;;YAG9DD,SAAS,mBACR,KAACT;0BACC,cAAA,KAACC;oBAAKc,QAAQ;8BAAC;;;;;AAKzB,GAAG"}
|
package/dist/esm/constants.js
CHANGED
|
@@ -9,3 +9,19 @@ export const BATCH_MAX_WAIT_MS = 50;
|
|
|
9
9
|
export const DEFAULT_MAX_FPS = 20;
|
|
10
10
|
// Expansion
|
|
11
11
|
export const EXPANDED_MAX_VISIBLE_LINES = 10;
|
|
12
|
+
// From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2
|
|
13
|
+
export const SPINNER = {
|
|
14
|
+
interval: 80,
|
|
15
|
+
frames: [
|
|
16
|
+
'⠋',
|
|
17
|
+
'⠙',
|
|
18
|
+
'⠹',
|
|
19
|
+
'⠸',
|
|
20
|
+
'⠼',
|
|
21
|
+
'⠴',
|
|
22
|
+
'⠦',
|
|
23
|
+
'⠧',
|
|
24
|
+
'⠇',
|
|
25
|
+
'⠏'
|
|
26
|
+
]
|
|
27
|
+
};
|
|
@@ -1 +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
|
+
{"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\n// From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2\nexport const SPINNER = {\n interval: 80,\n frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],\n};\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","SPINNER","interval","frames"],"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;AAE7C,oHAAoH;AACpH,OAAO,MAAMC,UAAU;IACrBC,UAAU;IACVC,QAAQ;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;KAAI;AAC5D,EAAE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import ansiRegex from './ansiRegex.js';
|
|
2
|
+
const regex = ansiRegex();
|
|
3
|
+
/**
|
|
4
|
+
* Get the visible length of a string, ignoring ANSI escape codes.
|
|
5
|
+
*/ export function visibleLength(str) {
|
|
6
|
+
return str.replace(regex, '').length;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Clip text to a maximum visible width, accounting for ANSI escape codes.
|
|
10
|
+
* Adds ellipsis (…) if truncated.
|
|
11
|
+
*/ export function clipText(str, maxWidth) {
|
|
12
|
+
if (maxWidth <= 0) return '';
|
|
13
|
+
if (maxWidth === 1) return '…';
|
|
14
|
+
const stripped = str.replace(regex, '');
|
|
15
|
+
if (stripped.length <= maxWidth) return str;
|
|
16
|
+
// Need to truncate - walk through and preserve ANSI codes
|
|
17
|
+
let visibleCount = 0;
|
|
18
|
+
let result = '';
|
|
19
|
+
let i = 0;
|
|
20
|
+
while(i < str.length && visibleCount < maxWidth - 1){
|
|
21
|
+
// Check for ANSI escape sequence (ESC [ params m)
|
|
22
|
+
const remaining = str.slice(i);
|
|
23
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape requires control char
|
|
24
|
+
const match = remaining.match(/^(\u001B\[[0-9;]*m)/);
|
|
25
|
+
if (match) {
|
|
26
|
+
// Include the ANSI code in output but don't count toward visible length
|
|
27
|
+
result += match[1];
|
|
28
|
+
i += match[1].length;
|
|
29
|
+
} else {
|
|
30
|
+
// Regular character
|
|
31
|
+
result += str[i];
|
|
32
|
+
visibleCount++;
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return `${result}…`;
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/lib/clipText.ts"],"sourcesContent":["import ansiRegex from './ansiRegex.ts';\n\nconst regex = ansiRegex();\n\n/**\n * Get the visible length of a string, ignoring ANSI escape codes.\n */\nexport function visibleLength(str: string): number {\n return str.replace(regex, '').length;\n}\n\n/**\n * Clip text to a maximum visible width, accounting for ANSI escape codes.\n * Adds ellipsis (…) if truncated.\n */\nexport function clipText(str: string, maxWidth: number): string {\n if (maxWidth <= 0) return '';\n if (maxWidth === 1) return '…';\n\n const stripped = str.replace(regex, '');\n if (stripped.length <= maxWidth) return str;\n\n // Need to truncate - walk through and preserve ANSI codes\n let visibleCount = 0;\n let result = '';\n let i = 0;\n\n while (i < str.length && visibleCount < maxWidth - 1) {\n // Check for ANSI escape sequence (ESC [ params m)\n const remaining = str.slice(i);\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape requires control char\n const match = remaining.match(/^(\\u001B\\[[0-9;]*m)/);\n\n if (match) {\n // Include the ANSI code in output but don't count toward visible length\n result += match[1];\n i += match[1].length;\n } else {\n // Regular character\n result += str[i];\n visibleCount++;\n i++;\n }\n }\n\n return `${result}…`;\n}\n"],"names":["ansiRegex","regex","visibleLength","str","replace","length","clipText","maxWidth","stripped","visibleCount","result","i","remaining","slice","match"],"mappings":"AAAA,OAAOA,eAAe,iBAAiB;AAEvC,MAAMC,QAAQD;AAEd;;CAEC,GACD,OAAO,SAASE,cAAcC,GAAW;IACvC,OAAOA,IAAIC,OAAO,CAACH,OAAO,IAAII,MAAM;AACtC;AAEA;;;CAGC,GACD,OAAO,SAASC,SAASH,GAAW,EAAEI,QAAgB;IACpD,IAAIA,YAAY,GAAG,OAAO;IAC1B,IAAIA,aAAa,GAAG,OAAO;IAE3B,MAAMC,WAAWL,IAAIC,OAAO,CAACH,OAAO;IACpC,IAAIO,SAASH,MAAM,IAAIE,UAAU,OAAOJ;IAExC,0DAA0D;IAC1D,IAAIM,eAAe;IACnB,IAAIC,SAAS;IACb,IAAIC,IAAI;IAER,MAAOA,IAAIR,IAAIE,MAAM,IAAII,eAAeF,WAAW,EAAG;QACpD,kDAAkD;QAClD,MAAMK,YAAYT,IAAIU,KAAK,CAACF;QAC5B,6FAA6F;QAC7F,MAAMG,QAAQF,UAAUE,KAAK,CAAC;QAE9B,IAAIA,OAAO;YACT,wEAAwE;YACxEJ,UAAUI,KAAK,CAAC,EAAE;YAClBH,KAAKG,KAAK,CAAC,EAAE,CAACT,MAAM;QACtB,OAAO;YACL,oBAAoB;YACpBK,UAAUP,GAAG,CAACQ,EAAE;YAChBF;YACAE;QACF;IACF;IAEA,OAAO,GAAGD,OAAO,CAAC,CAAC;AACrB"}
|
package/dist/esm/session.js
CHANGED
|
@@ -164,23 +164,47 @@ class SessionImpl {
|
|
|
164
164
|
callback === null || callback === void 0 ? void 0 : callback();
|
|
165
165
|
return;
|
|
166
166
|
}
|
|
167
|
+
if (callback) this.waitCallbacks.push(callback);
|
|
167
168
|
if (this.runningCount === 0) {
|
|
168
|
-
this.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
if (this.isInteractive) {
|
|
170
|
+
// In interactive mode, wait for user to quit (press 'q')
|
|
171
|
+
const unsubscribe = this.store.subscribe(()=>{
|
|
172
|
+
if (this.store.getShouldExit()) {
|
|
173
|
+
unsubscribe();
|
|
174
|
+
this.closeAndCallWaitCallbacks();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
} else {
|
|
178
|
+
this.closeAndCallWaitCallbacks();
|
|
179
|
+
}
|
|
173
180
|
}
|
|
181
|
+
// If runningCount > 0, will close when it hits 0 in onProcessComplete
|
|
174
182
|
}
|
|
175
183
|
onProcessComplete() {
|
|
176
184
|
this.runningCount--;
|
|
177
185
|
if (this.runningCount === 0 && this.waitCallbacks.length > 0) {
|
|
178
|
-
this.
|
|
186
|
+
if (this.isInteractive) {
|
|
187
|
+
// In interactive mode, wait for user to quit (press 'q')
|
|
188
|
+
const unsubscribe = this.store.subscribe(()=>{
|
|
189
|
+
if (this.store.getShouldExit()) {
|
|
190
|
+
unsubscribe();
|
|
191
|
+
this.closeAndCallWaitCallbacks();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
} else {
|
|
195
|
+
this.closeAndCallWaitCallbacks();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
closeAndCallWaitCallbacks() {
|
|
200
|
+
if (this.closed) return;
|
|
201
|
+
this.closed = true;
|
|
202
|
+
this.cleanup(()=>{
|
|
179
203
|
for (const cb of this.waitCallbacks)cb();
|
|
180
204
|
this.waitCallbacks = [];
|
|
181
|
-
}
|
|
205
|
+
});
|
|
182
206
|
}
|
|
183
|
-
cleanup() {
|
|
207
|
+
cleanup(onComplete) {
|
|
184
208
|
// Signal exit to React component
|
|
185
209
|
this.store.signalExit(()=>{
|
|
186
210
|
this.store.reset();
|
|
@@ -191,11 +215,15 @@ class SessionImpl {
|
|
|
191
215
|
this.inkApp.waitUntilExit().then(()=>{
|
|
192
216
|
const cb = this.store.getExitCallback();
|
|
193
217
|
cb === null || cb === void 0 ? void 0 : cb();
|
|
218
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
|
|
194
219
|
}).catch(()=>{
|
|
195
220
|
const cb = this.store.getExitCallback();
|
|
196
221
|
cb === null || cb === void 0 ? void 0 : cb();
|
|
222
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
|
|
197
223
|
});
|
|
198
224
|
this.inkApp = null;
|
|
225
|
+
} else {
|
|
226
|
+
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
|
|
199
227
|
}
|
|
200
228
|
}
|
|
201
229
|
constructor(options = {}){
|
|
@@ -204,6 +232,8 @@ class SessionImpl {
|
|
|
204
232
|
this.closed = false;
|
|
205
233
|
this.waitCallbacks = [];
|
|
206
234
|
this.store = new ProcessStore(options);
|
|
235
|
+
var _options_interactive;
|
|
236
|
+
this.isInteractive = (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false;
|
|
207
237
|
// Render Ink app immediately
|
|
208
238
|
this.inkApp = render(/*#__PURE__*/ _jsx(App, {
|
|
209
239
|
store: this.store
|
package/dist/esm/session.js.map
CHANGED
|
@@ -1 +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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAOA,SAASC,UAAU,QAA0B,iBAAiB;AACrE,OAAOC,YAAY,SAAS;AAC5B,SAASC,MAAM,QAAQ,MAAM;AAC7B,OAAOC,QAAQ,SAAS;AACxB,OAAOC,WAAW,WAAW;AAE7B,OAAOC,SAAS,sBAAsB;AACtC,SAASC,eAAe,QAAQ,iBAAiB;AACjD,OAAOC,cAAc,oBAAoB;AACzC,OAAOC,oBAAoB,0BAA0B;AACrD,OAAOC,qBAAqB,2BAA2B;AACvD,SAASC,YAAY,QAAQ,0BAA0B;AAEvD,SAASC,QAAQ,QAAQ,aAAa;AAQtC,MAAMC;IAiBJb,MAAMc,OAAe,EAAEC,IAAc,EAAEC,YAA0B,EAAEC,OAAuB,EAAEC,QAA0B,EAAQ;QAC5H,IAAI,IAAI,CAACC,MAAM,EAAE;YACf,MAAM,IAAIC,MAAM;QAClB;QAEA,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAgB,GAAGN,cAAdO,uCAAcP;YAAlCK;YAAUC;;QAElB,IAAIA,UAAU,WAAW;YACvB,IAAI,CAACE,YAAY;YACjB,MAAMC,KAAKvB,OAAOwB,UAAU;YAC5B,IAAI,CAACC,KAAK,CAACC,UAAU,CAAC;gBACpBH;gBACAI,OAAO;oBAACf;iBAAQ,CAACgB,MAAM,CAACpB,gBAAgBK,OAAOgB,IAAI,CAAC;gBACpDC,OAAO;gBACPC,OAAO,EAAE;gBACTC,OAAOjB,QAAQiB,KAAK;gBACpBC,UAAUlB,QAAQkB,QAAQ;YAC5B;YAEA,MAAMC,KAAKnC,WAAWa,SAASC,MAAMQ;YACrC,MAAMc,UAAU;gBAAEC,QAAQ;gBAA4CC,QAAQ;YAA2C;YAEzH,MAAMC,QAAQ,IAAInC;YAClB,IAAI+B,GAAGE,MAAM,EAAE;gBACbD,QAAQC,MAAM,GAAG9B,SAAS,CAACyB;oBACzB,IAAI,CAACN,KAAK,CAACc,WAAW,CACpBhB,IACAQ,MAAMS,GAAG,CAAC,CAACC,OAAU,CAAA;4BAAEC,MAAMhC,SAAS0B,MAAM;4BAAEK;wBAAK,CAAA;gBAEvD;gBACAH,MAAMK,KAAK,CAACzC,GAAG0C,IAAI,CAAC,MAAMV,GAAGE,MAAM,CAACS,IAAI,CAACV,QAAQC,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACA,IAAIF,GAAGG,MAAM,EAAE;gBACbF,QAAQE,MAAM,GAAG/B,SAAS,CAACyB;oBACzB,IAAI,CAACN,KAAK,CAACc,WAAW,CACpBhB,IACAQ,MAAMS,GAAG,CAAC,CAACC,OAAU,CAAA;4BAAEC,MAAMhC,SAAS2B,MAAM;4BAAEI;wBAAK,CAAA;gBAEvD;gBACAH,MAAMK,KAAK,CAACzC,GAAG0C,IAAI,CAAC,MAAMV,GAAGG,MAAM,CAACQ,IAAI,CAACV,QAAQE,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACAC,MAAMK,KAAK,CAAC7C,MAAMgD,MAAM,CAACF,IAAI,CAAC,MAAMV,IAAIb;YACxCiB,MAAMS,KAAK,CAAC,CAACC;gBACX,MAAMC,MAAOD,MAAMA,MAAM,CAAC;gBAC1BC,IAAIb,MAAM,GAAGD,QAAQC,MAAM,GAAG,AAACD,QAAQC,MAAM,CAAmCc,MAAM,GAAG;gBACzFD,IAAIZ,MAAM,GAAGF,QAAQE,MAAM,GAAG,AAACF,QAAQE,MAAM,CAAmCa,MAAM,GAAG;gBACzFD,IAAIC,MAAM,GAAG;oBAACD,IAAIb,MAAM;oBAAEa,IAAIZ,MAAM;oBAAE;iBAAK;gBAC3C,IAAI,CAACZ,KAAK,CAAC0B,aAAa,CAAC5B,IAAI;oBAAEO,OAAOkB,MAAM,UAAU;gBAAU;gBAEhE,IAAI,CAACI,iBAAiB;gBACtBJ,MAAMhC,SAASgC,OAAOhC,SAAS,MAAMiC;YACvC;QACF,OAAO;YACL,2DAA2D;YAC3D,MAAMf,KAAKnC,WAAWa,SAASC,MAAMQ;YACrC,MAAMc,UAAU;gBAAEC,QAAQ;gBAAkDC,QAAQ;YAAiD;YAErI,MAAMC,QAAQ,IAAInC;YAClB,IAAI+B,GAAGE,MAAM,EAAE;gBACbD,QAAQC,MAAM,GAAG7B,eAAe,CAAC2C;oBAC9Bf,QAAQC,MAAM,CAAmCc,MAAM,GAAGA,OAAOG,QAAQ,CAAClC,YAAY;gBACzF;gBACAmB,MAAMK,KAAK,CAACzC,GAAG0C,IAAI,CAAC,MAAMV,GAAGE,MAAM,CAACS,IAAI,CAACV,QAAQC,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACA,IAAIF,GAAGG,MAAM,EAAE;gBACbF,QAAQE,MAAM,GAAG9B,eAAe,CAAC2C;oBAC9Bf,QAAQE,MAAM,CAAmCa,MAAM,GAAGA,OAAOG,QAAQ,CAAClC,YAAY;gBACzF;gBACAmB,MAAMK,KAAK,CAACzC,GAAG0C,IAAI,CAAC,MAAMV,GAAGG,MAAM,CAACQ,IAAI,CAACV,QAAQE,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACAC,MAAMK,KAAK,CAAC7C,MAAMgD,MAAM,CAACF,IAAI,CAAC,MAAMV,IAAIb;YACxCiB,MAAMS,KAAK,CAAC,CAACC;gBACX,MAAMC,MAAOD,MAAMA,MAAM,CAAC;gBAC1BC,IAAIb,MAAM,GAAGD,QAAQC,MAAM,GAAG,AAACD,QAAQC,MAAM,CAAmCc,MAAM,GAAG;gBACzFD,IAAIZ,MAAM,GAAGF,QAAQE,MAAM,GAAG,AAACF,QAAQE,MAAM,CAAmCa,MAAM,GAAG;gBACzFD,IAAIC,MAAM,GAAG;oBAACD,IAAIb,MAAM;oBAAEa,IAAIZ,MAAM;oBAAE;iBAAK;gBAC3CW,MAAMhC,SAASgC,OAAOhC,SAAS,MAAMiC;YACvC;QACF;IACF;IAEAK,QAAc;QACZ,IAAI,IAAI,CAACrC,MAAM,EAAE;QACjB,IAAI,CAACA,MAAM,GAAG;QACd,IAAI,CAACsC,OAAO;IACd;IAEAC,aAAaxC,QAAqB,EAAQ;QACxC,IAAI,IAAI,CAACC,MAAM,EAAE;YACfD,qBAAAA,+BAAAA;YACA;QACF;QAEA,IAAI,IAAI,CAACM,YAAY,KAAK,GAAG;YAC3B,IAAI,CAACgC,KAAK;YACVtC,qBAAAA,+BAAAA;QACF,OAAO;YACL,IAAIA,UAAU,IAAI,CAACyC,aAAa,CAACC,IAAI,CAAC1C;QACtC,sCAAsC;QACxC;IACF;IAEQoC,oBAA0B;QAChC,IAAI,CAAC9B,YAAY;QACjB,IAAI,IAAI,CAACA,YAAY,KAAK,KAAK,IAAI,CAACmC,aAAa,CAACE,MAAM,GAAG,GAAG;YAC5D,IAAI,CAACL,KAAK;YACV,KAAK,MAAMM,MAAM,IAAI,CAACH,aAAa,CAAEG;YACrC,IAAI,CAACH,aAAa,GAAG,EAAE;QACzB;IACF;IAEQF,UAAgB;QACtB,iCAAiC;QACjC,IAAI,CAAC9B,KAAK,CAACoC,UAAU,CAAC;YACpB,IAAI,CAACpC,KAAK,CAACqC,KAAK;YAChBC,QAAQ3B,MAAM,CAAC4B,KAAK,CAAC,cAAc,cAAc;QACnD;QAEA,yBAAyB;QACzB,IAAI,IAAI,CAACC,MAAM,EAAE;YACf,IAAI,CAACA,MAAM,CACRC,aAAa,GACbC,IAAI,CAAC;gBACJ,MAAMP,KAAK,IAAI,CAACnC,KAAK,CAAC2C,eAAe;gBACrCR,eAAAA,yBAAAA;YACF,GACCS,KAAK,CAAC;gBACL,MAAMT,KAAK,IAAI,CAACnC,KAAK,CAAC2C,eAAe;gBACrCR,eAAAA,yBAAAA;YACF;YACF,IAAI,CAACK,MAAM,GAAG;QAChB;IACF;IA9IA,YAAYlD,UAA0B,CAAC,CAAC,CAAE;aALlCkD,SAA2C;aAC3C3C,eAAe;aACfL,SAAS;aACTwC,gBAAgC,EAAE;QAGxC,IAAI,CAAChC,KAAK,GAAG,IAAIhB,aAAaM;QAE9B,6BAA6B;QAC7B,IAAI,CAACkD,MAAM,GAAGhE,qBAAO,KAACG;YAAIqB,OAAO,IAAI,CAACA,KAAK;YAAM;YAC/C6C,sBAAsB;YACtBC,QAAQlE;QACV;IACF;AAuIF;AAEA,OAAO,SAASmE,cAAczD,UAA0B,CAAC,CAAC;IACxD,OAAO,IAAIJ,YAAYI;AACzB"}
|
|
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 private isInteractive: boolean;\n\n constructor(options: SessionOptions = {}) {\n this.store = new ProcessStore(options);\n this.isInteractive = options.interactive ?? false;\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 (callback) this.waitCallbacks.push(callback);\n\n if (this.runningCount === 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n // If runningCount > 0, will close when it hits 0 in onProcessComplete\n }\n\n private onProcessComplete(): void {\n this.runningCount--;\n if (this.runningCount === 0 && this.waitCallbacks.length > 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n }\n\n private closeAndCallWaitCallbacks(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup(() => {\n for (const cb of this.waitCallbacks) cb();\n this.waitCallbacks = [];\n });\n }\n\n private cleanup(onComplete?: () => void): 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 onComplete?.();\n })\n .catch(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n });\n this.inkApp = null;\n } else {\n onComplete?.();\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","isInteractive","unsubscribe","subscribe","getShouldExit","closeAndCallWaitCallbacks","length","cb","onComplete","signalExit","reset","process","write","inkApp","waitUntilExit","then","getExitCallback","catch","interactive","incrementalRendering","maxFps","createSession"],"mappings":"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"}
|
|
@@ -5,3 +5,7 @@ export declare const BATCH_MAX_LINES = 20;
|
|
|
5
5
|
export declare const BATCH_MAX_WAIT_MS = 50;
|
|
6
6
|
export declare const DEFAULT_MAX_FPS = 20;
|
|
7
7
|
export declare const EXPANDED_MAX_VISIBLE_LINES = 10;
|
|
8
|
+
export declare const SPINNER: {
|
|
9
|
+
interval: number;
|
|
10
|
+
frames: string[];
|
|
11
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the visible length of a string, ignoring ANSI escape codes.
|
|
3
|
+
*/
|
|
4
|
+
export declare function visibleLength(str: string): number;
|
|
5
|
+
/**
|
|
6
|
+
* Clip text to a maximum visible width, accounting for ANSI escape codes.
|
|
7
|
+
* Adds ellipsis (…) if truncated.
|
|
8
|
+
*/
|
|
9
|
+
export declare function clipText(str: string, maxWidth: number): string;
|
|
@@ -12,6 +12,7 @@ export declare class ProcessStore {
|
|
|
12
12
|
private selectedErrorIndex;
|
|
13
13
|
private expandedId;
|
|
14
14
|
private scrollOffset;
|
|
15
|
+
private listScrollOffset;
|
|
15
16
|
private header;
|
|
16
17
|
private showStatusBar;
|
|
17
18
|
private isInteractive;
|
|
@@ -31,6 +32,7 @@ export declare class ProcessStore {
|
|
|
31
32
|
getSelectedErrorIndex: () => number;
|
|
32
33
|
getExpandedId: () => string | null;
|
|
33
34
|
getScrollOffset: () => number;
|
|
35
|
+
getListScrollOffset: () => number;
|
|
34
36
|
getHeader: () => string | undefined;
|
|
35
37
|
getShowStatusBar: () => boolean;
|
|
36
38
|
getIsInteractive: () => boolean;
|
|
@@ -40,8 +42,9 @@ export declare class ProcessStore {
|
|
|
40
42
|
appendLines(id: string, newLines: Line[]): void;
|
|
41
43
|
getProcess(id: string): ChildProcess | undefined;
|
|
42
44
|
setMode(mode: Mode): void;
|
|
43
|
-
selectNext(): void;
|
|
44
|
-
selectPrev(): void;
|
|
45
|
+
selectNext(visibleCount?: number): void;
|
|
46
|
+
selectPrev(visibleCount?: number): void;
|
|
47
|
+
private adjustListScroll;
|
|
45
48
|
getSelectedProcess(): ChildProcess | undefined;
|
|
46
49
|
selectNextError(): void;
|
|
47
50
|
selectPrevError(): void;
|
|
@@ -73,18 +73,31 @@ export class ProcessStore {
|
|
|
73
73
|
this.notify();
|
|
74
74
|
}
|
|
75
75
|
// Interactive mode navigation
|
|
76
|
-
selectNext() {
|
|
76
|
+
selectNext(visibleCount) {
|
|
77
77
|
if (this.processes.length > 0) {
|
|
78
78
|
this.selectedIndex = (this.selectedIndex + 1) % this.processes.length;
|
|
79
|
+
this.adjustListScroll(visibleCount);
|
|
79
80
|
this.notify();
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
|
-
selectPrev() {
|
|
83
|
+
selectPrev(visibleCount) {
|
|
83
84
|
if (this.processes.length > 0) {
|
|
84
85
|
this.selectedIndex = (this.selectedIndex - 1 + this.processes.length) % this.processes.length;
|
|
86
|
+
this.adjustListScroll(visibleCount);
|
|
85
87
|
this.notify();
|
|
86
88
|
}
|
|
87
89
|
}
|
|
90
|
+
adjustListScroll(visibleCount) {
|
|
91
|
+
if (!visibleCount || visibleCount <= 0) return;
|
|
92
|
+
// Ensure selected item is visible in viewport
|
|
93
|
+
if (this.selectedIndex < this.listScrollOffset) {
|
|
94
|
+
// Selected is above viewport - scroll up
|
|
95
|
+
this.listScrollOffset = this.selectedIndex;
|
|
96
|
+
} else if (this.selectedIndex >= this.listScrollOffset + visibleCount) {
|
|
97
|
+
// Selected is below viewport - scroll down
|
|
98
|
+
this.listScrollOffset = this.selectedIndex - visibleCount + 1;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
88
101
|
getSelectedProcess() {
|
|
89
102
|
return this.processes[this.selectedIndex];
|
|
90
103
|
}
|
|
@@ -159,6 +172,7 @@ export class ProcessStore {
|
|
|
159
172
|
this.selectedErrorIndex = 0;
|
|
160
173
|
this.expandedId = null;
|
|
161
174
|
this.scrollOffset = 0;
|
|
175
|
+
this.listScrollOffset = 0;
|
|
162
176
|
this.header = undefined;
|
|
163
177
|
}
|
|
164
178
|
notify() {
|
|
@@ -178,6 +192,7 @@ export class ProcessStore {
|
|
|
178
192
|
this.selectedErrorIndex = 0;
|
|
179
193
|
this.expandedId = null;
|
|
180
194
|
this.scrollOffset = 0;
|
|
195
|
+
this.listScrollOffset = 0; // Viewport offset for process list
|
|
181
196
|
this.showStatusBar = false;
|
|
182
197
|
this.isInteractive = false;
|
|
183
198
|
// useSyncExternalStore API
|
|
@@ -214,6 +229,7 @@ export class ProcessStore {
|
|
|
214
229
|
this.getSelectedErrorIndex = ()=>this.selectedErrorIndex;
|
|
215
230
|
this.getExpandedId = ()=>this.expandedId;
|
|
216
231
|
this.getScrollOffset = ()=>this.scrollOffset;
|
|
232
|
+
this.getListScrollOffset = ()=>this.listScrollOffset;
|
|
217
233
|
// Session-level getters (set at session creation, immutable)
|
|
218
234
|
this.getHeader = ()=>this.header;
|
|
219
235
|
this.getShowStatusBar = ()=>this.showStatusBar;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/state/processStore.ts"],"sourcesContent":["import { DEFAULT_COLUMN_WIDTH } from '../constants.ts';\nimport type { ChildProcess, Line, SessionOptions } from '../types.ts';\nimport { LineType } from '../types.ts';\n\ntype Listener = () => void;\ntype Mode = 'normal' | 'interactive' | 'errorList' | 'errorDetail';\n\nexport class ProcessStore {\n private processes: ChildProcess[] = [];\n private completedIds: string[] = []; // Track completion order\n private listeners = new Set<Listener>();\n private shouldExit = false;\n private exitCallback: (() => void) | null = null;\n\n // UI state\n private mode: Mode = 'normal';\n private selectedIndex = 0;\n private selectedErrorIndex = 0;\n private expandedId: string | null = null;\n private scrollOffset = 0;\n\n // Session-level display settings (set once at session creation)\n private header: string | undefined;\n private showStatusBar = false;\n private isInteractive = false;\n\n constructor(options: SessionOptions = {}) {\n this.header = options.header;\n this.showStatusBar = options.showStatusBar ?? false;\n this.isInteractive = options.interactive ?? false;\n }\n\n // useSyncExternalStore API\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n getSnapshot = (): ChildProcess[] => this.processes;\n\n // Filtered getters\n getRunningProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'running');\n };\n\n getCompletedProcesses = (): ChildProcess[] => {\n // Return in completion order\n return this.completedIds.map((id) => this.processes.find((p) => p.id === id)).filter((p): p is ChildProcess => p !== undefined);\n };\n\n getFailedProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'error');\n };\n\n // Counts\n getRunningCount = (): number => this.processes.filter((p) => p.state === 'running').length;\n getMaxGroupLength = (): number => {\n if (this.processes.length === 0) return DEFAULT_COLUMN_WIDTH;\n return Math.max(...this.processes.map((p) => (p.group || p.title).length));\n };\n getDoneCount = (): number => this.processes.filter((p) => p.state !== 'running').length;\n getErrorCount = (): number => this.processes.filter((p) => p.state === 'error').length;\n getErrorLineCount = (): number => {\n return this.processes.filter((p) => p.state === 'error').reduce((total, p) => total + p.lines.filter((l) => l.type === LineType.stderr).length, 0);\n };\n\n // UI state getters\n getMode = (): Mode => this.mode;\n getSelectedIndex = (): number => this.selectedIndex;\n getSelectedErrorIndex = (): number => this.selectedErrorIndex;\n getExpandedId = (): string | null => this.expandedId;\n getScrollOffset = (): number => this.scrollOffset;\n // Session-level getters (set at session creation, immutable)\n getHeader = (): string | undefined => this.header;\n getShowStatusBar = (): boolean => this.showStatusBar;\n getIsInteractive = (): boolean => this.isInteractive;\n isAllComplete = (): boolean => this.processes.length > 0 && this.processes.every((p) => p.state !== 'running');\n\n // Mutations - Ink handles render throttling at 30 FPS\n addProcess(process: ChildProcess): void {\n this.processes = [...this.processes, process];\n this.notify();\n }\n\n updateProcess(id: string, update: Partial<ChildProcess>): void {\n const oldProcess = this.processes.find((p) => p.id === id);\n const wasRunning = oldProcess?.state === 'running';\n const isNowComplete = update.state && update.state !== 'running';\n\n this.processes = this.processes.map((p) => (p.id === id ? { ...p, ...update } : p));\n\n // Track completion order\n if (wasRunning && isNowComplete && !this.completedIds.includes(id)) {\n this.completedIds = [...this.completedIds, id];\n }\n\n this.notify();\n }\n\n appendLines(id: string, newLines: Line[]): void {\n const process = this.processes.find((p) => p.id === id);\n if (process) {\n this.updateProcess(id, { lines: process.lines.concat(newLines) });\n }\n }\n\n getProcess(id: string): ChildProcess | undefined {\n return this.processes.find((p) => p.id === id);\n }\n\n // UI state mutations\n setMode(mode: Mode): void {\n this.mode = mode;\n if (mode === 'interactive') {\n this.selectedIndex = 0;\n } else if (mode === 'errorList') {\n this.selectedErrorIndex = 0;\n }\n this.notify();\n }\n\n // Interactive mode navigation\n selectNext(): void {\n if (this.processes.length > 0) {\n this.selectedIndex = (this.selectedIndex + 1) % this.processes.length;\n this.notify();\n }\n }\n\n selectPrev(): void {\n if (this.processes.length > 0) {\n this.selectedIndex = (this.selectedIndex - 1 + this.processes.length) % this.processes.length;\n this.notify();\n }\n }\n\n getSelectedProcess(): ChildProcess | undefined {\n return this.processes[this.selectedIndex];\n }\n\n selectNextError(): void {\n const failed = this.getFailedProcesses();\n if (failed.length > 0) {\n this.selectedErrorIndex = (this.selectedErrorIndex + 1) % failed.length;\n this.notify();\n }\n }\n\n selectPrevError(): void {\n const failed = this.getFailedProcesses();\n if (failed.length > 0) {\n this.selectedErrorIndex = (this.selectedErrorIndex - 1 + failed.length) % failed.length;\n this.notify();\n }\n }\n\n getSelectedError(): ChildProcess | undefined {\n const failed = this.getFailedProcesses();\n return failed[this.selectedErrorIndex];\n }\n\n // Expansion methods\n toggleExpand(): void {\n const selected = this.getSelectedProcess();\n if (!selected) return;\n\n if (this.expandedId === selected.id) {\n // Collapse\n this.expandedId = null;\n this.scrollOffset = 0;\n } else {\n // Expand\n this.expandedId = selected.id;\n this.scrollOffset = 0;\n }\n this.notify();\n }\n\n collapse(): void {\n this.expandedId = null;\n this.scrollOffset = 0;\n this.notify();\n }\n\n scrollDown(maxVisible: number): void {\n if (!this.expandedId) return;\n const process = this.getProcess(this.expandedId);\n if (!process) return;\n\n const maxOffset = Math.max(0, process.lines.length - maxVisible);\n if (this.scrollOffset < maxOffset) {\n this.scrollOffset++;\n this.notify();\n }\n }\n\n scrollUp(): void {\n if (!this.expandedId) return;\n if (this.scrollOffset > 0) {\n this.scrollOffset--;\n this.notify();\n }\n }\n\n // Exit signaling\n signalExit(callback: () => void): void {\n this.shouldExit = true;\n this.exitCallback = callback;\n this.notify();\n }\n\n getShouldExit = (): boolean => this.shouldExit;\n getExitCallback = (): (() => void) | null => this.exitCallback;\n\n reset(): void {\n this.processes = [];\n this.completedIds = [];\n this.shouldExit = false;\n this.exitCallback = null;\n this.mode = 'normal';\n this.selectedIndex = 0;\n this.selectedErrorIndex = 0;\n this.expandedId = null;\n this.scrollOffset = 0;\n this.header = undefined;\n }\n\n private notify(): void {\n this.listeners.forEach((l) => {\n l();\n });\n }\n}\n\n// Note: No global singleton - session creates its own store instance\n"],"names":["DEFAULT_COLUMN_WIDTH","LineType","ProcessStore","addProcess","process","processes","notify","updateProcess","id","update","oldProcess","find","p","wasRunning","state","isNowComplete","map","completedIds","includes","appendLines","newLines","lines","concat","getProcess","setMode","mode","selectedIndex","selectedErrorIndex","selectNext","length","selectPrev","getSelectedProcess","selectNextError","failed","getFailedProcesses","selectPrevError","getSelectedError","toggleExpand","selected","expandedId","scrollOffset","collapse","scrollDown","maxVisible","maxOffset","Math","max","scrollUp","signalExit","callback","shouldExit","exitCallback","reset","header","undefined","listeners","forEach","l","options","Set","showStatusBar","isInteractive","subscribe","listener","add","delete","getSnapshot","getRunningProcesses","filter","getCompletedProcesses","getRunningCount","getMaxGroupLength","group","title","getDoneCount","getErrorCount","getErrorLineCount","reduce","total","type","stderr","getMode","getSelectedIndex","getSelectedErrorIndex","getExpandedId","getScrollOffset","getHeader","getShowStatusBar","getIsInteractive","isAllComplete","every","getShouldExit","getExitCallback","interactive"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/state/processStore.ts"],"sourcesContent":["import { DEFAULT_COLUMN_WIDTH } from '../constants.ts';\nimport type { ChildProcess, Line, SessionOptions } from '../types.ts';\nimport { LineType } from '../types.ts';\n\ntype Listener = () => void;\ntype Mode = 'normal' | 'interactive' | 'errorList' | 'errorDetail';\n\nexport class ProcessStore {\n private processes: ChildProcess[] = [];\n private completedIds: string[] = []; // Track completion order\n private listeners = new Set<Listener>();\n private shouldExit = false;\n private exitCallback: (() => void) | null = null;\n\n // UI state\n private mode: Mode = 'normal';\n private selectedIndex = 0;\n private selectedErrorIndex = 0;\n private expandedId: string | null = null;\n private scrollOffset = 0;\n private listScrollOffset = 0; // Viewport offset for process list\n\n // Session-level display settings (set once at session creation)\n private header: string | undefined;\n private showStatusBar = false;\n private isInteractive = false;\n\n constructor(options: SessionOptions = {}) {\n this.header = options.header;\n this.showStatusBar = options.showStatusBar ?? false;\n this.isInteractive = options.interactive ?? false;\n }\n\n // useSyncExternalStore API\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n getSnapshot = (): ChildProcess[] => this.processes;\n\n // Filtered getters\n getRunningProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'running');\n };\n\n getCompletedProcesses = (): ChildProcess[] => {\n // Return in completion order\n return this.completedIds.map((id) => this.processes.find((p) => p.id === id)).filter((p): p is ChildProcess => p !== undefined);\n };\n\n getFailedProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'error');\n };\n\n // Counts\n getRunningCount = (): number => this.processes.filter((p) => p.state === 'running').length;\n getMaxGroupLength = (): number => {\n if (this.processes.length === 0) return DEFAULT_COLUMN_WIDTH;\n return Math.max(...this.processes.map((p) => (p.group || p.title).length));\n };\n getDoneCount = (): number => this.processes.filter((p) => p.state !== 'running').length;\n getErrorCount = (): number => this.processes.filter((p) => p.state === 'error').length;\n getErrorLineCount = (): number => {\n return this.processes.filter((p) => p.state === 'error').reduce((total, p) => total + p.lines.filter((l) => l.type === LineType.stderr).length, 0);\n };\n\n // UI state getters\n getMode = (): Mode => this.mode;\n getSelectedIndex = (): number => this.selectedIndex;\n getSelectedErrorIndex = (): number => this.selectedErrorIndex;\n getExpandedId = (): string | null => this.expandedId;\n getScrollOffset = (): number => this.scrollOffset;\n getListScrollOffset = (): number => this.listScrollOffset;\n // Session-level getters (set at session creation, immutable)\n getHeader = (): string | undefined => this.header;\n getShowStatusBar = (): boolean => this.showStatusBar;\n getIsInteractive = (): boolean => this.isInteractive;\n isAllComplete = (): boolean => this.processes.length > 0 && this.processes.every((p) => p.state !== 'running');\n\n // Mutations - Ink handles render throttling at 30 FPS\n addProcess(process: ChildProcess): void {\n this.processes = [...this.processes, process];\n this.notify();\n }\n\n updateProcess(id: string, update: Partial<ChildProcess>): void {\n const oldProcess = this.processes.find((p) => p.id === id);\n const wasRunning = oldProcess?.state === 'running';\n const isNowComplete = update.state && update.state !== 'running';\n\n this.processes = this.processes.map((p) => (p.id === id ? { ...p, ...update } : p));\n\n // Track completion order\n if (wasRunning && isNowComplete && !this.completedIds.includes(id)) {\n this.completedIds = [...this.completedIds, id];\n }\n\n this.notify();\n }\n\n appendLines(id: string, newLines: Line[]): void {\n const process = this.processes.find((p) => p.id === id);\n if (process) {\n this.updateProcess(id, { lines: process.lines.concat(newLines) });\n }\n }\n\n getProcess(id: string): ChildProcess | undefined {\n return this.processes.find((p) => p.id === id);\n }\n\n // UI state mutations\n setMode(mode: Mode): void {\n this.mode = mode;\n if (mode === 'interactive') {\n this.selectedIndex = 0;\n } else if (mode === 'errorList') {\n this.selectedErrorIndex = 0;\n }\n this.notify();\n }\n\n // Interactive mode navigation\n selectNext(visibleCount?: number): void {\n if (this.processes.length > 0) {\n this.selectedIndex = (this.selectedIndex + 1) % this.processes.length;\n this.adjustListScroll(visibleCount);\n this.notify();\n }\n }\n\n selectPrev(visibleCount?: number): void {\n if (this.processes.length > 0) {\n this.selectedIndex = (this.selectedIndex - 1 + this.processes.length) % this.processes.length;\n this.adjustListScroll(visibleCount);\n this.notify();\n }\n }\n\n private adjustListScroll(visibleCount?: number): void {\n if (!visibleCount || visibleCount <= 0) return;\n\n // Ensure selected item is visible in viewport\n if (this.selectedIndex < this.listScrollOffset) {\n // Selected is above viewport - scroll up\n this.listScrollOffset = this.selectedIndex;\n } else if (this.selectedIndex >= this.listScrollOffset + visibleCount) {\n // Selected is below viewport - scroll down\n this.listScrollOffset = this.selectedIndex - visibleCount + 1;\n }\n }\n\n getSelectedProcess(): ChildProcess | undefined {\n return this.processes[this.selectedIndex];\n }\n\n selectNextError(): void {\n const failed = this.getFailedProcesses();\n if (failed.length > 0) {\n this.selectedErrorIndex = (this.selectedErrorIndex + 1) % failed.length;\n this.notify();\n }\n }\n\n selectPrevError(): void {\n const failed = this.getFailedProcesses();\n if (failed.length > 0) {\n this.selectedErrorIndex = (this.selectedErrorIndex - 1 + failed.length) % failed.length;\n this.notify();\n }\n }\n\n getSelectedError(): ChildProcess | undefined {\n const failed = this.getFailedProcesses();\n return failed[this.selectedErrorIndex];\n }\n\n // Expansion methods\n toggleExpand(): void {\n const selected = this.getSelectedProcess();\n if (!selected) return;\n\n if (this.expandedId === selected.id) {\n // Collapse\n this.expandedId = null;\n this.scrollOffset = 0;\n } else {\n // Expand\n this.expandedId = selected.id;\n this.scrollOffset = 0;\n }\n this.notify();\n }\n\n collapse(): void {\n this.expandedId = null;\n this.scrollOffset = 0;\n this.notify();\n }\n\n scrollDown(maxVisible: number): void {\n if (!this.expandedId) return;\n const process = this.getProcess(this.expandedId);\n if (!process) return;\n\n const maxOffset = Math.max(0, process.lines.length - maxVisible);\n if (this.scrollOffset < maxOffset) {\n this.scrollOffset++;\n this.notify();\n }\n }\n\n scrollUp(): void {\n if (!this.expandedId) return;\n if (this.scrollOffset > 0) {\n this.scrollOffset--;\n this.notify();\n }\n }\n\n // Exit signaling\n signalExit(callback: () => void): void {\n this.shouldExit = true;\n this.exitCallback = callback;\n this.notify();\n }\n\n getShouldExit = (): boolean => this.shouldExit;\n getExitCallback = (): (() => void) | null => this.exitCallback;\n\n reset(): void {\n this.processes = [];\n this.completedIds = [];\n this.shouldExit = false;\n this.exitCallback = null;\n this.mode = 'normal';\n this.selectedIndex = 0;\n this.selectedErrorIndex = 0;\n this.expandedId = null;\n this.scrollOffset = 0;\n this.listScrollOffset = 0;\n this.header = undefined;\n }\n\n private notify(): void {\n this.listeners.forEach((l) => {\n l();\n });\n }\n}\n\n// Note: No global singleton - session creates its own store instance\n"],"names":["DEFAULT_COLUMN_WIDTH","LineType","ProcessStore","addProcess","process","processes","notify","updateProcess","id","update","oldProcess","find","p","wasRunning","state","isNowComplete","map","completedIds","includes","appendLines","newLines","lines","concat","getProcess","setMode","mode","selectedIndex","selectedErrorIndex","selectNext","visibleCount","length","adjustListScroll","selectPrev","listScrollOffset","getSelectedProcess","selectNextError","failed","getFailedProcesses","selectPrevError","getSelectedError","toggleExpand","selected","expandedId","scrollOffset","collapse","scrollDown","maxVisible","maxOffset","Math","max","scrollUp","signalExit","callback","shouldExit","exitCallback","reset","header","undefined","listeners","forEach","l","options","Set","showStatusBar","isInteractive","subscribe","listener","add","delete","getSnapshot","getRunningProcesses","filter","getCompletedProcesses","getRunningCount","getMaxGroupLength","group","title","getDoneCount","getErrorCount","getErrorLineCount","reduce","total","type","stderr","getMode","getSelectedIndex","getSelectedErrorIndex","getExpandedId","getScrollOffset","getListScrollOffset","getHeader","getShowStatusBar","getIsInteractive","isAllComplete","every","getShouldExit","getExitCallback","interactive"],"mappings":"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"}
|