tmux-ide 2.6.1 → 2.8.0

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.
Files changed (154) hide show
  1. package/README.md +36 -14
  2. package/bin/cli.js +4174 -1235
  3. package/bin/cli.ts +426 -72
  4. package/package.json +10 -6
  5. package/packages/contracts/src/__tests__/control.test.ts +154 -0
  6. package/packages/contracts/src/control.ts +217 -0
  7. package/packages/contracts/src/index.ts +1 -0
  8. package/packages/daemon/dist/agent-explain.d.ts +8 -1
  9. package/packages/daemon/dist/agent-explain.js +19 -3
  10. package/packages/daemon/dist/control/client.d.ts +23 -0
  11. package/packages/daemon/dist/control/client.js +105 -0
  12. package/packages/daemon/dist/control/dispatch.d.ts +34 -0
  13. package/packages/daemon/dist/control/dispatch.js +83 -0
  14. package/packages/daemon/dist/control/fanout.d.ts +19 -0
  15. package/packages/daemon/dist/control/fanout.js +37 -0
  16. package/packages/daemon/dist/control/frames.d.ts +23 -0
  17. package/packages/daemon/dist/control/frames.js +37 -0
  18. package/packages/daemon/dist/control/lifecycle.d.ts +45 -0
  19. package/packages/daemon/dist/control/lifecycle.js +114 -0
  20. package/packages/daemon/dist/control/server.d.ts +16 -0
  21. package/packages/daemon/dist/control/server.js +214 -0
  22. package/packages/daemon/dist/control/verbs.d.ts +11 -0
  23. package/packages/daemon/dist/control/verbs.js +91 -0
  24. package/packages/daemon/dist/doctor.d.ts +18 -0
  25. package/packages/daemon/dist/doctor.js +105 -15
  26. package/packages/daemon/dist/lib/agent-discovery.d.ts +27 -2
  27. package/packages/daemon/dist/lib/agent-discovery.js +29 -14
  28. package/packages/daemon/dist/lib/app-config.d.ts +106 -0
  29. package/packages/daemon/dist/lib/app-config.js +104 -5
  30. package/packages/daemon/dist/lib/manifest-pack.d.ts +79 -0
  31. package/packages/daemon/dist/lib/manifest-pack.js +232 -0
  32. package/packages/daemon/dist/lib/state-home.d.ts +2 -0
  33. package/packages/daemon/dist/lib/state-home.js +12 -0
  34. package/packages/daemon/dist/lib/tui-binary.d.ts +57 -0
  35. package/packages/daemon/dist/lib/tui-binary.js +130 -0
  36. package/packages/daemon/dist/lib/update-check.js +5 -0
  37. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +34 -0
  38. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
  39. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/PkgInfo +1 -0
  40. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/AppIcon.icns +0 -0
  41. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
  42. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +139 -0
  43. package/packages/daemon/dist/restore.d.ts +35 -8
  44. package/packages/daemon/dist/restore.js +52 -15
  45. package/packages/daemon/dist/send.d.ts +33 -1
  46. package/packages/daemon/dist/send.js +32 -19
  47. package/packages/daemon/dist/widgets/explorer/breadcrumbs.d.ts +1 -1
  48. package/packages/daemon/dist/widgets/explorer/footer.d.ts +1 -1
  49. package/packages/daemon/dist/widgets/explorer/tree.d.ts +1 -1
  50. package/packages/daemon/dist/widgets/lib/help-overlay.d.ts +1 -1
  51. package/packages/daemon/dist/widgets/setup/agent-naming.d.ts +1 -1
  52. package/packages/daemon/dist/widgets/setup/config-tree.d.ts +1 -1
  53. package/packages/daemon/dist/widgets/setup/detect-panel.d.ts +1 -1
  54. package/packages/daemon/dist/widgets/setup/field-editor.d.ts +1 -1
  55. package/packages/daemon/dist/widgets/setup/footer.d.ts +1 -1
  56. package/packages/daemon/dist/widgets/setup/layout-picker.d.ts +1 -1
  57. package/packages/daemon/src/agent-explain.ts +34 -6
  58. package/packages/daemon/src/control/client.ts +128 -0
  59. package/packages/daemon/src/control/dispatch.ts +107 -0
  60. package/packages/daemon/src/control/fanout.ts +44 -0
  61. package/packages/daemon/src/control/frames.ts +40 -0
  62. package/packages/daemon/src/control/lifecycle.ts +151 -0
  63. package/packages/daemon/src/control/server.ts +237 -0
  64. package/packages/daemon/src/control/verbs.ts +118 -0
  65. package/packages/daemon/src/doctor.ts +113 -28
  66. package/packages/daemon/src/lib/agent-discovery.ts +53 -13
  67. package/packages/daemon/src/lib/app-config.ts +194 -5
  68. package/packages/daemon/src/lib/manifest-pack.ts +255 -0
  69. package/packages/daemon/src/lib/state-home.ts +13 -0
  70. package/packages/daemon/src/lib/tui-binary.ts +165 -0
  71. package/packages/daemon/src/lib/update-check.ts +5 -0
  72. package/packages/daemon/src/restore.ts +53 -15
  73. package/packages/daemon/src/send.ts +55 -21
  74. package/packages/daemon/src/tui/chrome/events.ts +4 -4
  75. package/packages/daemon/src/tui/chrome/front-door.ts +39 -0
  76. package/packages/daemon/src/tui/chrome/notify-prefs.ts +58 -0
  77. package/packages/daemon/src/tui/chrome/notify-state.ts +76 -0
  78. package/packages/daemon/src/tui/chrome/notify.ts +737 -52
  79. package/packages/daemon/src/tui/chrome/updater.ts +318 -30
  80. package/packages/daemon/src/tui/compiled.ts +11 -3
  81. package/packages/daemon/src/tui/detect/classify.ts +49 -0
  82. package/packages/daemon/src/tui/detect/manifest-loader.ts +56 -6
  83. package/packages/daemon/src/tui/detect/manifest.ts +39 -3
  84. package/packages/daemon/src/tui/detect/manifests.ts +395 -33
  85. package/packages/daemon/src/tui/detect/process-tree.ts +33 -3
  86. package/packages/daemon/src/tui/detect/session-id.ts +503 -0
  87. package/packages/daemon/src/tui/integrations/opencode.ts +121 -0
  88. package/packages/daemon/src/tui/main.ts +13 -1
  89. package/packages/daemon/src/tui/mirror/ack-writer.ts +77 -0
  90. package/packages/daemon/src/tui/mirror/agent-chip.ts +126 -0
  91. package/packages/daemon/src/tui/mirror/agent-lifecycle.ts +437 -0
  92. package/packages/daemon/src/tui/mirror/agent-rows.ts +155 -0
  93. package/packages/daemon/src/tui/mirror/app-state.ts +342 -0
  94. package/packages/daemon/src/tui/mirror/app.tsx +7048 -0
  95. package/packages/daemon/src/tui/mirror/attention.ts +110 -0
  96. package/packages/daemon/src/tui/mirror/blit.ts +186 -0
  97. package/packages/daemon/src/tui/mirror/control-client.ts +80 -9
  98. package/packages/daemon/src/tui/mirror/dialog-model.ts +298 -0
  99. package/packages/daemon/src/tui/mirror/dialog-stack.ts +367 -0
  100. package/packages/daemon/src/tui/mirror/diff-model.ts +387 -0
  101. package/packages/daemon/src/tui/mirror/editor-buffer.ts +117 -0
  102. package/packages/daemon/src/tui/mirror/file-tree.ts +322 -0
  103. package/packages/daemon/src/tui/mirror/focus-border.ts +57 -0
  104. package/packages/daemon/src/tui/mirror/folder-picker.ts +124 -0
  105. package/packages/daemon/src/tui/mirror/home-model.ts +174 -0
  106. package/packages/daemon/src/tui/mirror/host-terminal.ts +49 -0
  107. package/packages/daemon/src/tui/mirror/hosted.ts +205 -0
  108. package/packages/daemon/src/tui/mirror/input-coalescer.ts +105 -0
  109. package/packages/daemon/src/tui/mirror/layout-parse.ts +154 -0
  110. package/packages/daemon/src/tui/mirror/menu-model.ts +210 -0
  111. package/packages/daemon/src/tui/mirror/palette.ts +564 -0
  112. package/packages/daemon/src/tui/mirror/pane-mirror.ts +578 -20
  113. package/packages/daemon/src/tui/mirror/pane-surface.tsx +422 -0
  114. package/packages/daemon/src/tui/mirror/perf-tap.ts +186 -0
  115. package/packages/daemon/src/tui/mirror/resize-model.ts +85 -0
  116. package/packages/daemon/src/tui/mirror/scrollbar-model.ts +88 -0
  117. package/packages/daemon/src/tui/mirror/search-model.ts +70 -0
  118. package/packages/daemon/src/tui/mirror/selection.ts +376 -0
  119. package/packages/daemon/src/tui/mirror/session-mirror.ts +724 -0
  120. package/packages/daemon/src/tui/mirror/settings-model.ts +425 -0
  121. package/packages/daemon/src/tui/mirror/sidebar.tsx +218 -0
  122. package/packages/daemon/src/tui/mirror/size-truth.ts +130 -0
  123. package/packages/daemon/src/tui/mirror/spans.ts +46 -0
  124. package/packages/daemon/src/tui/mirror/status-grammar.ts +32 -0
  125. package/packages/daemon/src/tui/mirror/theme.ts +45 -0
  126. package/packages/daemon/src/tui/team/entry.ts +34 -7
  127. package/packages/daemon/src/tui/team/fuzzy.ts +20 -0
  128. package/packages/daemon/src/tui/team/report.ts +11 -1
  129. package/packages/daemon/src/tui/team/sessions.ts +184 -17
  130. package/packages/daemon/src/tui/team/wait.ts +144 -0
  131. package/scripts/build-macos-notifier.mjs +160 -0
  132. package/scripts/build-tui.mjs +11 -4
  133. package/scripts/perf-mirror.mjs +313 -0
  134. package/scripts/postinstall.js +8 -1
  135. package/scripts/prepublish-check.mjs +37 -1
  136. package/scripts/publish-tap.sh +55 -0
  137. package/skill/SKILL.md +110 -2
  138. package/templates/AGENTS.md +14 -7
  139. package/templates/agent-team-monorepo.yml +8 -0
  140. package/templates/agent-team-nextjs.yml +8 -0
  141. package/templates/agent-team.yml +10 -0
  142. package/templates/convex.yml +2 -0
  143. package/templates/default.yml +11 -5
  144. package/templates/go.yml +4 -0
  145. package/templates/missions.yml +6 -0
  146. package/templates/nextjs.yml +4 -0
  147. package/templates/python.yml +4 -0
  148. package/templates/skills/backend.md +5 -12
  149. package/templates/skills/frontend.md +5 -12
  150. package/templates/skills/general-worker.md +5 -12
  151. package/templates/skills/researcher.md +7 -12
  152. package/templates/skills/reviewer.md +7 -16
  153. package/templates/vite.yml +4 -0
  154. package/packages/daemon/src/tui/mirror/viewer.tsx +0 -166
