apex-code 0.0.1-alpha.3 → 0.0.1-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/README.md +7 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -2
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +20 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +91 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +17 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +10 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/lsp/client.d.ts +76 -0
- package/dist/core/lsp/client.d.ts.map +1 -0
- package/dist/core/lsp/client.js +469 -0
- package/dist/core/lsp/client.js.map +1 -0
- package/dist/core/lsp/diagnostics.d.ts +47 -0
- package/dist/core/lsp/diagnostics.d.ts.map +1 -0
- package/dist/core/lsp/diagnostics.js +224 -0
- package/dist/core/lsp/diagnostics.js.map +1 -0
- package/dist/core/lsp/pool.d.ts +48 -0
- package/dist/core/lsp/pool.d.ts.map +1 -0
- package/dist/core/lsp/pool.js +150 -0
- package/dist/core/lsp/pool.js.map +1 -0
- package/dist/core/lsp/registry.d.ts +49 -0
- package/dist/core/lsp/registry.d.ts.map +1 -0
- package/dist/core/lsp/registry.js +215 -0
- package/dist/core/lsp/registry.js.map +1 -0
- package/dist/core/lsp/status.d.ts +23 -0
- package/dist/core/lsp/status.d.ts.map +1 -0
- package/dist/core/lsp/status.js +35 -0
- package/dist/core/lsp/status.js.map +1 -0
- package/dist/core/lsp/sync.d.ts +13 -0
- package/dist/core/lsp/sync.d.ts.map +1 -0
- package/dist/core/lsp/sync.js +71 -0
- package/dist/core/lsp/sync.js.map +1 -0
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/sandbox/child-entry.d.ts.map +1 -1
- package/dist/core/sandbox/child-entry.js +4 -0
- package/dist/core/sandbox/child-entry.js.map +1 -1
- package/dist/core/sandbox/cli-launch.d.ts +10 -1
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
- package/dist/core/sandbox/cli-launch.js +50 -4
- package/dist/core/sandbox/cli-launch.js.map +1 -1
- package/dist/core/sandbox/cli-supervisor.d.ts +2 -0
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/cli-supervisor.js +1 -0
- package/dist/core/sandbox/cli-supervisor.js.map +1 -1
- package/dist/core/sandbox/default-hosts.d.ts +28 -0
- package/dist/core/sandbox/default-hosts.d.ts.map +1 -0
- package/dist/core/sandbox/default-hosts.js +69 -0
- package/dist/core/sandbox/default-hosts.js.map +1 -0
- package/dist/core/sandbox/linux-backend.d.ts +16 -0
- package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
- package/dist/core/sandbox/linux-backend.js +56 -10
- package/dist/core/sandbox/linux-backend.js.map +1 -1
- package/dist/core/sandbox/macos-backend.d.ts +7 -0
- package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
- package/dist/core/sandbox/macos-backend.js +17 -9
- package/dist/core/sandbox/macos-backend.js.map +1 -1
- package/dist/core/sandbox/network-refusal.d.ts +24 -0
- package/dist/core/sandbox/network-refusal.d.ts.map +1 -0
- package/dist/core/sandbox/network-refusal.js +72 -0
- package/dist/core/sandbox/network-refusal.js.map +1 -0
- package/dist/core/sandbox/supervisor.d.ts +9 -0
- package/dist/core/sandbox/supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/supervisor.js.map +1 -1
- package/dist/core/sdk.d.ts +4 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-share.d.ts +15 -0
- package/dist/core/session-share.d.ts.map +1 -1
- package/dist/core/session-share.js +23 -0
- package/dist/core/session-share.js.map +1 -1
- package/dist/core/settings-manager.d.ts +8 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/contract.d.ts +29 -2
- package/dist/core/tools/contract.d.ts.map +1 -1
- package/dist/core/tools/contract.js.map +1 -1
- package/dist/core/tools/diagnostics.d.ts +39 -0
- package/dist/core/tools/diagnostics.d.ts.map +1 -0
- package/dist/core/tools/diagnostics.js +62 -0
- package/dist/core/tools/diagnostics.js.map +1 -0
- package/dist/core/tools/edit.d.ts +10 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +17 -8
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts +15 -2
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +6 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/lsp.d.ts +62 -0
- package/dist/core/tools/lsp.d.ts.map +1 -0
- package/dist/core/tools/lsp.js +216 -0
- package/dist/core/tools/lsp.js.map +1 -0
- package/dist/core/tools/write.d.ts +10 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +16 -3
- 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/custom-editor.d.ts +49 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +235 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +2 -1
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/splash-header.d.ts +41 -0
- package/dist/modes/interactive/components/splash-header.d.ts.map +1 -0
- package/dist/modes/interactive/components/splash-header.js +145 -0
- package/dist/modes/interactive/components/splash-header.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +20 -17
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/apex.json +94 -0
- package/dist/modes/interactive/theme/light.json +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +6 -1
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/themes/apex-logo.d.ts +21 -0
- package/dist/themes/apex-logo.d.ts.map +1 -0
- package/dist/themes/apex-logo.js +31 -0
- package/dist/themes/apex-logo.js.map +1 -0
- package/dist/utils/changelog.d.ts +7 -2
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +82 -13
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +28 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +68 -5
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +2 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +2 -0
- package/dist/utils/version-check.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/npm-shrinkwrap.json +5 -5
- package/package.json +2 -2
|
@@ -1,9 +1,125 @@
|
|
|
1
|
-
import { Editor } from "@earendil-works/pi-tui";
|
|
1
|
+
import { CURSOR_MARKER, Editor, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
|
|
2
|
+
import { stripAnsi } from "../../../utils/ansi.js";
|
|
3
|
+
/** Box-drawing horizontal rule: the character the base Editor draws borders from. */
|
|
4
|
+
const BORDER_CHAR = "─";
|
|
5
|
+
/**
|
|
6
|
+
* The two shapes the base Editor draws a border in: a plain full-width rule, and
|
|
7
|
+
* the scroll indicator (`─── ↑ 3 more ────`).
|
|
8
|
+
*
|
|
9
|
+
* Both are anchored deliberately. A prefix test would count any content line that
|
|
10
|
+
* merely *starts* with the rule character — a pasted divider, `─notes` — as a
|
|
11
|
+
* border, which is worse than it sounds: {@link CustomEditor.render} tells the
|
|
12
|
+
* content region from the autocomplete region by counting borders, so one
|
|
13
|
+
* miscount shifts every later line into the wrong region.
|
|
14
|
+
*/
|
|
15
|
+
const BORDER_RULE = /^─+$/;
|
|
16
|
+
const BORDER_SCROLL = /^─+ [↑↓] \d+ more/;
|
|
17
|
+
/**
|
|
18
|
+
* A leading `/command` token: optional indent, a slash, then non-space.
|
|
19
|
+
* Matched against the line's *visible* text, not the rendered string.
|
|
20
|
+
*/
|
|
21
|
+
const COMMAND_TOKEN = /^(\s*)(\/\S+)/;
|
|
22
|
+
/**
|
|
23
|
+
* One ANSI control sequence: a CSI escape (`ESC [ … letter`) or the APC
|
|
24
|
+
* hardware-cursor marker (`ESC _ … BEL`). Sticky, for position-by-position
|
|
25
|
+
* scanning in {@link colorVisibleRange}.
|
|
26
|
+
*/
|
|
27
|
+
const CONTROL_SEQUENCE = /\x1b\[[0-9;]*[A-Za-z]|\x1b_[^\x07]*\x07/y;
|
|
28
|
+
/**
|
|
29
|
+
* ============================ ANSI measuring hazards ============================
|
|
30
|
+
*
|
|
31
|
+
* Two traps live in the rendered strings this file post-processes. Both produced
|
|
32
|
+
* real bugs; both are cheap to re-introduce. Read this before touching the width
|
|
33
|
+
* maths or any leading-sequence match.
|
|
34
|
+
*
|
|
35
|
+
* 1. DO NOT COMPOSE `stripAnsi` WITH `visibleWidth`.
|
|
36
|
+
*
|
|
37
|
+
* `stripAnsi` removes SGR colour codes but leaves the APC hardware-cursor
|
|
38
|
+
* marker (`ESC _pi:c BEL`, see CURSOR_MARKER in packages/tui) fully intact —
|
|
39
|
+
* all seven bytes of it. `visibleWidth` already handles that marker correctly
|
|
40
|
+
* and scores it zero.
|
|
41
|
+
*
|
|
42
|
+
* So `visibleWidth(stripAnsi(line))` counts seven phantom columns on every
|
|
43
|
+
* line carrying a cursor, which silently widened each placeholder line past
|
|
44
|
+
* the terminal width. Measure with `visibleWidth` alone. Use `stripAnsi` only
|
|
45
|
+
* to read text, never to measure it.
|
|
46
|
+
*
|
|
47
|
+
* 2. DO NOT MATCH THE LEADING SEQUENCE RUN GENERICALLY.
|
|
48
|
+
*
|
|
49
|
+
* The base Editor renders an empty line as:
|
|
50
|
+
* CURSOR_MARKER ESC[7m <space> ESC[0m <padding>
|
|
51
|
+
* The `ESC[7m` turns on reverse video for exactly the one-cell cursor block,
|
|
52
|
+
* and `ESC[0m` turns it back off.
|
|
53
|
+
*
|
|
54
|
+
* A permissive prefix match such as `(?:\x1b\[[0-9;]*m)*` consumes that
|
|
55
|
+
* `ESC[7m` as though it were ordinary leading styling. Whatever is appended
|
|
56
|
+
* next then inherits reverse video, and because the matching `ESC[0m` was
|
|
57
|
+
* left behind in the discarded remainder, the inversion runs on through the
|
|
58
|
+
* placeholder and the rest of the frame. Match the reverse-video run
|
|
59
|
+
* explicitly instead — see {@link CustomEditor.sliceLeadingCursorCell}.
|
|
60
|
+
*
|
|
61
|
+
* ==============================================================================
|
|
62
|
+
*/
|
|
63
|
+
/**
|
|
64
|
+
* Wrap the visible characters in `[start, end)` with `colorFn`, leaving every
|
|
65
|
+
* control sequence in place.
|
|
66
|
+
*
|
|
67
|
+
* The range is in visible columns, so it is unaffected by however many escape
|
|
68
|
+
* sequences are interleaved. That matters because the cursor cell can land in
|
|
69
|
+
* the middle of the token being coloured: with the caret at column 3 of
|
|
70
|
+
* `/model`, the line reads `/mo` + CURSOR_MARKER + `ESC[7m` + `d` + `ESC[0m` +
|
|
71
|
+
* `el`. Colour is therefore re-opened after every control sequence rather than
|
|
72
|
+
* once around the whole token, since the cursor cell's `ESC[0m` would otherwise
|
|
73
|
+
* cancel it partway through.
|
|
74
|
+
*/
|
|
75
|
+
function colorVisibleRange(line, start, end, colorFn) {
|
|
76
|
+
let out = "";
|
|
77
|
+
let buffer = "";
|
|
78
|
+
let column = 0;
|
|
79
|
+
let index = 0;
|
|
80
|
+
const flush = () => {
|
|
81
|
+
if (buffer) {
|
|
82
|
+
out += colorFn(buffer);
|
|
83
|
+
buffer = "";
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
while (index < line.length) {
|
|
87
|
+
CONTROL_SEQUENCE.lastIndex = index;
|
|
88
|
+
const control = CONTROL_SEQUENCE.exec(line);
|
|
89
|
+
if (control) {
|
|
90
|
+
flush();
|
|
91
|
+
out += control[0];
|
|
92
|
+
index = CONTROL_SEQUENCE.lastIndex;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const char = line[index];
|
|
96
|
+
if (column >= start && column < end) {
|
|
97
|
+
buffer += char;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
flush();
|
|
101
|
+
out += char;
|
|
102
|
+
}
|
|
103
|
+
column += visibleWidth(char);
|
|
104
|
+
index += 1;
|
|
105
|
+
}
|
|
106
|
+
flush();
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
2
109
|
/**
|
|
3
110
|
* Custom editor that handles app-level keybindings for coding-agent.
|
|
111
|
+
*
|
|
112
|
+
* The prompt prefix and placeholder are layered on in `render` rather than
|
|
113
|
+
* inside the base Editor, because `packages/tui` is frozen under ADR 0001 and
|
|
114
|
+
* its `EditorOptions` has no hook for either.
|
|
4
115
|
*/
|
|
5
116
|
export class CustomEditor extends Editor {
|
|
6
117
|
keybindings;
|
|
118
|
+
promptPrefix;
|
|
119
|
+
promptColor;
|
|
120
|
+
placeholderColor;
|
|
121
|
+
commandColor;
|
|
122
|
+
placeholder;
|
|
7
123
|
actionHandlers = new Map();
|
|
8
124
|
// Special handlers that can be dynamically replaced
|
|
9
125
|
onEscape;
|
|
@@ -14,6 +130,124 @@ export class CustomEditor extends Editor {
|
|
|
14
130
|
constructor(tui, theme, keybindings, options) {
|
|
15
131
|
super(tui, theme, options);
|
|
16
132
|
this.keybindings = keybindings;
|
|
133
|
+
this.promptPrefix = options?.promptPrefix ?? "";
|
|
134
|
+
this.promptColor = options?.promptColor ?? ((text) => text);
|
|
135
|
+
this.placeholder = options?.placeholder;
|
|
136
|
+
this.placeholderColor = options?.placeholderColor ?? ((text) => text);
|
|
137
|
+
this.commandColor = options?.commandColor;
|
|
138
|
+
}
|
|
139
|
+
render(width) {
|
|
140
|
+
const prefixWidth = visibleWidth(this.promptPrefix);
|
|
141
|
+
// Reserve the prefix out of the width handed to the base Editor, so its
|
|
142
|
+
// wrapping, scrolling and cursor column all account for the space the
|
|
143
|
+
// prefix occupies. Bail out entirely when the terminal cannot spare it.
|
|
144
|
+
if (prefixWidth === 0 || width <= prefixWidth + 4) {
|
|
145
|
+
return this.withCommandColor(this.withPlaceholder(super.render(width)));
|
|
146
|
+
}
|
|
147
|
+
const inner = this.withCommandColor(this.withPlaceholder(super.render(width - prefixWidth)));
|
|
148
|
+
const borderPad = this.borderColor(BORDER_CHAR.repeat(prefixWidth));
|
|
149
|
+
const blank = " ".repeat(prefixWidth);
|
|
150
|
+
const styledPrefix = this.promptColor(this.promptPrefix);
|
|
151
|
+
// Structure is: top border, content lines, bottom border, then optional
|
|
152
|
+
// autocomplete rows. Counting borders tells the three regions apart
|
|
153
|
+
// without depending on how many lines each contains.
|
|
154
|
+
let borders = 0;
|
|
155
|
+
let firstContentLine = true;
|
|
156
|
+
return inner.map((line) => {
|
|
157
|
+
if (this.isBorderLine(line)) {
|
|
158
|
+
borders++;
|
|
159
|
+
return borderPad + line;
|
|
160
|
+
}
|
|
161
|
+
if (borders !== 1) {
|
|
162
|
+
// Autocomplete rows sit below the box; indent them to match.
|
|
163
|
+
return blank + line;
|
|
164
|
+
}
|
|
165
|
+
if (firstContentLine) {
|
|
166
|
+
firstContentLine = false;
|
|
167
|
+
return styledPrefix + line;
|
|
168
|
+
}
|
|
169
|
+
return blank + line;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
isBorderLine(line) {
|
|
173
|
+
const visible = stripAnsi(line.split(CURSOR_MARKER).join(""));
|
|
174
|
+
return BORDER_RULE.test(visible) || BORDER_SCROLL.test(visible);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Tint a leading `/command` on the first input line.
|
|
178
|
+
*
|
|
179
|
+
* The base Editor emits input text unstyled, so the only styling already
|
|
180
|
+
* present on the line is the cursor cell — which `colorVisibleRange` steps
|
|
181
|
+
* over. Only the command token is tinted; arguments after it stay plain, so
|
|
182
|
+
* the highlight marks what is being invoked rather than the whole line.
|
|
183
|
+
*/
|
|
184
|
+
withCommandColor(lines) {
|
|
185
|
+
const commandColor = this.commandColor;
|
|
186
|
+
if (!commandColor || lines.length < 2) {
|
|
187
|
+
return lines;
|
|
188
|
+
}
|
|
189
|
+
// Read the token from the model rather than the rendered line: the first
|
|
190
|
+
// visual row is the start of the text only when the caret has not scrolled
|
|
191
|
+
// the view down, and getText is unambiguous either way.
|
|
192
|
+
const firstLine = this.getText().split("\n", 1)[0] ?? "";
|
|
193
|
+
const match = COMMAND_TOKEN.exec(firstLine);
|
|
194
|
+
if (!match) {
|
|
195
|
+
return lines;
|
|
196
|
+
}
|
|
197
|
+
const [, indent, token] = match;
|
|
198
|
+
// The rendered row must still begin with that token, or the view has
|
|
199
|
+
// scrolled and the highlight would land on unrelated text.
|
|
200
|
+
if (!stripAnsi(lines[1].split(CURSOR_MARKER).join("")).startsWith(`${indent}${token}`)) {
|
|
201
|
+
return lines;
|
|
202
|
+
}
|
|
203
|
+
const start = visibleWidth(indent);
|
|
204
|
+
const updated = [...lines];
|
|
205
|
+
updated[1] = colorVisibleRange(lines[1], start, start + visibleWidth(token), commandColor);
|
|
206
|
+
return updated;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Replace the sole content line with the placeholder while the input is
|
|
210
|
+
* empty. Index 1 is always the first content line, and an empty editor has
|
|
211
|
+
* exactly one, so no line classification is needed here.
|
|
212
|
+
*/
|
|
213
|
+
withPlaceholder(lines) {
|
|
214
|
+
const placeholder = this.placeholder;
|
|
215
|
+
if (!placeholder || lines.length < 2 || this.getText().length > 0 || this.isShowingAutocomplete()) {
|
|
216
|
+
return lines;
|
|
217
|
+
}
|
|
218
|
+
const contentLine = lines[1];
|
|
219
|
+
// Preserve the cursor cell when focused; unfocused there is none, and the
|
|
220
|
+
// placeholder simply starts at column zero.
|
|
221
|
+
const cursorCell = this.sliceLeadingCursorCell(contentLine);
|
|
222
|
+
// Measure with visibleWidth alone — never visibleWidth(stripAnsi(...)),
|
|
223
|
+
// which scores the cursor marker as seven columns. See hazard 1 at the top
|
|
224
|
+
// of this file.
|
|
225
|
+
const available = visibleWidth(contentLine) - visibleWidth(cursorCell);
|
|
226
|
+
if (available <= 0) {
|
|
227
|
+
return lines;
|
|
228
|
+
}
|
|
229
|
+
// Cut by display width, not by String.slice: `available` counts terminal
|
|
230
|
+
// columns, while slice counts UTF-16 code units. They only agree for
|
|
231
|
+
// plain ASCII, and disagreeing splits a surrogate pair or overruns the
|
|
232
|
+
// line on the first wide character.
|
|
233
|
+
const text = truncateToWidth(placeholder, available, "");
|
|
234
|
+
const padding = " ".repeat(Math.max(0, available - visibleWidth(text)));
|
|
235
|
+
const updated = [...lines];
|
|
236
|
+
updated[1] = cursorCell + this.placeholderColor(text) + padding;
|
|
237
|
+
return updated;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The base Editor renders an empty line as the hardware-cursor marker
|
|
241
|
+
* followed by a reverse-video cell: `CURSOR_MARKER ESC[7m <space> ESC[0m`.
|
|
242
|
+
* Both parts are kept verbatim.
|
|
243
|
+
*
|
|
244
|
+
* The `ESC[7m … ESC[0m` run is matched explicitly. Do not "simplify" this to
|
|
245
|
+
* a general leading-SGR match such as `(?:\x1b\[[0-9;]*m)*` — see hazard 2 at
|
|
246
|
+
* the top of this file for what that breaks.
|
|
247
|
+
*/
|
|
248
|
+
sliceLeadingCursorCell(line) {
|
|
249
|
+
const match = /^(?:\x1b_pi:c\x07)?(?:\x1b\[7m.\x1b\[(?:0|27)m)?/.exec(line);
|
|
250
|
+
return match?.[0] ?? "";
|
|
17
251
|
}
|
|
18
252
|
/**
|
|
19
253
|
* Register a handler for an app action.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-editor.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import { Editor, type EditorOptions, type EditorTheme, type TUI } from \"@earendil-works/pi-tui\";\nimport type { AppKeybinding, KeybindingsManager } from \"../../../core/keybindings.ts\";\n\n/**\n * Custom editor that handles app-level keybindings for coding-agent.\n */\nexport class CustomEditor extends Editor {\n\tprivate keybindings: KeybindingsManager;\n\tpublic actionHandlers: Map<AppKeybinding, () => void> = new Map();\n\n\t// Special handlers that can be dynamically replaced\n\tpublic onEscape?: () => void;\n\tpublic onCtrlD?: () => void;\n\tpublic onPasteImage?: () => void;\n\t/** Handler for extension-registered shortcuts. Returns true if handled. */\n\tpublic onExtensionShortcut?: (data: string) => boolean;\n\n\tconstructor(tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager, options?: EditorOptions) {\n\t\tsuper(tui, theme, options);\n\t\tthis.keybindings = keybindings;\n\t}\n\n\t/**\n\t * Register a handler for an app action.\n\t */\n\tonAction(action: AppKeybinding, handler: () => void): void {\n\t\tthis.actionHandlers.set(action, handler);\n\t}\n\n\thandleInput(data: string): void {\n\t\t// Check extension-registered shortcuts first\n\t\tif (this.onExtensionShortcut?.(data)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Check for clipboard paste keybinding\n\t\tif (this.keybindings.matches(data, \"app.clipboard.pasteImage\")) {\n\t\t\tthis.onPasteImage?.();\n\t\t\treturn;\n\t\t}\n\n\t\t// Check app keybindings first\n\n\t\t// Escape/interrupt - only if autocomplete is NOT active\n\t\tif (this.keybindings.matches(data, \"app.interrupt\")) {\n\t\t\tif (!this.isShowingAutocomplete()) {\n\t\t\t\t// Use dynamic onEscape if set, otherwise registered handler\n\t\t\t\tconst handler = this.onEscape ?? this.actionHandlers.get(\"app.interrupt\");\n\t\t\t\tif (handler) {\n\t\t\t\t\thandler();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Let parent handle escape for autocomplete cancellation\n\t\t\tsuper.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Exit (Ctrl+D) - only when editor is empty\n\t\tif (this.keybindings.matches(data, \"app.exit\")) {\n\t\t\tif (this.getText().length === 0) {\n\t\t\t\tconst handler = this.onCtrlD ?? this.actionHandlers.get(\"app.exit\");\n\t\t\t\tif (handler) handler();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Fall through to editor handling for delete-char-forward when not empty\n\t\t}\n\n\t\t// Explicit history bindings take precedence over app actions while the editor is focused.\n\t\t// This lets users bind Ctrl+P even though it cycles models by default.\n\t\tif (\n\t\t\tthis.keybindings.matches(data, \"tui.editor.historyPrevious\") ||\n\t\t\tthis.keybindings.matches(data, \"tui.editor.historyNext\")\n\t\t) {\n\t\t\tsuper.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check all other app actions\n\t\tfor (const [action, handler] of this.actionHandlers) {\n\t\t\tif (action !== \"app.interrupt\" && action !== \"app.exit\" && this.keybindings.matches(data, action)) {\n\t\t\t\thandler();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Pass to parent for editor handling\n\t\tsuper.handleInput(data);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"custom-editor.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tCURSOR_MARKER,\n\tEditor,\n\ttype EditorOptions,\n\ttype EditorTheme,\n\ttype TUI,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@earendil-works/pi-tui\";\nimport type { AppKeybinding, KeybindingsManager } from \"../../../core/keybindings.ts\";\nimport { stripAnsi } from \"../../../utils/ansi.ts\";\n\nexport interface CustomEditorOptions extends EditorOptions {\n\t/** Marker rendered at the start of the first input line, e.g. `\"> \"`. */\n\tpromptPrefix?: string;\n\t/** Styles the prompt prefix. Defaults to leaving it unstyled. */\n\tpromptColor?: (text: string) => string;\n\t/** Shown in place of the empty input line. */\n\tplaceholder?: string;\n\t/** Styles the placeholder. Defaults to leaving it unstyled. */\n\tplaceholderColor?: (text: string) => string;\n\t/** Styles a leading `/command` token. Defaults to leaving it unstyled. */\n\tcommandColor?: (text: string) => string;\n}\n\n/** Box-drawing horizontal rule: the character the base Editor draws borders from. */\nconst BORDER_CHAR = \"─\";\n\n/**\n * The two shapes the base Editor draws a border in: a plain full-width rule, and\n * the scroll indicator (`─── ↑ 3 more ────`).\n *\n * Both are anchored deliberately. A prefix test would count any content line that\n * merely *starts* with the rule character — a pasted divider, `─notes` — as a\n * border, which is worse than it sounds: {@link CustomEditor.render} tells the\n * content region from the autocomplete region by counting borders, so one\n * miscount shifts every later line into the wrong region.\n */\nconst BORDER_RULE = /^─+$/;\nconst BORDER_SCROLL = /^─+ [↑↓] \\d+ more/;\n\n/**\n * A leading `/command` token: optional indent, a slash, then non-space.\n * Matched against the line's *visible* text, not the rendered string.\n */\nconst COMMAND_TOKEN = /^(\\s*)(\\/\\S+)/;\n\n/**\n * One ANSI control sequence: a CSI escape (`ESC [ … letter`) or the APC\n * hardware-cursor marker (`ESC _ … BEL`). Sticky, for position-by-position\n * scanning in {@link colorVisibleRange}.\n */\nconst CONTROL_SEQUENCE = /\\x1b\\[[0-9;]*[A-Za-z]|\\x1b_[^\\x07]*\\x07/y;\n\n/**\n * ============================ ANSI measuring hazards ============================\n *\n * Two traps live in the rendered strings this file post-processes. Both produced\n * real bugs; both are cheap to re-introduce. Read this before touching the width\n * maths or any leading-sequence match.\n *\n * 1. DO NOT COMPOSE `stripAnsi` WITH `visibleWidth`.\n *\n * `stripAnsi` removes SGR colour codes but leaves the APC hardware-cursor\n * marker (`ESC _pi:c BEL`, see CURSOR_MARKER in packages/tui) fully intact —\n * all seven bytes of it. `visibleWidth` already handles that marker correctly\n * and scores it zero.\n *\n * So `visibleWidth(stripAnsi(line))` counts seven phantom columns on every\n * line carrying a cursor, which silently widened each placeholder line past\n * the terminal width. Measure with `visibleWidth` alone. Use `stripAnsi` only\n * to read text, never to measure it.\n *\n * 2. DO NOT MATCH THE LEADING SEQUENCE RUN GENERICALLY.\n *\n * The base Editor renders an empty line as:\n * CURSOR_MARKER ESC[7m <space> ESC[0m <padding>\n * The `ESC[7m` turns on reverse video for exactly the one-cell cursor block,\n * and `ESC[0m` turns it back off.\n *\n * A permissive prefix match such as `(?:\\x1b\\[[0-9;]*m)*` consumes that\n * `ESC[7m` as though it were ordinary leading styling. Whatever is appended\n * next then inherits reverse video, and because the matching `ESC[0m` was\n * left behind in the discarded remainder, the inversion runs on through the\n * placeholder and the rest of the frame. Match the reverse-video run\n * explicitly instead — see {@link CustomEditor.sliceLeadingCursorCell}.\n *\n * ==============================================================================\n */\n\n/**\n * Wrap the visible characters in `[start, end)` with `colorFn`, leaving every\n * control sequence in place.\n *\n * The range is in visible columns, so it is unaffected by however many escape\n * sequences are interleaved. That matters because the cursor cell can land in\n * the middle of the token being coloured: with the caret at column 3 of\n * `/model`, the line reads `/mo` + CURSOR_MARKER + `ESC[7m` + `d` + `ESC[0m` +\n * `el`. Colour is therefore re-opened after every control sequence rather than\n * once around the whole token, since the cursor cell's `ESC[0m` would otherwise\n * cancel it partway through.\n */\nfunction colorVisibleRange(line: string, start: number, end: number, colorFn: (text: string) => string): string {\n\tlet out = \"\";\n\tlet buffer = \"\";\n\tlet column = 0;\n\tlet index = 0;\n\n\tconst flush = () => {\n\t\tif (buffer) {\n\t\t\tout += colorFn(buffer);\n\t\t\tbuffer = \"\";\n\t\t}\n\t};\n\n\twhile (index < line.length) {\n\t\tCONTROL_SEQUENCE.lastIndex = index;\n\t\tconst control = CONTROL_SEQUENCE.exec(line);\n\t\tif (control) {\n\t\t\tflush();\n\t\t\tout += control[0];\n\t\t\tindex = CONTROL_SEQUENCE.lastIndex;\n\t\t\tcontinue;\n\t\t}\n\t\tconst char = line[index];\n\t\tif (column >= start && column < end) {\n\t\t\tbuffer += char;\n\t\t} else {\n\t\t\tflush();\n\t\t\tout += char;\n\t\t}\n\t\tcolumn += visibleWidth(char);\n\t\tindex += 1;\n\t}\n\tflush();\n\treturn out;\n}\n\n/**\n * Custom editor that handles app-level keybindings for coding-agent.\n *\n * The prompt prefix and placeholder are layered on in `render` rather than\n * inside the base Editor, because `packages/tui` is frozen under ADR 0001 and\n * its `EditorOptions` has no hook for either.\n */\nexport class CustomEditor extends Editor {\n\tprivate keybindings: KeybindingsManager;\n\tprivate readonly promptPrefix: string;\n\tprivate readonly promptColor: (text: string) => string;\n\tprivate readonly placeholderColor: (text: string) => string;\n\tprivate readonly commandColor: ((text: string) => string) | undefined;\n\tprivate readonly placeholder: string | undefined;\n\tpublic actionHandlers: Map<AppKeybinding, () => void> = new Map();\n\n\t// Special handlers that can be dynamically replaced\n\tpublic onEscape?: () => void;\n\tpublic onCtrlD?: () => void;\n\tpublic onPasteImage?: () => void;\n\t/** Handler for extension-registered shortcuts. Returns true if handled. */\n\tpublic onExtensionShortcut?: (data: string) => boolean;\n\n\tconstructor(tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager, options?: CustomEditorOptions) {\n\t\tsuper(tui, theme, options);\n\t\tthis.keybindings = keybindings;\n\t\tthis.promptPrefix = options?.promptPrefix ?? \"\";\n\t\tthis.promptColor = options?.promptColor ?? ((text) => text);\n\t\tthis.placeholder = options?.placeholder;\n\t\tthis.placeholderColor = options?.placeholderColor ?? ((text) => text);\n\t\tthis.commandColor = options?.commandColor;\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst prefixWidth = visibleWidth(this.promptPrefix);\n\t\t// Reserve the prefix out of the width handed to the base Editor, so its\n\t\t// wrapping, scrolling and cursor column all account for the space the\n\t\t// prefix occupies. Bail out entirely when the terminal cannot spare it.\n\t\tif (prefixWidth === 0 || width <= prefixWidth + 4) {\n\t\t\treturn this.withCommandColor(this.withPlaceholder(super.render(width)));\n\t\t}\n\n\t\tconst inner = this.withCommandColor(this.withPlaceholder(super.render(width - prefixWidth)));\n\t\tconst borderPad = this.borderColor(BORDER_CHAR.repeat(prefixWidth));\n\t\tconst blank = \" \".repeat(prefixWidth);\n\t\tconst styledPrefix = this.promptColor(this.promptPrefix);\n\n\t\t// Structure is: top border, content lines, bottom border, then optional\n\t\t// autocomplete rows. Counting borders tells the three regions apart\n\t\t// without depending on how many lines each contains.\n\t\tlet borders = 0;\n\t\tlet firstContentLine = true;\n\t\treturn inner.map((line) => {\n\t\t\tif (this.isBorderLine(line)) {\n\t\t\t\tborders++;\n\t\t\t\treturn borderPad + line;\n\t\t\t}\n\t\t\tif (borders !== 1) {\n\t\t\t\t// Autocomplete rows sit below the box; indent them to match.\n\t\t\t\treturn blank + line;\n\t\t\t}\n\t\t\tif (firstContentLine) {\n\t\t\t\tfirstContentLine = false;\n\t\t\t\treturn styledPrefix + line;\n\t\t\t}\n\t\t\treturn blank + line;\n\t\t});\n\t}\n\n\tprivate isBorderLine(line: string): boolean {\n\t\tconst visible = stripAnsi(line.split(CURSOR_MARKER).join(\"\"));\n\t\treturn BORDER_RULE.test(visible) || BORDER_SCROLL.test(visible);\n\t}\n\n\t/**\n\t * Tint a leading `/command` on the first input line.\n\t *\n\t * The base Editor emits input text unstyled, so the only styling already\n\t * present on the line is the cursor cell — which `colorVisibleRange` steps\n\t * over. Only the command token is tinted; arguments after it stay plain, so\n\t * the highlight marks what is being invoked rather than the whole line.\n\t */\n\tprivate withCommandColor(lines: string[]): string[] {\n\t\tconst commandColor = this.commandColor;\n\t\tif (!commandColor || lines.length < 2) {\n\t\t\treturn lines;\n\t\t}\n\t\t// Read the token from the model rather than the rendered line: the first\n\t\t// visual row is the start of the text only when the caret has not scrolled\n\t\t// the view down, and getText is unambiguous either way.\n\t\tconst firstLine = this.getText().split(\"\\n\", 1)[0] ?? \"\";\n\t\tconst match = COMMAND_TOKEN.exec(firstLine);\n\t\tif (!match) {\n\t\t\treturn lines;\n\t\t}\n\t\tconst [, indent, token] = match;\n\t\t// The rendered row must still begin with that token, or the view has\n\t\t// scrolled and the highlight would land on unrelated text.\n\t\tif (!stripAnsi(lines[1].split(CURSOR_MARKER).join(\"\")).startsWith(`${indent}${token}`)) {\n\t\t\treturn lines;\n\t\t}\n\t\tconst start = visibleWidth(indent);\n\t\tconst updated = [...lines];\n\t\tupdated[1] = colorVisibleRange(lines[1], start, start + visibleWidth(token), commandColor);\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Replace the sole content line with the placeholder while the input is\n\t * empty. Index 1 is always the first content line, and an empty editor has\n\t * exactly one, so no line classification is needed here.\n\t */\n\tprivate withPlaceholder(lines: string[]): string[] {\n\t\tconst placeholder = this.placeholder;\n\t\tif (!placeholder || lines.length < 2 || this.getText().length > 0 || this.isShowingAutocomplete()) {\n\t\t\treturn lines;\n\t\t}\n\t\tconst contentLine = lines[1];\n\t\t// Preserve the cursor cell when focused; unfocused there is none, and the\n\t\t// placeholder simply starts at column zero.\n\t\tconst cursorCell = this.sliceLeadingCursorCell(contentLine);\n\t\t// Measure with visibleWidth alone — never visibleWidth(stripAnsi(...)),\n\t\t// which scores the cursor marker as seven columns. See hazard 1 at the top\n\t\t// of this file.\n\t\tconst available = visibleWidth(contentLine) - visibleWidth(cursorCell);\n\t\tif (available <= 0) {\n\t\t\treturn lines;\n\t\t}\n\t\t// Cut by display width, not by String.slice: `available` counts terminal\n\t\t// columns, while slice counts UTF-16 code units. They only agree for\n\t\t// plain ASCII, and disagreeing splits a surrogate pair or overruns the\n\t\t// line on the first wide character.\n\t\tconst text = truncateToWidth(placeholder, available, \"\");\n\t\tconst padding = \" \".repeat(Math.max(0, available - visibleWidth(text)));\n\t\tconst updated = [...lines];\n\t\tupdated[1] = cursorCell + this.placeholderColor(text) + padding;\n\t\treturn updated;\n\t}\n\n\t/**\n\t * The base Editor renders an empty line as the hardware-cursor marker\n\t * followed by a reverse-video cell: `CURSOR_MARKER ESC[7m <space> ESC[0m`.\n\t * Both parts are kept verbatim.\n\t *\n\t * The `ESC[7m … ESC[0m` run is matched explicitly. Do not \"simplify\" this to\n\t * a general leading-SGR match such as `(?:\\x1b\\[[0-9;]*m)*` — see hazard 2 at\n\t * the top of this file for what that breaks.\n\t */\n\tprivate sliceLeadingCursorCell(line: string): string {\n\t\tconst match = /^(?:\\x1b_pi:c\\x07)?(?:\\x1b\\[7m.\\x1b\\[(?:0|27)m)?/.exec(line);\n\t\treturn match?.[0] ?? \"\";\n\t}\n\n\t/**\n\t * Register a handler for an app action.\n\t */\n\tonAction(action: AppKeybinding, handler: () => void): void {\n\t\tthis.actionHandlers.set(action, handler);\n\t}\n\n\thandleInput(data: string): void {\n\t\t// Check extension-registered shortcuts first\n\t\tif (this.onExtensionShortcut?.(data)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Check for clipboard paste keybinding\n\t\tif (this.keybindings.matches(data, \"app.clipboard.pasteImage\")) {\n\t\t\tthis.onPasteImage?.();\n\t\t\treturn;\n\t\t}\n\n\t\t// Check app keybindings first\n\n\t\t// Escape/interrupt - only if autocomplete is NOT active\n\t\tif (this.keybindings.matches(data, \"app.interrupt\")) {\n\t\t\tif (!this.isShowingAutocomplete()) {\n\t\t\t\t// Use dynamic onEscape if set, otherwise registered handler\n\t\t\t\tconst handler = this.onEscape ?? this.actionHandlers.get(\"app.interrupt\");\n\t\t\t\tif (handler) {\n\t\t\t\t\thandler();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Let parent handle escape for autocomplete cancellation\n\t\t\tsuper.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Exit (Ctrl+D) - only when editor is empty\n\t\tif (this.keybindings.matches(data, \"app.exit\")) {\n\t\t\tif (this.getText().length === 0) {\n\t\t\t\tconst handler = this.onCtrlD ?? this.actionHandlers.get(\"app.exit\");\n\t\t\t\tif (handler) handler();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Fall through to editor handling for delete-char-forward when not empty\n\t\t}\n\n\t\t// Explicit history bindings take precedence over app actions while the editor is focused.\n\t\t// This lets users bind Ctrl+P even though it cycles models by default.\n\t\tif (\n\t\t\tthis.keybindings.matches(data, \"tui.editor.historyPrevious\") ||\n\t\t\tthis.keybindings.matches(data, \"tui.editor.historyNext\")\n\t\t) {\n\t\t\tsuper.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check all other app actions\n\t\tfor (const [action, handler] of this.actionHandlers) {\n\t\t\tif (action !== \"app.interrupt\" && action !== \"app.exit\" && this.keybindings.matches(data, action)) {\n\t\t\t\thandler();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Pass to parent for editor handling\n\t\tsuper.handleInput(data);\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"first-time-setup.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-time-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgC,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"first-time-setup.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-time-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgC,MAAM,wBAAwB,CAAC;AAGjF,OAAO,EAAE,KAAK,aAAa,EAAS,MAAM,mBAAmB,CAAC;AAI9D,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACrC,aAAa,EAAE,aAAa,CAAC;IAC7B,cAAc,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACjD,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AASD,6CAA6C;AAC7C,qBAAa,uBAAwB,SAAQ,SAAS;IACrD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAEhD,YAAY,OAAO,EAAE,qBAAqB,EAQzC;IAGD,OAAO,CAAC,MAAM;IAmCd,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,aAAa;IASrB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAWjC;CACD","sourcesContent":["import { Container, getKeybindings, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport { APP_NAME } from \"../../../config.ts\";\nimport { APEX_PEAK_LOGO_COMPACT } from \"../../../themes/apex-logo.ts\";\nimport { type TerminalTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface FirstTimeSetupResult {\n\ttheme: TerminalTheme;\n}\n\nexport interface FirstTimeSetupOptions {\n\tdetectedTheme: TerminalTheme;\n\tonThemePreview: (themeName: TerminalTheme) => void;\n\tonSubmit: (result: FirstTimeSetupResult) => void;\n\tonCancel: () => void;\n}\n\nconst THEME_OPTIONS: Array<{ value: TerminalTheme; label: string }> = [\n\t{ value: \"dark\", label: \"Dark\" },\n\t{ value: \"light\", label: \"Light\" },\n];\n\nconst SETUP_LOGO_LINES = APEX_PEAK_LOGO_COMPACT.split(\"\\n\");\n\n/** First-time setup dialog: theme choice. */\nexport class FirstTimeSetupComponent extends Container {\n\tprivate themeIndex: number;\n\tprivate readonly options: FirstTimeSetupOptions;\n\n\tconstructor(options: FirstTimeSetupOptions) {\n\t\tsuper();\n\t\tthis.options = options;\n\t\tthis.themeIndex = Math.max(\n\t\t\t0,\n\t\t\tTHEME_OPTIONS.findIndex((option) => option.value === options.detectedTheme),\n\t\t);\n\t\tthis.update();\n\t}\n\n\t// Rebuild the whole dialog on every change so theme previews recolor all text.\n\tprivate update(): void {\n\t\tthis.clear();\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", SETUP_LOGO_LINES.join(\"\\n\")), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(theme.fg(\"accent\", theme.bold(`Welcome to ${APP_NAME}, the minimal coding agent.`)), 1, 0),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.addChild(new Text(theme.fg(\"text\", \"Pick a theme.\"), 1, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", `Detected system appearance: ${this.options.detectedTheme}`), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addOptionList(\n\t\t\tTHEME_OPTIONS.map((option) => option.label),\n\t\t\tthis.themeIndex,\n\t\t);\n\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", \"finish\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"skip setup\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tprivate addOptionList(labels: string[], selectedIndex: number): void {\n\t\tfor (let i = 0; i < labels.length; i++) {\n\t\t\tconst isSelected = i === selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst label = isSelected ? theme.fg(\"accent\", labels[i]) : theme.fg(\"text\", labels[i]);\n\t\t\tthis.addChild(new Text(`${prefix}${label}`, 1, 0));\n\t\t}\n\t}\n\n\tprivate moveSelection(delta: number): void {\n\t\tconst next = Math.max(0, Math.min(THEME_OPTIONS.length - 1, this.themeIndex + delta));\n\t\tif (next !== this.themeIndex) {\n\t\t\tthis.themeIndex = next;\n\t\t\tthis.options.onThemePreview(THEME_OPTIONS[this.themeIndex].value);\n\t\t}\n\t\tthis.update();\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.moveSelection(-1);\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.moveSelection(1);\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tthis.options.onSubmit({ theme: THEME_OPTIONS[this.themeIndex].value });\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.options.onCancel();\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
|
|
2
2
|
import { APP_NAME } from "../../../config.js";
|
|
3
|
+
import { APEX_PEAK_LOGO_COMPACT } from "../../../themes/apex-logo.js";
|
|
3
4
|
import { theme } from "../theme/theme.js";
|
|
4
5
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
5
6
|
import { keyHint, rawKeyHint } from "./keybinding-hints.js";
|
|
@@ -7,7 +8,7 @@ const THEME_OPTIONS = [
|
|
|
7
8
|
{ value: "dark", label: "Dark" },
|
|
8
9
|
{ value: "light", label: "Light" },
|
|
9
10
|
];
|
|
10
|
-
const SETUP_LOGO_LINES =
|
|
11
|
+
const SETUP_LOGO_LINES = APEX_PEAK_LOGO_COMPACT.split("\n");
|
|
11
12
|
/** First-time setup dialog: theme choice. */
|
|
12
13
|
export class FirstTimeSetupComponent extends Container {
|
|
13
14
|
themeIndex;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"first-time-setup.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-time-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAsB,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAa5D,MAAM,aAAa,GAAmD;IACrE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;IAChC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;CAClC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"first-time-setup.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-time-setup.ts"],"names":[],"mappings":"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","sourcesContent":["import { Container, getKeybindings, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport { APP_NAME } from \"../../../config.ts\";\nimport { APEX_PEAK_LOGO_COMPACT } from \"../../../themes/apex-logo.ts\";\nimport { type TerminalTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface FirstTimeSetupResult {\n\ttheme: TerminalTheme;\n}\n\nexport interface FirstTimeSetupOptions {\n\tdetectedTheme: TerminalTheme;\n\tonThemePreview: (themeName: TerminalTheme) => void;\n\tonSubmit: (result: FirstTimeSetupResult) => void;\n\tonCancel: () => void;\n}\n\nconst THEME_OPTIONS: Array<{ value: TerminalTheme; label: string }> = [\n\t{ value: \"dark\", label: \"Dark\" },\n\t{ value: \"light\", label: \"Light\" },\n];\n\nconst SETUP_LOGO_LINES = APEX_PEAK_LOGO_COMPACT.split(\"\\n\");\n\n/** First-time setup dialog: theme choice. */\nexport class FirstTimeSetupComponent extends Container {\n\tprivate themeIndex: number;\n\tprivate readonly options: FirstTimeSetupOptions;\n\n\tconstructor(options: FirstTimeSetupOptions) {\n\t\tsuper();\n\t\tthis.options = options;\n\t\tthis.themeIndex = Math.max(\n\t\t\t0,\n\t\t\tTHEME_OPTIONS.findIndex((option) => option.value === options.detectedTheme),\n\t\t);\n\t\tthis.update();\n\t}\n\n\t// Rebuild the whole dialog on every change so theme previews recolor all text.\n\tprivate update(): void {\n\t\tthis.clear();\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", SETUP_LOGO_LINES.join(\"\\n\")), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(theme.fg(\"accent\", theme.bold(`Welcome to ${APP_NAME}, the minimal coding agent.`)), 1, 0),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.addChild(new Text(theme.fg(\"text\", \"Pick a theme.\"), 1, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", `Detected system appearance: ${this.options.detectedTheme}`), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addOptionList(\n\t\t\tTHEME_OPTIONS.map((option) => option.label),\n\t\t\tthis.themeIndex,\n\t\t);\n\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", \"finish\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"skip setup\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tprivate addOptionList(labels: string[], selectedIndex: number): void {\n\t\tfor (let i = 0; i < labels.length; i++) {\n\t\t\tconst isSelected = i === selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst label = isSelected ? theme.fg(\"accent\", labels[i]) : theme.fg(\"text\", labels[i]);\n\t\t\tthis.addChild(new Text(`${prefix}${label}`, 1, 0));\n\t\t}\n\t}\n\n\tprivate moveSelection(delta: number): void {\n\t\tconst next = Math.max(0, Math.min(THEME_OPTIONS.length - 1, this.themeIndex + delta));\n\t\tif (next !== this.themeIndex) {\n\t\t\tthis.themeIndex = next;\n\t\t\tthis.options.onThemePreview(THEME_OPTIONS[this.themeIndex].value);\n\t\t}\n\t\tthis.update();\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.moveSelection(-1);\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.moveSelection(1);\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tthis.options.onSubmit({ theme: THEME_OPTIONS[this.themeIndex].value });\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.options.onCancel();\n\t\t}\n\t}\n}\n"]}
|
|
@@ -23,6 +23,7 @@ export { SessionSelectorComponent } from "./session-selector.ts";
|
|
|
23
23
|
export { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from "./settings-selector.ts";
|
|
24
24
|
export { ShowImagesSelectorComponent } from "./show-images-selector.ts";
|
|
25
25
|
export { SkillInvocationMessageComponent } from "./skill-invocation-message.ts";
|
|
26
|
+
export { ApexSplashHeader, type ApexSplashHeaderOptions, type SplashHeaderMetadataLine, } from "./splash-header.ts";
|
|
26
27
|
export { ThemeSelectorComponent } from "./theme-selector.ts";
|
|
27
28
|
export { ThinkingSelectorComponent } from "./thinking-selector.ts";
|
|
28
29
|
export { ToolExecutionComponent, type ToolExecutionOptions } from "./tool-execution.ts";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["// UI Components for extensions\nexport { ArminComponent } from \"./armin.ts\";\nexport { AssistantMessageComponent } from \"./assistant-message.ts\";\nexport { BashExecutionComponent } from \"./bash-execution.ts\";\nexport { BorderedLoader } from \"./bordered-loader.ts\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.ts\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.ts\";\nexport { CustomEditor } from \"./custom-editor.ts\";\nexport { CustomMessageComponent } from \"./custom-message.ts\";\nexport { DaxnutsComponent } from \"./daxnuts.ts\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.ts\";\nexport { DynamicBorder } from \"./dynamic-border.ts\";\nexport { ExtensionEditorComponent } from \"./extension-editor.ts\";\nexport { ExtensionInputComponent } from \"./extension-input.ts\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.ts\";\nexport {\n\tFirstTimeSetupComponent,\n\ttype FirstTimeSetupOptions,\n\ttype FirstTimeSetupResult,\n} from \"./first-time-setup.ts\";\nexport { FooterComponent } from \"./footer.ts\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.ts\";\nexport { LoginDialogComponent } from \"./login-dialog.ts\";\nexport { ModelSelectorComponent } from \"./model-selector.ts\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.ts\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.ts\";\nexport { SessionSelectorComponent } from \"./session-selector.ts\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.ts\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.ts\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.ts\";\nexport { ThemeSelectorComponent } from \"./theme-selector.ts\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.ts\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.ts\";\nexport { TreeSelectorComponent } from \"./tree-selector.ts\";\nexport { TrustSelectorComponent } from \"./trust-selector.ts\";\nexport { UserMessageComponent } from \"./user-message.ts\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.ts\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EACN,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["// UI Components for extensions\nexport { ArminComponent } from \"./armin.ts\";\nexport { AssistantMessageComponent } from \"./assistant-message.ts\";\nexport { BashExecutionComponent } from \"./bash-execution.ts\";\nexport { BorderedLoader } from \"./bordered-loader.ts\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.ts\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.ts\";\nexport { CustomEditor } from \"./custom-editor.ts\";\nexport { CustomMessageComponent } from \"./custom-message.ts\";\nexport { DaxnutsComponent } from \"./daxnuts.ts\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.ts\";\nexport { DynamicBorder } from \"./dynamic-border.ts\";\nexport { ExtensionEditorComponent } from \"./extension-editor.ts\";\nexport { ExtensionInputComponent } from \"./extension-input.ts\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.ts\";\nexport {\n\tFirstTimeSetupComponent,\n\ttype FirstTimeSetupOptions,\n\ttype FirstTimeSetupResult,\n} from \"./first-time-setup.ts\";\nexport { FooterComponent } from \"./footer.ts\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.ts\";\nexport { LoginDialogComponent } from \"./login-dialog.ts\";\nexport { ModelSelectorComponent } from \"./model-selector.ts\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.ts\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.ts\";\nexport { SessionSelectorComponent } from \"./session-selector.ts\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.ts\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.ts\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.ts\";\nexport {\n\tApexSplashHeader,\n\ttype ApexSplashHeaderOptions,\n\ttype SplashHeaderMetadataLine,\n} from \"./splash-header.ts\";\nexport { ThemeSelectorComponent } from \"./theme-selector.ts\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.ts\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.ts\";\nexport { TreeSelectorComponent } from \"./tree-selector.ts\";\nexport { TrustSelectorComponent } from \"./trust-selector.ts\";\nexport { UserMessageComponent } from \"./user-message.ts\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.ts\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.ts\";\n"]}
|
|
@@ -24,6 +24,7 @@ export { SessionSelectorComponent } from "./session-selector.js";
|
|
|
24
24
|
export { SettingsSelectorComponent } from "./settings-selector.js";
|
|
25
25
|
export { ShowImagesSelectorComponent } from "./show-images-selector.js";
|
|
26
26
|
export { SkillInvocationMessageComponent } from "./skill-invocation-message.js";
|
|
27
|
+
export { ApexSplashHeader, } from "./splash-header.js";
|
|
27
28
|
export { ThemeSelectorComponent } from "./theme-selector.js";
|
|
28
29
|
export { ThinkingSelectorComponent } from "./thinking-selector.js";
|
|
29
30
|
export { ToolExecutionComponent } from "./tool-execution.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAA0B,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,uBAAuB,GAGvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAA2C,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAA+C,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAA6B,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAA6B,MAAM,sBAAsB,CAAC","sourcesContent":["// UI Components for extensions\nexport { ArminComponent } from \"./armin.ts\";\nexport { AssistantMessageComponent } from \"./assistant-message.ts\";\nexport { BashExecutionComponent } from \"./bash-execution.ts\";\nexport { BorderedLoader } from \"./bordered-loader.ts\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.ts\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.ts\";\nexport { CustomEditor } from \"./custom-editor.ts\";\nexport { CustomMessageComponent } from \"./custom-message.ts\";\nexport { DaxnutsComponent } from \"./daxnuts.ts\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.ts\";\nexport { DynamicBorder } from \"./dynamic-border.ts\";\nexport { ExtensionEditorComponent } from \"./extension-editor.ts\";\nexport { ExtensionInputComponent } from \"./extension-input.ts\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.ts\";\nexport {\n\tFirstTimeSetupComponent,\n\ttype FirstTimeSetupOptions,\n\ttype FirstTimeSetupResult,\n} from \"./first-time-setup.ts\";\nexport { FooterComponent } from \"./footer.ts\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.ts\";\nexport { LoginDialogComponent } from \"./login-dialog.ts\";\nexport { ModelSelectorComponent } from \"./model-selector.ts\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.ts\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.ts\";\nexport { SessionSelectorComponent } from \"./session-selector.ts\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.ts\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.ts\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.ts\";\nexport { ThemeSelectorComponent } from \"./theme-selector.ts\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.ts\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.ts\";\nexport { TreeSelectorComponent } from \"./tree-selector.ts\";\nexport { TrustSelectorComponent } from \"./trust-selector.ts\";\nexport { UserMessageComponent } from \"./user-message.ts\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.ts\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAA0B,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,uBAAuB,GAGvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAA2C,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAA+C,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EACN,gBAAgB,GAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAA6B,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAA6B,MAAM,sBAAsB,CAAC","sourcesContent":["// UI Components for extensions\nexport { ArminComponent } from \"./armin.ts\";\nexport { AssistantMessageComponent } from \"./assistant-message.ts\";\nexport { BashExecutionComponent } from \"./bash-execution.ts\";\nexport { BorderedLoader } from \"./bordered-loader.ts\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.ts\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.ts\";\nexport { CustomEditor } from \"./custom-editor.ts\";\nexport { CustomMessageComponent } from \"./custom-message.ts\";\nexport { DaxnutsComponent } from \"./daxnuts.ts\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.ts\";\nexport { DynamicBorder } from \"./dynamic-border.ts\";\nexport { ExtensionEditorComponent } from \"./extension-editor.ts\";\nexport { ExtensionInputComponent } from \"./extension-input.ts\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.ts\";\nexport {\n\tFirstTimeSetupComponent,\n\ttype FirstTimeSetupOptions,\n\ttype FirstTimeSetupResult,\n} from \"./first-time-setup.ts\";\nexport { FooterComponent } from \"./footer.ts\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.ts\";\nexport { LoginDialogComponent } from \"./login-dialog.ts\";\nexport { ModelSelectorComponent } from \"./model-selector.ts\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.ts\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.ts\";\nexport { SessionSelectorComponent } from \"./session-selector.ts\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.ts\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.ts\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.ts\";\nexport {\n\tApexSplashHeader,\n\ttype ApexSplashHeaderOptions,\n\ttype SplashHeaderMetadataLine,\n} from \"./splash-header.ts\";\nexport { ThemeSelectorComponent } from \"./theme-selector.ts\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.ts\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.ts\";\nexport { TreeSelectorComponent } from \"./tree-selector.ts\";\nexport { TrustSelectorComponent } from \"./trust-selector.ts\";\nexport { UserMessageComponent } from \"./user-message.ts\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.ts\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.ts\";\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Component } from "@earendil-works/pi-tui";
|
|
2
|
+
/** One `label value` row in the metadata column. */
|
|
3
|
+
export interface SplashHeaderMetadataLine {
|
|
4
|
+
label: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ApexSplashHeaderOptions {
|
|
8
|
+
/** Rows appended after `cwd`, for extensions that want to contribute context. */
|
|
9
|
+
getExtraMetadata?: () => readonly SplashHeaderMetadataLine[];
|
|
10
|
+
/** Hint rendered under the metadata block. Return undefined for none. */
|
|
11
|
+
getHint?: () => string | undefined;
|
|
12
|
+
/** Resolves `terminal.symbolPreset`. Defaults to unicode. */
|
|
13
|
+
getSymbolPreset?: () => "unicode" | "ascii";
|
|
14
|
+
/** Emit a leading blank line above the mark. */
|
|
15
|
+
topPadding?: boolean;
|
|
16
|
+
/** Override the mark. Used by tests and by extensions that rebrand. */
|
|
17
|
+
logo?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Startup header: brand mark on the left, runtime metadata on the right.
|
|
21
|
+
*
|
|
22
|
+
* Values are read through getters rather than captured at construction, so the
|
|
23
|
+
* model fills in once the session binds without an explicit refresh.
|
|
24
|
+
*
|
|
25
|
+
* The metadata column is dropped when the terminal cannot hold it, and the mark
|
|
26
|
+
* falls back to a compact variant rather than being truncated mid-glyph.
|
|
27
|
+
*/
|
|
28
|
+
export declare class ApexSplashHeader implements Component {
|
|
29
|
+
private readonly version;
|
|
30
|
+
private readonly getModelId;
|
|
31
|
+
private readonly getCwd;
|
|
32
|
+
private readonly verboseInstructions;
|
|
33
|
+
private readonly options;
|
|
34
|
+
constructor(version: string, getModelId: () => string | undefined, getCwd: () => string, verboseInstructions?: string, options?: ApexSplashHeaderOptions);
|
|
35
|
+
invalidate(): void;
|
|
36
|
+
render(width: number): string[];
|
|
37
|
+
private selectLogo;
|
|
38
|
+
private buildMetadata;
|
|
39
|
+
private padLine;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=splash-header.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"splash-header.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/splash-header.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AAKvF,qDAAqD;AACrD,MAAM,WAAW,wBAAwB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACvC,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,SAAS,wBAAwB,EAAE,CAAC;IAC7D,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACnC,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,SAAS,GAAG,OAAO,CAAC;IAC5C,gDAAgD;IAChD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AA8DD;;;;;;;;GAQG;AACH,qBAAa,gBAAiB,YAAW,SAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAElD,YACC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,MAAM,GAAG,SAAS,EACpC,MAAM,EAAE,MAAM,MAAM,EACpB,mBAAmB,CAAC,EAAE,MAAM,EAC5B,OAAO,GAAE,uBAA4B,EAOrC;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAwC9B;IAED,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,OAAO;CAIf","sourcesContent":["import { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport { APEX_PEAK_LOGO, APEX_PEAK_LOGO_ASCII, APEX_PEAK_LOGO_COMPACT } from \"../../../themes/apex-logo.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { formatCwdForFooter } from \"./footer.ts\";\n\n/** One `label value` row in the metadata column. */\nexport interface SplashHeaderMetadataLine {\n\tlabel: string;\n\tvalue: string;\n}\n\nexport interface ApexSplashHeaderOptions {\n\t/** Rows appended after `cwd`, for extensions that want to contribute context. */\n\tgetExtraMetadata?: () => readonly SplashHeaderMetadataLine[];\n\t/** Hint rendered under the metadata block. Return undefined for none. */\n\tgetHint?: () => string | undefined;\n\t/** Resolves `terminal.symbolPreset`. Defaults to unicode. */\n\tgetSymbolPreset?: () => \"unicode\" | \"ascii\";\n\t/** Emit a leading blank line above the mark. */\n\ttopPadding?: boolean;\n\t/** Override the mark. Used by tests and by extensions that rebrand. */\n\tlogo?: string;\n}\n\nconst GUTTER = 4;\nconst LABEL_WIDTH = 9;\n/** Smallest value column worth rendering; below this the metadata column is dropped. */\nconst MIN_VALUE_WIDTH = 8;\n\n/**\n * Keep the rightmost path segments that fit, so the leaf directory stays visible.\n *\n * `truncateToWidth` clips the tail, which for a path hides the part that\n * identifies the session. This clips the head instead.\n */\nfunction truncatePathTail(path: string, maxWidth: number): string {\n\tif (maxWidth <= 0) {\n\t\treturn \"\";\n\t}\n\tif (visibleWidth(path) <= maxWidth) {\n\t\treturn path;\n\t}\n\tconst ellipsis = \"…/\";\n\tconst budget = Math.max(0, maxWidth - visibleWidth(ellipsis));\n\tconst segments = path.split(\"/\");\n\tlet tail = \"\";\n\tfor (let index = segments.length - 1; index >= 0; index--) {\n\t\tconst candidate = tail ? `${segments[index]}/${tail}` : segments[index];\n\t\tif (visibleWidth(candidate) > budget) {\n\t\t\tbreak;\n\t\t}\n\t\ttail = candidate;\n\t}\n\t// A single segment longer than the whole budget: clip it from the left.\n\treturn tail ? ellipsis + tail : ellipsis + trailingByWidth(path, budget);\n}\n\n/**\n * The widest suffix of `text` that fits `maxWidth` terminal columns.\n *\n * Walks grapheme clusters from the right rather than using `String.slice`, whose\n * UTF-16 offsets would cut an astral character in half — emitting a lone\n * surrogate — and would miscount every double-width character on the way.\n */\nfunction trailingByWidth(text: string, maxWidth: number): string {\n\tif (maxWidth <= 0) {\n\t\treturn \"\";\n\t}\n\tconst graphemes = [...new Intl.Segmenter(undefined, { granularity: \"grapheme\" }).segment(text)].map(\n\t\t(entry) => entry.segment,\n\t);\n\tlet width = 0;\n\tlet index = graphemes.length;\n\twhile (index > 0) {\n\t\tconst next = width + visibleWidth(graphemes[index - 1]);\n\t\tif (next > maxWidth) {\n\t\t\tbreak;\n\t\t}\n\t\twidth = next;\n\t\tindex -= 1;\n\t}\n\treturn graphemes.slice(index).join(\"\");\n}\n\n/**\n * Startup header: brand mark on the left, runtime metadata on the right.\n *\n * Values are read through getters rather than captured at construction, so the\n * model fills in once the session binds without an explicit refresh.\n *\n * The metadata column is dropped when the terminal cannot hold it, and the mark\n * falls back to a compact variant rather than being truncated mid-glyph.\n */\nexport class ApexSplashHeader implements Component {\n\tprivate readonly version: string;\n\tprivate readonly getModelId: () => string | undefined;\n\tprivate readonly getCwd: () => string;\n\tprivate readonly verboseInstructions: string | undefined;\n\tprivate readonly options: ApexSplashHeaderOptions;\n\n\tconstructor(\n\t\tversion: string,\n\t\tgetModelId: () => string | undefined,\n\t\tgetCwd: () => string,\n\t\tverboseInstructions?: string,\n\t\toptions: ApexSplashHeaderOptions = {},\n\t) {\n\t\tthis.version = version;\n\t\tthis.getModelId = getModelId;\n\t\tthis.getCwd = getCwd;\n\t\tthis.verboseInstructions = verboseInstructions;\n\t\tthis.options = options;\n\t}\n\n\tinvalidate(): void {\n\t\t// Render output is derived from current theme and session state.\n\t}\n\n\trender(width: number): string[] {\n\t\tconst safeWidth = Math.max(1, width);\n\t\tconst paddingX = safeWidth > 1 ? 1 : 0;\n\t\tconst contentWidth = Math.max(1, safeWidth - paddingX * 2);\n\n\t\tconst logoLines = this.selectLogo(contentWidth);\n\t\tconst logoWidth = logoLines.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);\n\n\t\tconst metaWidth = contentWidth - logoWidth - GUTTER;\n\t\tconst showMeta = metaWidth >= LABEL_WIDTH + MIN_VALUE_WIDTH;\n\t\tconst valueWidth = Math.max(1, metaWidth - LABEL_WIDTH);\n\t\tconst metaLines = showMeta ? this.buildMetadata(valueWidth) : [];\n\t\tconst metaStart = Math.max(0, Math.floor((logoLines.length - metaLines.length) / 2));\n\n\t\tconst lines = this.options.topPadding ? [this.padLine(\"\", safeWidth, paddingX)] : [];\n\n\t\tlogoLines.forEach((line, index) => {\n\t\t\tconst meta = index >= metaStart && index < metaStart + metaLines.length ? metaLines[index - metaStart] : \"\";\n\t\t\tconst gap = showMeta ? \" \".repeat(Math.max(0, logoWidth - visibleWidth(line) + GUTTER)) : \"\";\n\t\t\tconst content = truncateToWidth(theme.fg(\"text\", line) + gap + meta, contentWidth, \"\");\n\t\t\tlines.push(this.padLine(content, safeWidth, paddingX));\n\t\t});\n\n\t\t// The hint is product copy rather than a runtime fact, so it sits under the\n\t\t// whole block at full width instead of inside the narrow metadata column,\n\t\t// where it would truncate even on a wide terminal.\n\t\tconst hint = this.options.getHint?.();\n\t\tif (hint) {\n\t\t\tlines.push(this.padLine(\"\", safeWidth, paddingX));\n\t\t\tlines.push(this.padLine(theme.fg(\"dim\", truncateToWidth(hint, contentWidth)), safeWidth, paddingX));\n\t\t}\n\n\t\tif (this.verboseInstructions) {\n\t\t\tlines.push(this.padLine(\"\", safeWidth, paddingX));\n\t\t\tfor (const instruction of this.verboseInstructions.split(\"\\n\")) {\n\t\t\t\tlines.push(this.padLine(truncateToWidth(instruction, contentWidth), safeWidth, paddingX));\n\t\t\t}\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\tprivate selectLogo(contentWidth: number): string[] {\n\t\tif (this.options.logo) {\n\t\t\treturn this.options.logo.split(\"\\n\");\n\t\t}\n\t\tif (this.options.getSymbolPreset?.() === \"ascii\") {\n\t\t\treturn APEX_PEAK_LOGO_ASCII.split(\"\\n\");\n\t\t}\n\t\tconst full = APEX_PEAK_LOGO.split(\"\\n\");\n\t\tconst fullWidth = full.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);\n\t\treturn contentWidth >= fullWidth ? full : APEX_PEAK_LOGO_COMPACT.split(\"\\n\");\n\t}\n\n\tprivate buildMetadata(valueWidth: number): string[] {\n\t\tconst labelled = (label: string, value: string) =>\n\t\t\ttheme.fg(\"dim\", label.padEnd(LABEL_WIDTH)) + theme.fg(\"muted\", truncateToWidth(value, valueWidth));\n\n\t\tconst cwd = formatCwdForFooter(this.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\t\treturn [\n\t\t\tlabelled(\"version\", `v${this.version}`),\n\t\t\tlabelled(\"model\", this.getModelId() ?? \"—\"),\n\t\t\ttheme.fg(\"dim\", \"cwd\".padEnd(LABEL_WIDTH)) + theme.fg(\"muted\", truncatePathTail(cwd, valueWidth)),\n\t\t\t...(this.options.getExtraMetadata?.() ?? []).map((line) => labelled(line.label, line.value)),\n\t\t];\n\t}\n\n\tprivate padLine(content: string, safeWidth: number, paddingX: number): string {\n\t\tconst trailing = Math.max(0, safeWidth - paddingX - visibleWidth(content));\n\t\treturn \" \".repeat(paddingX) + content + \" \".repeat(trailing);\n\t}\n}\n"]}
|