spawn-term 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/cjs/components/App.js +25 -6
  2. package/dist/cjs/components/App.js.map +1 -1
  3. package/dist/cjs/components/ChildProcess.js +2 -17
  4. package/dist/cjs/components/ChildProcess.js.map +1 -1
  5. package/dist/cjs/components/CompactProcessLine.js +27 -36
  6. package/dist/cjs/components/CompactProcessLine.js.map +1 -1
  7. package/dist/cjs/components/StatusBar.js +4 -19
  8. package/dist/cjs/components/StatusBar.js.map +1 -1
  9. package/dist/cjs/constants.js +18 -0
  10. package/dist/cjs/constants.js.map +1 -1
  11. package/dist/cjs/lib/clipText.js +56 -0
  12. package/dist/cjs/lib/clipText.js.map +1 -0
  13. package/dist/cjs/session.js +43 -10
  14. package/dist/cjs/session.js.map +1 -1
  15. package/dist/cjs/src/constants.d.ts +4 -0
  16. package/dist/cjs/src/lib/clipText.d.ts +9 -0
  17. package/dist/cjs/src/state/processStore.d.ts +5 -2
  18. package/dist/cjs/state/processStore.js +20 -2
  19. package/dist/cjs/state/processStore.js.map +1 -1
  20. package/dist/esm/components/App.js +30 -9
  21. package/dist/esm/components/App.js.map +1 -1
  22. package/dist/esm/components/ChildProcess.js +1 -16
  23. package/dist/esm/components/ChildProcess.js.map +1 -1
  24. package/dist/esm/components/CompactProcessLine.js +26 -35
  25. package/dist/esm/components/CompactProcessLine.js.map +1 -1
  26. package/dist/esm/components/StatusBar.js +3 -18
  27. package/dist/esm/components/StatusBar.js.map +1 -1
  28. package/dist/esm/constants.js +16 -0
  29. package/dist/esm/constants.js.map +1 -1
  30. package/dist/esm/lib/clipText.js +37 -0
  31. package/dist/esm/lib/clipText.js.map +1 -0
  32. package/dist/esm/session.js +38 -8
  33. package/dist/esm/session.js.map +1 -1
  34. package/dist/esm/src/constants.d.ts +4 -0
  35. package/dist/esm/src/lib/clipText.d.ts +9 -0
  36. package/dist/esm/src/state/processStore.d.ts +5 -2
  37. package/dist/esm/state/processStore.js +18 -2
  38. package/dist/esm/state/processStore.js.map +1 -1
  39. package/package.json +1 -1
