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.
Files changed (39) hide show
  1. package/dist/cjs/components/App.js +25 -6
  2. package/dist/cjs/components/App.js.map +1 -1
  3. package/dist/cjs/components/ChildProcess.js +2 -17
  4. package/dist/cjs/components/ChildProcess.js.map +1 -1
  5. package/dist/cjs/components/CompactProcessLine.js +27 -36
  6. package/dist/cjs/components/CompactProcessLine.js.map +1 -1
  7. package/dist/cjs/components/StatusBar.js +4 -19
  8. package/dist/cjs/components/StatusBar.js.map +1 -1
  9. package/dist/cjs/constants.js +18 -0
  10. package/dist/cjs/constants.js.map +1 -1
  11. package/dist/cjs/lib/clipText.js +56 -0
  12. package/dist/cjs/lib/clipText.js.map +1 -0
  13. package/dist/cjs/session.js +43 -10
  14. package/dist/cjs/session.js.map +1 -1
  15. package/dist/cjs/src/constants.d.ts +4 -0
  16. package/dist/cjs/src/lib/clipText.d.ts +9 -0
  17. package/dist/cjs/src/state/processStore.d.ts +5 -2
  18. package/dist/cjs/state/processStore.js +20 -2
  19. package/dist/cjs/state/processStore.js.map +1 -1
  20. package/dist/esm/components/App.js +30 -9
  21. package/dist/esm/components/App.js.map +1 -1
  22. package/dist/esm/components/ChildProcess.js +1 -16
  23. package/dist/esm/components/ChildProcess.js.map +1 -1
  24. package/dist/esm/components/CompactProcessLine.js +26 -35
  25. package/dist/esm/components/CompactProcessLine.js.map +1 -1
  26. package/dist/esm/components/StatusBar.js +3 -18
  27. package/dist/esm/components/StatusBar.js.map +1 -1
  28. package/dist/esm/constants.js +16 -0
  29. package/dist/esm/constants.js.map +1 -1
  30. package/dist/esm/lib/clipText.js +37 -0
  31. package/dist/esm/lib/clipText.js.map +1 -0
  32. package/dist/esm/session.js +38 -8
  33. package/dist/esm/session.js.map +1 -1
  34. package/dist/esm/src/constants.d.ts +4 -0
  35. package/dist/esm/src/lib/clipText.d.ts +9 -0
  36. package/dist/esm/src/state/processStore.d.ts +5 -2
  37. package/dist/esm/state/processStore.js +18 -2
  38. package/dist/esm/state/processStore.js.map +1 -1
  39. 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
- // Truncate name if needed and pad to column width
83
- const truncatedName = truncate(displayName, nameColumnWidth).padEnd(nameColumnWidth);
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 ? truncate(lastLine, statusWidth) : '';
71
+ return lastLine ? clipText(lastLine, statusWidth) : '';
89
72
  }