@@ -1,5 +1,7 @@
1
1
  name: my-app
2
2
 
3
+ before: pnpm install
4
+
3
5
  rows:
4
6
  - size: 70%
5
7
  panes:
@@ -1,6 +1,8 @@
1
1
  name: my-project
2
2
 
3
- # before: npm install # optional pre-launch hook
3
+ # before: npm install # optional pre-launch hook (runs once, before panes start)
4
+
5
+ # sidebar: true # inject the fleet nav column (prefix b toggles it live)
4
6
 
5
7
  rows:
6
8
  - size: 70%
@@ -11,10 +13,14 @@ rows:
11
13
  command: claude
12
14
 
13
15
  - panes:
16
+ # Widget panes render native UI instead of a command:
17
+ # type: explorer | changes | preview | config | sidebar
18
+ - title: Changes
19
+ type: changes # live git changes — no command, tmux-ide draws it
14
20
  - title: Dev Server
15
21
  # command: npm run dev
16
- # dir: apps/web # per-pane working directory
17
- # env: # per-pane environment variables
18
- # PORT: 3000
22
+ # dir: apps/web # per-pane working directory
23
+ # env: # per-pane environment variables
24
+ # PORT: "3000"
19
25
  - title: Shell
20
- # focus: true # give this pane initial focus
26
+ focus: true # give this pane initial focus
package/templates/go.yml CHANGED
@@ -1,5 +1,7 @@
1
1
  name: my-app
