spawn-term 3.2.5 → 3.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -156,17 +156,18 @@ function AppContent(param) {
156
156
  var baseReserved1 = (header ? 2 : 0) + (showStatusBar ? 2 : 0);
157
157
  var visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved1 - _constantsts.EXPANDED_MAX_VISIBLE_LINES - 1);
158
158
  var visibleWhenCollapsed1 = Math.max(1, terminalHeight - baseReserved1 - 2); // -2 for filter bar + list hint
159
- if (input === 'q' || key.escape) {
159
+ if (input === 'q' || key.escape || key.return && expandedId) {
160
+ // q/Esc/Enter all exit expanded mode; q/Esc exit app when not expanded
160
161
  if (expandedId) {
161
162
  store.collapse(visibleWhenCollapsed1);
162
- } else if (searchTerm) {
163
+ } else if (key.escape && searchTerm) {
163
164
  // Clear search first before exiting
164
165
  store.clearSearch();
165
- } else {
166
+ } else if (input === 'q' || key.escape) {
166
167
  store.signalExit(function() {});
167
168
  }
168
- // Enter - fullscreen view (direct from list or from expanded)
169
- } else if (key.return) {
169
+ // Enter - fullscreen view (from list only, not expanded)
170
+ } else if (key.return && !expandedId) {
170
171
  store.enterFullscreen();
171
172
  // Space - toggle small expanded preview
172
173
  } else if (input === ' ') {
@@ -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 FullscreenOverlay from './FullscreenOverlay.ts';\nimport StatusBar from './StatusBar.ts';\n\nconst isMac = process.platform === 'darwin';\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 allProcesses = 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 const filterMode = useSyncExternalStore(store.subscribe, store.getFilterMode);\n const searchTerm = useSyncExternalStore(store.subscribe, store.getSearchTerm);\n const isSearching = useSyncExternalStore(store.subscribe, store.getIsSearching);\n const isFullscreen = useSyncExternalStore(store.subscribe, store.getIsFullscreen);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Use filtered processes for display\n const processes = store.getFilteredProcesses();\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 // In interactive mode without expansion, reserve space for filter bar and list scroll hint\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const filterBarHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for filter/search bar\n const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + filterBarHeight + listHintHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from allProcesses - total counts regardless of filter)\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 // Filter mode display labels\n const filterLabels: Record<string, string> = {\n all: 'All',\n running: 'Running',\n finished: 'Finished',\n failed: 'Failed',\n };\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 // Clamp viewport when collapsing to avoid empty space\n // This runs after render with correct visibleProcessCount\n useEffect(() => {\n if (mode === 'interactive' && !expandedId) {\n store.clampListViewport(visibleProcessCount);\n }\n }, [mode, expandedId, visibleProcessCount, store]);\n\n // Calculate fullscreen visible lines (terminal height minus header and footer)\n const fullscreenVisibleLines = Math.max(1, terminalHeight - 3); // -3 for title, divider, footer\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n // Fullscreen mode input handling\n if (isFullscreen) {\n // Pre-calculate for viewport adjustment\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2);\n\n if (input === 'q' || key.escape || key.return) {\n store.exitFullscreen(visibleWhenCollapsed);\n } else if ((key.meta && key.upArrow) || input === 'g') {\n store.scrollToTop();\n } else if ((key.meta && key.downArrow) || input === 'G') {\n store.scrollToBottom(fullscreenVisibleLines);\n } else if (key.tab && key.shift) {\n store.scrollPageUp(fullscreenVisibleLines);\n } else if (key.tab && !key.shift) {\n store.scrollPageDown(fullscreenVisibleLines);\n } else if (key.downArrow || input === 'j') {\n store.scrollDown(fullscreenVisibleLines);\n } else if (key.upArrow || input === 'k') {\n store.scrollUp();\n }\n return;\n }\n\n // Search mode input handling\n if (isSearching) {\n if (key.escape) {\n store.cancelSearch();\n } else if (key.return) {\n store.confirmSearch();\n } else if (key.backspace || key.delete) {\n store.updateSearchTerm(searchTerm.slice(0, -1));\n } else if (input && !key.ctrl && !key.meta) {\n store.updateSearchTerm(searchTerm + input);\n }\n return;\n }\n\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 // Pre-calculate visible counts for expand/collapse transitions\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint\n\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse(visibleWhenCollapsed);\n } else if (searchTerm) {\n // Clear search first before exiting\n store.clearSearch();\n } else {\n store.signalExit(() => {});\n }\n // Enter - fullscreen view (direct from list or from expanded)\n } else if (key.return) {\n store.enterFullscreen();\n // Space - toggle small expanded preview\n } else if (input === ' ') {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\n // Filter cycling - left/right arrows\n } else if (key.rightArrow && !expandedId) {\n store.cycleFilterNext();\n } else if (key.leftArrow && !expandedId) {\n store.cycleFilterPrev();\n // Search - '/' to start search\n } else if (input === '/' && !expandedId) {\n store.startSearch();\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 } else {\n store.selectFirst(visibleProcessCount);\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 } else {\n store.selectLast(visibleProcessCount);\n }\n // Page scrolling - Tab/Shift+Tab (use same page size as expanded view)\n } else if (key.tab && key.shift) {\n if (expandedId) {\n store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageUp(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n } else if (key.tab && !key.shift) {\n if (expandedId) {\n store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageDown(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\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}-${filterMode}-${searchTerm}-${isSearching}`;\n\n // Get expanded process info for fullscreen\n const expandedProcess = expandedId ? store.getProcess(expandedId) : null;\n\n // Render fullscreen overlay when active\n if (isFullscreen && expandedProcess) {\n return <FullscreenOverlay title={expandedProcess.group || expandedProcess.title} lines={store.getProcessLines(expandedProcess.id)} scrollOffset={scrollOffset} onExit={() => store.exitFullscreen()} />;\n }\n\n return (\n <Box key={layoutKey} flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Filter/Search bar (interactive mode only) */}\n {mode === 'interactive' && !expandedId && (\n <Box>\n <Text dimColor>◀ </Text>\n <Text color={filterMode === 'running' ? 'yellow' : filterMode === 'failed' ? 'red' : filterMode === 'finished' ? 'green' : 'cyan'} bold>\n {filterLabels[filterMode]}\n </Text>\n <Text dimColor> ▶</Text>\n {isSearching ? (\n <Text>\n {' '}\n <Text dimColor>/</Text>\n <Text>{searchTerm}</Text>\n <Text dimColor>▋</Text>\n </Text>\n ) : searchTerm ? (\n <Text dimColor> \"{searchTerm}\"</Text>\n ) : (\n <Text dimColor> (/ search)</Text>\n )}\n {processes.length !== allProcesses.length && (\n <Text dimColor>\n {' '}\n [{processes.length}/{allProcesses.length}]\n </Text>\n )}\n </Box>\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 {/* List scroll hint (interactive mode without expansion) */}\n {mode === 'interactive' && !expandedId && processes.length > visibleProcessCount && (\n <Text dimColor>\n [+{processes.length - visibleProcessCount} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom]\n </Text>\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":["App","isMac","process","platform","AppContent","store","exit","useApp","isRawModeSupported","useStdin","stdout","useStdout","terminalHeight","rows","allProcesses","useSyncExternalStore","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","filterMode","getFilterMode","searchTerm","getSearchTerm","isSearching","getIsSearching","isFullscreen","getIsFullscreen","_bufferVersion","getBufferVersion","processes","getFilteredProcesses","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","EXPANDED_MAX_VISIBLE_LINES","filterBarHeight","listHintHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","filterLabels","all","running","finished","failed","useEffect","setMode","clampListViewport","fullscreenVisibleLines","useInput","input","key","baseReserved","visibleWhenCollapsed","escape","return","exitFullscreen","meta","upArrow","scrollToTop","downArrow","scrollToBottom","tab","shift","scrollPageUp","scrollPageDown","scrollDown","scrollUp","cancelSearch","confirmSearch","backspace","delete","updateSearchTerm","slice","ctrl","toggleErrorFooter","visibleWhenExpanded","collapse","clearSearch","signalExit","enterFullscreen","toggleExpand","rightArrow","cycleFilterNext","leftArrow","cycleFilterPrev","startSearch","selectFirst","selectLast","selectPageUp","selectPageDown","selectNext","selectPrev","isActive","visibleProcesses","useMemo","showSelection","layoutKey","expandedProcess","getProcess","FullscreenOverlay","title","group","lines","getProcessLines","id","onExit","Box","flexDirection","Text","Divider","dimColor","color","bold","length","map","item","originalIndex","indexOf","CompactProcessLine","isSelected","ExpandedOutput","StatusBar","done","errors","ErrorFooter","isExpanded","StoreContext","Provider","value"],"mappings":";;;;+BA4TA,gDAAgD;AAChD;;;eAAwBA;;;;mBA7TyC;qBACR;2BACd;8BAEd;2EACE;gEACX;oEACI;uEACG;0EACG;kEACR;;;;;;AAEtB,IAAMC,QAAQC,QAAQC,QAAQ,KAAK;AAMnC,SAASC,WAAW,KAAmB;QAAnB,AAAEC,QAAF,MAAEA;IACpB,IAAM,AAAEC,OAASC,IAAAA,WAAM,IAAfD;IACR,IAAM,AAAEE,qBAAuBC,IAAAA,aAAQ,IAA/BD;IACR,IAAM,AAAEE,SAAWC,IAAAA,cAAS,IAApBD;IACR,IAAME,iBAAiBF,CAAAA,mBAAAA,6BAAAA,OAAQG,IAAI,KAAI;IAEvC,2BAA2B;IAC3B,IAAMC,eAAeC,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMY,WAAW;IAC5E,IAAMC,aAAaH,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMc,aAAa;IAC5E,IAAMC,OAAOL,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMgB,OAAO;IAChE,IAAMC,gBAAgBP,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMkB,gBAAgB;IAClF,IAAMC,aAAaT,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMoB,aAAa;IAC5E,IAAMC,eAAeX,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMsB,eAAe;IAChF,IAAMC,mBAAmBb,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMwB,mBAAmB;IACxF,IAAMC,sBAAsBf,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAM0B,sBAAsB;IAC9F,IAAMC,aAAajB,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAM4B,aAAa;IAC5E,IAAMC,aAAanB,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAM8B,aAAa;IAC5E,IAAMC,cAAcrB,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMgC,cAAc;IAC9E,IAAMC,eAAevB,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMkC,eAAe;IAChF,yFAAyF;IACzF,IAAMC,iBAAiBzB,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMoC,gBAAgB;IAEnF,qCAAqC;IACrC,IAAMC,YAAYrC,MAAMsC,oBAAoB;IAE5C,4CAA4C;IAC5C,IAAMC,SAAS7B,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAMwC,SAAS;IACpE,IAAMC,gBAAgB/B,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAM0C,gBAAgB;IAClF,IAAMC,gBAAgBjC,IAAAA,2BAAoB,EAACV,MAAMW,SAAS,EAAEX,MAAM4C,gBAAgB;IAElF,mGAAmG;IACnG,kGAAkG;IAClG,2FAA2F;IAC3F,IAAMC,iBAAiB1B,aAAa2B,uCAA0B,GAAG,IAAI,GAAG,qBAAqB;IAC7F,IAAMC,kBAAkBhC,SAAS,iBAAiB,CAACI,aAAa,IAAI,GAAG,gCAAgC;IACvG,IAAM6B,iBAAiBjC,SAAS,iBAAiB,CAACI,aAAa,IAAI,GAAG,+BAA+B;IACrG,IAAM8B,gBAAgB,AAACV,CAAAA,SAAS,IAAI,CAAA,IAAME,CAAAA,gBAAgB,IAAI,CAAA,IAAKI,iBAAiBE,kBAAkBC;IACtG,IAAME,sBAAsBC,KAAKC,GAAG,CAAC,GAAG7C,iBAAiB0C;IAEzD,iFAAiF;IACjF,IAAMI,eAAerD,MAAMsD,eAAe;IAC1C,IAAMC,YAAYvD,MAAMwD,YAAY;IACpC,IAAMC,aAAazD,MAAM0D,aAAa;IACtC,IAAMC,iBAAiB3D,MAAM4D,iBAAiB;IAC9C,IAAMC,iBAAiB7D,MAAM8D,aAAa;IAC1C,IAAMC,aAAa/D,MAAMgE,aAAa;IAEtC,6BAA6B;IAC7B,IAAMC,eAAuC;QAC3CC,KAAK;QACLC,SAAS;QACTC,UAAU;QACVC,QAAQ;IACV;IAEA,qBAAqB;IACrBC,IAAAA,gBAAS,EAAC;QACR,IAAIzD,YAAY;YACdZ;QACF;IACF,GAAG;QAACY;QAAYZ;KAAK;IAErB,uEAAuE;IACvE,8DAA8D;IAC9DqE,IAAAA,gBAAS,EAAC;QACR,IAAI3B,iBAAiB5B,SAAS,UAAU;YACtCf,MAAMuE,OAAO,CAAC;QAChB;IACF,GAAG;QAAC5B;QAAe5B;QAAMf;KAAM;IAE/B,sDAAsD;IACtD,0DAA0D;IAC1DsE,IAAAA,gBAAS,EAAC;QACR,IAAIvD,SAAS,iBAAiB,CAACI,YAAY;YACzCnB,MAAMwE,iBAAiB,CAACtB;QAC1B;IACF,GAAG;QAACnC;QAAMI;QAAY+B;QAAqBlD;KAAM;IAEjD,+EAA+E;IAC/E,IAAMyE,yBAAyBtB,KAAKC,GAAG,CAAC,GAAG7C,iBAAiB,IAAI,gCAAgC;IAEhG,6DAA6D;IAC7DmE,IAAAA,aAAQ,EACN,SAACC,OAAOC;QACN,iCAAiC;QACjC,IAAI3C,cAAc;YAChB,wCAAwC;YACxC,IAAM4C,eAAe,AAACtC,CAAAA,SAAS,IAAI,CAAA,IAAME,CAAAA,gBAAgB,IAAI,CAAA;YAC7D,IAAMqC,uBAAuB3B,KAAKC,GAAG,CAAC,GAAG7C,iBAAiBsE,eAAe;YAEzE,IAAIF,UAAU,OAAOC,IAAIG,MAAM,IAAIH,IAAII,MAAM,EAAE;gBAC7ChF,MAAMiF,cAAc,CAACH;YACvB,OAAO,IAAI,AAACF,IAAIM,IAAI,IAAIN,IAAIO,OAAO,IAAKR,UAAU,KAAK;gBACrD3E,MAAMoF,WAAW;YACnB,OAAO,IAAI,AAACR,IAAIM,IAAI,IAAIN,IAAIS,SAAS,IAAKV,UAAU,KAAK;gBACvD3E,MAAMsF,cAAc,CAACb;YACvB,OAAO,IAAIG,IAAIW,GAAG,IAAIX,IAAIY,KAAK,EAAE;gBAC/BxF,MAAMyF,YAAY,CAAChB;YACrB,OAAO,IAAIG,IAAIW,GAAG,IAAI,CAACX,IAAIY,KAAK,EAAE;gBAChCxF,MAAM0F,cAAc,CAACjB;YACvB,OAAO,IAAIG,IAAIS,SAAS,IAAIV,UAAU,KAAK;gBACzC3E,MAAM2F,UAAU,CAAClB;YACnB,OAAO,IAAIG,IAAIO,OAAO,IAAIR,UAAU,KAAK;gBACvC3E,MAAM4F,QAAQ;YAChB;YACA;QACF;QAEA,6BAA6B;QAC7B,IAAI7D,aAAa;YACf,IAAI6C,IAAIG,MAAM,EAAE;gBACd/E,MAAM6F,YAAY;YACpB,OAAO,IAAIjB,IAAII,MAAM,EAAE;gBACrBhF,MAAM8F,aAAa;YACrB,OAAO,IAAIlB,IAAImB,SAAS,IAAInB,IAAIoB,MAAM,EAAE;gBACtChG,MAAMiG,gBAAgB,CAACpE,WAAWqE,KAAK,CAAC,GAAG,CAAC;YAC9C,OAAO,IAAIvB,SAAS,CAACC,IAAIuB,IAAI,IAAI,CAACvB,IAAIM,IAAI,EAAE;gBAC1ClF,MAAMiG,gBAAgB,CAACpE,aAAa8C;YACtC;YACA;QACF;QAEA,IAAI5D,SAAS,UAAU;YACrB,oDAAoD;YACpD,IAAI4D,UAAU,OAAOlB,aAAa,GAAG;gBACnCzD,MAAMoG,iBAAiB;YACzB;QACF,OAAO,IAAIrF,SAAS,eAAe;YACjC,+DAA+D;YAC/D,IAAM8D,gBAAe,AAACtC,CAAAA,SAAS,IAAI,CAAA,IAAME,CAAAA,gBAAgB,IAAI,CAAA;YAC7D,IAAM4D,sBAAsBlD,KAAKC,GAAG,CAAC,GAAG7C,iBAAiBsE,gBAAe/B,uCAA0B,GAAG;YACrG,IAAMgC,wBAAuB3B,KAAKC,GAAG,CAAC,GAAG7C,iBAAiBsE,gBAAe,IAAI,gCAAgC;YAE7G,IAAIF,UAAU,OAAOC,IAAIG,MAAM,EAAE;gBAC/B,IAAI5D,YAAY;oBACdnB,MAAMsG,QAAQ,CAACxB;gBACjB,OAAO,IAAIjD,YAAY;oBACrB,oCAAoC;oBACpC7B,MAAMuG,WAAW;gBACnB,OAAO;oBACLvG,MAAMwG,UAAU,CAAC,YAAO;gBAC1B;YACA,8DAA8D;YAChE,OAAO,IAAI5B,IAAII,MAAM,EAAE;gBACrBhF,MAAMyG,eAAe;YACrB,wCAAwC;YAC1C,OAAO,IAAI9B,UAAU,KAAK;gBACxB3E,MAAM0G,YAAY,CAACL,qBAAqBvB;YACxC,qCAAqC;YACvC,OAAO,IAAIF,IAAI+B,UAAU,IAAI,CAACxF,YAAY;gBACxCnB,MAAM4G,eAAe;YACvB,OAAO,IAAIhC,IAAIiC,SAAS,IAAI,CAAC1F,YAAY;gBACvCnB,MAAM8G,eAAe;YACrB,+BAA+B;YACjC,OAAO,IAAInC,UAAU,OAAO,CAACxD,YAAY;gBACvCnB,MAAM+G,WAAW;YACjB,oDAAoD;YACpD,2CAA2C;YAC7C,OAAO,IAAI,AAACnC,IAAIM,IAAI,IAAIN,IAAIO,OAAO,IAAKR,UAAU,KAAK;gBACrD,IAAIxD,YAAY;oBACdnB,MAAMoF,WAAW;gBACnB,OAAO;oBACLpF,MAAMgH,WAAW,CAAC9D;gBACpB;YACA,uDAAuD;YACzD,OAAO,IAAI,AAAC0B,IAAIM,IAAI,IAAIN,IAAIS,SAAS,IAAKV,UAAU,KAAK;gBACvD,IAAIxD,YAAY;oBACdnB,MAAMsF,cAAc,CAACxC,uCAA0B;gBACjD,OAAO;oBACL9C,MAAMiH,UAAU,CAAC/D;gBACnB;YACA,uEAAuE;YACzE,OAAO,IAAI0B,IAAIW,GAAG,IAAIX,IAAIY,KAAK,EAAE;gBAC/B,IAAIrE,YAAY;oBACdnB,MAAMyF,YAAY,CAAC3C,uCAA0B;gBAC/C,OAAO;oBACL9C,MAAMkH,YAAY,CAACpE,uCAA0B,EAAEI;gBACjD;YACF,OAAO,IAAI0B,IAAIW,GAAG,IAAI,CAACX,IAAIY,KAAK,EAAE;gBAChC,IAAIrE,YAAY;oBACdnB,MAAM0F,cAAc,CAAC5C,uCAA0B;gBACjD,OAAO;oBACL9C,MAAMmH,cAAc,CAACrE,uCAA0B,EAAEI;gBACnD;YACA,sCAAsC;YACxC,OAAO,IAAI0B,IAAIS,SAAS,IAAIV,UAAU,KAAK;gBACzC,IAAIxD,YAAY;oBACdnB,MAAM2F,UAAU,CAAC7C,uCAA0B;gBAC7C,OAAO;oBACL9C,MAAMoH,UAAU,CAAClE;gBACnB;YACF,OAAO,IAAI0B,IAAIO,OAAO,IAAIR,UAAU,KAAK;gBACvC,IAAIxD,YAAY;oBACdnB,MAAM4F,QAAQ;gBAChB,OAAO;oBACL5F,MAAMqH,UAAU,CAACnE;gBACnB;YACF;QACF;IACF,GACA;QAAEoE,UAAUnH,uBAAuB;IAAK;IAG1C,0DAA0D;IAC1D,IAAMoH,mBAAmBC,IAAAA,cAAO,EAAC;QAC/B,IAAIzG,SAAS,eAAe;YAC1B,OAAOsB,UAAU6D,KAAK,CAAC3E,kBAAkBA,mBAAmB2B;QAC9D;QACA,OAAOb;IACT,GAAG;QAACA;QAAWtB;QAAMQ;QAAkB2B;KAAoB;IAE3D,kEAAkE;IAClE,IAAMuE,gBAAgB1G,SAAS;IAE/B,qDAAqD;IACrD,2FAA2F;IAC3F,IAAM2G,YAAY,AAAC,GAAsBvG,OAApBI,kBAAiB,KAAiBkC,OAAdtC,YAAW,KAAiBM,OAAdgC,YAAW,KAA0B9B,OAAvBF,qBAAoB,KAAiBI,OAAdF,YAAW,KAAiBI,OAAdF,YAAW,KAAe,OAAZE;IAExH,2CAA2C;IAC3C,IAAM4F,kBAAkBxG,aAAanB,MAAM4H,UAAU,CAACzG,cAAc;IAEpE,wCAAwC;IACxC,IAAIc,gBAAgB0F,iBAAiB;QACnC,qBAAO,qBAACE,4BAAiB;YAACC,OAAOH,gBAAgBI,KAAK,IAAIJ,gBAAgBG,KAAK;YAAEE,OAAOhI,MAAMiI,eAAe,CAACN,gBAAgBO,EAAE;YAAG7G,cAAcA;YAAc8G,QAAQ;uBAAMnI,MAAMiF,cAAc;;;IACnM;IAEA,qBACE,sBAACmD,QAAG;QAAiBC,eAAc;;YAEhC9F,wBACC;;kCACE,qBAAC+F,SAAI;kCAAE/F;;kCACP,qBAACgG,kBAAO;;;YAKXxH,SAAS,iBAAiB,CAACI,4BAC1B,sBAACiH,QAAG;;kCACF,qBAACE,SAAI;wBAACE,QAAQ;kCAAC;;kCACf,qBAACF,SAAI;wBAACG,OAAO9G,eAAe,YAAY,WAAWA,eAAe,WAAW,QAAQA,eAAe,aAAa,UAAU;wBAAQ+G,IAAI;kCACpIzE,YAAY,CAACtC,WAAW;;kCAE3B,qBAAC2G,SAAI;wBAACE,QAAQ;kCAAC;;oBACdzG,4BACC,sBAACuG,SAAI;;4BACF;0CACD,qBAACA,SAAI;gCAACE,QAAQ;0CAAC;;0CACf,qBAACF,SAAI;0CAAEzG;;0CACP,qBAACyG,SAAI;gCAACE,QAAQ;0CAAC;;;yBAEf3G,2BACF,sBAACyG,SAAI;wBAACE,QAAQ;;4BAAC;4BAAG3G;4BAAW;;uCAE7B,qBAACyG,SAAI;wBAACE,QAAQ;kCAAC;;oBAEhBnG,UAAUsG,MAAM,KAAKlI,aAAakI,MAAM,kBACvC,sBAACL,SAAI;wBAACE,QAAQ;;4BACX;4BAAI;4BACHnG,UAAUsG,MAAM;4BAAC;4BAAElI,aAAakI,MAAM;4BAAC;;;;;0BAOjD,sBAACP,QAAG;gBAACC,eAAc;;oBAChBd,iBAAiBqB,GAAG,CAAC,SAACC;wBACrB,IAAMC,gBAAgBzG,UAAU0G,OAAO,CAACF;wBACxC,qBACE,sBAACT,QAAG;4BAAeC,eAAc;;8CAC/B,qBAACW,6BAAkB;oCAACH,MAAMA;oCAAMI,YAAYxB,iBAAiBqB,kBAAkB7H;;gCAC9EE,eAAe0H,KAAKX,EAAE,kBAAI,qBAACgB,yBAAc;oCAAClB,OAAOhI,MAAMiI,eAAe,CAACY,KAAKX,EAAE;oCAAG7G,cAAcA;;;2BAFxFwH,KAAKX,EAAE;oBAKrB;oBAECnH,SAAS,iBAAiB,CAACI,cAAckB,UAAUsG,MAAM,GAAGzF,qCAC3D,sBAACoF,SAAI;wBAACE,QAAQ;;4BAAC;4BACVnG,UAAUsG,MAAM,GAAGzF;4BAAoB;4BAAuBtD,QAAQ,SAAS;4BAAM;;;;;YAM7F6C,iBAAiBJ,UAAUsG,MAAM,GAAG,mBACnC;;kCACE,qBAACJ,kBAAO;kCACR,qBAACY,oBAAS;wBAAChF,SAASd;wBAAc+F,MAAM7F;wBAAW8F,QAAQ5F;wBAAYM,YAAYJ;;;;YAKtF,CAAChB,iBAAiBc,aAAa,mBAAK,qBAAC6F,sBAAW;gBAACD,QAAQtF;gBAAYwF,YAAY9H;;;OAlE1EiG;AAqEd;AAGe,SAAS/H,IAAI,KAAmB;QAAnB,AAAEK,QAAF,MAAEA;IAC5B,qBACE,qBAACwJ,4BAAY,CAACC,QAAQ;QAACC,OAAO1J;kBAC5B,cAAA,qBAACD;YAAWC,OAAOA;;;AAGzB"}
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 FullscreenOverlay from './FullscreenOverlay.ts';\nimport StatusBar from './StatusBar.ts';\n\nconst isMac = process.platform === 'darwin';\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 allProcesses = 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 const filterMode = useSyncExternalStore(store.subscribe, store.getFilterMode);\n const searchTerm = useSyncExternalStore(store.subscribe, store.getSearchTerm);\n const isSearching = useSyncExternalStore(store.subscribe, store.getIsSearching);\n const isFullscreen = useSyncExternalStore(store.subscribe, store.getIsFullscreen);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Use filtered processes for display\n const processes = store.getFilteredProcesses();\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 // In interactive mode without expansion, reserve space for filter bar and list scroll hint\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const filterBarHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for filter/search bar\n const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + filterBarHeight + listHintHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from allProcesses - total counts regardless of filter)\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 // Filter mode display labels\n const filterLabels: Record<string, string> = {\n all: 'All',\n running: 'Running',\n finished: 'Finished',\n failed: 'Failed',\n };\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 // Clamp viewport when collapsing to avoid empty space\n // This runs after render with correct visibleProcessCount\n useEffect(() => {\n if (mode === 'interactive' && !expandedId) {\n store.clampListViewport(visibleProcessCount);\n }\n }, [mode, expandedId, visibleProcessCount, store]);\n\n // Calculate fullscreen visible lines (terminal height minus header and footer)\n const fullscreenVisibleLines = Math.max(1, terminalHeight - 3); // -3 for title, divider, footer\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n // Fullscreen mode input handling\n if (isFullscreen) {\n // Pre-calculate for viewport adjustment\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2);\n\n if (input === 'q' || key.escape || key.return) {\n store.exitFullscreen(visibleWhenCollapsed);\n } else if ((key.meta && key.upArrow) || input === 'g') {\n store.scrollToTop();\n } else if ((key.meta && key.downArrow) || input === 'G') {\n store.scrollToBottom(fullscreenVisibleLines);\n } else if (key.tab && key.shift) {\n store.scrollPageUp(fullscreenVisibleLines);\n } else if (key.tab && !key.shift) {\n store.scrollPageDown(fullscreenVisibleLines);\n } else if (key.downArrow || input === 'j') {\n store.scrollDown(fullscreenVisibleLines);\n } else if (key.upArrow || input === 'k') {\n store.scrollUp();\n }\n return;\n }\n\n // Search mode input handling\n if (isSearching) {\n if (key.escape) {\n store.cancelSearch();\n } else if (key.return) {\n store.confirmSearch();\n } else if (key.backspace || key.delete) {\n store.updateSearchTerm(searchTerm.slice(0, -1));\n } else if (input && !key.ctrl && !key.meta) {\n store.updateSearchTerm(searchTerm + input);\n }\n return;\n }\n\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 // Pre-calculate visible counts for expand/collapse transitions\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint\n\n if (input === 'q' || key.escape || (key.return && expandedId)) {\n // q/Esc/Enter all exit expanded mode; q/Esc exit app when not expanded\n if (expandedId) {\n store.collapse(visibleWhenCollapsed);\n } else if (key.escape && searchTerm) {\n // Clear search first before exiting\n store.clearSearch();\n } else if (input === 'q' || key.escape) {\n store.signalExit(() => {});\n }\n // Enter - fullscreen view (from list only, not expanded)\n } else if (key.return && !expandedId) {\n store.enterFullscreen();\n // Space - toggle small expanded preview\n } else if (input === ' ') {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\n // Filter cycling - left/right arrows\n } else if (key.rightArrow && !expandedId) {\n store.cycleFilterNext();\n } else if (key.leftArrow && !expandedId) {\n store.cycleFilterPrev();\n // Search - '/' to start search\n } else if (input === '/' && !expandedId) {\n store.startSearch();\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 } else {\n store.selectFirst(visibleProcessCount);\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 } else {\n store.selectLast(visibleProcessCount);\n }\n // Page scrolling - Tab/Shift+Tab (use same page size as expanded view)\n } else if (key.tab && key.shift) {\n if (expandedId) {\n store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageUp(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n } else if (key.tab && !key.shift) {\n if (expandedId) {\n store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageDown(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\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}-${filterMode}-${searchTerm}-${isSearching}`;\n\n // Get expanded process info for fullscreen\n const expandedProcess = expandedId ? store.getProcess(expandedId) : null;\n\n // Render fullscreen overlay when active\n if (isFullscreen && expandedProcess) {\n return <FullscreenOverlay title={expandedProcess.group || expandedProcess.title} lines={store.getProcessLines(expandedProcess.id)} scrollOffset={scrollOffset} onExit={() => store.exitFullscreen()} />;\n }\n\n return (\n <Box key={layoutKey} flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Filter/Search bar (interactive mode only) */}\n {mode === 'interactive' && !expandedId && (\n <Box>\n <Text dimColor>◀ </Text>\n <Text color={filterMode === 'running' ? 'yellow' : filterMode === 'failed' ? 'red' : filterMode === 'finished' ? 'green' : 'cyan'} bold>\n {filterLabels[filterMode]}\n </Text>\n <Text dimColor> ▶</Text>\n {isSearching ? (\n <Text>\n {' '}\n <Text dimColor>/</Text>\n <Text>{searchTerm}</Text>\n <Text dimColor>▋</Text>\n </Text>\n ) : searchTerm ? (\n <Text dimColor> \"{searchTerm}\"</Text>\n ) : (\n <Text dimColor> (/ search)</Text>\n )}\n {processes.length !== allProcesses.length && (\n <Text dimColor>\n {' '}\n [{processes.length}/{allProcesses.length}]\n </Text>\n )}\n </Box>\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 {/* List scroll hint (interactive mode without expansion) */}\n {mode === 'interactive' && !expandedId && processes.length > visibleProcessCount && (\n <Text dimColor>\n [+{processes.length - visibleProcessCount} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom]\n </Text>\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":["App","isMac","process","platform","AppContent","store","exit","useApp","isRawModeSupported","useStdin","stdout","useStdout","terminalHeight","rows","allProcesses","useSyncExternalStore","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","filterMode","getFilterMode","searchTerm","getSearchTerm","isSearching","getIsSearching","isFullscreen","getIsFullscreen","_bufferVersion","getBufferVersion","processes","getFilteredProcesses","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","EXPANDED_MAX_VISIBLE_LINES","filterBarHeight","listHintHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","filterLabels","all","running","finished","failed","useEffect","setMode","clampListViewport","fullscreenVisibleLines","useInput","input","key","baseReserved","visibleWhenCollapsed","escape","return","exitFullscreen","meta","upArrow","scrollToTop","downArrow","scrollToBottom","tab","shift","scrollPageUp","scrollPageDown","scrollDown","scrollUp","cancelSearch","confirmSearch","backspace","delete","updateSearchTerm","slice","ctrl","toggleErrorFooter","visibleWhenExpanded","collapse","clearSearch","signalExit","enterFullscreen","toggleExpand","rightArrow","cycleFilterNext","leftArrow","cycleFilterPrev","startSearch","selectFirst","selectLast","selectPageUp","selectPageDown","selectNext","selectPrev","isActive","visibleProcesses","useMemo","showSelection","layoutKey","expandedProcess","getProcess","FullscreenOverlay","title","group","lines","getProcessLines","id","onExit","Box","flexDirection","Text","Divider","dimColor","color","bold","length","map","item","originalIndex","indexOf","CompactProcessLine","isSelected","ExpandedOutput","StatusBar","done","errors","ErrorFooter","isExpanded","StoreContext","Provider","value"],"mappings":"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"}
@@ -47,11 +47,11 @@ var _default = /*#__PURE__*/ (0, _react.memo)(function ExpandedOutput(param) {
47
47
  remaining,
48
48
  " more, Tab/⇧Tab page, ",
49
49
  isMac ? '⌥↑/↓' : 'g/G',
50
- " top/bottom, ↵ fullscreen]"
50
+ " top/bottom, ↵ close]"
51
51
  ]
52
52
  }) : /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
53
53
  dimColor: true,
54
- children: "│ [↵ fullscreen]"
54
+ children: "│ [↵ close]"
55
55
  })
