spawn-term 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/App.js +79 -16
- package/dist/cjs/components/App.js.map +1 -1
- package/dist/cjs/components/CompactProcessLine.js +12 -3
- package/dist/cjs/components/CompactProcessLine.js.map +1 -1
- package/dist/cjs/components/ExpandedOutput.js +55 -0
- package/dist/cjs/components/ExpandedOutput.js.map +1 -0
- package/dist/cjs/constants.js +42 -0
- package/dist/cjs/constants.js.map +1 -0
- package/dist/cjs/createApp.js +11 -11
- package/dist/cjs/createApp.js.map +1 -1
- package/dist/cjs/lib/addLines.js +58 -4
- package/dist/cjs/lib/addLines.js.map +1 -1
- package/dist/cjs/lib/format.js +21 -0
- package/dist/cjs/lib/format.js.map +1 -0
- package/dist/cjs/src/components/CompactProcessLine.d.ts +1 -0
- package/dist/cjs/src/components/ExpandedOutput.d.ts +8 -0
- package/dist/cjs/src/constants.d.ts +7 -0
- package/dist/cjs/src/lib/addLines.d.ts +6 -1
- package/dist/cjs/src/lib/format.d.ts +2 -0
- package/dist/cjs/src/state/processStore.d.ts +20 -1
- package/dist/cjs/src/types.d.ts +6 -0
- package/dist/cjs/state/processStore.js +105 -1
- package/dist/cjs/state/processStore.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/components/App.js +79 -14
- package/dist/esm/components/App.js.map +1 -1
- package/dist/esm/components/CompactProcessLine.js +12 -3
- package/dist/esm/components/CompactProcessLine.js.map +1 -1
- package/dist/esm/components/ExpandedOutput.js +41 -0
- package/dist/esm/components/ExpandedOutput.js.map +1 -0
- package/dist/esm/constants.js +11 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/createApp.js +11 -11
- package/dist/esm/createApp.js.map +1 -1
- package/dist/esm/lib/addLines.js +32 -5
- package/dist/esm/lib/addLines.js.map +1 -1
- package/dist/esm/lib/format.js +10 -0
- package/dist/esm/lib/format.js.map +1 -0
- package/dist/esm/src/components/CompactProcessLine.d.ts +1 -0
- package/dist/esm/src/components/ExpandedOutput.d.ts +8 -0
- package/dist/esm/src/constants.d.ts +7 -0
- package/dist/esm/src/lib/addLines.d.ts +6 -1
- package/dist/esm/src/lib/format.d.ts +2 -0
- package/dist/esm/src/state/processStore.d.ts +20 -1
- package/dist/esm/src/types.d.ts +6 -0
- package/dist/esm/state/processStore.js +82 -1
- package/dist/esm/state/processStore.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Column width defaults
|
|
2
|
+
export const DEFAULT_COLUMN_WIDTH = 15;
|
|
3
|
+
export const MAX_COLUMN_WIDTH_PERCENT = 0.4; // 40% of terminal width
|
|
4
|
+
export const FALLBACK_COLUMN_WIDTH = 25;
|
|
5
|
+
// Batching defaults
|
|
6
|
+
export const BATCH_MAX_LINES = 20;
|
|
7
|
+
export const BATCH_MAX_WAIT_MS = 50;
|
|
8
|
+
// Rendering
|
|
9
|
+
export const DEFAULT_MAX_FPS = 20;
|
|
10
|
+
// Expansion
|
|
11
|
+
export const EXPANDED_MAX_VISIBLE_LINES = 10;
|
|
@@ -0,0 +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"}
|
package/dist/esm/createApp.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { render } from 'ink';
|
|
3
3
|
import App from './components/App.js';
|
|
4
|
+
import { DEFAULT_MAX_FPS } from './constants.js';
|
|
4
5
|
import { processStore } from './state/processStore.js';
|
|
5
6
|
export default function createApp() {
|
|
6
7
|
let refCount = 0;
|
|
@@ -9,7 +10,11 @@ export default function createApp() {
|
|
|
9
10
|
retain () {
|
|
10
11
|
if (++refCount > 1) return processStore;
|
|
11
12
|
// Render once - React handles all subsequent updates via useSyncExternalStore
|
|
12
|
-
|
|
13
|
+
// Enable incremental rendering to only rewrite changed lines (reduces flicker)
|
|
14
|
+
inkApp = render(/*#__PURE__*/ _jsx(App, {}), {
|
|
15
|
+
incrementalRendering: true,
|
|
16
|
+
maxFps: DEFAULT_MAX_FPS
|
|
17
|
+
});
|
|
13
18
|
return processStore;
|
|
14
19
|
},
|
|
15
20
|
release (callback) {
|
|
@@ -18,16 +23,11 @@ export default function createApp() {
|
|
|
18
23
|
return;
|
|
19
24
|
}
|
|
20
25
|
if (!inkApp) throw new Error('Expecting inkApp');
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
processStore.signalExit(()=>{
|
|
27
|
-
processStore.reset();
|
|
28
|
-
process.stdout.write('\x1b[?25h'); // show cursor
|
|
29
|
-
callback();
|
|
30
|
-
});
|
|
26
|
+
// Signal exit to React component
|
|
27
|
+
processStore.signalExit(()=>{
|
|
28
|
+
processStore.reset();
|
|
29
|
+
process.stdout.write('\x1b[?25h'); // show cursor
|
|
30
|
+
callback();
|
|
31
31
|
});
|
|
32
32
|
// Wait for Ink to finish, then call the callback
|
|
33
33
|
inkApp.waitUntilExit().then(()=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/createApp.tsx"],"sourcesContent":["import { render } from 'ink';\nimport App from './components/App.ts';\nimport { type ProcessStore, processStore } from './state/processStore.ts';\n\nexport type ReleaseCallback = () => void;\n\nexport default function createApp() {\n let refCount = 0;\n let inkApp: ReturnType<typeof render> | null = null;\n\n return {\n retain(): ProcessStore {\n if (++refCount > 1) return processStore;\n\n // Render once - React handles all subsequent updates via useSyncExternalStore\n inkApp = render(<App
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/createApp.tsx"],"sourcesContent":["import { render } from 'ink';\nimport App from './components/App.ts';\nimport { DEFAULT_MAX_FPS } from './constants.ts';\nimport { type ProcessStore, processStore } from './state/processStore.ts';\n\nexport type ReleaseCallback = () => void;\n\nexport default function createApp() {\n let refCount = 0;\n let inkApp: ReturnType<typeof render> | null = null;\n\n return {\n retain(): ProcessStore {\n if (++refCount > 1) return processStore;\n\n // Render once - React handles all subsequent updates via useSyncExternalStore\n // Enable incremental rendering to only rewrite changed lines (reduces flicker)\n inkApp = render(<App />, {\n incrementalRendering: true,\n maxFps: DEFAULT_MAX_FPS,\n });\n return processStore;\n },\n\n release(callback: ReleaseCallback): void {\n if (--refCount > 0) {\n callback();\n return;\n }\n if (!inkApp) throw new Error('Expecting inkApp');\n\n // Signal exit to React component\n processStore.signalExit(() => {\n processStore.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n callback();\n });\n\n // Wait for Ink to finish, then call the callback\n inkApp\n .waitUntilExit()\n .then(() => {\n const cb = processStore.getExitCallback();\n cb?.();\n })\n .catch(() => {\n const cb = processStore.getExitCallback();\n cb?.();\n });\n\n inkApp = null;\n },\n };\n}\n"],"names":["render","App","DEFAULT_MAX_FPS","processStore","createApp","refCount","inkApp","retain","incrementalRendering","maxFps","release","callback","Error","signalExit","reset","process","stdout","write","waitUntilExit","then","cb","getExitCallback","catch"],"mappings":";AAAA,SAASA,MAAM,QAAQ,MAAM;AAC7B,OAAOC,SAAS,sBAAsB;AACtC,SAASC,eAAe,QAAQ,iBAAiB;AACjD,SAA4BC,YAAY,QAAQ,0BAA0B;AAI1E,eAAe,SAASC;IACtB,IAAIC,WAAW;IACf,IAAIC,SAA2C;IAE/C,OAAO;QACLC;YACE,IAAI,EAAEF,WAAW,GAAG,OAAOF;YAE3B,8EAA8E;YAC9E,+EAA+E;YAC/EG,SAASN,qBAAO,KAACC,UAAQ;gBACvBO,sBAAsB;gBACtBC,QAAQP;YACV;YACA,OAAOC;QACT;QAEAO,SAAQC,QAAyB;YAC/B,IAAI,EAAEN,WAAW,GAAG;gBAClBM;gBACA;YACF;YACA,IAAI,CAACL,QAAQ,MAAM,IAAIM,MAAM;YAE7B,iCAAiC;YACjCT,aAAaU,UAAU,CAAC;gBACtBV,aAAaW,KAAK;gBAClBC,QAAQC,MAAM,CAACC,KAAK,CAAC,cAAc,cAAc;gBACjDN;YACF;YAEA,iDAAiD;YACjDL,OACGY,aAAa,GACbC,IAAI,CAAC;gBACJ,MAAMC,KAAKjB,aAAakB,eAAe;gBACvCD,eAAAA,yBAAAA;YACF,GACCE,KAAK,CAAC;gBACL,MAAMF,KAAKjB,aAAakB,eAAe;gBACvCD,eAAAA,yBAAAA;YACF;YAEFd,SAAS;QACX;IACF;AACF"}
|
package/dist/esm/lib/addLines.js
CHANGED
|
@@ -1,20 +1,47 @@
|
|
|
1
1
|
import { Writable } from 'stream';
|
|
2
|
+
import { BATCH_MAX_LINES, BATCH_MAX_WAIT_MS } from '../constants.js';
|
|
2
3
|
const REGEX_NEW_LINE = /\r?\n|\r/g;
|
|
3
|
-
export default function addLines(fn) {
|
|
4
|
+
export default function addLines(fn, options = {}) {
|
|
5
|
+
const { maxLines = BATCH_MAX_LINES, maxWait = BATCH_MAX_WAIT_MS } = options;
|
|
4
6
|
let last = '';
|
|
7
|
+
let lineBuffer = [];
|
|
8
|
+
let flushTimer = null;
|
|
9
|
+
const flush = ()=>{
|
|
10
|
+
if (flushTimer) {
|
|
11
|
+
clearTimeout(flushTimer);
|
|
12
|
+
flushTimer = null;
|
|
13
|
+
}
|
|
14
|
+
if (lineBuffer.length > 0) {
|
|
15
|
+
fn(lineBuffer);
|
|
16
|
+
lineBuffer = [];
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const scheduleFlush = ()=>{
|
|
20
|
+
if (!flushTimer) {
|
|
21
|
+
flushTimer = setTimeout(flush, maxWait);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
5
24
|
const stream = new Writable({
|
|
6
25
|
write (chunk, _enc, callback) {
|
|
7
26
|
const more = last + chunk.toString('utf8');
|
|
8
27
|
const lines = more.split(REGEX_NEW_LINE);
|
|
9
28
|
last = lines.pop();
|
|
10
|
-
if (lines.length > 0)
|
|
29
|
+
if (lines.length > 0) {
|
|
30
|
+
lineBuffer.push(...lines);
|
|
31
|
+
// Flush immediately if buffer is large enough
|
|
32
|
+
if (lineBuffer.length >= maxLines) {
|
|
33
|
+
flush();
|
|
34
|
+
} else {
|
|
35
|
+
scheduleFlush();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
11
38
|
callback();
|
|
12
39
|
}
|
|
13
40
|
});
|
|
14
41
|
stream.on('finish', ()=>{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
42
|
+
// Flush any remaining buffered lines
|
|
43
|
+
if (last.length) lineBuffer.push(last);
|
|
44
|
+
flush();
|
|
18
45
|
last = '';
|
|
19
46
|
});
|
|
20
47
|
return stream;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/lib/addLines.ts"],"sourcesContent":["import { Writable } from 'stream';\n\nconst REGEX_NEW_LINE = /\\r?\\n|\\r/g;\n\nexport type Callback = (lines: string[]) => undefined;\n\nexport default function addLines(fn: Callback): Writable {\n let last = '';\n\n const stream = new Writable({\n write(chunk, _enc, callback) {\n const more = last + chunk.toString('utf8');\n const lines = more.split(REGEX_NEW_LINE);\n last = lines.pop();\n if (lines.length > 0)
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/lib/addLines.ts"],"sourcesContent":["import { Writable } from 'stream';\nimport { BATCH_MAX_LINES, BATCH_MAX_WAIT_MS } from '../constants.ts';\n\nconst REGEX_NEW_LINE = /\\r?\\n|\\r/g;\n\nexport type Callback = (lines: string[]) => undefined;\n\ninterface BatchOptions {\n maxLines?: number;\n maxWait?: number;\n}\n\nexport default function addLines(fn: Callback, options: BatchOptions = {}): Writable {\n const { maxLines = BATCH_MAX_LINES, maxWait = BATCH_MAX_WAIT_MS } = options;\n\n let last = '';\n let lineBuffer: string[] = [];\n let flushTimer: NodeJS.Timeout | null = null;\n\n const flush = () => {\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n if (lineBuffer.length > 0) {\n fn(lineBuffer);\n lineBuffer = [];\n }\n };\n\n const scheduleFlush = () => {\n if (!flushTimer) {\n flushTimer = setTimeout(flush, maxWait);\n }\n };\n\n const stream = new Writable({\n write(chunk, _enc, callback) {\n const more = last + chunk.toString('utf8');\n const lines = more.split(REGEX_NEW_LINE);\n last = lines.pop();\n\n if (lines.length > 0) {\n lineBuffer.push(...lines);\n\n // Flush immediately if buffer is large enough\n if (lineBuffer.length >= maxLines) {\n flush();\n } else {\n scheduleFlush();\n }\n }\n callback();\n },\n });\n\n stream.on('finish', () => {\n // Flush any remaining buffered lines\n if (last.length) lineBuffer.push(last);\n flush();\n last = '';\n });\n\n return stream;\n}\n"],"names":["Writable","BATCH_MAX_LINES","BATCH_MAX_WAIT_MS","REGEX_NEW_LINE","addLines","fn","options","maxLines","maxWait","last","lineBuffer","flushTimer","flush","clearTimeout","length","scheduleFlush","setTimeout","stream","write","chunk","_enc","callback","more","toString","lines","split","pop","push","on"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,SAAS;AAClC,SAASC,eAAe,EAAEC,iBAAiB,QAAQ,kBAAkB;AAErE,MAAMC,iBAAiB;AASvB,eAAe,SAASC,SAASC,EAAY,EAAEC,UAAwB,CAAC,CAAC;IACvE,MAAM,EAAEC,WAAWN,eAAe,EAAEO,UAAUN,iBAAiB,EAAE,GAAGI;IAEpE,IAAIG,OAAO;IACX,IAAIC,aAAuB,EAAE;IAC7B,IAAIC,aAAoC;IAExC,MAAMC,QAAQ;QACZ,IAAID,YAAY;YACdE,aAAaF;YACbA,aAAa;QACf;QACA,IAAID,WAAWI,MAAM,GAAG,GAAG;YACzBT,GAAGK;YACHA,aAAa,EAAE;QACjB;IACF;IAEA,MAAMK,gBAAgB;QACpB,IAAI,CAACJ,YAAY;YACfA,aAAaK,WAAWJ,OAAOJ;QACjC;IACF;IAEA,MAAMS,SAAS,IAAIjB,SAAS;QAC1BkB,OAAMC,KAAK,EAAEC,IAAI,EAAEC,QAAQ;YACzB,MAAMC,OAAOb,OAAOU,MAAMI,QAAQ,CAAC;YACnC,MAAMC,QAAQF,KAAKG,KAAK,CAACtB;YACzBM,OAAOe,MAAME,GAAG;YAEhB,IAAIF,MAAMV,MAAM,GAAG,GAAG;gBACpBJ,WAAWiB,IAAI,IAAIH;gBAEnB,8CAA8C;gBAC9C,IAAId,WAAWI,MAAM,IAAIP,UAAU;oBACjCK;gBACF,OAAO;oBACLG;gBACF;YACF;YACAM;QACF;IACF;IAEAJ,OAAOW,EAAE,CAAC,UAAU;QAClB,qCAAqC;QACrC,IAAInB,KAAKK,MAAM,EAAEJ,WAAWiB,IAAI,CAAClB;QACjCG;QACAH,OAAO;IACT;IAEA,OAAOQ;AACT"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FALLBACK_COLUMN_WIDTH, MAX_COLUMN_WIDTH_PERCENT } from '../constants.js';
|
|
2
|
+
export function calculateColumnWidth(groupWidth, terminalWidth, maxGroupLength) {
|
|
3
|
+
if (typeof groupWidth === 'number') return groupWidth;
|
|
4
|
+
if (groupWidth === 'max') return Math.min(maxGroupLength, Math.floor(terminalWidth * MAX_COLUMN_WIDTH_PERCENT));
|
|
5
|
+
if (typeof groupWidth === 'string' && groupWidth.endsWith('%')) {
|
|
6
|
+
const pct = parseInt(groupWidth, 10) / 100;
|
|
7
|
+
return Math.floor(terminalWidth * pct);
|
|
8
|
+
}
|
|
9
|
+
return FALLBACK_COLUMN_WIDTH;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/lib/format.ts"],"sourcesContent":["import { FALLBACK_COLUMN_WIDTH, MAX_COLUMN_WIDTH_PERCENT } from '../constants.ts';\n\nexport type GroupWidth = number | `${number}%` | 'max';\n\nexport function calculateColumnWidth(groupWidth: GroupWidth, terminalWidth: number, maxGroupLength: number): number {\n if (typeof groupWidth === 'number') return groupWidth;\n if (groupWidth === 'max') return Math.min(maxGroupLength, Math.floor(terminalWidth * MAX_COLUMN_WIDTH_PERCENT));\n if (typeof groupWidth === 'string' && groupWidth.endsWith('%')) {\n const pct = parseInt(groupWidth, 10) / 100;\n return Math.floor(terminalWidth * pct);\n }\n return FALLBACK_COLUMN_WIDTH;\n}\n"],"names":["FALLBACK_COLUMN_WIDTH","MAX_COLUMN_WIDTH_PERCENT","calculateColumnWidth","groupWidth","terminalWidth","maxGroupLength","Math","min","floor","endsWith","pct","parseInt"],"mappings":"AAAA,SAASA,qBAAqB,EAAEC,wBAAwB,QAAQ,kBAAkB;AAIlF,OAAO,SAASC,qBAAqBC,UAAsB,EAAEC,aAAqB,EAAEC,cAAsB;IACxG,IAAI,OAAOF,eAAe,UAAU,OAAOA;IAC3C,IAAIA,eAAe,OAAO,OAAOG,KAAKC,GAAG,CAACF,gBAAgBC,KAAKE,KAAK,CAACJ,gBAAgBH;IACrF,IAAI,OAAOE,eAAe,YAAYA,WAAWM,QAAQ,CAAC,MAAM;QAC9D,MAAMC,MAAMC,SAASR,YAAY,MAAM;QACvC,OAAOG,KAAKE,KAAK,CAACJ,gBAAgBM;IACpC;IACA,OAAOV;AACT"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const DEFAULT_COLUMN_WIDTH = 15;
|
|
2
|
+
export declare const MAX_COLUMN_WIDTH_PERCENT = 0.4;
|
|
3
|
+
export declare const FALLBACK_COLUMN_WIDTH = 25;
|
|
4
|
+
export declare const BATCH_MAX_LINES = 20;
|
|
5
|
+
export declare const BATCH_MAX_WAIT_MS = 50;
|
|
6
|
+
export declare const DEFAULT_MAX_FPS = 20;
|
|
7
|
+
export declare const EXPANDED_MAX_VISIBLE_LINES = 10;
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import { Writable } from 'stream';
|
|
2
2
|
export type Callback = (lines: string[]) => undefined;
|
|
3
|
-
|
|
3
|
+
interface BatchOptions {
|
|
4
|
+
maxLines?: number;
|
|
5
|
+
maxWait?: number;
|
|
6
|
+
}
|
|
7
|
+
export default function addLines(fn: Callback, options?: BatchOptions): Writable;
|
|
8
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ChildProcess, Line } from '../types.js';
|
|
2
2
|
type Listener = () => void;
|
|
3
|
-
type Mode = 'normal' | 'errorList' | 'errorDetail';
|
|
3
|
+
type Mode = 'normal' | 'interactive' | 'errorList' | 'errorDetail';
|
|
4
4
|
declare class ProcessStore {
|
|
5
5
|
private processes;
|
|
6
6
|
private completedIds;
|
|
@@ -8,26 +8,45 @@ declare class ProcessStore {
|
|
|
8
8
|
private shouldExit;
|
|
9
9
|
private exitCallback;
|
|
10
10
|
private mode;
|
|
11
|
+
private selectedIndex;
|
|
11
12
|
private selectedErrorIndex;
|
|
13
|
+
private expandedId;
|
|
14
|
+
private scrollOffset;
|
|
15
|
+
private header;
|
|
12
16
|
subscribe: (listener: Listener) => (() => void);
|
|
13
17
|
getSnapshot: () => ChildProcess[];
|
|
14
18
|
getRunningProcesses: () => ChildProcess[];
|
|
15
19
|
getCompletedProcesses: () => ChildProcess[];
|
|
16
20
|
getFailedProcesses: () => ChildProcess[];
|
|
17
21
|
getRunningCount: () => number;
|
|
22
|
+
getMaxGroupLength: () => number;
|
|
18
23
|
getDoneCount: () => number;
|
|
19
24
|
getErrorCount: () => number;
|
|
20
25
|
getErrorLineCount: () => number;
|
|
21
26
|
getMode: () => Mode;
|
|
27
|
+
getSelectedIndex: () => number;
|
|
22
28
|
getSelectedErrorIndex: () => number;
|
|
29
|
+
getExpandedId: () => string | null;
|
|
30
|
+
getScrollOffset: () => number;
|
|
31
|
+
getHeader: () => string | undefined;
|
|
32
|
+
getShowStatusBar: () => boolean;
|
|
33
|
+
getIsInteractive: () => boolean;
|
|
34
|
+
isAllComplete: () => boolean;
|
|
23
35
|
addProcess(process: ChildProcess): void;
|
|
24
36
|
updateProcess(id: string, update: Partial<ChildProcess>): void;
|
|
25
37
|
appendLines(id: string, newLines: Line[]): void;
|
|
26
38
|
getProcess(id: string): ChildProcess | undefined;
|
|
27
39
|
setMode(mode: Mode): void;
|
|
40
|
+
selectNext(): void;
|
|
41
|
+
selectPrev(): void;
|
|
42
|
+
getSelectedProcess(): ChildProcess | undefined;
|
|
28
43
|
selectNextError(): void;
|
|
29
44
|
selectPrevError(): void;
|
|
30
45
|
getSelectedError(): ChildProcess | undefined;
|
|
46
|
+
toggleExpand(): void;
|
|
47
|
+
collapse(): void;
|
|
48
|
+
scrollDown(maxVisible: number): void;
|
|
49
|
+
scrollUp(): void;
|
|
31
50
|
signalExit(callback: () => void): void;
|
|
32
51
|
getShouldExit: () => boolean;
|
|
33
52
|
getExitCallback: () => (() => void) | null;
|
package/dist/esm/src/types.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ import type { SpawnError, SpawnResult } from 'cross-spawn-cb';
|
|
|
3
3
|
export type TerminalOptions = {
|
|
4
4
|
group?: string;
|
|
5
5
|
expanded?: boolean;
|
|
6
|
+
header?: string;
|
|
7
|
+
showStatusBar?: boolean;
|
|
8
|
+
interactive?: boolean;
|
|
6
9
|
};
|
|
7
10
|
export type TerminalCallback = (error?: SpawnError, result?: SpawnResult) => undefined;
|
|
8
11
|
export declare const LineType: {
|
|
@@ -21,4 +24,7 @@ export type ChildProcess = {
|
|
|
21
24
|
state: State;
|
|
22
25
|
lines: Line[];
|
|
23
26
|
expanded?: boolean;
|
|
27
|
+
header?: string;
|
|
28
|
+
showStatusBar?: boolean;
|
|
29
|
+
interactive?: boolean;
|
|
24
30
|
};
|
|
@@ -26,10 +26,15 @@ function _object_spread(target) {
|
|
|
26
26
|
}
|
|
27
27
|
return target;
|
|
28
28
|
}
|
|
29
|
+
import { DEFAULT_COLUMN_WIDTH } from '../constants.js';
|
|
29
30
|
import { LineType } from '../types.js';
|
|
30
31
|
class ProcessStore {
|
|
31
32
|
// Mutations - Ink handles render throttling at 30 FPS
|
|
32
33
|
addProcess(process) {
|
|
34
|
+
// Set header on first process that provides one
|
|
35
|
+
if (this.header === undefined && process.header !== undefined) {
|
|
36
|
+
this.header = process.header;
|
|
37
|
+
}
|
|
33
38
|
this.processes = [
|
|
34
39
|
...this.processes,
|
|
35
40
|
process
|
|
@@ -64,11 +69,29 @@ class ProcessStore {
|
|
|
64
69
|
// UI state mutations
|
|
65
70
|
setMode(mode) {
|
|
66
71
|
this.mode = mode;
|
|
67
|
-
if (mode === '
|
|
72
|
+
if (mode === 'interactive') {
|
|
73
|
+
this.selectedIndex = 0;
|
|
74
|
+
} else if (mode === 'errorList') {
|
|
68
75
|
this.selectedErrorIndex = 0;
|
|
69
76
|
}
|
|
70
77
|
this.notify();
|
|
71
78
|
}
|
|
79
|
+
// Interactive mode navigation
|
|
80
|
+
selectNext() {
|
|
81
|
+
if (this.processes.length > 0) {
|
|
82
|
+
this.selectedIndex = (this.selectedIndex + 1) % this.processes.length;
|
|
83
|
+
this.notify();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
selectPrev() {
|
|
87
|
+
if (this.processes.length > 0) {
|
|
88
|
+
this.selectedIndex = (this.selectedIndex - 1 + this.processes.length) % this.processes.length;
|
|
89
|
+
this.notify();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
getSelectedProcess() {
|
|
93
|
+
return this.processes[this.selectedIndex];
|
|
94
|
+
}
|
|
72
95
|
selectNextError() {
|
|
73
96
|
const failed = this.getFailedProcesses();
|
|
74
97
|
if (failed.length > 0) {
|
|
@@ -87,6 +110,43 @@ class ProcessStore {
|
|
|
87
110
|
const failed = this.getFailedProcesses();
|
|
88
111
|
return failed[this.selectedErrorIndex];
|
|
89
112
|
}
|
|
113
|
+
// Expansion methods
|
|
114
|
+
toggleExpand() {
|
|
115
|
+
const selected = this.getSelectedProcess();
|
|
116
|
+
if (!selected) return;
|
|
117
|
+
if (this.expandedId === selected.id) {
|
|
118
|
+
// Collapse
|
|
119
|
+
this.expandedId = null;
|
|
120
|
+
this.scrollOffset = 0;
|
|
121
|
+
} else {
|
|
122
|
+
// Expand
|
|
123
|
+
this.expandedId = selected.id;
|
|
124
|
+
this.scrollOffset = 0;
|
|
125
|
+
}
|
|
126
|
+
this.notify();
|
|
127
|
+
}
|
|
128
|
+
collapse() {
|
|
129
|
+
this.expandedId = null;
|
|
130
|
+
this.scrollOffset = 0;
|
|
131
|
+
this.notify();
|
|
132
|
+
}
|
|
133
|
+
scrollDown(maxVisible) {
|
|
134
|
+
if (!this.expandedId) return;
|
|
135
|
+
const process = this.getProcess(this.expandedId);
|
|
136
|
+
if (!process) return;
|
|
137
|
+
const maxOffset = Math.max(0, process.lines.length - maxVisible);
|
|
138
|
+
if (this.scrollOffset < maxOffset) {
|
|
139
|
+
this.scrollOffset++;
|
|
140
|
+
this.notify();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
scrollUp() {
|
|
144
|
+
if (!this.expandedId) return;
|
|
145
|
+
if (this.scrollOffset > 0) {
|
|
146
|
+
this.scrollOffset--;
|
|
147
|
+
this.notify();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
90
150
|
// Exit signaling
|
|
91
151
|
signalExit(callback) {
|
|
92
152
|
this.shouldExit = true;
|
|
@@ -99,7 +159,11 @@ class ProcessStore {
|
|
|
99
159
|
this.shouldExit = false;
|
|
100
160
|
this.exitCallback = null;
|
|
101
161
|
this.mode = 'normal';
|
|
162
|
+
this.selectedIndex = 0;
|
|
102
163
|
this.selectedErrorIndex = 0;
|
|
164
|
+
this.expandedId = null;
|
|
165
|
+
this.scrollOffset = 0;
|
|
166
|
+
this.header = undefined;
|
|
103
167
|
}
|
|
104
168
|
notify() {
|
|
105
169
|
this.listeners.forEach((l)=>{
|
|
@@ -114,7 +178,10 @@ class ProcessStore {
|
|
|
114
178
|
this.exitCallback = null;
|
|
115
179
|
// UI state
|
|
116
180
|
this.mode = 'normal';
|
|
181
|
+
this.selectedIndex = 0;
|
|
117
182
|
this.selectedErrorIndex = 0;
|
|
183
|
+
this.expandedId = null;
|
|
184
|
+
this.scrollOffset = 0;
|
|
118
185
|
// useSyncExternalStore API
|
|
119
186
|
this.subscribe = (listener)=>{
|
|
120
187
|
this.listeners.add(listener);
|
|
@@ -134,6 +201,10 @@ class ProcessStore {
|
|
|
134
201
|
};
|
|
135
202
|
// Counts
|
|
136
203
|
this.getRunningCount = ()=>this.processes.filter((p)=>p.state === 'running').length;
|
|
204
|
+
this.getMaxGroupLength = ()=>{
|
|
205
|
+
if (this.processes.length === 0) return DEFAULT_COLUMN_WIDTH;
|
|
206
|
+
return Math.max(...this.processes.map((p)=>(p.group || p.title).length));
|
|
207
|
+
};
|
|
137
208
|
this.getDoneCount = ()=>this.processes.filter((p)=>p.state !== 'running').length;
|
|
138
209
|
this.getErrorCount = ()=>this.processes.filter((p)=>p.state === 'error').length;
|
|
139
210
|
this.getErrorLineCount = ()=>{
|
|
@@ -141,7 +212,17 @@ class ProcessStore {
|
|
|
141
212
|
};
|
|
142
213
|
// UI state getters
|
|
143
214
|
this.getMode = ()=>this.mode;
|
|
215
|
+
this.getSelectedIndex = ()=>this.selectedIndex;
|
|
144
216
|
this.getSelectedErrorIndex = ()=>this.selectedErrorIndex;
|
|
217
|
+
this.getExpandedId = ()=>this.expandedId;
|
|
218
|
+
this.getScrollOffset = ()=>this.scrollOffset;
|
|
219
|
+
// Get header
|
|
220
|
+
this.getHeader = ()=>this.header;
|
|
221
|
+
// Show status bar only if any process sets showStatusBar: true (default: false)
|
|
222
|
+
this.getShowStatusBar = ()=>this.processes.some((p)=>p.showStatusBar === true);
|
|
223
|
+
// Interactive mode if any process has interactive: true
|
|
224
|
+
this.getIsInteractive = ()=>this.processes.some((p)=>p.interactive === true);
|
|
225
|
+
this.isAllComplete = ()=>this.processes.length > 0 && this.processes.every((p)=>p.state !== 'running');
|
|
145
226
|
this.getShouldExit = ()=>this.shouldExit;
|
|
146
227
|
this.getExitCallback = ()=>this.exitCallback;
|
|
147
228
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/state/processStore.ts"],"sourcesContent":["import type { ChildProcess, Line } from '../types.ts';\nimport { LineType } from '../types.ts';\n\ntype Listener = () => void;\ntype Mode = 'normal' | 'errorList' | 'errorDetail';\n\nclass 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 selectedErrorIndex = 0;\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 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 getSelectedErrorIndex = (): number => this.selectedErrorIndex;\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 === 'errorList') {\n this.selectedErrorIndex = 0;\n }\n this.notify();\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 // 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.selectedErrorIndex = 0;\n }\n\n private notify(): void {\n this.listeners.forEach((l) => {\n l();\n });\n }\n}\n\nexport const processStore = new ProcessStore();\nexport type { ProcessStore };\n"],"names":["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","selectedErrorIndex","selectNextError","failed","getFailedProcesses","length","selectPrevError","getSelectedError","signalExit","callback","shouldExit","exitCallback","reset","listeners","forEach","l","Set","subscribe","listener","add","delete","getSnapshot","getRunningProcesses","filter","getCompletedProcesses","undefined","getRunningCount","getDoneCount","getErrorCount","getErrorLineCount","reduce","total","type","stderr","getMode","getSelectedErrorIndex","getShouldExit","getExitCallback","processStore"],"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 } from '../types.ts';\nimport { LineType } from '../types.ts';\n\ntype Listener = () => void;\ntype Mode = 'normal' | 'interactive' | 'errorList' | 'errorDetail';\n\nclass 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 // App-level display settings\n private header: string | undefined;\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 // Get header\n getHeader = (): string | undefined => this.header;\n // Show status bar only if any process sets showStatusBar: true (default: false)\n getShowStatusBar = (): boolean => this.processes.some((p) => p.showStatusBar === true);\n // Interactive mode if any process has interactive: true\n getIsInteractive = (): boolean => this.processes.some((p) => p.interactive === true);\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 // Set header on first process that provides one\n if (this.header === undefined && process.header !== undefined) {\n this.header = process.header;\n }\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\nexport const processStore = new ProcessStore();\nexport type { ProcessStore };\n"],"names":["DEFAULT_COLUMN_WIDTH","LineType","ProcessStore","addProcess","process","header","undefined","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","listeners","forEach","l","Set","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","some","showStatusBar","getIsInteractive","interactive","isAllComplete","every","getShouldExit","getExitCallback","processStore"],"mappings":"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"}
|
package/dist/esm/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/types.ts"],"sourcesContent":["export type { SpawnCallback, SpawnError, SpawnOptions, SpawnResult } from 'cross-spawn-cb';\n\nimport type { SpawnError, SpawnResult } from 'cross-spawn-cb';\n\nexport type TerminalOptions = {\n group?: string;\n expanded?: boolean;\n};\n\nexport type TerminalCallback = (error?: SpawnError, result?: SpawnResult) => undefined;\n\nexport const LineType = {\n stdout: 1,\n stderr: 2,\n} as const;\n\nexport type Line = {\n type: (typeof LineType)[keyof typeof LineType];\n text: string;\n};\n\nexport type State = 'running' | 'error' | 'success';\nexport type ChildProcess = {\n id: string;\n group?: string;\n title: string;\n state: State;\n lines: Line[];\n expanded?: boolean;\n};\n"],"names":["LineType","stdout","stderr"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/types.ts"],"sourcesContent":["export type { SpawnCallback, SpawnError, SpawnOptions, SpawnResult } from 'cross-spawn-cb';\n\nimport type { SpawnError, SpawnResult } from 'cross-spawn-cb';\n\nexport type TerminalOptions = {\n group?: string;\n expanded?: boolean;\n header?: string;\n showStatusBar?: boolean;\n interactive?: boolean;\n};\n\nexport type TerminalCallback = (error?: SpawnError, result?: SpawnResult) => undefined;\n\nexport const LineType = {\n stdout: 1,\n stderr: 2,\n} as const;\n\nexport type Line = {\n type: (typeof LineType)[keyof typeof LineType];\n text: string;\n};\n\nexport type State = 'running' | 'error' | 'success';\nexport type ChildProcess = {\n id: string;\n group?: string;\n title: string;\n state: State;\n lines: Line[];\n expanded?: boolean;\n header?: string;\n showStatusBar?: boolean;\n interactive?: boolean;\n};\n"],"names":["LineType","stdout","stderr"],"mappings":"AAcA,OAAO,MAAMA,WAAW;IACtBC,QAAQ;IACRC,QAAQ;AACV,EAAW"}
|