2
2
 
3
+ before: go mod download
4
+
3
5
  rows:
4
6
  - size: 70%
5
7
  panes:
@@ -11,5 +13,7 @@ rows:
11
13
  - panes:
12
14
  - title: Go
13
15
  command: go run .
16
+ - title: Changes
17
+ type: changes # live git changes widget
14
18
  - title: Shell
15
19
  focus: true
@@ -1,8 +1,14 @@
1
1
  name: project-name
2
2
 
3
+ # Ground-truth agent status (working/blocked/done glyphs on the fleet tabs)
4
+ # comes from Claude Code's lifecycle hooks. Wire them once with:
5
+ # tmux-ide integration install claude
6
+
3
7
  team:
4
8
  name: missions
5
9
 
10
+ sidebar: true # fleet nav column — jump between agents (toggle: prefix b)
11
+
6
12
  rows:
7
13
  - size: 70%
8
14
  panes:
@@ -1,5 +1,7 @@
1
1
  name: my-app
2
2
 
3
+ before: pnpm install
4
+
3
5
  rows:
4
6
  - size: 70%
5
7
  panes:
@@ -13,5 +15,7 @@ rows:
13
15
  - panes:
14
16
  - title: Next.js
15
17
  command: pnpm dev
18
+ - title: Changes
19
+ type: changes # live git changes widget
16
20
  - title: Shell
