@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.
- package/CHANGELOG.md +32 -0
- package/README.md +2 -2
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/broker/client.ts +19 -1
- package/dist/builtin/intercom/index.bundle.mjs +8 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +6 -0
- package/dist/builtin/subagents/README.md +3 -1
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +7 -7
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +3 -1
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +2 -1
- package/dist/builtin/web-access/CHANGELOG.md +13 -0
- package/dist/builtin/web-access/README.md +31 -24
- package/dist/builtin/web-access/index.bundle.mjs +16121 -80
- package/dist/builtin/web-access/package.json +2 -1
- package/dist/builtin/workflows/CHANGELOG.md +15 -0
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/builtin/adversarial-verification.js +2 -2
- package/dist/builtin/workflows/builtin/{chunk-vhmnzdj9.js → chunk-1jth021m.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-p8jjwfg6.js → chunk-2377r57g.js} +4 -2
- package/dist/builtin/workflows/builtin/{chunk-hbnwv56f.js → chunk-31j2gs7h.js} +2 -2
- package/dist/builtin/workflows/builtin/{chunk-n8zysf1t.js → chunk-ngkqkzej.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-1nbq96fh.js → chunk-nkrafh9s.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-1kx4aggw.js → chunk-wf741bv3.js} +2 -2
- package/dist/builtin/workflows/builtin/{chunk-t6x2d78s.js → chunk-z29qbehr.js} +1 -1
- package/dist/builtin/workflows/builtin/generate-and-filter.js +2 -2
- package/dist/builtin/workflows/builtin/goal.js +3 -3
- package/dist/builtin/workflows/builtin/index.js +7 -7
- package/dist/builtin/workflows/builtin/ralph.js +3 -3
- package/dist/builtin/workflows/builtin/tournament.js +2 -2
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +339 -31
- package/dist/builtin/workflows/src/index.js +67 -19
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +2 -0
- package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
- package/dist/core/agent-session-tool-registry.js +10 -0
- package/dist/core/agent-session-tool-registry.js.map +1 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/tools/bash.d.ts +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +3 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts +3 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +91 -12
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-coordinator.d.ts +255 -0
- package/dist/core/tools/file-mutation-coordinator.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-coordinator.js +300 -0
- package/dist/core/tools/file-mutation-coordinator.js.map +1 -0
- package/dist/core/tools/file-mutation-queue.d.ts +14 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +15 -3
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/hashline.d.ts +9 -0
- package/dist/core/tools/hashline.d.ts.map +1 -1
- package/dist/core/tools/hashline.js +12 -1
- package/dist/core/tools/hashline.js.map +1 -1
- package/dist/core/tools/index.d.ts +9 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +67 -12
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/non-minting-snapshot-store.d.ts +49 -0
- package/dist/core/tools/non-minting-snapshot-store.d.ts.map +1 -0
- package/dist/core/tools/non-minting-snapshot-store.js +64 -0
- package/dist/core/tools/non-minting-snapshot-store.js.map +1 -0
- package/dist/core/tools/powershell.d.ts +2 -2
- package/dist/core/tools/powershell.d.ts.map +1 -1
- package/dist/core/tools/powershell.js +2 -5
- package/dist/core/tools/powershell.js.map +1 -1
- package/dist/core/tools/read-document-extract.js.map +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/write.d.ts +30 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +97 -8
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/task-live-transcript.d.ts +1 -1
- package/dist/modes/interactive/components/task-live-transcript.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-live-transcript.js +19 -11
- package/dist/modes/interactive/components/task-live-transcript.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +3 -1
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +28 -4
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +3 -0
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive-engine/activity-watchdog.d.ts +2 -2
- package/dist/modes/interactive-engine/activity-watchdog.d.ts.map +1 -1
- package/dist/modes/interactive-engine/activity-watchdog.js.map +1 -1
- package/dist/modes/interactive-engine/engine-diagnostic-view.d.ts +3 -1
- package/dist/modes/interactive-engine/engine-diagnostic-view.d.ts.map +1 -1
- package/dist/modes/interactive-engine/engine-diagnostic-view.js +11 -0
- package/dist/modes/interactive-engine/engine-diagnostic-view.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +2 -1
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/rpc/rpc-client-process.d.ts +2 -0
- package/dist/modes/rpc/rpc-client-process.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client-process.js +23 -1
- package/dist/modes/rpc/rpc-client-process.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +20 -3
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/utils/markit.d.ts +2 -0
- package/dist/utils/markit.d.ts.map +1 -1
- package/dist/utils/markit.js +65 -5
- package/dist/utils/markit.js.map +1 -1
- package/docs/background-tasks.md +15 -11
- package/docs/build.md +51 -0
- package/docs/changelog.mdx +8 -8
- package/docs/compaction/reference.md +267 -0
- package/docs/compaction.md +36 -219
- package/docs/computer-use.md +35 -0
- package/docs/containerization.md +9 -5
- package/docs/custom-provider/api-reference.md +145 -0
- package/docs/custom-provider/oauth.md +131 -0
- package/docs/custom-provider/override.md +34 -0
- package/docs/custom-provider/registration.md +163 -0
- package/docs/custom-provider/streaming.md +214 -0
- package/docs/custom-provider.md +43 -607
- package/docs/development.md +87 -0
- package/docs/docs.json +199 -76
- package/docs/environment-variables.md +1 -1
- package/docs/extensions/api-reference.md +1001 -0
- package/docs/extensions/authoring.md +676 -0
- package/docs/extensions/events.md +793 -0
- package/docs/extensions/examples.md +94 -0
- package/docs/extensions/ui.md +470 -0
- package/docs/extensions.md +270 -2768
- package/docs/getting-started/authentication.md +55 -0
- package/docs/getting-started/first-session.md +172 -0
- package/docs/getting-started/installation.md +154 -0
- package/docs/getting-started/project-instructions.md +43 -0
- package/docs/guides/configuration.md +65 -0
- package/docs/guides/intercom.md +24 -0
- package/docs/guides/non-interactive.md +33 -0
- package/docs/guides/subagents.md +24 -0
- package/docs/guides/workflows.md +27 -0
- package/docs/guides.md +41 -0
- package/docs/index.md +13 -45
- package/docs/intercom/operations.md +178 -0
- package/docs/intercom/reference.md +146 -0
- package/docs/intercom.md +54 -259
- package/docs/json.md +17 -0
- package/docs/llama-cpp.md +5 -0
- package/docs/models/artificial-analysis-index.md +89 -0
- package/docs/models/evals.md +2 -2
- package/docs/models/model-selection.md +3 -3
- package/docs/models/pareto-efficiency.md +2 -2
- package/docs/models/reference.md +601 -0
- package/docs/models.md +45 -550
- package/docs/packages/authoring.md +88 -0
- package/docs/packages/reference.md +43 -0
- package/docs/packages.md +28 -107
- package/docs/programmatic.md +29 -0
- package/docs/prompt-templates.md +11 -0
- package/docs/providers/reference.md +23 -0
- package/docs/providers.md +10 -15
- package/docs/quickstart.md +44 -238
- package/docs/reference/cli.md +252 -0
- package/docs/reference.md +52 -0
- package/docs/rpc/examples.md +64 -0
- package/docs/rpc/extension-ui.md +198 -0
- package/docs/rpc/protocol.md +1341 -0
- package/docs/rpc.md +109 -1346
- package/docs/sdk/reference.md +1090 -0
- package/docs/sdk.md +56 -1002
- package/docs/session-format.md +2 -0
- package/docs/sessions.md +6 -10
- package/docs/settings.md +6 -45
- package/docs/skills/authoring.md +95 -0
- package/docs/skills/reference.md +60 -0
- package/docs/skills.md +20 -122
- package/docs/subagents/authoring.md +37 -0
- package/docs/subagents/reference.md +60 -0
- package/docs/subagents.md +42 -96
- package/docs/terminal-setup.md +1 -1
- package/docs/themes/reference.md +176 -0
- package/docs/themes.md +21 -135
- package/docs/tmux.md +5 -0
- package/docs/tools/edit.md +5 -0
- package/docs/tools.md +31 -0
- package/docs/tui/reference.md +343 -0
- package/docs/tui.md +23 -286
- package/docs/usage.md +26 -209
- package/docs/web-access.md +40 -0
- package/docs/windows.md +2 -1
- package/docs/workflows/api-reference.md +11 -4
- package/docs/workflows/authoring.md +4 -2
- package/docs/workflows/builtins.md +2 -2
- package/docs/workflows/operations.md +11 -7
- package/docs/workflows/reliable-design.md +21 -3
- package/docs/workflows/verification.md +8 -0
- package/examples/extensions/gondolin/index.ts +116 -148
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/ssh.ts +56 -34
- package/npm-shrinkwrap.json +33 -33
- 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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20
|
+
Moved to [TUI API reference](/tui/reference#component-interface).
|
|
36
21
|
|
|
37
22
|
## Focusable Interface (IME Support)
|
|
38
23
|
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
387
|
+
Moved to [TUI API reference](/tui/reference#invalidation-and-theme-changes).
|
|
569
388
|
|
|
570
389
|
### The Problem
|
|
571
390
|
|
|
572
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|