panex 0.9.5 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +57 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +57 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -19,8 +19,10 @@ import { EventEmitter } from "events";
|
|
|
19
19
|
|
|
20
20
|
// src/terminal-buffer.ts
|
|
21
21
|
import { Terminal } from "@xterm/headless";
|
|
22
|
+
import { SerializeAddon } from "@xterm/addon-serialize";
|
|
22
23
|
var TerminalBuffer = class {
|
|
23
24
|
terminal;
|
|
25
|
+
serializeAddon;
|
|
24
26
|
rows;
|
|
25
27
|
cols;
|
|
26
28
|
constructor(cols = 200, rows = 500) {
|
|
@@ -32,6 +34,8 @@ var TerminalBuffer = class {
|
|
|
32
34
|
scrollback: 1e4,
|
|
33
35
|
allowProposedApi: true
|
|
34
36
|
});
|
|
37
|
+
this.serializeAddon = new SerializeAddon();
|
|
38
|
+
this.terminal.loadAddon(this.serializeAddon);
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
37
41
|
* Write data to the terminal buffer.
|
|
@@ -61,9 +65,10 @@ var TerminalBuffer = class {
|
|
|
61
65
|
}
|
|
62
66
|
/**
|
|
63
67
|
* Get the terminal content as a single string with newlines.
|
|
68
|
+
* Preserves ANSI color codes and formatting.
|
|
64
69
|
*/
|
|
65
70
|
toString() {
|
|
66
|
-
return this.
|
|
71
|
+
return this.serializeAddon.serialize();
|
|
67
72
|
}
|
|
68
73
|
/**
|
|
69
74
|
* Clear the terminal buffer.
|
|
@@ -404,6 +409,13 @@ var OutputPanel = forwardRef2(
|
|
|
404
409
|
const [scrollOffset, setScrollOffset] = useState3(0);
|
|
405
410
|
const [contentHeight, setContentHeight] = useState3(0);
|
|
406
411
|
const [viewportHeight, setViewportHeight] = useState3(0);
|
|
412
|
+
const [prevName, setPrevName] = useState3(name);
|
|
413
|
+
if (name !== prevName) {
|
|
414
|
+
setPrevName(name);
|
|
415
|
+
setScrollOffset(0);
|
|
416
|
+
setContentHeight(0);
|
|
417
|
+
setViewportHeight(0);
|
|
418
|
+
}
|
|
407
419
|
useEffect4(() => {
|
|
408
420
|
const handleResize = () => scrollRef.current?.remeasure();
|
|
409
421
|
stdout?.on("resize", handleResize);
|
|
@@ -467,7 +479,8 @@ var OutputPanel = forwardRef2(
|
|
|
467
479
|
onContentHeightChange: handleContentHeightChange,
|
|
468
480
|
onViewportSizeChange: (layout) => setViewportHeight(layout.height),
|
|
469
481
|
children: lines.map((line, i) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", children: line }, i))
|
|
470
|
-
}
|
|
482
|
+
},
|
|
483
|
+
name
|
|
471
484
|
) }),
|
|
472
485
|
hasScroll && /* @__PURE__ */ jsx3(
|
|
473
486
|
Scrollbar,
|
|
@@ -571,6 +584,8 @@ function App({ config }) {
|
|
|
571
584
|
const outputRef = useRef4(null);
|
|
572
585
|
const processListRef = useRef4(null);
|
|
573
586
|
const [autoScroll, setAutoScroll] = useState4({});
|
|
587
|
+
const scrollPositionsRef = useRef4({});
|
|
588
|
+
const pendingRestoreRef = useRef4(null);
|
|
574
589
|
const {
|
|
575
590
|
names,
|
|
576
591
|
getOutput,
|
|
@@ -613,6 +628,42 @@ function App({ config }) {
|
|
|
613
628
|
const selectedName = names[selected] ?? "";
|
|
614
629
|
const output = selectedName ? getOutput(selectedName) : "";
|
|
615
630
|
const currentAutoScroll = selectedName ? autoScroll[selectedName] ?? true : true;
|
|
631
|
+
const saveCurrentScrollPosition = useCallback5(() => {
|
|
632
|
+
if (selectedName && outputRef.current) {
|
|
633
|
+
scrollPositionsRef.current[selectedName] = outputRef.current.getScrollOffset();
|
|
634
|
+
}
|
|
635
|
+
}, [selectedName]);
|
|
636
|
+
const handleSetSelected = useCallback5((newSelected) => {
|
|
637
|
+
saveCurrentScrollPosition();
|
|
638
|
+
setSelected((prev) => {
|
|
639
|
+
const next = typeof newSelected === "function" ? newSelected(prev) : newSelected;
|
|
640
|
+
if (next !== prev) {
|
|
641
|
+
pendingRestoreRef.current = names[next] ?? null;
|
|
642
|
+
}
|
|
643
|
+
return next;
|
|
644
|
+
});
|
|
645
|
+
}, [saveCurrentScrollPosition, names]);
|
|
646
|
+
useEffect5(() => {
|
|
647
|
+
const restoreName = pendingRestoreRef.current;
|
|
648
|
+
if (restoreName && restoreName === selectedName) {
|
|
649
|
+
pendingRestoreRef.current = null;
|
|
650
|
+
const savedOffset = scrollPositionsRef.current[restoreName];
|
|
651
|
+
if (savedOffset !== void 0 && savedOffset > 0 && !autoScroll[restoreName]) {
|
|
652
|
+
setImmediate(() => {
|
|
653
|
+
if (outputRef.current) {
|
|
654
|
+
const contentHeight = outputRef.current.getContentHeight();
|
|
655
|
+
const viewportHeight = outputRef.current.getViewportHeight();
|
|
656
|
+
if (contentHeight > viewportHeight) {
|
|
657
|
+
const currentOffset = outputRef.current.getScrollOffset();
|
|
658
|
+
if (currentOffset !== savedOffset) {
|
|
659
|
+
outputRef.current.scrollBy(savedOffset - currentOffset);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}, [selectedName, autoScroll]);
|
|
616
667
|
const handleAutoScrollChange = useCallback5((enabled) => {
|
|
617
668
|
if (selectedName) {
|
|
618
669
|
setAutoScroll((prev) => ({ ...prev, [selectedName]: enabled }));
|
|
@@ -634,14 +685,14 @@ function App({ config }) {
|
|
|
634
685
|
}
|
|
635
686
|
const clickedIndex = event.y - 2;
|
|
636
687
|
if (clickedIndex >= 0 && clickedIndex < names.length) {
|
|
637
|
-
|
|
688
|
+
handleSetSelected(clickedIndex);
|
|
638
689
|
}
|
|
639
690
|
} else {
|
|
640
691
|
if (!focusMode) {
|
|
641
692
|
enterFocus();
|
|
642
693
|
}
|
|
643
694
|
}
|
|
644
|
-
}, [names.length, focusMode, enterFocus, exitFocus]);
|
|
695
|
+
}, [names.length, focusMode, enterFocus, exitFocus, handleSetSelected]);
|
|
645
696
|
useMouseWheel({
|
|
646
697
|
enabled: !showHelp,
|
|
647
698
|
// Disable when help is shown
|
|
@@ -715,11 +766,11 @@ function App({ config }) {
|
|
|
715
766
|
return;
|
|
716
767
|
}
|
|
717
768
|
if (key.upArrow || input === "k") {
|
|
718
|
-
|
|
769
|
+
handleSetSelected((s) => Math.max(s - 1, 0));
|
|
719
770
|
return;
|
|
720
771
|
}
|
|
721
772
|
if (key.downArrow || input === "j") {
|
|
722
|
-
|
|
773
|
+
handleSetSelected((s) => Math.min(s + 1, names.length - 1));
|
|
723
774
|
return;
|
|
724
775
|
}
|
|
725
776
|
if (key.return || key.tab) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/tui.ts","../src/components/App.tsx","../src/hooks/useProcessManager.ts","../src/process-manager.ts","../src/terminal-buffer.ts","../src/hooks/useFocusMode.ts","../src/hooks/useMouseWheel.ts","../src/components/ProcessList.tsx","../src/components/OutputPanel.tsx","../src/components/Scrollbar.tsx","../src/components/StatusBar.tsx","../src/components/HelpPopup.tsx"],"sourcesContent":["// Check for Bun runtime - must be at top before any Bun APIs are used\nif (typeof Bun === 'undefined') {\n console.error('Error: panex requires Bun runtime.');\n console.error('Please run with bunx instead of npx:');\n console.error('\\tbunx panex \"cmd1\" \"cmd2\"');\n process.exit(1);\n}\n\nimport { Command } from 'commander';\nimport type { PanexConfig } from './types';\nimport { createTUI } from './tui';\n\nconst program = new Command();\n\nprogram\n .name('panex')\n .description('Terminal UI for running multiple processes in parallel')\n .version('0.1.0')\n .argument('<commands...>', 'Commands to run in parallel')\n .option('-n, --names <names>', 'Comma-separated names for each process')\n .option('--no-shift-tab [names]', 'Disable Shift-Tab to exit focus (all or comma-separated process names)')\n .action(async (commands: string[], options: { names?: string; shiftTab?: boolean | string }) => {\n const rawNames = options.names?.split(',') ?? commands.map((_, i) => `proc${i + 1}`);\n\n // Ensure unique names by adding suffix for duplicates\n const usedNames = new Map<string, number>();\n const names = rawNames.map((name, i) => {\n const baseName = name || `proc${i + 1}`;\n const count = usedNames.get(baseName) ?? 0;\n usedNames.set(baseName, count + 1);\n return count === 0 ? baseName : `${baseName}-${count + 1}`;\n });\n\n // Parse noShiftTab option\n let noShiftTab: boolean | string[] | undefined;\n if (options.shiftTab === false) {\n noShiftTab = true; // --no-shift-tab without args disables for all\n } else if (typeof options.shiftTab === 'string') {\n noShiftTab = options.shiftTab.split(','); // --no-shift-tab api,mobile\n }\n\n const config: PanexConfig = {\n procs: Object.fromEntries(\n commands.map((cmd, i) => [names[i], { shell: cmd }])\n ),\n settings: {\n noShiftTab,\n },\n };\n\n await createTUI(config);\n });\n\nprogram.parse();","import { render } from 'ink';\nimport { createElement } from 'react';\nimport type { PanexConfig } from './types';\nimport { App } from './components/App';\n\nexport async function createTUI(config: PanexConfig): Promise<void> {\n const { waitUntilExit } = render(createElement(App, { config }));\n await waitUntilExit();\n}\n","import { useState, useEffect, useRef, useCallback } from 'react';\nimport { Box, useApp, useInput, useStdin, useStdout } from 'ink';\nimport type { PanexConfig } from '../types';\nimport { useProcessManager } from '../hooks/useProcessManager';\nimport { useFocusMode } from '../hooks/useFocusMode';\nimport { useMouseWheel } from '../hooks/useMouseWheel';\nimport { ProcessList, ProcessListRef, PROCESS_LIST_WIDTH } from './ProcessList';\nimport { OutputPanel, OutputPanelRef } from './OutputPanel';\nimport { StatusBar } from './StatusBar';\nimport { HelpPopup } from './HelpPopup';\n\ninterface AppProps {\n config: PanexConfig;\n}\n\nexport function App({ config }: AppProps) {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const { setRawMode } = useStdin();\n const [selected, setSelected] = useState(0);\n const [showHelp, setShowHelp] = useState(false);\n const { focusMode, enterFocus, exitFocus } = useFocusMode();\n\n // Refs to control scrolling\n const outputRef = useRef<OutputPanelRef>(null);\n const processListRef = useRef<ProcessListRef>(null);\n\n // Track auto-scroll state per process\n const [autoScroll, setAutoScroll] = useState<Record<string, boolean>>({});\n\n const {\n names,\n getOutput,\n getStatus,\n restart,\n restartAll,\n kill,\n killAll,\n write,\n resize,\n } = useProcessManager(config);\n\n // Check if Shift-Tab is disabled for a process\n const isShiftTabDisabled = (name: string): boolean => {\n const setting = config.settings?.noShiftTab;\n if (setting === true) return true;\n if (Array.isArray(setting)) return setting.includes(name);\n return false;\n };\n\n // Calculate max panel height: terminal rows - status bar (1)\n const maxPanelHeight = stdout ? stdout.rows - 1 : undefined;\n\n // Resize on terminal resize\n useEffect(() => {\n const name = names[selected];\n if (name && stdout) {\n const cols = Math.floor(stdout.columns * 0.8) - 2;\n const rows = stdout.rows - 3;\n resize(name, cols, rows);\n }\n }, [stdout?.columns, stdout?.rows, selected, names, resize]);\n\n // Initialize auto-scroll for new processes\n useEffect(() => {\n setAutoScroll(prev => {\n const next = { ...prev };\n let changed = false;\n for (const name of names) {\n if (next[name] === undefined) {\n next[name] = true;\n changed = true;\n }\n }\n return changed ? next : prev;\n });\n }, [names]);\n\n const selectedName = names[selected] ?? '';\n const output = selectedName ? getOutput(selectedName) : '';\n const currentAutoScroll = selectedName ? (autoScroll[selectedName] ?? true) : true;\n\n // Handle auto-scroll state changes from OutputPanel\n const handleAutoScrollChange = useCallback((enabled: boolean) => {\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: enabled }));\n }\n }, [selectedName]);\n\n // Handle mouse wheel events\n const handleWheel = useCallback((event: { type: 'wheel-up' | 'wheel-down'; x: number; y: number; }) => {\n const delta = event.type === 'wheel-up' ? -3 : 3;\n\n if (outputRef.current) {\n outputRef.current.scrollBy(delta);\n // Disable auto-scroll when user scrolls up\n if (event.type === 'wheel-up' && selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n }\n }, [selectedName]);\n\n // Handle mouse click events\n const handleClick = useCallback((event: { type: 'click'; x: number; y: number; }) => {\n if (event.x <= PROCESS_LIST_WIDTH) {\n // Click on process list - exit focus mode, select process if clicked\n if (focusMode) {\n exitFocus();\n }\n const clickedIndex = event.y - 2; // Adjust for border\n if (clickedIndex >= 0 && clickedIndex < names.length) {\n setSelected(clickedIndex);\n }\n } else {\n // Click on output panel - enter focus mode\n if (!focusMode) {\n enterFocus();\n }\n }\n }, [names.length, focusMode, enterFocus, exitFocus]);\n\n // Enable mouse tracking\n useMouseWheel({\n enabled: !showHelp, // Disable when help is shown\n onWheel: handleWheel,\n onClick: handleClick,\n });\n\n useInput((input, key) => {\n // Handle help popup\n if (showHelp) {\n setShowHelp(false);\n return;\n }\n\n // Quit (Ctrl+C always works, 'q' only in normal mode)\n if (key.ctrl && input === 'c') {\n killAll();\n // Restore terminal: disable raw mode, move cursor to bottom, clear below, disable mouse, show cursor\n setRawMode(false);\n const rows = stdout?.rows ?? 999;\n stdout?.write(`\\x1b[${rows};1H\\x1b[J\\x1b[?1000l\\x1b[?1006l\\x1b[?25h\\x1b[0m\\n`);\n exit();\n process.exit(0);\n }\n\n // Focus mode input handling (before normal mode keys like 'q')\n if (focusMode) {\n const name = names[selected];\n if (!name) return;\n\n // Exit focus\n if (key.escape) {\n exitFocus();\n return;\n }\n\n // Shift-Tab exit (unless disabled)\n if (key.shift && key.tab && !isShiftTabDisabled(name)) {\n exitFocus();\n return;\n }\n\n // Forward Enter\n if (key.return) {\n write(name, '\\r');\n return;\n }\n\n // Forward arrow keys\n if (key.upArrow) {\n write(name, '\\x1b[A');\n return;\n }\n if (key.downArrow) {\n write(name, '\\x1b[B');\n return;\n }\n if (key.leftArrow) {\n write(name, '\\x1b[D');\n return;\n }\n if (key.rightArrow) {\n write(name, '\\x1b[C');\n return;\n }\n\n // Forward regular input (filter out mouse escape sequences)\n if (input && !key.ctrl && !key.meta) {\n // Remove SGR mouse sequences like \\x1b[<64;45;5M or [<0;12;7M\n const filtered = input.replace(/\\x1b?\\[<\\d+;\\d+;\\d+[Mm]/g, '');\n if (filtered) {\n write(name, filtered);\n }\n }\n return;\n }\n\n // Normal mode\n\n // Quit with 'q' (only in normal mode)\n if (input === 'q') {\n killAll();\n setRawMode(false);\n const rows = stdout?.rows ?? 999;\n stdout?.write(`\\x1b[${rows};1H\\x1b[J\\x1b[?1000l\\x1b[?1006l\\x1b[?25h\\x1b[0m\\n`);\n exit();\n process.exit(0);\n }\n\n // Help\n if (input === '?') {\n setShowHelp(true);\n return;\n }\n\n // Navigation\n if (key.upArrow || input === 'k') {\n setSelected(s => Math.max(s - 1, 0));\n return;\n }\n if (key.downArrow || input === 'j') {\n setSelected(s => Math.min(s + 1, names.length - 1));\n return;\n }\n\n // Enter focus mode\n if (key.return || key.tab) {\n enterFocus();\n return;\n }\n\n // Process control\n if (input === 'r') {\n const name = names[selected];\n if (name) restart(name);\n return;\n }\n if (input === 'A') {\n restartAll();\n return;\n }\n if (input === 'x') {\n const name = names[selected];\n if (name) kill(name);\n return;\n }\n\n // Output scrolling\n if (input === 'g') {\n outputRef.current?.scrollToTop();\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n return;\n }\n if (input === 'G') {\n outputRef.current?.scrollToBottom();\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: true }));\n }\n return;\n }\n if (key.pageUp) {\n const pageSize = outputRef.current?.getViewportHeight() ?? 10;\n outputRef.current?.scrollBy(-pageSize);\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n return;\n }\n if (key.pageDown) {\n const pageSize = outputRef.current?.getViewportHeight() ?? 10;\n outputRef.current?.scrollBy(pageSize);\n return;\n }\n });\n\n const showShiftTabHint = selectedName ? !isShiftTabDisabled(selectedName) : true;\n\n return (\n <Box flexDirection=\"column\" height={maxPanelHeight}>\n <Box flexDirection=\"row\" flexGrow={1} height={maxPanelHeight ? maxPanelHeight - 1 : undefined}>\n <ProcessList\n ref={processListRef}\n names={names}\n selected={selected}\n getStatus={getStatus}\n active={!focusMode}\n height={maxPanelHeight ? maxPanelHeight - 1 : undefined}\n />\n <OutputPanel\n ref={outputRef}\n name={selectedName}\n output={output}\n active={focusMode}\n height={maxPanelHeight ? maxPanelHeight - 1 : undefined}\n autoScroll={currentAutoScroll}\n onAutoScrollChange={handleAutoScrollChange}\n />\n </Box>\n <StatusBar\n focusMode={focusMode}\n processName={selectedName}\n showShiftTabHint={showShiftTabHint}\n />\n <HelpPopup visible={showHelp} />\n </Box>\n );\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { ProcessManager, type ManagedProcess } from '../process-manager';\nimport type { PanexConfig, ProcessStatus } from '../types';\n\nexport interface UseProcessManagerResult {\n processManager: ProcessManager;\n processes: Map<string, ManagedProcess>;\n names: string[];\n getOutput: (name: string) => string;\n getStatus: (name: string) => ProcessStatus;\n restart: (name: string) => void;\n restartAll: () => void;\n kill: (name: string) => void;\n killAll: () => void;\n write: (name: string, data: string) => void;\n resize: (name: string, cols: number, rows: number) => void;\n}\n\nexport function useProcessManager(config: PanexConfig): UseProcessManagerResult {\n const [, forceUpdate] = useState({});\n const processManagerRef = useRef<ProcessManager | null>(null);\n\n if (!processManagerRef.current) {\n processManagerRef.current = new ProcessManager(config.procs);\n }\n\n const pm = processManagerRef.current;\n\n useEffect(() => {\n const update = () => forceUpdate({});\n pm.on('output', update);\n pm.on('started', update);\n pm.on('exit', update);\n pm.on('error', update);\n\n pm.startAll();\n\n return () => {\n pm.removeAllListeners();\n pm.killAll();\n };\n }, [pm]);\n\n const getOutput = useCallback((name: string) => pm.getOutput(name), [pm]);\n\n const getStatus = useCallback((name: string): ProcessStatus => {\n const proc = pm.getProcess(name);\n return proc?.status ?? 'stopped';\n }, [pm]);\n\n const restart = useCallback((name: string) => pm.restart(name), [pm]);\n const restartAll = useCallback(() => pm.restartAll(), [pm]);\n const kill = useCallback((name: string) => pm.kill(name), [pm]);\n const killAll = useCallback(() => pm.killAll(), [pm]);\n const write = useCallback((name: string, data: string) => pm.write(name, data), [pm]);\n const resize = useCallback((name: string, cols: number, rows: number) => pm.resize(name, cols, rows), [pm]);\n\n return {\n processManager: pm,\n processes: new Map(pm.getNames().map(n => [n, pm.getProcess(n)!])),\n names: pm.getNames(),\n getOutput,\n getStatus,\n restart,\n restartAll,\n kill,\n killAll,\n write,\n resize,\n };\n}\n","import { EventEmitter } from 'events';\nimport type { ProcessConfig } from './types';\nimport { TerminalBuffer } from './terminal-buffer';\n\ninterface PtyHandle {\n write(data: string): void;\n resize(cols: number, rows: number): void;\n kill(): void;\n}\n\nexport interface ManagedProcess {\n name: string;\n config: ProcessConfig;\n pty: PtyHandle | null;\n status: 'running' | 'stopped' | 'error';\n terminalBuffer: TerminalBuffer;\n exitCode: number | null;\n}\n\nexport class ProcessManager extends EventEmitter {\n private processes: Map<string, ManagedProcess> = new Map();\n\n constructor(private procs: Record<string, ProcessConfig>) {\n super();\n }\n\n async startAll(): Promise<void> {\n for (const [name, config] of Object.entries(this.procs)) {\n await this.start(name, config);\n }\n }\n\n async start(name: string, config: ProcessConfig): Promise<void> {\n const existing = this.processes.get(name);\n if (existing?.pty) {\n existing.pty.kill();\n }\n\n const shell = process.platform === 'win32' ? 'powershell.exe' : 'bash';\n const args = config.shell\n ? ['-c', config.shell]\n : config.cmd\n ? ['-c', config.cmd.join(' ')]\n : [];\n\n const cwd = config.cwd ?? process.cwd();\n const env = { ...process.env, ...config.env };\n\n const managed: ManagedProcess = {\n name,\n config,\n pty: null,\n status: 'running',\n terminalBuffer: new TerminalBuffer(120, 30),\n exitCode: null,\n };\n\n this.processes.set(name, managed);\n\n try {\n const proc = Bun.spawn([shell, ...args], {\n cwd,\n env: env as Record<string, string>,\n terminal: {\n cols: 120,\n rows: 30,\n data: (_terminal: unknown, data: Uint8Array) => {\n const str = new TextDecoder().decode(data);\n managed.terminalBuffer.write(str);\n this.emit('output', name, str);\n },\n },\n });\n\n managed.pty = {\n write: (data: string) => proc.terminal?.write(data),\n resize: (cols: number, rows: number) => proc.terminal?.resize(cols, rows),\n kill: () => proc.kill(),\n };\n\n // Handle exit\n proc.exited.then((exitCode) => {\n managed.status = exitCode === 0 ? 'stopped' : 'error';\n managed.exitCode = exitCode;\n managed.pty = null;\n this.emit('exit', name, exitCode);\n\n if (managed.config.autoRestart && exitCode !== 0) {\n setTimeout(() => this.start(name, managed.config), 1000);\n }\n });\n\n this.emit('started', name);\n } catch (error) {\n managed.status = 'error';\n managed.terminalBuffer.write(`Error starting process: ${error}`);\n managed.exitCode = -1;\n this.emit('error', name, error);\n }\n }\n\n restart(name: string): void {\n const proc = this.processes.get(name);\n if (proc) {\n if (proc.pty) {\n proc.pty.kill();\n }\n proc.terminalBuffer.clear();\n this.start(name, proc.config);\n }\n }\n\n restartAll(): void {\n for (const name of this.processes.keys()) {\n this.restart(name);\n }\n }\n\n kill(name: string): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.kill();\n }\n }\n\n killAll(): void {\n for (const proc of this.processes.values()) {\n if (proc.pty) {\n proc.pty.kill();\n }\n }\n }\n\n write(name: string, data: string): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.write(data);\n }\n }\n\n resize(name: string, cols: number, rows: number): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.resize(cols, rows);\n }\n }\n\n getProcess(name: string): ManagedProcess | undefined {\n return this.processes.get(name);\n }\n\n getProcesses(): ManagedProcess[] {\n return Array.from(this.processes.values());\n }\n\n getNames(): string[] {\n return Array.from(this.processes.keys());\n }\n\n getOutput(name: string): string {\n return this.processes.get(name)?.terminalBuffer.toString() ?? '';\n }\n}\n","import { Terminal } from '@xterm/headless';\n\n/**\n * A terminal buffer that properly interprets ANSI escape sequences including:\n * - Cursor movement (\\x1b[A up, \\x1b[B down, \\x1b[C right, \\x1b[D left)\n * - Cursor positioning (\\x1b[H, \\x1b[row;colH)\n * - Line clearing (\\x1b[K erase to end, \\x1b[2K erase line)\n * - Screen clearing (\\x1b[2J clear screen)\n * - Carriage return (\\r) for in-place updates like progress bars\n */\nexport class TerminalBuffer {\n private terminal: Terminal;\n private rows: number;\n private cols: number;\n\n constructor(cols = 200, rows = 500) {\n this.rows = rows;\n this.cols = cols;\n this.terminal = new Terminal({\n cols,\n rows,\n scrollback: 10000,\n allowProposedApi: true,\n });\n }\n\n /**\n * Write data to the terminal buffer.\n * The terminal will interpret all ANSI escape sequences.\n */\n write(data: string): void {\n this.terminal.write(data);\n }\n\n /**\n * Get the current terminal buffer content as an array of lines.\n * Only returns lines that have content (not all 500 rows).\n */\n getLines(): string[] {\n const buffer = this.terminal.buffer.active;\n const lines: string[] = [];\n\n // Get actual content length (baseY is lines scrolled off + cursorY + 1)\n const contentLength = buffer.baseY + buffer.cursorY + 1;\n\n for (let i = 0; i < contentLength; i++) {\n const line = buffer.getLine(i);\n if (line) {\n lines.push(line.translateToString(true)); // trim trailing whitespace\n }\n }\n\n // Remove trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n\n return lines;\n }\n\n /**\n * Get the terminal content as a single string with newlines.\n */\n toString(): string {\n return this.getLines().join('\\n');\n }\n\n /**\n * Clear the terminal buffer.\n */\n clear(): void {\n this.terminal.reset();\n }\n\n /**\n * Resize the terminal.\n */\n resize(cols: number, rows: number): void {\n this.cols = cols;\n this.rows = rows;\n this.terminal.resize(cols, rows);\n }\n\n /**\n * Dispose of the terminal to free resources.\n */\n dispose(): void {\n this.terminal.dispose();\n }\n}\n","import { useState, useCallback } from 'react';\n\nexport interface UseFocusModeResult {\n focusMode: boolean;\n enterFocus: () => void;\n exitFocus: () => void;\n toggleFocus: () => void;\n}\n\nexport function useFocusMode(): UseFocusModeResult {\n const [focusMode, setFocusMode] = useState(false);\n\n const enterFocus = useCallback(() => setFocusMode(true), []);\n const exitFocus = useCallback(() => setFocusMode(false), []);\n const toggleFocus = useCallback(() => setFocusMode(f => !f), []);\n\n return { focusMode, enterFocus, exitFocus, toggleFocus };\n}\n","import { useEffect, useCallback } from 'react';\nimport { useStdin, useStdout } from 'ink';\n\ninterface MouseWheelEvent {\n type: 'wheel-up' | 'wheel-down';\n x: number;\n y: number;\n}\n\ninterface MouseClickEvent {\n type: 'click';\n x: number;\n y: number;\n}\n\ninterface UseMouseWheelOptions {\n enabled?: boolean;\n onWheel?: (event: MouseWheelEvent) => void;\n onClick?: (event: MouseClickEvent) => void;\n}\n\n/**\n * Hook to enable mouse tracking in the terminal.\n *\n * Uses ANSI escape sequences for SGR extended mouse mode:\n * - \\x1b[?1000h - Enable mouse button tracking\n * - \\x1b[?1006h - Enable SGR extended mouse mode\n *\n * Mouse events (SGR mode):\n * - Left click: \\x1b[<0;X;YM (button 0 = left click press)\n * - Scroll up: \\x1b[<64;X;YM (button 64 = wheel up)\n * - Scroll down: \\x1b[<65;X;YM (button 65 = wheel down)\n */\nexport function useMouseWheel({ enabled = true, onWheel, onClick }: UseMouseWheelOptions = {}) {\n const { stdin, setRawMode } = useStdin();\n const { stdout } = useStdout();\n\n const handleData = useCallback((data: Buffer) => {\n const str = data.toString();\n\n // Parse SGR mouse events: \\x1b[<button;x;yM or \\x1b[<button;x;ym\n // Button 64 = wheel up, Button 65 = wheel down\n const sgrRegex = /\\x1b\\[<(\\d+);(\\d+);(\\d+)([Mm])/g;\n let match;\n\n while ((match = sgrRegex.exec(str)) !== null) {\n const button = parseInt(match[1] ?? '0', 10);\n const x = parseInt(match[2] ?? '0', 10);\n const y = parseInt(match[3] ?? '0', 10);\n // M = press, m = release (we only care about press for wheel)\n const isPress = match[4] === 'M';\n\n if (isPress) {\n if (button === 0) {\n onClick?.({ type: 'click', x, y });\n } else if (button === 64) {\n onWheel?.({ type: 'wheel-up', x, y });\n } else if (button === 65) {\n onWheel?.({ type: 'wheel-down', x, y });\n }\n }\n }\n }, [onWheel, onClick]);\n\n useEffect(() => {\n if (!enabled || !stdin || !stdout) return;\n\n // Enable mouse tracking\n // 1000h: X11 mouse button tracking\n // 1006h: SGR extended mouse mode (for proper coordinates)\n stdout.write('\\x1b[?1000h\\x1b[?1006h');\n\n // Ensure raw mode is enabled\n setRawMode?.(true);\n\n // Listen for mouse events\n stdin.on('data', handleData);\n\n return () => {\n stdin.off('data', handleData);\n // Disable mouse tracking on cleanup\n stdout.write('\\x1b[?1000l\\x1b[?1006l');\n };\n }, [enabled, stdin, stdout, setRawMode, handleData]);\n}\n","import { Box, Text, useStdout } from 'ink';\nimport { ScrollList, ScrollListRef } from 'ink-scroll-list';\nimport { forwardRef, useImperativeHandle, useRef, useEffect } from 'react';\nimport type { ProcessStatus } from '../types';\n\nexport const PROCESS_LIST_WIDTH = 20;\n\ninterface ProcessListProps {\n names: string[];\n selected: number;\n getStatus: (name: string) => ProcessStatus;\n active: boolean;\n height?: number;\n}\n\nexport interface ProcessListRef {\n scrollBy: (delta: number) => void;\n scrollToTop: () => void;\n scrollToBottom: () => void;\n}\n\nexport const ProcessList = forwardRef<ProcessListRef, ProcessListProps>(\n function ProcessList({ names, selected, getStatus, active, height }, ref) {\n const borderStyle = active ? 'double' : 'single';\n const listRef = useRef<ScrollListRef>(null);\n const { stdout } = useStdout();\n\n // Handle terminal resize\n useEffect(() => {\n const handleResize = () => listRef.current?.remeasure();\n stdout?.on('resize', handleResize);\n return () => {\n stdout?.off('resize', handleResize);\n };\n }, [stdout]);\n\n // Expose scroll methods via ref\n useImperativeHandle(ref, () => ({\n scrollBy: (delta: number) => listRef.current?.scrollBy(delta),\n scrollToTop: () => listRef.current?.scrollToTop(),\n scrollToBottom: () => listRef.current?.scrollToBottom(),\n }));\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle={borderStyle}\n borderColor={active ? 'blue' : 'gray'}\n width={PROCESS_LIST_WIDTH}\n height={height}\n paddingX={1}\n >\n <Box flexDirection=\"column\" marginTop={0} height={height ? height - 2 : undefined}>\n <ScrollList\n ref={listRef}\n selectedIndex={selected}\n scrollAlignment=\"auto\"\n >\n {names.map((name, i) => {\n const status = getStatus(name);\n const statusIcon = status === 'running' ? '●' : status === 'error' ? '✗' : '○';\n const statusColor = status === 'running' ? 'green' : status === 'error' ? 'red' : 'gray';\n const isSelected = i === selected;\n\n return (\n <Box key={name} backgroundColor={isSelected ? 'blue' : undefined}>\n <Text color={isSelected ? 'black' : undefined}>\n {name}{' '}\n </Text>\n <Text color={statusColor}>{statusIcon}</Text>\n </Box>\n );\n })}\n </ScrollList>\n </Box>\n </Box>\n );\n }\n);\n","import { Box, Text, useStdout } from 'ink';\nimport { ScrollView, ScrollViewRef } from 'ink-scroll-view';\nimport { forwardRef, useImperativeHandle, useRef, useEffect, useState, useCallback } from 'react';\nimport { Scrollbar } from './Scrollbar';\n\ninterface OutputPanelProps {\n name: string;\n output: string;\n active: boolean;\n height?: number;\n autoScroll?: boolean;\n onAutoScrollChange?: (enabled: boolean) => void;\n}\n\nexport interface OutputPanelRef {\n scrollBy: (delta: number) => void;\n scrollToTop: () => void;\n scrollToBottom: () => void;\n getScrollOffset: () => number;\n getContentHeight: () => number;\n getViewportHeight: () => number;\n isAtBottom: () => boolean;\n}\n\nexport const OutputPanel = forwardRef<OutputPanelRef, OutputPanelProps>(\n function OutputPanel({ name, output, active, height, autoScroll = true, onAutoScrollChange }, ref) {\n const borderStyle = active ? 'double' : 'single';\n const lines = output.split('\\n');\n const scrollRef = useRef<ScrollViewRef>(null);\n const { stdout } = useStdout();\n\n // Track scroll state for scrollbar\n const [scrollOffset, setScrollOffset] = useState(0);\n const [contentHeight, setContentHeight] = useState(0);\n const [viewportHeight, setViewportHeight] = useState(0);\n\n // Handle terminal resize\n useEffect(() => {\n const handleResize = () => scrollRef.current?.remeasure();\n stdout?.on('resize', handleResize);\n return () => {\n stdout?.off('resize', handleResize);\n };\n }, [stdout]);\n\n // Check if at bottom with small tolerance\n const isAtBottom = useCallback(() => {\n if (!scrollRef.current) return true;\n const offset = scrollRef.current.getScrollOffset();\n const bottom = scrollRef.current.getBottomOffset();\n // Allow 1 line tolerance for rounding issues\n return offset >= bottom - 1;\n }, []);\n\n // Auto-scroll when content height changes (if enabled)\n const handleContentHeightChange = useCallback((newHeight: number) => {\n setContentHeight(newHeight);\n if (autoScroll && scrollRef.current) {\n // Use setTimeout to ensure layout is complete\n setTimeout(() => {\n scrollRef.current?.scrollToBottom();\n }, 0);\n }\n }, [autoScroll]);\n\n // Track scroll and update auto-scroll state\n const handleScroll = useCallback((offset: number) => {\n setScrollOffset(offset);\n\n // If user manually scrolled away from bottom, disable auto-scroll\n if (scrollRef.current) {\n const bottom = scrollRef.current.getBottomOffset();\n const atBottom = offset >= bottom - 1;\n\n if (!atBottom && autoScroll) {\n onAutoScrollChange?.(false);\n } else if (atBottom && !autoScroll) {\n onAutoScrollChange?.(true);\n }\n }\n }, [autoScroll, onAutoScrollChange]);\n\n // Expose scroll methods via ref\n useImperativeHandle(ref, () => ({\n scrollBy: (delta: number) => scrollRef.current?.scrollBy(delta),\n scrollToTop: () => scrollRef.current?.scrollToTop(),\n scrollToBottom: () => scrollRef.current?.scrollToBottom(),\n getScrollOffset: () => scrollRef.current?.getScrollOffset() ?? 0,\n getContentHeight: () => scrollRef.current?.getContentHeight() ?? 0,\n getViewportHeight: () => scrollRef.current?.getViewportHeight() ?? 0,\n isAtBottom,\n }));\n\n // Scrollbar height (same as content area)\n const scrollbarHeight = height ? height - 4 : 20;\n const hasScroll = contentHeight > viewportHeight;\n\n // Show pin indicator when auto-scroll is disabled (user scrolled up)\n const pinIndicator = !autoScroll && hasScroll ? ' ⍗' : '';\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle={borderStyle}\n borderColor={active ? 'green' : 'gray'}\n flexGrow={1}\n height={height}\n paddingLeft={1}\n >\n <Box flexDirection=\"row\" marginTop={0} height={height ? height - 2 : undefined}>\n <Box flexDirection=\"column\" flexGrow={1}>\n <ScrollView\n ref={scrollRef}\n onScroll={handleScroll}\n onContentHeightChange={handleContentHeightChange}\n onViewportSizeChange={(layout) => setViewportHeight(layout.height)}\n >\n {lines.map((line, i) => (\n <Text key={i} wrap=\"truncate\">{line}</Text>\n ))}\n </ScrollView>\n </Box>\n {hasScroll && (\n <Scrollbar\n scrollOffset={scrollOffset}\n contentHeight={contentHeight}\n viewportHeight={viewportHeight}\n height={scrollbarHeight}\n />\n )}\n </Box>\n </Box>\n );\n }\n);\n","import { Box, Text } from 'ink';\n\ninterface ScrollbarProps {\n /** Current scroll offset */\n scrollOffset: number;\n /** Total content height */\n contentHeight: number;\n /** Visible viewport height */\n viewportHeight: number;\n /** Height of the scrollbar track (usually same as viewport) */\n height: number;\n}\n\n/**\n * A simple vertical scrollbar component.\n * Uses Unicode block characters to show scroll position.\n */\nexport function Scrollbar({\n scrollOffset,\n contentHeight,\n viewportHeight,\n height,\n}: ScrollbarProps) {\n // Don't show scrollbar if content fits in viewport\n if (contentHeight <= viewportHeight) {\n return (\n <Box flexDirection=\"column\" width={1}>\n {Array.from({ length: height }).map((_, i) => (\n <Text key={i} dimColor> </Text>\n ))}\n </Box>\n );\n }\n\n // Calculate thumb size and position\n const trackHeight = height;\n const thumbRatio = viewportHeight / contentHeight;\n const thumbHeight = Math.max(1, Math.round(trackHeight * thumbRatio));\n\n const maxScroll = contentHeight - viewportHeight;\n const scrollRatio = maxScroll > 0 ? scrollOffset / maxScroll : 0;\n const thumbPosition = Math.round((trackHeight - thumbHeight) * scrollRatio);\n\n // Build the scrollbar\n const lines: string[] = [];\n for (let i = 0; i < trackHeight; i++) {\n if (i >= thumbPosition && i < thumbPosition + thumbHeight) {\n lines.push('█'); // Thumb\n } else {\n lines.push('░'); // Track\n }\n }\n\n return (\n <Box flexDirection=\"column\" width={1}>\n {lines.map((char, i) => (\n <Text key={i} dimColor={char === '░'}>{char}</Text>\n ))}\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\n\ninterface StatusBarProps {\n focusMode: boolean;\n processName?: string;\n showShiftTabHint?: boolean;\n}\n\nexport function StatusBar({ focusMode, processName, showShiftTabHint = true }: StatusBarProps) {\n if (focusMode && processName) {\n const shiftTabHint = showShiftTabHint ? 'Shift-Tab/' : '';\n return (\n <Box backgroundColor=\"green\" width=\"100%\">\n <Text bold color=\"black\" backgroundColor=\"green\">\n {' '}{processName} | [{shiftTabHint}Esc] to exit focus mode{' '}\n </Text>\n </Box>\n );\n }\n\n return (\n <Box backgroundColor=\"blue\" width=\"100%\">\n <Text bold color=\"black\" backgroundColor=\"blue\">\n {' '}[↑↓/jk] select [Tab/Enter] focus [r] restart [A] restart All [x] kill [q] quit [?] help{' '}\n </Text>\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\n\ninterface HelpPopupProps {\n visible: boolean;\n}\n\nexport function HelpPopup({ visible }: HelpPopupProps) {\n if (!visible) return null;\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"yellow\"\n padding={1}\n position=\"absolute\"\n marginLeft={10}\n marginTop={5}\n >\n <Text bold color=\"yellow\"> Help </Text>\n <Text>{'\\n'}Keyboard Shortcuts</Text>\n <Text>{'─'.repeat(18)}</Text>\n <Text>{'\\n'}Navigation</Text>\n <Text> ↑/↓ or j/k Navigate process list</Text>\n <Text> g/G Scroll to top/bottom of output</Text>\n <Text> PgUp/PgDn Scroll output</Text>\n <Text>{'\\n'}Process Control</Text>\n <Text> Tab/Enter Focus process (interactive mode)</Text>\n <Text> Esc Exit focus mode</Text>\n <Text> r Restart selected process</Text>\n <Text> A Restart all processes</Text>\n <Text> x Kill selected process</Text>\n <Text>{'\\n'}General</Text>\n <Text> ? Toggle this help</Text>\n <Text> q Quit panex</Text>\n <Text>{'\\n'}Press any key to close this help...</Text>\n </Box>\n );\n}\n"],"mappings":"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","names":["useState","useEffect","useRef","useCallback","Box","useStdin","useStdout","useState","useCallback","useEffect","useCallback","useStdout","useRef","useEffect","ProcessList","Box","Text","useStdout","forwardRef","useImperativeHandle","useRef","useEffect","useState","useCallback","Box","Text","jsx","jsx","jsxs","forwardRef","OutputPanel","useRef","useStdout","useState","useEffect","useCallback","useImperativeHandle","Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","jsx","jsxs","useStdout","useStdin","useState","useRef","useEffect","useCallback","Box"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/tui.ts","../src/components/App.tsx","../src/hooks/useProcessManager.ts","../src/process-manager.ts","../src/terminal-buffer.ts","../src/hooks/useFocusMode.ts","../src/hooks/useMouseWheel.ts","../src/components/ProcessList.tsx","../src/components/OutputPanel.tsx","../src/components/Scrollbar.tsx","../src/components/StatusBar.tsx","../src/components/HelpPopup.tsx"],"sourcesContent":["// Check for Bun runtime - must be at top before any Bun APIs are used\nif (typeof Bun === 'undefined') {\n console.error('Error: panex requires Bun runtime.');\n console.error('Please run with bunx instead of npx:');\n console.error('\\tbunx panex \"cmd1\" \"cmd2\"');\n process.exit(1);\n}\n\nimport { Command } from 'commander';\nimport type { PanexConfig } from './types';\nimport { createTUI } from './tui';\n\nconst program = new Command();\n\nprogram\n .name('panex')\n .description('Terminal UI for running multiple processes in parallel')\n .version('0.1.0')\n .argument('<commands...>', 'Commands to run in parallel')\n .option('-n, --names <names>', 'Comma-separated names for each process')\n .option('--no-shift-tab [names]', 'Disable Shift-Tab to exit focus (all or comma-separated process names)')\n .action(async (commands: string[], options: { names?: string; shiftTab?: boolean | string }) => {\n const rawNames = options.names?.split(',') ?? commands.map((_, i) => `proc${i + 1}`);\n\n // Ensure unique names by adding suffix for duplicates\n const usedNames = new Map<string, number>();\n const names = rawNames.map((name, i) => {\n const baseName = name || `proc${i + 1}`;\n const count = usedNames.get(baseName) ?? 0;\n usedNames.set(baseName, count + 1);\n return count === 0 ? baseName : `${baseName}-${count + 1}`;\n });\n\n // Parse noShiftTab option\n let noShiftTab: boolean | string[] | undefined;\n if (options.shiftTab === false) {\n noShiftTab = true; // --no-shift-tab without args disables for all\n } else if (typeof options.shiftTab === 'string') {\n noShiftTab = options.shiftTab.split(','); // --no-shift-tab api,mobile\n }\n\n const config: PanexConfig = {\n procs: Object.fromEntries(\n commands.map((cmd, i) => [names[i], { shell: cmd }])\n ),\n settings: {\n noShiftTab,\n },\n };\n\n await createTUI(config);\n });\n\nprogram.parse();","import { render } from 'ink';\nimport { createElement } from 'react';\nimport type { PanexConfig } from './types';\nimport { App } from './components/App';\n\nexport async function createTUI(config: PanexConfig): Promise<void> {\n const { waitUntilExit } = render(createElement(App, { config }));\n await waitUntilExit();\n}\n","import { useState, useEffect, useRef, useCallback } from 'react';\nimport { Box, useApp, useInput, useStdin, useStdout } from 'ink';\nimport type { PanexConfig } from '../types';\nimport { useProcessManager } from '../hooks/useProcessManager';\nimport { useFocusMode } from '../hooks/useFocusMode';\nimport { useMouseWheel } from '../hooks/useMouseWheel';\nimport { ProcessList, ProcessListRef, PROCESS_LIST_WIDTH } from './ProcessList';\nimport { OutputPanel, OutputPanelRef } from './OutputPanel';\nimport { StatusBar } from './StatusBar';\nimport { HelpPopup } from './HelpPopup';\n\ninterface AppProps {\n config: PanexConfig;\n}\n\nexport function App({ config }: AppProps) {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const { setRawMode } = useStdin();\n const [selected, setSelected] = useState(0);\n const [showHelp, setShowHelp] = useState(false);\n const { focusMode, enterFocus, exitFocus } = useFocusMode();\n\n // Refs to control scrolling\n const outputRef = useRef<OutputPanelRef>(null);\n const processListRef = useRef<ProcessListRef>(null);\n\n // Track auto-scroll state per process\n const [autoScroll, setAutoScroll] = useState<Record<string, boolean>>({});\n\n // Track scroll position per process (use ref to avoid closure issues)\n const scrollPositionsRef = useRef<Record<string, number>>({});\n const pendingRestoreRef = useRef<string | null>(null);\n\n const {\n names,\n getOutput,\n getStatus,\n restart,\n restartAll,\n kill,\n killAll,\n write,\n resize,\n } = useProcessManager(config);\n\n // Check if Shift-Tab is disabled for a process\n const isShiftTabDisabled = (name: string): boolean => {\n const setting = config.settings?.noShiftTab;\n if (setting === true) return true;\n if (Array.isArray(setting)) return setting.includes(name);\n return false;\n };\n\n // Calculate max panel height: terminal rows - status bar (1)\n const maxPanelHeight = stdout ? stdout.rows - 1 : undefined;\n\n // Resize on terminal resize\n useEffect(() => {\n const name = names[selected];\n if (name && stdout) {\n const cols = Math.floor(stdout.columns * 0.8) - 2;\n const rows = stdout.rows - 3;\n resize(name, cols, rows);\n }\n }, [stdout?.columns, stdout?.rows, selected, names, resize]);\n\n // Initialize auto-scroll for new processes\n useEffect(() => {\n setAutoScroll(prev => {\n const next = { ...prev };\n let changed = false;\n for (const name of names) {\n if (next[name] === undefined) {\n next[name] = true;\n changed = true;\n }\n }\n return changed ? next : prev;\n });\n }, [names]);\n\n const selectedName = names[selected] ?? '';\n const output = selectedName ? getOutput(selectedName) : '';\n const currentAutoScroll = selectedName ? (autoScroll[selectedName] ?? true) : true;\n\n // Helper to save current scroll position before switching\n const saveCurrentScrollPosition = useCallback(() => {\n if (selectedName && outputRef.current) {\n scrollPositionsRef.current[selectedName] = outputRef.current.getScrollOffset();\n }\n }, [selectedName]);\n\n // Custom setSelected that saves scroll position first\n const handleSetSelected = useCallback((newSelected: number | ((s: number) => number)) => {\n saveCurrentScrollPosition();\n setSelected(prev => {\n const next = typeof newSelected === 'function' ? newSelected(prev) : newSelected;\n if (next !== prev) {\n // Mark that we need to restore scroll for the new process\n pendingRestoreRef.current = names[next] ?? null;\n }\n return next;\n });\n }, [saveCurrentScrollPosition, names]);\n\n // Restore scroll position after render when process changes\n useEffect(() => {\n const restoreName = pendingRestoreRef.current;\n if (restoreName && restoreName === selectedName) {\n pendingRestoreRef.current = null;\n const savedOffset = scrollPositionsRef.current[restoreName];\n // Only restore if we have a saved position AND auto-scroll is disabled\n if (savedOffset !== undefined && savedOffset > 0 && !autoScroll[restoreName]) {\n // Use setImmediate to ensure render is complete\n setImmediate(() => {\n if (outputRef.current) {\n // Only restore if content actually needs scrolling\n const contentHeight = outputRef.current.getContentHeight();\n const viewportHeight = outputRef.current.getViewportHeight();\n if (contentHeight > viewportHeight) {\n const currentOffset = outputRef.current.getScrollOffset();\n if (currentOffset !== savedOffset) {\n outputRef.current.scrollBy(savedOffset - currentOffset);\n }\n }\n }\n });\n }\n }\n }, [selectedName, autoScroll]);\n\n // Handle auto-scroll state changes from OutputPanel\n const handleAutoScrollChange = useCallback((enabled: boolean) => {\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: enabled }));\n }\n }, [selectedName]);\n\n // Handle mouse wheel events\n const handleWheel = useCallback((event: { type: 'wheel-up' | 'wheel-down'; x: number; y: number; }) => {\n const delta = event.type === 'wheel-up' ? -3 : 3;\n\n if (outputRef.current) {\n outputRef.current.scrollBy(delta);\n // Disable auto-scroll when user scrolls up\n if (event.type === 'wheel-up' && selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n }\n }, [selectedName]);\n\n // Handle mouse click events\n const handleClick = useCallback((event: { type: 'click'; x: number; y: number; }) => {\n if (event.x <= PROCESS_LIST_WIDTH) {\n // Click on process list - exit focus mode, select process if clicked\n if (focusMode) {\n exitFocus();\n }\n const clickedIndex = event.y - 2; // Adjust for border\n if (clickedIndex >= 0 && clickedIndex < names.length) {\n handleSetSelected(clickedIndex);\n }\n } else {\n // Click on output panel - enter focus mode\n if (!focusMode) {\n enterFocus();\n }\n }\n }, [names.length, focusMode, enterFocus, exitFocus, handleSetSelected]);\n\n // Enable mouse tracking\n useMouseWheel({\n enabled: !showHelp, // Disable when help is shown\n onWheel: handleWheel,\n onClick: handleClick,\n });\n\n useInput((input, key) => {\n // Handle help popup\n if (showHelp) {\n setShowHelp(false);\n return;\n }\n\n // Quit (Ctrl+C always works, 'q' only in normal mode)\n if (key.ctrl && input === 'c') {\n killAll();\n // Restore terminal: disable raw mode, move cursor to bottom, clear below, disable mouse, show cursor\n setRawMode(false);\n const rows = stdout?.rows ?? 999;\n stdout?.write(`\\x1b[${rows};1H\\x1b[J\\x1b[?1000l\\x1b[?1006l\\x1b[?25h\\x1b[0m\\n`);\n exit();\n process.exit(0);\n }\n\n // Focus mode input handling (before normal mode keys like 'q')\n if (focusMode) {\n const name = names[selected];\n if (!name) return;\n\n // Exit focus\n if (key.escape) {\n exitFocus();\n return;\n }\n\n // Shift-Tab exit (unless disabled)\n if (key.shift && key.tab && !isShiftTabDisabled(name)) {\n exitFocus();\n return;\n }\n\n // Forward Enter\n if (key.return) {\n write(name, '\\r');\n return;\n }\n\n // Forward arrow keys\n if (key.upArrow) {\n write(name, '\\x1b[A');\n return;\n }\n if (key.downArrow) {\n write(name, '\\x1b[B');\n return;\n }\n if (key.leftArrow) {\n write(name, '\\x1b[D');\n return;\n }\n if (key.rightArrow) {\n write(name, '\\x1b[C');\n return;\n }\n\n // Forward regular input (filter out mouse escape sequences)\n if (input && !key.ctrl && !key.meta) {\n // Remove SGR mouse sequences like \\x1b[<64;45;5M or [<0;12;7M\n const filtered = input.replace(/\\x1b?\\[<\\d+;\\d+;\\d+[Mm]/g, '');\n if (filtered) {\n write(name, filtered);\n }\n }\n return;\n }\n\n // Normal mode\n\n // Quit with 'q' (only in normal mode)\n if (input === 'q') {\n killAll();\n setRawMode(false);\n const rows = stdout?.rows ?? 999;\n stdout?.write(`\\x1b[${rows};1H\\x1b[J\\x1b[?1000l\\x1b[?1006l\\x1b[?25h\\x1b[0m\\n`);\n exit();\n process.exit(0);\n }\n\n // Help\n if (input === '?') {\n setShowHelp(true);\n return;\n }\n\n // Navigation\n if (key.upArrow || input === 'k') {\n handleSetSelected(s => Math.max(s - 1, 0));\n return;\n }\n if (key.downArrow || input === 'j') {\n handleSetSelected(s => Math.min(s + 1, names.length - 1));\n return;\n }\n\n // Enter focus mode\n if (key.return || key.tab) {\n enterFocus();\n return;\n }\n\n // Process control\n if (input === 'r') {\n const name = names[selected];\n if (name) restart(name);\n return;\n }\n if (input === 'A') {\n restartAll();\n return;\n }\n if (input === 'x') {\n const name = names[selected];\n if (name) kill(name);\n return;\n }\n\n // Output scrolling\n if (input === 'g') {\n outputRef.current?.scrollToTop();\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n return;\n }\n if (input === 'G') {\n outputRef.current?.scrollToBottom();\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: true }));\n }\n return;\n }\n if (key.pageUp) {\n const pageSize = outputRef.current?.getViewportHeight() ?? 10;\n outputRef.current?.scrollBy(-pageSize);\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n return;\n }\n if (key.pageDown) {\n const pageSize = outputRef.current?.getViewportHeight() ?? 10;\n outputRef.current?.scrollBy(pageSize);\n return;\n }\n });\n\n const showShiftTabHint = selectedName ? !isShiftTabDisabled(selectedName) : true;\n\n return (\n <Box flexDirection=\"column\" height={maxPanelHeight}>\n <Box flexDirection=\"row\" flexGrow={1} height={maxPanelHeight ? maxPanelHeight - 1 : undefined}>\n <ProcessList\n ref={processListRef}\n names={names}\n selected={selected}\n getStatus={getStatus}\n active={!focusMode}\n height={maxPanelHeight ? maxPanelHeight - 1 : undefined}\n />\n <OutputPanel\n ref={outputRef}\n name={selectedName}\n output={output}\n active={focusMode}\n height={maxPanelHeight ? maxPanelHeight - 1 : undefined}\n autoScroll={currentAutoScroll}\n onAutoScrollChange={handleAutoScrollChange}\n />\n </Box>\n <StatusBar\n focusMode={focusMode}\n processName={selectedName}\n showShiftTabHint={showShiftTabHint}\n />\n <HelpPopup visible={showHelp} />\n </Box>\n );\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { ProcessManager, type ManagedProcess } from '../process-manager';\nimport type { PanexConfig, ProcessStatus } from '../types';\n\nexport interface UseProcessManagerResult {\n processManager: ProcessManager;\n processes: Map<string, ManagedProcess>;\n names: string[];\n getOutput: (name: string) => string;\n getStatus: (name: string) => ProcessStatus;\n restart: (name: string) => void;\n restartAll: () => void;\n kill: (name: string) => void;\n killAll: () => void;\n write: (name: string, data: string) => void;\n resize: (name: string, cols: number, rows: number) => void;\n}\n\nexport function useProcessManager(config: PanexConfig): UseProcessManagerResult {\n const [, forceUpdate] = useState({});\n const processManagerRef = useRef<ProcessManager | null>(null);\n\n if (!processManagerRef.current) {\n processManagerRef.current = new ProcessManager(config.procs);\n }\n\n const pm = processManagerRef.current;\n\n useEffect(() => {\n const update = () => forceUpdate({});\n pm.on('output', update);\n pm.on('started', update);\n pm.on('exit', update);\n pm.on('error', update);\n\n pm.startAll();\n\n return () => {\n pm.removeAllListeners();\n pm.killAll();\n };\n }, [pm]);\n\n const getOutput = useCallback((name: string) => pm.getOutput(name), [pm]);\n\n const getStatus = useCallback((name: string): ProcessStatus => {\n const proc = pm.getProcess(name);\n return proc?.status ?? 'stopped';\n }, [pm]);\n\n const restart = useCallback((name: string) => pm.restart(name), [pm]);\n const restartAll = useCallback(() => pm.restartAll(), [pm]);\n const kill = useCallback((name: string) => pm.kill(name), [pm]);\n const killAll = useCallback(() => pm.killAll(), [pm]);\n const write = useCallback((name: string, data: string) => pm.write(name, data), [pm]);\n const resize = useCallback((name: string, cols: number, rows: number) => pm.resize(name, cols, rows), [pm]);\n\n return {\n processManager: pm,\n processes: new Map(pm.getNames().map(n => [n, pm.getProcess(n)!])),\n names: pm.getNames(),\n getOutput,\n getStatus,\n restart,\n restartAll,\n kill,\n killAll,\n write,\n resize,\n };\n}\n","import { EventEmitter } from 'events';\nimport type { ProcessConfig } from './types';\nimport { TerminalBuffer } from './terminal-buffer';\n\ninterface PtyHandle {\n write(data: string): void;\n resize(cols: number, rows: number): void;\n kill(): void;\n}\n\nexport interface ManagedProcess {\n name: string;\n config: ProcessConfig;\n pty: PtyHandle | null;\n status: 'running' | 'stopped' | 'error';\n terminalBuffer: TerminalBuffer;\n exitCode: number | null;\n}\n\nexport class ProcessManager extends EventEmitter {\n private processes: Map<string, ManagedProcess> = new Map();\n\n constructor(private procs: Record<string, ProcessConfig>) {\n super();\n }\n\n async startAll(): Promise<void> {\n for (const [name, config] of Object.entries(this.procs)) {\n await this.start(name, config);\n }\n }\n\n async start(name: string, config: ProcessConfig): Promise<void> {\n const existing = this.processes.get(name);\n if (existing?.pty) {\n existing.pty.kill();\n }\n\n const shell = process.platform === 'win32' ? 'powershell.exe' : 'bash';\n const args = config.shell\n ? ['-c', config.shell]\n : config.cmd\n ? ['-c', config.cmd.join(' ')]\n : [];\n\n const cwd = config.cwd ?? process.cwd();\n const env = { ...process.env, ...config.env };\n\n const managed: ManagedProcess = {\n name,\n config,\n pty: null,\n status: 'running',\n terminalBuffer: new TerminalBuffer(120, 30),\n exitCode: null,\n };\n\n this.processes.set(name, managed);\n\n try {\n const proc = Bun.spawn([shell, ...args], {\n cwd,\n env: env as Record<string, string>,\n terminal: {\n cols: 120,\n rows: 30,\n data: (_terminal: unknown, data: Uint8Array) => {\n const str = new TextDecoder().decode(data);\n managed.terminalBuffer.write(str);\n this.emit('output', name, str);\n },\n },\n });\n\n managed.pty = {\n write: (data: string) => proc.terminal?.write(data),\n resize: (cols: number, rows: number) => proc.terminal?.resize(cols, rows),\n kill: () => proc.kill(),\n };\n\n // Handle exit\n proc.exited.then((exitCode) => {\n managed.status = exitCode === 0 ? 'stopped' : 'error';\n managed.exitCode = exitCode;\n managed.pty = null;\n this.emit('exit', name, exitCode);\n\n if (managed.config.autoRestart && exitCode !== 0) {\n setTimeout(() => this.start(name, managed.config), 1000);\n }\n });\n\n this.emit('started', name);\n } catch (error) {\n managed.status = 'error';\n managed.terminalBuffer.write(`Error starting process: ${error}`);\n managed.exitCode = -1;\n this.emit('error', name, error);\n }\n }\n\n restart(name: string): void {\n const proc = this.processes.get(name);\n if (proc) {\n if (proc.pty) {\n proc.pty.kill();\n }\n proc.terminalBuffer.clear();\n this.start(name, proc.config);\n }\n }\n\n restartAll(): void {\n for (const name of this.processes.keys()) {\n this.restart(name);\n }\n }\n\n kill(name: string): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.kill();\n }\n }\n\n killAll(): void {\n for (const proc of this.processes.values()) {\n if (proc.pty) {\n proc.pty.kill();\n }\n }\n }\n\n write(name: string, data: string): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.write(data);\n }\n }\n\n resize(name: string, cols: number, rows: number): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.resize(cols, rows);\n }\n }\n\n getProcess(name: string): ManagedProcess | undefined {\n return this.processes.get(name);\n }\n\n getProcesses(): ManagedProcess[] {\n return Array.from(this.processes.values());\n }\n\n getNames(): string[] {\n return Array.from(this.processes.keys());\n }\n\n getOutput(name: string): string {\n return this.processes.get(name)?.terminalBuffer.toString() ?? '';\n }\n}\n","import { Terminal } from '@xterm/headless';\nimport { SerializeAddon } from '@xterm/addon-serialize';\n\n/**\n * A terminal buffer that properly interprets ANSI escape sequences including:\n * - Cursor movement (\\x1b[A up, \\x1b[B down, \\x1b[C right, \\x1b[D left)\n * - Cursor positioning (\\x1b[H, \\x1b[row;colH)\n * - Line clearing (\\x1b[K erase to end, \\x1b[2K erase line)\n * - Screen clearing (\\x1b[2J clear screen)\n * - Carriage return (\\r) for in-place updates like progress bars\n * - ANSI colors and formatting (preserved in output via SerializeAddon)\n */\nexport class TerminalBuffer {\n private terminal: Terminal;\n private serializeAddon: SerializeAddon;\n private rows: number;\n private cols: number;\n\n constructor(cols = 200, rows = 500) {\n this.rows = rows;\n this.cols = cols;\n this.terminal = new Terminal({\n cols,\n rows,\n scrollback: 10000,\n allowProposedApi: true,\n });\n this.serializeAddon = new SerializeAddon();\n this.terminal.loadAddon(this.serializeAddon);\n }\n\n /**\n * Write data to the terminal buffer.\n * The terminal will interpret all ANSI escape sequences.\n */\n write(data: string): void {\n this.terminal.write(data);\n }\n\n /**\n * Get the current terminal buffer content as an array of lines.\n * Only returns lines that have content (not all 500 rows).\n */\n getLines(): string[] {\n const buffer = this.terminal.buffer.active;\n const lines: string[] = [];\n\n // Get actual content length (baseY is lines scrolled off + cursorY + 1)\n const contentLength = buffer.baseY + buffer.cursorY + 1;\n\n for (let i = 0; i < contentLength; i++) {\n const line = buffer.getLine(i);\n if (line) {\n lines.push(line.translateToString(true)); // trim trailing whitespace\n }\n }\n\n // Remove trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n\n return lines;\n }\n\n /**\n * Get the terminal content as a single string with newlines.\n * Preserves ANSI color codes and formatting.\n */\n toString(): string {\n // Use serialize addon to get output with ANSI codes preserved\n return this.serializeAddon.serialize();\n }\n\n /**\n * Clear the terminal buffer.\n */\n clear(): void {\n this.terminal.reset();\n }\n\n /**\n * Resize the terminal.\n */\n resize(cols: number, rows: number): void {\n this.cols = cols;\n this.rows = rows;\n this.terminal.resize(cols, rows);\n }\n\n /**\n * Dispose of the terminal to free resources.\n */\n dispose(): void {\n this.terminal.dispose();\n }\n}\n","import { useState, useCallback } from 'react';\n\nexport interface UseFocusModeResult {\n focusMode: boolean;\n enterFocus: () => void;\n exitFocus: () => void;\n toggleFocus: () => void;\n}\n\nexport function useFocusMode(): UseFocusModeResult {\n const [focusMode, setFocusMode] = useState(false);\n\n const enterFocus = useCallback(() => setFocusMode(true), []);\n const exitFocus = useCallback(() => setFocusMode(false), []);\n const toggleFocus = useCallback(() => setFocusMode(f => !f), []);\n\n return { focusMode, enterFocus, exitFocus, toggleFocus };\n}\n","import { useEffect, useCallback } from 'react';\nimport { useStdin, useStdout } from 'ink';\n\ninterface MouseWheelEvent {\n type: 'wheel-up' | 'wheel-down';\n x: number;\n y: number;\n}\n\ninterface MouseClickEvent {\n type: 'click';\n x: number;\n y: number;\n}\n\ninterface UseMouseWheelOptions {\n enabled?: boolean;\n onWheel?: (event: MouseWheelEvent) => void;\n onClick?: (event: MouseClickEvent) => void;\n}\n\n/**\n * Hook to enable mouse tracking in the terminal.\n *\n * Uses ANSI escape sequences for SGR extended mouse mode:\n * - \\x1b[?1000h - Enable mouse button tracking\n * - \\x1b[?1006h - Enable SGR extended mouse mode\n *\n * Mouse events (SGR mode):\n * - Left click: \\x1b[<0;X;YM (button 0 = left click press)\n * - Scroll up: \\x1b[<64;X;YM (button 64 = wheel up)\n * - Scroll down: \\x1b[<65;X;YM (button 65 = wheel down)\n */\nexport function useMouseWheel({ enabled = true, onWheel, onClick }: UseMouseWheelOptions = {}) {\n const { stdin, setRawMode } = useStdin();\n const { stdout } = useStdout();\n\n const handleData = useCallback((data: Buffer) => {\n const str = data.toString();\n\n // Parse SGR mouse events: \\x1b[<button;x;yM or \\x1b[<button;x;ym\n // Button 64 = wheel up, Button 65 = wheel down\n const sgrRegex = /\\x1b\\[<(\\d+);(\\d+);(\\d+)([Mm])/g;\n let match;\n\n while ((match = sgrRegex.exec(str)) !== null) {\n const button = parseInt(match[1] ?? '0', 10);\n const x = parseInt(match[2] ?? '0', 10);\n const y = parseInt(match[3] ?? '0', 10);\n // M = press, m = release (we only care about press for wheel)\n const isPress = match[4] === 'M';\n\n if (isPress) {\n if (button === 0) {\n onClick?.({ type: 'click', x, y });\n } else if (button === 64) {\n onWheel?.({ type: 'wheel-up', x, y });\n } else if (button === 65) {\n onWheel?.({ type: 'wheel-down', x, y });\n }\n }\n }\n }, [onWheel, onClick]);\n\n useEffect(() => {\n if (!enabled || !stdin || !stdout) return;\n\n // Enable mouse tracking\n // 1000h: X11 mouse button tracking\n // 1006h: SGR extended mouse mode (for proper coordinates)\n stdout.write('\\x1b[?1000h\\x1b[?1006h');\n\n // Ensure raw mode is enabled\n setRawMode?.(true);\n\n // Listen for mouse events\n stdin.on('data', handleData);\n\n return () => {\n stdin.off('data', handleData);\n // Disable mouse tracking on cleanup\n stdout.write('\\x1b[?1000l\\x1b[?1006l');\n };\n }, [enabled, stdin, stdout, setRawMode, handleData]);\n}\n","import { Box, Text, useStdout } from 'ink';\nimport { ScrollList, ScrollListRef } from 'ink-scroll-list';\nimport { forwardRef, useImperativeHandle, useRef, useEffect } from 'react';\nimport type { ProcessStatus } from '../types';\n\nexport const PROCESS_LIST_WIDTH = 20;\n\ninterface ProcessListProps {\n names: string[];\n selected: number;\n getStatus: (name: string) => ProcessStatus;\n active: boolean;\n height?: number;\n}\n\nexport interface ProcessListRef {\n scrollBy: (delta: number) => void;\n scrollToTop: () => void;\n scrollToBottom: () => void;\n}\n\nexport const ProcessList = forwardRef<ProcessListRef, ProcessListProps>(\n function ProcessList({ names, selected, getStatus, active, height }, ref) {\n const borderStyle = active ? 'double' : 'single';\n const listRef = useRef<ScrollListRef>(null);\n const { stdout } = useStdout();\n\n // Handle terminal resize\n useEffect(() => {\n const handleResize = () => listRef.current?.remeasure();\n stdout?.on('resize', handleResize);\n return () => {\n stdout?.off('resize', handleResize);\n };\n }, [stdout]);\n\n // Expose scroll methods via ref\n useImperativeHandle(ref, () => ({\n scrollBy: (delta: number) => listRef.current?.scrollBy(delta),\n scrollToTop: () => listRef.current?.scrollToTop(),\n scrollToBottom: () => listRef.current?.scrollToBottom(),\n }));\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle={borderStyle}\n borderColor={active ? 'blue' : 'gray'}\n width={PROCESS_LIST_WIDTH}\n height={height}\n paddingX={1}\n >\n <Box flexDirection=\"column\" marginTop={0} height={height ? height - 2 : undefined}>\n <ScrollList\n ref={listRef}\n selectedIndex={selected}\n scrollAlignment=\"auto\"\n >\n {names.map((name, i) => {\n const status = getStatus(name);\n const statusIcon = status === 'running' ? '●' : status === 'error' ? '✗' : '○';\n const statusColor = status === 'running' ? 'green' : status === 'error' ? 'red' : 'gray';\n const isSelected = i === selected;\n\n return (\n <Box key={name} backgroundColor={isSelected ? 'blue' : undefined}>\n <Text color={isSelected ? 'black' : undefined}>\n {name}{' '}\n </Text>\n <Text color={statusColor}>{statusIcon}</Text>\n </Box>\n );\n })}\n </ScrollList>\n </Box>\n </Box>\n );\n }\n);\n","import { Box, Text, useStdout } from 'ink';\nimport { ScrollView, ScrollViewRef } from 'ink-scroll-view';\nimport { forwardRef, useImperativeHandle, useRef, useEffect, useState, useCallback } from 'react';\nimport { Scrollbar } from './Scrollbar';\n\ninterface OutputPanelProps {\n name: string;\n output: string;\n active: boolean;\n height?: number;\n autoScroll?: boolean;\n onAutoScrollChange?: (enabled: boolean) => void;\n}\n\nexport interface OutputPanelRef {\n scrollBy: (delta: number) => void;\n scrollToTop: () => void;\n scrollToBottom: () => void;\n getScrollOffset: () => number;\n getContentHeight: () => number;\n getViewportHeight: () => number;\n isAtBottom: () => boolean;\n}\n\nexport const OutputPanel = forwardRef<OutputPanelRef, OutputPanelProps>(\n function OutputPanel({ name, output, active, height, autoScroll = true, onAutoScrollChange }, ref) {\n const borderStyle = active ? 'double' : 'single';\n const lines = output.split('\\n');\n const scrollRef = useRef<ScrollViewRef>(null);\n const { stdout } = useStdout();\n\n // Track scroll state for scrollbar\n const [scrollOffset, setScrollOffset] = useState(0);\n const [contentHeight, setContentHeight] = useState(0);\n const [viewportHeight, setViewportHeight] = useState(0);\n\n // Reset state synchronously when process changes (before render)\n const [prevName, setPrevName] = useState(name);\n if (name !== prevName) {\n setPrevName(name);\n setScrollOffset(0);\n setContentHeight(0);\n setViewportHeight(0);\n }\n\n // Handle terminal resize\n useEffect(() => {\n const handleResize = () => scrollRef.current?.remeasure();\n stdout?.on('resize', handleResize);\n return () => {\n stdout?.off('resize', handleResize);\n };\n }, [stdout]);\n\n // Check if at bottom with small tolerance\n const isAtBottom = useCallback(() => {\n if (!scrollRef.current) return true;\n const offset = scrollRef.current.getScrollOffset();\n const bottom = scrollRef.current.getBottomOffset();\n // Allow 1 line tolerance for rounding issues\n return offset >= bottom - 1;\n }, []);\n\n // Auto-scroll when content height changes (if enabled)\n const handleContentHeightChange = useCallback((newHeight: number) => {\n setContentHeight(newHeight);\n if (autoScroll && scrollRef.current) {\n // Use setTimeout to ensure layout is complete\n setTimeout(() => {\n scrollRef.current?.scrollToBottom();\n }, 0);\n }\n }, [autoScroll]);\n\n // Track scroll and update auto-scroll state\n const handleScroll = useCallback((offset: number) => {\n setScrollOffset(offset);\n\n // If user manually scrolled away from bottom, disable auto-scroll\n if (scrollRef.current) {\n const bottom = scrollRef.current.getBottomOffset();\n const atBottom = offset >= bottom - 1;\n\n if (!atBottom && autoScroll) {\n onAutoScrollChange?.(false);\n } else if (atBottom && !autoScroll) {\n onAutoScrollChange?.(true);\n }\n }\n }, [autoScroll, onAutoScrollChange]);\n\n // Expose scroll methods via ref\n useImperativeHandle(ref, () => ({\n scrollBy: (delta: number) => scrollRef.current?.scrollBy(delta),\n scrollToTop: () => scrollRef.current?.scrollToTop(),\n scrollToBottom: () => scrollRef.current?.scrollToBottom(),\n getScrollOffset: () => scrollRef.current?.getScrollOffset() ?? 0,\n getContentHeight: () => scrollRef.current?.getContentHeight() ?? 0,\n getViewportHeight: () => scrollRef.current?.getViewportHeight() ?? 0,\n isAtBottom,\n }));\n\n // Scrollbar height (same as content area)\n const scrollbarHeight = height ? height - 4 : 20;\n const hasScroll = contentHeight > viewportHeight;\n\n // Show pin indicator when auto-scroll is disabled (user scrolled up)\n const pinIndicator = !autoScroll && hasScroll ? ' ⍗' : '';\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle={borderStyle}\n borderColor={active ? 'green' : 'gray'}\n flexGrow={1}\n height={height}\n paddingLeft={1}\n >\n <Box flexDirection=\"row\" marginTop={0} height={height ? height - 2 : undefined}>\n <Box flexDirection=\"column\" flexGrow={1}>\n <ScrollView\n key={name}\n ref={scrollRef}\n onScroll={handleScroll}\n onContentHeightChange={handleContentHeightChange}\n onViewportSizeChange={(layout) => setViewportHeight(layout.height)}\n >\n {lines.map((line, i) => (\n <Text key={i} wrap=\"truncate\">{line}</Text>\n ))}\n </ScrollView>\n </Box>\n {hasScroll && (\n <Scrollbar\n scrollOffset={scrollOffset}\n contentHeight={contentHeight}\n viewportHeight={viewportHeight}\n height={scrollbarHeight}\n />\n )}\n </Box>\n </Box>\n );\n }\n);\n","import { Box, Text } from 'ink';\n\ninterface ScrollbarProps {\n /** Current scroll offset */\n scrollOffset: number;\n /** Total content height */\n contentHeight: number;\n /** Visible viewport height */\n viewportHeight: number;\n /** Height of the scrollbar track (usually same as viewport) */\n height: number;\n}\n\n/**\n * A simple vertical scrollbar component.\n * Uses Unicode block characters to show scroll position.\n */\nexport function Scrollbar({\n scrollOffset,\n contentHeight,\n viewportHeight,\n height,\n}: ScrollbarProps) {\n // Don't show scrollbar if content fits in viewport\n if (contentHeight <= viewportHeight) {\n return (\n <Box flexDirection=\"column\" width={1}>\n {Array.from({ length: height }).map((_, i) => (\n <Text key={i} dimColor> </Text>\n ))}\n </Box>\n );\n }\n\n // Calculate thumb size and position\n const trackHeight = height;\n const thumbRatio = viewportHeight / contentHeight;\n const thumbHeight = Math.max(1, Math.round(trackHeight * thumbRatio));\n\n const maxScroll = contentHeight - viewportHeight;\n const scrollRatio = maxScroll > 0 ? scrollOffset / maxScroll : 0;\n const thumbPosition = Math.round((trackHeight - thumbHeight) * scrollRatio);\n\n // Build the scrollbar\n const lines: string[] = [];\n for (let i = 0; i < trackHeight; i++) {\n if (i >= thumbPosition && i < thumbPosition + thumbHeight) {\n lines.push('█'); // Thumb\n } else {\n lines.push('░'); // Track\n }\n }\n\n return (\n <Box flexDirection=\"column\" width={1}>\n {lines.map((char, i) => (\n <Text key={i} dimColor={char === '░'}>{char}</Text>\n ))}\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\n\ninterface StatusBarProps {\n focusMode: boolean;\n processName?: string;\n showShiftTabHint?: boolean;\n}\n\nexport function StatusBar({ focusMode, processName, showShiftTabHint = true }: StatusBarProps) {\n if (focusMode && processName) {\n const shiftTabHint = showShiftTabHint ? 'Shift-Tab/' : '';\n return (\n <Box backgroundColor=\"green\" width=\"100%\">\n <Text bold color=\"black\" backgroundColor=\"green\">\n {' '}{processName} | [{shiftTabHint}Esc] to exit focus mode{' '}\n </Text>\n </Box>\n );\n }\n\n return (\n <Box backgroundColor=\"blue\" width=\"100%\">\n <Text bold color=\"black\" backgroundColor=\"blue\">\n {' '}[↑↓/jk] select [Tab/Enter] focus [r] restart [A] restart All [x] kill [q] quit [?] help{' '}\n </Text>\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\n\ninterface HelpPopupProps {\n visible: boolean;\n}\n\nexport function HelpPopup({ visible }: HelpPopupProps) {\n if (!visible) return null;\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"yellow\"\n padding={1}\n position=\"absolute\"\n marginLeft={10}\n marginTop={5}\n >\n <Text bold color=\"yellow\"> Help </Text>\n <Text>{'\\n'}Keyboard Shortcuts</Text>\n <Text>{'─'.repeat(18)}</Text>\n <Text>{'\\n'}Navigation</Text>\n <Text> ↑/↓ or j/k Navigate process list</Text>\n <Text> g/G Scroll to top/bottom of output</Text>\n <Text> PgUp/PgDn Scroll output</Text>\n <Text>{'\\n'}Process Control</Text>\n <Text> Tab/Enter Focus process (interactive mode)</Text>\n <Text> Esc Exit focus mode</Text>\n <Text> r Restart selected process</Text>\n <Text> A Restart all processes</Text>\n <Text> x Kill selected process</Text>\n <Text>{'\\n'}General</Text>\n <Text> ? Toggle this help</Text>\n <Text> q Quit panex</Text>\n <Text>{'\\n'}Press any key to close this help...</Text>\n </Box>\n );\n}\n"],"mappings":"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","names":["useState","useEffect","useRef","useCallback","Box","useStdin","useStdout","useState","useCallback","useEffect","useCallback","useStdout","useRef","useEffect","ProcessList","Box","Text","useStdout","forwardRef","useImperativeHandle","useRef","useEffect","useState","useCallback","Box","Text","jsx","jsx","jsxs","forwardRef","OutputPanel","useRef","useStdout","useState","useEffect","useCallback","useImperativeHandle","Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","jsx","jsxs","useStdout","useStdin","useState","useRef","useEffect","useCallback","Box"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -25,9 +25,11 @@ declare function createTUI(config: PanexConfig): Promise<void>;
|
|
|
25
25
|
* - Line clearing (\x1b[K erase to end, \x1b[2K erase line)
|
|
26
26
|
* - Screen clearing (\x1b[2J clear screen)
|
|
27
27
|
* - Carriage return (\r) for in-place updates like progress bars
|
|
28
|
+
* - ANSI colors and formatting (preserved in output via SerializeAddon)
|
|
28
29
|
*/
|
|
29
30
|
declare class TerminalBuffer {
|
|
30
31
|
private terminal;
|
|
32
|
+
private serializeAddon;
|
|
31
33
|
private rows;
|
|
32
34
|
private cols;
|
|
33
35
|
constructor(cols?: number, rows?: number);
|
|
@@ -43,6 +45,7 @@ declare class TerminalBuffer {
|
|
|
43
45
|
getLines(): string[];
|
|
44
46
|
/**
|
|
45
47
|
* Get the terminal content as a single string with newlines.
|
|
48
|
+
* Preserves ANSI color codes and formatting.
|
|
46
49
|
*/
|
|
47
50
|
toString(): string;
|
|
48
51
|
/**
|
package/dist/index.js
CHANGED
|
@@ -14,8 +14,10 @@ import { EventEmitter } from "events";
|
|
|
14
14
|
|
|
15
15
|
// src/terminal-buffer.ts
|
|
16
16
|
import { Terminal } from "@xterm/headless";
|
|
17
|
+
import { SerializeAddon } from "@xterm/addon-serialize";
|
|
17
18
|
var TerminalBuffer = class {
|
|
18
19
|
terminal;
|
|
20
|
+
serializeAddon;
|
|
19
21
|
rows;
|
|
20
22
|
cols;
|
|
21
23
|
constructor(cols = 200, rows = 500) {
|
|
@@ -27,6 +29,8 @@ var TerminalBuffer = class {
|
|
|
27
29
|
scrollback: 1e4,
|
|
28
30
|
allowProposedApi: true
|
|
29
31
|
});
|
|
32
|
+
this.serializeAddon = new SerializeAddon();
|
|
33
|
+
this.terminal.loadAddon(this.serializeAddon);
|
|
30
34
|
}
|
|
31
35
|
/**
|
|
32
36
|
* Write data to the terminal buffer.
|
|
@@ -56,9 +60,10 @@ var TerminalBuffer = class {
|
|
|
56
60
|
}
|
|
57
61
|
/**
|
|
58
62
|
* Get the terminal content as a single string with newlines.
|
|
63
|
+
* Preserves ANSI color codes and formatting.
|
|
59
64
|
*/
|
|
60
65
|
toString() {
|
|
61
|
-
return this.
|
|
66
|
+
return this.serializeAddon.serialize();
|
|
62
67
|
}
|
|
63
68
|
/**
|
|
64
69
|
* Clear the terminal buffer.
|
|
@@ -399,6 +404,13 @@ var OutputPanel = forwardRef2(
|
|
|
399
404
|
const [scrollOffset, setScrollOffset] = useState3(0);
|
|
400
405
|
const [contentHeight, setContentHeight] = useState3(0);
|
|
401
406
|
const [viewportHeight, setViewportHeight] = useState3(0);
|
|
407
|
+
const [prevName, setPrevName] = useState3(name);
|
|
408
|
+
if (name !== prevName) {
|
|
409
|
+
setPrevName(name);
|
|
410
|
+
setScrollOffset(0);
|
|
411
|
+
setContentHeight(0);
|
|
412
|
+
setViewportHeight(0);
|
|
413
|
+
}
|
|
402
414
|
useEffect4(() => {
|
|
403
415
|
const handleResize = () => scrollRef.current?.remeasure();
|
|
404
416
|
stdout?.on("resize", handleResize);
|
|
@@ -462,7 +474,8 @@ var OutputPanel = forwardRef2(
|
|
|
462
474
|
onContentHeightChange: handleContentHeightChange,
|
|
463
475
|
onViewportSizeChange: (layout) => setViewportHeight(layout.height),
|
|
464
476
|
children: lines.map((line, i) => /* @__PURE__ */ jsx3(Text3, { wrap: "truncate", children: line }, i))
|
|
465
|
-
}
|
|
477
|
+
},
|
|
478
|
+
name
|
|
466
479
|
) }),
|
|
467
480
|
hasScroll && /* @__PURE__ */ jsx3(
|
|
468
481
|
Scrollbar,
|
|
@@ -566,6 +579,8 @@ function App({ config }) {
|
|
|
566
579
|
const outputRef = useRef4(null);
|
|
567
580
|
const processListRef = useRef4(null);
|
|
568
581
|
const [autoScroll, setAutoScroll] = useState4({});
|
|
582
|
+
const scrollPositionsRef = useRef4({});
|
|
583
|
+
const pendingRestoreRef = useRef4(null);
|
|
569
584
|
const {
|
|
570
585
|
names,
|
|
571
586
|
getOutput,
|
|
@@ -608,6 +623,42 @@ function App({ config }) {
|
|
|
608
623
|
const selectedName = names[selected] ?? "";
|
|
609
624
|
const output = selectedName ? getOutput(selectedName) : "";
|
|
610
625
|
const currentAutoScroll = selectedName ? autoScroll[selectedName] ?? true : true;
|
|
626
|
+
const saveCurrentScrollPosition = useCallback5(() => {
|
|
627
|
+
if (selectedName && outputRef.current) {
|
|
628
|
+
scrollPositionsRef.current[selectedName] = outputRef.current.getScrollOffset();
|
|
629
|
+
}
|
|
630
|
+
}, [selectedName]);
|
|
631
|
+
const handleSetSelected = useCallback5((newSelected) => {
|
|
632
|
+
saveCurrentScrollPosition();
|
|
633
|
+
setSelected((prev) => {
|
|
634
|
+
const next = typeof newSelected === "function" ? newSelected(prev) : newSelected;
|
|
635
|
+
if (next !== prev) {
|
|
636
|
+
pendingRestoreRef.current = names[next] ?? null;
|
|
637
|
+
}
|
|
638
|
+
return next;
|
|
639
|
+
});
|
|
640
|
+
}, [saveCurrentScrollPosition, names]);
|
|
641
|
+
useEffect5(() => {
|
|
642
|
+
const restoreName = pendingRestoreRef.current;
|
|
643
|
+
if (restoreName && restoreName === selectedName) {
|
|
644
|
+
pendingRestoreRef.current = null;
|
|
645
|
+
const savedOffset = scrollPositionsRef.current[restoreName];
|
|
646
|
+
if (savedOffset !== void 0 && savedOffset > 0 && !autoScroll[restoreName]) {
|
|
647
|
+
setImmediate(() => {
|
|
648
|
+
if (outputRef.current) {
|
|
649
|
+
const contentHeight = outputRef.current.getContentHeight();
|
|
650
|
+
const viewportHeight = outputRef.current.getViewportHeight();
|
|
651
|
+
if (contentHeight > viewportHeight) {
|
|
652
|
+
const currentOffset = outputRef.current.getScrollOffset();
|
|
653
|
+
if (currentOffset !== savedOffset) {
|
|
654
|
+
outputRef.current.scrollBy(savedOffset - currentOffset);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}, [selectedName, autoScroll]);
|
|
611
662
|
const handleAutoScrollChange = useCallback5((enabled) => {
|
|
612
663
|
if (selectedName) {
|
|
613
664
|
setAutoScroll((prev) => ({ ...prev, [selectedName]: enabled }));
|
|
@@ -629,14 +680,14 @@ function App({ config }) {
|
|
|
629
680
|
}
|
|
630
681
|
const clickedIndex = event.y - 2;
|
|
631
682
|
if (clickedIndex >= 0 && clickedIndex < names.length) {
|
|
632
|
-
|
|
683
|
+
handleSetSelected(clickedIndex);
|
|
633
684
|
}
|
|
634
685
|
} else {
|
|
635
686
|
if (!focusMode) {
|
|
636
687
|
enterFocus();
|
|
637
688
|
}
|
|
638
689
|
}
|
|
639
|
-
}, [names.length, focusMode, enterFocus, exitFocus]);
|
|
690
|
+
}, [names.length, focusMode, enterFocus, exitFocus, handleSetSelected]);
|
|
640
691
|
useMouseWheel({
|
|
641
692
|
enabled: !showHelp,
|
|
642
693
|
// Disable when help is shown
|
|
@@ -710,11 +761,11 @@ function App({ config }) {
|
|
|
710
761
|
return;
|
|
711
762
|
}
|
|
712
763
|
if (key.upArrow || input === "k") {
|
|
713
|
-
|
|
764
|
+
handleSetSelected((s) => Math.max(s - 1, 0));
|
|
714
765
|
return;
|
|
715
766
|
}
|
|
716
767
|
if (key.downArrow || input === "j") {
|
|
717
|
-
|
|
768
|
+
handleSetSelected((s) => Math.min(s + 1, names.length - 1));
|
|
718
769
|
return;
|
|
719
770
|
}
|
|
720
771
|
if (key.return || key.tab) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tui.ts","../src/components/App.tsx","../src/hooks/useProcessManager.ts","../src/process-manager.ts","../src/terminal-buffer.ts","../src/hooks/useFocusMode.ts","../src/hooks/useMouseWheel.ts","../src/components/ProcessList.tsx","../src/components/OutputPanel.tsx","../src/components/Scrollbar.tsx","../src/components/StatusBar.tsx","../src/components/HelpPopup.tsx"],"sourcesContent":["import { render } from 'ink';\nimport { createElement } from 'react';\nimport type { PanexConfig } from './types';\nimport { App } from './components/App';\n\nexport async function createTUI(config: PanexConfig): Promise<void> {\n const { waitUntilExit } = render(createElement(App, { config }));\n await waitUntilExit();\n}\n","import { useState, useEffect, useRef, useCallback } from 'react';\nimport { Box, useApp, useInput, useStdin, useStdout } from 'ink';\nimport type { PanexConfig } from '../types';\nimport { useProcessManager } from '../hooks/useProcessManager';\nimport { useFocusMode } from '../hooks/useFocusMode';\nimport { useMouseWheel } from '../hooks/useMouseWheel';\nimport { ProcessList, ProcessListRef, PROCESS_LIST_WIDTH } from './ProcessList';\nimport { OutputPanel, OutputPanelRef } from './OutputPanel';\nimport { StatusBar } from './StatusBar';\nimport { HelpPopup } from './HelpPopup';\n\ninterface AppProps {\n config: PanexConfig;\n}\n\nexport function App({ config }: AppProps) {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const { setRawMode } = useStdin();\n const [selected, setSelected] = useState(0);\n const [showHelp, setShowHelp] = useState(false);\n const { focusMode, enterFocus, exitFocus } = useFocusMode();\n\n // Refs to control scrolling\n const outputRef = useRef<OutputPanelRef>(null);\n const processListRef = useRef<ProcessListRef>(null);\n\n // Track auto-scroll state per process\n const [autoScroll, setAutoScroll] = useState<Record<string, boolean>>({});\n\n const {\n names,\n getOutput,\n getStatus,\n restart,\n restartAll,\n kill,\n killAll,\n write,\n resize,\n } = useProcessManager(config);\n\n // Check if Shift-Tab is disabled for a process\n const isShiftTabDisabled = (name: string): boolean => {\n const setting = config.settings?.noShiftTab;\n if (setting === true) return true;\n if (Array.isArray(setting)) return setting.includes(name);\n return false;\n };\n\n // Calculate max panel height: terminal rows - status bar (1)\n const maxPanelHeight = stdout ? stdout.rows - 1 : undefined;\n\n // Resize on terminal resize\n useEffect(() => {\n const name = names[selected];\n if (name && stdout) {\n const cols = Math.floor(stdout.columns * 0.8) - 2;\n const rows = stdout.rows - 3;\n resize(name, cols, rows);\n }\n }, [stdout?.columns, stdout?.rows, selected, names, resize]);\n\n // Initialize auto-scroll for new processes\n useEffect(() => {\n setAutoScroll(prev => {\n const next = { ...prev };\n let changed = false;\n for (const name of names) {\n if (next[name] === undefined) {\n next[name] = true;\n changed = true;\n }\n }\n return changed ? next : prev;\n });\n }, [names]);\n\n const selectedName = names[selected] ?? '';\n const output = selectedName ? getOutput(selectedName) : '';\n const currentAutoScroll = selectedName ? (autoScroll[selectedName] ?? true) : true;\n\n // Handle auto-scroll state changes from OutputPanel\n const handleAutoScrollChange = useCallback((enabled: boolean) => {\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: enabled }));\n }\n }, [selectedName]);\n\n // Handle mouse wheel events\n const handleWheel = useCallback((event: { type: 'wheel-up' | 'wheel-down'; x: number; y: number; }) => {\n const delta = event.type === 'wheel-up' ? -3 : 3;\n\n if (outputRef.current) {\n outputRef.current.scrollBy(delta);\n // Disable auto-scroll when user scrolls up\n if (event.type === 'wheel-up' && selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n }\n }, [selectedName]);\n\n // Handle mouse click events\n const handleClick = useCallback((event: { type: 'click'; x: number; y: number; }) => {\n if (event.x <= PROCESS_LIST_WIDTH) {\n // Click on process list - exit focus mode, select process if clicked\n if (focusMode) {\n exitFocus();\n }\n const clickedIndex = event.y - 2; // Adjust for border\n if (clickedIndex >= 0 && clickedIndex < names.length) {\n setSelected(clickedIndex);\n }\n } else {\n // Click on output panel - enter focus mode\n if (!focusMode) {\n enterFocus();\n }\n }\n }, [names.length, focusMode, enterFocus, exitFocus]);\n\n // Enable mouse tracking\n useMouseWheel({\n enabled: !showHelp, // Disable when help is shown\n onWheel: handleWheel,\n onClick: handleClick,\n });\n\n useInput((input, key) => {\n // Handle help popup\n if (showHelp) {\n setShowHelp(false);\n return;\n }\n\n // Quit (Ctrl+C always works, 'q' only in normal mode)\n if (key.ctrl && input === 'c') {\n killAll();\n // Restore terminal: disable raw mode, move cursor to bottom, clear below, disable mouse, show cursor\n setRawMode(false);\n const rows = stdout?.rows ?? 999;\n stdout?.write(`\\x1b[${rows};1H\\x1b[J\\x1b[?1000l\\x1b[?1006l\\x1b[?25h\\x1b[0m\\n`);\n exit();\n process.exit(0);\n }\n\n // Focus mode input handling (before normal mode keys like 'q')\n if (focusMode) {\n const name = names[selected];\n if (!name) return;\n\n // Exit focus\n if (key.escape) {\n exitFocus();\n return;\n }\n\n // Shift-Tab exit (unless disabled)\n if (key.shift && key.tab && !isShiftTabDisabled(name)) {\n exitFocus();\n return;\n }\n\n // Forward Enter\n if (key.return) {\n write(name, '\\r');\n return;\n }\n\n // Forward arrow keys\n if (key.upArrow) {\n write(name, '\\x1b[A');\n return;\n }\n if (key.downArrow) {\n write(name, '\\x1b[B');\n return;\n }\n if (key.leftArrow) {\n write(name, '\\x1b[D');\n return;\n }\n if (key.rightArrow) {\n write(name, '\\x1b[C');\n return;\n }\n\n // Forward regular input (filter out mouse escape sequences)\n if (input && !key.ctrl && !key.meta) {\n // Remove SGR mouse sequences like \\x1b[<64;45;5M or [<0;12;7M\n const filtered = input.replace(/\\x1b?\\[<\\d+;\\d+;\\d+[Mm]/g, '');\n if (filtered) {\n write(name, filtered);\n }\n }\n return;\n }\n\n // Normal mode\n\n // Quit with 'q' (only in normal mode)\n if (input === 'q') {\n killAll();\n setRawMode(false);\n const rows = stdout?.rows ?? 999;\n stdout?.write(`\\x1b[${rows};1H\\x1b[J\\x1b[?1000l\\x1b[?1006l\\x1b[?25h\\x1b[0m\\n`);\n exit();\n process.exit(0);\n }\n\n // Help\n if (input === '?') {\n setShowHelp(true);\n return;\n }\n\n // Navigation\n if (key.upArrow || input === 'k') {\n setSelected(s => Math.max(s - 1, 0));\n return;\n }\n if (key.downArrow || input === 'j') {\n setSelected(s => Math.min(s + 1, names.length - 1));\n return;\n }\n\n // Enter focus mode\n if (key.return || key.tab) {\n enterFocus();\n return;\n }\n\n // Process control\n if (input === 'r') {\n const name = names[selected];\n if (name) restart(name);\n return;\n }\n if (input === 'A') {\n restartAll();\n return;\n }\n if (input === 'x') {\n const name = names[selected];\n if (name) kill(name);\n return;\n }\n\n // Output scrolling\n if (input === 'g') {\n outputRef.current?.scrollToTop();\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n return;\n }\n if (input === 'G') {\n outputRef.current?.scrollToBottom();\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: true }));\n }\n return;\n }\n if (key.pageUp) {\n const pageSize = outputRef.current?.getViewportHeight() ?? 10;\n outputRef.current?.scrollBy(-pageSize);\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n return;\n }\n if (key.pageDown) {\n const pageSize = outputRef.current?.getViewportHeight() ?? 10;\n outputRef.current?.scrollBy(pageSize);\n return;\n }\n });\n\n const showShiftTabHint = selectedName ? !isShiftTabDisabled(selectedName) : true;\n\n return (\n <Box flexDirection=\"column\" height={maxPanelHeight}>\n <Box flexDirection=\"row\" flexGrow={1} height={maxPanelHeight ? maxPanelHeight - 1 : undefined}>\n <ProcessList\n ref={processListRef}\n names={names}\n selected={selected}\n getStatus={getStatus}\n active={!focusMode}\n height={maxPanelHeight ? maxPanelHeight - 1 : undefined}\n />\n <OutputPanel\n ref={outputRef}\n name={selectedName}\n output={output}\n active={focusMode}\n height={maxPanelHeight ? maxPanelHeight - 1 : undefined}\n autoScroll={currentAutoScroll}\n onAutoScrollChange={handleAutoScrollChange}\n />\n </Box>\n <StatusBar\n focusMode={focusMode}\n processName={selectedName}\n showShiftTabHint={showShiftTabHint}\n />\n <HelpPopup visible={showHelp} />\n </Box>\n );\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { ProcessManager, type ManagedProcess } from '../process-manager';\nimport type { PanexConfig, ProcessStatus } from '../types';\n\nexport interface UseProcessManagerResult {\n processManager: ProcessManager;\n processes: Map<string, ManagedProcess>;\n names: string[];\n getOutput: (name: string) => string;\n getStatus: (name: string) => ProcessStatus;\n restart: (name: string) => void;\n restartAll: () => void;\n kill: (name: string) => void;\n killAll: () => void;\n write: (name: string, data: string) => void;\n resize: (name: string, cols: number, rows: number) => void;\n}\n\nexport function useProcessManager(config: PanexConfig): UseProcessManagerResult {\n const [, forceUpdate] = useState({});\n const processManagerRef = useRef<ProcessManager | null>(null);\n\n if (!processManagerRef.current) {\n processManagerRef.current = new ProcessManager(config.procs);\n }\n\n const pm = processManagerRef.current;\n\n useEffect(() => {\n const update = () => forceUpdate({});\n pm.on('output', update);\n pm.on('started', update);\n pm.on('exit', update);\n pm.on('error', update);\n\n pm.startAll();\n\n return () => {\n pm.removeAllListeners();\n pm.killAll();\n };\n }, [pm]);\n\n const getOutput = useCallback((name: string) => pm.getOutput(name), [pm]);\n\n const getStatus = useCallback((name: string): ProcessStatus => {\n const proc = pm.getProcess(name);\n return proc?.status ?? 'stopped';\n }, [pm]);\n\n const restart = useCallback((name: string) => pm.restart(name), [pm]);\n const restartAll = useCallback(() => pm.restartAll(), [pm]);\n const kill = useCallback((name: string) => pm.kill(name), [pm]);\n const killAll = useCallback(() => pm.killAll(), [pm]);\n const write = useCallback((name: string, data: string) => pm.write(name, data), [pm]);\n const resize = useCallback((name: string, cols: number, rows: number) => pm.resize(name, cols, rows), [pm]);\n\n return {\n processManager: pm,\n processes: new Map(pm.getNames().map(n => [n, pm.getProcess(n)!])),\n names: pm.getNames(),\n getOutput,\n getStatus,\n restart,\n restartAll,\n kill,\n killAll,\n write,\n resize,\n };\n}\n","import { EventEmitter } from 'events';\nimport type { ProcessConfig } from './types';\nimport { TerminalBuffer } from './terminal-buffer';\n\ninterface PtyHandle {\n write(data: string): void;\n resize(cols: number, rows: number): void;\n kill(): void;\n}\n\nexport interface ManagedProcess {\n name: string;\n config: ProcessConfig;\n pty: PtyHandle | null;\n status: 'running' | 'stopped' | 'error';\n terminalBuffer: TerminalBuffer;\n exitCode: number | null;\n}\n\nexport class ProcessManager extends EventEmitter {\n private processes: Map<string, ManagedProcess> = new Map();\n\n constructor(private procs: Record<string, ProcessConfig>) {\n super();\n }\n\n async startAll(): Promise<void> {\n for (const [name, config] of Object.entries(this.procs)) {\n await this.start(name, config);\n }\n }\n\n async start(name: string, config: ProcessConfig): Promise<void> {\n const existing = this.processes.get(name);\n if (existing?.pty) {\n existing.pty.kill();\n }\n\n const shell = process.platform === 'win32' ? 'powershell.exe' : 'bash';\n const args = config.shell\n ? ['-c', config.shell]\n : config.cmd\n ? ['-c', config.cmd.join(' ')]\n : [];\n\n const cwd = config.cwd ?? process.cwd();\n const env = { ...process.env, ...config.env };\n\n const managed: ManagedProcess = {\n name,\n config,\n pty: null,\n status: 'running',\n terminalBuffer: new TerminalBuffer(120, 30),\n exitCode: null,\n };\n\n this.processes.set(name, managed);\n\n try {\n const proc = Bun.spawn([shell, ...args], {\n cwd,\n env: env as Record<string, string>,\n terminal: {\n cols: 120,\n rows: 30,\n data: (_terminal: unknown, data: Uint8Array) => {\n const str = new TextDecoder().decode(data);\n managed.terminalBuffer.write(str);\n this.emit('output', name, str);\n },\n },\n });\n\n managed.pty = {\n write: (data: string) => proc.terminal?.write(data),\n resize: (cols: number, rows: number) => proc.terminal?.resize(cols, rows),\n kill: () => proc.kill(),\n };\n\n // Handle exit\n proc.exited.then((exitCode) => {\n managed.status = exitCode === 0 ? 'stopped' : 'error';\n managed.exitCode = exitCode;\n managed.pty = null;\n this.emit('exit', name, exitCode);\n\n if (managed.config.autoRestart && exitCode !== 0) {\n setTimeout(() => this.start(name, managed.config), 1000);\n }\n });\n\n this.emit('started', name);\n } catch (error) {\n managed.status = 'error';\n managed.terminalBuffer.write(`Error starting process: ${error}`);\n managed.exitCode = -1;\n this.emit('error', name, error);\n }\n }\n\n restart(name: string): void {\n const proc = this.processes.get(name);\n if (proc) {\n if (proc.pty) {\n proc.pty.kill();\n }\n proc.terminalBuffer.clear();\n this.start(name, proc.config);\n }\n }\n\n restartAll(): void {\n for (const name of this.processes.keys()) {\n this.restart(name);\n }\n }\n\n kill(name: string): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.kill();\n }\n }\n\n killAll(): void {\n for (const proc of this.processes.values()) {\n if (proc.pty) {\n proc.pty.kill();\n }\n }\n }\n\n write(name: string, data: string): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.write(data);\n }\n }\n\n resize(name: string, cols: number, rows: number): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.resize(cols, rows);\n }\n }\n\n getProcess(name: string): ManagedProcess | undefined {\n return this.processes.get(name);\n }\n\n getProcesses(): ManagedProcess[] {\n return Array.from(this.processes.values());\n }\n\n getNames(): string[] {\n return Array.from(this.processes.keys());\n }\n\n getOutput(name: string): string {\n return this.processes.get(name)?.terminalBuffer.toString() ?? '';\n }\n}\n","import { Terminal } from '@xterm/headless';\n\n/**\n * A terminal buffer that properly interprets ANSI escape sequences including:\n * - Cursor movement (\\x1b[A up, \\x1b[B down, \\x1b[C right, \\x1b[D left)\n * - Cursor positioning (\\x1b[H, \\x1b[row;colH)\n * - Line clearing (\\x1b[K erase to end, \\x1b[2K erase line)\n * - Screen clearing (\\x1b[2J clear screen)\n * - Carriage return (\\r) for in-place updates like progress bars\n */\nexport class TerminalBuffer {\n private terminal: Terminal;\n private rows: number;\n private cols: number;\n\n constructor(cols = 200, rows = 500) {\n this.rows = rows;\n this.cols = cols;\n this.terminal = new Terminal({\n cols,\n rows,\n scrollback: 10000,\n allowProposedApi: true,\n });\n }\n\n /**\n * Write data to the terminal buffer.\n * The terminal will interpret all ANSI escape sequences.\n */\n write(data: string): void {\n this.terminal.write(data);\n }\n\n /**\n * Get the current terminal buffer content as an array of lines.\n * Only returns lines that have content (not all 500 rows).\n */\n getLines(): string[] {\n const buffer = this.terminal.buffer.active;\n const lines: string[] = [];\n\n // Get actual content length (baseY is lines scrolled off + cursorY + 1)\n const contentLength = buffer.baseY + buffer.cursorY + 1;\n\n for (let i = 0; i < contentLength; i++) {\n const line = buffer.getLine(i);\n if (line) {\n lines.push(line.translateToString(true)); // trim trailing whitespace\n }\n }\n\n // Remove trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n\n return lines;\n }\n\n /**\n * Get the terminal content as a single string with newlines.\n */\n toString(): string {\n return this.getLines().join('\\n');\n }\n\n /**\n * Clear the terminal buffer.\n */\n clear(): void {\n this.terminal.reset();\n }\n\n /**\n * Resize the terminal.\n */\n resize(cols: number, rows: number): void {\n this.cols = cols;\n this.rows = rows;\n this.terminal.resize(cols, rows);\n }\n\n /**\n * Dispose of the terminal to free resources.\n */\n dispose(): void {\n this.terminal.dispose();\n }\n}\n","import { useState, useCallback } from 'react';\n\nexport interface UseFocusModeResult {\n focusMode: boolean;\n enterFocus: () => void;\n exitFocus: () => void;\n toggleFocus: () => void;\n}\n\nexport function useFocusMode(): UseFocusModeResult {\n const [focusMode, setFocusMode] = useState(false);\n\n const enterFocus = useCallback(() => setFocusMode(true), []);\n const exitFocus = useCallback(() => setFocusMode(false), []);\n const toggleFocus = useCallback(() => setFocusMode(f => !f), []);\n\n return { focusMode, enterFocus, exitFocus, toggleFocus };\n}\n","import { useEffect, useCallback } from 'react';\nimport { useStdin, useStdout } from 'ink';\n\ninterface MouseWheelEvent {\n type: 'wheel-up' | 'wheel-down';\n x: number;\n y: number;\n}\n\ninterface MouseClickEvent {\n type: 'click';\n x: number;\n y: number;\n}\n\ninterface UseMouseWheelOptions {\n enabled?: boolean;\n onWheel?: (event: MouseWheelEvent) => void;\n onClick?: (event: MouseClickEvent) => void;\n}\n\n/**\n * Hook to enable mouse tracking in the terminal.\n *\n * Uses ANSI escape sequences for SGR extended mouse mode:\n * - \\x1b[?1000h - Enable mouse button tracking\n * - \\x1b[?1006h - Enable SGR extended mouse mode\n *\n * Mouse events (SGR mode):\n * - Left click: \\x1b[<0;X;YM (button 0 = left click press)\n * - Scroll up: \\x1b[<64;X;YM (button 64 = wheel up)\n * - Scroll down: \\x1b[<65;X;YM (button 65 = wheel down)\n */\nexport function useMouseWheel({ enabled = true, onWheel, onClick }: UseMouseWheelOptions = {}) {\n const { stdin, setRawMode } = useStdin();\n const { stdout } = useStdout();\n\n const handleData = useCallback((data: Buffer) => {\n const str = data.toString();\n\n // Parse SGR mouse events: \\x1b[<button;x;yM or \\x1b[<button;x;ym\n // Button 64 = wheel up, Button 65 = wheel down\n const sgrRegex = /\\x1b\\[<(\\d+);(\\d+);(\\d+)([Mm])/g;\n let match;\n\n while ((match = sgrRegex.exec(str)) !== null) {\n const button = parseInt(match[1] ?? '0', 10);\n const x = parseInt(match[2] ?? '0', 10);\n const y = parseInt(match[3] ?? '0', 10);\n // M = press, m = release (we only care about press for wheel)\n const isPress = match[4] === 'M';\n\n if (isPress) {\n if (button === 0) {\n onClick?.({ type: 'click', x, y });\n } else if (button === 64) {\n onWheel?.({ type: 'wheel-up', x, y });\n } else if (button === 65) {\n onWheel?.({ type: 'wheel-down', x, y });\n }\n }\n }\n }, [onWheel, onClick]);\n\n useEffect(() => {\n if (!enabled || !stdin || !stdout) return;\n\n // Enable mouse tracking\n // 1000h: X11 mouse button tracking\n // 1006h: SGR extended mouse mode (for proper coordinates)\n stdout.write('\\x1b[?1000h\\x1b[?1006h');\n\n // Ensure raw mode is enabled\n setRawMode?.(true);\n\n // Listen for mouse events\n stdin.on('data', handleData);\n\n return () => {\n stdin.off('data', handleData);\n // Disable mouse tracking on cleanup\n stdout.write('\\x1b[?1000l\\x1b[?1006l');\n };\n }, [enabled, stdin, stdout, setRawMode, handleData]);\n}\n","import { Box, Text, useStdout } from 'ink';\nimport { ScrollList, ScrollListRef } from 'ink-scroll-list';\nimport { forwardRef, useImperativeHandle, useRef, useEffect } from 'react';\nimport type { ProcessStatus } from '../types';\n\nexport const PROCESS_LIST_WIDTH = 20;\n\ninterface ProcessListProps {\n names: string[];\n selected: number;\n getStatus: (name: string) => ProcessStatus;\n active: boolean;\n height?: number;\n}\n\nexport interface ProcessListRef {\n scrollBy: (delta: number) => void;\n scrollToTop: () => void;\n scrollToBottom: () => void;\n}\n\nexport const ProcessList = forwardRef<ProcessListRef, ProcessListProps>(\n function ProcessList({ names, selected, getStatus, active, height }, ref) {\n const borderStyle = active ? 'double' : 'single';\n const listRef = useRef<ScrollListRef>(null);\n const { stdout } = useStdout();\n\n // Handle terminal resize\n useEffect(() => {\n const handleResize = () => listRef.current?.remeasure();\n stdout?.on('resize', handleResize);\n return () => {\n stdout?.off('resize', handleResize);\n };\n }, [stdout]);\n\n // Expose scroll methods via ref\n useImperativeHandle(ref, () => ({\n scrollBy: (delta: number) => listRef.current?.scrollBy(delta),\n scrollToTop: () => listRef.current?.scrollToTop(),\n scrollToBottom: () => listRef.current?.scrollToBottom(),\n }));\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle={borderStyle}\n borderColor={active ? 'blue' : 'gray'}\n width={PROCESS_LIST_WIDTH}\n height={height}\n paddingX={1}\n >\n <Box flexDirection=\"column\" marginTop={0} height={height ? height - 2 : undefined}>\n <ScrollList\n ref={listRef}\n selectedIndex={selected}\n scrollAlignment=\"auto\"\n >\n {names.map((name, i) => {\n const status = getStatus(name);\n const statusIcon = status === 'running' ? '●' : status === 'error' ? '✗' : '○';\n const statusColor = status === 'running' ? 'green' : status === 'error' ? 'red' : 'gray';\n const isSelected = i === selected;\n\n return (\n <Box key={name} backgroundColor={isSelected ? 'blue' : undefined}>\n <Text color={isSelected ? 'black' : undefined}>\n {name}{' '}\n </Text>\n <Text color={statusColor}>{statusIcon}</Text>\n </Box>\n );\n })}\n </ScrollList>\n </Box>\n </Box>\n );\n }\n);\n","import { Box, Text, useStdout } from 'ink';\nimport { ScrollView, ScrollViewRef } from 'ink-scroll-view';\nimport { forwardRef, useImperativeHandle, useRef, useEffect, useState, useCallback } from 'react';\nimport { Scrollbar } from './Scrollbar';\n\ninterface OutputPanelProps {\n name: string;\n output: string;\n active: boolean;\n height?: number;\n autoScroll?: boolean;\n onAutoScrollChange?: (enabled: boolean) => void;\n}\n\nexport interface OutputPanelRef {\n scrollBy: (delta: number) => void;\n scrollToTop: () => void;\n scrollToBottom: () => void;\n getScrollOffset: () => number;\n getContentHeight: () => number;\n getViewportHeight: () => number;\n isAtBottom: () => boolean;\n}\n\nexport const OutputPanel = forwardRef<OutputPanelRef, OutputPanelProps>(\n function OutputPanel({ name, output, active, height, autoScroll = true, onAutoScrollChange }, ref) {\n const borderStyle = active ? 'double' : 'single';\n const lines = output.split('\\n');\n const scrollRef = useRef<ScrollViewRef>(null);\n const { stdout } = useStdout();\n\n // Track scroll state for scrollbar\n const [scrollOffset, setScrollOffset] = useState(0);\n const [contentHeight, setContentHeight] = useState(0);\n const [viewportHeight, setViewportHeight] = useState(0);\n\n // Handle terminal resize\n useEffect(() => {\n const handleResize = () => scrollRef.current?.remeasure();\n stdout?.on('resize', handleResize);\n return () => {\n stdout?.off('resize', handleResize);\n };\n }, [stdout]);\n\n // Check if at bottom with small tolerance\n const isAtBottom = useCallback(() => {\n if (!scrollRef.current) return true;\n const offset = scrollRef.current.getScrollOffset();\n const bottom = scrollRef.current.getBottomOffset();\n // Allow 1 line tolerance for rounding issues\n return offset >= bottom - 1;\n }, []);\n\n // Auto-scroll when content height changes (if enabled)\n const handleContentHeightChange = useCallback((newHeight: number) => {\n setContentHeight(newHeight);\n if (autoScroll && scrollRef.current) {\n // Use setTimeout to ensure layout is complete\n setTimeout(() => {\n scrollRef.current?.scrollToBottom();\n }, 0);\n }\n }, [autoScroll]);\n\n // Track scroll and update auto-scroll state\n const handleScroll = useCallback((offset: number) => {\n setScrollOffset(offset);\n\n // If user manually scrolled away from bottom, disable auto-scroll\n if (scrollRef.current) {\n const bottom = scrollRef.current.getBottomOffset();\n const atBottom = offset >= bottom - 1;\n\n if (!atBottom && autoScroll) {\n onAutoScrollChange?.(false);\n } else if (atBottom && !autoScroll) {\n onAutoScrollChange?.(true);\n }\n }\n }, [autoScroll, onAutoScrollChange]);\n\n // Expose scroll methods via ref\n useImperativeHandle(ref, () => ({\n scrollBy: (delta: number) => scrollRef.current?.scrollBy(delta),\n scrollToTop: () => scrollRef.current?.scrollToTop(),\n scrollToBottom: () => scrollRef.current?.scrollToBottom(),\n getScrollOffset: () => scrollRef.current?.getScrollOffset() ?? 0,\n getContentHeight: () => scrollRef.current?.getContentHeight() ?? 0,\n getViewportHeight: () => scrollRef.current?.getViewportHeight() ?? 0,\n isAtBottom,\n }));\n\n // Scrollbar height (same as content area)\n const scrollbarHeight = height ? height - 4 : 20;\n const hasScroll = contentHeight > viewportHeight;\n\n // Show pin indicator when auto-scroll is disabled (user scrolled up)\n const pinIndicator = !autoScroll && hasScroll ? ' ⍗' : '';\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle={borderStyle}\n borderColor={active ? 'green' : 'gray'}\n flexGrow={1}\n height={height}\n paddingLeft={1}\n >\n <Box flexDirection=\"row\" marginTop={0} height={height ? height - 2 : undefined}>\n <Box flexDirection=\"column\" flexGrow={1}>\n <ScrollView\n ref={scrollRef}\n onScroll={handleScroll}\n onContentHeightChange={handleContentHeightChange}\n onViewportSizeChange={(layout) => setViewportHeight(layout.height)}\n >\n {lines.map((line, i) => (\n <Text key={i} wrap=\"truncate\">{line}</Text>\n ))}\n </ScrollView>\n </Box>\n {hasScroll && (\n <Scrollbar\n scrollOffset={scrollOffset}\n contentHeight={contentHeight}\n viewportHeight={viewportHeight}\n height={scrollbarHeight}\n />\n )}\n </Box>\n </Box>\n );\n }\n);\n","import { Box, Text } from 'ink';\n\ninterface ScrollbarProps {\n /** Current scroll offset */\n scrollOffset: number;\n /** Total content height */\n contentHeight: number;\n /** Visible viewport height */\n viewportHeight: number;\n /** Height of the scrollbar track (usually same as viewport) */\n height: number;\n}\n\n/**\n * A simple vertical scrollbar component.\n * Uses Unicode block characters to show scroll position.\n */\nexport function Scrollbar({\n scrollOffset,\n contentHeight,\n viewportHeight,\n height,\n}: ScrollbarProps) {\n // Don't show scrollbar if content fits in viewport\n if (contentHeight <= viewportHeight) {\n return (\n <Box flexDirection=\"column\" width={1}>\n {Array.from({ length: height }).map((_, i) => (\n <Text key={i} dimColor> </Text>\n ))}\n </Box>\n );\n }\n\n // Calculate thumb size and position\n const trackHeight = height;\n const thumbRatio = viewportHeight / contentHeight;\n const thumbHeight = Math.max(1, Math.round(trackHeight * thumbRatio));\n\n const maxScroll = contentHeight - viewportHeight;\n const scrollRatio = maxScroll > 0 ? scrollOffset / maxScroll : 0;\n const thumbPosition = Math.round((trackHeight - thumbHeight) * scrollRatio);\n\n // Build the scrollbar\n const lines: string[] = [];\n for (let i = 0; i < trackHeight; i++) {\n if (i >= thumbPosition && i < thumbPosition + thumbHeight) {\n lines.push('█'); // Thumb\n } else {\n lines.push('░'); // Track\n }\n }\n\n return (\n <Box flexDirection=\"column\" width={1}>\n {lines.map((char, i) => (\n <Text key={i} dimColor={char === '░'}>{char}</Text>\n ))}\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\n\ninterface StatusBarProps {\n focusMode: boolean;\n processName?: string;\n showShiftTabHint?: boolean;\n}\n\nexport function StatusBar({ focusMode, processName, showShiftTabHint = true }: StatusBarProps) {\n if (focusMode && processName) {\n const shiftTabHint = showShiftTabHint ? 'Shift-Tab/' : '';\n return (\n <Box backgroundColor=\"green\" width=\"100%\">\n <Text bold color=\"black\" backgroundColor=\"green\">\n {' '}{processName} | [{shiftTabHint}Esc] to exit focus mode{' '}\n </Text>\n </Box>\n );\n }\n\n return (\n <Box backgroundColor=\"blue\" width=\"100%\">\n <Text bold color=\"black\" backgroundColor=\"blue\">\n {' '}[↑↓/jk] select [Tab/Enter] focus [r] restart [A] restart All [x] kill [q] quit [?] help{' '}\n </Text>\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\n\ninterface HelpPopupProps {\n visible: boolean;\n}\n\nexport function HelpPopup({ visible }: HelpPopupProps) {\n if (!visible) return null;\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"yellow\"\n padding={1}\n position=\"absolute\"\n marginLeft={10}\n marginTop={5}\n >\n <Text bold color=\"yellow\"> Help </Text>\n <Text>{'\\n'}Keyboard Shortcuts</Text>\n <Text>{'─'.repeat(18)}</Text>\n <Text>{'\\n'}Navigation</Text>\n <Text> ↑/↓ or j/k Navigate process list</Text>\n <Text> g/G Scroll to top/bottom of output</Text>\n <Text> PgUp/PgDn Scroll output</Text>\n <Text>{'\\n'}Process Control</Text>\n <Text> Tab/Enter Focus process (interactive mode)</Text>\n <Text> Esc Exit focus mode</Text>\n <Text> r Restart selected process</Text>\n <Text> A Restart all processes</Text>\n <Text> x Kill selected process</Text>\n <Text>{'\\n'}General</Text>\n <Text> ? Toggle this help</Text>\n <Text> q Quit panex</Text>\n <Text>{'\\n'}Press any key to close this help...</Text>\n </Box>\n );\n}\n"],"mappings":"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","names":["useState","useEffect","useRef","useCallback","Box","useStdin","useStdout","useState","useCallback","useEffect","useCallback","useStdout","useRef","useEffect","ProcessList","Box","Text","useStdout","forwardRef","useImperativeHandle","useRef","useEffect","useState","useCallback","Box","Text","jsx","jsx","jsxs","forwardRef","OutputPanel","useRef","useStdout","useState","useEffect","useCallback","useImperativeHandle","Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","jsx","jsxs","useStdout","useStdin","useState","useRef","useEffect","useCallback","Box"]}
|
|
1
|
+
{"version":3,"sources":["../src/tui.ts","../src/components/App.tsx","../src/hooks/useProcessManager.ts","../src/process-manager.ts","../src/terminal-buffer.ts","../src/hooks/useFocusMode.ts","../src/hooks/useMouseWheel.ts","../src/components/ProcessList.tsx","../src/components/OutputPanel.tsx","../src/components/Scrollbar.tsx","../src/components/StatusBar.tsx","../src/components/HelpPopup.tsx"],"sourcesContent":["import { render } from 'ink';\nimport { createElement } from 'react';\nimport type { PanexConfig } from './types';\nimport { App } from './components/App';\n\nexport async function createTUI(config: PanexConfig): Promise<void> {\n const { waitUntilExit } = render(createElement(App, { config }));\n await waitUntilExit();\n}\n","import { useState, useEffect, useRef, useCallback } from 'react';\nimport { Box, useApp, useInput, useStdin, useStdout } from 'ink';\nimport type { PanexConfig } from '../types';\nimport { useProcessManager } from '../hooks/useProcessManager';\nimport { useFocusMode } from '../hooks/useFocusMode';\nimport { useMouseWheel } from '../hooks/useMouseWheel';\nimport { ProcessList, ProcessListRef, PROCESS_LIST_WIDTH } from './ProcessList';\nimport { OutputPanel, OutputPanelRef } from './OutputPanel';\nimport { StatusBar } from './StatusBar';\nimport { HelpPopup } from './HelpPopup';\n\ninterface AppProps {\n config: PanexConfig;\n}\n\nexport function App({ config }: AppProps) {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const { setRawMode } = useStdin();\n const [selected, setSelected] = useState(0);\n const [showHelp, setShowHelp] = useState(false);\n const { focusMode, enterFocus, exitFocus } = useFocusMode();\n\n // Refs to control scrolling\n const outputRef = useRef<OutputPanelRef>(null);\n const processListRef = useRef<ProcessListRef>(null);\n\n // Track auto-scroll state per process\n const [autoScroll, setAutoScroll] = useState<Record<string, boolean>>({});\n\n // Track scroll position per process (use ref to avoid closure issues)\n const scrollPositionsRef = useRef<Record<string, number>>({});\n const pendingRestoreRef = useRef<string | null>(null);\n\n const {\n names,\n getOutput,\n getStatus,\n restart,\n restartAll,\n kill,\n killAll,\n write,\n resize,\n } = useProcessManager(config);\n\n // Check if Shift-Tab is disabled for a process\n const isShiftTabDisabled = (name: string): boolean => {\n const setting = config.settings?.noShiftTab;\n if (setting === true) return true;\n if (Array.isArray(setting)) return setting.includes(name);\n return false;\n };\n\n // Calculate max panel height: terminal rows - status bar (1)\n const maxPanelHeight = stdout ? stdout.rows - 1 : undefined;\n\n // Resize on terminal resize\n useEffect(() => {\n const name = names[selected];\n if (name && stdout) {\n const cols = Math.floor(stdout.columns * 0.8) - 2;\n const rows = stdout.rows - 3;\n resize(name, cols, rows);\n }\n }, [stdout?.columns, stdout?.rows, selected, names, resize]);\n\n // Initialize auto-scroll for new processes\n useEffect(() => {\n setAutoScroll(prev => {\n const next = { ...prev };\n let changed = false;\n for (const name of names) {\n if (next[name] === undefined) {\n next[name] = true;\n changed = true;\n }\n }\n return changed ? next : prev;\n });\n }, [names]);\n\n const selectedName = names[selected] ?? '';\n const output = selectedName ? getOutput(selectedName) : '';\n const currentAutoScroll = selectedName ? (autoScroll[selectedName] ?? true) : true;\n\n // Helper to save current scroll position before switching\n const saveCurrentScrollPosition = useCallback(() => {\n if (selectedName && outputRef.current) {\n scrollPositionsRef.current[selectedName] = outputRef.current.getScrollOffset();\n }\n }, [selectedName]);\n\n // Custom setSelected that saves scroll position first\n const handleSetSelected = useCallback((newSelected: number | ((s: number) => number)) => {\n saveCurrentScrollPosition();\n setSelected(prev => {\n const next = typeof newSelected === 'function' ? newSelected(prev) : newSelected;\n if (next !== prev) {\n // Mark that we need to restore scroll for the new process\n pendingRestoreRef.current = names[next] ?? null;\n }\n return next;\n });\n }, [saveCurrentScrollPosition, names]);\n\n // Restore scroll position after render when process changes\n useEffect(() => {\n const restoreName = pendingRestoreRef.current;\n if (restoreName && restoreName === selectedName) {\n pendingRestoreRef.current = null;\n const savedOffset = scrollPositionsRef.current[restoreName];\n // Only restore if we have a saved position AND auto-scroll is disabled\n if (savedOffset !== undefined && savedOffset > 0 && !autoScroll[restoreName]) {\n // Use setImmediate to ensure render is complete\n setImmediate(() => {\n if (outputRef.current) {\n // Only restore if content actually needs scrolling\n const contentHeight = outputRef.current.getContentHeight();\n const viewportHeight = outputRef.current.getViewportHeight();\n if (contentHeight > viewportHeight) {\n const currentOffset = outputRef.current.getScrollOffset();\n if (currentOffset !== savedOffset) {\n outputRef.current.scrollBy(savedOffset - currentOffset);\n }\n }\n }\n });\n }\n }\n }, [selectedName, autoScroll]);\n\n // Handle auto-scroll state changes from OutputPanel\n const handleAutoScrollChange = useCallback((enabled: boolean) => {\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: enabled }));\n }\n }, [selectedName]);\n\n // Handle mouse wheel events\n const handleWheel = useCallback((event: { type: 'wheel-up' | 'wheel-down'; x: number; y: number; }) => {\n const delta = event.type === 'wheel-up' ? -3 : 3;\n\n if (outputRef.current) {\n outputRef.current.scrollBy(delta);\n // Disable auto-scroll when user scrolls up\n if (event.type === 'wheel-up' && selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n }\n }, [selectedName]);\n\n // Handle mouse click events\n const handleClick = useCallback((event: { type: 'click'; x: number; y: number; }) => {\n if (event.x <= PROCESS_LIST_WIDTH) {\n // Click on process list - exit focus mode, select process if clicked\n if (focusMode) {\n exitFocus();\n }\n const clickedIndex = event.y - 2; // Adjust for border\n if (clickedIndex >= 0 && clickedIndex < names.length) {\n handleSetSelected(clickedIndex);\n }\n } else {\n // Click on output panel - enter focus mode\n if (!focusMode) {\n enterFocus();\n }\n }\n }, [names.length, focusMode, enterFocus, exitFocus, handleSetSelected]);\n\n // Enable mouse tracking\n useMouseWheel({\n enabled: !showHelp, // Disable when help is shown\n onWheel: handleWheel,\n onClick: handleClick,\n });\n\n useInput((input, key) => {\n // Handle help popup\n if (showHelp) {\n setShowHelp(false);\n return;\n }\n\n // Quit (Ctrl+C always works, 'q' only in normal mode)\n if (key.ctrl && input === 'c') {\n killAll();\n // Restore terminal: disable raw mode, move cursor to bottom, clear below, disable mouse, show cursor\n setRawMode(false);\n const rows = stdout?.rows ?? 999;\n stdout?.write(`\\x1b[${rows};1H\\x1b[J\\x1b[?1000l\\x1b[?1006l\\x1b[?25h\\x1b[0m\\n`);\n exit();\n process.exit(0);\n }\n\n // Focus mode input handling (before normal mode keys like 'q')\n if (focusMode) {\n const name = names[selected];\n if (!name) return;\n\n // Exit focus\n if (key.escape) {\n exitFocus();\n return;\n }\n\n // Shift-Tab exit (unless disabled)\n if (key.shift && key.tab && !isShiftTabDisabled(name)) {\n exitFocus();\n return;\n }\n\n // Forward Enter\n if (key.return) {\n write(name, '\\r');\n return;\n }\n\n // Forward arrow keys\n if (key.upArrow) {\n write(name, '\\x1b[A');\n return;\n }\n if (key.downArrow) {\n write(name, '\\x1b[B');\n return;\n }\n if (key.leftArrow) {\n write(name, '\\x1b[D');\n return;\n }\n if (key.rightArrow) {\n write(name, '\\x1b[C');\n return;\n }\n\n // Forward regular input (filter out mouse escape sequences)\n if (input && !key.ctrl && !key.meta) {\n // Remove SGR mouse sequences like \\x1b[<64;45;5M or [<0;12;7M\n const filtered = input.replace(/\\x1b?\\[<\\d+;\\d+;\\d+[Mm]/g, '');\n if (filtered) {\n write(name, filtered);\n }\n }\n return;\n }\n\n // Normal mode\n\n // Quit with 'q' (only in normal mode)\n if (input === 'q') {\n killAll();\n setRawMode(false);\n const rows = stdout?.rows ?? 999;\n stdout?.write(`\\x1b[${rows};1H\\x1b[J\\x1b[?1000l\\x1b[?1006l\\x1b[?25h\\x1b[0m\\n`);\n exit();\n process.exit(0);\n }\n\n // Help\n if (input === '?') {\n setShowHelp(true);\n return;\n }\n\n // Navigation\n if (key.upArrow || input === 'k') {\n handleSetSelected(s => Math.max(s - 1, 0));\n return;\n }\n if (key.downArrow || input === 'j') {\n handleSetSelected(s => Math.min(s + 1, names.length - 1));\n return;\n }\n\n // Enter focus mode\n if (key.return || key.tab) {\n enterFocus();\n return;\n }\n\n // Process control\n if (input === 'r') {\n const name = names[selected];\n if (name) restart(name);\n return;\n }\n if (input === 'A') {\n restartAll();\n return;\n }\n if (input === 'x') {\n const name = names[selected];\n if (name) kill(name);\n return;\n }\n\n // Output scrolling\n if (input === 'g') {\n outputRef.current?.scrollToTop();\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n return;\n }\n if (input === 'G') {\n outputRef.current?.scrollToBottom();\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: true }));\n }\n return;\n }\n if (key.pageUp) {\n const pageSize = outputRef.current?.getViewportHeight() ?? 10;\n outputRef.current?.scrollBy(-pageSize);\n if (selectedName) {\n setAutoScroll(prev => ({ ...prev, [selectedName]: false }));\n }\n return;\n }\n if (key.pageDown) {\n const pageSize = outputRef.current?.getViewportHeight() ?? 10;\n outputRef.current?.scrollBy(pageSize);\n return;\n }\n });\n\n const showShiftTabHint = selectedName ? !isShiftTabDisabled(selectedName) : true;\n\n return (\n <Box flexDirection=\"column\" height={maxPanelHeight}>\n <Box flexDirection=\"row\" flexGrow={1} height={maxPanelHeight ? maxPanelHeight - 1 : undefined}>\n <ProcessList\n ref={processListRef}\n names={names}\n selected={selected}\n getStatus={getStatus}\n active={!focusMode}\n height={maxPanelHeight ? maxPanelHeight - 1 : undefined}\n />\n <OutputPanel\n ref={outputRef}\n name={selectedName}\n output={output}\n active={focusMode}\n height={maxPanelHeight ? maxPanelHeight - 1 : undefined}\n autoScroll={currentAutoScroll}\n onAutoScrollChange={handleAutoScrollChange}\n />\n </Box>\n <StatusBar\n focusMode={focusMode}\n processName={selectedName}\n showShiftTabHint={showShiftTabHint}\n />\n <HelpPopup visible={showHelp} />\n </Box>\n );\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { ProcessManager, type ManagedProcess } from '../process-manager';\nimport type { PanexConfig, ProcessStatus } from '../types';\n\nexport interface UseProcessManagerResult {\n processManager: ProcessManager;\n processes: Map<string, ManagedProcess>;\n names: string[];\n getOutput: (name: string) => string;\n getStatus: (name: string) => ProcessStatus;\n restart: (name: string) => void;\n restartAll: () => void;\n kill: (name: string) => void;\n killAll: () => void;\n write: (name: string, data: string) => void;\n resize: (name: string, cols: number, rows: number) => void;\n}\n\nexport function useProcessManager(config: PanexConfig): UseProcessManagerResult {\n const [, forceUpdate] = useState({});\n const processManagerRef = useRef<ProcessManager | null>(null);\n\n if (!processManagerRef.current) {\n processManagerRef.current = new ProcessManager(config.procs);\n }\n\n const pm = processManagerRef.current;\n\n useEffect(() => {\n const update = () => forceUpdate({});\n pm.on('output', update);\n pm.on('started', update);\n pm.on('exit', update);\n pm.on('error', update);\n\n pm.startAll();\n\n return () => {\n pm.removeAllListeners();\n pm.killAll();\n };\n }, [pm]);\n\n const getOutput = useCallback((name: string) => pm.getOutput(name), [pm]);\n\n const getStatus = useCallback((name: string): ProcessStatus => {\n const proc = pm.getProcess(name);\n return proc?.status ?? 'stopped';\n }, [pm]);\n\n const restart = useCallback((name: string) => pm.restart(name), [pm]);\n const restartAll = useCallback(() => pm.restartAll(), [pm]);\n const kill = useCallback((name: string) => pm.kill(name), [pm]);\n const killAll = useCallback(() => pm.killAll(), [pm]);\n const write = useCallback((name: string, data: string) => pm.write(name, data), [pm]);\n const resize = useCallback((name: string, cols: number, rows: number) => pm.resize(name, cols, rows), [pm]);\n\n return {\n processManager: pm,\n processes: new Map(pm.getNames().map(n => [n, pm.getProcess(n)!])),\n names: pm.getNames(),\n getOutput,\n getStatus,\n restart,\n restartAll,\n kill,\n killAll,\n write,\n resize,\n };\n}\n","import { EventEmitter } from 'events';\nimport type { ProcessConfig } from './types';\nimport { TerminalBuffer } from './terminal-buffer';\n\ninterface PtyHandle {\n write(data: string): void;\n resize(cols: number, rows: number): void;\n kill(): void;\n}\n\nexport interface ManagedProcess {\n name: string;\n config: ProcessConfig;\n pty: PtyHandle | null;\n status: 'running' | 'stopped' | 'error';\n terminalBuffer: TerminalBuffer;\n exitCode: number | null;\n}\n\nexport class ProcessManager extends EventEmitter {\n private processes: Map<string, ManagedProcess> = new Map();\n\n constructor(private procs: Record<string, ProcessConfig>) {\n super();\n }\n\n async startAll(): Promise<void> {\n for (const [name, config] of Object.entries(this.procs)) {\n await this.start(name, config);\n }\n }\n\n async start(name: string, config: ProcessConfig): Promise<void> {\n const existing = this.processes.get(name);\n if (existing?.pty) {\n existing.pty.kill();\n }\n\n const shell = process.platform === 'win32' ? 'powershell.exe' : 'bash';\n const args = config.shell\n ? ['-c', config.shell]\n : config.cmd\n ? ['-c', config.cmd.join(' ')]\n : [];\n\n const cwd = config.cwd ?? process.cwd();\n const env = { ...process.env, ...config.env };\n\n const managed: ManagedProcess = {\n name,\n config,\n pty: null,\n status: 'running',\n terminalBuffer: new TerminalBuffer(120, 30),\n exitCode: null,\n };\n\n this.processes.set(name, managed);\n\n try {\n const proc = Bun.spawn([shell, ...args], {\n cwd,\n env: env as Record<string, string>,\n terminal: {\n cols: 120,\n rows: 30,\n data: (_terminal: unknown, data: Uint8Array) => {\n const str = new TextDecoder().decode(data);\n managed.terminalBuffer.write(str);\n this.emit('output', name, str);\n },\n },\n });\n\n managed.pty = {\n write: (data: string) => proc.terminal?.write(data),\n resize: (cols: number, rows: number) => proc.terminal?.resize(cols, rows),\n kill: () => proc.kill(),\n };\n\n // Handle exit\n proc.exited.then((exitCode) => {\n managed.status = exitCode === 0 ? 'stopped' : 'error';\n managed.exitCode = exitCode;\n managed.pty = null;\n this.emit('exit', name, exitCode);\n\n if (managed.config.autoRestart && exitCode !== 0) {\n setTimeout(() => this.start(name, managed.config), 1000);\n }\n });\n\n this.emit('started', name);\n } catch (error) {\n managed.status = 'error';\n managed.terminalBuffer.write(`Error starting process: ${error}`);\n managed.exitCode = -1;\n this.emit('error', name, error);\n }\n }\n\n restart(name: string): void {\n const proc = this.processes.get(name);\n if (proc) {\n if (proc.pty) {\n proc.pty.kill();\n }\n proc.terminalBuffer.clear();\n this.start(name, proc.config);\n }\n }\n\n restartAll(): void {\n for (const name of this.processes.keys()) {\n this.restart(name);\n }\n }\n\n kill(name: string): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.kill();\n }\n }\n\n killAll(): void {\n for (const proc of this.processes.values()) {\n if (proc.pty) {\n proc.pty.kill();\n }\n }\n }\n\n write(name: string, data: string): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.write(data);\n }\n }\n\n resize(name: string, cols: number, rows: number): void {\n const proc = this.processes.get(name);\n if (proc?.pty) {\n proc.pty.resize(cols, rows);\n }\n }\n\n getProcess(name: string): ManagedProcess | undefined {\n return this.processes.get(name);\n }\n\n getProcesses(): ManagedProcess[] {\n return Array.from(this.processes.values());\n }\n\n getNames(): string[] {\n return Array.from(this.processes.keys());\n }\n\n getOutput(name: string): string {\n return this.processes.get(name)?.terminalBuffer.toString() ?? '';\n }\n}\n","import { Terminal } from '@xterm/headless';\nimport { SerializeAddon } from '@xterm/addon-serialize';\n\n/**\n * A terminal buffer that properly interprets ANSI escape sequences including:\n * - Cursor movement (\\x1b[A up, \\x1b[B down, \\x1b[C right, \\x1b[D left)\n * - Cursor positioning (\\x1b[H, \\x1b[row;colH)\n * - Line clearing (\\x1b[K erase to end, \\x1b[2K erase line)\n * - Screen clearing (\\x1b[2J clear screen)\n * - Carriage return (\\r) for in-place updates like progress bars\n * - ANSI colors and formatting (preserved in output via SerializeAddon)\n */\nexport class TerminalBuffer {\n private terminal: Terminal;\n private serializeAddon: SerializeAddon;\n private rows: number;\n private cols: number;\n\n constructor(cols = 200, rows = 500) {\n this.rows = rows;\n this.cols = cols;\n this.terminal = new Terminal({\n cols,\n rows,\n scrollback: 10000,\n allowProposedApi: true,\n });\n this.serializeAddon = new SerializeAddon();\n this.terminal.loadAddon(this.serializeAddon);\n }\n\n /**\n * Write data to the terminal buffer.\n * The terminal will interpret all ANSI escape sequences.\n */\n write(data: string): void {\n this.terminal.write(data);\n }\n\n /**\n * Get the current terminal buffer content as an array of lines.\n * Only returns lines that have content (not all 500 rows).\n */\n getLines(): string[] {\n const buffer = this.terminal.buffer.active;\n const lines: string[] = [];\n\n // Get actual content length (baseY is lines scrolled off + cursorY + 1)\n const contentLength = buffer.baseY + buffer.cursorY + 1;\n\n for (let i = 0; i < contentLength; i++) {\n const line = buffer.getLine(i);\n if (line) {\n lines.push(line.translateToString(true)); // trim trailing whitespace\n }\n }\n\n // Remove trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n\n return lines;\n }\n\n /**\n * Get the terminal content as a single string with newlines.\n * Preserves ANSI color codes and formatting.\n */\n toString(): string {\n // Use serialize addon to get output with ANSI codes preserved\n return this.serializeAddon.serialize();\n }\n\n /**\n * Clear the terminal buffer.\n */\n clear(): void {\n this.terminal.reset();\n }\n\n /**\n * Resize the terminal.\n */\n resize(cols: number, rows: number): void {\n this.cols = cols;\n this.rows = rows;\n this.terminal.resize(cols, rows);\n }\n\n /**\n * Dispose of the terminal to free resources.\n */\n dispose(): void {\n this.terminal.dispose();\n }\n}\n","import { useState, useCallback } from 'react';\n\nexport interface UseFocusModeResult {\n focusMode: boolean;\n enterFocus: () => void;\n exitFocus: () => void;\n toggleFocus: () => void;\n}\n\nexport function useFocusMode(): UseFocusModeResult {\n const [focusMode, setFocusMode] = useState(false);\n\n const enterFocus = useCallback(() => setFocusMode(true), []);\n const exitFocus = useCallback(() => setFocusMode(false), []);\n const toggleFocus = useCallback(() => setFocusMode(f => !f), []);\n\n return { focusMode, enterFocus, exitFocus, toggleFocus };\n}\n","import { useEffect, useCallback } from 'react';\nimport { useStdin, useStdout } from 'ink';\n\ninterface MouseWheelEvent {\n type: 'wheel-up' | 'wheel-down';\n x: number;\n y: number;\n}\n\ninterface MouseClickEvent {\n type: 'click';\n x: number;\n y: number;\n}\n\ninterface UseMouseWheelOptions {\n enabled?: boolean;\n onWheel?: (event: MouseWheelEvent) => void;\n onClick?: (event: MouseClickEvent) => void;\n}\n\n/**\n * Hook to enable mouse tracking in the terminal.\n *\n * Uses ANSI escape sequences for SGR extended mouse mode:\n * - \\x1b[?1000h - Enable mouse button tracking\n * - \\x1b[?1006h - Enable SGR extended mouse mode\n *\n * Mouse events (SGR mode):\n * - Left click: \\x1b[<0;X;YM (button 0 = left click press)\n * - Scroll up: \\x1b[<64;X;YM (button 64 = wheel up)\n * - Scroll down: \\x1b[<65;X;YM (button 65 = wheel down)\n */\nexport function useMouseWheel({ enabled = true, onWheel, onClick }: UseMouseWheelOptions = {}) {\n const { stdin, setRawMode } = useStdin();\n const { stdout } = useStdout();\n\n const handleData = useCallback((data: Buffer) => {\n const str = data.toString();\n\n // Parse SGR mouse events: \\x1b[<button;x;yM or \\x1b[<button;x;ym\n // Button 64 = wheel up, Button 65 = wheel down\n const sgrRegex = /\\x1b\\[<(\\d+);(\\d+);(\\d+)([Mm])/g;\n let match;\n\n while ((match = sgrRegex.exec(str)) !== null) {\n const button = parseInt(match[1] ?? '0', 10);\n const x = parseInt(match[2] ?? '0', 10);\n const y = parseInt(match[3] ?? '0', 10);\n // M = press, m = release (we only care about press for wheel)\n const isPress = match[4] === 'M';\n\n if (isPress) {\n if (button === 0) {\n onClick?.({ type: 'click', x, y });\n } else if (button === 64) {\n onWheel?.({ type: 'wheel-up', x, y });\n } else if (button === 65) {\n onWheel?.({ type: 'wheel-down', x, y });\n }\n }\n }\n }, [onWheel, onClick]);\n\n useEffect(() => {\n if (!enabled || !stdin || !stdout) return;\n\n // Enable mouse tracking\n // 1000h: X11 mouse button tracking\n // 1006h: SGR extended mouse mode (for proper coordinates)\n stdout.write('\\x1b[?1000h\\x1b[?1006h');\n\n // Ensure raw mode is enabled\n setRawMode?.(true);\n\n // Listen for mouse events\n stdin.on('data', handleData);\n\n return () => {\n stdin.off('data', handleData);\n // Disable mouse tracking on cleanup\n stdout.write('\\x1b[?1000l\\x1b[?1006l');\n };\n }, [enabled, stdin, stdout, setRawMode, handleData]);\n}\n","import { Box, Text, useStdout } from 'ink';\nimport { ScrollList, ScrollListRef } from 'ink-scroll-list';\nimport { forwardRef, useImperativeHandle, useRef, useEffect } from 'react';\nimport type { ProcessStatus } from '../types';\n\nexport const PROCESS_LIST_WIDTH = 20;\n\ninterface ProcessListProps {\n names: string[];\n selected: number;\n getStatus: (name: string) => ProcessStatus;\n active: boolean;\n height?: number;\n}\n\nexport interface ProcessListRef {\n scrollBy: (delta: number) => void;\n scrollToTop: () => void;\n scrollToBottom: () => void;\n}\n\nexport const ProcessList = forwardRef<ProcessListRef, ProcessListProps>(\n function ProcessList({ names, selected, getStatus, active, height }, ref) {\n const borderStyle = active ? 'double' : 'single';\n const listRef = useRef<ScrollListRef>(null);\n const { stdout } = useStdout();\n\n // Handle terminal resize\n useEffect(() => {\n const handleResize = () => listRef.current?.remeasure();\n stdout?.on('resize', handleResize);\n return () => {\n stdout?.off('resize', handleResize);\n };\n }, [stdout]);\n\n // Expose scroll methods via ref\n useImperativeHandle(ref, () => ({\n scrollBy: (delta: number) => listRef.current?.scrollBy(delta),\n scrollToTop: () => listRef.current?.scrollToTop(),\n scrollToBottom: () => listRef.current?.scrollToBottom(),\n }));\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle={borderStyle}\n borderColor={active ? 'blue' : 'gray'}\n width={PROCESS_LIST_WIDTH}\n height={height}\n paddingX={1}\n >\n <Box flexDirection=\"column\" marginTop={0} height={height ? height - 2 : undefined}>\n <ScrollList\n ref={listRef}\n selectedIndex={selected}\n scrollAlignment=\"auto\"\n >\n {names.map((name, i) => {\n const status = getStatus(name);\n const statusIcon = status === 'running' ? '●' : status === 'error' ? '✗' : '○';\n const statusColor = status === 'running' ? 'green' : status === 'error' ? 'red' : 'gray';\n const isSelected = i === selected;\n\n return (\n <Box key={name} backgroundColor={isSelected ? 'blue' : undefined}>\n <Text color={isSelected ? 'black' : undefined}>\n {name}{' '}\n </Text>\n <Text color={statusColor}>{statusIcon}</Text>\n </Box>\n );\n })}\n </ScrollList>\n </Box>\n </Box>\n );\n }\n);\n","import { Box, Text, useStdout } from 'ink';\nimport { ScrollView, ScrollViewRef } from 'ink-scroll-view';\nimport { forwardRef, useImperativeHandle, useRef, useEffect, useState, useCallback } from 'react';\nimport { Scrollbar } from './Scrollbar';\n\ninterface OutputPanelProps {\n name: string;\n output: string;\n active: boolean;\n height?: number;\n autoScroll?: boolean;\n onAutoScrollChange?: (enabled: boolean) => void;\n}\n\nexport interface OutputPanelRef {\n scrollBy: (delta: number) => void;\n scrollToTop: () => void;\n scrollToBottom: () => void;\n getScrollOffset: () => number;\n getContentHeight: () => number;\n getViewportHeight: () => number;\n isAtBottom: () => boolean;\n}\n\nexport const OutputPanel = forwardRef<OutputPanelRef, OutputPanelProps>(\n function OutputPanel({ name, output, active, height, autoScroll = true, onAutoScrollChange }, ref) {\n const borderStyle = active ? 'double' : 'single';\n const lines = output.split('\\n');\n const scrollRef = useRef<ScrollViewRef>(null);\n const { stdout } = useStdout();\n\n // Track scroll state for scrollbar\n const [scrollOffset, setScrollOffset] = useState(0);\n const [contentHeight, setContentHeight] = useState(0);\n const [viewportHeight, setViewportHeight] = useState(0);\n\n // Reset state synchronously when process changes (before render)\n const [prevName, setPrevName] = useState(name);\n if (name !== prevName) {\n setPrevName(name);\n setScrollOffset(0);\n setContentHeight(0);\n setViewportHeight(0);\n }\n\n // Handle terminal resize\n useEffect(() => {\n const handleResize = () => scrollRef.current?.remeasure();\n stdout?.on('resize', handleResize);\n return () => {\n stdout?.off('resize', handleResize);\n };\n }, [stdout]);\n\n // Check if at bottom with small tolerance\n const isAtBottom = useCallback(() => {\n if (!scrollRef.current) return true;\n const offset = scrollRef.current.getScrollOffset();\n const bottom = scrollRef.current.getBottomOffset();\n // Allow 1 line tolerance for rounding issues\n return offset >= bottom - 1;\n }, []);\n\n // Auto-scroll when content height changes (if enabled)\n const handleContentHeightChange = useCallback((newHeight: number) => {\n setContentHeight(newHeight);\n if (autoScroll && scrollRef.current) {\n // Use setTimeout to ensure layout is complete\n setTimeout(() => {\n scrollRef.current?.scrollToBottom();\n }, 0);\n }\n }, [autoScroll]);\n\n // Track scroll and update auto-scroll state\n const handleScroll = useCallback((offset: number) => {\n setScrollOffset(offset);\n\n // If user manually scrolled away from bottom, disable auto-scroll\n if (scrollRef.current) {\n const bottom = scrollRef.current.getBottomOffset();\n const atBottom = offset >= bottom - 1;\n\n if (!atBottom && autoScroll) {\n onAutoScrollChange?.(false);\n } else if (atBottom && !autoScroll) {\n onAutoScrollChange?.(true);\n }\n }\n }, [autoScroll, onAutoScrollChange]);\n\n // Expose scroll methods via ref\n useImperativeHandle(ref, () => ({\n scrollBy: (delta: number) => scrollRef.current?.scrollBy(delta),\n scrollToTop: () => scrollRef.current?.scrollToTop(),\n scrollToBottom: () => scrollRef.current?.scrollToBottom(),\n getScrollOffset: () => scrollRef.current?.getScrollOffset() ?? 0,\n getContentHeight: () => scrollRef.current?.getContentHeight() ?? 0,\n getViewportHeight: () => scrollRef.current?.getViewportHeight() ?? 0,\n isAtBottom,\n }));\n\n // Scrollbar height (same as content area)\n const scrollbarHeight = height ? height - 4 : 20;\n const hasScroll = contentHeight > viewportHeight;\n\n // Show pin indicator when auto-scroll is disabled (user scrolled up)\n const pinIndicator = !autoScroll && hasScroll ? ' ⍗' : '';\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle={borderStyle}\n borderColor={active ? 'green' : 'gray'}\n flexGrow={1}\n height={height}\n paddingLeft={1}\n >\n <Box flexDirection=\"row\" marginTop={0} height={height ? height - 2 : undefined}>\n <Box flexDirection=\"column\" flexGrow={1}>\n <ScrollView\n key={name}\n ref={scrollRef}\n onScroll={handleScroll}\n onContentHeightChange={handleContentHeightChange}\n onViewportSizeChange={(layout) => setViewportHeight(layout.height)}\n >\n {lines.map((line, i) => (\n <Text key={i} wrap=\"truncate\">{line}</Text>\n ))}\n </ScrollView>\n </Box>\n {hasScroll && (\n <Scrollbar\n scrollOffset={scrollOffset}\n contentHeight={contentHeight}\n viewportHeight={viewportHeight}\n height={scrollbarHeight}\n />\n )}\n </Box>\n </Box>\n );\n }\n);\n","import { Box, Text } from 'ink';\n\ninterface ScrollbarProps {\n /** Current scroll offset */\n scrollOffset: number;\n /** Total content height */\n contentHeight: number;\n /** Visible viewport height */\n viewportHeight: number;\n /** Height of the scrollbar track (usually same as viewport) */\n height: number;\n}\n\n/**\n * A simple vertical scrollbar component.\n * Uses Unicode block characters to show scroll position.\n */\nexport function Scrollbar({\n scrollOffset,\n contentHeight,\n viewportHeight,\n height,\n}: ScrollbarProps) {\n // Don't show scrollbar if content fits in viewport\n if (contentHeight <= viewportHeight) {\n return (\n <Box flexDirection=\"column\" width={1}>\n {Array.from({ length: height }).map((_, i) => (\n <Text key={i} dimColor> </Text>\n ))}\n </Box>\n );\n }\n\n // Calculate thumb size and position\n const trackHeight = height;\n const thumbRatio = viewportHeight / contentHeight;\n const thumbHeight = Math.max(1, Math.round(trackHeight * thumbRatio));\n\n const maxScroll = contentHeight - viewportHeight;\n const scrollRatio = maxScroll > 0 ? scrollOffset / maxScroll : 0;\n const thumbPosition = Math.round((trackHeight - thumbHeight) * scrollRatio);\n\n // Build the scrollbar\n const lines: string[] = [];\n for (let i = 0; i < trackHeight; i++) {\n if (i >= thumbPosition && i < thumbPosition + thumbHeight) {\n lines.push('█'); // Thumb\n } else {\n lines.push('░'); // Track\n }\n }\n\n return (\n <Box flexDirection=\"column\" width={1}>\n {lines.map((char, i) => (\n <Text key={i} dimColor={char === '░'}>{char}</Text>\n ))}\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\n\ninterface StatusBarProps {\n focusMode: boolean;\n processName?: string;\n showShiftTabHint?: boolean;\n}\n\nexport function StatusBar({ focusMode, processName, showShiftTabHint = true }: StatusBarProps) {\n if (focusMode && processName) {\n const shiftTabHint = showShiftTabHint ? 'Shift-Tab/' : '';\n return (\n <Box backgroundColor=\"green\" width=\"100%\">\n <Text bold color=\"black\" backgroundColor=\"green\">\n {' '}{processName} | [{shiftTabHint}Esc] to exit focus mode{' '}\n </Text>\n </Box>\n );\n }\n\n return (\n <Box backgroundColor=\"blue\" width=\"100%\">\n <Text bold color=\"black\" backgroundColor=\"blue\">\n {' '}[↑↓/jk] select [Tab/Enter] focus [r] restart [A] restart All [x] kill [q] quit [?] help{' '}\n </Text>\n </Box>\n );\n}\n","import { Box, Text } from 'ink';\n\ninterface HelpPopupProps {\n visible: boolean;\n}\n\nexport function HelpPopup({ visible }: HelpPopupProps) {\n if (!visible) return null;\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"yellow\"\n padding={1}\n position=\"absolute\"\n marginLeft={10}\n marginTop={5}\n >\n <Text bold color=\"yellow\"> Help </Text>\n <Text>{'\\n'}Keyboard Shortcuts</Text>\n <Text>{'─'.repeat(18)}</Text>\n <Text>{'\\n'}Navigation</Text>\n <Text> ↑/↓ or j/k Navigate process list</Text>\n <Text> g/G Scroll to top/bottom of output</Text>\n <Text> PgUp/PgDn Scroll output</Text>\n <Text>{'\\n'}Process Control</Text>\n <Text> Tab/Enter Focus process (interactive mode)</Text>\n <Text> Esc Exit focus mode</Text>\n <Text> r Restart selected process</Text>\n <Text> A Restart all processes</Text>\n <Text> x Kill selected process</Text>\n <Text>{'\\n'}General</Text>\n <Text> ? Toggle this help</Text>\n <Text> q Quit panex</Text>\n <Text>{'\\n'}Press any key to close this help...</Text>\n </Box>\n );\n}\n"],"mappings":"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","names":["useState","useEffect","useRef","useCallback","Box","useStdin","useStdout","useState","useCallback","useEffect","useCallback","useStdout","useRef","useEffect","ProcessList","Box","Text","useStdout","forwardRef","useImperativeHandle","useRef","useEffect","useState","useCallback","Box","Text","jsx","jsx","jsxs","forwardRef","OutputPanel","useRef","useStdout","useState","useEffect","useCallback","useImperativeHandle","Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","jsx","jsxs","useStdout","useStdin","useState","useRef","useEffect","useCallback","Box"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "panex",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"description": "Terminal UI for running multiple processes in parallel",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"bun": ">=1.3.5"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
+
"@xterm/addon-serialize": "^0.14.0",
|
|
42
43
|
"@xterm/headless": "^6.0.0",
|
|
43
44
|
"chalk": "^5.3.0",
|
|
44
45
|
"commander": "^12.1.0",
|