spawn-term 3.0.1 → 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 +26 -2
  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 +24 -2
  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
@@ -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"}
@@ -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,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.1",
3
+ "version": "3.0.2",
4
4
  "description": "Formats spawn with for terminal grouping",
5
5
  "keywords": [
6
6
  "spawn",