17
21
  focus: true
@@ -1,5 +1,7 @@
1
1
  name: my-app
2
2
 
3
+ # before: pip install -r requirements.txt # or: uv sync / poetry install
4
+
3
5
  rows:
4
6
  - size: 70%
5
7
  panes:
@@ -11,5 +13,7 @@ rows:
11
13
  - panes:
12
14
  - title: Server
13
15
  # command: uvicorn main:app --reload
16
+ - title: Changes
17
+ type: changes # live git changes widget
14
18
  - title: Shell
15
19
  focus: true
@@ -25,19 +25,12 @@ You are a backend development specialist.
25
25
 
26
26
  Your dispatch prompt includes relevant excerpts from the knowledge library (.tmux-ide/library/) and AGENTS.md. Use these for architectural decisions.
27
27
 
28
- ## After Completion
28
+ ## Reporting
29
29
 
30
- When you finish, the orchestrator will:
30
+ Self-report your state — this drives the fleet status glyphs (see AGENTS.md):
31
31
 
32
- - Notify the lead with your proof and summary
33
- - Run any configured after-run hooks (e.g., linting)
34
- - Auto-dispatch the next available task
35
- - Append your summary to the learnings library
32
+ tmux set-option -p @agent_state "done:$(date +%s)" # working|blocked|done|idle
36
33
 
37
- ## Completion Protocol
34
+ Then report the result to the lead, and flag anything out of scope:
38
35
 
39
- When done, run:
40
- tmux-ide task done <TASK_ID> --proof "what changed and how you verified it" --summary "backend outcome"
41
-
42
- If you find out-of-scope issues, report them:
43
- tmux-ide task update <TASK_ID> --discovered-issues "description of issue"
36
+ tmux-ide send <lead-pane> "what changed, how you verified it, operational concerns"
@@ -25,19 +25,12 @@ You are a frontend development specialist.
25
25
 
26
26
  Your dispatch prompt includes relevant excerpts from the knowledge library (.tmux-ide/library/) and AGENTS.md. Use these for architectural decisions.
27
27
 
28
- ## After Completion
28
+ ## Reporting
29
29
 
