@bubblebrain-ai/bubble 0.0.11 → 0.0.13
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/dist/agent/input-controller.d.ts +11 -0
- package/dist/agent/input-controller.js +30 -0
- package/dist/agent.d.ts +6 -4
- package/dist/agent.js +39 -2
- package/dist/feishu/agent-host/run-driver.js +13 -6
- package/dist/feishu/agent-host/runtime-deps.d.ts +2 -2
- package/dist/feishu/router/commands.js +2 -1
- package/dist/feishu/scope/session-binder.js +1 -1
- package/dist/feishu/serve.js +3 -3
- package/dist/main.js +78 -12
- package/dist/prompt/compose.js +3 -3
- package/dist/prompt/environment.js +2 -0
- package/dist/prompt/reminders.js +1 -1
- package/dist/provider-openai-codex.d.ts +8 -1
- package/dist/provider-openai-codex.js +33 -9
- package/dist/provider.d.ts +2 -0
- package/dist/session-title.d.ts +16 -0
- package/dist/session-title.js +134 -0
- package/dist/session-types.d.ts +5 -0
- package/dist/session.d.ts +5 -0
- package/dist/session.js +75 -9
- package/dist/skills/invocation.js +0 -18
- package/dist/skills/registry.d.ts +1 -0
- package/dist/skills/registry.js +2 -0
- package/dist/slash-commands/commands.js +29 -22
- package/dist/slash-commands/registry.js +1 -1
- package/dist/slash-commands/types.d.ts +10 -0
- package/dist/text-display.d.ts +3 -0
- package/dist/text-display.js +25 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +3 -1
- package/dist/tools/skill-search.d.ts +10 -0
- package/dist/tools/skill-search.js +134 -0
- package/dist/tools/skill.js +1 -4
- package/dist/tui/clipboard.d.ts +1 -0
- package/dist/tui/clipboard.js +53 -0
- package/dist/tui/detect-theme.d.ts +2 -0
- package/dist/tui/detect-theme.js +87 -0
- package/dist/tui/display-history.d.ts +62 -0
- package/dist/tui/display-history.js +305 -0
- package/dist/tui/edit-diff.d.ts +11 -0
- package/dist/tui/edit-diff.js +52 -0
- package/dist/tui/escape-confirmation.d.ts +15 -0
- package/dist/tui/escape-confirmation.js +30 -0
- package/dist/tui/file-mentions.d.ts +29 -0
- package/dist/tui/file-mentions.js +174 -0
- package/dist/tui/global-key-router.d.ts +3 -0
- package/dist/tui/global-key-router.js +87 -0
- package/dist/tui/image-paste.d.ts +95 -0
- package/dist/tui/image-paste.js +505 -0
- package/dist/tui/input-history.d.ts +16 -0
- package/dist/tui/input-history.js +79 -0
- package/dist/tui/markdown-inline.d.ts +22 -0
- package/dist/tui/markdown-inline.js +68 -0
- package/dist/tui/markdown-theme-rules.d.ts +23 -0
- package/dist/tui/markdown-theme-rules.js +164 -0
- package/dist/tui/markdown-theme.d.ts +5 -0
- package/dist/tui/markdown-theme.js +27 -0
- package/dist/tui/opencode-spinner.d.ts +22 -0
- package/dist/tui/opencode-spinner.js +216 -0
- package/dist/tui/prompt-keybindings.d.ts +42 -0
- package/dist/tui/prompt-keybindings.js +35 -0
- package/dist/tui/recent-activity.d.ts +8 -0
- package/dist/tui/recent-activity.js +71 -0
- package/dist/tui/render-signature.d.ts +1 -0
- package/dist/tui/render-signature.js +7 -0
- package/dist/tui/run.d.ts +45 -0
- package/dist/tui/run.js +8816 -0
- package/dist/tui/session-display.d.ts +6 -0
- package/dist/tui/session-display.js +12 -0
- package/dist/tui/sidebar-mcp.d.ts +31 -0
- package/dist/tui/sidebar-mcp.js +62 -0
- package/dist/tui/sidebar-state.d.ts +12 -0
- package/dist/tui/sidebar-state.js +69 -0
- package/dist/tui/streaming-tool-args.d.ts +15 -0
- package/dist/tui/streaming-tool-args.js +30 -0
- package/dist/tui/tool-renderers/fallback.d.ts +2 -0
- package/dist/tui/tool-renderers/fallback.js +75 -0
- package/dist/tui/tool-renderers/registry.d.ts +3 -0
- package/dist/tui/tool-renderers/registry.js +11 -0
- package/dist/tui/tool-renderers/subagent.d.ts +2 -0
- package/dist/tui/tool-renderers/subagent.js +135 -0
- package/dist/tui/tool-renderers/types.d.ts +36 -0
- package/dist/tui/tool-renderers/types.js +1 -0
- package/dist/tui/tool-renderers/write-preview.d.ts +12 -0
- package/dist/tui/tool-renderers/write-preview.js +30 -0
- package/dist/tui/tool-renderers/write.d.ts +6 -0
- package/dist/tui/tool-renderers/write.js +88 -0
- package/dist/tui/trace-groups.d.ts +27 -0
- package/dist/tui/trace-groups.js +412 -0
- package/dist/tui/wordmark.d.ts +15 -0
- package/dist/tui/wordmark.js +179 -0
- package/dist/tui-ink/app.js +98 -70
- package/dist/tui-ink/input-box.d.ts +22 -1
- package/dist/tui-ink/input-box.js +105 -11
- package/dist/tui-ink/message-list.js +12 -3
- package/dist/tui-ink/model-picker.d.ts +18 -0
- package/dist/tui-ink/model-picker.js +80 -23
- package/dist/tui-ink/session-picker.js +5 -7
- package/dist/tui-ink/theme.d.ts +3 -9
- package/dist/tui-ink/theme.js +39 -45
- package/dist/tui-ink/welcome.js +22 -78
- package/dist/tui-opentui/app.d.ts +54 -0
- package/dist/tui-opentui/app.js +1363 -0
- package/dist/tui-opentui/approval/approval-dialog.d.ts +15 -0
- package/dist/tui-opentui/approval/approval-dialog.js +139 -0
- package/dist/tui-opentui/approval/diff-view.d.ts +9 -0
- package/dist/tui-opentui/approval/diff-view.js +43 -0
- package/dist/tui-opentui/approval/select.d.ts +37 -0
- package/dist/tui-opentui/approval/select.js +91 -0
- package/dist/tui-opentui/detect-theme.d.ts +2 -0
- package/dist/tui-opentui/detect-theme.js +87 -0
- package/dist/tui-opentui/display-history.d.ts +55 -0
- package/dist/tui-opentui/display-history.js +129 -0
- package/dist/tui-opentui/edit-diff.d.ts +11 -0
- package/dist/tui-opentui/edit-diff.js +52 -0
- package/dist/tui-opentui/feedback-dialog.d.ts +21 -0
- package/dist/tui-opentui/feedback-dialog.js +164 -0
- package/dist/tui-opentui/feishu-setup-picker.d.ts +7 -0
- package/dist/tui-opentui/feishu-setup-picker.js +272 -0
- package/dist/tui-opentui/file-mentions.d.ts +29 -0
- package/dist/tui-opentui/file-mentions.js +174 -0
- package/dist/tui-opentui/footer.d.ts +26 -0
- package/dist/tui-opentui/footer.js +40 -0
- package/dist/tui-opentui/image-paste.d.ts +54 -0
- package/dist/tui-opentui/image-paste.js +288 -0
- package/dist/tui-opentui/input-box.d.ts +34 -0
- package/dist/tui-opentui/input-box.js +471 -0
- package/dist/tui-opentui/input-history.d.ts +16 -0
- package/dist/tui-opentui/input-history.js +79 -0
- package/dist/tui-opentui/markdown.d.ts +66 -0
- package/dist/tui-opentui/markdown.js +127 -0
- package/dist/tui-opentui/message-list.d.ts +31 -0
- package/dist/tui-opentui/message-list.js +125 -0
- package/dist/tui-opentui/model-picker.d.ts +63 -0
- package/dist/tui-opentui/model-picker.js +450 -0
- package/dist/tui-opentui/plan-confirm.d.ts +9 -0
- package/dist/tui-opentui/plan-confirm.js +124 -0
- package/dist/tui-opentui/question-dialog.d.ts +10 -0
- package/dist/tui-opentui/question-dialog.js +110 -0
- package/dist/tui-opentui/recent-activity.d.ts +8 -0
- package/dist/tui-opentui/recent-activity.js +71 -0
- package/dist/tui-opentui/run-session-picker.d.ts +10 -0
- package/dist/tui-opentui/run-session-picker.js +28 -0
- package/dist/tui-opentui/run.d.ts +38 -0
- package/dist/tui-opentui/run.js +48 -0
- package/dist/tui-opentui/session-picker.d.ts +12 -0
- package/dist/tui-opentui/session-picker.js +120 -0
- package/dist/tui-opentui/theme.d.ts +89 -0
- package/dist/tui-opentui/theme.js +157 -0
- package/dist/tui-opentui/todos.d.ts +9 -0
- package/dist/tui-opentui/todos.js +45 -0
- package/dist/tui-opentui/trace-groups.d.ts +27 -0
- package/dist/tui-opentui/trace-groups.js +412 -0
- package/dist/tui-opentui/use-terminal-size.d.ts +4 -0
- package/dist/tui-opentui/use-terminal-size.js +5 -0
- package/dist/tui-opentui/welcome.d.ts +25 -0
- package/dist/tui-opentui/welcome.js +77 -0
- package/dist/types.d.ts +24 -0
- package/package.json +5 -1
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import { ProviderRegistry } from "../provider-registry.js";
|
|
2
|
+
export { padVisual, truncateVisual } from "../text-display.js";
|
|
2
3
|
export interface ModelPickerOption {
|
|
3
4
|
id: string;
|
|
4
5
|
label: string;
|
|
5
6
|
group: string;
|
|
6
7
|
providerBadge: string;
|
|
7
8
|
}
|
|
9
|
+
export type PickerKeyAction = "up" | "down" | "enter" | "escape" | "backspace" | "delete";
|
|
10
|
+
export declare function resolvePickerKeyAction(input: string, key: {
|
|
11
|
+
upArrow?: boolean;
|
|
12
|
+
downArrow?: boolean;
|
|
13
|
+
return?: boolean;
|
|
14
|
+
escape?: boolean;
|
|
15
|
+
backspace?: boolean;
|
|
16
|
+
delete?: boolean;
|
|
17
|
+
}): PickerKeyAction | undefined;
|
|
18
|
+
export declare function isPrintablePickerInput(input: string): boolean;
|
|
19
|
+
export declare function formatSkillPickerRow(skill: {
|
|
20
|
+
name: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
}, options: {
|
|
23
|
+
selected: boolean;
|
|
24
|
+
width: number;
|
|
25
|
+
}): string;
|
|
8
26
|
export interface ModelPickerProps {
|
|
9
27
|
registry: ProviderRegistry;
|
|
10
28
|
current: string;
|
|
@@ -4,6 +4,55 @@ import { Box, Text, useInput, usePaste, useStdout } from "ink";
|
|
|
4
4
|
import { useTheme } from "./theme.js";
|
|
5
5
|
import { encodeModel, decodeModel, displayModel, isUserVisibleProvider } from "../provider-registry.js";
|
|
6
6
|
import { listBuiltinModels } from "../model-catalog.js";
|
|
7
|
+
import { padVisual, truncateVisual } from "../text-display.js";
|
|
8
|
+
export { padVisual, truncateVisual } from "../text-display.js";
|
|
9
|
+
export function resolvePickerKeyAction(input, key) {
|
|
10
|
+
if (key.escape)
|
|
11
|
+
return "escape";
|
|
12
|
+
if (key.return)
|
|
13
|
+
return "enter";
|
|
14
|
+
if (key.upArrow)
|
|
15
|
+
return "up";
|
|
16
|
+
if (key.downArrow)
|
|
17
|
+
return "down";
|
|
18
|
+
if (key.backspace)
|
|
19
|
+
return "backspace";
|
|
20
|
+
if (key.delete)
|
|
21
|
+
return "delete";
|
|
22
|
+
const sequence = normalizeEscapeSequence(input);
|
|
23
|
+
if (/^(?:O|\[[\d;:]*)A$/.test(sequence))
|
|
24
|
+
return "up";
|
|
25
|
+
if (/^(?:O|\[[\d;:]*)B$/.test(sequence))
|
|
26
|
+
return "down";
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
export function isPrintablePickerInput(input) {
|
|
30
|
+
if (!input)
|
|
31
|
+
return false;
|
|
32
|
+
if (input.startsWith("\x1b"))
|
|
33
|
+
return false;
|
|
34
|
+
if (isRawEscapeTail(input))
|
|
35
|
+
return false;
|
|
36
|
+
return !/[\x00-\x1f\x7f]/.test(input);
|
|
37
|
+
}
|
|
38
|
+
export function formatSkillPickerRow(skill, options) {
|
|
39
|
+
const width = Math.max(12, options.width);
|
|
40
|
+
const marker = options.selected ? "> " : " ";
|
|
41
|
+
const nameBudget = Math.max(8, Math.min(28, Math.floor(width * 0.35)));
|
|
42
|
+
const name = truncateVisual(skill.name, nameBudget);
|
|
43
|
+
const nameCell = padVisual(name, nameBudget);
|
|
44
|
+
const description = (skill.description ?? "").replace(/\s+/g, " ").trim();
|
|
45
|
+
const row = description
|
|
46
|
+
? `${marker}${nameCell} ${description}`
|
|
47
|
+
: `${marker}${nameCell}`;
|
|
48
|
+
return padVisual(truncateVisual(row, width), width);
|
|
49
|
+
}
|
|
50
|
+
function normalizeEscapeSequence(input) {
|
|
51
|
+
return input.startsWith("\x1b") ? input.slice(1) : input;
|
|
52
|
+
}
|
|
53
|
+
function isRawEscapeTail(input) {
|
|
54
|
+
return /^(?:O[ABCDHF]|\[[\d;:]*[A-Za-z~])$/.test(input);
|
|
55
|
+
}
|
|
7
56
|
export function ModelPicker({ registry, current, recent, onSelect, onCancel }) {
|
|
8
57
|
const theme = useTheme();
|
|
9
58
|
const { stdout } = useStdout();
|
|
@@ -76,25 +125,26 @@ export function ModelPicker({ registry, current, recent, onSelect, onCancel }) {
|
|
|
76
125
|
return rawOptions.filter((opt) => opt.label.toLowerCase().includes(q) || opt.providerBadge.toLowerCase().includes(q));
|
|
77
126
|
}, [rawOptions, query]);
|
|
78
127
|
useInput((input, key) => {
|
|
79
|
-
|
|
128
|
+
const action = resolvePickerKeyAction(input, key);
|
|
129
|
+
if (action === "escape") {
|
|
80
130
|
onCancel();
|
|
81
131
|
return;
|
|
82
132
|
}
|
|
83
|
-
if (
|
|
133
|
+
if (action === "enter") {
|
|
84
134
|
const opt = options[selectedIndex];
|
|
85
135
|
if (opt)
|
|
86
136
|
onSelect(opt.id);
|
|
87
137
|
return;
|
|
88
138
|
}
|
|
89
|
-
if (
|
|
139
|
+
if (action === "up") {
|
|
90
140
|
setSelectedIndex((i) => Math.max(0, i - 1));
|
|
91
141
|
return;
|
|
92
142
|
}
|
|
93
|
-
if (
|
|
143
|
+
if (action === "down") {
|
|
94
144
|
setSelectedIndex((i) => Math.min(options.length - 1, i + 1));
|
|
95
145
|
return;
|
|
96
146
|
}
|
|
97
|
-
if (
|
|
147
|
+
if (action === "backspace" || action === "delete") {
|
|
98
148
|
setQuery((q) => {
|
|
99
149
|
const next = q.slice(0, -1);
|
|
100
150
|
setSelectedIndex(0);
|
|
@@ -102,7 +152,7 @@ export function ModelPicker({ registry, current, recent, onSelect, onCancel }) {
|
|
|
102
152
|
});
|
|
103
153
|
return;
|
|
104
154
|
}
|
|
105
|
-
if (input && !key.ctrl && !key.meta) {
|
|
155
|
+
if (isPrintablePickerInput(input) && !key.ctrl && !key.meta) {
|
|
106
156
|
setQuery((q) => {
|
|
107
157
|
const next = q + input;
|
|
108
158
|
setSelectedIndex(0);
|
|
@@ -197,25 +247,26 @@ export function ProviderPicker({ providers, current, onSelect, onCancel, title }
|
|
|
197
247
|
return idx >= 0 ? idx : 0;
|
|
198
248
|
});
|
|
199
249
|
useInput((input, key) => {
|
|
200
|
-
|
|
250
|
+
const action = resolvePickerKeyAction(input, key);
|
|
251
|
+
if (action === "escape") {
|
|
201
252
|
onCancel();
|
|
202
253
|
return;
|
|
203
254
|
}
|
|
204
|
-
if (
|
|
255
|
+
if (action === "enter") {
|
|
205
256
|
const p = providers[selectedIndex];
|
|
206
257
|
if (p)
|
|
207
258
|
onSelect(p.id);
|
|
208
259
|
return;
|
|
209
260
|
}
|
|
210
|
-
if (
|
|
261
|
+
if (action === "up") {
|
|
211
262
|
setSelectedIndex((i) => Math.max(0, i - 1));
|
|
212
263
|
return;
|
|
213
264
|
}
|
|
214
|
-
if (
|
|
265
|
+
if (action === "down") {
|
|
215
266
|
setSelectedIndex((i) => Math.min(providers.length - 1, i + 1));
|
|
216
267
|
return;
|
|
217
268
|
}
|
|
218
|
-
if (input && input.length === 1 && /[a-z]/i.test(input)) {
|
|
269
|
+
if (isPrintablePickerInput(input) && input.length === 1 && /[a-z]/i.test(input)) {
|
|
219
270
|
const char = input.toLowerCase();
|
|
220
271
|
for (let i = selectedIndex + 1; i < providers.length; i++) {
|
|
221
272
|
if (providers[i].name.toLowerCase().startsWith(char)) {
|
|
@@ -243,20 +294,21 @@ export function KeyPicker({ providerName, onSubmit, onCancel }) {
|
|
|
243
294
|
const theme = useTheme();
|
|
244
295
|
const [value, setValue] = useState("");
|
|
245
296
|
useInput((input, key) => {
|
|
246
|
-
|
|
297
|
+
const action = resolvePickerKeyAction(input, key);
|
|
298
|
+
if (action === "escape") {
|
|
247
299
|
onCancel();
|
|
248
300
|
return;
|
|
249
301
|
}
|
|
250
|
-
if (
|
|
302
|
+
if (action === "enter") {
|
|
251
303
|
if (value.trim())
|
|
252
304
|
onSubmit(value.trim());
|
|
253
305
|
return;
|
|
254
306
|
}
|
|
255
|
-
if (
|
|
307
|
+
if (action === "backspace" || action === "delete") {
|
|
256
308
|
setValue((v) => v.slice(0, -1));
|
|
257
309
|
return;
|
|
258
310
|
}
|
|
259
|
-
if (input && !key.ctrl && !key.meta) {
|
|
311
|
+
if (isPrintablePickerInput(input) && !key.ctrl && !key.meta) {
|
|
260
312
|
setValue((v) => v + input);
|
|
261
313
|
}
|
|
262
314
|
});
|
|
@@ -274,7 +326,9 @@ export function SkillPicker({ skills, onSelect, onCancel }) {
|
|
|
274
326
|
const theme = useTheme();
|
|
275
327
|
const { stdout } = useStdout();
|
|
276
328
|
const termHeight = stdout?.rows || 24;
|
|
329
|
+
const terminalColumns = stdout?.columns || 80;
|
|
277
330
|
const maxVisible = Math.max(5, termHeight - 8);
|
|
331
|
+
const rowWidth = Math.max(36, Math.min(96, terminalColumns - 6));
|
|
278
332
|
const [query, setQuery] = useState("");
|
|
279
333
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
280
334
|
const options = useMemo(() => {
|
|
@@ -284,25 +338,26 @@ export function SkillPicker({ skills, onSelect, onCancel }) {
|
|
|
284
338
|
return skills.filter((skill) => skill.name.toLowerCase().includes(q) || skill.description.toLowerCase().includes(q));
|
|
285
339
|
}, [query, skills]);
|
|
286
340
|
useInput((input, key) => {
|
|
287
|
-
|
|
341
|
+
const action = resolvePickerKeyAction(input, key);
|
|
342
|
+
if (action === "escape") {
|
|
288
343
|
onCancel();
|
|
289
344
|
return;
|
|
290
345
|
}
|
|
291
|
-
if (
|
|
346
|
+
if (action === "enter") {
|
|
292
347
|
const skill = options[selectedIndex];
|
|
293
348
|
if (skill)
|
|
294
349
|
onSelect(skill.name);
|
|
295
350
|
return;
|
|
296
351
|
}
|
|
297
|
-
if (
|
|
352
|
+
if (action === "up") {
|
|
298
353
|
setSelectedIndex((i) => Math.max(0, i - 1));
|
|
299
354
|
return;
|
|
300
355
|
}
|
|
301
|
-
if (
|
|
356
|
+
if (action === "down") {
|
|
302
357
|
setSelectedIndex((i) => Math.min(Math.max(0, options.length - 1), i + 1));
|
|
303
358
|
return;
|
|
304
359
|
}
|
|
305
|
-
if (
|
|
360
|
+
if (action === "backspace" || action === "delete") {
|
|
306
361
|
setQuery((q) => {
|
|
307
362
|
const next = q.slice(0, -1);
|
|
308
363
|
setSelectedIndex(0);
|
|
@@ -310,7 +365,7 @@ export function SkillPicker({ skills, onSelect, onCancel }) {
|
|
|
310
365
|
});
|
|
311
366
|
return;
|
|
312
367
|
}
|
|
313
|
-
if (input && !key.ctrl && !key.meta) {
|
|
368
|
+
if (isPrintablePickerInput(input) && !key.ctrl && !key.meta) {
|
|
314
369
|
setQuery((q) => {
|
|
315
370
|
const next = q + input;
|
|
316
371
|
setSelectedIndex(0);
|
|
@@ -318,11 +373,13 @@ export function SkillPicker({ skills, onSelect, onCancel }) {
|
|
|
318
373
|
});
|
|
319
374
|
}
|
|
320
375
|
});
|
|
321
|
-
const
|
|
376
|
+
const maxStart = Math.max(0, options.length - maxVisible);
|
|
377
|
+
const start = Math.max(0, Math.min(maxStart, selectedIndex - Math.floor(maxVisible / 2)));
|
|
322
378
|
const visible = options.slice(start, start + maxVisible);
|
|
323
379
|
return (_jsxs(Box, { flexDirection: "column", marginY: 1, paddingX: 1, borderStyle: "round", borderColor: theme.borderActive, children: [_jsx(Text, { bold: true, color: theme.accent, children: "Select Skill" }), _jsx(SearchField, { query: query, placeholder: "Type to search skills..." }), _jsx(Text, { color: theme.muted, children: "\u2191/\u2193 navigate \u00B7 Enter load \u00B7 Esc cancel \u00B7 Backspace clear" }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [options.length === 0 && (_jsxs(Text, { color: theme.muted, children: ["No skills match \"", query, "\""] })), visible.map((skill, i) => {
|
|
324
380
|
const actualIndex = start + i;
|
|
325
381
|
const isSelected = actualIndex === selectedIndex;
|
|
326
|
-
|
|
382
|
+
const row = formatSkillPickerRow(skill, { selected: isSelected, width: rowWidth });
|
|
383
|
+
return (_jsx(Box, { children: _jsx(Text, { inverse: isSelected, color: isSelected ? theme.accent : undefined, bold: isSelected, children: row }) }, skill.name));
|
|
327
384
|
})] })] }));
|
|
328
385
|
}
|
|
@@ -3,10 +3,12 @@ import { useMemo, useState } from "react";
|
|
|
3
3
|
import { Box, Text, useInput, useStdout } from "ink";
|
|
4
4
|
import { useTheme } from "./theme.js";
|
|
5
5
|
import { formatRelativeTime } from "./recent-activity.js";
|
|
6
|
+
import { padVisual, truncateVisual } from "../text-display.js";
|
|
6
7
|
export function SessionPicker({ currentCwd, currentSessions, allSessions, onSelect, onCancel }) {
|
|
7
8
|
const theme = useTheme();
|
|
8
9
|
const { stdout } = useStdout();
|
|
9
10
|
const termHeight = stdout?.rows || 24;
|
|
11
|
+
const termWidth = stdout?.columns || 80;
|
|
10
12
|
const maxVisible = Math.max(6, termHeight - 10);
|
|
11
13
|
const [mode, setMode] = useState("current");
|
|
12
14
|
const [selectedSessionIdx, setSelectedSessionIdx] = useState(0);
|
|
@@ -55,8 +57,9 @@ export function SessionPicker({ currentCwd, currentSessions, allSessions, onSele
|
|
|
55
57
|
}
|
|
56
58
|
const session = row.session;
|
|
57
59
|
const isSelected = actualIndex === selectedRowIndex;
|
|
58
|
-
const time = formatRelativeTime(session.mtime)
|
|
59
|
-
|
|
60
|
+
const time = padVisual(formatRelativeTime(session.mtime), 9);
|
|
61
|
+
const titleWidth = Math.max(20, Math.min(80, termWidth - 30));
|
|
62
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? theme.accent : undefined, children: [isSelected ? "> " : " ", time, " ", padVisual(truncateVisual(session.title, titleWidth), titleWidth)] }), _jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: theme.muted, dimColor: true, children: ["\u00B7 ", session.messageCount, " msg", session.messageCount === 1 ? "" : "s"] }) })] }, session.file));
|
|
60
63
|
})] })] }));
|
|
61
64
|
}
|
|
62
65
|
function buildRows(mode, currentCwd, currentSessions, allSessions) {
|
|
@@ -105,8 +108,3 @@ function clampWindowStart(rows, selectedRowIndex, maxVisible) {
|
|
|
105
108
|
start = rows.length - maxVisible;
|
|
106
109
|
return Math.max(0, start);
|
|
107
110
|
}
|
|
108
|
-
function truncate(text, max) {
|
|
109
|
-
if (text.length <= max)
|
|
110
|
-
return text.padEnd(max);
|
|
111
|
-
return text.slice(0, max - 1) + "…";
|
|
112
|
-
}
|
package/dist/tui-ink/theme.d.ts
CHANGED
|
@@ -49,15 +49,9 @@ export interface Theme {
|
|
|
49
49
|
}
|
|
50
50
|
export declare const darkTheme: Theme;
|
|
51
51
|
/**
|
|
52
|
-
* Light palette
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* effective.
|
|
56
|
-
* 2. Specific hex values are used wherever the dark palette assumed a dark
|
|
57
|
-
* background (notably accent/code/trace colors and message bubbles).
|
|
58
|
-
* Each hex was picked to clear WCAG AA contrast (4.5:1) against a near-
|
|
59
|
-
* white background (#fafafa) or, when applicable, against the explicit
|
|
60
|
-
* surface color in the same palette (e.g. diffAddFg vs diffAdd).
|
|
52
|
+
* Light palette aligned with the restored OpenTUI runtime: paper-neutral
|
|
53
|
+
* surfaces, blue focus/user rails, warm command accent, and semantic tool
|
|
54
|
+
* colors tuned for readable contrast on a light terminal background.
|
|
61
55
|
*/
|
|
62
56
|
export declare const lightTheme: Theme;
|
|
63
57
|
export declare const ThemeProvider: import("react").Provider<Theme>;
|
package/dist/tui-ink/theme.js
CHANGED
|
@@ -47,53 +47,47 @@ export const darkTheme = {
|
|
|
47
47
|
diffRemoveFg: "#F48771",
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
|
-
* Light palette
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* effective.
|
|
54
|
-
* 2. Specific hex values are used wherever the dark palette assumed a dark
|
|
55
|
-
* background (notably accent/code/trace colors and message bubbles).
|
|
56
|
-
* Each hex was picked to clear WCAG AA contrast (4.5:1) against a near-
|
|
57
|
-
* white background (#fafafa) or, when applicable, against the explicit
|
|
58
|
-
* surface color in the same palette (e.g. diffAddFg vs diffAdd).
|
|
50
|
+
* Light palette aligned with the restored OpenTUI runtime: paper-neutral
|
|
51
|
+
* surfaces, blue focus/user rails, warm command accent, and semantic tool
|
|
52
|
+
* colors tuned for readable contrast on a light terminal background.
|
|
59
53
|
*/
|
|
60
54
|
export const lightTheme = {
|
|
61
|
-
user: "
|
|
62
|
-
agent: "
|
|
63
|
-
error: "
|
|
64
|
-
warning: "#
|
|
65
|
-
success: "
|
|
66
|
-
accent: "#
|
|
67
|
-
border: "
|
|
68
|
-
borderActive: "#
|
|
69
|
-
inputBorder: "#
|
|
70
|
-
inputBorderDisabled: "#
|
|
71
|
-
inputBg: "#
|
|
72
|
-
inputBgDisabled: "#
|
|
73
|
-
inputText: "#
|
|
74
|
-
inputPlaceholder: "#
|
|
75
|
-
muted: "
|
|
76
|
-
dim: "
|
|
77
|
-
thinking: "
|
|
78
|
-
thinkingDim: "
|
|
79
|
-
toolName: "#
|
|
80
|
-
toolResult: "
|
|
81
|
-
toolError: "
|
|
82
|
-
toolPending: "#
|
|
83
|
-
code: "#
|
|
84
|
-
traceAction: "#
|
|
85
|
-
traceCount: "#
|
|
86
|
-
traceDetail: "
|
|
87
|
-
traceCommand: "#
|
|
88
|
-
tracePending: "#
|
|
89
|
-
userMessageBorder: "#
|
|
90
|
-
userMessageBg: "#
|
|
91
|
-
userMessageText: "#
|
|
92
|
-
userRail: "#
|
|
93
|
-
diffAdd: "#
|
|
94
|
-
diffRemove: "#
|
|
95
|
-
diffAddFg: "#
|
|
96
|
-
diffRemoveFg: "#
|
|
55
|
+
user: "#356FD2",
|
|
56
|
+
agent: "#171717",
|
|
57
|
+
error: "#B62633",
|
|
58
|
+
warning: "#8B4A00",
|
|
59
|
+
success: "#2F7D4A",
|
|
60
|
+
accent: "#8B4A00",
|
|
61
|
+
border: "#B9BDB8",
|
|
62
|
+
borderActive: "#356FD2",
|
|
63
|
+
inputBorder: "#356FD2",
|
|
64
|
+
inputBorderDisabled: "#D7DAD4",
|
|
65
|
+
inputBg: "#F1F3F0",
|
|
66
|
+
inputBgDisabled: "#E6E8E3",
|
|
67
|
+
inputText: "#171717",
|
|
68
|
+
inputPlaceholder: "#6F7377",
|
|
69
|
+
muted: "#6F7377",
|
|
70
|
+
dim: "#8B9094",
|
|
71
|
+
thinking: "#5F666D",
|
|
72
|
+
thinkingDim: "#8B9094",
|
|
73
|
+
toolName: "#495057",
|
|
74
|
+
toolResult: "#171717",
|
|
75
|
+
toolError: "#B62633",
|
|
76
|
+
toolPending: "#8B4A00",
|
|
77
|
+
code: "#2F7D4A",
|
|
78
|
+
traceAction: "#8B4A00",
|
|
79
|
+
traceCount: "#6F7377",
|
|
80
|
+
traceDetail: "#8B9094",
|
|
81
|
+
traceCommand: "#257E8A",
|
|
82
|
+
tracePending: "#8B4A00",
|
|
83
|
+
userMessageBorder: "#356FD2",
|
|
84
|
+
userMessageBg: "#F1F3F0",
|
|
85
|
+
userMessageText: "#234B93",
|
|
86
|
+
userRail: "#356FD2",
|
|
87
|
+
diffAdd: "#D7E8D8",
|
|
88
|
+
diffRemove: "#F7DADC",
|
|
89
|
+
diffAddFg: "#173D2D",
|
|
90
|
+
diffRemoveFg: "#5D1922",
|
|
97
91
|
};
|
|
98
92
|
const ThemeContext = createContext(darkTheme);
|
|
99
93
|
export const ThemeProvider = ThemeContext.Provider;
|
package/dist/tui-ink/welcome.js
CHANGED
|
@@ -3,80 +3,10 @@ import React from "react";
|
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { useTheme } from "./theme.js";
|
|
6
|
+
import { BUBBLE_COMPACT_WORDMARK, BUBBLE_WORDMARK, bubbleWordmarkLineText, bubbleWordmarkMaxWidth, } from "../tui/wordmark.js";
|
|
6
7
|
const require = createRequire(import.meta.url);
|
|
7
8
|
const PACKAGE_VERSION = readPackageVersion();
|
|
8
|
-
const
|
|
9
|
-
[
|
|
10
|
-
"██████ ",
|
|
11
|
-
"██ ██",
|
|
12
|
-
"██ ██",
|
|
13
|
-
"██████ ",
|
|
14
|
-
"██ ██",
|
|
15
|
-
"██ ██",
|
|
16
|
-
"██████ ",
|
|
17
|
-
],
|
|
18
|
-
[
|
|
19
|
-
"██ ██",
|
|
20
|
-
"██ ██",
|
|
21
|
-
"██ ██",
|
|
22
|
-
"██ ██",
|
|
23
|
-
"██ ██",
|
|
24
|
-
"██ ██",
|
|
25
|
-
" █████ ",
|
|
26
|
-
],
|
|
27
|
-
[
|
|
28
|
-
"██████ ",
|
|
29
|
-
"██ ██",
|
|
30
|
-
"██ ██",
|
|
31
|
-
"██████ ",
|
|
32
|
-
"██ ██",
|
|
33
|
-
"██ ██",
|
|
34
|
-
"██████ ",
|
|
35
|
-
],
|
|
36
|
-
[
|
|
37
|
-
"██████ ",
|
|
38
|
-
"██ ██",
|
|
39
|
-
"██ ██",
|
|
40
|
-
"██████ ",
|
|
41
|
-
"██ ██",
|
|
42
|
-
"██ ██",
|
|
43
|
-
"██████ ",
|
|
44
|
-
],
|
|
45
|
-
[
|
|
46
|
-
"██ ",
|
|
47
|
-
"██ ",
|
|
48
|
-
"██ ",
|
|
49
|
-
"██ ",
|
|
50
|
-
"██ ",
|
|
51
|
-
"██ ",
|
|
52
|
-
"███████",
|
|
53
|
-
],
|
|
54
|
-
[
|
|
55
|
-
"███████",
|
|
56
|
-
"██ ",
|
|
57
|
-
"██ ",
|
|
58
|
-
"██████ ",
|
|
59
|
-
"██ ",
|
|
60
|
-
"██ ",
|
|
61
|
-
"███████",
|
|
62
|
-
],
|
|
63
|
-
];
|
|
64
|
-
/**
|
|
65
|
-
* Derive a 6-step logo gradient from the active theme tokens so the banner
|
|
66
|
-
* stays readable on both dark and light backgrounds.
|
|
67
|
-
*/
|
|
68
|
-
function logoColors(theme) {
|
|
69
|
-
return [
|
|
70
|
-
theme.userMessageText,
|
|
71
|
-
theme.userMessageText,
|
|
72
|
-
theme.inputBorder,
|
|
73
|
-
theme.inputBorder,
|
|
74
|
-
theme.traceCommand,
|
|
75
|
-
theme.traceCommand,
|
|
76
|
-
];
|
|
77
|
-
}
|
|
78
|
-
const COMPACT_LOGO = ["B", "U", "B", "B", "L", "E"];
|
|
79
|
-
const WIDE_LOGO_MIN_WIDTH = 52;
|
|
9
|
+
const WIDE_LOGO_MIN_WIDTH = bubbleWordmarkMaxWidth(BUBBLE_WORDMARK) + 4;
|
|
80
10
|
export function shouldShowWelcomeBanner({ startedWithVisibleHistory, }) {
|
|
81
11
|
// Keep banner visibility tied to the initial history, not transient overlays,
|
|
82
12
|
// so opening and closing a picker does not move it in the transcript.
|
|
@@ -96,18 +26,32 @@ export function WelcomeBanner({ terminalColumns, modelLabel, cwd, tips, skillsCo
|
|
|
96
26
|
: "Type / for commands and @ to reference files";
|
|
97
27
|
const modelLine = modelLabel ? `${modelLabel}${cwd ? ` · ${cwd}` : ""}` : cwd;
|
|
98
28
|
return (_jsxs(Box, { width: effectiveWidth, flexDirection: "column", alignItems: "center", marginBottom: 1, children: [_jsx(Box, { flexDirection: "column", alignItems: "center", children: useWideLogo
|
|
99
|
-
?
|
|
29
|
+
? BUBBLE_WORDMARK.map((line, rowIndex) => (_jsx(LogoRow, { line: line }, `logo-row-${rowIndex}`)))
|
|
100
30
|
: _jsx(CompactLogo, {}) }), _jsx(Box, { marginTop: 2, children: _jsx(Text, { bold: true, color: theme.muted, children: PACKAGE_VERSION }) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { bold: true, color: theme.userMessageText, children: "TIP: " }), _jsx(Text, { bold: true, color: theme.userMessageText, children: tip })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.muted, children: "shift+tab to cycle modes \u00B7 ctrl+r for reasoning \u00B7 ctrl+o for trace" }) }), modelLine && (_jsx(Box, { children: _jsx(Text, { color: theme.muted, children: truncateToWidth(modelLine, effectiveWidth - 4) }) })), _jsxs(Box, { marginTop: 1, children: [_jsx(StatusItem, { label: "Skills", count: skillsCount, ok: skillsCount > 0 }), _jsx(Text, { color: theme.muted, children: " " }), _jsx(StatusItem, { label: "MCPs", count: mcpConnectedCount, total: mcpTotalCount, ok: mcpTotalCount === 0 || mcpConnectedCount === mcpTotalCount }), _jsx(Text, { color: theme.muted, children: " " }), _jsx(StatusItem, { label: "AGENTS.md", ok: hasAgentsFile })] })] }));
|
|
101
31
|
}
|
|
102
|
-
function LogoRow({
|
|
32
|
+
function LogoRow({ line }) {
|
|
103
33
|
const theme = useTheme();
|
|
104
|
-
|
|
105
|
-
|
|
34
|
+
if (!line.segments) {
|
|
35
|
+
return _jsx(Text, { bold: true, color: logoColor(theme, line.tone ?? "caption"), children: line.text ?? "" });
|
|
36
|
+
}
|
|
37
|
+
return (_jsx(Box, { children: line.segments.map((segment, index) => (_jsx(React.Fragment, { children: _jsx(Text, { bold: true, color: logoColor(theme, segment.tone), children: segment.text }) }, `${index}-${segment.text}`))) }));
|
|
106
38
|
}
|
|
107
39
|
function CompactLogo() {
|
|
108
40
|
const theme = useTheme();
|
|
109
|
-
const
|
|
110
|
-
|
|
41
|
+
const line = BUBBLE_COMPACT_WORDMARK[0];
|
|
42
|
+
if (!line?.segments) {
|
|
43
|
+
return _jsx(Text, { bold: true, color: theme.warning, children: bubbleWordmarkLineText(line ?? { text: "" }) });
|
|
44
|
+
}
|
|
45
|
+
return (_jsx(Box, { children: line.segments.map((segment, index) => (_jsx(Text, { bold: true, color: logoColor(theme, segment.tone), children: segment.text }, `${segment.text}-${index}`))) }));
|
|
46
|
+
}
|
|
47
|
+
function logoColor(theme, tone) {
|
|
48
|
+
switch (tone) {
|
|
49
|
+
case "brand": return theme.warning;
|
|
50
|
+
case "ink": return theme.userMessageText;
|
|
51
|
+
case "stone": return theme.muted;
|
|
52
|
+
case "soft": return theme.dim;
|
|
53
|
+
case "caption": return theme.muted;
|
|
54
|
+
}
|
|
111
55
|
}
|
|
112
56
|
function StatusItem({ label, count, total, ok, }) {
|
|
113
57
|
const theme = useTheme();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/react */
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { type Agent } from "../agent.js";
|
|
4
|
+
import type { CliArgs } from "../cli.js";
|
|
5
|
+
import type { SessionManager } from "../session.js";
|
|
6
|
+
import type { PlanDecision, Provider } from "../types.js";
|
|
7
|
+
import { type ResolvedTheme, type ThemeMode } from "./theme.js";
|
|
8
|
+
import { ProviderRegistry } from "../provider-registry.js";
|
|
9
|
+
import { SkillRegistry } from "../skills/registry.js";
|
|
10
|
+
import type { ApprovalDecision, ApprovalRequest } from "../approval/types.js";
|
|
11
|
+
import type { BashAllowlist } from "../approval/session-cache.js";
|
|
12
|
+
import type { SettingsManager } from "../permissions/settings.js";
|
|
13
|
+
import type { McpManager } from "../mcp/manager.js";
|
|
14
|
+
import type { LspService } from "../lsp/index.js";
|
|
15
|
+
import type { QuestionController } from "../question/index.js";
|
|
16
|
+
import type { MemoryScope } from "../memory/index.js";
|
|
17
|
+
export interface PlanHandlerRef {
|
|
18
|
+
current?: (plan: string) => Promise<PlanDecision>;
|
|
19
|
+
}
|
|
20
|
+
export interface ApprovalHandlerRef {
|
|
21
|
+
current?: (req: ApprovalRequest) => Promise<ApprovalDecision>;
|
|
22
|
+
}
|
|
23
|
+
interface AppProps {
|
|
24
|
+
agent: Agent;
|
|
25
|
+
args: CliArgs;
|
|
26
|
+
sessionManager?: SessionManager;
|
|
27
|
+
createProvider?: (providerId: string, apiKey: string, baseURL: string) => Provider;
|
|
28
|
+
registry?: ProviderRegistry;
|
|
29
|
+
skillRegistry?: SkillRegistry;
|
|
30
|
+
planHandlerRef?: PlanHandlerRef;
|
|
31
|
+
approvalHandlerRef?: ApprovalHandlerRef;
|
|
32
|
+
questionController?: QuestionController;
|
|
33
|
+
bashAllowlist?: BashAllowlist;
|
|
34
|
+
settingsManager?: SettingsManager;
|
|
35
|
+
lspService?: LspService;
|
|
36
|
+
mcpManager?: McpManager;
|
|
37
|
+
themeMode?: ThemeMode;
|
|
38
|
+
themeOverrides?: Record<string, string>;
|
|
39
|
+
detectedTheme?: ResolvedTheme;
|
|
40
|
+
onThemeModeChange?: (mode: ThemeMode) => void;
|
|
41
|
+
flushMemory?: () => Promise<void>;
|
|
42
|
+
runMemoryCompaction?: () => Promise<string>;
|
|
43
|
+
runMemorySummary?: (scope?: MemoryScope) => Promise<string>;
|
|
44
|
+
runMemoryRefresh?: (scope?: MemoryScope) => Promise<string>;
|
|
45
|
+
/** Whether the bypassPermissions mode is reachable via Shift+Tab cycling. */
|
|
46
|
+
bypassEnabled?: boolean;
|
|
47
|
+
onExit?: (summary: ExitSummary) => void;
|
|
48
|
+
}
|
|
49
|
+
export interface ExitSummary {
|
|
50
|
+
/** Wall-clock duration of the session, in milliseconds. */
|
|
51
|
+
wallMs: number;
|
|
52
|
+
}
|
|
53
|
+
export declare function App({ agent, args, sessionManager, createProvider, registry, skillRegistry, planHandlerRef, approvalHandlerRef, questionController, bashAllowlist, settingsManager, lspService, mcpManager, themeMode: initialThemeMode, themeOverrides, detectedTheme, onThemeModeChange, flushMemory, runMemoryCompaction, runMemorySummary, runMemoryRefresh, bypassEnabled, onExit }: AppProps): React.ReactNode;
|
|
54
|
+
export {};
|