tmux-ide 2.6.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/bin/cli.js +13 -5
  2. package/bin/cli.ts +1 -1
  3. package/bunfig.toml +4 -0
  4. package/package.json +11 -3
  5. package/packages/contracts/package.json +22 -0
  6. package/packages/contracts/src/__tests__/ide-config.test.ts +46 -0
  7. package/packages/contracts/src/__tests__/terminals.test.ts +87 -0
  8. package/packages/contracts/src/actions-contract.ts +310 -0
  9. package/packages/contracts/src/actions-errors.ts +41 -0
  10. package/packages/contracts/src/domain.ts +36 -0
  11. package/packages/contracts/src/ide-config.ts +170 -0
  12. package/packages/contracts/src/index.ts +24 -0
  13. package/packages/contracts/src/lib-internal/auth.ts +13 -0
  14. package/packages/contracts/src/lib-internal/hq.ts +38 -0
  15. package/packages/contracts/src/terminals.ts +116 -0
  16. package/packages/contracts/src/tmux.ts +60 -0
  17. package/packages/contracts/src/workspace.ts +67 -0
  18. package/packages/daemon/src/agent-explain.ts +270 -0
  19. package/packages/daemon/src/attach.ts +20 -0
  20. package/packages/daemon/src/bin.ts +4 -0
  21. package/packages/daemon/src/canonical.ts +7 -0
  22. package/packages/daemon/src/cli.ts +499 -0
  23. package/packages/daemon/src/command-center/actions/contract.ts +2 -0
  24. package/packages/daemon/src/command-center/actions/dispatcher.ts +137 -0
  25. package/packages/daemon/src/command-center/actions/errors.ts +105 -0
  26. package/packages/daemon/src/command-center/actions/handlers/_project-context.ts +30 -0
  27. package/packages/daemon/src/command-center/actions/handlers/_resolve-project.ts +78 -0
  28. package/packages/daemon/src/command-center/actions/handlers/app-set-remote-access.ts +118 -0
  29. package/packages/daemon/src/command-center/actions/handlers/config-actions.ts +113 -0
  30. package/packages/daemon/src/command-center/actions/handlers/daemon-shutdown.ts +38 -0
  31. package/packages/daemon/src/command-center/actions/handlers/project-activate.ts +30 -0
  32. package/packages/daemon/src/command-center/actions/handlers/project-launch.ts +70 -0
  33. package/packages/daemon/src/command-center/actions/handlers/project-open-terminal.ts +87 -0
  34. package/packages/daemon/src/command-center/actions/handlers/project-restart.ts +38 -0
  35. package/packages/daemon/src/command-center/actions/handlers/project-stop.ts +62 -0
  36. package/packages/daemon/src/command-center/actions/handlers/terminal-respawn.ts +119 -0
  37. package/packages/daemon/src/command-center/actions/handlers/terminal-stop.ts +35 -0
  38. package/packages/daemon/src/command-center/actions/registry.ts +149 -0
  39. package/packages/daemon/src/command-center/discovery.ts +96 -0
  40. package/packages/daemon/src/command-center/index.ts +31 -0
  41. package/packages/daemon/src/command-center/schemas.ts +85 -0
  42. package/packages/daemon/src/command-center/server.ts +1260 -0
  43. package/packages/daemon/src/command-center/ws-events.ts +316 -0
  44. package/packages/daemon/src/config.ts +549 -0
  45. package/packages/daemon/src/detect.ts +248 -0
  46. package/packages/daemon/src/doctor.ts +242 -0
  47. package/packages/daemon/src/embed.ts +5 -0
  48. package/packages/daemon/src/index.ts +12 -0
  49. package/packages/daemon/src/init.ts +211 -0
  50. package/packages/daemon/src/inspect.ts +178 -0
  51. package/packages/daemon/src/js-yaml.d.ts +10 -0
  52. package/packages/daemon/src/launch.ts +349 -0
  53. package/packages/daemon/src/lib/active-projects.ts +49 -0
  54. package/packages/daemon/src/lib/agent-discovery.ts +121 -0
  55. package/packages/daemon/src/lib/app-config.ts +336 -0
  56. package/packages/daemon/src/lib/app-settings.ts +53 -0
  57. package/packages/daemon/src/lib/auth/auth-service.ts +227 -0
  58. package/packages/daemon/src/lib/auth/middleware.ts +56 -0
  59. package/packages/daemon/src/lib/auth/types.ts +2 -0
  60. package/packages/daemon/src/lib/auth-token.ts +5 -0
  61. package/packages/daemon/src/lib/authorship.ts +280 -0
  62. package/packages/daemon/src/lib/canonical-daemon.ts +122 -0
  63. package/packages/daemon/src/lib/cli-action-bridge.ts +216 -0
  64. package/packages/daemon/src/lib/daemon-embed.ts +782 -0
  65. package/packages/daemon/src/lib/daemon-watchdog.ts +111 -0
  66. package/packages/daemon/src/lib/daemon.ts +79 -0
  67. package/packages/daemon/src/lib/dot-path.ts +17 -0
  68. package/packages/daemon/src/lib/errors.ts +67 -0
  69. package/packages/daemon/src/lib/filesystem-browser.ts +292 -0
  70. package/packages/daemon/src/lib/launch-plan.ts +90 -0
  71. package/packages/daemon/src/lib/log.ts +134 -0
  72. package/packages/daemon/src/lib/output.ts +76 -0
  73. package/packages/daemon/src/lib/project-init-runner.ts +150 -0
  74. package/packages/daemon/src/lib/project-inspect.ts +80 -0
  75. package/packages/daemon/src/lib/project-onboard.ts +149 -0
  76. package/packages/daemon/src/lib/project-probe.ts +92 -0
  77. package/packages/daemon/src/lib/project-registry.ts +296 -0
  78. package/packages/daemon/src/lib/session-monitor.ts +122 -0
  79. package/packages/daemon/src/lib/session-options.ts +100 -0
  80. package/packages/daemon/src/lib/shell.ts +8 -0
  81. package/packages/daemon/src/lib/sizes.ts +36 -0
  82. package/packages/daemon/src/lib/skill-sync.ts +155 -0
  83. package/packages/daemon/src/lib/slugify.ts +10 -0
  84. package/packages/daemon/src/lib/terminals-store.ts +125 -0
  85. package/packages/daemon/src/lib/update-check.ts +298 -0
  86. package/packages/daemon/src/lib/update.ts +158 -0
  87. package/packages/daemon/src/lib/workspace-registry.ts +229 -0
  88. package/packages/daemon/src/lib/worktree.ts +289 -0
  89. package/packages/daemon/src/lib/yaml-io.ts +27 -0
  90. package/packages/daemon/src/ls.ts +40 -0
  91. package/packages/daemon/src/restart.ts +24 -0
  92. package/packages/daemon/src/restore.ts +514 -0
  93. package/packages/daemon/src/schemas/domain.ts +2 -0
  94. package/packages/daemon/src/schemas/filesystem.ts +34 -0
  95. package/packages/daemon/src/schemas/ide-config.ts +2 -0
  96. package/packages/daemon/src/schemas/index.ts +59 -0
  97. package/packages/daemon/src/schemas/inspect.ts +67 -0
  98. package/packages/daemon/src/schemas/registry.ts +55 -0
  99. package/packages/daemon/src/schemas/ws-events.ts +135 -0
  100. package/packages/daemon/src/send.ts +171 -0
  101. package/packages/daemon/src/server/README.md +15 -0
  102. package/packages/daemon/src/server/index.ts +74 -0
  103. package/packages/daemon/src/server/pty-bridge.ts +532 -0
  104. package/packages/daemon/src/server/standalone.ts +18 -0
  105. package/packages/daemon/src/server/ws-route.ts +483 -0
  106. package/packages/daemon/src/status.ts +59 -0
  107. package/packages/daemon/src/stop.ts +28 -0
  108. package/packages/daemon/src/terminal/NodePtyAdapter.ts +271 -0
  109. package/packages/daemon/src/terminal/PtyAdapter.ts +140 -0
  110. package/packages/daemon/src/terminal/README.md +92 -0
  111. package/packages/daemon/src/tui/chrome/cheatsheet.ts +260 -0
  112. package/packages/daemon/src/tui/chrome/chip.ts +30 -0
  113. package/packages/daemon/src/tui/chrome/events.ts +119 -0
  114. package/packages/daemon/src/tui/chrome/kitty-keys.ts +55 -0
  115. package/packages/daemon/src/tui/chrome/menu.ts +289 -0
  116. package/packages/daemon/src/tui/chrome/notify.ts +195 -0
  117. package/packages/daemon/src/tui/chrome/panels.ts +111 -0
  118. package/packages/daemon/src/tui/chrome/sidebar.ts +222 -0
  119. package/packages/daemon/src/tui/chrome/snapshot.ts +425 -0
  120. package/packages/daemon/src/tui/chrome/statusline.ts +595 -0
  121. package/packages/daemon/src/tui/chrome/updater.ts +428 -0
  122. package/packages/daemon/src/tui/chrome/welcome.ts +124 -0
  123. package/packages/daemon/src/tui/compiled.ts +113 -0
  124. package/packages/daemon/src/tui/detect/classify.ts +193 -0
  125. package/packages/daemon/src/tui/detect/manifest-loader.ts +192 -0
  126. package/packages/daemon/src/tui/detect/manifest.ts +184 -0
  127. package/packages/daemon/src/tui/detect/manifests.ts +226 -0
  128. package/packages/daemon/src/tui/detect/process-tree.ts +197 -0
  129. package/packages/daemon/src/tui/detect/snapshot.ts +70 -0
  130. package/packages/daemon/src/tui/integrations/claude.ts +176 -0
  131. package/packages/daemon/src/tui/integrations/offer.ts +145 -0
  132. package/packages/daemon/src/tui/main.ts +70 -0
  133. package/packages/daemon/src/tui/mirror/control-client.ts +143 -0
  134. package/packages/daemon/src/tui/mirror/control.ts +97 -0
  135. package/packages/daemon/src/tui/mirror/pane-mirror.ts +81 -0
  136. package/packages/daemon/src/tui/mirror/viewer.tsx +166 -0
  137. package/packages/daemon/src/tui/team/CONTROL.md +50 -0
  138. package/packages/daemon/src/tui/team/entry.ts +11 -0
  139. package/packages/daemon/src/tui/team/fuzzy.ts +133 -0
  140. package/packages/daemon/src/tui/team/home.ts +170 -0
  141. package/packages/daemon/src/tui/team/index.tsx +1521 -0
  142. package/packages/daemon/src/tui/team/input.ts +34 -0
  143. package/packages/daemon/src/tui/team/keymap.ts +127 -0
  144. package/packages/daemon/src/tui/team/mouse.ts +29 -0
  145. package/packages/daemon/src/tui/team/nav.ts +31 -0
  146. package/packages/daemon/src/tui/team/preview.ts +34 -0
  147. package/packages/daemon/src/tui/team/projects.ts +191 -0
  148. package/packages/daemon/src/tui/team/report.ts +73 -0
  149. package/packages/daemon/src/tui/team/sessions.ts +344 -0
  150. package/packages/daemon/src/tui/team/tree.ts +62 -0
  151. package/packages/daemon/src/types.ts +13 -0
  152. package/packages/daemon/src/ui/index.ts +32 -0
  153. package/packages/daemon/src/ui/terminal/index.ts +9 -0
  154. package/packages/daemon/src/ui/types.ts +91 -0
  155. package/packages/daemon/src/ui/web/base.css +80 -0
  156. package/packages/daemon/src/ui/web/components/Box.tsx +59 -0
  157. package/packages/daemon/src/ui/web/components/Input.tsx +32 -0
  158. package/packages/daemon/src/ui/web/components/ScrollBox.tsx +60 -0
  159. package/packages/daemon/src/ui/web/components/Text.tsx +28 -0
  160. package/packages/daemon/src/ui/web/hooks.ts +106 -0
  161. package/packages/daemon/src/ui/web/index.ts +27 -0
  162. package/packages/daemon/src/ui/web/render.ts +77 -0
  163. package/packages/daemon/src/ui/web/utils/color.ts +27 -0
  164. package/packages/daemon/src/validate.ts +217 -0
  165. package/packages/daemon/src/widgets/changes/README.md +3 -0
  166. package/packages/daemon/src/widgets/changes/index.tsx +691 -0
  167. package/packages/daemon/src/widgets/config/README.md +3 -0
  168. package/packages/daemon/src/widgets/config/index.tsx +481 -0
  169. package/packages/daemon/src/widgets/explorer/README.md +3 -0
  170. package/packages/daemon/src/widgets/explorer/breadcrumbs.tsx +77 -0
  171. package/packages/daemon/src/widgets/explorer/footer.tsx +20 -0
  172. package/packages/daemon/src/widgets/explorer/header.tsx +23 -0
  173. package/packages/daemon/src/widgets/explorer/index.tsx +456 -0
  174. package/packages/daemon/src/widgets/explorer/tree-model.ts +103 -0
  175. package/packages/daemon/src/widgets/explorer/tree.tsx +165 -0
  176. package/packages/daemon/src/widgets/lib/config-model.ts +116 -0
  177. package/packages/daemon/src/widgets/lib/files.ts +88 -0
  178. package/packages/daemon/src/widgets/lib/git.ts +88 -0
  179. package/packages/daemon/src/widgets/lib/grammar.ts +126 -0
  180. package/packages/daemon/src/widgets/lib/help-overlay.tsx +101 -0
  181. package/packages/daemon/src/widgets/lib/pane-comms.ts +209 -0
  182. package/packages/daemon/src/widgets/lib/theme.ts +194 -0
  183. package/packages/daemon/src/widgets/lib/watcher.ts +132 -0
  184. package/packages/daemon/src/widgets/preview/README.md +3 -0
  185. package/packages/daemon/src/widgets/preview/index.tsx +416 -0
  186. package/packages/daemon/src/widgets/resolve.ts +121 -0
  187. package/packages/daemon/src/widgets/setup/README.md +3 -0
  188. package/packages/daemon/src/widgets/setup/agent-naming.tsx +112 -0
  189. package/packages/daemon/src/widgets/setup/config-tree.tsx +246 -0
  190. package/packages/daemon/src/widgets/setup/detect-panel.tsx +72 -0
  191. package/packages/daemon/src/widgets/setup/field-editor.tsx +265 -0
  192. package/packages/daemon/src/widgets/setup/footer.tsx +107 -0
  193. package/packages/daemon/src/widgets/setup/index.tsx +341 -0
  194. package/packages/daemon/src/widgets/setup/layout-picker.tsx +96 -0
  195. package/packages/daemon/src/widgets/setup/orchestrator-panel.tsx +200 -0
  196. package/packages/daemon/src/widgets/setup/review-panel.tsx +140 -0
  197. package/packages/daemon/src/widgets/setup/setup-model.ts +188 -0
  198. package/packages/daemon/src/widgets/sidebar/index.tsx +527 -0
  199. package/packages/tmux-bridge/package.json +22 -0
  200. package/packages/tmux-bridge/src/errors.ts +28 -0
  201. package/packages/tmux-bridge/src/index.ts +31 -0
  202. package/packages/tmux-bridge/src/monitor.ts +77 -0
  203. package/packages/tmux-bridge/src/panes.ts +136 -0
  204. package/packages/tmux-bridge/src/runner.test.ts +501 -0
  205. package/packages/tmux-bridge/src/runner.ts +91 -0
  206. package/packages/tmux-bridge/src/sessions.ts +126 -0
  207. package/packages/tmux-bridge/src/targeting.test.ts +107 -0
  208. package/packages/tmux-bridge/src/targeting.ts +90 -0
  209. package/scripts/postinstall.js +26 -2