30
- When you finish, the orchestrator will:
30
+ Self-report your state — this drives the fleet status glyphs (see AGENTS.md):
31
31
 
32
- - Notify the lead with your proof and summary
33
- - Run any configured after-run hooks (e.g., linting)
34
- - Auto-dispatch the next available task
35
- - Append your summary to the learnings library
32
+ tmux set-option -p @agent_state "done:$(date +%s)" # working|blocked|done|idle
36
33
 
37
- ## Completion Protocol
34
+ Then report the result to the lead, and flag anything out of scope:
38
35
 
39
- When done, run:
40
- tmux-ide task done <TASK_ID> --proof "what changed and how you verified it" --summary "frontend outcome"
41
-
42
- If you find out-of-scope issues, report them:
43
- tmux-ide task update <TASK_ID> --discovered-issues "description of issue"
36
+ tmux-ide send <lead-pane> "what changed, what you verified, any follow-up risks"
@@ -25,19 +25,12 @@ You are a general-purpose development agent.
25
25
 
26
26
  You are a general-purpose agent. Your dispatch prompt includes mission, goal, and task context along with relevant library excerpts. Follow the task description closely.
27
27
 
28
- ## After Completion
28
+ ## Reporting
29
29
 
30
- When you finish, the orchestrator will:
30
+ Self-report your state — this drives the fleet status glyphs (see AGENTS.md):
31
31
 
32
- - Notify the lead with your proof and summary
33
- - Run any configured after-run hooks (e.g., linting)
34
- - Auto-dispatch the next available task
35
- - Append your summary to the learnings library
32
+ tmux set-option -p @agent_state "done:$(date +%s)" # working|blocked|done|idle
36
33
 
37
- ## Completion Protocol
34
+ Then report the result to the lead, and flag anything out of scope:
38
35
 
39
- When done, run:
40
- tmux-ide task done <TASK_ID> --proof "what you accomplished" --summary "key learnings for future tasks"
41
-
42
- If you discover out-of-scope issues, note them:
43
- tmux-ide task update <TASK_ID> --discovered-issues "description of issue"
36
+ tmux-ide send <lead-pane> "what you accomplished, how you verified it, follow-ups"
@@ -57,22 +57,17 @@ Surface useful findings early without stalling delivery. Prefer high-signal obse
57
57
 
58
58
  ## Reporting Protocol
59
59
 
60
- When you finish, record the result on your assigned task:
60
+ Self-report your state — this drives the fleet status glyphs (see AGENTS.md):
61
61
 
62
- `tmux-ide task done <TASK_ID> --proof "findings, evidence, severity, next actions" --summary "short research takeaway"`
62
+ tmux set-option -p @agent_state "done:$(date +%s)" # working|blocked|done|idle
63
63
 
64
- Your proof should include:
64
+ Then send your findings to the lead:
65
+
66
+ tmux-ide send <lead-pane> "findings, evidence, severity, next actions"
67
+
68
+ Your report should include:
65
69
 
66
70
  - What you inspected
67
71
  - The most important findings
68
72
  - Why they matter
69
73
  - The exact next action you recommend
70
-
71
- ## After Completion
72
-
73
- When you finish, the orchestrator will:
74
-
75
- - Notify the lead with your proof and summary
76
- - Run any configured after-run hooks
77
- - Auto-dispatch the next available task
78
- - Append your summary to the learnings library
@@ -20,23 +20,14 @@ You are a code review and validation agent.
20
20
  3. Run relevant tests or manual checks
21
21
  4. Report each assertion's status
22
22
 
23
- ## Assertion Reporting
23
+ ## Reporting
24
24
 
25
- For each assertion:
26
- tmux-ide validate assert <ASSERT_ID> --status passing --evidence "what you verified"
27
- tmux-ide validate assert <ASSERT_ID> --status failing --evidence "what's wrong"
28
- tmux-ide validate assert <ASSERT_ID> --status blocked --evidence "why it's blocked"
25
+ Self-report your state — this drives the fleet status glyphs (see AGENTS.md):
29
26
 
