spawn-term 1.1.8 → 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.
Files changed (73) hide show
  1. package/dist/cjs/components/App.js +159 -9
  2. package/dist/cjs/components/App.js.map +1 -1
  3. package/dist/cjs/components/CompactProcessLine.js +162 -0
  4. package/dist/cjs/components/CompactProcessLine.js.map +1 -0
  5. package/dist/cjs/components/Divider.js +24 -0
  6. package/dist/cjs/components/Divider.js.map +1 -0
  7. package/dist/cjs/components/ErrorDetailModal.js +115 -0
  8. package/dist/cjs/components/ErrorDetailModal.js.map +1 -0
  9. package/dist/cjs/components/ErrorListModal.js +135 -0
  10. package/dist/cjs/components/ErrorListModal.js.map +1 -0
  11. package/dist/cjs/components/ExpandedOutput.js +55 -0
  12. package/dist/cjs/components/ExpandedOutput.js.map +1 -0
  13. package/dist/cjs/components/StatusBar.js +104 -0
  14. package/dist/cjs/components/StatusBar.js.map +1 -0
  15. package/dist/cjs/constants.js +42 -0
  16. package/dist/cjs/constants.js.map +1 -0
  17. package/dist/cjs/createApp.js +7 -2
  18. package/dist/cjs/createApp.js.map +1 -1
  19. package/dist/cjs/lib/addLines.js +58 -4
  20. package/dist/cjs/lib/addLines.js.map +1 -1
  21. package/dist/cjs/lib/format.js +21 -0
  22. package/dist/cjs/lib/format.js.map +1 -0
  23. package/dist/cjs/src/components/CompactProcessLine.d.ts +7 -0
  24. package/dist/cjs/src/components/Divider.d.ts +2 -0
  25. package/dist/cjs/src/components/ErrorDetailModal.d.ts +8 -0
  26. package/dist/cjs/src/components/ErrorListModal.d.ts +8 -0
  27. package/dist/cjs/src/components/ExpandedOutput.d.ts +8 -0
  28. package/dist/cjs/src/components/StatusBar.d.ts +8 -0
  29. package/dist/cjs/src/constants.d.ts +7 -0
  30. package/dist/cjs/src/lib/addLines.d.ts +6 -1
  31. package/dist/cjs/src/lib/format.d.ts +2 -0
  32. package/dist/cjs/src/state/processStore.d.ts +36 -0
  33. package/dist/cjs/src/types.d.ts +6 -0
  34. package/dist/cjs/state/processStore.js +202 -0
  35. package/dist/cjs/state/processStore.js.map +1 -1
  36. package/dist/cjs/types.js.map +1 -1
  37. package/dist/esm/components/App.js +159 -9
  38. package/dist/esm/components/App.js.map +1 -1
  39. package/dist/esm/components/CompactProcessLine.js +143 -0
  40. package/dist/esm/components/CompactProcessLine.js.map +1 -0
  41. package/dist/esm/components/Divider.js +13 -0
  42. package/dist/esm/components/Divider.js.map +1 -0
  43. package/dist/esm/components/ErrorDetailModal.js +99 -0
  44. package/dist/esm/components/ErrorDetailModal.js.map +1 -0
  45. package/dist/esm/components/ErrorListModal.js +116 -0
  46. package/dist/esm/components/ErrorListModal.js.map +1 -0
  47. package/dist/esm/components/ExpandedOutput.js +41 -0
  48. package/dist/esm/components/ExpandedOutput.js.map +1 -0
  49. package/dist/esm/components/StatusBar.js +87 -0
  50. package/dist/esm/components/StatusBar.js.map +1 -0
  51. package/dist/esm/constants.js +11 -0
  52. package/dist/esm/constants.js.map +1 -0
  53. package/dist/esm/createApp.js +7 -2
  54. package/dist/esm/createApp.js.map +1 -1
  55. package/dist/esm/lib/addLines.js +32 -5
  56. package/dist/esm/lib/addLines.js.map +1 -1
  57. package/dist/esm/lib/format.js +10 -0
  58. package/dist/esm/lib/format.js.map +1 -0
  59. package/dist/esm/src/components/CompactProcessLine.d.ts +7 -0
  60. package/dist/esm/src/components/Divider.d.ts +2 -0
  61. package/dist/esm/src/components/ErrorDetailModal.d.ts +8 -0
  62. package/dist/esm/src/components/ErrorListModal.d.ts +8 -0
  63. package/dist/esm/src/components/ExpandedOutput.d.ts +8 -0
  64. package/dist/esm/src/components/StatusBar.d.ts +8 -0
  65. package/dist/esm/src/constants.d.ts +7 -0
  66. package/dist/esm/src/lib/addLines.d.ts +6 -1
  67. package/dist/esm/src/lib/format.d.ts +2 -0
  68. package/dist/esm/src/state/processStore.d.ts +36 -0
  69. package/dist/esm/src/types.d.ts +6 -0
  70. package/dist/esm/state/processStore.js +146 -0
  71. package/dist/esm/state/processStore.js.map +1 -1
  72. package/dist/esm/types.js.map +1 -1
  73. package/package.json +1 -1
@@ -1,16 +1,52 @@
1
1
  import type { ChildProcess, Line } from '../types.js';
2
2
  type Listener = () => void;
3
+ type Mode = 'normal' | 'interactive' | 'errorList' | 'errorDetail';
3
4
  declare class ProcessStore {
4
5
  private processes;
6
+ private completedIds;
5
7
  private listeners;
6
8
  private shouldExit;
7
9
  private exitCallback;
10
+ private mode;
11
+ private selectedIndex;
12
+ private selectedErrorIndex;
13
+ private expandedId;
14
+ private scrollOffset;
15
+ private header;
8
16
  subscribe: (listener: Listener) => (() => void);
9
17
  getSnapshot: () => ChildProcess[];
18
+ getRunningProcesses: () => ChildProcess[];
19
+ getCompletedProcesses: () => ChildProcess[];
20
+ getFailedProcesses: () => ChildProcess[];
21
+ getRunningCount: () => number;
22
+ getMaxGroupLength: () => number;
23
+ getDoneCount: () => number;
24
+ getErrorCount: () => number;
25
+ getErrorLineCount: () => number;
26
+ getMode: () => Mode;
27
+ getSelectedIndex: () => number;
28
+ getSelectedErrorIndex: () => number;
29
+ getExpandedId: () => string | null;
30
+ getScrollOffset: () => number;
31
+ getHeader: () => string | undefined;
32
+ getShowStatusBar: () => boolean;
33
+ getIsInteractive: () => boolean;
34
+ isAllComplete: () => boolean;
10
35
  addProcess(process: ChildProcess): void;
11
36
  updateProcess(id: string, update: Partial<ChildProcess>): void;
12
37
  appendLines(id: string, newLines: Line[]): void;
13
38
  getProcess(id: string): ChildProcess | undefined;
39
+ setMode(mode: Mode): void;
40
+ selectNext(): void;
41
+ selectPrev(): void;
42
+ getSelectedProcess(): ChildProcess | undefined;
43
+ selectNextError(): void;
44
+ selectPrevError(): void;
45
+ getSelectedError(): ChildProcess | undefined;
46
+ toggleExpand(): void;
47
+ collapse(): void;
48
+ scrollDown(maxVisible: number): void;
49
+ scrollUp(): void;
14
50
  signalExit(callback: () => void): void;
15
51
  getShouldExit: () => boolean;
16
52
  getExitCallback: () => (() => void) | null;
@@ -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
  };