56
56
  ]
57
57
  });
@@ -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\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, ↵ fullscreen]\n </Text>\n ) : (\n <Text dimColor>│ [↵ fullscreen]</Text>\n )}\n </Box>\n );\n});\n"],"names":["isMac","process","platform","memo","ExpandedOutput","lines","scrollOffset","maxVisible","EXPANDED_MAX_VISIBLE_LINES","visibleLines","slice","hasMore","length","remaining","Box","paddingLeft","Text","dimColor","flexDirection","map","line","i","text"],"mappings":";;;;+BAaA;;;eAAA;;;;mBAb0B;qBACL;2BACsB;AAG3C,IAAMA,QAAQC,QAAQC,QAAQ,KAAK;IAQnC,yBAAeC,IAAAA,WAAI,EAAC,SAASC,eAAe,KAAuE;QAArEC,QAAF,MAAEA,OAAOC,eAAT,MAASA,kCAAT,MAAuBC,YAAAA,4CAAaC,uCAA0B;IACxG,IAAMC,eAAeJ,MAAMK,KAAK,CAACJ,cAAcA,eAAeC;IAC9D,IAAMI,UAAUN,MAAMO,MAAM,GAAGN,eAAeC;IAC9C,IAAMM,YAAYR,MAAMO,MAAM,GAAGN,eAAeC;IAEhD,IAAIF,MAAMO,MAAM,KAAK,GAAG;QACtB,qBACE,qBAACE,QAAG;YAACC,aAAa;sBAChB,cAAA,qBAACC,SAAI;gBAACC,QAAQ;0BAAC;;;IAGrB;IAEA,qBACE,sBAACH,QAAG;QAACI,eAAc;QAASH,aAAa;;YACtCN,aAAaU,GAAG,CAAC,SAACC,MAAMC;uBACvB,iHAAiH;8BACjH,sBAACL,SAAI;;wBAAwB;wBAAGI,KAAKE,IAAI;;mBAA9BhB,eAAee;;YAE3BV,wBACC,sBAACK,SAAI;gBAACC,QAAQ;;oBAAC;oBACRJ;oBAAU;oBAAuBb,QAAQ,SAAS;oBAAM;;+BAG/D,qBAACgB,SAAI;gBAACC,QAAQ;0BAAC;;;;AAIvB"}
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, ↵ close]\n </Text>\n ) : (\n <Text dimColor>│ [↵ close]</Text>\n )}\n </Box>\n );\n});\n"],"names":["isMac","process","platform","memo","ExpandedOutput","lines","scrollOffset","maxVisible","EXPANDED_MAX_VISIBLE_LINES","visibleLines","slice","hasMore","length","remaining","Box","paddingLeft","Text","dimColor","flexDirection","map","line","i","text"],"mappings":";;;;+BAaA;;;eAAA;;;;mBAb0B;qBACL;2BACsB;AAG3C,IAAMA,QAAQC,QAAQC,QAAQ,KAAK;IAQnC,yBAAeC,IAAAA,WAAI,EAAC,SAASC,eAAe,KAAuE;QAArEC,QAAF,MAAEA,OAAOC,eAAT,MAASA,kCAAT,MAAuBC,YAAAA,4CAAaC,uCAA0B;IACxG,IAAMC,eAAeJ,MAAMK,KAAK,CAACJ,cAAcA,eAAeC;IAC9D,IAAMI,UAAUN,MAAMO,MAAM,GAAGN,eAAeC;IAC9C,IAAMM,YAAYR,MAAMO,MAAM,GAAGN,eAAeC;IAEhD,IAAIF,MAAMO,MAAM,KAAK,GAAG;QACtB,qBACE,qBAACE,QAAG;YAACC,aAAa;sBAChB,cAAA,qBAACC,SAAI;gBAACC,QAAQ;0BAAC;;;IAGrB;IAEA,qBACE,sBAACH,QAAG;QAACI,eAAc;QAASH,aAAa;;YACtCN,aAAaU,GAAG,CAAC,SAACC,MAAMC;uBACvB,iHAAiH;8BACjH,sBAACL,SAAI;;wBAAwB;wBAAGI,KAAKE,IAAI;;mBAA9BhB,eAAee;;YAE3BV,wBACC,sBAACK,SAAI;gBAACC,QAAQ;;oBAAC;oBACRJ;oBAAU;oBAAuBb,QAAQ,SAAS;oBAAM;;+BAG/D,qBAACgB,SAAI;gBAACC,QAAQ;0BAAC;;;;AAIvB"}
@@ -139,17 +139,18 @@ function AppContent({ store }) {
139
139
  const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);