30
- The orchestrator will detect your results and advance the milestone automatically.
27
+ tmux set-option -p @agent_state "done:$(date +%s)" # working|blocked|done|idle
31
28
 
32
- If you find issues beyond the validation contract:
33
- tmux-ide task update <TASK_ID> --discovered-issues "description of issue"
29
+ Then report the verdict to the lead: for each assertion give its status
30
+ (passing / failing / blocked) and the evidence, plus any issues you found beyond
31
+ the validation contract:
34
32
 
35
- ## After Completion
36
-
37
- When you finish, the orchestrator will:
38
-
39
- - Notify the lead with your proof and summary
40
- - Run any configured after-run hooks (e.g., linting)
41
- - Auto-dispatch the next available task
42
- - Append your summary to the learnings library
33
+ tmux-ide send <lead-pane> "assertion results with evidence, plus out-of-scope issues"
@@ -1,5 +1,7 @@
1
1
  name: my-app
2
2
 
3
+ before: pnpm install
4
+
3
5
  rows:
4
6
  - size: 70%
5
7
  panes:
@@ -11,5 +13,7 @@ rows:
11
13
  - panes:
12
14
  - title: Vite
13
15
  command: pnpm dev
16
+ - title: Changes
17
+ type: changes # live git changes widget
14
18
  - title: Shell
15
19
  focus: true