@@ -0,0 +1,32 @@
1
+ import type { JSX } from "solid-js";
2
+ import { rgbaToCSS } from "../utils/color.ts";
3
+ import type { InputProps } from "../../types.ts";
4
+
5
+ export function Input(props: InputProps) {
6
+ const style = (): JSX.CSSProperties => ({
7
+ "font-family": "inherit",
8
+ "font-size": "inherit",
9
+ "line-height": "inherit",
10
+ background: props.focusedBackgroundColor
11
+ ? rgbaToCSS(props.focusedBackgroundColor)
12
+ : "rgb(25,25,35)",
13
+ color: props.focusedTextColor ? rgbaToCSS(props.focusedTextColor) : "inherit",
14
+ border: "none",
15
+ outline: "none",
16
+ padding: "0 1ch",
17
+ width: "100%",
18
+ "caret-color": props.cursorColor ? rgbaToCSS(props.cursorColor) : "inherit",
19
+ });
20
+
21
+ return (
22
+ <input
23
+ type="text"
24
+ value={props.value ?? ""}
25
+ placeholder={props.placeholder}
26
+ style={style()}
27
+ onInput={(e) => props.onInput?.(e.currentTarget.value)}
28
+ onMouseDown={props.onMouseDown}
29
+ ref={props.ref}
30
+ />
31
+ );
32
+ }
@@ -0,0 +1,60 @@
1
+ import type { ParentProps, JSX } from "solid-js";
2
+ import { rgbaToCSS } from "../utils/color.ts";
3
+ import type { ScrollBoxProps } from "../../types.ts";
4
+
5
+ export function ScrollBox(props: ParentProps<ScrollBoxProps>) {
6
+ const style = (): JSX.CSSProperties => ({
7
+ display: "flex",
8
+ "flex-direction": props.flexDirection ?? "column",
9
+ "flex-grow": props.flexGrow,
10
+ "flex-shrink": props.flexShrink,
11
+ gap: props.gap !== undefined ? `${props.gap}ch` : undefined,
12
+ "justify-content": props.justifyContent,
13
+ "align-items": props.alignItems,
14
+ position: props.position ?? "relative",
15
+ overflow: "hidden",
16
+ "overflow-y": "auto",
17
+ width: typeof props.width === "number" ? `${props.width}ch` : props.width,
18
+ height:
19
+ typeof props.height === "number"
20
+ ? `calc(${props.height} * var(--line-height))`
21
+ : props.height,
22
+ "max-width": typeof props.maxWidth === "number" ? `${props.maxWidth}ch` : props.maxWidth,
23
+ "max-height":
24
+ typeof props.maxHeight === "number"
25
+ ? `calc(${props.maxHeight} * var(--line-height))`
26
+ : props.maxHeight,
27
+ padding:
28
+ props.padding !== undefined
29
+ ? `calc(${props.padding} * var(--line-height)) ${props.padding}ch`
30
+ : undefined,
31
+ "padding-left": props.paddingLeft !== undefined ? `${props.paddingLeft}ch` : undefined,
32
+ "padding-right": props.paddingRight !== undefined ? `${props.paddingRight}ch` : undefined,
33
+ "padding-top":
34
+ props.paddingTop !== undefined ? `calc(${props.paddingTop} * var(--line-height))` : undefined,
35
+ "padding-bottom":
36
+ props.paddingBottom !== undefined
37
+ ? `calc(${props.paddingBottom} * var(--line-height))`
38
+ : undefined,
39
+ background: props.backgroundColor ? rgbaToCSS(props.backgroundColor) : undefined,
40
+ "border-color": props.borderColor ? rgbaToCSS(props.borderColor) : undefined,
41
+ top: props.top !== undefined ? `calc(${props.top} * var(--line-height))` : undefined,
42
+ left: props.left !== undefined ? `${props.left}ch` : undefined,
43
+ right: props.right !== undefined ? `${props.right}ch` : undefined,
44
+ bottom: props.bottom !== undefined ? `calc(${props.bottom} * var(--line-height))` : undefined,
45
+ });
46
+
47
+ return (
48
+ <div
49
+ id={props.id}
50
+ style={style()}
51
+ onMouseDown={props.onMouseDown}
52
+ onMouseUp={props.onMouseUp}
53
+ onMouseMove={props.onMouseMove}
54
+ onMouseOver={props.onMouseOver}
55
+ ref={props.ref}
56
+ >
57
+ {props.children}
58
+ </div>
59
+ );
60
+ }
@@ -0,0 +1,28 @@
1
+ import type { ParentProps, JSX } from "solid-js";
2
+ import { rgbaToCSS, attributesToStyle } from "../utils/color.ts";
3
+ import type { TextProps } from "../../types.ts";
4
+
5
+ export function Text(props: ParentProps<TextProps>) {
6
+ const style = (): JSX.CSSProperties => ({
7
+ color: props.fg ? rgbaToCSS(props.fg) : undefined,
8
+ background: props.bg ? rgbaToCSS(props.bg) : undefined,
9
+ "white-space":
10
+ props.wrapMode === "none" ? "nowrap" : props.wrapMode === "word" ? "pre-wrap" : undefined,
11
+ overflow: props.wrapMode === "none" ? "hidden" : undefined,
12
+ "word-break": props.wrapMode === "word" ? "break-word" : undefined,
13
+ width: typeof props.width === "number" ? `${props.width}ch` : props.width,
14
+ height:
15
+ typeof props.height === "number"
16
+ ? `calc(${props.height} * var(--line-height))`
17
+ : props.height,
18
+ "flex-grow": props.flexGrow,
19
+ "flex-shrink": props.flexShrink,
20
+ ...attributesToStyle(props.attributes ?? 0),
21
+ });
22
+
23
+ return (
24
+ <span style={style()} onMouseUp={props.onMouseUp} onMouseDown={props.onMouseDown}>
25
+ {props.children}
26
+ </span>
27
+ );
28
+ }
@@ -0,0 +1,106 @@
1
+ import { createSignal, onMount, onCleanup } from "solid-js";
2
+
3
+ /** Synthetic key event passed to handlers (mirrors OpenTUI naming). */
4
+ export interface UiKeyEvent {
5
+ name: string;
6
+ ctrl: boolean;
7
+ shift: boolean;
8
+ alt: boolean;
9
+ meta: boolean;
10
+ preventDefault: () => void;
11
+ stopPropagation: () => void;
12
+ }
13
+
14
+ export function useKeyboard(handler: (evt: UiKeyEvent) => void): void {
15
+ onMount(() => {
16
+ const listener = (e: KeyboardEvent) => {
17
+ const evt = {
18
+ name: mapKeyName(e),
19
+ ctrl: e.ctrlKey,
20
+ shift: e.shiftKey,
21
+ alt: e.altKey,
22
+ meta: e.metaKey,
23
+ preventDefault: () => e.preventDefault(),
24
+ stopPropagation: () => e.stopPropagation(),
25
+ };
26
+ handler(evt);
27
+ };
28
+ document.addEventListener("keydown", listener);
29
+ onCleanup(() => document.removeEventListener("keydown", listener));
30
+ });
31
+ }
32
+
33
+ function mapKeyName(e: KeyboardEvent): string {
34
+ switch (e.key) {
35
+ case "ArrowUp":
36
+ return "up";
37
+ case "ArrowDown":
38
+ return "down";
39
+ case "ArrowLeft":
40
+ return "left";
41
+ case "ArrowRight":
42
+ return "right";
43
+ case "Enter":
44
+ return "return";
45
+ case "Escape":
46
+ return "escape";
47
+ case "Backspace":
48
+ return "backspace";
49
+ case "Tab":
50
+ return "tab";
51
+ case "Delete":
52
+ return "delete";
53
+ case " ":
54
+ return "space";
55
+ case "Home":
56
+ return "home";
57
+ case "End":
58
+ return "end";
59
+ case "PageUp":
60
+ return "pageup";
61
+ case "PageDown":
62
+ return "pagedown";
63
+ case "Insert":
64
+ return "insert";
65
+ default:
66
+ // @opentui convention: single-char keys are always lowercase, with shift as a modifier.
67
+ // Browser gives uppercase e.key for Shift+letter (e.g. "G"), so normalize to lowercase.
68
+ if (e.key.length === 1) return e.key.toLowerCase();
69
+ return e.key;
70
+ }
71
+ }
72
+
73
+ export function useTerminalDimensions() {
74
+ const [dims, setDims] = createSignal({ width: 80, height: 24 });
75
+
76
+ onMount(() => {
77
+ const update = () => {
78
+ // Measure a ch unit by using a temporary element
79
+ const measure = document.createElement("span");
80
+ measure.style.position = "absolute";
81
+ measure.style.visibility = "hidden";
82
+ measure.style.fontFamily = "var(--font-family, 'IBM Plex Mono', ui-monospace, monospace)";
83
+ measure.style.fontSize = "var(--font-size, 13px)";
84
+ measure.textContent = "0";
85
+ document.body.appendChild(measure);
86
+ const chWidth = measure.getBoundingClientRect().width;
87
+ document.body.removeChild(measure);
88
+
89
+ const lh = parseFloat(getComputedStyle(document.documentElement).lineHeight);
90
+
91
+ const effectiveCh = chWidth > 0 ? chWidth : 7.8; // fallback
92
+ const effectiveLh = lh > 0 ? lh : 18.2; // fallback
93
+
94
+ setDims({
95
+ width: Math.floor(window.innerWidth / effectiveCh),
96
+ height: Math.floor(window.innerHeight / effectiveLh),
97
+ });
98
+ };
99
+
100
+ update();
101
+ window.addEventListener("resize", update);
102
+ onCleanup(() => window.removeEventListener("resize", update));
103
+ });
104
+
105
+ return dims;
106
+ }
@@ -0,0 +1,27 @@
1
+ // Web mirror components — lowercase exports match @opentui JSX element names
2
+ export { Box as box } from "./components/Box.tsx";
3
+ export { Text as text } from "./components/Text.tsx";
4
+ export { ScrollBox as scrollbox } from "./components/ScrollBox.tsx";
5
+ export { Input as input } from "./components/Input.tsx";
6
+
7
+ // Render and hooks
8
+ export { render } from "./render.ts";
9
+ export { useKeyboard, useTerminalDimensions } from "./hooks.ts";
10
+
11
+ // Re-export Solid.js primitives
12
+ export {
13
+ Show,
14
+ For,
15
+ Switch,
16
+ Match,
17
+ createSignal,
18
+ createMemo,
19
+ createEffect,
20
+ onMount,
21
+ onCleanup,
22
+ batch,
23
+ } from "solid-js";
24
+
25
+ // Re-export shared types
26
+ export { RGBA, TextAttributes } from "../types.ts";
27
+ export type { BoxProps, TextProps, ScrollBoxProps, InputProps } from "../types.ts";
@@ -0,0 +1,77 @@
1
+ import type { JSX } from "solid-js";
2
+ import { render as solidRender } from "solid-js/web";
3
+
4
+ const BASE_CSS = `
5
+ :root {
6
+ --line-height: 1.4em;
7
+ --font-family: 'IBM Plex Mono', ui-monospace, monospace;
8
+ --font-size: 13px;
9
+ }
10
+
11
+ body, #root {
12
+ font-family: var(--font-family);
13
+ font-size: var(--font-size);
14
+ line-height: var(--line-height);
15
+ background: rgb(19, 16, 16);
16
+ color: rgb(232, 228, 228);
17
+ margin: 0;
18
+ padding: 0;
19
+ -webkit-font-smoothing: antialiased;
20
+ }
21
+
22
+ * {
23
+ box-sizing: border-box;
24
+ }
25
+
26
+ div {
27
+ display: flex;
28
+ flex-direction: column;
29
+ min-width: 0;
30
+ min-height: 0;
31
+ }
32
+
33
+ span {
34
+ font-family: inherit;
35
+ font-size: inherit;
36
+ line-height: inherit;
37
+ }
38
+
39
+ input {
40
+ font-family: inherit;
41
+ font-size: inherit;
42
+ line-height: inherit;
43
+ }
44
+
45
+ ::-webkit-scrollbar {
46
+ width: 8px;
47
+ }
48
+
49
+ ::-webkit-scrollbar-track {
50
+ background: rgb(25, 25, 35);
51
+ }
52
+
53
+ ::-webkit-scrollbar-thumb {
54
+ background: rgb(60, 60, 80);
55
+ border-radius: 4px;
56
+ }
57
+
58
+ ::-webkit-scrollbar-thumb:hover {
59
+ background: rgb(80, 80, 100);
60
+ }
61
+ `;
62
+
63
+ let stylesInjected = false;
64
+
65
+ function injectBaseStyles(): void {
66
+ if (stylesInjected) return;
67
+ const style = document.createElement("style");
68
+ style.textContent = BASE_CSS;
69
+ document.head.appendChild(style);
70
+ stylesInjected = true;
71
+ }
72
+
73
+ export function render(component: () => JSX.Element, _options?: unknown): void {
74
+ injectBaseStyles();
75
+ const root = document.getElementById("root") ?? document.body;
76
+ solidRender(component, root);
77
+ }
@@ -0,0 +1,27 @@
1
+ import type { RGBA } from "../../types.ts";
2
+
3
+ export function rgbaToCSS(color: RGBA): string {
4
+ if (color.a === 0) return "transparent";
5
+ if (color.a === 255 || color.a === undefined) {
6
+ return `rgb(${color.r},${color.g},${color.b})`;
7
+ }
8
+ return `rgba(${color.r},${color.g},${color.b},${(color.a / 255).toFixed(2)})`;
9
+ }
10
+
11
+ export function attributesToStyle(attrs: number): Record<string, string> {
12
+ const style: Record<string, string> = {};
13
+ if (attrs & 1) style["font-weight"] = "bold"; // BOLD
14
+ if (attrs & 2) style.opacity = "0.6"; // DIM
15
+ if (attrs & 4) style["font-style"] = "italic"; // ITALIC
16
+
17
+ // Compose text-decoration: underline and line-through can coexist
18
+ const decorations: string[] = [];
19
+ if (attrs & 8) decorations.push("underline"); // UNDERLINE
20
+ if (attrs & 128) decorations.push("line-through"); // STRIKETHROUGH
21
+ if (decorations.length > 0) style["text-decoration"] = decorations.join(" ");
22
+
23
+ if (attrs & 16) style.animation = "blink 1s step-end infinite"; // BLINK
24
+ if (attrs & 32) style.filter = "invert(1)"; // INVERSE
25
+ if (attrs & 64) style.visibility = "hidden"; // HIDDEN
26
+ return style;
27
+ }
@@ -0,0 +1,217 @@
1
+ import { resolve } from "node:path";
2
+ import { readConfig } from "./lib/yaml-io.ts";
3
+ import { outputError } from "./lib/output.ts";
4
+ import { IdeConfigSchema } from "./schemas/ide-config.ts";
5
+
6
+ /** Minimal shape of Zod parse issues (v3/v4 compatible for our mappers). */
7
+ interface ZodLikeIssue {
8
+ path?: (string | number)[];
9
+ code?: string;
10
+ message?: string;
11
+ expected?: string;
12
+ received?: string;
13
+ }
14
+
15
+ export function validateConfig(config: unknown): string[] {
16
+ if (config == null || typeof config !== "object" || Array.isArray(config)) {
17
+ return ["config must be an object"];
18
+ }
19
+
20
+ const result = IdeConfigSchema.safeParse(config);
21
+
22
+ if (!result.success) {
23
+ return result.error.issues.map((issue) => mapZodIssue(issue as ZodLikeIssue, config));
24
+ }
25
+
26
+ // Phase 2: semantic checks (only when schema parse succeeds)
27
+ const errors: string[] = [];
28
+ const cfg = result.data;
29
+
30
+ // 1. Row sizes sum ≤ 100%
31
+ const rowSizes = cfg.rows.filter((r) => r.size !== undefined).map((r) => parseInt(r.size!, 10));
32
+ const rowSum = rowSizes.reduce((a, b) => a + b, 0);
33
+ if (rowSum > 100) {
34
+ errors.push(`Row sizes sum to ${rowSum}%, which exceeds 100%`);
35
+ }
36
+
37
+ for (let i = 0; i < cfg.rows.length; i++) {
38
+ const row = cfg.rows[i]!;
39
+
40
+ // 2. Per-row pane sizes sum ≤ 100%
41
+ const paneSizes = row.panes
42
+ .filter((p) => p.size !== undefined)
43
+ .map((p) => parseInt(p.size!, 10));
44
+ const paneSum = paneSizes.reduce((a, b) => a + b, 0);
45
+ if (paneSum > 100) {
46
+ errors.push(`Row ${i} pane sizes sum to ${paneSum}%, which exceeds 100%`);
47
+ }
48
+
49
+ // 3. Max 1 focus:true per row
50
+ const focusCount = row.panes.filter((p) => p.focus === true).length;
51
+ if (focusCount > 1) {
52
+ errors.push(`Row ${i} has ${focusCount} panes with focus: true (max 1)`);
53
+ }
54
+
55
+ // 4. type and command cannot coexist
56
+ for (let j = 0; j < row.panes.length; j++) {
57
+ const pane = row.panes[j]!;
58
+ if (pane.type !== undefined && pane.command !== undefined) {
59
+ errors.push(`rows[${i}].panes[${j}] cannot have both 'type' and 'command'`);
60
+ }
61
+ }
62
+ }
63
+
64
+ return errors;
65
+ }
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Zod issue → legacy error string mapping
69
+ // ---------------------------------------------------------------------------
70
+
71
+ function formatPath(path: (string | number)[]): string {
72
+ let result = "";
73
+ for (let i = 0; i < path.length; i++) {
74
+ const seg = path[i]!;
75
+ if (typeof seg === "number") {
76
+ result += `[${seg}]`;
77
+ } else if (i === 0) {
78
+ result += seg;
79
+ } else {
80
+ result += `.${seg}`;
81
+ }
82
+ }
83
+ return result;
84
+ }
85
+
86
+ function shouldQuote(path: (string | number)[]): boolean {
87
+ if (path.length === 1 && typeof path[0] === "string") return true;
88
+ if (path[0] === "team") return true;
89
+ return false;
90
+ }
91
+
92
+ function isSizePath(path: (string | number)[]): boolean {
93
+ return path[path.length - 1] === "size";
94
+ }
95
+
96
+ function isEnvValuePath(path: (string | number)[]): boolean {
97
+ const envIdx = path.indexOf("env");
98
+ return envIdx >= 0 && envIdx < path.length - 1;
99
+ }
100
+
101
+ function getValueAtPath(obj: unknown, path: (string | number)[]): unknown {
102
+ let current: unknown = obj;
103
+ for (const seg of path) {
104
+ if (current == null || typeof current !== "object") return undefined;
105
+ current = (current as Record<string, unknown>)[String(seg)];
106
+ }
107
+ return current;
108
+ }
109
+
110
+ const MS_FIELDS = new Set(["stall_timeout", "poll_interval"]);
111
+
112
+ function typeDesc(path: (string | number)[], expected: string): string {
113
+ const base = expected.replace(/\s*\|\s*undefined/g, "").trim();
114
+ let desc: string;
115
+ if (base === "string") desc = "a string";
116
+ else if (base === "boolean") desc = "a boolean";
117
+ else if (base === "number") desc = "a number";
118
+ else if (base === "array") desc = "an array";
119
+ else if (base === "object" || base === "record") desc = "an object";
120
+ else desc = base;
121
+
122
+ const field = path[path.length - 1];
123
+ if (path[0] === "orchestrator" && typeof field === "string" && MS_FIELDS.has(field)) {
124
+ return `${desc} (ms)`;
125
+ }
126
+ return desc;
127
+ }
128
+
129
+ function mapZodIssue(issue: ZodLikeIssue, config: unknown): string {
130
+ const path: (string | number)[] = issue.path ?? [];
131
+ const code: string = issue.code ?? "";
132
+ const rawPath = formatPath(path);
133
+ const display = shouldQuote(path) ? `'${rawPath}'` : rawPath;
134
+ const lastSeg = path[path.length - 1];
135
+
136
+ // Env value: rows[i].panes[j].env.KEY
137
+ if (isEnvValuePath(path)) {
138
+ return `${formatPath(path)} must be a string or number`;
139
+ }
140
+
141
+ // Size field: regex or refine failure (but not a type error like size: 42)
142
+ if (isSizePath(path) && code !== "invalid_type") {
143
+ if (code === "custom") {
144
+ return `${rawPath} must not exceed 100%`;
145
+ }
146
+ const val = getValueAtPath(config, path);
147
+ return `${rawPath} "${val}" must be a percentage (e.g. "50%")`;
148
+ }
149
+
150
+ // Empty array (too_small from .min(1))
151
+ if (code === "too_small") {
152
+ return `${display} must not be empty`;
153
+ }
154
+
155
+ // Enum: pane type
156
+ if (code === "invalid_value" && lastSeg === "type" && path.includes("panes")) {
157
+ return `${rawPath} must be one of: explorer, changes, preview, tasks, costs, config, mission-control`;
158
+ }
159
+
160
+ // Enum: pane role
161
+ if (code === "invalid_value" && lastSeg === "role") {
162
+ return `${rawPath} must be "lead", "teammate", or "planner"`;
163
+ }
164
+
165
+ // Enum: dispatch_mode
166
+ if (code === "invalid_value" && lastSeg === "dispatch_mode") {
167
+ return `${rawPath} must be "tasks" or "goals"`;
168
+ }
169
+
170
+ // Team.name missing vs wrong type
171
+ if (path.length === 2 && path[0] === "team" && path[1] === "name") {
172
+ if (issue.received === "undefined") {
173
+ return "'team.name' is required when team is specified";
174
+ }
175
+ }
176
+
177
+ // Type mismatch
178
+ if (code === "invalid_type") {
179
+ return `${display} must be ${typeDesc(path, issue.expected ?? "")}`;
180
+ }
181
+
182
+ // Fallback
183
+ return `${display}: ${issue.message ?? "invalid value"}`;
184
+ }
185
+
186
+ export async function validate(
187
+ targetDir: string | undefined,
188
+ { json }: { json?: boolean } = {},
189
+ ): Promise<void> {
190
+ const dir = resolve(targetDir ?? ".");
191
+ let config;
192
+
193
+ try {
194
+ ({ config } = readConfig(dir));
195
+ } catch (e) {
196
+ outputError(`Cannot read ide.yml: ${(e as Error).message}`, "READ_ERROR");
197
+ return;
198
+ }
199
+
200
+ const errors = validateConfig(config);
201
+ const valid = errors.length === 0;
202
+
203
+ if (json) {
204
+ console.log(JSON.stringify({ valid, errors }, null, 2));
205
+ return;
206
+ }
207
+
208
+ if (valid) {
209
+ console.log("✓ ide.yml is valid");
210
+ } else {
211
+ console.log("✗ ide.yml has errors:");
212
+ for (const e of errors) {
213
+ console.log(` - ${e}`);
214
+ }
215
+ process.exitCode = 1;
216
+ }
217
+ }
@@ -0,0 +1,3 @@
1
+ # `changes` — daemon TUI widget
2
+
3
+ Git diff viewer for the working tree. See [ARCHITECTURE.md §3](../../../../../ARCHITECTURE.md#§3--package-map) and the [`changes` row in docs/widget-index.md](../../../../../docs/widget-index.md#daemon-tui-widgets-8).