90
73
  if (state === 'error') {
91
74
  const errorCount = getErrorCount(lines);
92
- return errorCount > 0 ? `${errorCount} error${errorCount > 1 ? 's' : ''}` : 'failed';
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(Text, {
132
- inverse: isSelected,
133
- children: truncatedName
116
+ /*#__PURE__*/ _jsx(Box, {
117
+ width: nameColumnWidth,
118
+ children: /*#__PURE__*/ _jsx(Text, {
119
+ inverse: isSelected,
120
+ children: clippedName
121
+ })
134
122
  }),
135
- statusText && /*#__PURE__*/ _jsxs(Text, {
136
- color: statusColor,
137
- children: [
138
- " ",
139
- statusText
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\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAASA,GAAG,EAAEC,IAAI,EAAEC,SAAS,QAAQ,MAAM;AAC3C,SAASC,IAAI,EAAEC,OAAO,QAAQ,QAAQ;AACtC,OAAOC,aAAa,oBAAoB;AACxC,SAASC,oBAAoB,QAAQ,mBAAmB;AACxD,SAASC,QAAQ,QAAQ,2BAA2B;AAEpD,SAASC,QAAQ,QAAQ,cAAc;AACvC,OAAOC,aAAa,eAAe;AAEnC,oHAAoH;AACpH,MAAMC,UAAU;IACdC,UAAU;IACVC,QAAQ;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;KAAI;AAC5D;AAOA,SAASC,SAASC,GAAW,EAAEC,SAAiB;IAC9C,IAAID,IAAIE,MAAM,IAAID,WAAW,OAAOD;IACpC,OAAO,GAAGA,IAAIG,KAAK,CAAC,GAAGF,YAAY,GAAG,CAAC,CAAC;AAC1C;AAEA,SAASG,kBAAkBC,KAAa;IACtC,IAAK,IAAIC,IAAID,MAAMH,MAAM,GAAG,GAAGI,KAAK,GAAGA,IAAK;QAC1C,IAAID,KAAK,CAACC,EAAE,CAACC,IAAI,CAACL,MAAM,GAAG,GAAG;YAC5B,OAAOG,KAAK,CAACC,EAAE,CAACC,IAAI;QACtB;IACF;IACA,OAAO;AACT;AAEA,SAASC,cAAcH,KAAa;IAClC,OAAOA,MAAMI,MAAM,CAAC,CAACC,OAASA,KAAKC,IAAI,KAAKjB,SAASkB,MAAM,EAAEV,MAAM;AACrE;AAEA,6BAAeb,KAAK,SAASwB,mBAAmB,EAAEC,IAAI,EAAEC,aAAa,KAAK,EAAS;IACjF,MAAMC,QAAQvB;IACd,MAAM,EAAEwB,MAAM,EAAE,GAAG7B;IACnB,MAAM8B,gBAAgBD,CAAAA,mBAAAA,6BAAAA,OAAQE,OAAO,KAAI;IAEzC,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAEC,KAAK,EAAEjB,KAAK,EAAE,GAAGS;IACvC,MAAMS,qBAAqBR,aAAaxB,QAAQiC,OAAO,GAAG;IAE1D,iDAAiD;IACjD,MAAMC,cAAcL,SAASC;IAE7B,oEAAoE;IACpE,MAAMK,YAAY,GAAG,eAAe;IACpC,MAAMC,iBAAiBX,MAAMY,iBAAiB;IAC9C,MAAMC,kBAAkBrC,qBAAqB,OAAO0B,eAAeS;IACnE,MAAMG,MAAM,GAAG,gCAAgC;IAC/C,MAAMC,cAAcb,gBAAgBQ,YAAYG,kBAAkBC;IAElE,kDAAkD;IAClD,MAAME,gBAAgBjC,SAAS0B,aAAaI,iBAAiBI,MAAM,CAACJ;IAEpE,6BAA6B;IAC7B,MAAMK,aAAa5C,QAAQ;QACzB,IAAIgC,UAAU,WAAW;YACvB,MAAMa,WAAW/B,kBAAkBC;YACnC,OAAO8B,WAAWpC,SAASoC,UAAUJ,eAAe;QACtD;QACA,IAAIT,UAAU,SAAS;YACrB,MAAMc,aAAa5B,cAAcH;YACjC,OAAO+B,aAAa,IAAI,GAAGA,WAAW,MAAM,EAAEA,aAAa,IAAI,MAAM,IAAI,GAAG;QAC9E;QACA,OAAO,IAAI,2BAA2B;IACxC,GAAG;QAACd;QAAOjB;QAAO0B;KAAY;IAE9B,sBAAsB;IACtB,MAAMM,OAAO/C,QAAQ;QACnB,OAAQgC;YACN,KAAK;gBACH,qBAAO,KAAC3B,4BAAYC;YACtB,KAAK;gBACH,qBAAO,KAACT;oBAAKmD,OAAM;8BAAS/C,QAAQgD,IAAI;;YAC1C,KAAK;gBACH,qBAAO,KAACpD;oBAAKmD,OAAM;8BAAO/C,QAAQiD,KAAK;;QAC3C;IACF,GAAG;QAAClB;KAAM;IAEV,oBAAoB;IACpB,MAAMmB,cAAcnB,UAAU,UAAU,QAAQ;IAEhD,qBACE,MAACpC;;0BACC,KAACC;gBAAKmD,OAAOvB,aAAa,SAAS2B;0BAAYnB;;0BAC/C,KAACrC;gBAAIyD,OAAOjB;0BAAYW;;0BACxB,KAAClD;gBAAKyD,SAAS7B;0BAAaiB;;YAC3BE,4BAAc,MAAC/C;gBAAKmD,OAAOG;;oBAAa;oBAAEP;;;;;AAGjD,GAAG"}
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\n// From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2\nconst SPINNER = {\n interval: 80,\n frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],\n};\n\ntype Props = {\n running: number;\n done: number;\n errors: number;\n errorLines: number;\n};\n\nexport default memo(function StatusBar({ running, done, errors, errorLines }: Props) {\n return (\n <Box justifyContent=\"space-between\">\n <Box>\n <Text>\n {running > 0 ? <Spinner {...SPINNER} /> : <Text color=\"green\">{figures.tick}</Text>}\n {` Running: ${running} `}\n <Text color=\"green\">{figures.tick}</Text>\n {` Done: ${done} `}\n <Text color=\"red\">{figures.cross}</Text>\n {` Errors: ${errors}`}\n {errorLines > 0 && <Text dimColor>{` (${errorLines} lines)`}</Text>}\n </Text>\n </Box>\n {errors > 0 && (\n <Box>\n <Text dimColor>[e]rrors</Text>\n </Box>\n )}\n </Box>\n );\n});\n"],"names":["Box","Text","memo","figures","Spinner","SPINNER","interval","frames","StatusBar","running","done","errors","errorLines","justifyContent","color","tick","cross","dimColor"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAASA,GAAG,EAAEC,IAAI,QAAQ,MAAM;AAChC,SAASC,IAAI,QAAQ,QAAQ;AAC7B,OAAOC,aAAa,oBAAoB;AACxC,OAAOC,aAAa,eAAe;AAEnC,oHAAoH;AACpH,MAAMC,UAAU;IACdC,UAAU;IACVC,QAAQ;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;QAAK;KAAI;AAC5D;AASA,6BAAeL,KAAK,SAASM,UAAU,EAAEC,OAAO,EAAEC,IAAI,EAAEC,MAAM,EAAEC,UAAU,EAAS;IACjF,qBACE,MAACZ;QAAIa,gBAAe;;0BAClB,KAACb;0BACC,cAAA,MAACC;;wBACEQ,UAAU,kBAAI,KAACL,4BAAYC,0BAAc,KAACJ;4BAAKa,OAAM;sCAASX,QAAQY,IAAI;;wBAC1E,CAAC,UAAU,EAAEN,QAAQ,EAAE,CAAC;sCACzB,KAACR;4BAAKa,OAAM;sCAASX,QAAQY,IAAI;;wBAChC,CAAC,OAAO,EAAEL,KAAK,EAAE,CAAC;sCACnB,KAACT;4BAAKa,OAAM;sCAAOX,QAAQa,KAAK;;wBAC/B,CAAC,SAAS,EAAEL,QAAQ;wBACpBC,aAAa,mBAAK,KAACX;4BAAKgB,QAAQ;sCAAE,CAAC,EAAE,EAAEL,WAAW,OAAO,CAAC;;;;;YAG9DD,SAAS,mBACR,KAACX;0BACC,cAAA,KAACC;oBAAKgB,QAAQ;8BAAC;;;;;AAKzB,GAAG"}
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"}
@@ -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"}
@@ -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.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
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.close();
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
@@ -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":"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"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spawn-term",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Formats spawn with for terminal grouping",
5
5
  "keywords": [
6
6
  "spawn",