spawn-term 3.0.1 → 3.0.3

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 (51) 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/createSessionWrapper.js +101 -0
  12. package/dist/cjs/createSessionWrapper.js.map +1 -0
  13. package/dist/cjs/index-esm.js +2 -2
  14. package/dist/cjs/index-esm.js.map +1 -1
  15. package/dist/cjs/lib/clipText.js +56 -0
  16. package/dist/cjs/lib/clipText.js.map +1 -0
  17. package/dist/cjs/session.js +26 -2
  18. package/dist/cjs/session.js.map +1 -1
  19. package/dist/cjs/src/constants.d.ts +4 -0
  20. package/dist/cjs/src/createSessionWrapper.d.ts +7 -0
  21. package/dist/cjs/src/index-esm.d.ts +2 -2
  22. package/dist/cjs/src/lib/clipText.d.ts +9 -0
  23. package/dist/cjs/src/state/processStore.d.ts +5 -2
  24. package/dist/cjs/state/processStore.js +20 -2
  25. package/dist/cjs/state/processStore.js.map +1 -1
  26. package/dist/esm/components/App.js +30 -9
  27. package/dist/esm/components/App.js.map +1 -1
  28. package/dist/esm/components/ChildProcess.js +1 -16
  29. package/dist/esm/components/ChildProcess.js.map +1 -1
  30. package/dist/esm/components/CompactProcessLine.js +26 -35
  31. package/dist/esm/components/CompactProcessLine.js.map +1 -1
  32. package/dist/esm/components/StatusBar.js +3 -18
  33. package/dist/esm/components/StatusBar.js.map +1 -1
  34. package/dist/esm/constants.js +16 -0
  35. package/dist/esm/constants.js.map +1 -1
  36. package/dist/esm/createSessionWrapper.js +43 -0
  37. package/dist/esm/createSessionWrapper.js.map +1 -0
  38. package/dist/esm/index-esm.js +1 -1
  39. package/dist/esm/index-esm.js.map +1 -1
  40. package/dist/esm/lib/clipText.js +37 -0
  41. package/dist/esm/lib/clipText.js.map +1 -0
  42. package/dist/esm/session.js +24 -2
  43. package/dist/esm/session.js.map +1 -1
  44. package/dist/esm/src/constants.d.ts +4 -0
  45. package/dist/esm/src/createSessionWrapper.d.ts +7 -0
  46. package/dist/esm/src/index-esm.d.ts +2 -2
  47. package/dist/esm/src/lib/clipText.d.ts +9 -0
  48. package/dist/esm/src/state/processStore.d.ts +5 -2
  49. package/dist/esm/state/processStore.js +18 -2
  50. package/dist/esm/state/processStore.js.map +1 -1
  51. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ChildProcess.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo, useMemo } from 'react';\nimport ansiRegex from '../lib/ansiRegex.ts';\nimport figures from '../lib/figures.ts';\nimport type { ChildProcess as ChildProcessT, Line, State } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport Spinner from './Spinner.ts';\n\nconst REGEX_ANSI = ansiRegex();\nconst BLANK_LINE = { type: LineType.stdout, text: '' };\n\n// From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2\nconst SPINNER = {\n interval: 80,\n frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],\n};\n\nconst ICONS = {\n error: <Text color=\"red\">{figures.cross}</Text>,\n success: <Text color=\"green\">{figures.tick}</Text>,\n running: <Spinner {...SPINNER} />,\n};\n\ntype ItemProps = {\n item: ChildProcessT;\n};\n\ntype HeaderProps = {\n group?: string;\n title: string;\n state: State;\n};\n\nconst Header = memo(\n function Header({ group, title, state }: HeaderProps) {\n const icon = ICONS[state];\n\n return (\n <Box>\n {icon}\n {group && <Text bold>{`${group}${figures.pointer} `}</Text>}\n <Text>{title}</Text>\n </Box>\n );\n },\n (a, b) => a.group === b.group && a.title === b.title && a.state === b.state\n);\n\ntype RunningSummaryProps = {\n line: Line;\n};\n\nconst RunningSummary = memo(function RunningSummary({ line }: RunningSummaryProps) {\n return (\n <Box marginLeft={2}>\n <Text color=\"gray\">{line.text.replace(REGEX_ANSI, '')}</Text>\n </Box>\n );\n});\n\ntype LinesProps = {\n lines: Line[];\n};\n\nconst renderLine = (line, index) => {\n return (\n <Box key={index} minHeight={1}>\n <Text>{line.text}</Text>\n </Box>\n );\n};\n\nconst Lines = memo(function Lines({ lines }: LinesProps) {\n return (\n <Box flexDirection=\"column\" marginLeft={2}>\n {lines.map(renderLine)}\n </Box>\n );\n});\n\nconst Expanded = memo(function Expanded({ item }: ItemProps) {\n const { lines } = item;\n\n return (\n <Box flexDirection=\"column\">\n <Header group={item.group} title={item.title} state={item.state} />\n <Lines lines={lines} />\n </Box>\n );\n});\n\nconst Contracted = memo(function Contracted({ item }: ItemProps) {\n const { state, lines } = item;\n\n // remove ansi codes when displaying single lines\n const errors = useMemo(() => lines.filter((line) => line.type === LineType.stderr), [lines]);\n const summary = useMemo(() => lines.filter((line) => line.text.length > 0 && errors.indexOf(line) < 0).pop(), [lines, errors]);\n\n return (\n <Box flexDirection=\"column\">\n <Header group={item.group} title={item.title} state={item.state} />\n {state === 'running' && <RunningSummary line={summary || BLANK_LINE} />}\n {errors.length > 0 && <Lines lines={errors} />}\n </Box>\n );\n});\n\nexport default memo(function ChildProcess({ item }: ItemProps) {\n const { expanded } = item;\n return expanded ? <Expanded item={item} /> : <Contracted item={item} />;\n});\n"],"names":["Box","Text","memo","useMemo","ansiRegex","figures","LineType","Spinner","REGEX_ANSI","BLANK_LINE","type","stdout","text","SPINNER","interval","frames","ICONS","error","color","cross","success","tick","running","Header","group","title","state","icon","bold","pointer","a","b","RunningSummary","line","marginLeft","replace","renderLine","index","minHeight","Lines","lines","flexDirection","map","Expanded","item","Contracted","errors","filter","stderr","summary","length","indexOf","pop","ChildProcess","expanded"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ChildProcess.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo, useMemo } from 'react';\nimport { SPINNER } from '../constants.ts';\nimport ansiRegex from '../lib/ansiRegex.ts';\nimport figures from '../lib/figures.ts';\nimport type { ChildProcess as ChildProcessT, Line, State } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport Spinner from './Spinner.ts';\n\nconst REGEX_ANSI = ansiRegex();\nconst BLANK_LINE = { type: LineType.stdout, text: '' };\n\nconst ICONS = {\n error: <Text color=\"red\">{figures.cross}</Text>,\n success: <Text color=\"green\">{figures.tick}</Text>,\n running: <Spinner {...SPINNER} />,\n};\n\ntype ItemProps = {\n item: ChildProcessT;\n};\n\ntype HeaderProps = {\n group?: string;\n title: string;\n state: State;\n};\n\nconst Header = memo(\n function Header({ group, title, state }: HeaderProps) {\n const icon = ICONS[state];\n\n return (\n <Box>\n {icon}\n {group && <Text bold>{`${group}${figures.pointer} `}</Text>}\n <Text>{title}</Text>\n </Box>\n );\n },\n (a, b) => a.group === b.group && a.title === b.title && a.state === b.state\n);\n\ntype RunningSummaryProps = {\n line: Line;\n};\n\nconst RunningSummary = memo(function RunningSummary({ line }: RunningSummaryProps) {\n return (\n <Box marginLeft={2}>\n <Text color=\"gray\">{line.text.replace(REGEX_ANSI, '')}</Text>\n </Box>\n );\n});\n\ntype LinesProps = {\n lines: Line[];\n};\n\nconst renderLine = (line, index) => {\n return (\n <Box key={index} minHeight={1}>\n <Text>{line.text}</Text>\n </Box>\n );\n};\n\nconst Lines = memo(function Lines({ lines }: LinesProps) {\n return (\n <Box flexDirection=\"column\" marginLeft={2}>\n {lines.map(renderLine)}\n </Box>\n );\n});\n\nconst Expanded = memo(function Expanded({ item }: ItemProps) {\n const { lines } = item;\n\n return (\n <Box flexDirection=\"column\">\n <Header group={item.group} title={item.title} state={item.state} />\n <Lines lines={lines} />\n </Box>\n );\n});\n\nconst Contracted = memo(function Contracted({ item }: ItemProps) {\n const { state, lines } = item;\n\n // remove ansi codes when displaying single lines\n const errors = useMemo(() => lines.filter((line) => line.type === LineType.stderr), [lines]);\n const summary = useMemo(() => lines.filter((line) => line.text.length > 0 && errors.indexOf(line) < 0).pop(), [lines, errors]);\n\n return (\n <Box flexDirection=\"column\">\n <Header group={item.group} title={item.title} state={item.state} />\n {state === 'running' && <RunningSummary line={summary || BLANK_LINE} />}\n {errors.length > 0 && <Lines lines={errors} />}\n </Box>\n );\n});\n\nexport default memo(function ChildProcess({ item }: ItemProps) {\n const { expanded } = item;\n return expanded ? <Expanded item={item} /> : <Contracted item={item} />;\n});\n"],"names":["Box","Text","memo","useMemo","SPINNER","ansiRegex","figures","LineType","Spinner","REGEX_ANSI","BLANK_LINE","type","stdout","text","ICONS","error","color","cross","success","tick","running","Header","group","title","state","icon","bold","pointer","a","b","RunningSummary","line","marginLeft","replace","renderLine","index","minHeight","Lines","lines","flexDirection","map","Expanded","item","Contracted","errors","filter","stderr","summary","length","indexOf","pop","ChildProcess","expanded"],"mappings":"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"}
