spawn-term 3.0.4 → 3.0.6
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 +28 -56
- package/dist/cjs/components/App.js.map +1 -1
- package/dist/cjs/components/ErrorFooter.js +120 -0
- package/dist/cjs/components/ErrorFooter.js.map +1 -0
- package/dist/cjs/components/ExpandedOutput.js +0 -2
- package/dist/cjs/components/ExpandedOutput.js.map +1 -1
- package/dist/cjs/components/StatusBar.js +22 -33
- package/dist/cjs/components/StatusBar.js.map +1 -1
- package/dist/cjs/index-esm.js +4 -0
- package/dist/cjs/index-esm.js.map +1 -1
- package/dist/cjs/lib/TerminalBuffer.js +139 -0
- package/dist/cjs/lib/TerminalBuffer.js.map +1 -0
- package/dist/cjs/session.js +129 -56
- package/dist/cjs/session.js.map +1 -1
- package/dist/cjs/src/components/ErrorFooter.d.ts +11 -0
- package/dist/cjs/src/index-esm.d.ts +1 -0
- package/dist/cjs/src/lib/TerminalBuffer.d.ts +32 -0
- package/dist/cjs/src/state/processStore.d.ts +14 -7
- package/dist/cjs/src/types.d.ts +2 -0
- package/dist/cjs/state/processStore.js +79 -26
- package/dist/cjs/state/processStore.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/components/App.js +28 -56
- package/dist/esm/components/App.js.map +1 -1
- package/dist/esm/components/ErrorFooter.js +95 -0
- package/dist/esm/components/ErrorFooter.js.map +1 -0
- package/dist/esm/components/ExpandedOutput.js +0 -2
- package/dist/esm/components/ExpandedOutput.js.map +1 -1
- package/dist/esm/components/StatusBar.js +22 -33
- package/dist/esm/components/StatusBar.js.map +1 -1
- package/dist/esm/index-esm.js +1 -0
- package/dist/esm/index-esm.js.map +1 -1
- package/dist/esm/lib/TerminalBuffer.js +62 -0
- package/dist/esm/lib/TerminalBuffer.js.map +1 -0
- package/dist/esm/session.js +112 -35
- package/dist/esm/session.js.map +1 -1
- package/dist/esm/src/components/ErrorFooter.d.ts +11 -0
- package/dist/esm/src/index-esm.d.ts +1 -0
- package/dist/esm/src/lib/TerminalBuffer.d.ts +32 -0
- package/dist/esm/src/state/processStore.d.ts +14 -7
- package/dist/esm/src/types.d.ts +2 -0
- package/dist/esm/state/processStore.js +53 -22
- package/dist/esm/state/processStore.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +2 -1
- package/dist/cjs/components/ErrorDetailModal.js +0 -115
- package/dist/cjs/components/ErrorDetailModal.js.map +0 -1
- package/dist/cjs/components/ErrorListModal.js +0 -135
- package/dist/cjs/components/ErrorListModal.js.map +0 -1
- package/dist/cjs/src/components/ErrorDetailModal.d.ts +0 -8
- package/dist/cjs/src/components/ErrorListModal.d.ts +0 -8
- package/dist/esm/components/ErrorDetailModal.js +0 -99
- package/dist/esm/components/ErrorDetailModal.js.map +0 -1
- package/dist/esm/components/ErrorListModal.js +0 -116
- package/dist/esm/components/ErrorListModal.js.map +0 -1
- package/dist/esm/src/components/ErrorDetailModal.d.ts +0 -8
- package/dist/esm/src/components/ErrorListModal.d.ts +0 -8
|
@@ -82,10 +82,11 @@ var ProcessStore = /*#__PURE__*/ function() {
|
|
|
82
82
|
// UI state
|
|
83
83
|
this.mode = 'normal';
|
|
84
84
|
this.selectedIndex = 0;
|
|
85
|
-
this.selectedErrorIndex = 0;
|
|
86
85
|
this.expandedId = null;
|
|
87
86
|
this.scrollOffset = 0;
|
|
88
87
|
this.listScrollOffset = 0; // Viewport offset for process list
|
|
88
|
+
this.errorFooterExpanded = false; // For non-interactive error footer
|
|
89
|
+
this.bufferVersion = 0; // Increments on every notify() to trigger re-renders
|
|
89
90
|
this.showStatusBar = false;
|
|
90
91
|
this.isInteractive = false;
|
|
91
92
|
// useSyncExternalStore API
|
|
@@ -146,9 +147,7 @@ var ProcessStore = /*#__PURE__*/ function() {
|
|
|
146
147
|
return _this.processes.filter(function(p) {
|
|
147
148
|
return p.state === 'error';
|
|
148
149
|
}).reduce(function(total, p) {
|
|
149
|
-
return total + p.
|
|
150
|
-
return l.type === _typests.LineType.stderr;
|
|
151
|
-
}).length;
|
|
150
|
+
return total + _this.getProcessLineCount(p.id);
|
|
152
151
|
}, 0);
|
|
153
152
|
};
|
|
154
153
|
// UI state getters
|
|
@@ -158,9 +157,6 @@ var ProcessStore = /*#__PURE__*/ function() {
|
|
|
158
157
|
this.getSelectedIndex = function() {
|
|
159
158
|
return _this.selectedIndex;
|
|
160
159
|
};
|
|
161
|
-
this.getSelectedErrorIndex = function() {
|
|
162
|
-
return _this.selectedErrorIndex;
|
|
163
|
-
};
|
|
164
160
|
this.getExpandedId = function() {
|
|
165
161
|
return _this.expandedId;
|
|
166
162
|
};
|
|
@@ -170,6 +166,12 @@ var ProcessStore = /*#__PURE__*/ function() {
|
|
|
170
166
|
this.getListScrollOffset = function() {
|
|
171
167
|
return _this.listScrollOffset;
|
|
172
168
|
};
|
|
169
|
+
this.getErrorFooterExpanded = function() {
|
|
170
|
+
return _this.errorFooterExpanded;
|
|
171
|
+
};
|
|
172
|
+
this.getBufferVersion = function() {
|
|
173
|
+
return _this.bufferVersion;
|
|
174
|
+
};
|
|
173
175
|
// Session-level getters (set at session creation, immutable)
|
|
174
176
|
this.getHeader = function() {
|
|
175
177
|
return _this.header;
|
|
@@ -220,6 +222,10 @@ var ProcessStore = /*#__PURE__*/ function() {
|
|
|
220
222
|
id
|
|
221
223
|
]);
|
|
222
224
|
}
|
|
225
|
+
// Auto-expand error footer when all complete with errors (non-interactive only)
|
|
226
|
+
if (!this.isInteractive && this.isAllComplete() && this.getErrorCount() > 0) {
|
|
227
|
+
this.errorFooterExpanded = true;
|
|
228
|
+
}
|
|
223
229
|
this.notify();
|
|
224
230
|
};
|
|
225
231
|
_proto.appendLines = function appendLines(id, newLines) {
|
|
@@ -237,13 +243,34 @@ var ProcessStore = /*#__PURE__*/ function() {
|
|
|
237
243
|
return p.id === id;
|
|
238
244
|
});
|
|
239
245
|
};
|
|
246
|
+
// Get rendered lines from terminal buffer or fallback to lines array
|
|
247
|
+
_proto.getProcessLines = function getProcessLines(id) {
|
|
248
|
+
var process = this.getProcess(id);
|
|
249
|
+
if (!process) return [];
|
|
250
|
+
if (process.terminalBuffer) {
|
|
251
|
+
return process.terminalBuffer.getLines().map(function(text) {
|
|
252
|
+
return {
|
|
253
|
+
type: _typests.LineType.stdout,
|
|
254
|
+
text: text
|
|
255
|
+
};
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return process.lines;
|
|
259
|
+
};
|
|
260
|
+
// Get line count from terminal buffer or lines array
|
|
261
|
+
_proto.getProcessLineCount = function getProcessLineCount(id) {
|
|
262
|
+
var process = this.getProcess(id);
|
|
263
|
+
if (!process) return 0;
|
|
264
|
+
if (process.terminalBuffer) {
|
|
265
|
+
return process.terminalBuffer.lineCount;
|
|
266
|
+
}
|
|
267
|
+
return process.lines.length;
|
|
268
|
+
};
|
|
240
269
|
// UI state mutations
|
|
241
270
|
_proto.setMode = function setMode(mode) {
|
|
242
271
|
this.mode = mode;
|
|
243
272
|
if (mode === 'interactive') {
|
|
244
273
|
this.selectedIndex = 0;
|
|
245
|
-
} else if (mode === 'errorList') {
|
|
246
|
-
this.selectedErrorIndex = 0;
|
|
247
274
|
}
|
|
248
275
|
this.notify();
|
|
249
276
|
};
|
|
@@ -276,23 +303,25 @@ var ProcessStore = /*#__PURE__*/ function() {
|
|
|
276
303
|
_proto.getSelectedProcess = function getSelectedProcess() {
|
|
277
304
|
return this.processes[this.selectedIndex];
|
|
278
305
|
};
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.notify();
|
|
284
|
-
}
|
|
306
|
+
// Error footer methods (for non-interactive mode)
|
|
307
|
+
_proto.toggleErrorFooter = function toggleErrorFooter() {
|
|
308
|
+
this.errorFooterExpanded = !this.errorFooterExpanded;
|
|
309
|
+
this.notify();
|
|
285
310
|
};
|
|
286
|
-
_proto.
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
this.selectedErrorIndex = (this.selectedErrorIndex - 1 + failed.length) % failed.length;
|
|
311
|
+
_proto.expandErrorFooter = function expandErrorFooter() {
|
|
312
|
+
if (!this.errorFooterExpanded) {
|
|
313
|
+
this.errorFooterExpanded = true;
|
|
290
314
|
this.notify();
|
|
291
315
|
}
|
|
292
316
|
};
|
|
293
|
-
_proto.
|
|
294
|
-
var
|
|
295
|
-
return
|
|
317
|
+
_proto.getErrorLines = function getErrorLines() {
|
|
318
|
+
var _this = this;
|
|
319
|
+
return this.getFailedProcesses().map(function(p) {
|
|
320
|
+
return {
|
|
321
|
+
processName: p.group || p.title,
|
|
322
|
+
lines: _this.getProcessLines(p.id)
|
|
323
|
+
};
|
|
324
|
+
});
|
|
296
325
|
};
|
|
297
326
|
// Expansion methods
|
|
298
327
|
_proto.toggleExpand = function toggleExpand() {
|
|
@@ -316,9 +345,9 @@ var ProcessStore = /*#__PURE__*/ function() {
|
|
|
316
345
|
};
|
|
317
346
|
_proto.scrollDown = function scrollDown(maxVisible) {
|
|
318
347
|
if (!this.expandedId) return;
|
|
319
|
-
var
|
|
320
|
-
if (
|
|
321
|
-
var maxOffset = Math.max(0,
|
|
348
|
+
var lineCount = this.getProcessLineCount(this.expandedId);
|
|
349
|
+
if (lineCount === 0) return;
|
|
350
|
+
var maxOffset = Math.max(0, lineCount - maxVisible);
|
|
322
351
|
if (this.scrollOffset < maxOffset) {
|
|
323
352
|
this.scrollOffset++;
|
|
324
353
|
this.notify();
|
|
@@ -338,19 +367,43 @@ var ProcessStore = /*#__PURE__*/ function() {
|
|
|
338
367
|
this.notify();
|
|
339
368
|
};
|
|
340
369
|
_proto.reset = function reset() {
|
|
370
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
371
|
+
try {
|
|
372
|
+
// Dispose terminal buffers before clearing
|
|
373
|
+
for(var _iterator = this.processes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
374
|
+
var process = _step.value;
|
|
375
|
+
var _process_terminalBuffer;
|
|
376
|
+
(_process_terminalBuffer = process.terminalBuffer) === null || _process_terminalBuffer === void 0 ? void 0 : _process_terminalBuffer.dispose();
|
|
377
|
+
}
|
|
378
|
+
} catch (err) {
|
|
379
|
+
_didIteratorError = true;
|
|
380
|
+
_iteratorError = err;
|
|
381
|
+
} finally{
|
|
382
|
+
try {
|
|
383
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
384
|
+
_iterator.return();
|
|
385
|
+
}
|
|
386
|
+
} finally{
|
|
387
|
+
if (_didIteratorError) {
|
|
388
|
+
throw _iteratorError;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
341
392
|
this.processes = [];
|
|
342
393
|
this.completedIds = [];
|
|
343
394
|
this.shouldExit = false;
|
|
344
395
|
this.exitCallback = null;
|
|
345
396
|
this.mode = 'normal';
|
|
346
397
|
this.selectedIndex = 0;
|
|
347
|
-
this.selectedErrorIndex = 0;
|
|
348
398
|
this.expandedId = null;
|
|
349
399
|
this.scrollOffset = 0;
|
|
350
400
|
this.listScrollOffset = 0;
|
|
401
|
+
this.errorFooterExpanded = false;
|
|
351
402
|
this.header = undefined;
|
|
352
403
|
};
|
|
404
|
+
// Public notify for session to trigger updates when terminal buffer changes
|
|
353
405
|
_proto.notify = function notify() {
|
|
406
|
+
this.bufferVersion++;
|
|
354
407
|
this.listeners.forEach(function(l) {
|
|
355
408
|
l();
|
|
356
409
|
});
|
|
@@ -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 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":["ProcessStore","options","processes","completedIds","listeners","Set","shouldExit","exitCallback","mode","selectedIndex","selectedErrorIndex","expandedId","scrollOffset","listScrollOffset","showStatusBar","isInteractive","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","getListScrollOffset","getHeader","header","getShowStatusBar","getIsInteractive","isAllComplete","every","getShouldExit","getExitCallback","interactive","addProcess","process","notify","updateProcess","update","oldProcess","wasRunning","isNowComplete","includes","appendLines","newLines","concat","getProcess","setMode","selectNext","visibleCount","adjustListScroll","selectPrev","getSelectedProcess","selectNextError","failed","selectPrevError","getSelectedError","toggleExpand","selected","collapse","scrollDown","maxVisible","maxOffset","scrollUp","signalExit","callback","reset","forEach"],"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';\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 expandedId: string | null = null;\n private scrollOffset = 0;\n private listScrollOffset = 0; // Viewport offset for process list\n private errorFooterExpanded = false; // For non-interactive error footer\n private bufferVersion = 0; // Increments on every notify() to trigger re-renders\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 + this.getProcessLineCount(p.id), 0);\n };\n\n // UI state getters\n getMode = (): Mode => this.mode;\n getSelectedIndex = (): number => this.selectedIndex;\n getExpandedId = (): string | null => this.expandedId;\n getScrollOffset = (): number => this.scrollOffset;\n getListScrollOffset = (): number => this.listScrollOffset;\n getErrorFooterExpanded = (): boolean => this.errorFooterExpanded;\n getBufferVersion = (): number => this.bufferVersion;\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 // Auto-expand error footer when all complete with errors (non-interactive only)\n if (!this.isInteractive && this.isAllComplete() && this.getErrorCount() > 0) {\n this.errorFooterExpanded = true;\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 // Get rendered lines from terminal buffer or fallback to lines array\n getProcessLines(id: string): Line[] {\n const process = this.getProcess(id);\n if (!process) return [];\n if (process.terminalBuffer) {\n return process.terminalBuffer.getLines().map((text) => ({\n type: LineType.stdout,\n text,\n }));\n }\n return process.lines;\n }\n\n // Get line count from terminal buffer or lines array\n getProcessLineCount(id: string): number {\n const process = this.getProcess(id);\n if (!process) return 0;\n if (process.terminalBuffer) {\n return process.terminalBuffer.lineCount;\n }\n return process.lines.length;\n }\n\n // UI state mutations\n setMode(mode: Mode): void {\n this.mode = mode;\n if (mode === 'interactive') {\n this.selectedIndex = 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 // Error footer methods (for non-interactive mode)\n toggleErrorFooter(): void {\n this.errorFooterExpanded = !this.errorFooterExpanded;\n this.notify();\n }\n\n expandErrorFooter(): void {\n if (!this.errorFooterExpanded) {\n this.errorFooterExpanded = true;\n this.notify();\n }\n }\n\n getErrorLines(): Array<{ processName: string; lines: Line[] }> {\n return this.getFailedProcesses().map((p) => ({\n processName: p.group || p.title,\n lines: this.getProcessLines(p.id),\n }));\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 lineCount = this.getProcessLineCount(this.expandedId);\n if (lineCount === 0) return;\n\n const maxOffset = Math.max(0, lineCount - 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 // Dispose terminal buffers before clearing\n for (const process of this.processes) {\n process.terminalBuffer?.dispose();\n }\n this.processes = [];\n this.completedIds = [];\n this.shouldExit = false;\n this.exitCallback = null;\n this.mode = 'normal';\n this.selectedIndex = 0;\n this.expandedId = null;\n this.scrollOffset = 0;\n this.listScrollOffset = 0;\n this.errorFooterExpanded = false;\n this.header = undefined;\n }\n\n // Public notify for session to trigger updates when terminal buffer changes\n notify(): void {\n this.bufferVersion++;\n this.listeners.forEach((l) => {\n l();\n });\n }\n}\n\n// Note: No global singleton - session creates its own store instance\n"],"names":["ProcessStore","options","processes","completedIds","listeners","Set","shouldExit","exitCallback","mode","selectedIndex","expandedId","scrollOffset","listScrollOffset","errorFooterExpanded","bufferVersion","showStatusBar","isInteractive","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","getProcessLineCount","getMode","getSelectedIndex","getExpandedId","getScrollOffset","getListScrollOffset","getErrorFooterExpanded","getBufferVersion","getHeader","header","getShowStatusBar","getIsInteractive","isAllComplete","every","getShouldExit","getExitCallback","interactive","addProcess","process","notify","updateProcess","update","oldProcess","wasRunning","isNowComplete","includes","appendLines","newLines","lines","concat","getProcess","getProcessLines","terminalBuffer","getLines","text","type","LineType","stdout","lineCount","setMode","selectNext","visibleCount","adjustListScroll","selectPrev","getSelectedProcess","toggleErrorFooter","expandErrorFooter","getErrorLines","processName","toggleExpand","selected","collapse","scrollDown","maxVisible","maxOffset","scrollUp","signalExit","callback","reset","dispose","forEach","l"],"mappings":"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"}
|
package/dist/cjs/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\n// Session-level options (set at session creation, immutable)\nexport type SessionOptions = {\n header?: string;\n showStatusBar?: boolean;\n interactive?: boolean;\n};\n\n// Per-process options (set when spawning each process)\nexport type ProcessOptions = {\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';\n\n// Internal representation of a child process\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":";;;;+BAmBaA;;;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\n// Session-level options (set at session creation, immutable)\nexport type SessionOptions = {\n header?: string;\n showStatusBar?: boolean;\n interactive?: boolean;\n};\n\n// Per-process options (set when spawning each process)\nexport type ProcessOptions = {\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';\n\n// Import type for TerminalBuffer (avoid circular dependency)\nimport type { TerminalBuffer } from './lib/TerminalBuffer.ts';\n\n// Internal representation of a child process\nexport type ChildProcess = {\n id: string;\n group?: string;\n title: string;\n state: State;\n lines: Line[];\n terminalBuffer?: TerminalBuffer; // Virtual terminal for ANSI interpretation\n expanded?: boolean;\n};\n"],"names":["LineType","stdout","stderr"],"mappings":";;;;+BAmBaA;;;eAAAA;;;AAAN,IAAMA,WAAW;IACtBC,QAAQ;IACRC,QAAQ;AACV"}
|
|
@@ -5,8 +5,7 @@ import { EXPANDED_MAX_VISIBLE_LINES } from '../constants.js';
|
|
|
5
5
|
import { StoreContext } from '../state/StoreContext.js';
|
|
6
6
|
import CompactProcessLine from './CompactProcessLine.js';
|
|
7
7
|
import Divider from './Divider.js';
|
|
8
|
-
import
|
|
9
|
-
import ErrorListModal from './ErrorListModal.js';
|
|
8
|
+
import ErrorFooter from './ErrorFooter.js';
|
|
10
9
|
import ExpandedOutput from './ExpandedOutput.js';
|
|
11
10
|
import StatusBar from './StatusBar.js';
|
|
12
11
|
function AppContent({ store }) {
|
|
@@ -19,24 +18,28 @@ function AppContent({ store }) {
|
|
|
19
18
|
const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);
|
|
20
19
|
const mode = useSyncExternalStore(store.subscribe, store.getMode);
|
|
21
20
|
const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);
|
|
22
|
-
const selectedErrorIndex = useSyncExternalStore(store.subscribe, store.getSelectedErrorIndex);
|
|
23
21
|
const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);
|
|
24
22
|
const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);
|
|
25
23
|
const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);
|
|
24
|
+
const errorFooterExpanded = useSyncExternalStore(store.subscribe, store.getErrorFooterExpanded);
|
|
25
|
+
// Subscribe to buffer version to trigger re-renders when terminal buffer content changes
|
|
26
|
+
const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);
|
|
26
27
|
// Subscribed state that triggers re-renders
|
|
27
28
|
const header = useSyncExternalStore(store.subscribe, store.getHeader);
|
|
28
29
|
const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);
|
|
29
30
|
const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);
|
|
30
|
-
// Calculate visible process count (reserve lines for header, divider, status bar)
|
|
31
|
-
|
|
31
|
+
// Calculate visible process count (reserve lines for header, divider, status bar, expanded output)
|
|
32
|
+
// When a process is expanded, reserve space for the expanded output to prevent terminal scrolling
|
|
33
|
+
const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint
|
|
34
|
+
const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight;
|
|
32
35
|
const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);
|
|
33
36
|
// Derived state (computed from processes which is already subscribed)
|
|
34
|
-
const failedProcesses = store.getFailedProcesses();
|
|
35
37
|
const runningCount = store.getRunningCount();
|
|
36
38
|
const doneCount = store.getDoneCount();
|
|
37
39
|
const errorCount = store.getErrorCount();
|
|
38
40
|
const errorLineCount = store.getErrorLineCount();
|
|
39
|
-
const
|
|
41
|
+
const _isAllComplete = store.isAllComplete();
|
|
42
|
+
const errorLines = store.getErrorLines();
|
|
40
43
|
// Handle exit signal
|
|
41
44
|
useEffect(()=>{
|
|
42
45
|
if (shouldExit) {
|
|
@@ -46,13 +49,13 @@ function AppContent({ store }) {
|
|
|
46
49
|
shouldExit,
|
|
47
50
|
exit
|
|
48
51
|
]);
|
|
49
|
-
// Auto-enter interactive mode when
|
|
52
|
+
// Auto-enter interactive mode immediately when interactive flag is set
|
|
53
|
+
// This allows selecting and viewing logs of running processes
|
|
50
54
|
useEffect(()=>{
|
|
51
|
-
if (
|
|
55
|
+
if (isInteractive && mode === 'normal') {
|
|
52
56
|
store.setMode('interactive');
|
|
53
57
|
}
|
|
54
58
|
}, [
|
|
55
|
-
isAllComplete,
|
|
56
59
|
isInteractive,
|
|
57
60
|
mode,
|
|
58
61
|
store
|
|
@@ -60,8 +63,9 @@ function AppContent({ store }) {
|
|
|
60
63
|
// Keyboard handling (only active when raw mode is supported)
|
|
61
64
|
useInput((input, key)=>{
|
|
62
65
|
if (mode === 'normal') {
|
|
66
|
+
// In non-interactive mode, 'e' toggles error footer
|
|
63
67
|
if (input === 'e' && errorCount > 0) {
|
|
64
|
-
store.
|
|
68
|
+
store.toggleErrorFooter();
|
|
65
69
|
}
|
|
66
70
|
} else if (mode === 'interactive') {
|
|
67
71
|
if (input === 'q' || key.escape) {
|
|
@@ -96,32 +100,12 @@ function AppContent({ store }) {
|
|
|
96
100
|
} else {
|
|
97
101
|
store.selectPrev(visibleProcessCount);
|
|
98
102
|
}
|
|
99
|
-
} else if (input === 'e' && errorCount > 0) {
|
|
100
|
-
store.setMode('errorList');
|
|
101
|
-
}
|
|
102
|
-
} else if (mode === 'errorList') {
|
|
103
|
-
if (key.escape) {
|
|
104
|
-
store.setMode(isInteractive ? 'interactive' : 'normal');
|
|
105
|
-
} else if (key.downArrow) {
|
|
106
|
-
store.selectNextError();
|
|
107
|
-
} else if (key.upArrow) {
|
|
108
|
-
store.selectPrevError();
|
|
109
|
-
} else if (key.return) {
|
|
110
|
-
store.setMode('errorDetail');
|
|
111
|
-
}
|
|
112
|
-
} else if (mode === 'errorDetail') {
|
|
113
|
-
if (key.escape) {
|
|
114
|
-
store.setMode('errorList');
|
|
115
|
-
} else if (key.downArrow) {
|
|
116
|
-
store.selectNextError();
|
|
117
|
-
} else if (key.upArrow) {
|
|
118
|
-
store.selectPrevError();
|
|
119
103
|
}
|
|
120
104
|
}
|
|
121
105
|
}, {
|
|
122
106
|
isActive: isRawModeSupported === true
|
|
123
107
|
});
|
|
124
|
-
// Slice processes to visible viewport in interactive mode
|
|
108
|
+
// Slice processes to visible viewport in interactive mode
|
|
125
109
|
const visibleProcesses = useMemo(()=>{
|
|
126
110
|
if (mode === 'interactive') {
|
|
127
111
|
return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);
|
|
@@ -133,31 +117,15 @@ function AppContent({ store }) {
|
|
|
133
117
|
listScrollOffset,
|
|
134
118
|
visibleProcessCount
|
|
135
119
|
]);
|
|
136
|
-
// Error list modal
|
|
137
|
-
if (mode === 'errorList') {
|
|
138
|
-
return /*#__PURE__*/ _jsx(ErrorListModal, {
|
|
139
|
-
errors: failedProcesses,
|
|
140
|
-
selectedIndex: selectedErrorIndex,
|
|
141
|
-
totalErrorLines: errorLineCount
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
// Error detail modal
|
|
145
|
-
if (mode === 'errorDetail') {
|
|
146
|
-
const selectedError = store.getSelectedError();
|
|
147
|
-
if (selectedError) {
|
|
148
|
-
return /*#__PURE__*/ _jsx(ErrorDetailModal, {
|
|
149
|
-
error: selectedError,
|
|
150
|
-
currentIndex: selectedErrorIndex,
|
|
151
|
-
totalErrors: failedProcesses.length
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
// Fallback if no error selected
|
|
155
|
-
store.setMode('errorList');
|
|
156
|
-
}
|
|
157
120
|
// Normal/Interactive view - render in original registration order
|
|
158
121
|
const showSelection = mode === 'interactive';
|
|
122
|
+
// Force full re-render when layout HEIGHT changes (not content)
|
|
123
|
+
// Combined with incrementalRendering: false in session.tsx, this ensures clean redraws
|
|
124
|
+
// Note: scrollOffset is NOT included - scrolling within expansion doesn't change height
|
|
125
|
+
const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}`;
|
|
159
126
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
160
127
|
flexDirection: "column",
|
|
128
|
+
height: terminalHeight,
|
|
161
129
|
children: [
|
|
162
130
|
header && /*#__PURE__*/ _jsxs(_Fragment, {
|
|
163
131
|
children: [
|
|
@@ -179,13 +147,13 @@ function AppContent({ store }) {
|
|
|
179
147
|
isSelected: showSelection && originalIndex === selectedIndex
|
|
180
148
|
}),
|
|
181
149
|
expandedId === item.id && /*#__PURE__*/ _jsx(ExpandedOutput, {
|
|
182
|
-
lines: item.
|
|
150
|
+
lines: store.getProcessLines(item.id),
|
|
183
151
|
scrollOffset: scrollOffset
|
|
184
152
|
})
|
|
185
153
|
]
|
|
186
154
|
}, item.id);
|
|
187
155
|
})
|
|
188
|
-
}
|
|
156
|
+
}),
|
|
189
157
|
showStatusBar && processes.length > 0 && /*#__PURE__*/ _jsxs(_Fragment, {
|
|
190
158
|
children: [
|
|
191
159
|
/*#__PURE__*/ _jsx(Divider, {}),
|
|
@@ -196,9 +164,13 @@ function AppContent({ store }) {
|
|
|
196
164
|
errorLines: errorLineCount
|
|
197
165
|
})
|
|
198
166
|
]
|
|
167
|
+
}),
|
|
168
|
+
!isInteractive && errorCount > 0 && /*#__PURE__*/ _jsx(ErrorFooter, {
|
|
169
|
+
errors: errorLines,
|
|
170
|
+
isExpanded: errorFooterExpanded
|
|
199
171
|
})
|
|
200
172
|
]
|
|
201
|
-
});
|
|
173
|
+
}, layoutKey);
|
|
202
174
|
}
|
|
203
175
|
// Wrapper component that provides store context
|
|
204
176
|
export default function App({ store }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';\nimport { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { ProcessStore } from '../state/processStore.ts';\nimport { StoreContext } from '../state/StoreContext.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\ninterface AppProps {\n store: ProcessStore;\n}\n\nfunction AppContent({ store }: AppProps): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Subscribe to store state\n const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);\n const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);\n const mode = useSyncExternalStore(store.subscribe, store.getMode);\n const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);\n const selectedErrorIndex = useSyncExternalStore(store.subscribe, store.getSelectedErrorIndex);\n const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);\n const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);\n const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);\n\n // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(store.subscribe, store.getHeader);\n const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);\n const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);\n\n // Calculate visible process count (reserve lines for header, divider, status bar)\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from processes which is already subscribed)\n const failedProcesses = store.getFailedProcesses();\n const runningCount = store.getRunningCount();\n const doneCount = store.getDoneCount();\n const errorCount = store.getErrorCount();\n const errorLineCount = store.getErrorLineCount();\n const isAllComplete = store.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 store.setMode('interactive');\n }\n }, [isAllComplete, isInteractive, mode, store]);\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 store.setMode('errorList');\n }\n } else if (mode === 'interactive') {\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse();\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand();\n } else if (key.downArrow) {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow) {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n } else if (input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n } else if (input === 'e' && errorCount > 0) {\n store.setMode('errorList');\n }\n } else if (mode === 'errorList') {\n if (key.escape) {\n store.setMode(isInteractive ? 'interactive' : 'normal');\n } else if (key.downArrow) {\n store.selectNextError();\n } else if (key.upArrow) {\n store.selectPrevError();\n } else if (key.return) {\n store.setMode('errorDetail');\n }\n } else if (mode === 'errorDetail') {\n if (key.escape) {\n store.setMode('errorList');\n } else if (key.downArrow) {\n store.selectNextError();\n } else if (key.upArrow) {\n store.selectPrevError();\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Slice processes to visible viewport in interactive mode (must be before early returns)\n const visibleProcesses = useMemo(() => {\n if (mode === 'interactive') {\n return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);\n }\n return processes;\n }, [processes, mode, listScrollOffset, visibleProcessCount]);\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 = store.getSelectedError();\n if (selectedError) {\n return <ErrorDetailModal error={selectedError} currentIndex={selectedErrorIndex} totalErrors={failedProcesses.length} />;\n }\n // Fallback if no error selected\n store.setMode('errorList');\n }\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n\n return (\n <Box flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Visible processes - key forces clean re-render on scroll */}\n <Box key={`processes-${listScrollOffset}`} flexDirection=\"column\">\n {visibleProcesses.map((item) => {\n const originalIndex = processes.indexOf(item);\n return (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && originalIndex === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={item.lines} scrollOffset={scrollOffset} />}\n </Box>\n );\n })}\n </Box>\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\n// Wrapper component that provides store context\nexport default function App({ store }: AppProps): React.JSX.Element {\n return (\n <StoreContext.Provider value={store}>\n <AppContent store={store} />\n </StoreContext.Provider>\n );\n}\n"],"names":["Box","Text","useApp","useInput","useStdin","useStdout","useEffect","useMemo","useSyncExternalStore","EXPANDED_MAX_VISIBLE_LINES","StoreContext","CompactProcessLine","Divider","ErrorDetailModal","ErrorListModal","ExpandedOutput","StatusBar","AppContent","store","exit","isRawModeSupported","stdout","terminalHeight","rows","processes","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","selectedErrorIndex","getSelectedErrorIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","reservedLines","visibleProcessCount","Math","max","failedProcesses","getFailedProcesses","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","isAllComplete","setMode","input","key","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","selectNext","upArrow","scrollUp","selectPrev","selectNextError","selectPrevError","isActive","visibleProcesses","slice","errors","totalErrorLines","selectedError","getSelectedError","error","currentIndex","totalErrors","length","showSelection","flexDirection","map","item","originalIndex","indexOf","isSelected","id","lines","running","done","errorLines","App","Provider","value"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';\nimport { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { ProcessStore } from '../state/processStore.ts';\nimport { StoreContext } from '../state/StoreContext.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorFooter from './ErrorFooter.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\n\ninterface AppProps {\n store: ProcessStore;\n}\n\nfunction AppContent({ store }: AppProps): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Subscribe to store state\n const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);\n const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);\n const mode = useSyncExternalStore(store.subscribe, store.getMode);\n const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);\n const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);\n const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);\n const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);\n const errorFooterExpanded = useSyncExternalStore(store.subscribe, store.getErrorFooterExpanded);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(store.subscribe, store.getHeader);\n const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);\n const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);\n\n // Calculate visible process count (reserve lines for header, divider, status bar, expanded output)\n // When a process is expanded, reserve space for the expanded output to prevent terminal scrolling\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from processes which is already subscribed)\n const runningCount = store.getRunningCount();\n const doneCount = store.getDoneCount();\n const errorCount = store.getErrorCount();\n const errorLineCount = store.getErrorLineCount();\n const _isAllComplete = store.isAllComplete();\n const errorLines = store.getErrorLines();\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode immediately when interactive flag is set\n // This allows selecting and viewing logs of running processes\n useEffect(() => {\n if (isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isInteractive, mode, store]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n if (mode === 'normal') {\n // In non-interactive mode, 'e' toggles error footer\n if (input === 'e' && errorCount > 0) {\n store.toggleErrorFooter();\n }\n } else if (mode === 'interactive') {\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse();\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand();\n } else if (key.downArrow) {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow) {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n } else if (input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Slice processes to visible viewport in interactive mode\n const visibleProcesses = useMemo(() => {\n if (mode === 'interactive') {\n return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);\n }\n return processes;\n }, [processes, mode, listScrollOffset, visibleProcessCount]);\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n\n // Force full re-render when layout HEIGHT changes (not content)\n // Combined with incrementalRendering: false in session.tsx, this ensures clean redraws\n // Note: scrollOffset is NOT included - scrolling within expansion doesn't change height\n const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}`;\n\n return (\n <Box key={layoutKey} flexDirection=\"column\" height={terminalHeight}>\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Visible processes */}\n <Box flexDirection=\"column\">\n {visibleProcesses.map((item) => {\n const originalIndex = processes.indexOf(item);\n return (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && originalIndex === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={store.getProcessLines(item.id)} scrollOffset={scrollOffset} />}\n </Box>\n );\n })}\n </Box>\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\n {/* Error footer (non-interactive mode only) */}\n {!isInteractive && errorCount > 0 && <ErrorFooter errors={errorLines} isExpanded={errorFooterExpanded} />}\n </Box>\n );\n}\n\n// Wrapper component that provides store context\nexport default function App({ store }: AppProps): React.JSX.Element {\n return (\n <StoreContext.Provider value={store}>\n <AppContent store={store} />\n </StoreContext.Provider>\n );\n}\n"],"names":["Box","Text","useApp","useInput","useStdin","useStdout","useEffect","useMemo","useSyncExternalStore","EXPANDED_MAX_VISIBLE_LINES","StoreContext","CompactProcessLine","Divider","ErrorFooter","ExpandedOutput","StatusBar","AppContent","store","exit","isRawModeSupported","stdout","terminalHeight","rows","processes","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","_bufferVersion","getBufferVersion","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","setMode","input","key","toggleErrorFooter","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","selectNext","upArrow","scrollUp","selectPrev","isActive","visibleProcesses","slice","showSelection","layoutKey","flexDirection","height","map","item","originalIndex","indexOf","isSelected","id","lines","getProcessLines","length","running","done","errors","isExpanded","App","Provider","value"],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { LineType } from '../types.js';
|
|
5
|
+
import Divider from './Divider.js';
|
|
6
|
+
export default /*#__PURE__*/ memo(function ErrorFooter({ errors, isExpanded }) {
|
|
7
|
+
// Calculate totals for collapsed summary
|
|
8
|
+
const totalLines = errors.reduce((sum, e)=>sum + e.lines.filter((l)=>l.type === LineType.stderr).length, 0);
|
|
9
|
+
const totalProcesses = errors.length;
|
|
10
|
+
if (totalProcesses === 0) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const processText = totalProcesses === 1 ? 'process' : 'processes';
|
|
14
|
+
if (!isExpanded) {
|
|
15
|
+
// Collapsed view - single summary line
|
|
16
|
+
const summary = totalLines > 0 ? `${totalLines} error line${totalLines === 1 ? '' : 's'} in ${totalProcesses} ${processText}` : `${totalProcesses} failed ${processText}`;
|
|
17
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
18
|
+
children: [
|
|
19
|
+
/*#__PURE__*/ _jsx(Divider, {}),
|
|
20
|
+
/*#__PURE__*/ _jsxs(Text, {
|
|
21
|
+
children: [
|
|
22
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
23
|
+
color: "red",
|
|
24
|
+
children: '\u25b8'
|
|
25
|
+
}),
|
|
26
|
+
` ${summary} `,
|
|
27
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
28
|
+
dimColor: true,
|
|
29
|
+
children: "[e]"
|
|
30
|
+
})
|
|
31
|
+
]
|
|
32
|
+
})
|
|
33
|
+
]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Expanded view - show all error lines (or just process names if no stderr)
|
|
37
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
38
|
+
children: [
|
|
39
|
+
/*#__PURE__*/ _jsx(Divider, {}),
|
|
40
|
+
/*#__PURE__*/ _jsxs(Text, {
|
|
41
|
+
children: [
|
|
42
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
43
|
+
color: "red",
|
|
44
|
+
children: '\u25be'
|
|
45
|
+
}),
|
|
46
|
+
' Errors ',
|
|
47
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
48
|
+
dimColor: true,
|
|
49
|
+
children: "[e]"
|
|
50
|
+
})
|
|
51
|
+
]
|
|
52
|
+
}),
|
|
53
|
+
/*#__PURE__*/ _jsx(Box, {
|
|
54
|
+
flexDirection: "column",
|
|
55
|
+
children: errors.map((errorGroup)=>{
|
|
56
|
+
const stderrLines = errorGroup.lines.filter((line)=>line.type === LineType.stderr);
|
|
57
|
+
if (stderrLines.length === 0) {
|
|
58
|
+
// No stderr output - just show process name
|
|
59
|
+
return /*#__PURE__*/ _jsxs(Text, {
|
|
60
|
+
children: [
|
|
61
|
+
/*#__PURE__*/ _jsxs(Text, {
|
|
62
|
+
dimColor: true,
|
|
63
|
+
children: [
|
|
64
|
+
"[",
|
|
65
|
+
errorGroup.processName,
|
|
66
|
+
"]"
|
|
67
|
+
]
|
|
68
|
+
}),
|
|
69
|
+
" ",
|
|
70
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
71
|
+
color: "red",
|
|
72
|
+
children: "(failed)"
|
|
73
|
+
})
|
|
74
|
+
]
|
|
75
|
+
}, errorGroup.processName);
|
|
76
|
+
}
|
|
77
|
+
return stderrLines.map((line, index)=>/*#__PURE__*/ _jsxs(Text, {
|
|
78
|
+
children: [
|
|
79
|
+
/*#__PURE__*/ _jsxs(Text, {
|
|
80
|
+
dimColor: true,
|
|
81
|
+
children: [
|
|
82
|
+
"[",
|
|
83
|
+
errorGroup.processName,
|
|
84
|
+
"]"
|
|
85
|
+
]
|
|
86
|
+
}),
|
|
87
|
+
" ",
|
|
88
|
+
line.text
|
|
89
|
+
]
|
|
90
|
+
}, `${errorGroup.processName}-${index}`));
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
]
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ErrorFooter.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport type { Line } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport Divider from './Divider.ts';\n\ntype ErrorGroup = {\n processName: string;\n lines: Line[];\n};\n\ntype Props = {\n errors: ErrorGroup[];\n isExpanded: boolean;\n};\n\nexport default memo(function ErrorFooter({ errors, isExpanded }: Props) {\n // Calculate totals for collapsed summary\n const totalLines = errors.reduce((sum, e) => sum + e.lines.filter((l) => l.type === LineType.stderr).length, 0);\n const totalProcesses = errors.length;\n\n if (totalProcesses === 0) {\n return null;\n }\n\n const processText = totalProcesses === 1 ? 'process' : 'processes';\n\n if (!isExpanded) {\n // Collapsed view - single summary line\n const summary = totalLines > 0 ? `${totalLines} error line${totalLines === 1 ? '' : 's'} in ${totalProcesses} ${processText}` : `${totalProcesses} failed ${processText}`;\n return (\n <>\n <Divider />\n <Text>\n <Text color=\"red\">{'\\u25b8'}</Text>\n {` ${summary} `}\n <Text dimColor>[e]</Text>\n </Text>\n </>\n );\n }\n\n // Expanded view - show all error lines (or just process names if no stderr)\n return (\n <>\n <Divider />\n <Text>\n <Text color=\"red\">{'\\u25be'}</Text>\n {' Errors '}\n <Text dimColor>[e]</Text>\n </Text>\n <Box flexDirection=\"column\">\n {errors.map((errorGroup) => {\n const stderrLines = errorGroup.lines.filter((line) => line.type === LineType.stderr);\n if (stderrLines.length === 0) {\n // No stderr output - just show process name\n return (\n <Text key={errorGroup.processName}>\n <Text dimColor>[{errorGroup.processName}]</Text> <Text color=\"red\">(failed)</Text>\n </Text>\n );\n }\n return stderrLines.map((line, index) => (\n <Text key={`${errorGroup.processName}-${index}`}>\n <Text dimColor>[{errorGroup.processName}]</Text> {line.text}\n </Text>\n ));\n })}\n </Box>\n </>\n );\n});\n"],"names":["Box","Text","memo","LineType","Divider","ErrorFooter","errors","isExpanded","totalLines","reduce","sum","e","lines","filter","l","type","stderr","length","totalProcesses","processText","summary","color","dimColor","flexDirection","map","errorGroup","stderrLines","line","processName","index","text"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,QAAQ,MAAM;AAChC,SAASC,IAAI,QAAQ,QAAQ;AAE7B,SAASC,QAAQ,QAAQ,cAAc;AACvC,OAAOC,aAAa,eAAe;AAYnC,6BAAeF,KAAK,SAASG,YAAY,EAAEC,MAAM,EAAEC,UAAU,EAAS;IACpE,yCAAyC;IACzC,MAAMC,aAAaF,OAAOG,MAAM,CAAC,CAACC,KAAKC,IAAMD,MAAMC,EAAEC,KAAK,CAACC,MAAM,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKZ,SAASa,MAAM,EAAEC,MAAM,EAAE;IAC7G,MAAMC,iBAAiBZ,OAAOW,MAAM;IAEpC,IAAIC,mBAAmB,GAAG;QACxB,OAAO;IACT;IAEA,MAAMC,cAAcD,mBAAmB,IAAI,YAAY;IAEvD,IAAI,CAACX,YAAY;QACf,uCAAuC;QACvC,MAAMa,UAAUZ,aAAa,IAAI,GAAGA,WAAW,WAAW,EAAEA,eAAe,IAAI,KAAK,IAAI,IAAI,EAAEU,eAAe,CAAC,EAAEC,aAAa,GAAG,GAAGD,eAAe,QAAQ,EAAEC,aAAa;QACzK,qBACE;;8BACE,KAACf;8BACD,MAACH;;sCACC,KAACA;4BAAKoB,OAAM;sCAAO;;wBAClB,CAAC,CAAC,EAAED,QAAQ,CAAC,CAAC;sCACf,KAACnB;4BAAKqB,QAAQ;sCAAC;;;;;;IAIvB;IAEA,4EAA4E;IAC5E,qBACE;;0BACE,KAAClB;0BACD,MAACH;;kCACC,KAACA;wBAAKoB,OAAM;kCAAO;;oBAClB;kCACD,KAACpB;wBAAKqB,QAAQ;kCAAC;;;;0BAEjB,KAACtB;gBAAIuB,eAAc;0BAChBjB,OAAOkB,GAAG,CAAC,CAACC;oBACX,MAAMC,cAAcD,WAAWb,KAAK,CAACC,MAAM,CAAC,CAACc,OAASA,KAAKZ,IAAI,KAAKZ,SAASa,MAAM;oBACnF,IAAIU,YAAYT,MAAM,KAAK,GAAG;wBAC5B,4CAA4C;wBAC5C,qBACE,MAAChB;;8CACC,MAACA;oCAAKqB,QAAQ;;wCAAC;wCAAEG,WAAWG,WAAW;wCAAC;;;gCAAQ;8CAAC,KAAC3B;oCAAKoB,OAAM;8CAAM;;;2BAD1DI,WAAWG,WAAW;oBAIrC;oBACA,OAAOF,YAAYF,GAAG,CAAC,CAACG,MAAME,sBAC5B,MAAC5B;;8CACC,MAACA;oCAAKqB,QAAQ;;wCAAC;wCAAEG,WAAWG,WAAW;wCAAC;;;gCAAQ;gCAAED,KAAKG,IAAI;;2BADlD,GAAGL,WAAWG,WAAW,CAAC,CAAC,EAAEC,OAAO;gBAInD;;;;AAIR,GAAG"}
|