@bastani/atomic 0.9.19-alpha.5 → 0.9.19-alpha.7

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/CHANGELOG.md +32 -0
  2. package/README.md +2 -2
  3. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  4. package/dist/builtin/intercom/broker/client.ts +19 -1
  5. package/dist/builtin/intercom/index.bundle.mjs +8 -1
  6. package/dist/builtin/intercom/package.json +1 -1
  7. package/dist/builtin/mcp/package.json +1 -1
  8. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  9. package/dist/builtin/subagents/README.md +3 -1
  10. package/dist/builtin/subagents/agents/codebase-online-researcher.md +7 -7
  11. package/dist/builtin/subagents/package.json +1 -1
  12. package/dist/builtin/subagents/skills/subagent/SKILL.md +3 -1
  13. package/dist/builtin/subagents/src/extension/index.bundle.mjs +2 -1
  14. package/dist/builtin/web-access/CHANGELOG.md +13 -0
  15. package/dist/builtin/web-access/README.md +31 -24
  16. package/dist/builtin/web-access/index.bundle.mjs +16121 -80
  17. package/dist/builtin/web-access/package.json +2 -1
  18. package/dist/builtin/workflows/CHANGELOG.md +15 -0
  19. package/dist/builtin/workflows/README.md +1 -1
  20. package/dist/builtin/workflows/builtin/adversarial-verification.js +2 -2
  21. package/dist/builtin/workflows/builtin/{chunk-vhmnzdj9.js → chunk-1jth021m.js} +1 -1
  22. package/dist/builtin/workflows/builtin/{chunk-p8jjwfg6.js → chunk-2377r57g.js} +4 -2
  23. package/dist/builtin/workflows/builtin/{chunk-hbnwv56f.js → chunk-31j2gs7h.js} +2 -2
  24. package/dist/builtin/workflows/builtin/{chunk-n8zysf1t.js → chunk-ngkqkzej.js} +1 -1
  25. package/dist/builtin/workflows/builtin/{chunk-1nbq96fh.js → chunk-nkrafh9s.js} +1 -1
  26. package/dist/builtin/workflows/builtin/{chunk-1kx4aggw.js → chunk-wf741bv3.js} +2 -2
  27. package/dist/builtin/workflows/builtin/{chunk-t6x2d78s.js → chunk-z29qbehr.js} +1 -1
  28. package/dist/builtin/workflows/builtin/generate-and-filter.js +2 -2
  29. package/dist/builtin/workflows/builtin/goal.js +3 -3
  30. package/dist/builtin/workflows/builtin/index.js +7 -7
  31. package/dist/builtin/workflows/builtin/ralph.js +3 -3
  32. package/dist/builtin/workflows/builtin/tournament.js +2 -2
  33. package/dist/builtin/workflows/package.json +1 -1
  34. package/dist/builtin/workflows/src/extension/index.bundle.mjs +339 -31
  35. package/dist/builtin/workflows/src/index.js +67 -19
  36. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +2 -0
  37. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  38. package/dist/core/agent-session-tool-registry.js +10 -0
  39. package/dist/core/agent-session-tool-registry.js.map +1 -1
  40. package/dist/core/bash-executor.js.map +1 -1
  41. package/dist/core/tools/bash.d.ts +1 -1
  42. package/dist/core/tools/bash.d.ts.map +1 -1
  43. package/dist/core/tools/bash.js +3 -2
  44. package/dist/core/tools/bash.js.map +1 -1
  45. package/dist/core/tools/edit.d.ts +3 -0
  46. package/dist/core/tools/edit.d.ts.map +1 -1
  47. package/dist/core/tools/edit.js +91 -12
  48. package/dist/core/tools/edit.js.map +1 -1
  49. package/dist/core/tools/file-mutation-coordinator.d.ts +255 -0
  50. package/dist/core/tools/file-mutation-coordinator.d.ts.map +1 -0
  51. package/dist/core/tools/file-mutation-coordinator.js +300 -0
  52. package/dist/core/tools/file-mutation-coordinator.js.map +1 -0
  53. package/dist/core/tools/file-mutation-queue.d.ts +14 -1
  54. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  55. package/dist/core/tools/file-mutation-queue.js +15 -3
  56. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  57. package/dist/core/tools/hashline.d.ts +9 -0
  58. package/dist/core/tools/hashline.d.ts.map +1 -1
  59. package/dist/core/tools/hashline.js +12 -1
  60. package/dist/core/tools/hashline.js.map +1 -1
  61. package/dist/core/tools/index.d.ts +9 -1
  62. package/dist/core/tools/index.d.ts.map +1 -1
  63. package/dist/core/tools/index.js +67 -12
  64. package/dist/core/tools/index.js.map +1 -1
  65. package/dist/core/tools/non-minting-snapshot-store.d.ts +49 -0
  66. package/dist/core/tools/non-minting-snapshot-store.d.ts.map +1 -0
  67. package/dist/core/tools/non-minting-snapshot-store.js +64 -0
  68. package/dist/core/tools/non-minting-snapshot-store.js.map +1 -0
  69. package/dist/core/tools/powershell.d.ts +2 -2
  70. package/dist/core/tools/powershell.d.ts.map +1 -1
  71. package/dist/core/tools/powershell.js +2 -5
  72. package/dist/core/tools/powershell.js.map +1 -1
  73. package/dist/core/tools/read-document-extract.js.map +1 -1
  74. package/dist/core/tools/render-utils.js.map +1 -1
  75. package/dist/core/tools/write.d.ts +30 -1
  76. package/dist/core/tools/write.d.ts.map +1 -1
  77. package/dist/core/tools/write.js +97 -8
  78. package/dist/core/tools/write.js.map +1 -1
  79. package/dist/index.d.ts +1 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js.map +1 -1
  82. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  83. package/dist/modes/interactive/components/task-live-transcript.d.ts +1 -1
  84. package/dist/modes/interactive/components/task-live-transcript.d.ts.map +1 -1
  85. package/dist/modes/interactive/components/task-live-transcript.js +19 -11
  86. package/dist/modes/interactive/components/task-live-transcript.js.map +1 -1
  87. package/dist/modes/interactive/interactive-mode-base.d.ts +1 -0
  88. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  89. package/dist/modes/interactive/interactive-mode-base.js +3 -1
  90. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  91. package/dist/modes/interactive/interactive-mode-surface.d.ts +1 -1
  92. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  93. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  94. package/dist/modes/interactive/interactive-render-chat.js +28 -4
  95. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  96. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  97. package/dist/modes/interactive/interactive-startup.js +3 -0
  98. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  99. package/dist/modes/interactive-engine/activity-watchdog.d.ts +2 -2
  100. package/dist/modes/interactive-engine/activity-watchdog.d.ts.map +1 -1
  101. package/dist/modes/interactive-engine/activity-watchdog.js.map +1 -1
  102. package/dist/modes/interactive-engine/engine-diagnostic-view.d.ts +3 -1
  103. package/dist/modes/interactive-engine/engine-diagnostic-view.d.ts.map +1 -1
  104. package/dist/modes/interactive-engine/engine-diagnostic-view.js +11 -0
  105. package/dist/modes/interactive-engine/engine-diagnostic-view.js.map +1 -1
  106. package/dist/modes/interactive-engine/isolated-runtime.js +2 -1
  107. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  108. package/dist/modes/rpc/rpc-client-process.d.ts +2 -0
  109. package/dist/modes/rpc/rpc-client-process.d.ts.map +1 -1
  110. package/dist/modes/rpc/rpc-client-process.js +23 -1
  111. package/dist/modes/rpc/rpc-client-process.js.map +1 -1
  112. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  113. package/dist/modes/rpc/rpc-client.js +20 -3
  114. package/dist/modes/rpc/rpc-client.js.map +1 -1
  115. package/dist/utils/markit.d.ts +2 -0
  116. package/dist/utils/markit.d.ts.map +1 -1
  117. package/dist/utils/markit.js +65 -5
  118. package/dist/utils/markit.js.map +1 -1
  119. package/docs/background-tasks.md +15 -11
  120. package/docs/build.md +51 -0
  121. package/docs/changelog.mdx +8 -8
  122. package/docs/compaction/reference.md +267 -0
  123. package/docs/compaction.md +36 -219
  124. package/docs/computer-use.md +35 -0
  125. package/docs/containerization.md +9 -5
  126. package/docs/custom-provider/api-reference.md +145 -0
  127. package/docs/custom-provider/oauth.md +131 -0
  128. package/docs/custom-provider/override.md +34 -0
  129. package/docs/custom-provider/registration.md +163 -0
  130. package/docs/custom-provider/streaming.md +214 -0
  131. package/docs/custom-provider.md +43 -607
  132. package/docs/development.md +87 -0
  133. package/docs/docs.json +199 -76
  134. package/docs/environment-variables.md +1 -1
  135. package/docs/extensions/api-reference.md +1001 -0
  136. package/docs/extensions/authoring.md +676 -0
  137. package/docs/extensions/events.md +793 -0
  138. package/docs/extensions/examples.md +94 -0
  139. package/docs/extensions/ui.md +470 -0
  140. package/docs/extensions.md +270 -2768
  141. package/docs/getting-started/authentication.md +55 -0
  142. package/docs/getting-started/first-session.md +172 -0
  143. package/docs/getting-started/installation.md +154 -0
  144. package/docs/getting-started/project-instructions.md +43 -0
  145. package/docs/guides/configuration.md +65 -0
  146. package/docs/guides/intercom.md +24 -0
  147. package/docs/guides/non-interactive.md +33 -0
  148. package/docs/guides/subagents.md +24 -0
  149. package/docs/guides/workflows.md +27 -0
  150. package/docs/guides.md +41 -0
  151. package/docs/index.md +13 -45
  152. package/docs/intercom/operations.md +178 -0
  153. package/docs/intercom/reference.md +146 -0
  154. package/docs/intercom.md +54 -259
  155. package/docs/json.md +17 -0
  156. package/docs/llama-cpp.md +5 -0
  157. package/docs/models/artificial-analysis-index.md +89 -0
  158. package/docs/models/evals.md +2 -2
  159. package/docs/models/model-selection.md +3 -3
  160. package/docs/models/pareto-efficiency.md +2 -2
  161. package/docs/models/reference.md +601 -0
  162. package/docs/models.md +45 -550
  163. package/docs/packages/authoring.md +88 -0
  164. package/docs/packages/reference.md +43 -0
  165. package/docs/packages.md +28 -107
  166. package/docs/programmatic.md +29 -0
  167. package/docs/prompt-templates.md +11 -0
  168. package/docs/providers/reference.md +23 -0
  169. package/docs/providers.md +10 -15
  170. package/docs/quickstart.md +44 -238
  171. package/docs/reference/cli.md +252 -0
  172. package/docs/reference.md +52 -0
  173. package/docs/rpc/examples.md +64 -0
  174. package/docs/rpc/extension-ui.md +198 -0
  175. package/docs/rpc/protocol.md +1341 -0
  176. package/docs/rpc.md +109 -1346
  177. package/docs/sdk/reference.md +1090 -0
  178. package/docs/sdk.md +56 -1002
  179. package/docs/session-format.md +2 -0
  180. package/docs/sessions.md +6 -10
  181. package/docs/settings.md +6 -45
  182. package/docs/skills/authoring.md +95 -0
  183. package/docs/skills/reference.md +60 -0
  184. package/docs/skills.md +20 -122
  185. package/docs/subagents/authoring.md +37 -0
  186. package/docs/subagents/reference.md +60 -0
  187. package/docs/subagents.md +42 -96
  188. package/docs/terminal-setup.md +1 -1
  189. package/docs/themes/reference.md +176 -0
  190. package/docs/themes.md +21 -135
  191. package/docs/tmux.md +5 -0
  192. package/docs/tools/edit.md +5 -0
  193. package/docs/tools.md +31 -0
  194. package/docs/tui/reference.md +343 -0
  195. package/docs/tui.md +23 -286
  196. package/docs/usage.md +26 -209
  197. package/docs/web-access.md +40 -0
  198. package/docs/windows.md +2 -1
  199. package/docs/workflows/api-reference.md +11 -4
  200. package/docs/workflows/authoring.md +4 -2
  201. package/docs/workflows/builtins.md +2 -2
  202. package/docs/workflows/operations.md +11 -7
  203. package/docs/workflows/reliable-design.md +21 -3
  204. package/docs/workflows/verification.md +8 -0
  205. package/examples/extensions/gondolin/index.ts +116 -148
  206. package/examples/extensions/gondolin/package.json +1 -1
  207. package/examples/extensions/ssh.ts +56 -34
  208. package/npm-shrinkwrap.json +33 -33
  209. package/package.json +4 -4