@@ -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":"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"}
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,43 @@
1
+ export function createSession(options) {
2
+ let realSession = null;
3
+ let loadError = null;
4
+ // Start loading immediately
5
+ import('./session.js').then((mod)=>{
6
+ realSession = mod.createSession(options);
7
+ }).catch((err)=>{
8
+ loadError = err;
9
+ });
10
+ return {
11
+ spawn (command, args, spawnOptions, processOptions, callback) {
12
+ if (loadError) {
13
+ callback(loadError);
14
+ return;
15
+ }
16
+ if (realSession) {
17
+ realSession.spawn(command, args, spawnOptions, processOptions, callback);
18
+ return;
19
+ }
20
+ // Still loading, wait for it
21
+ import('./session.js').then((mod)=>{
22
+ if (!realSession) realSession = mod.createSession(options);
23
+ realSession.spawn(command, args, spawnOptions, processOptions, callback);
24
+ }).catch(callback);
25
+ },
26
+ close () {
27
+ if (realSession) realSession.close();
28
+ },
29
+ waitAndClose (callback) {
30
+ if (realSession) {
31
+ realSession.waitAndClose(callback);
32
+ return;
33
+ }
34
+ // Still loading, wait for it
35
+ import('./session.js').then((mod)=>{
36
+ if (!realSession) realSession = mod.createSession(options);
37
+ realSession.waitAndClose(callback);
38
+ }).catch(()=>{
39
+ callback === null || callback === void 0 ? void 0 : callback();
40
+ });
41
+ }
42
+ };
43
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/createSessionWrapper.ts"],"sourcesContent":["import type { ProcessOptions, SessionOptions, SpawnError, SpawnOptions, TerminalCallback } 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\nexport function createSession(options?: SessionOptions): Session {\n let realSession: Session | null = null;\n let loadError: SpawnError | null = null;\n\n // Start loading immediately\n import('./session.ts')\n .then((mod) => {\n realSession = mod.createSession(options);\n })\n .catch((err) => {\n loadError = err;\n });\n\n return {\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, processOptions: ProcessOptions, callback: TerminalCallback): void {\n if (loadError) {\n callback(loadError);\n return;\n }\n if (realSession) {\n realSession.spawn(command, args, spawnOptions, processOptions, callback);\n return;\n }\n // Still loading, wait for it\n import('./session.ts')\n .then((mod) => {\n if (!realSession) realSession = mod.createSession(options);\n realSession.spawn(command, args, spawnOptions, processOptions, callback);\n })\n .catch(callback);\n },\n close(): void {\n if (realSession) realSession.close();\n },\n waitAndClose(callback?: () => void): void {\n if (realSession) {\n realSession.waitAndClose(callback);\n return;\n }\n // Still loading, wait for it\n import('./session.ts')\n .then((mod) => {\n if (!realSession) realSession = mod.createSession(options);\n realSession.waitAndClose(callback);\n })\n .catch(() => {\n callback?.();\n });\n },\n };\n}\n"],"names":["createSession","options","realSession","loadError","then","mod","catch","err","spawn","command","args","spawnOptions","processOptions","callback","close","waitAndClose"],"mappings":"AAQA,OAAO,SAASA,cAAcC,OAAwB;IACpD,IAAIC,cAA8B;IAClC,IAAIC,YAA+B;IAEnC,4BAA4B;IAC5B,MAAM,CAAC,gBACJC,IAAI,CAAC,CAACC;QACLH,cAAcG,IAAIL,aAAa,CAACC;IAClC,GACCK,KAAK,CAAC,CAACC;QACNJ,YAAYI;IACd;IAEF,OAAO;QACLC,OAAMC,OAAe,EAAEC,IAAc,EAAEC,YAA0B,EAAEC,cAA8B,EAAEC,QAA0B;YAC3H,IAAIV,WAAW;gBACbU,SAASV;gBACT;YACF;YACA,IAAID,aAAa;gBACfA,YAAYM,KAAK,CAACC,SAASC,MAAMC,cAAcC,gBAAgBC;gBAC/D;YACF;YACA,6BAA6B;YAC7B,MAAM,CAAC,gBACJT,IAAI,CAAC,CAACC;gBACL,IAAI,CAACH,aAAaA,cAAcG,IAAIL,aAAa,CAACC;gBAClDC,YAAYM,KAAK,CAACC,SAASC,MAAMC,cAAcC,gBAAgBC;YACjE,GACCP,KAAK,CAACO;QACX;QACAC;YACE,IAAIZ,aAAaA,YAAYY,KAAK;QACpC;QACAC,cAAaF,QAAqB;YAChC,IAAIX,aAAa;gBACfA,YAAYa,YAAY,CAACF;gBACzB;YACF;YACA,6BAA6B;YAC7B,MAAM,CAAC,gBACJT,IAAI,CAAC,CAACC;gBACL,IAAI,CAACH,aAAaA,cAAcG,IAAIL,aAAa,CAACC;gBAClDC,YAAYa,YAAY,CAACF;YAC3B,GACCP,KAAK,CAAC;gBACLO,qBAAAA,+BAAAA;YACF;QACJ;IACF;AACF"}
@@ -2,5 +2,5 @@ export { default as figures } from './lib/figures.js';
2
2
  export { default as formatArguments } from './lib/formatArguments.js';
3
3
  export * from './types.js';
4
4
  const major = +process.versions.node.split('.')[0];
5
- import { createSession as createSessionImpl } from './session.js';
5
+ import { createSession as createSessionImpl } from './createSessionWrapper.js';
6
6
  export const createSession = major > 18 ? createSessionImpl : undefined;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/index-esm.ts"],"sourcesContent":["export { default as figures } from './lib/figures.ts';\nexport { default as formatArguments } from './lib/formatArguments.ts';\nexport * from './types.ts';\n\nconst major = +process.versions.node.split('.')[0];\n\nimport { createSession as createSessionImpl, type Session } from './session.ts';\nexport type { Session };\nexport const createSession = major > 18 ? createSessionImpl : (undefined as typeof createSessionImpl);\n"],"names":["default","figures","formatArguments","major","process","versions","node","split","createSession","createSessionImpl","undefined"],"mappings":"AAAA,SAASA,WAAWC,OAAO,QAAQ,mBAAmB;AACtD,SAASD,WAAWE,eAAe,QAAQ,2BAA2B;AACtE,cAAc,aAAa;AAE3B,MAAMC,QAAQ,CAACC,QAAQC,QAAQ,CAACC,IAAI,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE;AAElD,SAASC,iBAAiBC,iBAAiB,QAAsB,eAAe;AAEhF,OAAO,MAAMD,gBAAgBL,QAAQ,KAAKM,oBAAqBC,UAAuC"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/index-esm.ts"],"sourcesContent":["export { default as figures } from './lib/figures.ts';\nexport { default as formatArguments } from './lib/formatArguments.ts';\nexport * from './types.ts';\n\nimport type { createSession as createSessionType, Session } from './createSessionWrapper.ts';\nexport type { Session };\n\nconst major = +process.versions.node.split('.')[0];\n\nimport { createSession as createSessionImpl } from './createSessionWrapper.ts';\nexport const createSession = major > 18 ? createSessionImpl : (undefined as typeof createSessionType);\n"],"names":["default","figures","formatArguments","major","process","versions","node","split","createSession","createSessionImpl","undefined"],"mappings":"AAAA,SAASA,WAAWC,OAAO,QAAQ,mBAAmB;AACtD,SAASD,WAAWE,eAAe,QAAQ,2BAA2B;AACtE,cAAc,aAAa;AAK3B,MAAMC,QAAQ,CAACC,QAAQC,QAAQ,CAACC,IAAI,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE;AAElD,SAASC,iBAAiBC,iBAAiB,QAAQ,4BAA4B;AAC/E,OAAO,MAAMD,gBAAgBL,QAAQ,KAAKM,oBAAqBC,UAAuC"}