140
140
  const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);
141
141
  const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint
142
- if (input === 'q' || key.escape) {
142
+ if (input === 'q' || key.escape || key.return && expandedId) {
143
+ // q/Esc/Enter all exit expanded mode; q/Esc exit app when not expanded
143
144
  if (expandedId) {
144
145
  store.collapse(visibleWhenCollapsed);
145
- } else if (searchTerm) {
146
+ } else if (key.escape && searchTerm) {
146
147
  // Clear search first before exiting
147
148
  store.clearSearch();
148
- } else {
149
+ } else if (input === 'q' || key.escape) {
149
150
  store.signalExit(()=>{});
150
151
  }
151
- // Enter - fullscreen view (direct from list or from expanded)
152
- } else if (key.return) {
152
+ // Enter - fullscreen view (from list only, not expanded)
153
+ } else if (key.return && !expandedId) {
153
154
  store.enterFullscreen();
154
155
  // Space - toggle small expanded preview
155
156
  } else if (input === ' ') {
@@ -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 FullscreenOverlay from './FullscreenOverlay.ts';\nimport StatusBar from './StatusBar.ts';\n\nconst isMac = process.platform === 'darwin';\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 allProcesses = 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 const filterMode = useSyncExternalStore(store.subscribe, store.getFilterMode);\n const searchTerm = useSyncExternalStore(store.subscribe, store.getSearchTerm);\n const isSearching = useSyncExternalStore(store.subscribe, store.getIsSearching);\n const isFullscreen = useSyncExternalStore(store.subscribe, store.getIsFullscreen);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Use filtered processes for display\n const processes = store.getFilteredProcesses();\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 // In interactive mode without expansion, reserve space for filter bar and list scroll hint\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const filterBarHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for filter/search bar\n const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + filterBarHeight + listHintHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from allProcesses - total counts regardless of filter)\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 // Filter mode display labels\n const filterLabels: Record<string, string> = {\n all: 'All',\n running: 'Running',\n finished: 'Finished',\n failed: 'Failed',\n };\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 // Clamp viewport when collapsing to avoid empty space\n // This runs after render with correct visibleProcessCount\n useEffect(() => {\n if (mode === 'interactive' && !expandedId) {\n store.clampListViewport(visibleProcessCount);\n }\n }, [mode, expandedId, visibleProcessCount, store]);\n\n // Calculate fullscreen visible lines (terminal height minus header and footer)\n const fullscreenVisibleLines = Math.max(1, terminalHeight - 3); // -3 for title, divider, footer\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n // Fullscreen mode input handling\n if (isFullscreen) {\n // Pre-calculate for viewport adjustment\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2);\n\n if (input === 'q' || key.escape || key.return) {\n store.exitFullscreen(visibleWhenCollapsed);\n } else if ((key.meta && key.upArrow) || input === 'g') {\n store.scrollToTop();\n } else if ((key.meta && key.downArrow) || input === 'G') {\n store.scrollToBottom(fullscreenVisibleLines);\n } else if (key.tab && key.shift) {\n store.scrollPageUp(fullscreenVisibleLines);\n } else if (key.tab && !key.shift) {\n store.scrollPageDown(fullscreenVisibleLines);\n } else if (key.downArrow || input === 'j') {\n store.scrollDown(fullscreenVisibleLines);\n } else if (key.upArrow || input === 'k') {\n store.scrollUp();\n }\n return;\n }\n\n // Search mode input handling\n if (isSearching) {\n if (key.escape) {\n store.cancelSearch();\n } else if (key.return) {\n store.confirmSearch();\n } else if (key.backspace || key.delete) {\n store.updateSearchTerm(searchTerm.slice(0, -1));\n } else if (input && !key.ctrl && !key.meta) {\n store.updateSearchTerm(searchTerm + input);\n }\n return;\n }\n\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 // Pre-calculate visible counts for expand/collapse transitions\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint\n\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse(visibleWhenCollapsed);\n } else if (searchTerm) {\n // Clear search first before exiting\n store.clearSearch();\n } else {\n store.signalExit(() => {});\n }\n // Enter - fullscreen view (direct from list or from expanded)\n } else if (key.return) {\n store.enterFullscreen();\n // Space - toggle small expanded preview\n } else if (input === ' ') {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\n // Filter cycling - left/right arrows\n } else if (key.rightArrow && !expandedId) {\n store.cycleFilterNext();\n } else if (key.leftArrow && !expandedId) {\n store.cycleFilterPrev();\n // Search - '/' to start search\n } else if (input === '/' && !expandedId) {\n store.startSearch();\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 } else {\n store.selectFirst(visibleProcessCount);\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 } else {\n store.selectLast(visibleProcessCount);\n }\n // Page scrolling - Tab/Shift+Tab (use same page size as expanded view)\n } else if (key.tab && key.shift) {\n if (expandedId) {\n store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageUp(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n } else if (key.tab && !key.shift) {\n if (expandedId) {\n store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageDown(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\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}-${filterMode}-${searchTerm}-${isSearching}`;\n\n // Get expanded process info for fullscreen\n const expandedProcess = expandedId ? store.getProcess(expandedId) : null;\n\n // Render fullscreen overlay when active\n if (isFullscreen && expandedProcess) {\n return <FullscreenOverlay title={expandedProcess.group || expandedProcess.title} lines={store.getProcessLines(expandedProcess.id)} scrollOffset={scrollOffset} onExit={() => store.exitFullscreen()} />;\n }\n\n return (\n <Box key={layoutKey} flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Filter/Search bar (interactive mode only) */}\n {mode === 'interactive' && !expandedId && (\n <Box>\n <Text dimColor>◀ </Text>\n <Text color={filterMode === 'running' ? 'yellow' : filterMode === 'failed' ? 'red' : filterMode === 'finished' ? 'green' : 'cyan'} bold>\n {filterLabels[filterMode]}\n </Text>\n <Text dimColor> ▶</Text>\n {isSearching ? (\n <Text>\n {' '}\n <Text dimColor>/</Text>\n <Text>{searchTerm}</Text>\n <Text dimColor>▋</Text>\n </Text>\n ) : searchTerm ? (\n <Text dimColor> \"{searchTerm}\"</Text>\n ) : (\n <Text dimColor> (/ search)</Text>\n )}\n {processes.length !== allProcesses.length && (\n <Text dimColor>\n {' '}\n [{processes.length}/{allProcesses.length}]\n </Text>\n )}\n </Box>\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 {/* List scroll hint (interactive mode without expansion) */}\n {mode === 'interactive' && !expandedId && processes.length > visibleProcessCount && (\n <Text dimColor>\n [+{processes.length - visibleProcessCount} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom]\n </Text>\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","FullscreenOverlay","StatusBar","isMac","process","platform","AppContent","store","exit","isRawModeSupported","stdout","terminalHeight","rows","allProcesses","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","filterMode","getFilterMode","searchTerm","getSearchTerm","isSearching","getIsSearching","isFullscreen","getIsFullscreen","_bufferVersion","getBufferVersion","processes","getFilteredProcesses","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","filterBarHeight","listHintHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","filterLabels","all","running","finished","failed","setMode","clampListViewport","fullscreenVisibleLines","input","key","baseReserved","visibleWhenCollapsed","escape","return","exitFullscreen","meta","upArrow","scrollToTop","downArrow","scrollToBottom","tab","shift","scrollPageUp","scrollPageDown","scrollDown","scrollUp","cancelSearch","confirmSearch","backspace","delete","updateSearchTerm","slice","ctrl","toggleErrorFooter","visibleWhenExpanded","collapse","clearSearch","signalExit","enterFullscreen","toggleExpand","rightArrow","cycleFilterNext","leftArrow","cycleFilterPrev","startSearch","selectFirst","selectLast","selectPageUp","selectPageDown","selectNext","selectPrev","isActive","visibleProcesses","showSelection","layoutKey","expandedProcess","getProcess","title","group","lines","getProcessLines","id","onExit","flexDirection","dimColor","color","bold","length","map","item","originalIndex","indexOf","isSelected","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 FullscreenOverlay from './FullscreenOverlay.ts';\nimport StatusBar from './StatusBar.ts';\n\nconst isMac = process.platform === 'darwin';\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 allProcesses = 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 const filterMode = useSyncExternalStore(store.subscribe, store.getFilterMode);\n const searchTerm = useSyncExternalStore(store.subscribe, store.getSearchTerm);\n const isSearching = useSyncExternalStore(store.subscribe, store.getIsSearching);\n const isFullscreen = useSyncExternalStore(store.subscribe, store.getIsFullscreen);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Use filtered processes for display\n const processes = store.getFilteredProcesses();\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 // In interactive mode without expansion, reserve space for filter bar and list scroll hint\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const filterBarHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for filter/search bar\n const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + filterBarHeight + listHintHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from allProcesses - total counts regardless of filter)\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 // Filter mode display labels\n const filterLabels: Record<string, string> = {\n all: 'All',\n running: 'Running',\n finished: 'Finished',\n failed: 'Failed',\n };\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 // Clamp viewport when collapsing to avoid empty space\n // This runs after render with correct visibleProcessCount\n useEffect(() => {\n if (mode === 'interactive' && !expandedId) {\n store.clampListViewport(visibleProcessCount);\n }\n }, [mode, expandedId, visibleProcessCount, store]);\n\n // Calculate fullscreen visible lines (terminal height minus header and footer)\n const fullscreenVisibleLines = Math.max(1, terminalHeight - 3); // -3 for title, divider, footer\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n // Fullscreen mode input handling\n if (isFullscreen) {\n // Pre-calculate for viewport adjustment\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2);\n\n if (input === 'q' || key.escape || key.return) {\n store.exitFullscreen(visibleWhenCollapsed);\n } else if ((key.meta && key.upArrow) || input === 'g') {\n store.scrollToTop();\n } else if ((key.meta && key.downArrow) || input === 'G') {\n store.scrollToBottom(fullscreenVisibleLines);\n } else if (key.tab && key.shift) {\n store.scrollPageUp(fullscreenVisibleLines);\n } else if (key.tab && !key.shift) {\n store.scrollPageDown(fullscreenVisibleLines);\n } else if (key.downArrow || input === 'j') {\n store.scrollDown(fullscreenVisibleLines);\n } else if (key.upArrow || input === 'k') {\n store.scrollUp();\n }\n return;\n }\n\n // Search mode input handling\n if (isSearching) {\n if (key.escape) {\n store.cancelSearch();\n } else if (key.return) {\n store.confirmSearch();\n } else if (key.backspace || key.delete) {\n store.updateSearchTerm(searchTerm.slice(0, -1));\n } else if (input && !key.ctrl && !key.meta) {\n store.updateSearchTerm(searchTerm + input);\n }\n return;\n }\n\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 // Pre-calculate visible counts for expand/collapse transitions\n const baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - EXPANDED_MAX_VISIBLE_LINES - 1);\n const visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint\n\n if (input === 'q' || key.escape || (key.return && expandedId)) {\n // q/Esc/Enter all exit expanded mode; q/Esc exit app when not expanded\n if (expandedId) {\n store.collapse(visibleWhenCollapsed);\n } else if (key.escape && searchTerm) {\n // Clear search first before exiting\n store.clearSearch();\n } else if (input === 'q' || key.escape) {\n store.signalExit(() => {});\n }\n // Enter - fullscreen view (from list only, not expanded)\n } else if (key.return && !expandedId) {\n store.enterFullscreen();\n // Space - toggle small expanded preview\n } else if (input === ' ') {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\n // Filter cycling - left/right arrows\n } else if (key.rightArrow && !expandedId) {\n store.cycleFilterNext();\n } else if (key.leftArrow && !expandedId) {\n store.cycleFilterPrev();\n // Search - '/' to start search\n } else if (input === '/' && !expandedId) {\n store.startSearch();\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 } else {\n store.selectFirst(visibleProcessCount);\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 } else {\n store.selectLast(visibleProcessCount);\n }\n // Page scrolling - Tab/Shift+Tab (use same page size as expanded view)\n } else if (key.tab && key.shift) {\n if (expandedId) {\n store.scrollPageUp(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageUp(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\n }\n } else if (key.tab && !key.shift) {\n if (expandedId) {\n store.scrollPageDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectPageDown(EXPANDED_MAX_VISIBLE_LINES, visibleProcessCount);\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}-${filterMode}-${searchTerm}-${isSearching}`;\n\n // Get expanded process info for fullscreen\n const expandedProcess = expandedId ? store.getProcess(expandedId) : null;\n\n // Render fullscreen overlay when active\n if (isFullscreen && expandedProcess) {\n return <FullscreenOverlay title={expandedProcess.group || expandedProcess.title} lines={store.getProcessLines(expandedProcess.id)} scrollOffset={scrollOffset} onExit={() => store.exitFullscreen()} />;\n }\n\n return (\n <Box key={layoutKey} flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Filter/Search bar (interactive mode only) */}\n {mode === 'interactive' && !expandedId && (\n <Box>\n <Text dimColor>◀ </Text>\n <Text color={filterMode === 'running' ? 'yellow' : filterMode === 'failed' ? 'red' : filterMode === 'finished' ? 'green' : 'cyan'} bold>\n {filterLabels[filterMode]}\n </Text>\n <Text dimColor> ▶</Text>\n {isSearching ? (\n <Text>\n {' '}\n <Text dimColor>/</Text>\n <Text>{searchTerm}</Text>\n <Text dimColor>▋</Text>\n </Text>\n ) : searchTerm ? (\n <Text dimColor> \"{searchTerm}\"</Text>\n ) : (\n <Text dimColor> (/ search)</Text>\n )}\n {processes.length !== allProcesses.length && (\n <Text dimColor>\n {' '}\n [{processes.length}/{allProcesses.length}]\n </Text>\n )}\n </Box>\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 {/* List scroll hint (interactive mode without expansion) */}\n {mode === 'interactive' && !expandedId && processes.length > visibleProcessCount && (\n <Text dimColor>\n [+{processes.length - visibleProcessCount} more, Tab/⇧Tab page, {isMac ? '⌥↑/↓' : 'g/G'} top/bottom]\n </Text>\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","FullscreenOverlay","StatusBar","isMac","process","platform","AppContent","store","exit","isRawModeSupported","stdout","terminalHeight","rows","allProcesses","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","filterMode","getFilterMode","searchTerm","getSearchTerm","isSearching","getIsSearching","isFullscreen","getIsFullscreen","_bufferVersion","getBufferVersion","processes","getFilteredProcesses","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","filterBarHeight","listHintHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","filterLabels","all","running","finished","failed","setMode","clampListViewport","fullscreenVisibleLines","input","key","baseReserved","visibleWhenCollapsed","escape","return","exitFullscreen","meta","upArrow","scrollToTop","downArrow","scrollToBottom","tab","shift","scrollPageUp","scrollPageDown","scrollDown","scrollUp","cancelSearch","confirmSearch","backspace","delete","updateSearchTerm","slice","ctrl","toggleErrorFooter","visibleWhenExpanded","collapse","clearSearch","signalExit","enterFullscreen","toggleExpand","rightArrow","cycleFilterNext","leftArrow","cycleFilterPrev","startSearch","selectFirst","selectLast","selectPageUp","selectPageDown","selectNext","selectPrev","isActive","visibleProcesses","showSelection","layoutKey","expandedProcess","getProcess","title","group","lines","getProcessLines","id","onExit","flexDirection","dimColor","color","bold","length","map","item","originalIndex","indexOf","isSelected","done","errors","isExpanded","App","Provider","value"],"mappings":"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"}
@@ -34,11 +34,11 @@ export default /*#__PURE__*/ memo(function ExpandedOutput({ lines, scrollOffset,
34
34
  remaining,
35
35
  " more, Tab/⇧Tab page, ",
36
36
  isMac ? '⌥↑/↓' : 'g/G',
37
- " top/bottom, ↵ fullscreen]"
37
+ " top/bottom, ↵ close]"
38
38
  ]
39
39
  }) : /*#__PURE__*/ _jsx(Text, {
40
40
  dimColor: true,
41
- children: "│ [↵ fullscreen]"
41
+ children: "│ [↵ close]"
42
42
  })
43
43
  ]
44
44
  });
@@ -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\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, ↵ fullscreen]\n </Text>\n ) : (\n <Text dimColor>│ [↵ fullscreen]</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,wBACC,MAACZ;gBAAKgB,QAAQ;;oBAAC;oBACRF;oBAAU;oBAAuBX,QAAQ,SAAS;oBAAM;;+BAG/D,KAACH;gBAAKgB,QAAQ;0BAAC;;;;AAIvB,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, ↵ close]\n </Text>\n ) : (\n <Text dimColor>│ [↵ close]</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,wBACC,MAACZ;gBAAKgB,QAAQ;;oBAAC;oBACRF;oBAAU;oBAAuBX,QAAQ,SAAS;oBAAM;;+BAG/D,KAACH;gBAAKgB,QAAQ;0BAAC;;;;AAIvB,GAAG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spawn-term",
3
- "version": "3.2.5",
3
+ "version": "3.2.6",
4
4
  "description": "Formats spawn with for terminal grouping",
5
5
  "keywords": [
6
6
  "spawn",