package/docs/tui.md CHANGED
@@ -1,3 +1,8 @@
1
+ ---
2
+ title: "TUI components"
3
+ description: "Build custom terminal UI for extensions."
4
+ ---
5
+
1
6
  > Atomic can create TUI components. Ask it to build one for your use case.
2
7
 
3
8
  # TUI Components
@@ -6,89 +11,21 @@ Extensions and custom tools can render custom TUI components for interactive use
6
11
 
7
12
  **Source:** TUI components are provided by Atomic's installed `@earendil-works/pi-tui` runtime dependency (`node_modules/@earendil-works/pi-tui/dist/`).
8
13
 
9
- ## Component Interface
10
-
11
- All components implement:
12
-
13
- ```typescript
14
- interface Component {
15
- render(width: number): string[];
16
- handleInput?(data: string): boolean | void;
17
- wantsKeyRelease?: boolean;
18
- invalidate(): void;
19
- }
20
- ```
21
-
22
- | Method | Description |
23
- |--------|-------------|
24
- | `render(width)` | Return array of strings (one per line). Each line **must not exceed `width`**. |
25
- | `handleInput?(data)` | Receive keyboard input when the component has focus. A focused overlay also receives mouse input before the fullscreen viewport. Return `true` when it consumes input; return `false`, `undefined`, or `void` when a matching fullscreen viewport key or overlay mouse event should fall through to viewport handling. Non-overlay focused components leave mouse input with pi-tui so transcript scrolling, scrollbar interaction, and drag selection remain available. |
26
- | `wantsKeyRelease?` | If true, component receives key release events (Kitty protocol). Default: false. |
27
- | `invalidate()` | Clear cached render state. Called on theme changes. |
14
+ ## On this page and its reference
28
15
 