@@ -1,166 +0,0 @@
1
- /**
2
- * SPIKE viewer — renders ONE live tmux pane inside an OpenTUI app.
3
- *
4
- * The proof for the unified-TUI architecture: a control-mode client streams
5
- * the pane's bytes into a headless terminal (PaneMirror), this app draws the
6
- * grid, and keystrokes are forwarded back through `send-keys`. tmux stays the
7
- * multiplexer underneath; tmux-ide owns the screen.
8
- *
9
- * Run (needs the repo-root bunfig preload):
10
- * bun packages/daemon/src/tui/mirror/viewer.tsx --target <session>
11
- * Exit with ctrl+q (everything else is forwarded to the pane).
12
- */
13
- import { parseArgs } from "node:util";
14
- import { render, useKeyboard, useTerminalDimensions } from "@opentui/solid";
15
- import { RGBA } from "@opentui/core";
16
- import { createSignal, onMount, onCleanup, For } from "solid-js";
17
- import { ControlModeClient } from "./control-client.ts";
18
- import { PaneMirror, type MirrorSnapshot } from "./pane-mirror.ts";
19
-
20
- const { values } = parseArgs({ options: { target: { type: "string" } } });
21
- const target = values.target ?? "_spike-target";
22
-
23
- // The 16 base ANSI palette colors, mapped for OpenTUI. Higher palette
24
- // entries fall back to the default foreground — fine for a spike.
25
- const PALETTE: Array<[number, number, number]> = [
26
- [40, 40, 48],
27
- [220, 90, 90],
28
- [110, 200, 130],
29
- [220, 200, 100],
30
- [110, 150, 240],
31
- [190, 120, 220],
32
- [100, 200, 210],
33
- [200, 200, 210],
34
- [120, 120, 140],
35
- [250, 120, 120],
36
- [140, 230, 160],
37
- [240, 220, 130],
38
- [140, 180, 250],
39
- [220, 150, 250],
40
- [130, 230, 240],
41
- [240, 240, 250],
42
- ];
43
-
44
- /** Map named OpenTUI key events to tmux send-keys names. */
45
- const KEYMAP: Record<string, string> = {
46
- return: "Enter",
47
- backspace: "BSpace",
48
- tab: "Tab",
49
- escape: "Escape",
50
- up: "Up",
51
- down: "Down",
52
- left: "Left",
53
- right: "Right",
54
- pageup: "PgUp",
55
- pagedown: "PgDn",
56
- home: "Home",
57
- end: "End",
58
- delete: "DC",
59
- space: "Space",
60
- };
61
-
62
- render(() => {
63
- const dims = useTerminalDimensions();
64
- const cols = Math.max(20, dims().width - 2);
65
- const rows = Math.max(5, dims().height - 3);
66
-
67
- const mirror = new PaneMirror(cols, rows);
68
- const [snap, setSnap] = createSignal<MirrorSnapshot>(mirror.snapshot());
69
- const [status, setStatus] = createSignal(`attaching to ${target}…`);
70
- let pane = "";
71
- let dirty = false;
72
-
73
- const client = new ControlModeClient({
74
- attachTarget: target,
75
- onOutput: (p, data) => {
76
- if (p === pane || pane === "") {
77
- mirror.write(data);
78
- dirty = true;
79
- }
80
- },
81
- onExit: () => {
82
- setStatus("control client exited");
83
- },
84
- });
85
-
86
- onMount(() => {
87
- (async () => {
88
- try {
89
- await client.start();
90
- // Pin our control client's size so tmux lays the window out to match
91
- // the mirror grid, then seed with the pane's current content (-e keeps
92
- // colors) so we don't start from a blank screen.
93
- await client.command(`refresh-client -C ${cols}x${rows}`);
94
- const panes = await client.command(`list-panes -t ${target} -F "#{pane_id}"`);
95
- pane = panes[0] ?? "";
96
- const seed = await client.command(`capture-pane -p -e -t ${pane}`);
97
- mirror.write(seed.join("\r\n") + "\r\n");
98
- dirty = true;
99
- setStatus(`live: ${target} ${pane} (ctrl+q to quit)`);
100
- } catch (e) {
101
- setStatus(`error: ${(e as Error).message}`);
102
- }
103
- })();
104
-
105
- // Coalesce bursts: redraw at most ~30fps instead of per %output event.
106
- const timer = setInterval(() => {
107
- if (!dirty) return;
108
- dirty = false;
109
- setSnap(mirror.snapshot());
110
- }, 33);
111
- onCleanup(() => {
112
- clearInterval(timer);
113
- client.dispose();
114
- mirror.dispose();
115
- });
116
- });
117
-
118
- useKeyboard((evt) => {
119
- if (evt.ctrl && evt.name === "q") {
120
- client.dispose();
121
- process.exit(0);
122
- }
123
- if (!pane) return;
124
- if (evt.ctrl && evt.name.length === 1) {
125
- void client.sendKey(pane, `C-${evt.name}`).catch(() => {});
126
- return;
127
- }
128
- const named = KEYMAP[evt.name];
129
- if (named) {
130
- void client.sendKey(pane, named).catch(() => {});
131
- return;
132
- }
133
- if (evt.name.length === 1 && !evt.meta && !evt.alt) {
134
- void client.sendText(pane, evt.name).catch(() => {});
135
- }
136
- });
137
-
138
- const fgFor = (idx: number | null): RGBA => {
139
- if (idx === null || idx < 0 || idx > 15) return RGBA.fromInts(200, 200, 210, 255);
140
- const [r, g, b] = PALETTE[idx]!;
141
- return RGBA.fromInts(r, g, b, 255);
142
- };
143
-
144
- return (
145
- <box flexDirection="column" flexGrow={1} backgroundColor={RGBA.fromInts(18, 18, 24, 255)}>
146
- <box paddingLeft={1} flexDirection="row" gap={1}>
147
- <text fg={RGBA.fromInts(130, 170, 255, 255)}>tmux-ide mirror</text>
148
- <text fg={RGBA.fromInts(120, 120, 140, 255)}>{status()}</text>
149
- </box>
150
- <box flexDirection="column" flexGrow={1} paddingLeft={1}>
151
- <For each={snap().rows}>
152
- {(runs) => (
153
- <box flexDirection="row">
154
- <For each={runs}>{(run) => <text fg={fgFor(run.fg)}>{run.text}</text>}</For>
155
- </box>
156
- )}
157
- </For>
158
- </box>
159
- <box paddingLeft={1}>
160
- <text fg={RGBA.fromInts(120, 120, 140, 255)}>
161
- {`cursor ${snap().cursorX},${snap().cursorY} · keys forwarded · ctrl+q quits`}
162
- </text>
163
- </box>
164
- </box>
165
- );
166
- });