spawn-term 3.0.7 → 3.0.9

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.
@@ -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';\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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"}
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 // Page scrolling (scroll by maxVisible lines at once)\n scrollPageDown(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 this.scrollOffset = Math.min(this.scrollOffset + maxVisible, maxOffset);\n this.notify();\n }\n\n scrollPageUp(maxVisible: number): void {\n if (!this.expandedId) return;\n this.scrollOffset = Math.max(0, this.scrollOffset - maxVisible);\n this.notify();\n }\n\n // Jump to top/bottom\n scrollToTop(): void {\n if (!this.expandedId) return;\n this.scrollOffset = 0;\n this.notify();\n }\n\n scrollToBottom(maxVisible: number): void {\n if (!this.expandedId) return;\n const lineCount = this.getProcessLineCount(this.expandedId);\n if (lineCount === 0) return;\n\n this.scrollOffset = Math.max(0, lineCount - maxVisible);\n this.notify();\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","scrollPageDown","min","scrollPageUp","scrollToTop","scrollToBottom","signalExit","callback","reset","dispose","forEach","l"],"mappings":"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"}
@@ -76,25 +76,34 @@ function AppContent({ store }) {
76
76
  }
77
77
  } else if (key.return) {
78
78
  store.toggleExpand();
79
- } else if (key.downArrow) {
79
+ // Jump to top - Option+↑ (detected as meta), vim: g
80
+ // Must check meta+arrow BEFORE plain arrow
81
+ } else if (key.meta && key.upArrow || input === 'g') {
80
82
  if (expandedId) {
81
- store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);
82
- } else {
83
- store.selectNext(visibleProcessCount);
83
+ store.scrollToTop();
84
84
  }
85
- } else if (key.upArrow) {
85
+ // Jump to bottom - Option+↓ (detected as meta), vim: G
86
+ } else if (key.meta && key.downArrow || input === 'G') {
86
87
  if (expandedId) {
87
- store.scrollUp();
88
- } else {
89
- store.selectPrev(visibleProcessCount);
88
+ store.scrollToBottom(EXPANDED_MAX_VISIBLE_LINES);
89
+ }
90
+ // Page scrolling - Tab/Shift+Tab
91
+ } else if (key.tab && key.shift) {
92
+ if (expandedId) {
93
+ store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);
94
+ }
95
+ } else if (key.tab && !key.shift) {
96
+ if (expandedId) {
97
+ store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);
90
98
  }
91
- } else if (input === 'j') {
99
+ // Line scrolling - arrows and vim j/k
100
+ } else if (key.downArrow || input === 'j') {
92
101
  if (expandedId) {
93
102
  store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);
94
103
  } else {
95
104
  store.selectNext(visibleProcessCount);
96
105
  }
97
- } else if (input === 'k') {
106
+ } else if (key.upArrow || input === 'k') {
98
107
  if (expandedId) {
99
108
  store.scrollUp();
100
109
  } else {
@@ -119,13 +128,11 @@ function AppContent({ store }) {
119
128
  ]);
120
129
  // Normal/Interactive view - render in original registration order
121
130
  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
131
+ // Force full re-render when layout structure changes
132
+ // Note: scrollOffset is NOT included - scrolling within expansion doesn't change structure
125
133
  const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}`;
126
134
  return /*#__PURE__*/ _jsxs(Box, {
127
135
  flexDirection: "column",
128
- height: terminalHeight,
129
136
  children: [
130
137
  header && /*#__PURE__*/ _jsxs(_Fragment, {
131
138
  children: [
@@ -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 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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"}
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 // Jump to top - Option+↑ (detected as meta), vim: g\n // Must check meta+arrow BEFORE plain arrow\n } else if ((key.meta && key.upArrow) || input === 'g') {\n if (expandedId) {\n store.scrollToTop();\n }\n // Jump to bottom - Option+↓ (detected as meta), vim: G\n } else if ((key.meta && key.downArrow) || input === 'G') {\n if (expandedId) {\n store.scrollToBottom(EXPANDED_MAX_VISIBLE_LINES);\n }\n // Page scrolling - Tab/Shift+Tab\n } else if (key.tab && key.shift) {\n if (expandedId) {\n store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);\n }\n } else if (key.tab && !key.shift) {\n if (expandedId) {\n store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);\n }\n // Line scrolling - arrows and vim j/k\n } else if (key.downArrow || input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow || 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 structure changes\n // Note: scrollOffset is NOT included - scrolling within expansion doesn't change structure\n const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}`;\n\n return (\n <Box key={layoutKey} flexDirection=\"column\">\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","meta","upArrow","scrollToTop","downArrow","scrollToBottom","tab","shift","scrollPageUp","scrollPageDown","scrollDown","selectNext","scrollUp","selectPrev","isActive","visibleProcesses","slice","showSelection","layoutKey","flexDirection","map","item","originalIndex","indexOf","isSelected","id","lines","getProcessLines","length","running","done","errors","isExpanded","App","Provider","value"],"mappings":"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"}
@@ -30,11 +30,9 @@ 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
32
  import { SPINNER } from '../constants.js';
