panex 0.9.4 → 0.9.5

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/README.md CHANGED
@@ -108,4 +108,4 @@ node dist/cli.js "echo test"
108
108
 
109
109
  ## License
110
110
 
111
- MIT
111
+ MIT Anton Veretennikov (king8fisher)
package/dist/cli.js CHANGED
@@ -16,13 +16,84 @@ import { useState, useEffect, useCallback, useRef } from "react";
16
16
 
17
17
  // src/process-manager.ts
18
18
  import { EventEmitter } from "events";
19
+
20
+ // src/terminal-buffer.ts
21
+ import { Terminal } from "@xterm/headless";
22
+ var TerminalBuffer = class {
23
+ terminal;
24
+ rows;
25
+ cols;
26
+ constructor(cols = 200, rows = 500) {
27
+ this.rows = rows;
28
+ this.cols = cols;
29
+ this.terminal = new Terminal({
30
+ cols,
31
+ rows,
32
+ scrollback: 1e4,
33
+ allowProposedApi: true
34
+ });
35
+ }
36
+ /**
37
+ * Write data to the terminal buffer.
38
+ * The terminal will interpret all ANSI escape sequences.
39
+ */
40
+ write(data) {
41
+ this.terminal.write(data);
42
+ }
43
+ /**
44
+ * Get the current terminal buffer content as an array of lines.
45
+ * Only returns lines that have content (not all 500 rows).
46
+ */
47
+ getLines() {
48
+ const buffer = this.terminal.buffer.active;
49
+ const lines = [];
50
+ const contentLength = buffer.baseY + buffer.cursorY + 1;
51
+ for (let i = 0; i < contentLength; i++) {
52
+ const line = buffer.getLine(i);
53
+ if (line) {
54
+ lines.push(line.translateToString(true));
55
+ }
56
+ }
57
+ while (lines.length > 0 && lines[lines.length - 1] === "") {
58
+ lines.pop();
59
+ }
60
+ return lines;
61
+ }
62
+ /**
63
+ * Get the terminal content as a single string with newlines.
64
+ */
65
+ toString() {
66
+ return this.getLines().join("\n");
67
+ }
68
+ /**
69
+ * Clear the terminal buffer.
70
+ */
71
+ clear() {
72
+ this.terminal.reset();
73
+ }
74
+ /**
75
+ * Resize the terminal.
76
+ */
77
+ resize(cols, rows) {
78
+ this.cols = cols;
79
+ this.rows = rows;
80
+ this.terminal.resize(cols, rows);
81
+ }
82
+ /**
83
+ * Dispose of the terminal to free resources.
84
+ */
85
+ dispose() {
86
+ this.terminal.dispose();
87
+ }
88
+ };
89
+
90
+ // src/process-manager.ts
19
91
  var ProcessManager = class extends EventEmitter {
20
92
  constructor(procs) {
21
93
  super();
22
94
  this.procs = procs;
23
95
  }
24
96
  processes = /* @__PURE__ */ new Map();
25
- maxOutputLines = 1e4;
26
97
  async startAll() {
27
98
  for (const [name, config] of Object.entries(this.procs)) {
28
99
  await this.start(name, config);
@@ -42,7 +113,7 @@ var ProcessManager = class extends EventEmitter {
42
113
  config,
43
114
  pty: null,
44
115
  status: "running",
45
- output: [],
116
+ terminalBuffer: new TerminalBuffer(120, 30),
46
117
  exitCode: null
47
118
  };
48
119
  this.processes.set(name, managed);
@@ -55,10 +126,7 @@ var ProcessManager = class extends EventEmitter {
55
126
  rows: 30,
56
127
  data: (_terminal, data) => {
57
128
  const str = new TextDecoder().decode(data);
58
- managed.output.push(str);
59
- if (managed.output.length > this.maxOutputLines) {
60
- managed.output = managed.output.slice(-this.maxOutputLines);
61
- }
129
+ managed.terminalBuffer.write(str);
62
130
  this.emit("output", name, str);
63
131
  }
64
132
  }
@@ -80,7 +148,7 @@ var ProcessManager = class extends EventEmitter {
80
148
  this.emit("started", name);
81
149
  } catch (error) {
82
150
  managed.status = "error";
83
- managed.output = [`Error starting process: ${error}`];
151
+ managed.terminalBuffer.write(`Error starting process: ${error}`);
84
152
  managed.exitCode = -1;
85
153
  this.emit("error", name, error);
86
154
  }
@@ -91,7 +159,7 @@ var ProcessManager = class extends EventEmitter {
91
159
  if (proc.pty) {
92
160
  proc.pty.kill();
93
161
  }
94
- proc.output = [];
162
+ proc.terminalBuffer.clear();
95
163
  this.start(name, proc.config);
96
164
  }
97
165
  }
@@ -135,7 +203,7 @@ var ProcessManager = class extends EventEmitter {
135
203
  return Array.from(this.processes.keys());
136
204
  }
137
205
  getOutput(name) {
138
- return this.processes.get(name)?.output.join("") ?? "";
206
+ return this.processes.get(name)?.terminalBuffer.toString() ?? "";
139
207
  }
140
208
  };
141
209
 
@@ -198,7 +266,7 @@ function useFocusMode() {
198
266
  // src/hooks/useMouseWheel.ts
199
267
  import { useEffect as useEffect2, useCallback as useCallback3 } from "react";
200
268
  import { useStdin, useStdout } from "ink";
201
- function useMouseWheel({ enabled = true, onWheel } = {}) {
269
+ function useMouseWheel({ enabled = true, onWheel, onClick } = {}) {
202
270
  const { stdin, setRawMode } = useStdin();
203
271
  const { stdout } = useStdout();
204
272
  const handleData = useCallback3((data) => {
@@ -211,14 +279,16 @@ function useMouseWheel({ enabled = true, onWheel } = {}) {
211
279
  const y = parseInt(match[3] ?? "0", 10);
212
280
  const isPress = match[4] === "M";
213
281
  if (isPress) {
214
- if (button === 64) {
282
+ if (button === 0) {
283
+ onClick?.({ type: "click", x, y });
284
+ } else if (button === 64) {
215
285
  onWheel?.({ type: "wheel-up", x, y });
216
286
  } else if (button === 65) {
217
287
  onWheel?.({ type: "wheel-down", x, y });
218
288
  }
219
289
  }
220
290
  }
221
- }, [onWheel]);
291
+ }, [onWheel, onClick]);
222
292
  useEffect2(() => {
223
293
  if (!enabled || !stdin || !stdout) return;
224
294
  stdout.write("\x1B[?1000h\x1B[?1006h");
@@ -236,6 +306,7 @@ import { Box, Text, useStdout as useStdout2 } from "ink";
236
306
  import { ScrollList } from "ink-scroll-list";
237
307
  import { forwardRef, useImperativeHandle, useRef as useRef2, useEffect as useEffect3 } from "react";
238
308
  import { jsx, jsxs } from "react/jsx-runtime";
309
+ var PROCESS_LIST_WIDTH = 20;
239
310
  var ProcessList = forwardRef(
240
311
  function ProcessList2({ names, selected, getStatus, active, height }, ref) {
241
312
  const borderStyle = active ? "double" : "single";
@@ -259,7 +330,7 @@ var ProcessList = forwardRef(
259
330
  flexDirection: "column",
260
331
  borderStyle,
261
332
  borderColor: active ? "blue" : "gray",
262
- width: 20,
333
+ width: PROCESS_LIST_WIDTH,
263
334
  height,
264
335
  paddingX: 1,
265
336
  children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, height: height ? height - 2 : void 0, children: /* @__PURE__ */ jsx(
@@ -278,7 +349,7 @@ var ProcessList = forwardRef(
278
349
  name,
279
350
  " "
280
351
  ] }),
281
- /* @__PURE__ */ jsx(Text, { color: isSelected ? "black" : statusColor, children: statusIcon })
352
+ /* @__PURE__ */ jsx(Text, { color: statusColor, children: statusIcon })
282
353
  ] }, name);
283
354
  })
284
355
  }
@@ -421,9 +492,8 @@ function StatusBar({ focusMode, processName, showShiftTabHint = true }) {
421
492
  const shiftTabHint = showShiftTabHint ? "Shift-Tab/" : "";
422
493
  return /* @__PURE__ */ jsx4(Box4, { backgroundColor: "green", width: "100%", children: /* @__PURE__ */ jsxs3(Text4, { bold: true, color: "black", backgroundColor: "green", children: [
423
494
  " ",
424
- "FOCUS: ",
425
495
  processName,
426
- " - Type to interact, [",
496
+ " | [",
427
497
  shiftTabHint,
428
498
  "Esc] to exit focus mode",
429
499
  " "
@@ -557,17 +627,33 @@ function App({ config }) {
557
627
  }
558
628
  }
559
629
  }, [selectedName]);
630
+ const handleClick = useCallback5((event) => {
631
+ if (event.x <= PROCESS_LIST_WIDTH) {
632
+ if (focusMode) {
633
+ exitFocus();
634
+ }
635
+ const clickedIndex = event.y - 2;
636
+ if (clickedIndex >= 0 && clickedIndex < names.length) {
637
+ setSelected(clickedIndex);
638
+ }
639
+ } else {
640
+ if (!focusMode) {
641
+ enterFocus();
642
+ }
643
+ }
644
+ }, [names.length, focusMode, enterFocus, exitFocus]);
560
645
  useMouseWheel({
561
646
  enabled: !showHelp,
562
647
  // Disable when help is shown
563
- onWheel: handleWheel
648
+ onWheel: handleWheel,
649
+ onClick: handleClick
564
650
  });
565
651
  useInput((input, key) => {
566
652
  if (showHelp) {
567
653
  setShowHelp(false);
568
654
  return;
569
655
  }
570
- if (input === "q" || key.ctrl && input === "c") {
656
+ if (key.ctrl && input === "c") {
571
657
  killAll();
572
658
  setRawMode(false);
573
659
  const rows = stdout?.rows ?? 999;
@@ -576,10 +662,6 @@ function App({ config }) {
576
662
  exit();
577
663
  process.exit(0);
578
664
  }
579
- if (input === "?") {
580
- setShowHelp(true);
581
- return;
582
- }
583
665
  if (focusMode) {
584
666
  const name = names[selected];
585
667
  if (!name) return;
@@ -612,10 +694,26 @@ function App({ config }) {
612
694
  return;
613
695
  }
614
696
  if (input && !key.ctrl && !key.meta) {
615
- write(name, input);
697
+ const filtered = input.replace(/\x1b?\[<\d+;\d+;\d+[Mm]/g, "");
698
+ if (filtered) {
699
+ write(name, filtered);
700
+ }
616
701
  }
617
702
  return;
618
703
  }
704
+ if (input === "q") {
705
+ killAll();
706
+ setRawMode(false);
707
+ const rows = stdout?.rows ?? 999;
708
+ stdout?.write(`\x1B[${rows};1H\x1B[J\x1B[?1000l\x1B[?1006l\x1B[?25h\x1B[0m
709
+ `);
710
+ exit();
711
+ process.exit(0);
712
+ }
713
+ if (input === "?") {
714
+ setShowHelp(true);
715
+ return;
716
+ }
619
717
  if (key.upArrow || input === "k") {
620
718
  setSelected((s) => Math.max(s - 1, 0));
621
719
  return;
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/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 } 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 // Enable mouse wheel tracking\n useMouseWheel({\n enabled: !showHelp, // Disable when help is shown\n onWheel: handleWheel,\n });\n\n useInput((input, key) => {\n // Handle help popup\n if (showHelp) {\n setShowHelp(false);\n return;\n }\n\n // Quit\n if (input === 'q' || (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 // Help\n if (input === '?') {\n setShowHelp(true);\n return;\n }\n\n // Focus mode input handling\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\n if (input && !key.ctrl && !key.meta) {\n write(name, input);\n }\n return;\n }\n\n // Normal mode\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';\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 output: string[];\n exitCode: number | null;\n}\n\nexport class ProcessManager extends EventEmitter {\n private processes: Map<string, ManagedProcess> = new Map();\n private maxOutputLines = 10000;\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 output: [],\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.output.push(str);\n if (managed.output.length > this.maxOutputLines) {\n managed.output = managed.output.slice(-this.maxOutputLines);\n }\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.output = [`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.output = [];\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)?.output.join('') ?? '';\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 UseMouseWheelOptions {\n enabled?: boolean;\n onWheel?: (event: MouseWheelEvent) => void;\n}\n\n/**\n * Hook to enable mouse wheel 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 wheel events (SGR mode):\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 }: 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 === 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]);\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\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={20}\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={isSelected ? 'black' : 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 {' '}FOCUS: {processName} - Type to interact, [{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 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"]}
package/dist/index.d.ts CHANGED
@@ -18,6 +18,47 @@ interface PanexConfig {
18
18
 
19
19
  declare function createTUI(config: PanexConfig): Promise<void>;
20
20
 
21
+ /**
22
+ * A terminal buffer that properly interprets ANSI escape sequences including:
23
+ * - Cursor movement (\x1b[A up, \x1b[B down, \x1b[C right, \x1b[D left)
24
+ * - Cursor positioning (\x1b[H, \x1b[row;colH)
25
+ * - Line clearing (\x1b[K erase to end, \x1b[2K erase line)
26
+ * - Screen clearing (\x1b[2J clear screen)
27
+ * - Carriage return (\r) for in-place updates like progress bars
28
+ */
29
+ declare class TerminalBuffer {
30
+ private terminal;
31
+ private rows;
32
+ private cols;
33
+ constructor(cols?: number, rows?: number);
34
+ /**
35
+ * Write data to the terminal buffer.
36
+ * The terminal will interpret all ANSI escape sequences.
37
+ */
38
+ write(data: string): void;
39
+ /**
40
+ * Get the current terminal buffer content as an array of lines.
41
+ * Only returns lines that have content (not all 500 rows).
42
+ */
43
+ getLines(): string[];
44
+ /**
45
+ * Get the terminal content as a single string with newlines.
46
+ */
47
+ toString(): string;
48
+ /**
49
+ * Clear the terminal buffer.
50
+ */
51
+ clear(): void;
52
+ /**
53
+ * Resize the terminal.
54
+ */
55
+ resize(cols: number, rows: number): void;
56
+ /**
57
+ * Dispose of the terminal to free resources.
58
+ */
59
+ dispose(): void;
60
+ }
61
+
21
62
  interface PtyHandle {
22
63
  write(data: string): void;
23
64
  resize(cols: number, rows: number): void;
@@ -28,13 +69,12 @@ interface ManagedProcess {
28
69
  config: ProcessConfig;
29
70
  pty: PtyHandle | null;
30
71
  status: 'running' | 'stopped' | 'error';
31
- output: string[];
72
+ terminalBuffer: TerminalBuffer;
32
73
  exitCode: number | null;
33
74
  }
34
75
  declare class ProcessManager extends EventEmitter {
35
76
  private procs;
36
77
  private processes;
37
- private maxOutputLines;
38
78
  constructor(procs: Record<string, ProcessConfig>);
39
79
  startAll(): Promise<void>;
40
80
  start(name: string, config: ProcessConfig): Promise<void>;
package/dist/index.js CHANGED
@@ -11,13 +11,84 @@ import { useState, useEffect, useCallback, useRef } from "react";
11
11
 
12
12
  // src/process-manager.ts
13
13
  import { EventEmitter } from "events";
14
+
15
+ // src/terminal-buffer.ts
16
+ import { Terminal } from "@xterm/headless";
17
+ var TerminalBuffer = class {
18
+ terminal;
19
+ rows;
20
+ cols;
21
+ constructor(cols = 200, rows = 500) {
22
+ this.rows = rows;
23
+ this.cols = cols;
24
+ this.terminal = new Terminal({
25
+ cols,
26
+ rows,
27
+ scrollback: 1e4,
28
+ allowProposedApi: true
29
+ });
30
+ }
31
+ /**
32
+ * Write data to the terminal buffer.
33
+ * The terminal will interpret all ANSI escape sequences.
34
+ */
35
+ write(data) {
36
+ this.terminal.write(data);
37
+ }
38
+ /**
39
+ * Get the current terminal buffer content as an array of lines.
40
+ * Only returns lines that have content (not all 500 rows).
41
+ */
42
+ getLines() {
43
+ const buffer = this.terminal.buffer.active;
44
+ const lines = [];
45
+ const contentLength = buffer.baseY + buffer.cursorY + 1;
46
+ for (let i = 0; i < contentLength; i++) {
47
+ const line = buffer.getLine(i);
48
+ if (line) {
49
+ lines.push(line.translateToString(true));
50
+ }
51
+ }
52
+ while (lines.length > 0 && lines[lines.length - 1] === "") {
53
+ lines.pop();
54
+ }
55
+ return lines;
56
+ }
57
+ /**
58
+ * Get the terminal content as a single string with newlines.
59
+ */
60
+ toString() {
61
+ return this.getLines().join("\n");
62
+ }
63
+ /**
64
+ * Clear the terminal buffer.
65
+ */
66
+ clear() {
67
+ this.terminal.reset();
68
+ }
69
+ /**
70
+ * Resize the terminal.
71
+ */
72
+ resize(cols, rows) {
73
+ this.cols = cols;
74
+ this.rows = rows;
75
+ this.terminal.resize(cols, rows);
76
+ }
77
+ /**
78
+ * Dispose of the terminal to free resources.
79
+ */
80
+ dispose() {
81
+ this.terminal.dispose();
82
+ }
83
+ };
84
+
85
+ // src/process-manager.ts
14
86
  var ProcessManager = class extends EventEmitter {
15
87
  constructor(procs) {
16
88
  super();
17
89
  this.procs = procs;
18
90
  }
19
91
  processes = /* @__PURE__ */ new Map();
20
- maxOutputLines = 1e4;
21
92
  async startAll() {
22
93
  for (const [name, config] of Object.entries(this.procs)) {
23
94
  await this.start(name, config);
@@ -37,7 +108,7 @@ var ProcessManager = class extends EventEmitter {
37
108
  config,
38
109
  pty: null,
39
110
  status: "running",
40
- output: [],
111
+ terminalBuffer: new TerminalBuffer(120, 30),
41
112
  exitCode: null
42
113
  };
43
114
  this.processes.set(name, managed);
@@ -50,10 +121,7 @@ var ProcessManager = class extends EventEmitter {
50
121
  rows: 30,
51
122
  data: (_terminal, data) => {
52
123
  const str = new TextDecoder().decode(data);
53
- managed.output.push(str);
54
- if (managed.output.length > this.maxOutputLines) {
55
- managed.output = managed.output.slice(-this.maxOutputLines);
56
- }
124
+ managed.terminalBuffer.write(str);
57
125
  this.emit("output", name, str);
58
126
  }
59
127
  }
@@ -75,7 +143,7 @@ var ProcessManager = class extends EventEmitter {
75
143
  this.emit("started", name);
76
144
  } catch (error) {
77
145
  managed.status = "error";
78
- managed.output = [`Error starting process: ${error}`];
146
+ managed.terminalBuffer.write(`Error starting process: ${error}`);
79
147
  managed.exitCode = -1;
80
148
  this.emit("error", name, error);
81
149
  }
@@ -86,7 +154,7 @@ var ProcessManager = class extends EventEmitter {
86
154
  if (proc.pty) {
87
155
  proc.pty.kill();
88
156
  }
89
- proc.output = [];
157
+ proc.terminalBuffer.clear();
90
158
  this.start(name, proc.config);
91
159
  }
92
160
  }
@@ -130,7 +198,7 @@ var ProcessManager = class extends EventEmitter {
130
198
  return Array.from(this.processes.keys());
131
199
  }
132
200
  getOutput(name) {
133
- return this.processes.get(name)?.output.join("") ?? "";
201
+ return this.processes.get(name)?.terminalBuffer.toString() ?? "";
134
202
  }
135
203
  };
136
204
 
@@ -193,7 +261,7 @@ function useFocusMode() {
193
261
  // src/hooks/useMouseWheel.ts
194
262
  import { useEffect as useEffect2, useCallback as useCallback3 } from "react";
195
263
  import { useStdin, useStdout } from "ink";
196
- function useMouseWheel({ enabled = true, onWheel } = {}) {
264
+ function useMouseWheel({ enabled = true, onWheel, onClick } = {}) {
197
265
  const { stdin, setRawMode } = useStdin();
198
266
  const { stdout } = useStdout();
199
267
  const handleData = useCallback3((data) => {
@@ -206,14 +274,16 @@ function useMouseWheel({ enabled = true, onWheel } = {}) {
206
274
  const y = parseInt(match[3] ?? "0", 10);
207
275
  const isPress = match[4] === "M";
208
276
  if (isPress) {
209
- if (button === 64) {
277
+ if (button === 0) {
278
+ onClick?.({ type: "click", x, y });
279
+ } else if (button === 64) {
210
280
  onWheel?.({ type: "wheel-up", x, y });
211
281
  } else if (button === 65) {
212
282
  onWheel?.({ type: "wheel-down", x, y });
213
283
  }
214
284
  }
215
285
  }
216
- }, [onWheel]);
286
+ }, [onWheel, onClick]);
217
287
  useEffect2(() => {
218
288
  if (!enabled || !stdin || !stdout) return;
219
289
  stdout.write("\x1B[?1000h\x1B[?1006h");
@@ -231,6 +301,7 @@ import { Box, Text, useStdout as useStdout2 } from "ink";
231
301
  import { ScrollList } from "ink-scroll-list";
232
302
  import { forwardRef, useImperativeHandle, useRef as useRef2, useEffect as useEffect3 } from "react";
233
303
  import { jsx, jsxs } from "react/jsx-runtime";
304
+ var PROCESS_LIST_WIDTH = 20;
234
305
  var ProcessList = forwardRef(
235
306
  function ProcessList2({ names, selected, getStatus, active, height }, ref) {
236
307
  const borderStyle = active ? "double" : "single";
@@ -254,7 +325,7 @@ var ProcessList = forwardRef(
254
325
  flexDirection: "column",
255
326
  borderStyle,
256
327
  borderColor: active ? "blue" : "gray",
257
- width: 20,
328
+ width: PROCESS_LIST_WIDTH,
258
329
  height,
259
330
  paddingX: 1,
260
331
  children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, height: height ? height - 2 : void 0, children: /* @__PURE__ */ jsx(
@@ -273,7 +344,7 @@ var ProcessList = forwardRef(
273
344
  name,
274
345
  " "
275
346
  ] }),
276
- /* @__PURE__ */ jsx(Text, { color: isSelected ? "black" : statusColor, children: statusIcon })
347
+ /* @__PURE__ */ jsx(Text, { color: statusColor, children: statusIcon })
277
348
  ] }, name);
278
349
  })
279
350
  }
@@ -416,9 +487,8 @@ function StatusBar({ focusMode, processName, showShiftTabHint = true }) {
416
487
  const shiftTabHint = showShiftTabHint ? "Shift-Tab/" : "";
417
488
  return /* @__PURE__ */ jsx4(Box4, { backgroundColor: "green", width: "100%", children: /* @__PURE__ */ jsxs3(Text4, { bold: true, color: "black", backgroundColor: "green", children: [
418
489
  " ",
419
- "FOCUS: ",
420
490
  processName,
421
- " - Type to interact, [",
491
+ " | [",
422
492
  shiftTabHint,
423
493
  "Esc] to exit focus mode",
424
494
  " "
@@ -552,17 +622,33 @@ function App({ config }) {
552
622
  }
553
623
  }
554
624
  }, [selectedName]);
625
+ const handleClick = useCallback5((event) => {
626
+ if (event.x <= PROCESS_LIST_WIDTH) {
627
+ if (focusMode) {
628
+ exitFocus();
629
+ }
630
+ const clickedIndex = event.y - 2;
631
+ if (clickedIndex >= 0 && clickedIndex < names.length) {
632
+ setSelected(clickedIndex);
633
+ }
634
+ } else {
635
+ if (!focusMode) {
636
+ enterFocus();
637
+ }
638
+ }
639
+ }, [names.length, focusMode, enterFocus, exitFocus]);
555
640
  useMouseWheel({
556
641
  enabled: !showHelp,
557
642
  // Disable when help is shown
558
- onWheel: handleWheel
643
+ onWheel: handleWheel,
644
+ onClick: handleClick
559
645
  });
560
646
  useInput((input, key) => {
561
647
  if (showHelp) {
562
648
  setShowHelp(false);
563
649
  return;
564
650
  }
565
- if (input === "q" || key.ctrl && input === "c") {
651
+ if (key.ctrl && input === "c") {
566
652
  killAll();
567
653
  setRawMode(false);
568
654
  const rows = stdout?.rows ?? 999;
@@ -571,10 +657,6 @@ function App({ config }) {
571
657
  exit();
572
658
  process.exit(0);
573
659
  }
574
- if (input === "?") {
575
- setShowHelp(true);
576
- return;
577
- }
578
660
  if (focusMode) {
579
661
  const name = names[selected];
580
662
  if (!name) return;
@@ -607,10 +689,26 @@ function App({ config }) {
607
689
  return;
608
690
  }
609
691
  if (input && !key.ctrl && !key.meta) {
610
- write(name, input);
692
+ const filtered = input.replace(/\x1b?\[<\d+;\d+;\d+[Mm]/g, "");
693
+ if (filtered) {
694
+ write(name, filtered);
695
+ }
611
696
  }
612
697
  return;
613
698
  }
699
+ if (input === "q") {
700
+ killAll();
701
+ setRawMode(false);
702
+ const rows = stdout?.rows ?? 999;
703
+ stdout?.write(`\x1B[${rows};1H\x1B[J\x1B[?1000l\x1B[?1006l\x1B[?25h\x1B[0m
704
+ `);
705
+ exit();
706
+ process.exit(0);
707
+ }
708
+ if (input === "?") {
709
+ setShowHelp(true);
710
+ return;
711
+ }
614
712
  if (key.upArrow || input === "k") {
615
713
  setSelected((s) => Math.max(s - 1, 0));
616
714
  return;
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/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 } 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 // Enable mouse wheel tracking\n useMouseWheel({\n enabled: !showHelp, // Disable when help is shown\n onWheel: handleWheel,\n });\n\n useInput((input, key) => {\n // Handle help popup\n if (showHelp) {\n setShowHelp(false);\n return;\n }\n\n // Quit\n if (input === 'q' || (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 // Help\n if (input === '?') {\n setShowHelp(true);\n return;\n }\n\n // Focus mode input handling\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\n if (input && !key.ctrl && !key.meta) {\n write(name, input);\n }\n return;\n }\n\n // Normal mode\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';\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 output: string[];\n exitCode: number | null;\n}\n\nexport class ProcessManager extends EventEmitter {\n private processes: Map<string, ManagedProcess> = new Map();\n private maxOutputLines = 10000;\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 output: [],\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.output.push(str);\n if (managed.output.length > this.maxOutputLines) {\n managed.output = managed.output.slice(-this.maxOutputLines);\n }\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.output = [`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.output = [];\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)?.output.join('') ?? '';\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 UseMouseWheelOptions {\n enabled?: boolean;\n onWheel?: (event: MouseWheelEvent) => void;\n}\n\n/**\n * Hook to enable mouse wheel 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 wheel events (SGR mode):\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 }: 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 === 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]);\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\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={20}\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={isSelected ? 'black' : 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 {' '}FOCUS: {processName} - Type to interact, [{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 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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panex",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
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/headless": "^6.0.0",
42
43
  "chalk": "^5.3.0",
43
44
  "commander": "^12.1.0",
44
45
  "ink": "^6.6.0",