29
- The installed pi-tui type still permits handlers that return `void`; Atomic treats a missing or `undefined` result as unhandled only for a matching fullscreen viewport key or a mouse event deferred to a focused overlay. Components that mutate state for such an input must return `true` so the viewport does not apply it a second time.
16
+ This page covers writing your first component and the common interaction patterns. The component and focusable interfaces, host terminal modes, the host-native session picker and input form, keyboard input, line width, invalidation, debug logging, and performance contracts live in the [TUI API reference](/tui/reference).
30
17
 
31
- Omitting `handleInput` altogether is the same answer as declining: a focused overlay with no handler still lets fullscreen viewport keys and mouse wheel reports reach the transcript, so a notice or progress panel does not freeze scrolling behind it. An asynchronous handler is judged when it settles — only a promise that resolves `true` consumes the input, while `false`, `undefined`, and a rejection all fall through to the viewport. Input that moved focus while such a promise was pending is left to whatever holds focus when it settles.
32
-
33
- The TUI appends a full SGR reset and OSC 8 reset at the end of each rendered line. Styles do not carry across lines. If you emit multi-line text with styling, reapply styles per line or use `wrapTextWithAnsi()` so styles are preserved for each wrapped line.
18
+ ## Component Interface
34
19
 
35
- Bundled MCP tools render their server name in the call header before results arrive. Direct calls use the registered server; gateway calls use the explicit target or an unambiguous match in available metadata or configured prefixes. Unresolved calls still show the tool or operation without guessing a server. This display does not open connections or expose tool arguments.
20
+ Moved to [TUI API reference](/tui/reference#component-interface).
36
21
 
37
22
  ## Focusable Interface (IME Support)
38
23
 
39
- Components that display a text cursor and need IME (Input Method Editor) support should implement the `Focusable` interface:
40
-
41
- ```typescript
42
- import { CURSOR_MARKER, type Component, type Focusable } from "@earendil-works/pi-tui";
43
-
44
- class MyInput implements Component, Focusable {
45
- focused: boolean = false; // Set by TUI when focus changes
46
-
47
- render(width: number): string[] {
48
- const marker = this.focused ? CURSOR_MARKER : "";
49
- // Emit marker right before the fake cursor
50
- return [`> ${beforeCursor}${marker}\x1b[7m${atCursor}\x1b[27m${afterCursor}`];
51
- }
52
- }
53
- ```
54
-
55
- When a `Focusable` component has focus, TUI:
56
- 1. Sets `focused = true` on the component
57
- 2. Scans rendered output for `CURSOR_MARKER` (a zero-width APC escape sequence)
58
- 3. Positions the hardware terminal cursor at that location
59
- 4. Shows the hardware cursor only when `showHardwareCursor` is enabled
60
-
61
- The cursor remains hidden by default. This keeps the fake cursor rendering, while still positioning the hardware cursor for terminals that track IME candidate windows with hidden cursors. Some terminals require a visible hardware cursor for IME positioning; enable it with `showHardwareCursor`, `setShowHardwareCursor(true)`, or `ATOMIC_HARDWARE_CURSOR=1`. The `Editor` and `Input` built-in components already implement this interface.
24
+ Moved to [TUI API reference](/tui/reference#focusable-interface-ime-support).
62
25
 
63
26
  ### Container Components with Embedded Inputs
64
27
 
65
- When a container component (dialog, selector, etc.) contains an `Input` or `Editor` child, the container must implement `Focusable` and propagate the focus state to the child. Otherwise, the hardware cursor won't be positioned correctly for IME input.
66
-
67
- ```typescript
68
- import { Container, type Focusable, Input } from "@earendil-works/pi-tui";
69
-
70
- class SearchDialog extends Container implements Focusable {
71
- private searchInput: Input;
72
-
73
- // Focusable implementation - propagate to child input for IME cursor positioning
74
- private _focused = false;
75
- get focused(): boolean {
76
- return this._focused;
77
- }
78
- set focused(value: boolean) {
79
- this._focused = value;
80
- this.searchInput.focused = value;
81
- }
82
-
83
- constructor() {
84
- super();
85
- this.searchInput = new Input();
86
- this.addChild(this.searchInput);
87
- }
88
- }
89
- ```
90
-
91
- Without this propagation, typing with an IME (Chinese, Japanese, Korean, etc.) will show the candidate window in the wrong position on screen.
28
+ Moved to [TUI API reference](/tui/reference#container-components-with-embedded-inputs).
92
29
 
93
30
  ## Using Components
94
31
 
@@ -117,59 +54,15 @@ In Atomic's default interactive mode, the component instance remains in the isol
117
54
 
118
55
  ### Host terminal modes from an isolated component
119
56
 
120
- Because the component runs in the engine child — whose stdout is the JSONL transport, not a TTY — writing raw terminal escape sequences to `process.stdout` from `render()`/`handleInput()` is a no-op and never reaches the real host terminal. For the host autowrap mode an overlay may need, the factory `tui.terminal` exposes a typed, allowlisted setter that the host applies to the real TTY over the engine protocol:
121
-
122
- ```typescript
123
- await ctx.ui.custom((tui, theme, keybindings, done) => {
124
- tui.terminal.setAutowrap?.(false); // disable autowrap (DECAWM) — Windows terminals only
125
- return new MyOverlay({ onClose: done });
126
- }, { overlay: true });
127
- ```
128
-
129
- This is the only terminal control exposed; arbitrary child bytes are never forwarded to the terminal. The host resets the mode when a component hides, closes, is disposed, or when the engine child crashes or restarts. In fullscreen, pi-tui owns its baseline mouse and autowrap modes; non-isolated overlay fallbacks do not disable that baseline. On regular non-isolated hosts and test seams the setter is absent, and callers may fall back to writing escape sequences to their own `process.stdout`.
57
+ Moved to [TUI API reference](/tui/reference#host-terminal-modes-from-an-isolated-component).
130
58
 
131
59
  ### Host-native session picker
132
60
 
133
- Remote-rendered components pay one host⇄child round trip per keypress under engine isolation. For session-style list pickers, the `ctx.ui.hostSessionPicker(request)` capability avoids that entirely: the terminal host mounts the real built-in `SessionSelectorComponent` and feeds it JSON-safe rows, so arrow-key navigation and search stay host-local and survive extension event-loop stalls. Only semantic events cross the host⇄extension boundary: the extension pushes row `update`s and `error`s (and may `close()` the picker); the host reports selection, cancel, and confirmed Ctrl+D deletes.
134
-
135
- Every interactive host implements the same API — non-isolated mode mounts the selector directly in-process (no IPC at all), isolated mode routes it over the engine session-picker protocol channel — so callers never branch on the mode. The member is absent only on non-interactive surfaces (headless RPC, print); fail with an actionable error there instead of degrading to a hand-rolled picker.
136
-
137
- ```typescript
138
- const picker = ctx.ui.hostSessionPicker?.({
139
- sessions: rows, // HostSessionPickerRow[]: SessionInfo with createdAt/modifiedAt epoch millis
140
- showRenameHint: false,
141
- onDelete: async (path) => {
142
- // Deletion is extension-owned: the host keeps the row until you reply.
143
- const outcome = await remove(path);
144
- if (outcome.ok) picker!.update(rowsWithout(path));
145
- else picker!.error(outcome.message);
146
- },
147
- });
148
- if (!picker) throw new Error("This command requires an interactive session picker");
149
- picker.update(await loadMoreRows()); // merge late rows into the open picker
150
- const path = await picker.result; // selected row's path, or undefined on cancel
151
- ```
152
-
153
- The bundled workflows extension's `/workflow resume` picker is built exclusively on this channel.
61
+ Moved to [TUI API reference](/tui/reference#host-native-session-picker).
154
62
 
155
63
  ### Host-native input form
156
64
 
157
- Use `ctx.ui.hostInputForm(request)` for structured inline forms whose keyboard handling must remain responsive under interactive-engine isolation. The terminal host mounts and focuses the real form in the bottom editor slot (`overlay: false`); Tab/Shift+Tab, arrows, text editing, configured keybindings, Enter, Escape, and Ctrl+C are handled entirely in the host process. In isolated mode only the JSON-safe open request and the final submit/cancel event cross the engine boundary. Non-isolated mode mounts the same component directly.
158
-
159
- ```typescript
160
- const values = await ctx.ui.hostInputForm?.({
161
- title: "Release",
162
- fields: [
163
- { name: "version", type: "string", required: true, initialValue: "" },
164
- { name: "channel", type: "select", choices: ["stable", "beta"], initialValue: "stable" },
165
- ],
166
- });
167
- if (values === undefined) return; // Escape, Ctrl+C, teardown, or close
168
- ```
169
-
170
- Field types are `string`, `text`, `number`, `integer`, `boolean`, and `select`. Initial and returned values are raw strings; the caller owns domain coercion. Every current interactive Atomic host exposes the optional capability, while headless RPC and print surfaces omit it. Keep a legacy fallback only when compatibility with older hosts is required.
171
-
172
- The bundled `/workflow <name>` input picker uses this channel and retains its older custom-editor/`ctx.ui.custom()` paths only as compatibility fallbacks.
65
+ Moved to [TUI API reference](/tui/reference#host-native-input-form).
173
66
 
174
67
  ## Overlays
175
68
 
@@ -335,52 +228,11 @@ const image = new Image(
335
228
 
336
229
  ## Keyboard Input
337
230
 
338
- Use `matchesKey()` for key detection:
339
-
340
- ```typescript
341
- import { matchesKey, Key } from "@earendil-works/pi-tui";
342
-
343
- handleInput(data: string): boolean {
344
- if (matchesKey(data, Key.up)) {
345
- this.selectedIndex--;
346
- return true;
347
- } else if (matchesKey(data, Key.enter)) {
348
- this.onSelect?.(this.selectedIndex);
349
- return true;
350
- } else if (matchesKey(data, Key.escape)) {
351
- this.onCancel?.();
352
- return true;
353
- } else if (matchesKey(data, Key.ctrl("c"))) {
354
- // CTRL+C
355
- return true;
356
- }
357
- return false;
358
- }
359
- ```
360
-
361
- **Key identifiers** (use `Key.*` for autocomplete, or string literals):
362
- - Basic keys: `Key.enter`, `Key.escape`, `Key.tab`, `Key.space`, `Key.backspace`, `Key.delete`, `Key.home`, `Key.end`
363
- - Arrow keys: `Key.up`, `Key.down`, `Key.left`, `Key.right`
364
- - With modifiers: `Key.ctrl("c")`, `Key.shift("tab")`, `Key.alt("left")`, `Key.ctrlShift("p")`
365
- - String format also works: `"enter"`, `"ctrl+c"`, `"shift+tab"`, `"ctrl+shift+p"`
231
+ Moved to [TUI API reference](/tui/reference#keyboard-input).
366
232
 
367
233
  ## Line Width
368
234
 
369
- **Critical:** Each line from `render()` must not exceed the `width` parameter.
370
-
371
- ```typescript
372
- import { visibleWidth, truncateToWidth } from "@earendil-works/pi-tui";
373
-
374
- render(width: number): string[] {
375
- // Truncate long lines
376
- return [truncateToWidth(this.text, width)];
377
- }
378
- ```
379
-
380
- Utilities:
381
- - `visibleWidth(str)` - Get display width (ignores ANSI codes)
382
- - `truncateToWidth(str, width, ellipsis?)` - Truncate with optional ellipsis
383
- - `wrapTextWithAnsi(str, width)` - Word wrap preserving ANSI codes
235
+ Moved to [TUI API reference](/tui/reference#line-width).
384
236
 
385
237
  ## Creating Custom Components
386
238
 
@@ -524,146 +376,31 @@ interface MyTheme {
524
376
 
525
377
  ## Debug logging
526
378
 
527
- Set `PI_TUI_WRITE_LOG` to capture the raw ANSI stream written to stdout. The
528
- variable is read by the vendored `@earendil-works/pi-tui` terminal, so it keeps
529
- its upstream name; a directory path writes one `tui-<timestamp>-<pid>.log` file
530
- per process.
531
-
532
- ```bash
533
- PI_TUI_WRITE_LOG=/tmp/tui-ansi.log atomic
534
- ```
535
-
536
- Atomic vendors TUI components through the installed `@earendil-works/pi-tui` dependency.
379
+ Moved to [TUI API reference](/tui/reference#debug-logging).
537
380
 
538
381
  ## Performance
539
382
 
540
- Cache rendered output when possible:
541
-
542
- ```typescript
543
- class CachedComponent {
544
- private cachedWidth?: number;
545
- private cachedLines?: string[];
546
-
547
- render(width: number): string[] {
548
- if (this.cachedLines && this.cachedWidth === width) {
549
- return this.cachedLines;
550
- }
551
- // ... compute lines ...
552
- this.cachedWidth = width;
553
- this.cachedLines = lines;
554
- return lines;
555
- }
556
-
557
- invalidate(): void {
558
- this.cachedWidth = undefined;
559
- this.cachedLines = undefined;
560
- }
561
- }
562
- ```
563
-
564
- Call `invalidate()` when state changes, then `ctx.ui.requestRender()` from the extension context or `tui.requestRender()` from a `ctx.ui.custom()` factory to trigger re-render.
383
+ Moved to [TUI API reference](/tui/reference#performance).
565
384
 
566
385
  ## Invalidation and Theme Changes
567
386
 
568
- When the theme changes, the TUI calls `invalidate()` on all components to clear their caches. Components must properly implement `invalidate()` to ensure theme changes take effect.
387
+ Moved to [TUI API reference](/tui/reference#invalidation-and-theme-changes).
569
388
 
570
389
  ### The Problem
571
390
 
572
- If a component pre-bakes theme colors into strings (via `theme.fg()`, `theme.bg()`, etc.) and caches them, the cached strings contain ANSI escape codes from the old theme. Simply clearing the render cache isn't enough if the component stores the themed content separately.
573
-
574
- **Wrong approach** (theme colors won't update):
575
-
576
- ```typescript
577
- class BadComponent extends Container {
578
- private content: Text;
579
-
580
- constructor(message: string, theme: Theme) {
581
- super();
582
- // Pre-baked theme colors stored in Text component
583
- this.content = new Text(theme.fg("accent", message), 1, 0);
584
- this.addChild(this.content);
585
- }
586
- // No invalidate override - parent's invalidate only clears
587
- // child render caches, not the pre-baked content
588
- }
589
- ```
391
+ Moved to [TUI API reference](/tui/reference#the-problem).
590
392
 
591
393
  ### The Solution
592
394
 
593
- Components that build content with theme colors must rebuild that content when `invalidate()` is called:
594
-
595
- ```typescript
596
- class GoodComponent extends Container {
597
- private message: string;
598
- private content: Text;
599
-
600
- constructor(message: string) {
601
- super();
602
- this.message = message;
603
- this.content = new Text("", 1, 0);
604
- this.addChild(this.content);
605
- this.updateDisplay();
606
- }
607
-
608
- private updateDisplay(): void {
609
- // Rebuild content with current theme
610
- this.content.setText(theme.fg("accent", this.message));
611
- }
612
-
613
- override invalidate(): void {
614
- super.invalidate(); // Clear child caches
615
- this.updateDisplay(); // Rebuild with new theme
616
- }
617
- }
618
- ```
395
+ Moved to [TUI API reference](/tui/reference#the-solution).
619
396
 
620
397
  ### Pattern: Rebuild on Invalidate
621
398
 
622
- For components with complex content:
623
-
624
- ```typescript
625
- class ComplexComponent extends Container {
626
- private data: SomeData;
627
-
628
- constructor(data: SomeData) {
629
- super();
630
- this.data = data;
631
- this.rebuild();
632
- }
633
-
634
- private rebuild(): void {
635
- this.clear(); // Remove all children
636
-
637
- // Build UI with current theme
638
- this.addChild(new Text(theme.fg("accent", theme.bold("Title")), 1, 0));
639
- this.addChild(new Spacer(1));
640
-
641
- for (const item of this.data.items) {
642
- const color = item.active ? "success" : "muted";
643
- this.addChild(new Text(theme.fg(color, item.label), 1, 0));
644
- }
645
- }
646
-
647
- override invalidate(): void {
648
- super.invalidate();
649
- this.rebuild();
650
- }
651
- }
652
- ```
399
+ Moved to [TUI API reference](/tui/reference#pattern-rebuild-on-invalidate).
653
400
 
654
401
  ### When This Matters
655
402
 
656
- This pattern is needed when:
657
-
658
- 1. **Pre-baking theme colors** - Using `theme.fg()` or `theme.bg()` to create styled strings stored in child components
659
- 2. **Syntax highlighting** - Using `highlightCode()` which applies theme-based syntax colors
660
- 3. **Complex layouts** - Building child component trees that embed theme colors
661
-
662
- This pattern is NOT needed when:
663
-
664
- 1. **Using theme callbacks** - Passing functions like `(text) => theme.fg("accent", text)` that are called during render
665
- 2. **Simple containers** - Just grouping other components without adding themed content
666
- 3. **Stateless render** - Computing themed output fresh in every `render()` call (no caching)
403
+ Moved to [TUI API reference](/tui/reference#when-this-matters).
667
404
 
668
405
  ## Common Patterns
669
406