33
- import ansiRegex from '../lib/ansiRegex.js';
34
33
  import figures from '../lib/figures.js';
35
34
  import { LineType } from '../types.js';
36
35
  import Spinner from './Spinner.js';
37
- const REGEX_ANSI = ansiRegex();
38
36
  const BLANK_LINE = {
39
37
  type: LineType.stdout,
40
38
  text: ''
@@ -69,8 +67,7 @@ const RunningSummary = /*#__PURE__*/ memo(function RunningSummary({ line }) {
69
67
  return /*#__PURE__*/ _jsx(Box, {
70
68
  marginLeft: 2,
71
69
  children: /*#__PURE__*/ _jsx(Text, {
72
- color: "gray",
73
- children: line.text.replace(REGEX_ANSI, '')
70
+ children: line.text
74
71
  })
75
72
  });
76
73
  });
@@ -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 { 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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"}
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 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 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>{line.text}</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","figures","LineType","Spinner","BLANK_LINE","type","stdout","text","ICONS","error","color","cross","success","tick","running","Header","group","title","state","icon","bold","pointer","a","b","RunningSummary","line","marginLeft","renderLine","index","minHeight","Lines","lines","flexDirection","map","Expanded","item","Contracted","errors","filter","stderr","summary","length","indexOf","pop","ChildProcess","expanded"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
3
  import { memo } from 'react';
4
4
  import { EXPANDED_MAX_VISIBLE_LINES } from '../constants.js';
5
+ const isMac = process.platform === 'darwin';
5
6
  export default /*#__PURE__*/ memo(function ExpandedOutput({ lines, scrollOffset, maxVisible = EXPANDED_MAX_VISIBLE_LINES }) {
6
7
  const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);
7
8
  const hasMore = lines.length > scrollOffset + maxVisible;
@@ -31,7 +32,9 @@ export default /*#__PURE__*/ memo(function ExpandedOutput({ lines, scrollOffset,
31
32
  children: [
32
33
  "│ [+",
33
34
  remaining,
34
- " more, j/k to scroll]"
35
+ " more, Tab/⇧Tab page, ",
36
+ isMac ? '⌥↑/↓' : 'g/G',
37
+ " top/bottom]"
35
38
  ]
36
39
  })
37
40
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ExpandedOutput.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { Line } from '../types.ts';\n\ntype Props = {\n lines: Line[];\n scrollOffset: number;\n maxVisible?: number;\n};\n\nexport default memo(function ExpandedOutput({ lines, scrollOffset, maxVisible = EXPANDED_MAX_VISIBLE_LINES }: Props) {\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const hasMore = lines.length > scrollOffset + maxVisible;\n const remaining = lines.length - scrollOffset - maxVisible;\n\n if (lines.length === 0) {\n return (\n <Box paddingLeft={2}>\n <Text dimColor>│ (no output)</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n {visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>│ {line.text}</Text>\n ))}\n {hasMore && <Text dimColor>│ [+{remaining} more, j/k to scroll]</Text>}\n </Box>\n );\n});\n"],"names":["Box","Text","memo","EXPANDED_MAX_VISIBLE_LINES","ExpandedOutput","lines","scrollOffset","maxVisible","visibleLines","slice","hasMore","length","remaining","paddingLeft","dimColor","flexDirection","map","line","i","text"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,QAAQ,MAAM;AAChC,SAASC,IAAI,QAAQ,QAAQ;AAC7B,SAASC,0BAA0B,QAAQ,kBAAkB;AAS7D,6BAAeD,KAAK,SAASE,eAAe,EAAEC,KAAK,EAAEC,YAAY,EAAEC,aAAaJ,0BAA0B,EAAS;IACjH,MAAMK,eAAeH,MAAMI,KAAK,CAACH,cAAcA,eAAeC;IAC9D,MAAMG,UAAUL,MAAMM,MAAM,GAAGL,eAAeC;IAC9C,MAAMK,YAAYP,MAAMM,MAAM,GAAGL,eAAeC;IAEhD,IAAIF,MAAMM,MAAM,KAAK,GAAG;QACtB,qBACE,KAACX;YAAIa,aAAa;sBAChB,cAAA,KAACZ;gBAAKa,QAAQ;0BAAC;;;IAGrB;IAEA,qBACE,MAACd;QAAIe,eAAc;QAASF,aAAa;;YACtCL,aAAaQ,GAAG,CAAC,CAACC,MAAMC,IACvB,iHAAiH;8BACjH,MAACjB;;wBAA4B;wBAAGgB,KAAKE,IAAI;;mBAA9Bb,eAAeY;YAE3BR,yBAAW,MAACT;gBAAKa,QAAQ;;oBAAC;oBAAKF;oBAAU;;;;;AAGhD,GAAG"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/ExpandedOutput.tsx"],"sourcesContent":["import { Box, Text } from 'ink';\nimport { memo } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { Line } from '../types.ts';\n\nconst isMac = process.platform === 'darwin';\n\ntype Props = {\n lines: Line[];\n scrollOffset: number;\n maxVisible?: number;\n};\n\nexport default memo(function ExpandedOutput({ lines, scrollOffset, maxVisible = EXPANDED_MAX_VISIBLE_LINES }: Props) {\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const hasMore = lines.length > scrollOffset + maxVisible;\n const remaining = lines.length - scrollOffset - maxVisible;\n\n if (lines.length === 0) {\n return (\n <Box paddingLeft={2}>\n <Text dimColor>│ (no output)</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingLeft={2}>\n {visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>│ {line.text}</Text>\n ))}\n {hasMore && (\n <Text dimColor>\n │ [+{remaining} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom]\n </Text>\n )}\n </Box>\n );\n});\n"],"names":["Box","Text","memo","EXPANDED_MAX_VISIBLE_LINES","isMac","process","platform","ExpandedOutput","lines","scrollOffset","maxVisible","visibleLines","slice","hasMore","length","remaining","paddingLeft","dimColor","flexDirection","map","line","i","text"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,QAAQ,MAAM;AAChC,SAASC,IAAI,QAAQ,QAAQ;AAC7B,SAASC,0BAA0B,QAAQ,kBAAkB;AAG7D,MAAMC,QAAQC,QAAQC,QAAQ,KAAK;AAQnC,6BAAeJ,KAAK,SAASK,eAAe,EAAEC,KAAK,EAAEC,YAAY,EAAEC,aAAaP,0BAA0B,EAAS;IACjH,MAAMQ,eAAeH,MAAMI,KAAK,CAACH,cAAcA,eAAeC;IAC9D,MAAMG,UAAUL,MAAMM,MAAM,GAAGL,eAAeC;IAC9C,MAAMK,YAAYP,MAAMM,MAAM,GAAGL,eAAeC;IAEhD,IAAIF,MAAMM,MAAM,KAAK,GAAG;QACtB,qBACE,KAACd;YAAIgB,aAAa;sBAChB,cAAA,KAACf;gBAAKgB,QAAQ;0BAAC;;;IAGrB;IAEA,qBACE,MAACjB;QAAIkB,eAAc;QAASF,aAAa;;YACtCL,aAAaQ,GAAG,CAAC,CAACC,MAAMC,IACvB,iHAAiH;8BACjH,MAACpB;;wBAA4B;wBAAGmB,KAAKE,IAAI;;mBAA9Bb,eAAeY;YAE3BR,yBACC,MAACZ;gBAAKgB,QAAQ;;oBAAC;oBACRF;oBAAU;oBAAuBX,QAAQ,SAAS;oBAAM;;;;;AAKvE,GAAG"}
@@ -1,12 +1,100 @@
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
8
+ });
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _object_spread(target) {
15
+ for(var i = 1; i < arguments.length; i++){
16
+ var source = arguments[i] != null ? arguments[i] : {};
17
+ var ownKeys = Object.keys(source);
18
+ if (typeof Object.getOwnPropertySymbols === "function") {
19
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
20
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
21
+ }));
22
+ }
23
+ ownKeys.forEach(function(key) {
24
+ _define_property(target, key, source[key]);
25
+ });
26
+ }
27
+ return target;
28
+ }
1
29
  var _xterm_default;