@@ -8,6 +8,8 @@ Object.defineProperty(exports, "processStore", {
8
8
  return processStore;
9
9
  }
10
10
  });
11
+ var _constantsts = require("../constants.js");
12
+ var _typests = require("../types.js");
11
13
  function _array_like_to_array(arr, len) {
12
14
  if (len == null || len > arr.length) len = arr.length;
13
15
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
@@ -72,9 +74,16 @@ var ProcessStore = /*#__PURE__*/ function() {
72
74
  var _this = this;
73
75
  _class_call_check(this, ProcessStore);
74
76
  this.processes = [];
77
+ this.completedIds = []; // Track completion order
75
78
  this.listeners = new Set();
76
79
  this.shouldExit = false;
77
80
  this.exitCallback = null;
81
+ // UI state
82
+ this.mode = 'normal';
83
+ this.selectedIndex = 0;
84
+ this.selectedErrorIndex = 0;
85
+ this.expandedId = null;
86
+ this.scrollOffset = 0;
78
87
  // useSyncExternalStore API
79
88
  this.subscribe = function(listener) {
80
89
  _this.listeners.add(listener);
@@ -85,6 +94,96 @@ var ProcessStore = /*#__PURE__*/ function() {
85
94
  this.getSnapshot = function() {
86
95
  return _this.processes;
87
96
  };
97
+ // Filtered getters
98
+ this.getRunningProcesses = function() {
99
+ return _this.processes.filter(function(p) {
100
+ return p.state === 'running';
101
+ });
102
+ };
103
+ this.getCompletedProcesses = function() {
104
+ // Return in completion order
105
+ return _this.completedIds.map(function(id) {
106
+ return _this.processes.find(function(p) {
107
+ return p.id === id;
108
+ });
109
+ }).filter(function(p) {
110
+ return p !== undefined;
111
+ });
112
+ };
113
+ this.getFailedProcesses = function() {
114
+ return _this.processes.filter(function(p) {
115
+ return p.state === 'error';
116
+ });
117
+ };
118
+ // Counts
119
+ this.getRunningCount = function() {
120
+ return _this.processes.filter(function(p) {
121
+ return p.state === 'running';
122
+ }).length;
123
+ };
124
+ this.getMaxGroupLength = function() {
125
+ var _Math;
126
+ if (_this.processes.length === 0) return _constantsts.DEFAULT_COLUMN_WIDTH;
127
+ return (_Math = Math).max.apply(_Math, _to_consumable_array(_this.processes.map(function(p) {
128
+ return (p.group || p.title).length;
129
+ })));
130
+ };
131
+ this.getDoneCount = function() {
132
+ return _this.processes.filter(function(p) {
133
+ return p.state !== 'running';
134
+ }).length;
135
+ };
136
+ this.getErrorCount = function() {
137
+ return _this.processes.filter(function(p) {
138
+ return p.state === 'error';
139
+ }).length;
140
+ };
141
+ this.getErrorLineCount = function() {
142
+ return _this.processes.filter(function(p) {
143
+ return p.state === 'error';
144
+ }).reduce(function(total, p) {
145
+ return total + p.lines.filter(function(l) {
146
+ return l.type === _typests.LineType.stderr;
147
+ }).length;
148
+ }, 0);
149
+ };
150
+ // UI state getters
151
+ this.getMode = function() {
152
+ return _this.mode;
153
+ };
154
+ this.getSelectedIndex = function() {
155
+ return _this.selectedIndex;
156
+ };
157
+ this.getSelectedErrorIndex = function() {
158
+ return _this.selectedErrorIndex;
159
+ };
160
+ this.getExpandedId = function() {
161
+ return _this.expandedId;
162
+ };
163
+ this.getScrollOffset = function() {
164
+ return _this.scrollOffset;
165
+ };
166
+ // Get header
167
+ this.getHeader = function() {
168
+ return _this.header;
169
+ };
170
+ // Show status bar only if any process sets showStatusBar: true (default: false)
171
+ this.getShowStatusBar = function() {
172
+ return _this.processes.some(function(p) {
173
+ return p.showStatusBar === true;
174
+ });
175
+ };
176
+ // Interactive mode if any process has interactive: true
177
+ this.getIsInteractive = function() {
178
+ return _this.processes.some(function(p) {
179
+ return p.interactive === true;
180
+ });
181
+ };
182
+ this.isAllComplete = function() {
183
+ return _this.processes.length > 0 && _this.processes.every(function(p) {
184
+ return p.state !== 'running';
185
+ });
186
+ };
88
187
  this.getShouldExit = function() {
89
188
  return _this.shouldExit;
90
189
  };
@@ -95,15 +194,30 @@ var ProcessStore = /*#__PURE__*/ function() {
95
194
  var _proto = ProcessStore.prototype;
96
195
  // Mutations - Ink handles render throttling at 30 FPS
97
196
  _proto.addProcess = function addProcess(process) {
197
+ // Set header on first process that provides one
198
+ if (this.header === undefined && process.header !== undefined) {
199
+ this.header = process.header;
200
+ }
98
201
  this.processes = _to_consumable_array(this.processes).concat([
99
202
  process
100
203
  ]);
101
204
  this.notify();
102
205
  };
103
206
  _proto.updateProcess = function updateProcess(id, update) {
207
+ var oldProcess = this.processes.find(function(p) {
208
+ return p.id === id;
209
+ });
210
+ var wasRunning = (oldProcess === null || oldProcess === void 0 ? void 0 : oldProcess.state) === 'running';
211
+ var isNowComplete = update.state && update.state !== 'running';
104
212
  this.processes = this.processes.map(function(p) {
105
213
  return p.id === id ? _object_spread({}, p, update) : p;
106
214
  });
215
+ // Track completion order
216
+ if (wasRunning && isNowComplete && !this.completedIds.includes(id)) {
217
+ this.completedIds = _to_consumable_array(this.completedIds).concat([
218
+ id
219
+ ]);
220
+ }
107
221
  this.notify();
108
222
  };
109
223
  _proto.appendLines = function appendLines(id, newLines) {
@@ -121,6 +235,87 @@ var ProcessStore = /*#__PURE__*/ function() {
121
235
  return p.id === id;
122
236
  });
123
237
  };
238
+ // UI state mutations
239
+ _proto.setMode = function setMode(mode) {
240
+ this.mode = mode;
241
+ if (mode === 'interactive') {
242
+ this.selectedIndex = 0;
243
+ } else if (mode === 'errorList') {
244
+ this.selectedErrorIndex = 0;
245
+ }
246
+ this.notify();
247
+ };
248
+ // Interactive mode navigation
249
+ _proto.selectNext = function selectNext() {
250
+ if (this.processes.length > 0) {
251
+ this.selectedIndex = (this.selectedIndex + 1) % this.processes.length;
252
+ this.notify();
253
+ }
254
+ };
255
+ _proto.selectPrev = function selectPrev() {
256
+ if (this.processes.length > 0) {
257
+ this.selectedIndex = (this.selectedIndex - 1 + this.processes.length) % this.processes.length;
258
+ this.notify();
259
+ }
260
+ };
261
+ _proto.getSelectedProcess = function getSelectedProcess() {
262
+ return this.processes[this.selectedIndex];
263
+ };
264
+ _proto.selectNextError = function selectNextError() {
265
+ var failed = this.getFailedProcesses();
266
+ if (failed.length > 0) {
267
+ this.selectedErrorIndex = (this.selectedErrorIndex + 1) % failed.length;
268
+ this.notify();
269
+ }
270
+ };
271
+ _proto.selectPrevError = function selectPrevError() {
272
+ var failed = this.getFailedProcesses();
273
+ if (failed.length > 0) {
274
+ this.selectedErrorIndex = (this.selectedErrorIndex - 1 + failed.length) % failed.length;
275
+ this.notify();
276
+ }
277
+ };
278
+ _proto.getSelectedError = function getSelectedError() {
279
+ var failed = this.getFailedProcesses();
280
+ return failed[this.selectedErrorIndex];
281
+ };
282
+ // Expansion methods
283
+ _proto.toggleExpand = function toggleExpand() {
284
+ var selected = this.getSelectedProcess();
285
+ if (!selected) return;
286
+ if (this.expandedId === selected.id) {
287
+ // Collapse
288
+ this.expandedId = null;
289
+ this.scrollOffset = 0;
290
+ } else {
291
+ // Expand
292
+ this.expandedId = selected.id;
293
+ this.scrollOffset = 0;
294
+ }
295
+ this.notify();
296
+ };
297
+ _proto.collapse = function collapse() {
298
+ this.expandedId = null;
299
+ this.scrollOffset = 0;
300
+ this.notify();
301
+ };
302
+ _proto.scrollDown = function scrollDown(maxVisible) {
303
+ if (!this.expandedId) return;
304
+ var process = this.getProcess(this.expandedId);
305
+ if (!process) return;
306
+ var maxOffset = Math.max(0, process.lines.length - maxVisible);
307
+ if (this.scrollOffset < maxOffset) {
308
+ this.scrollOffset++;
309
+ this.notify();
310
+ }
311
+ };
312
+ _proto.scrollUp = function scrollUp() {
313
+ if (!this.expandedId) return;
314
+ if (this.scrollOffset > 0) {
315
+ this.scrollOffset--;
316
+ this.notify();
317
+ }
318
+ };
124
319
  // Exit signaling
125
320
  _proto.signalExit = function signalExit(callback) {
126
321
  this.shouldExit = true;
@@ -129,8 +324,15 @@ var ProcessStore = /*#__PURE__*/ function() {
129
324
  };
130
325
  _proto.reset = function reset() {
131
326
  this.processes = [];
327
+ this.completedIds = [];
132
328
  this.shouldExit = false;
133
329
  this.exitCallback = null;
330
+ this.mode = 'normal';
331
+ this.selectedIndex = 0;
332
+ this.selectedErrorIndex = 0;
333
+ this.expandedId = null;
334
+ this.scrollOffset = 0;
335
+ this.header = undefined;
134
336
  };
135
337
  _proto.notify = function notify() {
136
338
  this.listeners.forEach(function(l) {
@@ -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';\n\ntype Listener = () => void;\n\nclass ProcessStore {\n private processes: ChildProcess[] = [];\n private listeners = new Set<Listener>();\n private shouldExit = false;\n private exitCallback: (() => void) | null = null;\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 // 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 this.processes = this.processes.map((p) => (p.id === id ? { ...p, ...update } : p));\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 // 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.shouldExit = false;\n this.exitCallback = null;\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":["processStore","ProcessStore","processes","listeners","Set","shouldExit","exitCallback","subscribe","listener","add","delete","getSnapshot","getShouldExit","getExitCallback","addProcess","process","notify","updateProcess","id","update","map","p","appendLines","newLines","find","lines","concat","getProcess","signalExit","callback","reset","forEach","l"],"mappings":";;;;+BA+DaA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA3Db,IAAA,AAAMC,6BAAN;;aAAMA;;gCAAAA;aACIC,YAA4B,EAAE;aAC9BC,YAAY,IAAIC;aAChBC,aAAa;aACbC,eAAoC;QAE5C,2BAA2B;aAC3BC,YAAY,SAACC;YACX,MAAKL,SAAS,CAACM,GAAG,CAACD;YACnB,OAAO;uBAAM,MAAKL,SAAS,CAACO,MAAM,CAACF;;QACrC;aAEAG,cAAc;mBAAsB,MAAKT,SAAS;;aA+BlDU,gBAAgB;mBAAe,MAAKP,UAAU;;aAC9CQ,kBAAkB;mBAA2B,MAAKP,YAAY;;;iBA5C1DL;IAcJ,sDAAsD;IACtDa,OAAAA,UAGC,GAHDA,SAAAA,WAAWC,OAAqB;QAC9B,IAAI,CAACb,SAAS,GAAG,AAAC,qBAAG,IAAI,CAACA,SAAS,SAAlB;YAAoBa;SAAQ;QAC7C,IAAI,CAACC,MAAM;IACb;IAEAC,OAAAA,aAGC,GAHDA,SAAAA,cAAcC,EAAU,EAAEC,MAA6B;QACrD,IAAI,CAACjB,SAAS,GAAG,IAAI,CAACA,SAAS,CAACkB,GAAG,CAAC,SAACC;mBAAOA,EAAEH,EAAE,KAAKA,KAAK,mBAAKG,GAAMF,UAAWE;;QAChF,IAAI,CAACL,MAAM;IACb;IAEAM,OAAAA,WAKC,GALDA,SAAAA,YAAYJ,EAAU,EAAEK,QAAgB;QACtC,IAAMR,UAAU,IAAI,CAACb,SAAS,CAACsB,IAAI,CAAC,SAACH;mBAAMA,EAAEH,EAAE,KAAKA;;QACpD,IAAIH,SAAS;YACX,IAAI,CAACE,aAAa,CAACC,IAAI;gBAAEO,OAAOV,QAAQU,KAAK,CAACC,MAAM,CAACH;YAAU;QACjE;IACF;IAEAI,OAAAA,UAEC,GAFDA,SAAAA,WAAWT,EAAU;QACnB,OAAO,IAAI,CAAChB,SAAS,CAACsB,IAAI,CAAC,SAACH;mBAAMA,EAAEH,EAAE,KAAKA;;IAC7C;IAEA,iBAAiB;IACjBU,OAAAA,UAIC,GAJDA,SAAAA,WAAWC,QAAoB;QAC7B,IAAI,CAACxB,UAAU,GAAG;QAClB,IAAI,CAACC,YAAY,GAAGuB;QACpB,IAAI,CAACb,MAAM;IACb;IAKAc,OAAAA,KAIC,GAJDA,SAAAA;QACE,IAAI,CAAC5B,SAAS,GAAG,EAAE;QACnB,IAAI,CAACG,UAAU,GAAG;QAClB,IAAI,CAACC,YAAY,GAAG;IACtB;IAEA,OAAQU,MAIP,GAJD,SAAQA;QACN,IAAI,CAACb,SAAS,CAAC4B,OAAO,CAAC,SAACC;YACtBA;QACF;IACF;WAxDI/B;;AA2DC,IAAMD,eAAe,IAAIC"}
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":["processStore","ProcessStore","processes","completedIds","listeners","Set","shouldExit","exitCallback","mode","selectedIndex","selectedErrorIndex","expandedId","scrollOffset","subscribe","listener","add","delete","getSnapshot","getRunningProcesses","filter","p","state","getCompletedProcesses","map","id","find","undefined","getFailedProcesses","getRunningCount","length","getMaxGroupLength","Math","DEFAULT_COLUMN_WIDTH","max","group","title","getDoneCount","getErrorCount","getErrorLineCount","reduce","total","lines","l","type","LineType","stderr","getMode","getSelectedIndex","getSelectedErrorIndex","getExpandedId","getScrollOffset","getHeader","header","getShowStatusBar","some","showStatusBar","getIsInteractive","interactive","isAllComplete","every","getShouldExit","getExitCallback","addProcess","process","notify","updateProcess","update","oldProcess","wasRunning","isNowComplete","includes","appendLines","newLines","concat","getProcess","setMode","selectNext","selectPrev","getSelectedProcess","selectNextError","failed","selectPrevError","getSelectedError","toggleExpand","selected","collapse","scrollDown","maxVisible","maxOffset","scrollUp","signalExit","callback","reset","forEach"],"mappings":";;;;+BAwOaA;;;eAAAA;;;2BAxOwB;uBAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKzB,IAAA,AAAMC,6BAAN;;aAAMA;;gCAAAA;aACIC,YAA4B,EAAE;aAC9BC,eAAyB,EAAE,EAAE,yBAAyB;aACtDC,YAAY,IAAIC;aAChBC,aAAa;aACbC,eAAoC;QAE5C,WAAW;aACHC,OAAa;aACbC,gBAAgB;aAChBC,qBAAqB;aACrBC,aAA4B;aAC5BC,eAAe;QAKvB,2BAA2B;aAC3BC,YAAY,SAACC;YACX,MAAKV,SAAS,CAACW,GAAG,CAACD;YACnB,OAAO;uBAAM,MAAKV,SAAS,CAACY,MAAM,CAACF;;QACrC;aAEAG,cAAc;mBAAsB,MAAKf,SAAS;;QAElD,mBAAmB;aACnBgB,sBAAsB;YACpB,OAAO,MAAKhB,SAAS,CAACiB,MAAM,CAAC,SAACC;uBAAMA,EAAEC,KAAK,KAAK;;QAClD;aAEAC,wBAAwB;YACtB,6BAA6B;YAC7B,OAAO,MAAKnB,YAAY,CAACoB,GAAG,CAAC,SAACC;uBAAO,MAAKtB,SAAS,CAACuB,IAAI,CAAC,SAACL;2BAAMA,EAAEI,EAAE,KAAKA;;eAAKL,MAAM,CAAC,SAACC;uBAAyBA,MAAMM;;QACvH;aAEAC,qBAAqB;YACnB,OAAO,MAAKzB,SAAS,CAACiB,MAAM,CAAC,SAACC;uBAAMA,EAAEC,KAAK,KAAK;;QAClD;QAEA,SAAS;aACTO,kBAAkB;mBAAc,MAAK1B,SAAS,CAACiB,MAAM,CAAC,SAACC;uBAAMA,EAAEC,KAAK,KAAK;eAAWQ,MAAM;;aAC1FC,oBAAoB;gBAEXC;YADP,IAAI,MAAK7B,SAAS,CAAC2B,MAAM,KAAK,GAAG,OAAOG,iCAAoB;YAC5D,OAAOD,CAAAA,QAAAA,MAAKE,GAAG,OAARF,OAAS,qBAAG,MAAK7B,SAAS,CAACqB,GAAG,CAAC,SAACH;uBAAM,AAACA,CAAAA,EAAEc,KAAK,IAAId,EAAEe,KAAK,AAAD,EAAGN,MAAM;;QAC1E;aACAO,eAAe;mBAAc,MAAKlC,SAAS,CAACiB,MAAM,CAAC,SAACC;uBAAMA,EAAEC,KAAK,KAAK;eAAWQ,MAAM;;aACvFQ,gBAAgB;mBAAc,MAAKnC,SAAS,CAACiB,MAAM,CAAC,SAACC;uBAAMA,EAAEC,KAAK,KAAK;eAASQ,MAAM;;aACtFS,oBAAoB;YAClB,OAAO,MAAKpC,SAAS,CAACiB,MAAM,CAAC,SAACC;uBAAMA,EAAEC,KAAK,KAAK;eAASkB,MAAM,CAAC,SAACC,OAAOpB;uBAAMoB,QAAQpB,EAAEqB,KAAK,CAACtB,MAAM,CAAC,SAACuB;2BAAMA,EAAEC,IAAI,KAAKC,iBAAQ,CAACC,MAAM;mBAAEhB,MAAM;eAAE;QAClJ;QAEA,mBAAmB;aACnBiB,UAAU;mBAAY,MAAKtC,IAAI;;aAC/BuC,mBAAmB;mBAAc,MAAKtC,aAAa;;aACnDuC,wBAAwB;mBAAc,MAAKtC,kBAAkB;;aAC7DuC,gBAAgB;mBAAqB,MAAKtC,UAAU;;aACpDuC,kBAAkB;mBAAc,MAAKtC,YAAY;;QACjD,aAAa;aACbuC,YAAY;mBAA0B,MAAKC,MAAM;;QACjD,gFAAgF;aAChFC,mBAAmB;mBAAe,MAAKnD,SAAS,CAACoD,IAAI,CAAC,SAAClC;uBAAMA,EAAEmC,aAAa,KAAK;;;QACjF,wDAAwD;aACxDC,mBAAmB;mBAAe,MAAKtD,SAAS,CAACoD,IAAI,CAAC,SAAClC;uBAAMA,EAAEqC,WAAW,KAAK;;;aAC/EC,gBAAgB;mBAAe,MAAKxD,SAAS,CAAC2B,MAAM,GAAG,KAAK,MAAK3B,SAAS,CAACyD,KAAK,CAAC,SAACvC;uBAAMA,EAAEC,KAAK,KAAK;;;aA2IpGuC,gBAAgB;mBAAe,MAAKtD,UAAU;;aAC9CuD,kBAAkB;mBAA2B,MAAKtD,YAAY;;;iBA3M1DN;IAiEJ,sDAAsD;IACtD6D,OAAAA,UAOC,GAPDA,SAAAA,WAAWC,OAAqB;QAC9B,gDAAgD;QAChD,IAAI,IAAI,CAACX,MAAM,KAAK1B,aAAaqC,QAAQX,MAAM,KAAK1B,WAAW;YAC7D,IAAI,CAAC0B,MAAM,GAAGW,QAAQX,MAAM;QAC9B;QACA,IAAI,CAAClD,SAAS,GAAG,AAAC,qBAAG,IAAI,CAACA,SAAS,SAAlB;YAAoB6D;SAAQ;QAC7C,IAAI,CAACC,MAAM;IACb;IAEAC,OAAAA,aAaC,GAbDA,SAAAA,cAAczC,EAAU,EAAE0C,MAA6B;QACrD,IAAMC,aAAa,IAAI,CAACjE,SAAS,CAACuB,IAAI,CAAC,SAACL;mBAAMA,EAAEI,EAAE,KAAKA;;QACvD,IAAM4C,aAAaD,CAAAA,uBAAAA,iCAAAA,WAAY9C,KAAK,MAAK;QACzC,IAAMgD,gBAAgBH,OAAO7C,KAAK,IAAI6C,OAAO7C,KAAK,KAAK;QAEvD,IAAI,CAACnB,SAAS,GAAG,IAAI,CAACA,SAAS,CAACqB,GAAG,CAAC,SAACH;mBAAOA,EAAEI,EAAE,KAAKA,KAAK,mBAAKJ,GAAM8C,UAAW9C;;QAEhF,yBAAyB;QACzB,IAAIgD,cAAcC,iBAAiB,CAAC,IAAI,CAAClE,YAAY,CAACmE,QAAQ,CAAC9C,KAAK;YAClE,IAAI,CAACrB,YAAY,GAAG,AAAC,qBAAG,IAAI,CAACA,YAAY,SAArB;gBAAuBqB;aAAG;QAChD;QAEA,IAAI,CAACwC,MAAM;IACb;IAEAO,OAAAA,WAKC,GALDA,SAAAA,YAAY/C,EAAU,EAAEgD,QAAgB;QACtC,IAAMT,UAAU,IAAI,CAAC7D,SAAS,CAACuB,IAAI,CAAC,SAACL;mBAAMA,EAAEI,EAAE,KAAKA;;QACpD,IAAIuC,SAAS;YACX,IAAI,CAACE,aAAa,CAACzC,IAAI;gBAAEiB,OAAOsB,QAAQtB,KAAK,CAACgC,MAAM,CAACD;YAAU;QACjE;IACF;IAEAE,OAAAA,UAEC,GAFDA,SAAAA,WAAWlD,EAAU;QACnB,OAAO,IAAI,CAACtB,SAAS,CAACuB,IAAI,CAAC,SAACL;mBAAMA,EAAEI,EAAE,KAAKA;;IAC7C;IAEA,qBAAqB;IACrBmD,OAAAA,OAQC,GARDA,SAAAA,QAAQnE,IAAU;QAChB,IAAI,CAACA,IAAI,GAAGA;QACZ,IAAIA,SAAS,eAAe;YAC1B,IAAI,CAACC,aAAa,GAAG;QACvB,OAAO,IAAID,SAAS,aAAa;YAC/B,IAAI,CAACE,kBAAkB,GAAG;QAC5B;QACA,IAAI,CAACsD,MAAM;IACb;IAEA,8BAA8B;IAC9BY,OAAAA,UAKC,GALDA,SAAAA;QACE,IAAI,IAAI,CAAC1E,SAAS,CAAC2B,MAAM,GAAG,GAAG;YAC7B,IAAI,CAACpB,aAAa,GAAG,AAAC,CAAA,IAAI,CAACA,aAAa,GAAG,CAAA,IAAK,IAAI,CAACP,SAAS,CAAC2B,MAAM;YACrE,IAAI,CAACmC,MAAM;QACb;IACF;IAEAa,OAAAA,UAKC,GALDA,SAAAA;QACE,IAAI,IAAI,CAAC3E,SAAS,CAAC2B,MAAM,GAAG,GAAG;YAC7B,IAAI,CAACpB,aAAa,GAAG,AAAC,CAAA,IAAI,CAACA,aAAa,GAAG,IAAI,IAAI,CAACP,SAAS,CAAC2B,MAAM,AAAD,IAAK,IAAI,CAAC3B,SAAS,CAAC2B,MAAM;YAC7F,IAAI,CAACmC,MAAM;QACb;IACF;IAEAc,OAAAA,kBAEC,GAFDA,SAAAA;QACE,OAAO,IAAI,CAAC5E,SAAS,CAAC,IAAI,CAACO,aAAa,CAAC;IAC3C;IAEAsE,OAAAA,eAMC,GANDA,SAAAA;QACE,IAAMC,SAAS,IAAI,CAACrD,kBAAkB;QACtC,IAAIqD,OAAOnD,MAAM,GAAG,GAAG;YACrB,IAAI,CAACnB,kBAAkB,GAAG,AAAC,CAAA,IAAI,CAACA,kBAAkB,GAAG,CAAA,IAAKsE,OAAOnD,MAAM;YACvE,IAAI,CAACmC,MAAM;QACb;IACF;IAEAiB,OAAAA,eAMC,GANDA,SAAAA;QACE,IAAMD,SAAS,IAAI,CAACrD,kBAAkB;QACtC,IAAIqD,OAAOnD,MAAM,GAAG,GAAG;YACrB,IAAI,CAACnB,kBAAkB,GAAG,AAAC,CAAA,IAAI,CAACA,kBAAkB,GAAG,IAAIsE,OAAOnD,MAAM,AAAD,IAAKmD,OAAOnD,MAAM;YACvF,IAAI,CAACmC,MAAM;QACb;IACF;IAEAkB,OAAAA,gBAGC,GAHDA,SAAAA;QACE,IAAMF,SAAS,IAAI,CAACrD,kBAAkB;QACtC,OAAOqD,MAAM,CAAC,IAAI,CAACtE,kBAAkB,CAAC;IACxC;IAEA,oBAAoB;IACpByE,OAAAA,YAcC,GAdDA,SAAAA;QACE,IAAMC,WAAW,IAAI,CAACN,kBAAkB;QACxC,IAAI,CAACM,UAAU;QAEf,IAAI,IAAI,CAACzE,UAAU,KAAKyE,SAAS5D,EAAE,EAAE;YACnC,WAAW;YACX,IAAI,CAACb,UAAU,GAAG;YAClB,IAAI,CAACC,YAAY,GAAG;QACtB,OAAO;YACL,SAAS;YACT,IAAI,CAACD,UAAU,GAAGyE,SAAS5D,EAAE;YAC7B,IAAI,CAACZ,YAAY,GAAG;QACtB;QACA,IAAI,CAACoD,MAAM;IACb;IAEAqB,OAAAA,QAIC,GAJDA,SAAAA;QACE,IAAI,CAAC1E,UAAU,GAAG;QAClB,IAAI,CAACC,YAAY,GAAG;QACpB,IAAI,CAACoD,MAAM;IACb;IAEAsB,OAAAA,UAUC,GAVDA,SAAAA,WAAWC,UAAkB;QAC3B,IAAI,CAAC,IAAI,CAAC5E,UAAU,EAAE;QACtB,IAAMoD,UAAU,IAAI,CAACW,UAAU,CAAC,IAAI,CAAC/D,UAAU;QAC/C,IAAI,CAACoD,SAAS;QAEd,IAAMyB,YAAYzD,KAAKE,GAAG,CAAC,GAAG8B,QAAQtB,KAAK,CAACZ,MAAM,GAAG0D;QACrD,IAAI,IAAI,CAAC3E,YAAY,GAAG4E,WAAW;YACjC,IAAI,CAAC5E,YAAY;YACjB,IAAI,CAACoD,MAAM;QACb;IACF;IAEAyB,OAAAA,QAMC,GANDA,SAAAA;QACE,IAAI,CAAC,IAAI,CAAC9E,UAAU,EAAE;QACtB,IAAI,IAAI,CAACC,YAAY,GAAG,GAAG;YACzB,IAAI,CAACA,YAAY;YACjB,IAAI,CAACoD,MAAM;QACb;IACF;IAEA,iBAAiB;IACjB0B,OAAAA,UAIC,GAJDA,SAAAA,WAAWC,QAAoB;QAC7B,IAAI,CAACrF,UAAU,GAAG;QAClB,IAAI,CAACC,YAAY,GAAGoF;QACpB,IAAI,CAAC3B,MAAM;IACb;IAKA4B,OAAAA,KAWC,GAXDA,SAAAA;QACE,IAAI,CAAC1F,SAAS,GAAG,EAAE;QACnB,IAAI,CAACC,YAAY,GAAG,EAAE;QACtB,IAAI,CAACG,UAAU,GAAG;QAClB,IAAI,CAACC,YAAY,GAAG;QACpB,IAAI,CAACC,IAAI,GAAG;QACZ,IAAI,CAACC,aAAa,GAAG;QACrB,IAAI,CAACC,kBAAkB,GAAG;QAC1B,IAAI,CAACC,UAAU,GAAG;QAClB,IAAI,CAACC,YAAY,GAAG;QACpB,IAAI,CAACwC,MAAM,GAAG1B;IAChB;IAEA,OAAQsC,MAIP,GAJD,SAAQA;QACN,IAAI,CAAC5D,SAAS,CAACyF,OAAO,CAAC,SAACnD;YACtBA;QACF;IACF;WA9NIzC;;AAiOC,IAAMD,eAAe,IAAIC"}
@@ -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":";;;;+BAWaA;;;eAAAA;;;AAAN,IAAMA,WAAW;IACtBC,QAAQ;IACRC,QAAQ;AACV"}
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":";;;;+BAcaA;;;eAAAA;;;AAAN,IAAMA,WAAW;IACtBC,QAAQ;IACRC,QAAQ;AACV"}
@@ -1,14 +1,36 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Box, useApp } from 'ink';
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Box, Text, useApp, useInput, useStdin } from 'ink';
3
3
  import { useEffect, useSyncExternalStore } from 'react';
4
+ import { EXPANDED_MAX_VISIBLE_LINES } from '../constants.js';
4
5
  import { processStore } from '../state/processStore.js';
5
- import ChildProcess from './ChildProcess.js';
6
+ import CompactProcessLine from './CompactProcessLine.js';
7
+ import Divider from './Divider.js';
8
+ import ErrorDetailModal from './ErrorDetailModal.js';
9
+ import ErrorListModal from './ErrorListModal.js';
10
+ import ExpandedOutput from './ExpandedOutput.js';
11
+ import StatusBar from './StatusBar.js';
6
12
  export default function App() {
7
13
  const { exit } = useApp();
8
- // Subscribe to process state
14
+ const { isRawModeSupported } = useStdin();
15
+ // Subscribe to store state
9
16
  const processes = useSyncExternalStore(processStore.subscribe, processStore.getSnapshot);
10
- // Handle exit signal
11
17
  const shouldExit = useSyncExternalStore(processStore.subscribe, processStore.getShouldExit);
18
+ const mode = useSyncExternalStore(processStore.subscribe, processStore.getMode);
19
+ const selectedIndex = useSyncExternalStore(processStore.subscribe, processStore.getSelectedIndex);
20
+ const selectedErrorIndex = useSyncExternalStore(processStore.subscribe, processStore.getSelectedErrorIndex);
21
+ const expandedId = useSyncExternalStore(processStore.subscribe, processStore.getExpandedId);
22
+ const scrollOffset = useSyncExternalStore(processStore.subscribe, processStore.getScrollOffset);
23
+ // Derived state
24
+ const failedProcesses = processStore.getFailedProcesses();
25
+ const runningCount = processStore.getRunningCount();
26
+ const doneCount = processStore.getDoneCount();
27
+ const errorCount = processStore.getErrorCount();
28
+ const errorLineCount = processStore.getErrorLineCount();
29
+ const header = processStore.getHeader();
30
+ const showStatusBar = processStore.getShowStatusBar();
31
+ const isInteractive = processStore.getIsInteractive();
32
+ const isAllComplete = processStore.isAllComplete();
33
+ // Handle exit signal
12
34
  useEffect(()=>{
13
35
  if (shouldExit) {
14
36
  exit();
@@ -17,10 +39,138 @@ export default function App() {
17
39
  shouldExit,
18
40
  exit
19
41
  ]);
20
- return /*#__PURE__*/ _jsx(Box, {
42
+ // Auto-enter interactive mode when all complete and interactive flag is set
43
+ useEffect(()=>{
44
+ if (isAllComplete && isInteractive && mode === 'normal') {
45
+ processStore.setMode('interactive');
46
+ }
47
+ }, [
48
+ isAllComplete,
49
+ isInteractive,
50
+ mode
51
+ ]);
52
+ // Keyboard handling (only active when raw mode is supported)
53
+ useInput((input, key)=>{
54
+ if (mode === 'normal') {
55
+ if (input === 'e' && errorCount > 0) {
56
+ processStore.setMode('errorList');
57
+ }
58
+ } else if (mode === 'interactive') {
59
+ if (input === 'q' || key.escape) {
60
+ if (expandedId) {
61
+ processStore.collapse();
62
+ } else {
63
+ processStore.signalExit(()=>{});
64
+ }
65
+ } else if (key.return) {
66
+ processStore.toggleExpand();
67
+ } else if (key.downArrow) {
68
+ if (expandedId) {
69
+ processStore.scrollDown(EXPANDED_MAX_VISIBLE_LINES);
70
+ } else {
71
+ processStore.selectNext();
72
+ }
73
+ } else if (key.upArrow) {
74
+ if (expandedId) {
75
+ processStore.scrollUp();
76
+ } else {
77
+ processStore.selectPrev();
78
+ }
79
+ } else if (input === 'j') {
80
+ if (expandedId) {
81
+ processStore.scrollDown(EXPANDED_MAX_VISIBLE_LINES);
82
+ } else {
83
+ processStore.selectNext();
84
+ }
85
+ } else if (input === 'k') {
86
+ if (expandedId) {
87
+ processStore.scrollUp();
88
+ } else {
89
+ processStore.selectPrev();
90
+ }
91
+ } else if (input === 'e' && errorCount > 0) {
92
+ processStore.setMode('errorList');
93
+ }
94
+ } else if (mode === 'errorList') {
95
+ if (key.escape) {
96
+ processStore.setMode(isInteractive ? 'interactive' : 'normal');
97
+ } else if (key.downArrow) {
98
+ processStore.selectNextError();
99
+ } else if (key.upArrow) {
100
+ processStore.selectPrevError();
101
+ } else if (key.return) {
102
+ processStore.setMode('errorDetail');
103
+ }
104
+ } else if (mode === 'errorDetail') {
105
+ if (key.escape) {
106
+ processStore.setMode('errorList');
107
+ } else if (key.downArrow) {
108
+ processStore.selectNextError();
109
+ } else if (key.upArrow) {
110
+ processStore.selectPrevError();
111
+ }
112
+ }
113
+ }, {
114
+ isActive: isRawModeSupported === true
115
+ });
116
+ // Error list modal
117
+ if (mode === 'errorList') {
118
+ return /*#__PURE__*/ _jsx(ErrorListModal, {
119
+ errors: failedProcesses,
120
+ selectedIndex: selectedErrorIndex,
121
+ totalErrorLines: errorLineCount
122
+ });
123
+ }
124
+ // Error detail modal
125
+ if (mode === 'errorDetail') {
126
+ const selectedError = processStore.getSelectedError();
127
+ if (selectedError) {
128
+ return /*#__PURE__*/ _jsx(ErrorDetailModal, {
129
+ error: selectedError,
130
+ currentIndex: selectedErrorIndex,
131
+ totalErrors: failedProcesses.length
132
+ });
133
+ }
134
+ // Fallback if no error selected
135
+ processStore.setMode('errorList');
136
+ }
137
+ // Normal/Interactive view - render in original registration order
138
+ const showSelection = mode === 'interactive';
139
+ return /*#__PURE__*/ _jsxs(Box, {
21
140
  flexDirection: "column",
22
- children: processes.map((item)=>/*#__PURE__*/ _jsx(ChildProcess, {
23
- item: item
24
- }, item.id))
141
+ children: [
142
+ header && /*#__PURE__*/ _jsxs(_Fragment, {
143
+ children: [
144
+ /*#__PURE__*/ _jsx(Text, {
145
+ children: header
146
+ }),
147
+ /*#__PURE__*/ _jsx(Divider, {})
148
+ ]
149
+ }),
150
+ processes.map((item, index)=>/*#__PURE__*/ _jsxs(Box, {
151
+ flexDirection: "column",
152
+ children: [
153
+ /*#__PURE__*/ _jsx(CompactProcessLine, {
154
+ item: item,
155
+ isSelected: showSelection && index === selectedIndex
156
+ }),
157
+ expandedId === item.id && /*#__PURE__*/ _jsx(ExpandedOutput, {
158
+ lines: item.lines,
159
+ scrollOffset: scrollOffset
160
+ })
161
+ ]
162
+ }, item.id)),
163
+ showStatusBar && processes.length > 0 && /*#__PURE__*/ _jsxs(_Fragment, {
164
+ children: [
165
+ /*#__PURE__*/ _jsx(Divider, {}),
166
+ /*#__PURE__*/ _jsx(StatusBar, {
167
+ running: runningCount,
168
+ done: doneCount,
169
+ errors: errorCount,
170
+ errorLines: errorLineCount
171
+ })
172
+ ]
173
+ })
174
+ ]
25
175
  });
26
176
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, useApp } from 'ink';\nimport { useEffect, useSyncExternalStore } from 'react';\nimport { processStore } from '../state/processStore.ts';\nimport type { ChildProcess as ChildProcessT } from '../types.ts';\nimport ChildProcess from './ChildProcess.ts';\n\nexport default function App(): React.JSX.Element {\n const { exit } = useApp();\n\n // Subscribe to process state\n const processes = useSyncExternalStore(processStore.subscribe, processStore.getSnapshot);\n\n // Handle exit signal\n const shouldExit = useSyncExternalStore(processStore.subscribe, processStore.getShouldExit);\n\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n return (\n <Box flexDirection=\"column\">\n {processes.map((item: ChildProcessT) => (\n <ChildProcess key={item.id} item={item} />\n ))}\n </Box>\n );\n}\n"],"names":["Box","useApp","useEffect","useSyncExternalStore","processStore","ChildProcess","App","exit","processes","subscribe","getSnapshot","shouldExit","getShouldExit","flexDirection","map","item","id"],"mappings":";AAAA,SAASA,GAAG,EAAEC,MAAM,QAAQ,MAAM;AAClC,SAASC,SAAS,EAAEC,oBAAoB,QAAQ,QAAQ;AACxD,SAASC,YAAY,QAAQ,2BAA2B;AAExD,OAAOC,kBAAkB,oBAAoB;AAE7C,eAAe,SAASC;IACtB,MAAM,EAAEC,IAAI,EAAE,GAAGN;IAEjB,6BAA6B;IAC7B,MAAMO,YAAYL,qBAAqBC,aAAaK,SAAS,EAAEL,aAAaM,WAAW;IAEvF,qBAAqB;IACrB,MAAMC,aAAaR,qBAAqBC,aAAaK,SAAS,EAAEL,aAAaQ,aAAa;IAE1FV,UAAU;QACR,IAAIS,YAAY;YACdJ;QACF;IACF,GAAG;QAACI;QAAYJ;KAAK;IAErB,qBACE,KAACP;QAAIa,eAAc;kBAChBL,UAAUM,GAAG,CAAC,CAACC,qBACd,KAACV;gBAA2BU,MAAMA;eAAfA,KAAKC,EAAE;;AAIlC"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Text, useApp, useInput, useStdin } from 'ink';\nimport { useEffect, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport { processStore } from '../state/processStore.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorDetailModal from './ErrorDetailModal.ts';\nimport ErrorListModal from './ErrorListModal.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\n\nexport default function App(): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n\n // Subscribe to store state\n const processes = useSyncExternalStore(processStore.subscribe, processStore.getSnapshot);\n const shouldExit = useSyncExternalStore(processStore.subscribe, processStore.getShouldExit);\n const mode = useSyncExternalStore(processStore.subscribe, processStore.getMode);\n const selectedIndex = useSyncExternalStore(processStore.subscribe, processStore.getSelectedIndex);\n const selectedErrorIndex = useSyncExternalStore(processStore.subscribe, processStore.getSelectedErrorIndex);\n const expandedId = useSyncExternalStore(processStore.subscribe, processStore.getExpandedId);\n const scrollOffset = useSyncExternalStore(processStore.subscribe, processStore.getScrollOffset);\n\n // Derived state\n const failedProcesses = processStore.getFailedProcesses();\n const runningCount = processStore.getRunningCount();\n const doneCount = processStore.getDoneCount();\n const errorCount = processStore.getErrorCount();\n const errorLineCount = processStore.getErrorLineCount();\n const header = processStore.getHeader();\n const showStatusBar = processStore.getShowStatusBar();\n const isInteractive = processStore.getIsInteractive();\n const isAllComplete = processStore.isAllComplete();\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode when all complete and interactive flag is set\n useEffect(() => {\n if (isAllComplete && isInteractive && mode === 'normal') {\n processStore.setMode('interactive');\n }\n }, [isAllComplete, isInteractive, mode]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n if (mode === 'normal') {\n if (input === 'e' && errorCount > 0) {\n processStore.setMode('errorList');\n }\n } else if (mode === 'interactive') {\n if (input === 'q' || key.escape) {\n if (expandedId) {\n processStore.collapse();\n } else {\n processStore.signalExit(() => {});\n }\n } else if (key.return) {\n processStore.toggleExpand();\n } else if (key.downArrow) {\n if (expandedId) {\n processStore.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n processStore.selectNext();\n }\n } else if (key.upArrow) {\n if (expandedId) {\n processStore.scrollUp();\n } else {\n processStore.selectPrev();\n }\n } else if (input === 'j') {\n if (expandedId) {\n processStore.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n processStore.selectNext();\n }\n } else if (input === 'k') {\n if (expandedId) {\n processStore.scrollUp();\n } else {\n processStore.selectPrev();\n }\n } else if (input === 'e' && errorCount > 0) {\n processStore.setMode('errorList');\n }\n } else if (mode === 'errorList') {\n if (key.escape) {\n processStore.setMode(isInteractive ? 'interactive' : 'normal');\n } else if (key.downArrow) {\n processStore.selectNextError();\n } else if (key.upArrow) {\n processStore.selectPrevError();\n } else if (key.return) {\n processStore.setMode('errorDetail');\n }\n } else if (mode === 'errorDetail') {\n if (key.escape) {\n processStore.setMode('errorList');\n } else if (key.downArrow) {\n processStore.selectNextError();\n } else if (key.upArrow) {\n processStore.selectPrevError();\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Error list modal\n if (mode === 'errorList') {\n return <ErrorListModal errors={failedProcesses} selectedIndex={selectedErrorIndex} totalErrorLines={errorLineCount} />;\n }\n\n // Error detail modal\n if (mode === 'errorDetail') {\n const selectedError = processStore.getSelectedError();\n if (selectedError) {\n return <ErrorDetailModal error={selectedError} currentIndex={selectedErrorIndex} totalErrors={failedProcesses.length} />;\n }\n // Fallback if no error selected\n processStore.setMode('errorList');\n }\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n return (\n <Box flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* All processes in registration order */}\n {processes.map((item, index) => (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && index === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={item.lines} scrollOffset={scrollOffset} />}\n </Box>\n ))}\n\n {/* Status bar */}\n {showStatusBar && processes.length > 0 && (\n <>\n <Divider />\n <StatusBar running={runningCount} done={doneCount} errors={errorCount} errorLines={errorLineCount} />\n </>\n )}\n </Box>\n );\n}\n"],"names":["Box","Text","useApp","useInput","useStdin","useEffect","useSyncExternalStore","EXPANDED_MAX_VISIBLE_LINES","processStore","CompactProcessLine","Divider","ErrorDetailModal","ErrorListModal","ExpandedOutput","StatusBar","App","exit","isRawModeSupported","processes","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","selectedErrorIndex","getSelectedErrorIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","failedProcesses","getFailedProcesses","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","isAllComplete","setMode","input","key","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","selectNext","upArrow","scrollUp","selectPrev","selectNextError","selectPrevError","isActive","errors","totalErrorLines","selectedError","getSelectedError","error","currentIndex","totalErrors","length","showSelection","flexDirection","map","item","index","isSelected","id","lines","running","done","errorLines"],"mappings":"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"}