@@ -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"}
@@ -166,14 +166,34 @@ class SessionImpl {
166
166
  }
167
167
  if (callback) this.waitCallbacks.push(callback);
168
168
  if (this.runningCount === 0) {
169
- this.closeAndCallWaitCallbacks();
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
+ }
170
180
  }
171
181
  // If runningCount > 0, will close when it hits 0 in onProcessComplete
172
182
  }
173
183
  onProcessComplete() {
174
184
  this.runningCount--;
175
185
  if (this.runningCount === 0 && this.waitCallbacks.length > 0) {
176
- this.closeAndCallWaitCallbacks();
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
+ }
177
197
  }
178
198
  }
179
199
  closeAndCallWaitCallbacks() {
@@ -212,6 +232,8 @@ class SessionImpl {
212
232
  this.closed = false;
213
233
  this.waitCallbacks = [];
214
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;
215
237
  // Render Ink app immediately
216
238
  this.inkApp = render(/*#__PURE__*/ _jsx(App, {
217
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 (callback) this.waitCallbacks.push(callback);\n\n if (this.runningCount === 0) {\n this.closeAndCallWaitCallbacks();\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 this.closeAndCallWaitCallbacks();\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","closeAndCallWaitCallbacks","length","cb","onComplete","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,7 @@
1
+ import type { ProcessOptions, SessionOptions, SpawnOptions, TerminalCallback } from './types.js';
2
+ export interface Session {
3
+ spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void;
4
+ close(): void;
5
+ waitAndClose(callback?: () => void): void;
6
+ }
7
+ export declare function createSession(options?: SessionOptions): Session;
@@ -1,6 +1,6 @@
1
1
  export { default as figures } from './lib/figures.js';
2
2
  export { default as formatArguments } from './lib/formatArguments.js';
3
3
  export * from './types.js';
4
- import { createSession as createSessionImpl, type Session } from './session.js';
4
+ import type { createSession as createSessionType, Session } from './createSessionWrapper.js';
5
5
  export type { Session };
6
- export declare const createSession: typeof createSessionImpl;
6
+ export declare const createSession: typeof createSessionType;
@@ -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;