2
30
  import * as xterm from '@xterm/headless';
3
31
  // Handle both ESM and CJS module formats
4
32
  const Terminal = xterm.Terminal || ((_xterm_default = xterm.default) === null || _xterm_default === void 0 ? void 0 : _xterm_default.Terminal);
5
- /**
6
- * Wrapper around @xterm/headless Terminal that provides a virtual terminal buffer.
7
- * Interprets ANSI escape sequences (cursor movement, line clearing, etc.) to produce
8
- * the actual rendered output rather than raw intermediate states.
9
- */ export class TerminalBuffer {
33
+ // ANSI color mode constants from xterm.js
34
+ const COLOR_MODE_DEFAULT = 0;
35
+ const COLOR_MODE_16 = 16777216; // 0x1000000 - 16 color palette (0-15)
36
+ const COLOR_MODE_256 = 33554432; // 0x2000000 - 256 color palette
37
+ const COLOR_MODE_RGB = 50331648; // 0x3000000 - 24-bit RGB
38
+ const DEFAULT_STYLE = {
39
+ fg: -1,
40
+ fgMode: COLOR_MODE_DEFAULT,
41
+ bg: -1,
42
+ bgMode: COLOR_MODE_DEFAULT,
43
+ bold: false,
44
+ dim: false,
45
+ italic: false,
46
+ underline: false,
47
+ inverse: false,
48
+ strikethrough: false
49
+ };
50
+ function styleEquals(a, b) {
51
+ return a.fg === b.fg && a.fgMode === b.fgMode && a.bg === b.bg && a.bgMode === b.bgMode && a.bold === b.bold && a.dim === b.dim && a.italic === b.italic && a.underline === b.underline && a.inverse === b.inverse && a.strikethrough === b.strikethrough;
52
+ }
53
+ function buildAnsiCode(style) {
54
+ const codes = [];
55
+ // Attributes
56
+ if (style.bold) codes.push(1);
57
+ if (style.dim) codes.push(2);
58
+ if (style.italic) codes.push(3);
59
+ if (style.underline) codes.push(4);
60
+ if (style.inverse) codes.push(7);
61
+ if (style.strikethrough) codes.push(9);
62
+ // Foreground color
63
+ if (style.fgMode === COLOR_MODE_16) {
64
+ // 16-color palette: 0-7 are 30-37, 8-15 are 90-97
65
+ if (style.fg < 8) {
66
+ codes.push(30 + style.fg);
67
+ } else {
68
+ codes.push(90 + (style.fg - 8));
69
+ }
70
+ } else if (style.fgMode === COLOR_MODE_256) {
71
+ codes.push(38, 5, style.fg);
72
+ } else if (style.fgMode === COLOR_MODE_RGB) {
73
+ // RGB is encoded in the color value
74
+ const r = style.fg >> 16 & 0xff;
75
+ const g = style.fg >> 8 & 0xff;
76
+ const b = style.fg & 0xff;
77
+ codes.push(38, 2, r, g, b);
78
+ }
79
+ // Background color
80
+ if (style.bgMode === COLOR_MODE_16) {
81
+ if (style.bg < 8) {
82
+ codes.push(40 + style.bg);
83
+ } else {
84
+ codes.push(100 + (style.bg - 8));
85
+ }
86
+ } else if (style.bgMode === COLOR_MODE_256) {
87
+ codes.push(48, 5, style.bg);
88
+ } else if (style.bgMode === COLOR_MODE_RGB) {
89
+ const r = style.bg >> 16 & 0xff;
90
+ const g = style.bg >> 8 & 0xff;
91
+ const b = style.bg & 0xff;
92
+ codes.push(48, 2, r, g, b);
93
+ }
94
+ if (codes.length === 0) return '';
95
+ return `\x1b[${codes.join(';')}m`;
96
+ }
97
+ export class TerminalBuffer {
10
98
  /**
11
99
  * Write raw data to the terminal buffer.
12
100
  * The terminal interprets all ANSI sequences automatically.
@@ -22,18 +110,66 @@ const Terminal = xterm.Terminal || ((_xterm_default = xterm.default) === null ||
22
110
  /**
23
111
  * Extract the rendered lines from the terminal buffer.
24
112
  * This returns the actual visible content after all ANSI sequences
25
- * have been processed.
113
+ * have been processed, with color codes preserved.
26
114
  */ getLines() {
27
115
  const buffer = this.terminal.buffer.active;
28
116
  const lines = [];
29
117
  for(let i = 0; i < buffer.length; i++){
30
- const line = buffer.getLine(i);
31
- if (line) {
32
- // translateToString(trimRight) - trim trailing whitespace
33
- // Also trim leading whitespace - tools like ncu/npm use cursor positioning
34
- // which creates lines with leading spaces when interpreted by xterm
35
- lines.push(line.translateToString(true).trimStart());
118
+ const bufferLine = buffer.getLine(i);
119
+ if (!bufferLine) continue;
120
+ let result = '';
121
+ let currentStyle = _object_spread({}, DEFAULT_STYLE);
122
+ let _hasContent = false;
123
+ // First pass: find the last non-empty cell to know where content ends
124
+ let lastContentIndex = -1;
125
+ for(let j = bufferLine.length - 1; j >= 0; j--){
126
+ const cell = bufferLine.getCell(j);
127
+ if (cell && cell.getChars()) {
128
+ lastContentIndex = j;
129
+ break;
130
+ }
131
+ }
132
+ // Second pass: build the line with ANSI codes
133
+ for(let j = 0; j <= lastContentIndex; j++){
134
+ const cell = bufferLine.getCell(j);
135
+ if (!cell) continue;
136
+ const char = cell.getChars();
137
+ const cellStyle = {
138
+ fg: cell.getFgColor(),
139
+ fgMode: cell.getFgColorMode(),
140
+ bg: cell.getBgColor(),
141
+ bgMode: cell.getBgColorMode(),
142
+ bold: cell.isBold() !== 0,
143
+ dim: cell.isDim() !== 0,
144
+ italic: cell.isItalic() !== 0,
145
+ underline: cell.isUnderline() !== 0,
146
+ inverse: cell.isInverse() !== 0,
147
+ strikethrough: cell.isStrikethrough() !== 0
148
+ };
149
+ // Check if style changed
150
+ if (!styleEquals(cellStyle, currentStyle)) {
151
+ // Reset if going back to default, otherwise emit new style
152
+ if (styleEquals(cellStyle, DEFAULT_STYLE)) {
153
+ result += '\x1b[0m';
154
+ } else {
155
+ // If we had styling before, reset first for clean transition
156
+ if (!styleEquals(currentStyle, DEFAULT_STYLE)) {
157
+ result += '\x1b[0m';
158
+ }
159
+ result += buildAnsiCode(cellStyle);
160
+ }
161
+ currentStyle = cellStyle;
162
+ }
163
+ result += char || ' ';
164
+ if (char) _hasContent = true;
165
+ }
166
+ // Reset at end of line if we had styling
167
+ if (!styleEquals(currentStyle, DEFAULT_STYLE)) {
168
+ result += '\x1b[0m';
36
169
  }
170
+ // Trim leading whitespace - tools like ncu/npm use cursor positioning
171
+ // which creates lines with leading spaces when interpreted by xterm
172
+ lines.push(result.trimStart());
37
173
  }
38
174
  // Trim trailing empty lines
39
175
  while(lines.length > 0 && lines[lines.length - 1] === ''){
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/lib/TerminalBuffer.ts"],"sourcesContent":["import * as xterm from '@xterm/headless';\n\n// Handle both ESM and CJS module formats\nconst Terminal = (xterm as { Terminal: typeof xterm.Terminal; default?: { Terminal: typeof xterm.Terminal } }).Terminal || (xterm as { default?: { Terminal: typeof xterm.Terminal } }).default?.Terminal;\n\n/**\n * Wrapper around @xterm/headless Terminal that provides a virtual terminal buffer.\n * Interprets ANSI escape sequences (cursor movement, line clearing, etc.) to produce\n * the actual rendered output rather than raw intermediate states.\n */\nexport class TerminalBuffer {\n private terminal: InstanceType<typeof Terminal>;\n\n constructor(cols: number, scrollback = 10000) {\n this.terminal = new Terminal({\n cols,\n rows: 50, // Visible rows (doesn't matter much for headless)\n scrollback,\n allowProposedApi: true,\n });\n }\n\n /**\n * Write raw data to the terminal buffer.\n * The terminal interprets all ANSI sequences automatically.\n */\n write(data: string | Buffer): void {\n const str = typeof data === 'string' ? data : data.toString('utf8');\n this.terminal.write(str);\n }\n\n /**\n * Resize the terminal width.\n */\n resize(cols: number): void {\n this.terminal.resize(cols, this.terminal.rows);\n }\n\n /**\n * Extract the rendered lines from the terminal buffer.\n * This returns the actual visible content after all ANSI sequences\n * have been processed.\n */\n getLines(): string[] {\n const buffer = this.terminal.buffer.active;\n const lines: string[] = [];\n\n for (let i = 0; i < buffer.length; i++) {\n const line = buffer.getLine(i);\n if (line) {\n // translateToString(trimRight) - trim trailing whitespace\n // Also trim leading whitespace - tools like ncu/npm use cursor positioning\n // which creates lines with leading spaces when interpreted by xterm\n lines.push(line.translateToString(true).trimStart());\n }\n }\n\n // Trim trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n\n return lines;\n }\n\n /**\n * Get the number of rendered lines.\n */\n get lineCount(): number {\n return this.getLines().length;\n }\n\n /**\n * Clean up terminal resources.\n */\n dispose(): void {\n this.terminal.dispose();\n }\n}\n"],"names":["xterm","Terminal","default","TerminalBuffer","write","data","str","toString","terminal","resize","cols","rows","getLines","buffer","active","lines","i","length","line","getLine","push","translateToString","trimStart","pop","lineCount","dispose","scrollback","allowProposedApi"],"mappings":"IAG2H;AAH3H,YAAYA,WAAW,kBAAkB;AAEzC,yCAAyC;AACzC,MAAMC,WAAW,AAACD,MAA6FC,QAAQ,MAAI,iBAAA,AAACD,MAA4DE,OAAO,cAApE,qCAAA,eAAsED,QAAQ;AAEzM;;;;CAIC,GACD,OAAO,MAAME;IAYX;;;GAGC,GACDC,MAAMC,IAAqB,EAAQ;QACjC,MAAMC,MAAM,OAAOD,SAAS,WAAWA,OAAOA,KAAKE,QAAQ,CAAC;QAC5D,IAAI,CAACC,QAAQ,CAACJ,KAAK,CAACE;IACtB;IAEA;;GAEC,GACDG,OAAOC,IAAY,EAAQ;QACzB,IAAI,CAACF,QAAQ,CAACC,MAAM,CAACC,MAAM,IAAI,CAACF,QAAQ,CAACG,IAAI;IAC/C;IAEA;;;;GAIC,GACDC,WAAqB;QACnB,MAAMC,SAAS,IAAI,CAACL,QAAQ,CAACK,MAAM,CAACC,MAAM;QAC1C,MAAMC,QAAkB,EAAE;QAE1B,IAAK,IAAIC,IAAI,GAAGA,IAAIH,OAAOI,MAAM,EAAED,IAAK;YACtC,MAAME,OAAOL,OAAOM,OAAO,CAACH;YAC5B,IAAIE,MAAM;gBACR,0DAA0D;gBAC1D,2EAA2E;gBAC3E,oEAAoE;gBACpEH,MAAMK,IAAI,CAACF,KAAKG,iBAAiB,CAAC,MAAMC,SAAS;YACnD;QACF;QAEA,4BAA4B;QAC5B,MAAOP,MAAME,MAAM,GAAG,KAAKF,KAAK,CAACA,MAAME,MAAM,GAAG,EAAE,KAAK,GAAI;YACzDF,MAAMQ,GAAG;QACX;QAEA,OAAOR;IACT;IAEA;;GAEC,GACD,IAAIS,YAAoB;QACtB,OAAO,IAAI,CAACZ,QAAQ,GAAGK,MAAM;IAC/B;IAEA;;GAEC,GACDQ,UAAgB;QACd,IAAI,CAACjB,QAAQ,CAACiB,OAAO;IACvB;IAhEA,YAAYf,IAAY,EAAEgB,aAAa,KAAK,CAAE;QAC5C,IAAI,CAAClB,QAAQ,GAAG,IAAIP,SAAS;YAC3BS;YACAC,MAAM;YACNe;YACAC,kBAAkB;QACpB;IACF;AA0DF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/lib/TerminalBuffer.ts"],"sourcesContent":["import * as xterm from '@xterm/headless';\n\n// Handle both ESM and CJS module formats\nconst Terminal = (xterm as { Terminal: typeof xterm.Terminal; default?: { Terminal: typeof xterm.Terminal } }).Terminal || (xterm as { default?: { Terminal: typeof xterm.Terminal } }).default?.Terminal;\n\n// ANSI color mode constants from xterm.js\nconst COLOR_MODE_DEFAULT = 0;\nconst COLOR_MODE_16 = 16777216; // 0x1000000 - 16 color palette (0-15)\nconst COLOR_MODE_256 = 33554432; // 0x2000000 - 256 color palette\nconst COLOR_MODE_RGB = 50331648; // 0x3000000 - 24-bit RGB\n\n/**\n * Wrapper around @xterm/headless Terminal that provides a virtual terminal buffer.\n * Interprets ANSI escape sequences (cursor movement, line clearing, etc.) to produce\n * the actual rendered output rather than raw intermediate states.\n */\n// Cell attribute state for tracking changes\ninterface CellStyle {\n fg: number;\n fgMode: number;\n bg: number;\n bgMode: number;\n bold: boolean;\n dim: boolean;\n italic: boolean;\n underline: boolean;\n inverse: boolean;\n strikethrough: boolean;\n}\n\nconst DEFAULT_STYLE: CellStyle = {\n fg: -1,\n fgMode: COLOR_MODE_DEFAULT,\n bg: -1,\n bgMode: COLOR_MODE_DEFAULT,\n bold: false,\n dim: false,\n italic: false,\n underline: false,\n inverse: false,\n strikethrough: false,\n};\n\nfunction styleEquals(a: CellStyle, b: CellStyle): boolean {\n return a.fg === b.fg && a.fgMode === b.fgMode && a.bg === b.bg && a.bgMode === b.bgMode && a.bold === b.bold && a.dim === b.dim && a.italic === b.italic && a.underline === b.underline && a.inverse === b.inverse && a.strikethrough === b.strikethrough;\n}\n\nfunction buildAnsiCode(style: CellStyle): string {\n const codes: number[] = [];\n\n // Attributes\n if (style.bold) codes.push(1);\n if (style.dim) codes.push(2);\n if (style.italic) codes.push(3);\n if (style.underline) codes.push(4);\n if (style.inverse) codes.push(7);\n if (style.strikethrough) codes.push(9);\n\n // Foreground color\n if (style.fgMode === COLOR_MODE_16) {\n // 16-color palette: 0-7 are 30-37, 8-15 are 90-97\n if (style.fg < 8) {\n codes.push(30 + style.fg);\n } else {\n codes.push(90 + (style.fg - 8));\n }\n } else if (style.fgMode === COLOR_MODE_256) {\n codes.push(38, 5, style.fg);\n } else if (style.fgMode === COLOR_MODE_RGB) {\n // RGB is encoded in the color value\n const r = (style.fg >> 16) & 0xff;\n const g = (style.fg >> 8) & 0xff;\n const b = style.fg & 0xff;\n codes.push(38, 2, r, g, b);\n }\n\n // Background color\n if (style.bgMode === COLOR_MODE_16) {\n if (style.bg < 8) {\n codes.push(40 + style.bg);\n } else {\n codes.push(100 + (style.bg - 8));\n }\n } else if (style.bgMode === COLOR_MODE_256) {\n codes.push(48, 5, style.bg);\n } else if (style.bgMode === COLOR_MODE_RGB) {\n const r = (style.bg >> 16) & 0xff;\n const g = (style.bg >> 8) & 0xff;\n const b = style.bg & 0xff;\n codes.push(48, 2, r, g, b);\n }\n\n if (codes.length === 0) return '';\n return `\\x1b[${codes.join(';')}m`;\n}\n\nexport class TerminalBuffer {\n private terminal: InstanceType<typeof Terminal>;\n\n constructor(cols: number, scrollback = 10000) {\n this.terminal = new Terminal({\n cols,\n rows: 50, // Visible rows (doesn't matter much for headless)\n scrollback,\n allowProposedApi: true,\n });\n }\n\n /**\n * Write raw data to the terminal buffer.\n * The terminal interprets all ANSI sequences automatically.\n */\n write(data: string | Buffer): void {\n const str = typeof data === 'string' ? data : data.toString('utf8');\n this.terminal.write(str);\n }\n\n /**\n * Resize the terminal width.\n */\n resize(cols: number): void {\n this.terminal.resize(cols, this.terminal.rows);\n }\n\n /**\n * Extract the rendered lines from the terminal buffer.\n * This returns the actual visible content after all ANSI sequences\n * have been processed, with color codes preserved.\n */\n getLines(): string[] {\n const buffer = this.terminal.buffer.active;\n const lines: string[] = [];\n\n for (let i = 0; i < buffer.length; i++) {\n const bufferLine = buffer.getLine(i);\n if (!bufferLine) continue;\n\n let result = '';\n let currentStyle: CellStyle = { ...DEFAULT_STYLE };\n let _hasContent = false;\n\n // First pass: find the last non-empty cell to know where content ends\n let lastContentIndex = -1;\n for (let j = bufferLine.length - 1; j >= 0; j--) {\n const cell = bufferLine.getCell(j);\n if (cell && cell.getChars()) {\n lastContentIndex = j;\n break;\n }\n }\n\n // Second pass: build the line with ANSI codes\n for (let j = 0; j <= lastContentIndex; j++) {\n const cell = bufferLine.getCell(j);\n if (!cell) continue;\n\n const char = cell.getChars();\n const cellStyle: CellStyle = {\n fg: cell.getFgColor(),\n fgMode: cell.getFgColorMode(),\n bg: cell.getBgColor(),\n bgMode: cell.getBgColorMode(),\n bold: cell.isBold() !== 0,\n dim: cell.isDim() !== 0,\n italic: cell.isItalic() !== 0,\n underline: cell.isUnderline() !== 0,\n inverse: cell.isInverse() !== 0,\n strikethrough: cell.isStrikethrough() !== 0,\n };\n\n // Check if style changed\n if (!styleEquals(cellStyle, currentStyle)) {\n // Reset if going back to default, otherwise emit new style\n if (styleEquals(cellStyle, DEFAULT_STYLE)) {\n result += '\\x1b[0m';\n } else {\n // If we had styling before, reset first for clean transition\n if (!styleEquals(currentStyle, DEFAULT_STYLE)) {\n result += '\\x1b[0m';\n }\n result += buildAnsiCode(cellStyle);\n }\n currentStyle = cellStyle;\n }\n\n result += char || ' ';\n if (char) _hasContent = true;\n }\n\n // Reset at end of line if we had styling\n if (!styleEquals(currentStyle, DEFAULT_STYLE)) {\n result += '\\x1b[0m';\n }\n\n // Trim leading whitespace - tools like ncu/npm use cursor positioning\n // which creates lines with leading spaces when interpreted by xterm\n lines.push(result.trimStart());\n }\n\n // Trim trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n\n return lines;\n }\n\n /**\n * Get the number of rendered lines.\n */\n get lineCount(): number {\n return this.getLines().length;\n }\n\n /**\n * Clean up terminal resources.\n */\n dispose(): void {\n this.terminal.dispose();\n }\n}\n"],"names":["xterm","Terminal","default","COLOR_MODE_DEFAULT","COLOR_MODE_16","COLOR_MODE_256","COLOR_MODE_RGB","DEFAULT_STYLE","fg","fgMode","bg","bgMode","bold","dim","italic","underline","inverse","strikethrough","styleEquals","a","b","buildAnsiCode","style","codes","push","r","g","length","join","TerminalBuffer","write","data","str","toString","terminal","resize","cols","rows","getLines","buffer","active","lines","i","bufferLine","getLine","result","currentStyle","_hasContent","lastContentIndex","j","cell","getCell","getChars","char","cellStyle","getFgColor","getFgColorMode","getBgColor","getBgColorMode","isBold","isDim","isItalic","isUnderline","isInverse","isStrikethrough","trimStart","pop","lineCount","dispose","scrollback","allowProposedApi"],"mappings":"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"}
@@ -309,12 +309,9 @@ class SessionImpl {
309
309
  // Only render Ink when stdout is a real terminal
310
310
  // When piped (e.g., nested spawn-term), skip Ink to avoid cursor positioning artifacts
311
311
  if (process.stdout.isTTY) {
312
- // Note: incrementalRendering disabled to prevent corruption when content shifts vertically
313
- // (e.g., error footer appearing, processes completing, scroll position changes)
314
312
  this.inkApp = render(/*#__PURE__*/ _jsx(App, {
315
313
  store: this.store
316
314
  }), {
317
- incrementalRendering: false,
318
315
  maxFps: DEFAULT_MAX_FPS
319
316
  });
320
317
  }