spawn-term 3.1.5 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/App.js +136 -6
- package/dist/cjs/components/App.js.map +1 -1
- package/dist/cjs/components/ExpandedOutput.js +5 -2
- package/dist/cjs/components/ExpandedOutput.js.map +1 -1
- package/dist/cjs/components/FullscreenOverlay.js +88 -0
- package/dist/cjs/components/FullscreenOverlay.js.map +1 -0
- package/dist/cjs/src/components/FullscreenOverlay.d.ts +9 -0
- package/dist/cjs/src/state/processStore.d.ts +19 -0
- package/dist/cjs/state/processStore.js +119 -2
- package/dist/cjs/state/processStore.js.map +1 -1
- package/dist/esm/components/App.js +134 -6
- package/dist/esm/components/App.js.map +1 -1
- package/dist/esm/components/ExpandedOutput.js +5 -2
- package/dist/esm/components/ExpandedOutput.js.map +1 -1
- package/dist/esm/components/FullscreenOverlay.js +74 -0
- package/dist/esm/components/FullscreenOverlay.js.map +1 -0
- package/dist/esm/src/components/FullscreenOverlay.d.ts +9 -0
- package/dist/esm/src/state/processStore.d.ts +19 -0
- package/dist/esm/state/processStore.js +103 -2
- package/dist/esm/state/processStore.js.map +1 -1
- package/package.json +1 -1
|
@@ -18,6 +18,7 @@ var _CompactProcessLinets = /*#__PURE__*/ _interop_require_default(require("./Co
|
|
|
18
18
|
var _Dividerts = /*#__PURE__*/ _interop_require_default(require("./Divider.js"));
|
|
19
19
|
var _ErrorFooterts = /*#__PURE__*/ _interop_require_default(require("./ErrorFooter.js"));
|
|
20
20
|
var _ExpandedOutputts = /*#__PURE__*/ _interop_require_default(require("./ExpandedOutput.js"));
|
|
21
|
+
var _FullscreenOverlayts = /*#__PURE__*/ _interop_require_default(require("./FullscreenOverlay.js"));
|
|
21
22
|
var _StatusBarts = /*#__PURE__*/ _interop_require_default(require("./StatusBar.js"));
|
|
22
23
|
function _interop_require_default(obj) {
|
|
23
24
|
return obj && obj.__esModule ? obj : {
|
|
@@ -32,7 +33,7 @@ function AppContent(param) {
|
|
|
32
33
|
var stdout = (0, _ink.useStdout)().stdout;
|
|
33
34
|
var terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
|
|
34
35
|
// Subscribe to store state
|
|
35
|
-
var
|
|
36
|
+
var allProcesses = (0, _react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
36
37
|
var shouldExit = (0, _react.useSyncExternalStore)(store.subscribe, store.getShouldExit);
|
|
37
38
|
var mode = (0, _react.useSyncExternalStore)(store.subscribe, store.getMode);
|
|
38
39
|
var selectedIndex = (0, _react.useSyncExternalStore)(store.subscribe, store.getSelectedIndex);
|
|
@@ -40,26 +41,40 @@ function AppContent(param) {
|
|
|
40
41
|
var scrollOffset = (0, _react.useSyncExternalStore)(store.subscribe, store.getScrollOffset);
|
|
41
42
|
var listScrollOffset = (0, _react.useSyncExternalStore)(store.subscribe, store.getListScrollOffset);
|
|
42
43
|
var errorFooterExpanded = (0, _react.useSyncExternalStore)(store.subscribe, store.getErrorFooterExpanded);
|
|
44
|
+
var filterMode = (0, _react.useSyncExternalStore)(store.subscribe, store.getFilterMode);
|
|
45
|
+
var searchTerm = (0, _react.useSyncExternalStore)(store.subscribe, store.getSearchTerm);
|
|
46
|
+
var isSearching = (0, _react.useSyncExternalStore)(store.subscribe, store.getIsSearching);
|
|
47
|
+
var isFullscreen = (0, _react.useSyncExternalStore)(store.subscribe, store.getIsFullscreen);
|
|
43
48
|
// Subscribe to buffer version to trigger re-renders when terminal buffer content changes
|
|
44
49
|
var _bufferVersion = (0, _react.useSyncExternalStore)(store.subscribe, store.getBufferVersion);
|
|
50
|
+
// Use filtered processes for display
|
|
51
|
+
var processes = store.getFilteredProcesses();
|
|
45
52
|
// Subscribed state that triggers re-renders
|
|
46
53
|
var header = (0, _react.useSyncExternalStore)(store.subscribe, store.getHeader);
|
|
47
54
|
var showStatusBar = (0, _react.useSyncExternalStore)(store.subscribe, store.getShowStatusBar);
|
|
48
55
|
var isInteractive = (0, _react.useSyncExternalStore)(store.subscribe, store.getIsInteractive);
|
|
49
56
|
// Calculate visible process count (reserve lines for header, divider, status bar, expanded output)
|
|
50
57
|
// When a process is expanded, reserve space for the expanded output to prevent terminal scrolling
|
|
51
|
-
// In interactive mode without expansion, reserve space for
|
|
58
|
+
// In interactive mode without expansion, reserve space for filter bar and list scroll hint
|
|
52
59
|
var expandedHeight = expandedId ? _constantsts.EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint
|
|
60
|
+
var filterBarHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for filter/search bar
|
|
53
61
|
var listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint
|
|
54
|
-
var reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + listHintHeight;
|
|
62
|
+
var reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + filterBarHeight + listHintHeight;
|
|
55
63
|
var visibleProcessCount = Math.max(1, terminalHeight - reservedLines);
|
|
56
|
-
// Derived state (computed from
|
|
64
|
+
// Derived state (computed from allProcesses - total counts regardless of filter)
|
|
57
65
|
var runningCount = store.getRunningCount();
|
|
58
66
|
var doneCount = store.getDoneCount();
|
|
59
67
|
var errorCount = store.getErrorCount();
|
|
60
68
|
var errorLineCount = store.getErrorLineCount();
|
|
61
69
|
var _isAllComplete = store.isAllComplete();
|
|
62
70
|
var errorLines = store.getErrorLines();
|
|
71
|
+
// Filter mode display labels
|
|
72
|
+
var filterLabels = {
|
|
73
|
+
all: 'All',
|
|
74
|
+
running: 'Running',
|
|
75
|
+
finished: 'Finished',
|
|
76
|
+
failed: 'Failed'
|
|
77
|
+
};
|
|
63
78
|
// Handle exit signal
|
|
64
79
|
(0, _react.useEffect)(function() {
|
|
65
80
|
if (shouldExit) {
|
|
@@ -92,8 +107,42 @@ function AppContent(param) {
|
|
|
92
107
|
visibleProcessCount,
|
|
93
108
|
store
|
|
94
109
|
]);
|
|
110
|
+
// Calculate fullscreen visible lines (terminal height minus header and footer)
|
|
111
|
+
var fullscreenVisibleLines = Math.max(1, terminalHeight - 3); // -3 for title, divider, footer
|
|
95
112
|
// Keyboard handling (only active when raw mode is supported)
|
|
96
113
|
(0, _ink.useInput)(function(input, key) {
|
|
114
|
+
// Fullscreen mode input handling
|
|
115
|
+
if (isFullscreen) {
|
|
116
|
+
if (input === 'q' || key.escape) {
|
|
117
|
+
store.exitFullscreen();
|
|
118
|
+
} else if (key.meta && key.upArrow || input === 'g') {
|
|
119
|
+
store.scrollToTop();
|
|
120
|
+
} else if (key.meta && key.downArrow || input === 'G') {
|
|
121
|
+
store.scrollToBottom(fullscreenVisibleLines);
|
|
122
|
+
} else if (key.tab && key.shift) {
|
|
123
|
+
store.scrollPageUp(fullscreenVisibleLines);
|
|
124
|
+
} else if (key.tab && !key.shift) {
|
|
125
|
+
store.scrollPageDown(fullscreenVisibleLines);
|
|
126
|
+
} else if (key.downArrow || input === 'j') {
|
|
127
|
+
store.scrollDown(fullscreenVisibleLines);
|
|
128
|
+
} else if (key.upArrow || input === 'k') {
|
|
129
|
+
store.scrollUp();
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Search mode input handling
|
|
134
|
+
if (isSearching) {
|
|
135
|
+
if (key.escape) {
|
|
136
|
+
store.cancelSearch();
|
|
137
|
+
} else if (key.return) {
|
|
138
|
+
store.confirmSearch();
|
|
139
|
+
} else if (key.backspace || key.delete) {
|
|
140
|
+
store.updateSearchTerm(searchTerm.slice(0, -1));
|
|
141
|
+
} else if (input && !key.ctrl && !key.meta) {
|
|
142
|
+
store.updateSearchTerm(searchTerm + input);
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
97
146
|
if (mode === 'normal') {
|
|
98
147
|
// In non-interactive mode, 'e' toggles error footer
|
|
99
148
|
if (input === 'e' && errorCount > 0) {
|
|
@@ -103,15 +152,29 @@ function AppContent(param) {
|
|
|
103
152
|
// Pre-calculate visible counts for expand/collapse transitions
|
|
104
153
|
var baseReserved = (header ? 2 : 0) + (showStatusBar ? 2 : 0);
|
|
105
154
|
var visibleWhenExpanded = Math.max(1, terminalHeight - baseReserved - _constantsts.EXPANDED_MAX_VISIBLE_LINES - 1);
|
|
106
|
-
var visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved -
|
|
155
|
+
var visibleWhenCollapsed = Math.max(1, terminalHeight - baseReserved - 2); // -2 for filter bar + list hint
|
|
107
156
|
if (input === 'q' || key.escape) {
|
|
108
157
|
if (expandedId) {
|
|
109
158
|
store.collapse(visibleWhenCollapsed);
|
|
159
|
+
} else if (searchTerm) {
|
|
160
|
+
// Clear search first before exiting
|
|
161
|
+
store.clearSearch();
|
|
110
162
|
} else {
|
|
111
163
|
store.signalExit(function() {});
|
|
112
164
|
}
|
|
113
165
|
} else if (key.return) {
|
|
114
166
|
store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);
|
|
167
|
+
// Fullscreen - 'f' to enter fullscreen when expanded
|
|
168
|
+
} else if (input === 'f' && expandedId) {
|
|
169
|
+
store.enterFullscreen();
|
|
170
|
+
// Filter cycling - left/right arrows
|
|
171
|
+
} else if (key.rightArrow && !expandedId) {
|
|
172
|
+
store.cycleFilterNext();
|
|
173
|
+
} else if (key.leftArrow && !expandedId) {
|
|
174
|
+
store.cycleFilterPrev();
|
|
175
|
+
// Search - '/' to start search
|
|
176
|
+
} else if (input === '/' && !expandedId) {
|
|
177
|
+
store.startSearch();
|
|
115
178
|
// Jump to top - Option+↑ (detected as meta), vim: g
|
|
116
179
|
// Must check meta+arrow BEFORE plain arrow
|
|
117
180
|
} else if (key.meta && key.upArrow || input === 'g') {
|
|
@@ -174,7 +237,20 @@ function AppContent(param) {
|
|
|
174
237
|
var showSelection = mode === 'interactive';
|
|
175
238
|
// Force full re-render when layout structure changes
|
|
176
239
|
// Note: scrollOffset is NOT included - scrolling within expansion doesn't change structure
|
|
177
|
-
var layoutKey = "".concat(listScrollOffset, "-").concat(expandedId, "-").concat(errorCount, "-").concat(errorFooterExpanded);
|
|
240
|
+
var layoutKey = "".concat(listScrollOffset, "-").concat(expandedId, "-").concat(errorCount, "-").concat(errorFooterExpanded, "-").concat(filterMode, "-").concat(searchTerm, "-").concat(isSearching);
|
|
241
|
+
// Get expanded process info for fullscreen
|
|
242
|
+
var expandedProcess = expandedId ? store.getProcess(expandedId) : null;
|
|
243
|
+
// Render fullscreen overlay when active
|
|
244
|
+
if (isFullscreen && expandedProcess) {
|
|
245
|
+
return /*#__PURE__*/ (0, _jsxruntime.jsx)(_FullscreenOverlayts.default, {
|
|
246
|
+
title: expandedProcess.group || expandedProcess.title,
|
|
247
|
+
lines: store.getProcessLines(expandedProcess.id),
|
|
248
|
+
scrollOffset: scrollOffset,
|
|
249
|
+
onExit: function() {
|
|
250
|
+
return store.exitFullscreen();
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
178
254
|
return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
|
|
179
255
|
flexDirection: "column",
|
|
180
256
|
children: [
|
|
@@ -186,6 +262,60 @@ function AppContent(param) {
|
|
|
186
262
|
/*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {})
|
|
187
263
|
]
|
|
188
264
|
}),
|
|
265
|
+
mode === 'interactive' && !expandedId && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
|
|
266
|
+
children: [
|
|
267
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
268
|
+
dimColor: true,
|
|
269
|
+
children: "◀ "
|
|
270
|
+
}),
|
|
271
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
272
|
+
color: filterMode === 'running' ? 'yellow' : filterMode === 'failed' ? 'red' : filterMode === 'finished' ? 'green' : 'cyan',
|
|
273
|
+
bold: true,
|
|
274
|
+
children: filterLabels[filterMode]
|
|
275
|
+
}),
|
|
276
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
277
|
+
dimColor: true,
|
|
278
|
+
children: " ▶"
|
|
279
|
+
}),
|
|
280
|
+
isSearching ? /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
|
|
281
|
+
children: [
|
|
282
|
+
' ',
|
|
283
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
284
|
+
dimColor: true,
|
|
285
|
+
children: "/"
|
|
286
|
+
}),
|
|
287
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
288
|
+
children: searchTerm
|
|
289
|
+
}),
|
|
290
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
291
|
+
dimColor: true,
|
|
292
|
+
children: "▋"
|
|
293
|
+
})
|
|
294
|
+
]
|
|
295
|
+
}) : searchTerm ? /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
|
|
296
|
+
dimColor: true,
|
|
297
|
+
children: [
|
|
298
|
+
' "',
|
|
299
|
+
searchTerm,
|
|
300
|
+
'"'
|
|
301
|
+
]
|
|
302
|
+
}) : /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
303
|
+
dimColor: true,
|
|
304
|
+
children: " (/ search)"
|
|
305
|
+
}),
|
|
306
|
+
processes.length !== allProcesses.length && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
|
|
307
|
+
dimColor: true,
|
|
308
|
+
children: [
|
|
309
|
+
' ',
|
|
310
|
+
"[",
|
|
311
|
+
processes.length,
|
|
312
|
+
"/",
|
|
313
|
+
allProcesses.length,
|
|
314
|
+
"]"
|
|
315
|
+
]
|
|
316
|
+
})
|
|
317
|
+
]
|
|
318
|
+
}),
|
|
189
319
|
/*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
|
|
190
320
|
flexDirection: "column",
|
|
191
321
|
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\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 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 // In interactive mode without expansion, reserve space for potential list scroll hint\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const listHintHeight = mode === 'interactive' && !expandedId ? 1 : 0; // Reserve for list scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight + listHintHeight;\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 // 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 // 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 // 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 - 1); // -1 for list hint\n\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse(visibleWhenCollapsed);\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\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}`;\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 {/* 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","processes","useSyncExternalStore","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","EXPANDED_MAX_VISIBLE_LINES","listHintHeight","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","useEffect","setMode","clampListViewport","useInput","input","key","toggleErrorFooter","baseReserved","visibleWhenExpanded","visibleWhenCollapsed","escape","collapse","signalExit","return","toggleExpand","meta","upArrow","scrollToTop","selectFirst","downArrow","scrollToBottom","selectLast","tab","shift","scrollPageUp","selectPageUp","scrollPageDown","selectPageDown","scrollDown","selectNext","scrollUp","selectPrev","isActive","visibleProcesses","useMemo","slice","showSelection","layoutKey","Box","flexDirection","Text","Divider","map","item","originalIndex","indexOf","CompactProcessLine","isSelected","id","ExpandedOutput","lines","getProcessLines","length","dimColor","StatusBar","running","done","errors","ErrorFooter","isExpanded","StoreContext","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 if (input === 'q' || key.escape) {\n store.exitFullscreen();\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 } else if (key.return) {\n store.toggleExpand(visibleWhenExpanded, visibleWhenCollapsed);\n // Fullscreen - 'f' to enter fullscreen when expanded\n } else if (input === 'f' && expandedId) {\n store.enterFullscreen();\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","escape","exitFullscreen","meta","upArrow","scrollToTop","downArrow","scrollToBottom","tab","shift","scrollPageUp","scrollPageDown","scrollDown","scrollUp","cancelSearch","return","confirmSearch","backspace","delete","updateSearchTerm","slice","ctrl","toggleErrorFooter","baseReserved","visibleWhenExpanded","visibleWhenCollapsed","collapse","clearSearch","signalExit","toggleExpand","enterFullscreen","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":";;;;+BAuTA,gDAAgD;AAChD;;;eAAwBA;;;;mBAxTyC;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,IAAI0C,UAAU,OAAOC,IAAIC,MAAM,EAAE;gBAC/B7E,MAAM8E,cAAc;YACtB,OAAO,IAAI,AAACF,IAAIG,IAAI,IAAIH,IAAII,OAAO,IAAKL,UAAU,KAAK;gBACrD3E,MAAMiF,WAAW;YACnB,OAAO,IAAI,AAACL,IAAIG,IAAI,IAAIH,IAAIM,SAAS,IAAKP,UAAU,KAAK;gBACvD3E,MAAMmF,cAAc,CAACV;YACvB,OAAO,IAAIG,IAAIQ,GAAG,IAAIR,IAAIS,KAAK,EAAE;gBAC/BrF,MAAMsF,YAAY,CAACb;YACrB,OAAO,IAAIG,IAAIQ,GAAG,IAAI,CAACR,IAAIS,KAAK,EAAE;gBAChCrF,MAAMuF,cAAc,CAACd;YACvB,OAAO,IAAIG,IAAIM,SAAS,IAAIP,UAAU,KAAK;gBACzC3E,MAAMwF,UAAU,CAACf;YACnB,OAAO,IAAIG,IAAII,OAAO,IAAIL,UAAU,KAAK;gBACvC3E,MAAMyF,QAAQ;YAChB;YACA;QACF;QAEA,6BAA6B;QAC7B,IAAI1D,aAAa;YACf,IAAI6C,IAAIC,MAAM,EAAE;gBACd7E,MAAM0F,YAAY;YACpB,OAAO,IAAId,IAAIe,MAAM,EAAE;gBACrB3F,MAAM4F,aAAa;YACrB,OAAO,IAAIhB,IAAIiB,SAAS,IAAIjB,IAAIkB,MAAM,EAAE;gBACtC9F,MAAM+F,gBAAgB,CAAClE,WAAWmE,KAAK,CAAC,GAAG,CAAC;YAC9C,OAAO,IAAIrB,SAAS,CAACC,IAAIqB,IAAI,IAAI,CAACrB,IAAIG,IAAI,EAAE;gBAC1C/E,MAAM+F,gBAAgB,CAAClE,aAAa8C;YACtC;YACA;QACF;QAEA,IAAI5D,SAAS,UAAU;YACrB,oDAAoD;YACpD,IAAI4D,UAAU,OAAOlB,aAAa,GAAG;gBACnCzD,MAAMkG,iBAAiB;YACzB;QACF,OAAO,IAAInF,SAAS,eAAe;YACjC,+DAA+D;YAC/D,IAAMoF,eAAe,AAAC5D,CAAAA,SAAS,IAAI,CAAA,IAAME,CAAAA,gBAAgB,IAAI,CAAA;YAC7D,IAAM2D,sBAAsBjD,KAAKC,GAAG,CAAC,GAAG7C,iBAAiB4F,eAAerD,uCAA0B,GAAG;YACrG,IAAMuD,uBAAuBlD,KAAKC,GAAG,CAAC,GAAG7C,iBAAiB4F,eAAe,IAAI,gCAAgC;YAE7G,IAAIxB,UAAU,OAAOC,IAAIC,MAAM,EAAE;gBAC/B,IAAI1D,YAAY;oBACdnB,MAAMsG,QAAQ,CAACD;gBACjB,OAAO,IAAIxE,YAAY;oBACrB,oCAAoC;oBACpC7B,MAAMuG,WAAW;gBACnB,OAAO;oBACLvG,MAAMwG,UAAU,CAAC,YAAO;gBAC1B;YACF,OAAO,IAAI5B,IAAIe,MAAM,EAAE;gBACrB3F,MAAMyG,YAAY,CAACL,qBAAqBC;YACxC,qDAAqD;YACvD,OAAO,IAAI1B,UAAU,OAAOxD,YAAY;gBACtCnB,MAAM0G,eAAe;YACrB,qCAAqC;YACvC,OAAO,IAAI9B,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,IAAIG,IAAI,IAAIH,IAAII,OAAO,IAAKL,UAAU,KAAK;gBACrD,IAAIxD,YAAY;oBACdnB,MAAMiF,WAAW;gBACnB,OAAO;oBACLjF,MAAMgH,WAAW,CAAC9D;gBACpB;YACA,uDAAuD;YACzD,OAAO,IAAI,AAAC0B,IAAIG,IAAI,IAAIH,IAAIM,SAAS,IAAKP,UAAU,KAAK;gBACvD,IAAIxD,YAAY;oBACdnB,MAAMmF,cAAc,CAACrC,uCAA0B;gBACjD,OAAO;oBACL9C,MAAMiH,UAAU,CAAC/D;gBACnB;YACA,uEAAuE;YACzE,OAAO,IAAI0B,IAAIQ,GAAG,IAAIR,IAAIS,KAAK,EAAE;gBAC/B,IAAIlE,YAAY;oBACdnB,MAAMsF,YAAY,CAACxC,uCAA0B;gBAC/C,OAAO;oBACL9C,MAAMkH,YAAY,CAACpE,uCAA0B,EAAEI;gBACjD;YACF,OAAO,IAAI0B,IAAIQ,GAAG,IAAI,CAACR,IAAIS,KAAK,EAAE;gBAChC,IAAIlE,YAAY;oBACdnB,MAAMuF,cAAc,CAACzC,uCAA0B;gBACjD,OAAO;oBACL9C,MAAMmH,cAAc,CAACrE,uCAA0B,EAAEI;gBACnD;YACA,sCAAsC;YACxC,OAAO,IAAI0B,IAAIM,SAAS,IAAIP,UAAU,KAAK;gBACzC,IAAIxD,YAAY;oBACdnB,MAAMwF,UAAU,CAAC1C,uCAA0B;gBAC7C,OAAO;oBACL9C,MAAMoH,UAAU,CAAClE;gBACnB;YACF,OAAO,IAAI0B,IAAII,OAAO,IAAIL,UAAU,KAAK;gBACvC,IAAIxD,YAAY;oBACdnB,MAAMyF,QAAQ;gBAChB,OAAO;oBACLzF,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,UAAU2D,KAAK,CAACzE,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,MAAM8E,cAAc;;;IACnM;IAEA,qBACE,sBAACsD,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"}
|
|
@@ -40,15 +40,18 @@ var _default = /*#__PURE__*/ (0, _react.memo)(function ExpandedOutput(param) {
|
|
|
40
40
|
]
|
|
41
41
|
}, scrollOffset + i));
|
|
42
42
|
}),
|
|
43
|
-
hasMore
|
|
43
|
+
hasMore ? /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
|
|
44
44
|
dimColor: true,
|
|
45
45
|
children: [
|
|
46
46
|
"│ [+",
|
|
47
47
|
remaining,
|
|
48
48
|
" more, Tab/⇧Tab page, ",
|
|
49
49
|
isMac ? '⌥↑/↓' : 'g/G',
|
|
50
|
-
" top/bottom]"
|
|
50
|
+
" top/bottom, f fullscreen]"
|
|
51
51
|
]
|
|
52
|
+
}) : /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
53
|
+
dimColor: true,
|
|
54
|
+
children: "│ [f fullscreen]"
|
|
52
55
|
})
|
|
53
56
|
]
|
|
54
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
|
|
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, f fullscreen]\n </Text>\n ) : (\n <Text dimColor>│ [f 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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return _default;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
var _jsxruntime = require("react/jsx-runtime");
|
|
12
|
+
var _ink = require("ink");
|
|
13
|
+
var _react = require("react");
|
|
14
|
+
var isMac = process.platform === 'darwin';
|
|
15
|
+
// ANSI escape codes for alternate screen buffer
|
|
16
|
+
var ENTER_ALT_SCREEN = '\x1b[?1049h';
|
|
17
|
+
var EXIT_ALT_SCREEN = '\x1b[?1049l';
|
|
18
|
+
var CLEAR_SCREEN = '\x1b[2J';
|
|
19
|
+
var CURSOR_HOME = '\x1b[H';
|
|
20
|
+
var _default = /*#__PURE__*/ (0, _react.memo)(function FullscreenOverlay(param) {
|
|
21
|
+
var title = param.title, lines = param.lines, scrollOffset = param.scrollOffset;
|
|
22
|
+
var stdout = (0, _ink.useStdout)().stdout;
|
|
23
|
+
var terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
|
|
24
|
+
// Reserve lines for header (title + divider) and footer (scroll hint)
|
|
25
|
+
var headerLines = 2;
|
|
26
|
+
var footerLines = 1;
|
|
27
|
+
var maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);
|
|
28
|
+
// Enter alternate screen on mount, exit on unmount
|
|
29
|
+
(0, _react.useEffect)(function() {
|
|
30
|
+
if (stdout) {
|
|
31
|
+
stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME);
|
|
32
|
+
}
|
|
33
|
+
return function() {
|
|
34
|
+
if (stdout) {
|
|
35
|
+
stdout.write(EXIT_ALT_SCREEN);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}, [
|
|
39
|
+
stdout
|
|
40
|
+
]);
|
|
41
|
+
var visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);
|
|
42
|
+
var totalLines = lines.length;
|
|
43
|
+
var currentLine = scrollOffset + 1;
|
|
44
|
+
var endLine = Math.min(scrollOffset + maxVisible, totalLines);
|
|
45
|
+
return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
|
|
46
|
+
flexDirection: "column",
|
|
47
|
+
height: terminalHeight,
|
|
48
|
+
children: [
|
|
49
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
50
|
+
bold: true,
|
|
51
|
+
color: "cyan",
|
|
52
|
+
children: title
|
|
53
|
+
}),
|
|
54
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
55
|
+
dimColor: true,
|
|
56
|
+
children: '─'.repeat(Math.min(80, (stdout === null || stdout === void 0 ? void 0 : stdout.columns) || 80))
|
|
57
|
+
}),
|
|
58
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Box, {
|
|
59
|
+
flexDirection: "column",
|
|
60
|
+
flexGrow: 1,
|
|
61
|
+
children: lines.length === 0 ? /*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
62
|
+
dimColor: true,
|
|
63
|
+
children: "(no output)"
|
|
64
|
+
}) : visibleLines.map(function(line, i) {
|
|
65
|
+
return(// biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view
|
|
66
|
+
/*#__PURE__*/ (0, _jsxruntime.jsx)(_ink.Text, {
|
|
67
|
+
children: line.text
|
|
68
|
+
}, scrollOffset + i));
|
|
69
|
+
})
|
|
70
|
+
}),
|
|
71
|
+
/*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Text, {
|
|
72
|
+
dimColor: true,
|
|
73
|
+
children: [
|
|
74
|
+
"Lines ",
|
|
75
|
+
currentLine,
|
|
76
|
+
"-",
|
|
77
|
+
endLine,
|
|
78
|
+
" of ",
|
|
79
|
+
totalLines,
|
|
80
|
+
" | j/k scroll | Tab/⇧Tab page | ",
|
|
81
|
+
isMac ? '⌥↑/↓' : 'g/G',
|
|
82
|
+
" top/bottom | q exit"
|
|
83
|
+
]
|
|
84
|
+
})
|
|
85
|
+
]
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/FullscreenOverlay.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo, useEffect } from 'react';\nimport type { Line } from '../types.ts';\n\nconst isMac = process.platform === 'darwin';\n\n// ANSI escape codes for alternate screen buffer\nconst ENTER_ALT_SCREEN = '\\x1b[?1049h';\nconst EXIT_ALT_SCREEN = '\\x1b[?1049l';\nconst CLEAR_SCREEN = '\\x1b[2J';\nconst CURSOR_HOME = '\\x1b[H';\n\ntype Props = {\n title: string;\n lines: Line[];\n scrollOffset: number;\n onExit: () => void;\n};\n\nexport default memo(function FullscreenOverlay({ title, lines, scrollOffset }: Props) {\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Reserve lines for header (title + divider) and footer (scroll hint)\n const headerLines = 2;\n const footerLines = 1;\n const maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);\n\n // Enter alternate screen on mount, exit on unmount\n useEffect(() => {\n if (stdout) {\n stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME);\n }\n return () => {\n if (stdout) {\n stdout.write(EXIT_ALT_SCREEN);\n }\n };\n }, [stdout]);\n\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const totalLines = lines.length;\n const currentLine = scrollOffset + 1;\n const endLine = Math.min(scrollOffset + maxVisible, totalLines);\n\n return (\n <Box flexDirection=\"column\" height={terminalHeight}>\n {/* Header */}\n <Text bold color=\"cyan\">\n {title}\n </Text>\n <Text dimColor>{'─'.repeat(Math.min(80, stdout?.columns || 80))}</Text>\n\n {/* Content */}\n <Box flexDirection=\"column\" flexGrow={1}>\n {lines.length === 0 ? (\n <Text dimColor>(no output)</Text>\n ) : (\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 )}\n </Box>\n\n {/* Footer */}\n <Text dimColor>\n Lines {currentLine}-{endLine} of {totalLines} | j/k scroll | Tab/⇧Tab page | {isMac ? '⌥↑/↓' : 'g/G'} top/bottom | q exit\n </Text>\n </Box>\n );\n});\n"],"names":["isMac","process","platform","ENTER_ALT_SCREEN","EXIT_ALT_SCREEN","CLEAR_SCREEN","CURSOR_HOME","memo","FullscreenOverlay","title","lines","scrollOffset","stdout","useStdout","terminalHeight","rows","headerLines","footerLines","maxVisible","Math","max","useEffect","write","visibleLines","slice","totalLines","length","currentLine","endLine","min","Box","flexDirection","height","Text","bold","color","dimColor","repeat","columns","flexGrow","map","line","i","text"],"mappings":";;;;+BAmBA;;;eAAA;;;;mBAnBqC;qBACL;AAGhC,IAAMA,QAAQC,QAAQC,QAAQ,KAAK;AAEnC,gDAAgD;AAChD,IAAMC,mBAAmB;AACzB,IAAMC,kBAAkB;AACxB,IAAMC,eAAe;AACrB,IAAMC,cAAc;IASpB,yBAAeC,IAAAA,WAAI,EAAC,SAASC,kBAAkB,KAAqC;QAAnCC,QAAF,MAAEA,OAAOC,QAAT,MAASA,OAAOC,eAAhB,MAAgBA;IAC7D,IAAM,AAAEC,SAAWC,IAAAA,cAAS,IAApBD;IACR,IAAME,iBAAiBF,CAAAA,mBAAAA,6BAAAA,OAAQG,IAAI,KAAI;IAEvC,sEAAsE;IACtE,IAAMC,cAAc;IACpB,IAAMC,cAAc;IACpB,IAAMC,aAAaC,KAAKC,GAAG,CAAC,GAAGN,iBAAiBE,cAAcC;IAE9D,mDAAmD;IACnDI,IAAAA,gBAAS,EAAC;QACR,IAAIT,QAAQ;YACVA,OAAOU,KAAK,CAACnB,mBAAmBE,eAAeC;QACjD;QACA,OAAO;YACL,IAAIM,QAAQ;gBACVA,OAAOU,KAAK,CAAClB;YACf;QACF;IACF,GAAG;QAACQ;KAAO;IAEX,IAAMW,eAAeb,MAAMc,KAAK,CAACb,cAAcA,eAAeO;IAC9D,IAAMO,aAAaf,MAAMgB,MAAM;IAC/B,IAAMC,cAAchB,eAAe;IACnC,IAAMiB,UAAUT,KAAKU,GAAG,CAAClB,eAAeO,YAAYO;IAEpD,qBACE,sBAACK,QAAG;QAACC,eAAc;QAASC,QAAQlB;;0BAElC,qBAACmB,SAAI;gBAACC,IAAI;gBAACC,OAAM;0BACd1B;;0BAEH,qBAACwB,SAAI;gBAACG,QAAQ;0BAAE,IAAIC,MAAM,CAAClB,KAAKU,GAAG,CAAC,IAAIjB,CAAAA,mBAAAA,6BAAAA,OAAQ0B,OAAO,KAAI;;0BAG3D,qBAACR,QAAG;gBAACC,eAAc;gBAASQ,UAAU;0BACnC7B,MAAMgB,MAAM,KAAK,kBAChB,qBAACO,SAAI;oBAACG,QAAQ;8BAAC;qBAEfb,aAAaiB,GAAG,CAAC,SAACC,MAAMC;2BACtB,iHAAiH;kCACjH,qBAACT,SAAI;kCAAyBQ,KAAKE,IAAI;uBAA5BhC,eAAe+B;;;0BAMhC,sBAACT,SAAI;gBAACG,QAAQ;;oBAAC;oBACNT;oBAAY;oBAAEC;oBAAQ;oBAAKH;oBAAW;oBAAiCzB,QAAQ,SAAS;oBAAM;;;;;AAI7G"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ChildProcess, Line, SessionOptions } from '../types.js';
|
|
2
2
|
type Listener = () => void;
|
|
3
3
|
type Mode = 'normal' | 'interactive';
|
|
4
|
+
type FilterMode = 'all' | 'running' | 'finished' | 'failed';
|
|
4
5
|
export declare class ProcessStore {
|
|
5
6
|
private processes;
|
|
6
7
|
private completedIds;
|
|
@@ -8,6 +9,10 @@ export declare class ProcessStore {
|
|
|
8
9
|
private mode;
|
|
9
10
|
private expandedId;
|
|
10
11
|
private errorFooterExpanded;
|
|
12
|
+
private filterMode;
|
|
13
|
+
private searchTerm;
|
|
14
|
+
private isSearching;
|
|
15
|
+
private isFullscreen;
|
|
11
16
|
private header;
|
|
12
17
|
private showStatusBar;
|
|
13
18
|
private isInteractive;
|
|
@@ -42,6 +47,11 @@ export declare class ProcessStore {
|
|
|
42
47
|
getListScrollOffset: () => number;
|
|
43
48
|
getErrorFooterExpanded: () => boolean;
|
|
44
49
|
getBufferVersion: () => number;
|
|
50
|
+
getFilterMode: () => FilterMode;
|
|
51
|
+
getSearchTerm: () => string;
|
|
52
|
+
getIsSearching: () => boolean;
|
|
53
|
+
getIsFullscreen: () => boolean;
|
|
54
|
+
getFilteredProcesses: () => ChildProcess[];
|
|
45
55
|
getScrollOffset: () => number;
|
|
46
56
|
getHeader: () => string | undefined;
|
|
47
57
|
getShowStatusBar: () => boolean;
|
|
@@ -49,6 +59,15 @@ export declare class ProcessStore {
|
|
|
49
59
|
isAllComplete: () => boolean;
|
|
50
60
|
setMode(mode: Mode): void;
|
|
51
61
|
getSelectedProcess(): ChildProcess | undefined;
|
|
62
|
+
cycleFilterNext(): void;
|
|
63
|
+
cycleFilterPrev(): void;
|
|
64
|
+
startSearch(): void;
|
|
65
|
+
updateSearchTerm(term: string): void;
|
|
66
|
+
cancelSearch(): void;
|
|
67
|
+
confirmSearch(): void;
|
|
68
|
+
clearSearch(): void;
|
|
69
|
+
enterFullscreen(): void;
|
|
70
|
+
exitFullscreen(): void;
|
|
52
71
|
toggleErrorFooter(): void;
|
|
53
72
|
expandErrorFooter(): void;
|
|
54
73
|
selectNext(visibleCount?: number): void;
|