@@ -109,6 +109,8 @@ var SessionImpl = /*#__PURE__*/ function() {
109
109
  this.closed = false;
110
110
  this.waitCallbacks = [];
111
111
  this.store = new _processStorets.ProcessStore(options);
112
+ var _options_interactive;
113
+ this.isInteractive = (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false;
112
114
  // Render Ink app immediately
113
115
  this.inkApp = (0, _ink.render)(/*#__PURE__*/ (0, _jsxruntime.jsx)(_Appts.default, {
114
116
  store: this.store
@@ -244,25 +246,52 @@ var SessionImpl = /*#__PURE__*/ function() {
244
246
  this.cleanup();
245
247
  };
246
248
  _proto.waitAndClose = function waitAndClose(callback) {
249
+ var _this = this;
247
250
  if (this.closed) {
248
251
  callback === null || callback === void 0 ? void 0 : callback();
249
252
  return;
250
253
  }
254
+ if (callback) this.waitCallbacks.push(callback);
251
255
  if (this.runningCount === 0) {
252
- this.close();
253
- callback === null || callback === void 0 ? void 0 : callback();
254
- } else {
255
- if (callback) this.waitCallbacks.push(callback);
256
- // Will close when runningCount hits 0
256
+ if (this.isInteractive) {
257
+ // In interactive mode, wait for user to quit (press 'q')
258
+ var unsubscribe = this.store.subscribe(function() {
259
+ if (_this.store.getShouldExit()) {
260
+ unsubscribe();
261
+ _this.closeAndCallWaitCallbacks();
262
+ }
263
+ });
264
+ } else {
265
+ this.closeAndCallWaitCallbacks();
266
+ }
257
267
  }
268
+ // If runningCount > 0, will close when it hits 0 in onProcessComplete
258
269
  };
259
270
  _proto.onProcessComplete = function onProcessComplete() {
271
+ var _this = this;
260
272
  this.runningCount--;
261
273
  if (this.runningCount === 0 && this.waitCallbacks.length > 0) {
262
- this.close();
274
+ if (this.isInteractive) {
275
+ // In interactive mode, wait for user to quit (press 'q')
276
+ var unsubscribe = this.store.subscribe(function() {
277
+ if (_this.store.getShouldExit()) {
278
+ unsubscribe();
279
+ _this.closeAndCallWaitCallbacks();
280
+ }
281
+ });
282
+ } else {
283
+ this.closeAndCallWaitCallbacks();
284
+ }
285
+ }
286
+ };
287
+ _proto.closeAndCallWaitCallbacks = function closeAndCallWaitCallbacks() {
288
+ var _this = this;
289
+ if (this.closed) return;
290
+ this.closed = true;
291
+ this.cleanup(function() {
263
292
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
264
293
  try {
265
- for(var _iterator = this.waitCallbacks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
294
+ for(var _iterator = _this.waitCallbacks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
266
295
  var cb = _step.value;
267
296
  cb();
268
297
  }
@@ -280,10 +309,10 @@ var SessionImpl = /*#__PURE__*/ function() {
280
309
  }
281
310
  }
282
311
  }
283
- this.waitCallbacks = [];
284
- }
312
+ _this.waitCallbacks = [];
313
+ });
285
314
  };
286
- _proto.cleanup = function cleanup() {
315
+ _proto.cleanup = function cleanup(onComplete) {
287
316
  var _this = this;
288
317
  // Signal exit to React component
289
318
  this.store.signalExit(function() {
@@ -295,11 +324,15 @@ var SessionImpl = /*#__PURE__*/ function() {
295
324
  this.inkApp.waitUntilExit().then(function() {
296
325
  var cb = _this.store.getExitCallback();
297
326
  cb === null || cb === void 0 ? void 0 : cb();
327
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete();
298
328
  }).catch(function() {
299
329
  var cb = _this.store.getExitCallback();
300
330
  cb === null || cb === void 0 ? void 0 : cb();
331
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete();
301
332
  });
302
333
  this.inkApp = null;
334
+ } else {
335
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete();
303
336
  }
304
337
  };
305
338
  return SessionImpl;
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/session.tsx"],"sourcesContent":["import spawn, { crossSpawn, type SpawnResult } from 'cross-spawn-cb';\nimport crypto from 'crypto';\nimport { render } from 'ink';\nimport oo from 'on-one';\nimport Queue from 'queue-cb';\n\nimport App from './components/App.ts';\nimport { DEFAULT_MAX_FPS } from './constants.ts';\nimport addLines from './lib/addLines.ts';\nimport concatWritable from './lib/concatWritable.ts';\nimport formatArguments from './lib/formatArguments.ts';\nimport { ProcessStore } from './state/processStore.ts';\nimport type { ProcessOptions, SessionOptions, SpawnError, SpawnOptions, TerminalCallback } from './types.ts';\nimport { LineType } from './types.ts';\n\nexport interface Session {\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void;\n close(): void;\n waitAndClose(callback?: () => void): void;\n}\n\nclass SessionImpl implements Session {\n private store: ProcessStore;\n private inkApp: ReturnType<typeof render> | null = null;\n private runningCount = 0;\n private closed = false;\n private waitCallbacks: (() => void)[] = [];\n\n constructor(options: SessionOptions = {}) {\n this.store = new ProcessStore(options);\n\n // Render Ink app immediately\n this.inkApp = render(<App store={this.store} />, {\n incrementalRendering: true,\n maxFps: DEFAULT_MAX_FPS,\n });\n }\n\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void {\n if (this.closed) {\n throw new Error('Session is closed');\n }\n\n const { encoding, stdio, ...csOptions } = spawnOptions;\n\n if (stdio === 'inherit') {\n this.runningCount++;\n const id = crypto.randomUUID();\n this.store.addProcess({\n id,\n title: [command].concat(formatArguments(args)).join(' '),\n state: 'running',\n lines: [],\n group: options.group,\n expanded: options.expanded,\n });\n\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof addLines> | null, stderr: null as ReturnType<typeof addLines> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = addLines((lines) => {\n this.store.appendLines(\n id,\n lines.map((text) => ({ type: LineType.stdout, text }))\n );\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = addLines((lines) => {\n this.store.appendLines(\n id,\n lines.map((text) => ({ type: LineType.stderr, text }))\n );\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n this.store.updateProcess(id, { state: err ? 'error' : 'success' });\n\n this.onProcessComplete();\n err ? callback(err) : callback(null, res);\n });\n } else {\n // Non-inherit mode: collect output but don't display in UI\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof concatWritable> | null, stderr: null as ReturnType<typeof concatWritable> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = concatWritable((output) => {\n (outputs.stdout as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = concatWritable((output) => {\n (outputs.stderr as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n err ? callback(err) : callback(null, res);\n });\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup();\n }\n\n waitAndClose(callback?: () => void): void {\n if (this.closed) {\n callback?.();\n return;\n }\n\n if (this.runningCount === 0) {\n this.close();\n callback?.();\n } else {\n if (callback) this.waitCallbacks.push(callback);\n // Will close when runningCount hits 0\n }\n }\n\n private onProcessComplete(): void {\n this.runningCount--;\n if (this.runningCount === 0 && this.waitCallbacks.length > 0) {\n this.close();\n for (const cb of this.waitCallbacks) cb();\n this.waitCallbacks = [];\n }\n }\n\n private cleanup(): void {\n // Signal exit to React component\n this.store.signalExit(() => {\n this.store.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n });\n\n // Wait for Ink to finish\n if (this.inkApp) {\n this.inkApp\n .waitUntilExit()\n .then(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n })\n .catch(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n });\n this.inkApp = null;\n }\n }\n}\n\nexport function createSession(options: SessionOptions = {}): Session {\n return new SessionImpl(options);\n}\n"],"names":["createSession","SessionImpl","options","inkApp","runningCount","closed","waitCallbacks","store","ProcessStore","render","App","incrementalRendering","maxFps","DEFAULT_MAX_FPS","spawn","command","args","spawnOptions","callback","Error","encoding","stdio","csOptions","id","crypto","randomUUID","addProcess","title","concat","formatArguments","join","state","lines","group","expanded","cp","crossSpawn","outputs","stdout","stderr","queue","Queue","addLines","appendLines","map","text","type","LineType","defer","oo","bind","pipe","worker","await","err","res","output","updateProcess","onProcessComplete","concatWritable","toString","close","cleanup","waitAndClose","push","length","cb","signalExit","reset","process","write","waitUntilExit","then","getExitCallback","catch"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/session.tsx"],"sourcesContent":["import spawn, { crossSpawn, type SpawnResult } from 'cross-spawn-cb';\nimport crypto from 'crypto';\nimport { render } from 'ink';\nimport oo from 'on-one';\nimport Queue from 'queue-cb';\n\nimport App from './components/App.ts';\nimport { DEFAULT_MAX_FPS } from './constants.ts';\nimport addLines from './lib/addLines.ts';\nimport concatWritable from './lib/concatWritable.ts';\nimport formatArguments from './lib/formatArguments.ts';\nimport { ProcessStore } from './state/processStore.ts';\nimport type { ProcessOptions, SessionOptions, SpawnError, SpawnOptions, TerminalCallback } from './types.ts';\nimport { LineType } from './types.ts';\n\nexport interface Session {\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void;\n close(): void;\n waitAndClose(callback?: () => void): void;\n}\n\nclass SessionImpl implements Session {\n private store: ProcessStore;\n private inkApp: ReturnType<typeof render> | null = null;\n private runningCount = 0;\n private closed = false;\n private waitCallbacks: (() => void)[] = [];\n private isInteractive: boolean;\n\n constructor(options: SessionOptions = {}) {\n this.store = new ProcessStore(options);\n this.isInteractive = options.interactive ?? false;\n\n // Render Ink app immediately\n this.inkApp = render(<App store={this.store} />, {\n incrementalRendering: true,\n maxFps: DEFAULT_MAX_FPS,\n });\n }\n\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void {\n if (this.closed) {\n throw new Error('Session is closed');\n }\n\n const { encoding, stdio, ...csOptions } = spawnOptions;\n\n if (stdio === 'inherit') {\n this.runningCount++;\n const id = crypto.randomUUID();\n this.store.addProcess({\n id,\n title: [command].concat(formatArguments(args)).join(' '),\n state: 'running',\n lines: [],\n group: options.group,\n expanded: options.expanded,\n });\n\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof addLines> | null, stderr: null as ReturnType<typeof addLines> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = addLines((lines) => {\n this.store.appendLines(\n id,\n lines.map((text) => ({ type: LineType.stdout, text }))\n );\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = addLines((lines) => {\n this.store.appendLines(\n id,\n lines.map((text) => ({ type: LineType.stderr, text }))\n );\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n this.store.updateProcess(id, { state: err ? 'error' : 'success' });\n\n this.onProcessComplete();\n err ? callback(err) : callback(null, res);\n });\n } else {\n // Non-inherit mode: collect output but don't display in UI\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof concatWritable> | null, stderr: null as ReturnType<typeof concatWritable> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = concatWritable((output) => {\n (outputs.stdout as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = concatWritable((output) => {\n (outputs.stderr as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n err ? callback(err) : callback(null, res);\n });\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup();\n }\n\n waitAndClose(callback?: () => void): void {\n if (this.closed) {\n callback?.();\n return;\n }\n\n if (callback) this.waitCallbacks.push(callback);\n\n if (this.runningCount === 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n // If runningCount > 0, will close when it hits 0 in onProcessComplete\n }\n\n private onProcessComplete(): void {\n this.runningCount--;\n if (this.runningCount === 0 && this.waitCallbacks.length > 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n }\n\n private closeAndCallWaitCallbacks(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup(() => {\n for (const cb of this.waitCallbacks) cb();\n this.waitCallbacks = [];\n });\n }\n\n private cleanup(onComplete?: () => void): void {\n // Signal exit to React component\n this.store.signalExit(() => {\n this.store.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n });\n\n // Wait for Ink to finish\n if (this.inkApp) {\n this.inkApp\n .waitUntilExit()\n .then(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n })\n .catch(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n });\n this.inkApp = null;\n } else {\n onComplete?.();\n }\n }\n}\n\nexport function createSession(options: SessionOptions = {}): Session {\n return new SessionImpl(options);\n}\n"],"names":["createSession","SessionImpl","options","inkApp","runningCount","closed","waitCallbacks","store","ProcessStore","isInteractive","interactive","render","App","incrementalRendering","maxFps","DEFAULT_MAX_FPS","spawn","command","args","spawnOptions","callback","Error","encoding","stdio","csOptions","id","crypto","randomUUID","addProcess","title","concat","formatArguments","join","state","lines","group","expanded","cp","crossSpawn","outputs","stdout","stderr","queue","Queue","addLines","appendLines","map","text","type","LineType","defer","oo","bind","pipe","worker","await","err","res","output","updateProcess","onProcessComplete","concatWritable","toString","close","cleanup","waitAndClose","push","unsubscribe","subscribe","getShouldExit","closeAndCallWaitCallbacks","length","cb","onComplete","signalExit","reset","process","write","waitUntilExit","then","getExitCallback","catch"],"mappings":";;;;+BA6MgBA;;;eAAAA;;;;oEA7MoC;6DACjC;mBACI;4DACR;8DACG;4DAEF;2BACgB;iEACX;uEACM;wEACC;8BACC;uBAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQzB,IAAA,AAAMC,4BAAN;;aAAMA;YAQQC,UAAAA,iEAA0B,CAAC;gCARnCD;aAEIE,SAA2C;aAC3CC,eAAe;aACfC,SAAS;aACTC,gBAAgC,EAAE;QAIxC,IAAI,CAACC,KAAK,GAAG,IAAIC,4BAAY,CAACN;YACTA;QAArB,IAAI,CAACO,aAAa,GAAGP,CAAAA,uBAAAA,QAAQQ,WAAW,cAAnBR,kCAAAA,uBAAuB;QAE5C,6BAA6B;QAC7B,IAAI,CAACC,MAAM,GAAGQ,IAAAA,WAAM,gBAAC,qBAACC,cAAG;YAACL,OAAO,IAAI,CAACA,KAAK;YAAM;YAC/CM,sBAAsB;YACtBC,QAAQC,4BAAe;QACzB;;iBAhBEd;IAmBJe,OAAAA,KA+EC,GA/EDA,SAAAA,MAAMC,OAAe,EAAEC,IAAc,EAAEC,YAA0B,EAAEjB,OAAuB,EAAEkB,QAA0B;;QACpH,IAAI,IAAI,CAACf,MAAM,EAAE;YACf,MAAM,IAAIgB,MAAM;QAClB;QAEA,IAAQC,WAAkCH,aAAlCG,UAAUC,QAAwBJ,aAAxBI,OAAUC,uCAAcL;YAAlCG;YAAUC;;QAElB,IAAIA,UAAU,WAAW;YACvB,IAAI,CAACnB,YAAY;YACjB,IAAMqB,KAAKC,eAAM,CAACC,UAAU;YAC5B,IAAI,CAACpB,KAAK,CAACqB,UAAU,CAAC;gBACpBH,IAAAA;gBACAI,OAAO;oBAACZ;iBAAQ,CAACa,MAAM,CAACC,IAAAA,0BAAe,EAACb,OAAOc,IAAI,CAAC;gBACpDC,OAAO;gBACPC,OAAO,EAAE;gBACTC,OAAOjC,QAAQiC,KAAK;gBACpBC,UAAUlC,QAAQkC,QAAQ;YAC5B;YAEA,IAAMC,KAAKC,IAAAA,wBAAU,EAACrB,SAASC,MAAMM;YACrC,IAAMe,UAAU;gBAAEC,QAAQ;gBAA4CC,QAAQ;YAA2C;YAEzH,IAAMC,QAAQ,IAAIC,gBAAK;YACvB,IAAIN,GAAGG,MAAM,EAAE;gBACbD,QAAQC,MAAM,GAAGI,IAAAA,mBAAQ,EAAC,SAACV;oBACzB,MAAK3B,KAAK,CAACsC,WAAW,CACpBpB,IACAS,MAAMY,GAAG,CAAC,SAACC;+BAAU;4BAAEC,MAAMC,iBAAQ,CAACT,MAAM;4BAAEO,MAAAA;wBAAK;;gBAEvD;gBACAL,MAAMQ,KAAK,CAACC,cAAE,CAACC,IAAI,CAAC,MAAMf,GAAGG,MAAM,CAACa,IAAI,CAACd,QAAQC,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACA,IAAIH,GAAGI,MAAM,EAAE;gBACbF,QAAQE,MAAM,GAAGG,IAAAA,mBAAQ,EAAC,SAACV;oBACzB,MAAK3B,KAAK,CAACsC,WAAW,CACpBpB,IACAS,MAAMY,GAAG,CAAC,SAACC;+BAAU;4BAAEC,MAAMC,iBAAQ,CAACR,MAAM;4BAAEM,MAAAA;wBAAK;;gBAEvD;gBACAL,MAAMQ,KAAK,CAACC,cAAE,CAACC,IAAI,CAAC,MAAMf,GAAGI,MAAM,CAACY,IAAI,CAACd,QAAQE,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACAC,MAAMQ,KAAK,CAAClC,qBAAK,CAACsC,MAAM,CAACF,IAAI,CAAC,MAAMf,IAAIb;YACxCkB,MAAMa,KAAK,CAAC,SAACC;gBACX,IAAMC,MAAOD,MAAMA,MAAM,CAAC;gBAC1BC,IAAIjB,MAAM,GAAGD,QAAQC,MAAM,GAAG,AAACD,QAAQC,MAAM,CAAmCkB,MAAM,GAAG;gBACzFD,IAAIhB,MAAM,GAAGF,QAAQE,MAAM,GAAG,AAACF,QAAQE,MAAM,CAAmCiB,MAAM,GAAG;gBACzFD,IAAIC,MAAM,GAAG;oBAACD,IAAIjB,MAAM;oBAAEiB,IAAIhB,MAAM;oBAAE;iBAAK;gBAC3C,MAAKlC,KAAK,CAACoD,aAAa,CAAClC,IAAI;oBAAEQ,OAAOuB,MAAM,UAAU;gBAAU;gBAEhE,MAAKI,iBAAiB;gBACtBJ,MAAMpC,SAASoC,OAAOpC,SAAS,MAAMqC;YACvC;QACF,OAAO;YACL,2DAA2D;YAC3D,IAAMpB,MAAKC,IAAAA,wBAAU,EAACrB,SAASC,MAAMM;YACrC,IAAMe,WAAU;gBAAEC,QAAQ;gBAAkDC,QAAQ;YAAiD;YAErI,IAAMC,SAAQ,IAAIC,gBAAK;YACvB,IAAIN,IAAGG,MAAM,EAAE;gBACbD,SAAQC,MAAM,GAAGqB,IAAAA,yBAAc,EAAC,SAACH;oBAC9BnB,SAAQC,MAAM,CAAmCkB,MAAM,GAAGA,OAAOI,QAAQ,CAACxC,YAAY;gBACzF;gBACAoB,OAAMQ,KAAK,CAACC,cAAE,CAACC,IAAI,CAAC,MAAMf,IAAGG,MAAM,CAACa,IAAI,CAACd,SAAQC,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACA,IAAIH,IAAGI,MAAM,EAAE;gBACbF,SAAQE,MAAM,GAAGoB,IAAAA,yBAAc,EAAC,SAACH;oBAC9BnB,SAAQE,MAAM,CAAmCiB,MAAM,GAAGA,OAAOI,QAAQ,CAACxC,YAAY;gBACzF;gBACAoB,OAAMQ,KAAK,CAACC,cAAE,CAACC,IAAI,CAAC,MAAMf,IAAGI,MAAM,CAACY,IAAI,CAACd,SAAQE,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACAC,OAAMQ,KAAK,CAAClC,qBAAK,CAACsC,MAAM,CAACF,IAAI,CAAC,MAAMf,KAAIb;YACxCkB,OAAMa,KAAK,CAAC,SAACC;gBACX,IAAMC,MAAOD,MAAMA,MAAM,CAAC;gBAC1BC,IAAIjB,MAAM,GAAGD,SAAQC,MAAM,GAAG,AAACD,SAAQC,MAAM,CAAmCkB,MAAM,GAAG;gBACzFD,IAAIhB,MAAM,GAAGF,SAAQE,MAAM,GAAG,AAACF,SAAQE,MAAM,CAAmCiB,MAAM,GAAG;gBACzFD,IAAIC,MAAM,GAAG;oBAACD,IAAIjB,MAAM;oBAAEiB,IAAIhB,MAAM;oBAAE;iBAAK;gBAC3Ce,MAAMpC,SAASoC,OAAOpC,SAAS,MAAMqC;YACvC;QACF;IACF;IAEAM,OAAAA,KAIC,GAJDA,SAAAA;QACE,IAAI,IAAI,CAAC1D,MAAM,EAAE;QACjB,IAAI,CAACA,MAAM,GAAG;QACd,IAAI,CAAC2D,OAAO;IACd;IAEAC,OAAAA,YAsBC,GAtBDA,SAAAA,aAAa7C,QAAqB;;QAChC,IAAI,IAAI,CAACf,MAAM,EAAE;YACfe,qBAAAA,+BAAAA;YACA;QACF;QAEA,IAAIA,UAAU,IAAI,CAACd,aAAa,CAAC4D,IAAI,CAAC9C;QAEtC,IAAI,IAAI,CAAChB,YAAY,KAAK,GAAG;YAC3B,IAAI,IAAI,CAACK,aAAa,EAAE;gBACtB,yDAAyD;gBACzD,IAAM0D,cAAc,IAAI,CAAC5D,KAAK,CAAC6D,SAAS,CAAC;oBACvC,IAAI,MAAK7D,KAAK,CAAC8D,aAAa,IAAI;wBAC9BF;wBACA,MAAKG,yBAAyB;oBAChC;gBACF;YACF,OAAO;gBACL,IAAI,CAACA,yBAAyB;YAChC;QACF;IACA,sEAAsE;IACxE;IAEA,OAAQV,iBAeP,GAfD,SAAQA;;QACN,IAAI,CAACxD,YAAY;QACjB,IAAI,IAAI,CAACA,YAAY,KAAK,KAAK,IAAI,CAACE,aAAa,CAACiE,MAAM,GAAG,GAAG;YAC5D,IAAI,IAAI,CAAC9D,aAAa,EAAE;gBACtB,yDAAyD;gBACzD,IAAM0D,cAAc,IAAI,CAAC5D,KAAK,CAAC6D,SAAS,CAAC;oBACvC,IAAI,MAAK7D,KAAK,CAAC8D,aAAa,IAAI;wBAC9BF;wBACA,MAAKG,yBAAyB;oBAChC;gBACF;YACF,OAAO;gBACL,IAAI,CAACA,yBAAyB;YAChC;QACF;IACF;IAEA,OAAQA,yBAOP,GAPD,SAAQA;;QACN,IAAI,IAAI,CAACjE,MAAM,EAAE;QACjB,IAAI,CAACA,MAAM,GAAG;QACd,IAAI,CAAC2D,OAAO,CAAC;gBACN,kCAAA,2BAAA;;gBAAL,QAAK,YAAY,MAAK1D,aAAa,qBAA9B,SAAA,6BAAA,QAAA,yBAAA;oBAAA,IAAMkE,KAAN;oBAAgCA;;;gBAAhC;gBAAA;;;yBAAA,6BAAA;wBAAA;;;wBAAA;8BAAA;;;;YACL,MAAKlE,aAAa,GAAG,EAAE;QACzB;IACF;IAEA,OAAQ0D,OAyBP,GAzBD,SAAQA,QAAQS,UAAuB;;QACrC,iCAAiC;QACjC,IAAI,CAAClE,KAAK,CAACmE,UAAU,CAAC;YACpB,MAAKnE,KAAK,CAACoE,KAAK;YAChBC,QAAQpC,MAAM,CAACqC,KAAK,CAAC,cAAc,cAAc;QACnD;QAEA,yBAAyB;QACzB,IAAI,IAAI,CAAC1E,MAAM,EAAE;YACf,IAAI,CAACA,MAAM,CACR2E,aAAa,GACbC,IAAI,CAAC;gBACJ,IAAMP,KAAK,MAAKjE,KAAK,CAACyE,eAAe;gBACrCR,eAAAA,yBAAAA;gBACAC,uBAAAA,iCAAAA;YACF,GACCQ,KAAK,CAAC;gBACL,IAAMT,KAAK,MAAKjE,KAAK,CAACyE,eAAe;gBACrCR,eAAAA,yBAAAA;gBACAC,uBAAAA,iCAAAA;YACF;YACF,IAAI,CAACtE,MAAM,GAAG;QAChB,OAAO;YACLsE,uBAAAA,iCAAAA;QACF;IACF;WArLIxE;;AAwLC,SAASD;QAAcE,UAAAA,iEAA0B,CAAC;IACvD,OAAO,IAAID,YAAYC;AACzB"}
@@ -5,3 +5,7 @@ export declare const BATCH_MAX_LINES = 20;
5
5
  export declare const BATCH_MAX_WAIT_MS = 50;
6
6
  export declare const DEFAULT_MAX_FPS = 20;
7
7
  export declare const EXPANDED_MAX_VISIBLE_LINES = 10;
8
+ export declare const SPINNER: {
9
+ interval: number;
10
+ frames: string[];
11
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Get the visible length of a string, ignoring ANSI escape codes.
3
+ */
4
+ export declare function visibleLength(str: string): number;
5
+ /**
6
+ * Clip text to a maximum visible width, accounting for ANSI escape codes.
7
+ * Adds ellipsis (…) if truncated.
8
+ */
9
+ export declare function clipText(str: string, maxWidth: number): string;
@@ -12,6 +12,7 @@ export declare class ProcessStore {
12
12
  private selectedErrorIndex;
13
13
  private expandedId;
14
14
  private scrollOffset;
15
+ private listScrollOffset;
15
16
  private header;
16
17
  private showStatusBar;
17
18
  private isInteractive;
@@ -31,6 +32,7 @@ export declare class ProcessStore {
31
32
  getSelectedErrorIndex: () => number;
32
33
  getExpandedId: () => string | null;
33
34
  getScrollOffset: () => number;
35
+ getListScrollOffset: () => number;
34
36
  getHeader: () => string | undefined;
35
37
  getShowStatusBar: () => boolean;
36
38
  getIsInteractive: () => boolean;
@@ -40,8 +42,9 @@ export declare class ProcessStore {
40
42
  appendLines(id: string, newLines: Line[]): void;
41
43
  getProcess(id: string): ChildProcess | undefined;
42
44
  setMode(mode: Mode): void;
43
- selectNext(): void;
44
- selectPrev(): void;
45
+ selectNext(visibleCount?: number): void;
46
+ selectPrev(visibleCount?: number): void;
47
+ private adjustListScroll;
45
48
  getSelectedProcess(): ChildProcess | undefined;
46
49
  selectNextError(): void;
47
50
  selectPrevError(): void;
@@ -85,6 +85,7 @@ var ProcessStore = /*#__PURE__*/ function() {
85
85
  this.selectedErrorIndex = 0;
86
86
  this.expandedId = null;
87
87
  this.scrollOffset = 0;
88
+ this.listScrollOffset = 0; // Viewport offset for process list
88
89
  this.showStatusBar = false;
89
90
  this.isInteractive = false;
90
91
  // useSyncExternalStore API
@@ -166,6 +167,9 @@ var ProcessStore = /*#__PURE__*/ function() {
166
167
  this.getScrollOffset = function() {
167
168
  return _this.scrollOffset;
168
169
  };
170
+ this.getListScrollOffset = function() {
171
+ return _this.listScrollOffset;
172
+ };
169
173
  // Session-level getters (set at session creation, immutable)
170
174
  this.getHeader = function() {
171
175
  return _this.header;
@@ -244,18 +248,31 @@ var ProcessStore = /*#__PURE__*/ function() {
244
248
  this.notify();
245
249
  };
246
250
  // Interactive mode navigation
247
- _proto.selectNext = function selectNext() {
251
+ _proto.selectNext = function selectNext(visibleCount) {
248
252
  if (this.processes.length > 0) {
249
253
  this.selectedIndex = (this.selectedIndex + 1) % this.processes.length;
254
+ this.adjustListScroll(visibleCount);
250
255
  this.notify();
251
256
  }
252
257
  };
253
- _proto.selectPrev = function selectPrev() {
258
+ _proto.selectPrev = function selectPrev(visibleCount) {
254
259
  if (this.processes.length > 0) {
255
260
  this.selectedIndex = (this.selectedIndex - 1 + this.processes.length) % this.processes.length;
261
+ this.adjustListScroll(visibleCount);
256
262
  this.notify();
257
263
  }
258
264
  };
265
+ _proto.adjustListScroll = function adjustListScroll(visibleCount) {
266
+ if (!visibleCount || visibleCount <= 0) return;
267
+ // Ensure selected item is visible in viewport
268
+ if (this.selectedIndex < this.listScrollOffset) {
269
+ // Selected is above viewport - scroll up
270
+ this.listScrollOffset = this.selectedIndex;
271
+ } else if (this.selectedIndex >= this.listScrollOffset + visibleCount) {
272
+ // Selected is below viewport - scroll down
273
+ this.listScrollOffset = this.selectedIndex - visibleCount + 1;
274
+ }
275
+ };
259
276
  _proto.getSelectedProcess = function getSelectedProcess() {
260
277
  return this.processes[this.selectedIndex];
261
278
  };
@@ -330,6 +347,7 @@ var ProcessStore = /*#__PURE__*/ function() {
330
347
  this.selectedErrorIndex = 0;
331
348
  this.expandedId = null;
332
349
  this.scrollOffset = 0;
350
+ this.listScrollOffset = 0;
333
351
  this.header = undefined;
334
352
  };
335
353
  _proto.notify = function notify() {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/state/processStore.ts"],"sourcesContent":["import { DEFAULT_COLUMN_WIDTH } from '../constants.ts';\nimport type { ChildProcess, Line, SessionOptions } from '../types.ts';\nimport { LineType } from '../types.ts';\n\ntype Listener = () => void;\ntype Mode = 'normal' | 'interactive' | 'errorList' | 'errorDetail';\n\nexport class ProcessStore {\n private processes: ChildProcess[] = [];\n private completedIds: string[] = []; // Track completion order\n private listeners = new Set<Listener>();\n private shouldExit = false;\n private exitCallback: (() => void) | null = null;\n\n // UI state\n private mode: Mode = 'normal';\n private selectedIndex = 0;\n private selectedErrorIndex = 0;\n private expandedId: string | null = null;\n private scrollOffset = 0;\n\n // Session-level display settings (set once at session creation)\n private header: string | undefined;\n private showStatusBar = false;\n private isInteractive = false;\n\n constructor(options: SessionOptions = {}) {\n this.header = options.header;\n this.showStatusBar = options.showStatusBar ?? false;\n this.isInteractive = options.interactive ?? false;\n }\n\n // useSyncExternalStore API\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n getSnapshot = (): ChildProcess[] => this.processes;\n\n // Filtered getters\n getRunningProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'running');\n };\n\n getCompletedProcesses = (): ChildProcess[] => {\n // Return in completion order\n return this.completedIds.map((id) => this.processes.find((p) => p.id === id)).filter((p): p is ChildProcess => p !== undefined);\n };\n\n getFailedProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'error');\n };\n\n // Counts\n getRunningCount = (): number => this.processes.filter((p) => p.state === 'running').length;\n getMaxGroupLength = (): number => {\n if (this.processes.length === 0) return DEFAULT_COLUMN_WIDTH;\n return Math.max(...this.processes.map((p) => (p.group || p.title).length));\n };\n getDoneCount = (): number => this.processes.filter((p) => p.state !== 'running').length;\n getErrorCount = (): number => this.processes.filter((p) => p.state === 'error').length;\n getErrorLineCount = (): number => {\n return this.processes.filter((p) => p.state === 'error').reduce((total, p) => total + p.lines.filter((l) => l.type === LineType.stderr).length, 0);\n };\n\n // UI state getters\n getMode = (): Mode => this.mode;\n getSelectedIndex = (): number => this.selectedIndex;\n getSelectedErrorIndex = (): number => this.selectedErrorIndex;\n getExpandedId = (): string | null => this.expandedId;\n getScrollOffset = (): number => this.scrollOffset;\n // Session-level getters (set at session creation, immutable)\n getHeader = (): string | undefined => this.header;\n getShowStatusBar = (): boolean => this.showStatusBar;\n getIsInteractive = (): boolean => this.isInteractive;\n isAllComplete = (): boolean => this.processes.length > 0 && this.processes.every((p) => p.state !== 'running');\n\n // Mutations - Ink handles render throttling at 30 FPS\n addProcess(process: ChildProcess): void {\n this.processes = [...this.processes, process];\n this.notify();\n }\n\n updateProcess(id: string, update: Partial<ChildProcess>): void {\n const oldProcess = this.processes.find((p) => p.id === id);\n const wasRunning = oldProcess?.state === 'running';\n const isNowComplete = update.state && update.state !== 'running';\n\n this.processes = this.processes.map((p) => (p.id === id ? { ...p, ...update } : p));\n\n // Track completion order\n if (wasRunning && isNowComplete && !this.completedIds.includes(id)) {\n this.completedIds = [...this.completedIds, id];\n }\n\n this.notify();\n }\n\n appendLines(id: string, newLines: Line[]): void {\n const process = this.processes.find((p) => p.id === id);\n if (process) {\n this.updateProcess(id, { lines: process.lines.concat(newLines) });\n }\n }\n\n getProcess(id: string): ChildProcess | undefined {\n return this.processes.find((p) => p.id === id);\n }\n\n // UI state mutations\n setMode(mode: Mode): void {\n this.mode = mode;\n if (mode === 'interactive') {\n this.selectedIndex = 0;\n } else if (mode === 'errorList') {\n this.selectedErrorIndex = 0;\n }\n this.notify();\n }\n\n // Interactive mode navigation\n selectNext(): void {\n if (this.processes.length > 0) {\n this.selectedIndex = (this.selectedIndex + 1) % this.processes.length;\n this.notify();\n }\n }\n\n selectPrev(): void {\n if (this.processes.length > 0) {\n this.selectedIndex = (this.selectedIndex - 1 + this.processes.length) % this.processes.length;\n this.notify();\n }\n }\n\n getSelectedProcess(): ChildProcess | undefined {\n return this.processes[this.selectedIndex];\n }\n\n selectNextError(): void {\n const failed = this.getFailedProcesses();\n if (failed.length > 0) {\n this.selectedErrorIndex = (this.selectedErrorIndex + 1) % failed.length;\n this.notify();\n }\n }\n\n selectPrevError(): void {\n const failed = this.getFailedProcesses();\n if (failed.length > 0) {\n this.selectedErrorIndex = (this.selectedErrorIndex - 1 + failed.length) % failed.length;\n this.notify();\n }\n }\n\n getSelectedError(): ChildProcess | undefined {\n const failed = this.getFailedProcesses();\n return failed[this.selectedErrorIndex];\n }\n\n // Expansion methods\n toggleExpand(): void {\n const selected = this.getSelectedProcess();\n if (!selected) return;\n\n if (this.expandedId === selected.id) {\n // Collapse\n this.expandedId = null;\n this.scrollOffset = 0;\n } else {\n // Expand\n this.expandedId = selected.id;\n this.scrollOffset = 0;\n }\n this.notify();\n }\n\n collapse(): void {\n this.expandedId = null;\n this.scrollOffset = 0;\n this.notify();\n }\n\n scrollDown(maxVisible: number): void {\n if (!this.expandedId) return;\n const process = this.getProcess(this.expandedId);\n if (!process) return;\n\n const maxOffset = Math.max(0, process.lines.length - maxVisible);\n if (this.scrollOffset < maxOffset) {\n this.scrollOffset++;\n this.notify();\n }\n }\n\n scrollUp(): void {\n if (!this.expandedId) return;\n if (this.scrollOffset > 0) {\n this.scrollOffset--;\n this.notify();\n }\n }\n\n // Exit signaling\n signalExit(callback: () => void): void {\n this.shouldExit = true;\n this.exitCallback = callback;\n this.notify();\n }\n\n getShouldExit = (): boolean => this.shouldExit;\n getExitCallback = (): (() => void) | null => this.exitCallback;\n\n reset(): void {\n this.processes = [];\n this.completedIds = [];\n this.shouldExit = false;\n this.exitCallback = null;\n this.mode = 'normal';\n this.selectedIndex = 0;\n this.selectedErrorIndex = 0;\n this.expandedId = null;\n this.scrollOffset = 0;\n this.header = undefined;\n }\n\n private notify(): void {\n this.listeners.forEach((l) => {\n l();\n });\n }\n}\n\n// Note: No global singleton - session creates its own store instance\n"],"names":["ProcessStore","options","processes","completedIds","listeners","Set","shouldExit","exitCallback","mode","selectedIndex","selectedErrorIndex","expandedId","scrollOffset","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","getHeader","header","getShowStatusBar","getIsInteractive","isAllComplete","every","getShouldExit","getExitCallback","interactive","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":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/state/processStore.ts"],"sourcesContent":["import { DEFAULT_COLUMN_WIDTH } from '../constants.ts';\nimport type { ChildProcess, Line, SessionOptions } from '../types.ts';\nimport { LineType } from '../types.ts';\n\ntype Listener = () => void;\ntype Mode = 'normal' | 'interactive' | 'errorList' | 'errorDetail';\n\nexport class ProcessStore {\n private processes: ChildProcess[] = [];\n private completedIds: string[] = []; // Track completion order\n private listeners = new Set<Listener>();\n private shouldExit = false;\n private exitCallback: (() => void) | null = null;\n\n // UI state\n private mode: Mode = 'normal';\n private selectedIndex = 0;\n private selectedErrorIndex = 0;\n private expandedId: string | null = null;\n private scrollOffset = 0;\n private listScrollOffset = 0; // Viewport offset for process list\n\n // Session-level display settings (set once at session creation)\n private header: string | undefined;\n private showStatusBar = false;\n private isInteractive = false;\n\n constructor(options: SessionOptions = {}) {\n this.header = options.header;\n this.showStatusBar = options.showStatusBar ?? false;\n this.isInteractive = options.interactive ?? false;\n }\n\n // useSyncExternalStore API\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n getSnapshot = (): ChildProcess[] => this.processes;\n\n // Filtered getters\n getRunningProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'running');\n };\n\n getCompletedProcesses = (): ChildProcess[] => {\n // Return in completion order\n return this.completedIds.map((id) => this.processes.find((p) => p.id === id)).filter((p): p is ChildProcess => p !== undefined);\n };\n\n getFailedProcesses = (): ChildProcess[] => {\n return this.processes.filter((p) => p.state === 'error');\n };\n\n // Counts\n getRunningCount = (): number => this.processes.filter((p) => p.state === 'running').length;\n getMaxGroupLength = (): number => {\n if (this.processes.length === 0) return DEFAULT_COLUMN_WIDTH;\n return Math.max(...this.processes.map((p) => (p.group || p.title).length));\n };\n getDoneCount = (): number => this.processes.filter((p) => p.state !== 'running').length;\n getErrorCount = (): number => this.processes.filter((p) => p.state === 'error').length;\n getErrorLineCount = (): number => {\n return this.processes.filter((p) => p.state === 'error').reduce((total, p) => total + p.lines.filter((l) => l.type === LineType.stderr).length, 0);\n };\n\n // UI state getters\n getMode = (): Mode => this.mode;\n getSelectedIndex = (): number => this.selectedIndex;\n getSelectedErrorIndex = (): number => this.selectedErrorIndex;\n getExpandedId = (): string | null => this.expandedId;\n getScrollOffset = (): number => this.scrollOffset;\n getListScrollOffset = (): number => this.listScrollOffset;\n // Session-level getters (set at session creation, immutable)\n getHeader = (): string | undefined => this.header;\n getShowStatusBar = (): boolean => this.showStatusBar;\n getIsInteractive = (): boolean => this.isInteractive;\n isAllComplete = (): boolean => this.processes.length > 0 && this.processes.every((p) => p.state !== 'running');\n\n // Mutations - Ink handles render throttling at 30 FPS\n addProcess(process: ChildProcess): void {\n this.processes = [...this.processes, process];\n this.notify();\n }\n\n updateProcess(id: string, update: Partial<ChildProcess>): void {\n const oldProcess = this.processes.find((p) => p.id === id);\n const wasRunning = oldProcess?.state === 'running';\n const isNowComplete = update.state && update.state !== 'running';\n\n this.processes = this.processes.map((p) => (p.id === id ? { ...p, ...update } : p));\n\n // Track completion order\n if (wasRunning && isNowComplete && !this.completedIds.includes(id)) {\n this.completedIds = [...this.completedIds, id];\n }\n\n this.notify();\n }\n\n appendLines(id: string, newLines: Line[]): void {\n const process = this.processes.find((p) => p.id === id);\n if (process) {\n this.updateProcess(id, { lines: process.lines.concat(newLines) });\n }\n }\n\n getProcess(id: string): ChildProcess | undefined {\n return this.processes.find((p) => p.id === id);\n }\n\n // UI state mutations\n setMode(mode: Mode): void {\n this.mode = mode;\n if (mode === 'interactive') {\n this.selectedIndex = 0;\n } else if (mode === 'errorList') {\n this.selectedErrorIndex = 0;\n }\n this.notify();\n }\n\n // Interactive mode navigation\n selectNext(visibleCount?: number): void {\n if (this.processes.length > 0) {\n this.selectedIndex = (this.selectedIndex + 1) % this.processes.length;\n this.adjustListScroll(visibleCount);\n this.notify();\n }\n }\n\n selectPrev(visibleCount?: number): void {\n if (this.processes.length > 0) {\n this.selectedIndex = (this.selectedIndex - 1 + this.processes.length) % this.processes.length;\n this.adjustListScroll(visibleCount);\n this.notify();\n }\n }\n\n private adjustListScroll(visibleCount?: number): void {\n if (!visibleCount || visibleCount <= 0) return;\n\n // Ensure selected item is visible in viewport\n if (this.selectedIndex < this.listScrollOffset) {\n // Selected is above viewport - scroll up\n this.listScrollOffset = this.selectedIndex;\n } else if (this.selectedIndex >= this.listScrollOffset + visibleCount) {\n // Selected is below viewport - scroll down\n this.listScrollOffset = this.selectedIndex - visibleCount + 1;\n }\n }\n\n getSelectedProcess(): ChildProcess | undefined {\n return this.processes[this.selectedIndex];\n }\n\n selectNextError(): void {\n const failed = this.getFailedProcesses();\n if (failed.length > 0) {\n this.selectedErrorIndex = (this.selectedErrorIndex + 1) % failed.length;\n this.notify();\n }\n }\n\n selectPrevError(): void {\n const failed = this.getFailedProcesses();\n if (failed.length > 0) {\n this.selectedErrorIndex = (this.selectedErrorIndex - 1 + failed.length) % failed.length;\n this.notify();\n }\n }\n\n getSelectedError(): ChildProcess | undefined {\n const failed = this.getFailedProcesses();\n return failed[this.selectedErrorIndex];\n }\n\n // Expansion methods\n toggleExpand(): void {\n const selected = this.getSelectedProcess();\n if (!selected) return;\n\n if (this.expandedId === selected.id) {\n // Collapse\n this.expandedId = null;\n this.scrollOffset = 0;\n } else {\n // Expand\n this.expandedId = selected.id;\n this.scrollOffset = 0;\n }\n this.notify();\n }\n\n collapse(): void {\n this.expandedId = null;\n this.scrollOffset = 0;\n this.notify();\n }\n\n scrollDown(maxVisible: number): void {\n if (!this.expandedId) return;\n const process = this.getProcess(this.expandedId);\n if (!process) return;\n\n const maxOffset = Math.max(0, process.lines.length - maxVisible);\n if (this.scrollOffset < maxOffset) {\n this.scrollOffset++;\n this.notify();\n }\n }\n\n scrollUp(): void {\n if (!this.expandedId) return;\n if (this.scrollOffset > 0) {\n this.scrollOffset--;\n this.notify();\n }\n }\n\n // Exit signaling\n signalExit(callback: () => void): void {\n this.shouldExit = true;\n this.exitCallback = callback;\n this.notify();\n }\n\n getShouldExit = (): boolean => this.shouldExit;\n getExitCallback = (): (() => void) | null => this.exitCallback;\n\n reset(): void {\n this.processes = [];\n this.completedIds = [];\n this.shouldExit = false;\n this.exitCallback = null;\n this.mode = 'normal';\n this.selectedIndex = 0;\n this.selectedErrorIndex = 0;\n this.expandedId = null;\n this.scrollOffset = 0;\n this.listScrollOffset = 0;\n this.header = undefined;\n }\n\n private notify(): void {\n this.listeners.forEach((l) => {\n l();\n });\n }\n}\n\n// Note: No global singleton - session creates its own store instance\n"],"names":["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,6 +1,6 @@
1
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
- import { useEffect, useSyncExternalStore } from 'react';
2
+ import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';
3
+ import { useEffect, useMemo, useSyncExternalStore } from 'react';
4
4
  import { EXPANDED_MAX_VISIBLE_LINES } from '../constants.js';
5
5
  import { StoreContext } from '../state/StoreContext.js';
6
6
  import CompactProcessLine from './CompactProcessLine.js';
@@ -12,6 +12,8 @@ import StatusBar from './StatusBar.js';
12
12
  function AppContent({ store }) {
13
13
  const { exit } = useApp();
14
14
  const { isRawModeSupported } = useStdin();
15
+ const { stdout } = useStdout();
16
+ const terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
15
17
  // Subscribe to store state
16
18
  const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);
17
19
  const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);
@@ -20,10 +22,14 @@ function AppContent({ store }) {
20
22
  const selectedErrorIndex = useSyncExternalStore(store.subscribe, store.getSelectedErrorIndex);
21
23
  const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);
22
24
  const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);
25
+ const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);
23
26
  // Subscribed state that triggers re-renders
24
27
  const header = useSyncExternalStore(store.subscribe, store.getHeader);
25
28
  const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);
26
29
  const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);
30
+ // Calculate visible process count (reserve lines for header, divider, status bar)
31
+ const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0);
32
+ const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);
27
33
  // Derived state (computed from processes which is already subscribed)
28
34
  const failedProcesses = store.getFailedProcesses();
29
35
  const runningCount = store.getRunningCount();
@@ -70,25 +76,25 @@ function AppContent({ store }) {
70
76
  if (expandedId) {
71
77
  store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);
72
78
  } else {
73
- store.selectNext();
79
+ store.selectNext(visibleProcessCount);
74
80
  }
75
81
  } else if (key.upArrow) {
76
82
  if (expandedId) {
77
83
  store.scrollUp();
78
84
  } else {
79
- store.selectPrev();
85
+ store.selectPrev(visibleProcessCount);
80
86
  }
81
87
  } else if (input === 'j') {
82
88
  if (expandedId) {
83
89
  store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);
84
90
  } else {
85
- store.selectNext();
91
+ store.selectNext(visibleProcessCount);
86
92
  }
87
93
  } else if (input === 'k') {
88
94
  if (expandedId) {
89
95
  store.scrollUp();
90
96
  } else {
91
- store.selectPrev();
97
+ store.selectPrev(visibleProcessCount);
92
98
  }
93
99
  } else if (input === 'e' && errorCount > 0) {
94
100
  store.setMode('errorList');
@@ -115,6 +121,18 @@ function AppContent({ store }) {
115
121
  }, {
116
122
  isActive: isRawModeSupported === true
117
123
  });
124
+ // Slice processes to visible viewport in interactive mode (must be before early returns)
125
+ const visibleProcesses = useMemo(()=>{
126
+ if (mode === 'interactive') {
127
+ return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);
128
+ }
129
+ return processes;
130
+ }, [
131
+ processes,
132
+ mode,
133
+ listScrollOffset,
134
+ visibleProcessCount
135
+ ]);
118
136
  // Error list modal
119
137
  if (mode === 'errorList') {
120
138
  return /*#__PURE__*/ _jsx(ErrorListModal, {
@@ -149,19 +167,22 @@ function AppContent({ store }) {
149
167
  /*#__PURE__*/ _jsx(Divider, {})
150
168
  ]
151
169
  }),
152
- processes.map((item, index)=>/*#__PURE__*/ _jsxs(Box, {
170
+ visibleProcesses.map((item)=>{
171
+ const originalIndex = processes.indexOf(item);
172
+ return /*#__PURE__*/ _jsxs(Box, {
153
173
  flexDirection: "column",
154
174
  children: [
155
175
  /*#__PURE__*/ _jsx(CompactProcessLine, {
156
176
  item: item,
157
- isSelected: showSelection && index === selectedIndex
177
+ isSelected: showSelection && originalIndex === selectedIndex
158
178
  }),
159
179
  expandedId === item.id && /*#__PURE__*/ _jsx(ExpandedOutput, {
160
180
  lines: item.lines,
161
181
  scrollOffset: scrollOffset
162
182
  })
163
183
  ]
164
- }, item.id)),
184
+ }, item.id);
185
+ }),
165
186
  showStatusBar && processes.length > 0 && /*#__PURE__*/ _jsxs(_Fragment, {
166
187
  children: [
167
188
  /*#__PURE__*/ _jsx(Divider, {}),
@@ -1 +1 @@
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 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\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\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 // 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();\n }\n } else if (key.upArrow) {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev();\n }\n } else if (input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext();\n }\n } else if (input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev();\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 // 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 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\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","useEffect","useSyncExternalStore","EXPANDED_MAX_VISIBLE_LINES","StoreContext","CompactProcessLine","Divider","ErrorDetailModal","ErrorListModal","ExpandedOutput","StatusBar","AppContent","store","exit","isRawModeSupported","processes","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","selectedErrorIndex","getSelectedErrorIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","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","errors","totalErrorLines","selectedError","getSelectedError","error","currentIndex","totalErrors","length","showSelection","flexDirection","map","item","index","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 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 */}\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\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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"}
@@ -29,6 +29,7 @@ function _object_spread(target) {
29
29
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
30
30
  import { Box, Text } from 'ink';
31
31
  import { memo, useMemo } from 'react';
32
+ import { SPINNER } from '../constants.js';
32
33
  import ansiRegex from '../lib/ansiRegex.js';
33
34
  import figures from '../lib/figures.js';
34
35
  import { LineType } from '../types.js';
@@ -38,22 +39,6 @@ const BLANK_LINE = {
38
39
  type: LineType.stdout,
39
40
  text: ''
40
41
  };
41
- // From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2
42
- const SPINNER = {
43
- interval: 80,
44
- frames: [
45
- '⠋',
46
- '⠙',
47
- '⠹',
48
- '⠸',
49
- '⠼',
50
- '⠴',
51
- '⠦',
52
- '⠧',
53
- '⠇',
54
- '⠏'
55
- ]
56
- };
57
42
  const ICONS = {
58
43
  error: /*#__PURE__*/ _jsx(Text, {
59
44
  color: "red",
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ChildProcess.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo, useMemo } from 'react';\nimport ansiRegex from '../lib/ansiRegex.ts';\nimport figures from '../lib/figures.ts';\nimport type { ChildProcess as ChildProcessT, Line, State } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport Spinner from './Spinner.ts';\n\nconst REGEX_ANSI = ansiRegex();\nconst BLANK_LINE = { type: LineType.stdout, text: '' };\n\n// From: https://github.com/sindresorhus/cli-spinners/blob/00de8fbeee16fa49502fa4f687449f70f2c8ca2c/spinners.json#L2\nconst SPINNER = {\n interval: 80,\n frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],\n};\n\nconst ICONS = {\n error: <Text color=\"red\">{figures.cross}</Text>,\n success: <Text color=\"green\">{figures.tick}</Text>,\n running: <Spinner {...SPINNER} />,\n};\n\ntype ItemProps = {\n item: ChildProcessT;\n};\n\ntype HeaderProps = {\n group?: string;\n title: string;\n state: State;\n};\n\nconst Header = memo(\n function Header({ group, title, state }: HeaderProps) {\n const icon = ICONS[state];\n\n return (\n <Box>\n {icon}\n {group && <Text bold>{`${group}${figures.pointer} `}</Text>}\n <Text>{title}</Text>\n </Box>\n );\n },\n (a, b) => a.group === b.group && a.title === b.title && a.state === b.state\n);\n\ntype RunningSummaryProps = {\n line: Line;\n};\n\nconst RunningSummary = memo(function RunningSummary({ line }: RunningSummaryProps) {\n return (\n <Box marginLeft={2}>\n <Text color=\"gray\">{line.text.replace(REGEX_ANSI, '')}</Text>\n </Box>\n );\n});\n\ntype LinesProps = {\n lines: Line[];\n};\n\nconst renderLine = (line, index) => {\n return (\n <Box key={index} minHeight={1}>\n <Text>{line.text}</Text>\n </Box>\n );\n};\n\nconst Lines = memo(function Lines({ lines }: LinesProps) {\n return (\n <Box flexDirection=\"column\" marginLeft={2}>\n {lines.map(renderLine)}\n </Box>\n );\n});\n\nconst Expanded = memo(function Expanded({ item }: ItemProps) {\n const { lines } = item;\n\n return (\n <Box flexDirection=\"column\">\n <Header group={item.group} title={item.title} state={item.state} />\n <Lines lines={lines} />\n </Box>\n );\n});\n\nconst Contracted = memo(function Contracted({ item }: ItemProps) {\n const { state, lines } = item;\n\n // remove ansi codes when displaying single lines\n const errors = useMemo(() => lines.filter((line) => line.type === LineType.stderr), [lines]);\n const summary = useMemo(() => lines.filter((line) => line.text.length > 0 && errors.indexOf(line) < 0).pop(), [lines, errors]);\n\n return (\n <Box flexDirection=\"column\">\n <Header group={item.group} title={item.title} state={item.state} />\n {state === 'running' && <RunningSummary line={summary || BLANK_LINE} />}\n {errors.length > 0 && <Lines lines={errors} />}\n </Box>\n );\n});\n\nexport default memo(function ChildProcess({ item }: ItemProps) {\n const { expanded } = item;\n return expanded ? <Expanded item={item} /> : <Contracted item={item} />;\n});\n"],"names":["Box","Text","memo","useMemo","ansiRegex","figures","LineType","Spinner","REGEX_ANSI","BLANK_LINE","type","stdout","text","SPINNER","interval","frames","ICONS","error","color","cross","success","tick","running","Header","group","title","state","icon","bold","pointer","a","b","RunningSummary","line","marginLeft","replace","renderLine","index","minHeight","Lines","lines","flexDirection","map","Expanded","item","Contracted","errors","filter","stderr","summary","length","indexOf","pop","ChildProcess","expanded"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ChildProcess.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo, useMemo } from 'react';\nimport { SPINNER } from '../constants.ts';\nimport ansiRegex from '../lib/ansiRegex.ts';\nimport figures from '../lib/figures.ts';\nimport type { ChildProcess as ChildProcessT, Line, State } from '../types.ts';\nimport { LineType } from '../types.ts';\nimport Spinner from './Spinner.ts';\n\nconst REGEX_ANSI = ansiRegex();\nconst BLANK_LINE = { type: LineType.stdout, text: '' };\n\nconst ICONS = {\n error: <Text color=\"red\">{figures.cross}</Text>,\n success: <Text color=\"green\">{figures.tick}</Text>,\n running: <Spinner {...SPINNER} />,\n};\n\ntype ItemProps = {\n item: ChildProcessT;\n};\n\ntype HeaderProps = {\n group?: string;\n title: string;\n state: State;\n};\n\nconst Header = memo(\n function Header({ group, title, state }: HeaderProps) {\n const icon = ICONS[state];\n\n return (\n <Box>\n {icon}\n {group && <Text bold>{`${group}${figures.pointer} `}</Text>}\n <Text>{title}</Text>\n </Box>\n );\n },\n (a, b) => a.group === b.group && a.title === b.title && a.state === b.state\n);\n\ntype RunningSummaryProps = {\n line: Line;\n};\n\nconst RunningSummary = memo(function RunningSummary({ line }: RunningSummaryProps) {\n return (\n <Box marginLeft={2}>\n <Text color=\"gray\">{line.text.replace(REGEX_ANSI, '')}</Text>\n </Box>\n );\n});\n\ntype LinesProps = {\n lines: Line[];\n};\n\nconst renderLine = (line, index) => {\n return (\n <Box key={index} minHeight={1}>\n <Text>{line.text}</Text>\n </Box>\n );\n};\n\nconst Lines = memo(function Lines({ lines }: LinesProps) {\n return (\n <Box flexDirection=\"column\" marginLeft={2}>\n {lines.map(renderLine)}\n </Box>\n );\n});\n\nconst Expanded = memo(function Expanded({ item }: ItemProps) {\n const { lines } = item;\n\n return (\n <Box flexDirection=\"column\">\n <Header group={item.group} title={item.title} state={item.state} />\n <Lines lines={lines} />\n </Box>\n );\n});\n\nconst Contracted = memo(function Contracted({ item }: ItemProps) {\n const { state, lines } = item;\n\n // remove ansi codes when displaying single lines\n const errors = useMemo(() => lines.filter((line) => line.type === LineType.stderr), [lines]);\n const summary = useMemo(() => lines.filter((line) => line.text.length > 0 && errors.indexOf(line) < 0).pop(), [lines, errors]);\n\n return (\n <Box flexDirection=\"column\">\n <Header group={item.group} title={item.title} state={item.state} />\n {state === 'running' && <RunningSummary line={summary || BLANK_LINE} />}\n {errors.length > 0 && <Lines lines={errors} />}\n </Box>\n );\n});\n\nexport default memo(function ChildProcess({ item }: ItemProps) {\n const { expanded } = item;\n return expanded ? <Expanded item={item} /> : <Contracted item={item} />;\n});\n"],"names":["Box","Text","memo","useMemo","SPINNER","ansiRegex","figures","LineType","Spinner","REGEX_ANSI","BLANK_LINE","type","stdout","text","ICONS","error","color","cross","success","tick","running","Header","group","title","state","icon","bold","pointer","a","b","RunningSummary","line","marginLeft","replace","renderLine","index","minHeight","Lines","lines","flexDirection","map","Expanded","item","Contracted","errors","filter","stderr","summary","length","indexOf","pop","ChildProcess","expanded"],"mappings":"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"}