@bubblebrain-ai/bubble 0.0.19 → 0.0.21
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/internal-reminder-sanitizer.d.ts +1 -0
- package/dist/agent/internal-reminder-sanitizer.js +46 -0
- package/dist/agent.d.ts +10 -0
- package/dist/agent.js +310 -18
- package/dist/approval/controller.d.ts +6 -0
- package/dist/approval/controller.js +104 -11
- package/dist/checkpoints.d.ts +57 -0
- package/dist/checkpoints.js +0 -0
- package/dist/debug-trace.js +4 -0
- package/dist/feishu/agent-host/run-driver.js +29 -0
- package/dist/hooks/config.d.ts +9 -0
- package/dist/hooks/config.js +278 -0
- package/dist/hooks/controller.d.ts +24 -0
- package/dist/hooks/controller.js +254 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/log.d.ts +14 -0
- package/dist/hooks/log.js +54 -0
- package/dist/hooks/runner.d.ts +5 -0
- package/dist/hooks/runner.js +225 -0
- package/dist/hooks/trust.d.ts +37 -0
- package/dist/hooks/trust.js +143 -0
- package/dist/hooks/types.d.ts +173 -0
- package/dist/hooks/types.js +46 -0
- package/dist/main.js +86 -13
- package/dist/memory/prompts.js +3 -1
- package/dist/model-catalog.js +2 -0
- package/dist/model-pricing.js +8 -0
- package/dist/network/chatgpt-transport.d.ts +0 -1
- package/dist/network/chatgpt-transport.js +40 -121
- package/dist/network/provider-transport.d.ts +32 -0
- package/dist/network/provider-transport.js +265 -0
- package/dist/network/retry.d.ts +29 -0
- package/dist/network/retry.js +88 -0
- package/dist/network/system-proxy.d.ts +18 -0
- package/dist/network/system-proxy.js +175 -0
- package/dist/provider-anthropic.d.ts +1 -0
- package/dist/provider-anthropic.js +127 -52
- package/dist/provider-openai-codex.js +19 -29
- package/dist/session-log.js +3 -3
- package/dist/session.d.ts +31 -0
- package/dist/session.js +69 -0
- package/dist/slash-commands/commands.js +164 -0
- package/dist/slash-commands/types.d.ts +6 -0
- package/dist/tools/bash.js +4 -0
- package/dist/tools/edit-apply.js +63 -3
- package/dist/tools/edit.d.ts +2 -1
- package/dist/tools/edit.js +6 -5
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.js +2 -2
- package/dist/tools/write.d.ts +2 -1
- package/dist/tools/write.js +2 -1
- package/dist/tui/display-history.d.ts +4 -3
- package/dist/tui/display-history.js +34 -57
- package/dist/tui/display-sanitizer.d.ts +3 -0
- package/dist/tui/display-sanitizer.js +38 -0
- package/dist/tui/image-paste.d.ts +18 -0
- package/dist/tui/image-paste.js +60 -0
- package/dist/tui/paste-placeholder.d.ts +1 -0
- package/dist/tui/paste-placeholder.js +7 -0
- package/dist/tui/run.d.ts +2 -0
- package/dist/tui/run.js +568 -223
- package/dist/tui/trace-groups.d.ts +16 -0
- package/dist/tui/trace-groups.js +82 -5
- package/dist/tui/transcript-scroll.d.ts +25 -0
- package/dist/tui/transcript-scroll.js +20 -0
- package/dist/tui/wordmark.d.ts +1 -0
- package/dist/tui/wordmark.js +56 -54
- package/dist/tui-ink/app.d.ts +4 -1
- package/dist/tui-ink/app.js +303 -248
- package/dist/tui-ink/display-history.d.ts +16 -1
- package/dist/tui-ink/display-history.js +50 -21
- package/dist/tui-ink/footer.d.ts +6 -12
- package/dist/tui-ink/footer.js +10 -29
- package/dist/tui-ink/image-paste.d.ts +59 -0
- package/dist/tui-ink/image-paste.js +277 -0
- package/dist/tui-ink/input-box.d.ts +26 -1
- package/dist/tui-ink/input-box.js +171 -41
- package/dist/tui-ink/message-list.d.ts +1 -1
- package/dist/tui-ink/message-list.js +46 -29
- package/dist/tui-ink/run.d.ts +7 -2
- package/dist/tui-ink/run.js +73 -23
- package/dist/tui-ink/terminal-mouse.d.ts +1 -0
- package/dist/tui-ink/terminal-mouse.js +4 -0
- package/dist/tui-ink/trace-groups.d.ts +16 -0
- package/dist/tui-ink/trace-groups.js +90 -6
- package/dist/tui-ink/transcript-viewport-math.d.ts +11 -0
- package/dist/tui-ink/transcript-viewport-math.js +17 -0
- package/dist/tui-ink/transcript-viewport.d.ts +24 -0
- package/dist/tui-ink/transcript-viewport.js +83 -0
- package/dist/tui-ink/welcome.d.ts +9 -7
- package/dist/tui-ink/welcome.js +7 -33
- package/dist/tui-opentui/app.js +2 -1
- package/dist/tui-opentui/trace-groups.js +40 -4
- package/dist/types.d.ts +27 -0
- package/package.json +1 -1
|
@@ -7,6 +7,10 @@ export interface TraceGroup {
|
|
|
7
7
|
count?: number;
|
|
8
8
|
noun?: string;
|
|
9
9
|
command?: string;
|
|
10
|
+
/** Model-provided one-line summary of what the command does (bash `description` arg). */
|
|
11
|
+
description?: string;
|
|
12
|
+
/** Original command split into lines, line breaks preserved (execute groups only). */
|
|
13
|
+
commandLines?: string[];
|
|
10
14
|
items: string[];
|
|
11
15
|
previewLines: string[];
|
|
12
16
|
errorLines: string[];
|
|
@@ -25,3 +29,15 @@ export declare function buildTraceGroups(toolCalls: DisplayToolCall[], options?:
|
|
|
25
29
|
export declare function formatTracePath(value: unknown, homeDir?: string): string;
|
|
26
30
|
export declare function formatElapsed(startedAt: number | undefined, now?: number): string | null;
|
|
27
31
|
export declare function traceGroupLabel(group: TraceGroup): string;
|
|
32
|
+
/**
|
|
33
|
+
* An execute command is shown inline in the header only when nothing is lost:
|
|
34
|
+
* no description competing for the slot, a single logical line, and it fits
|
|
35
|
+
* the width budget. Otherwise the full command renders as a wrapped block
|
|
36
|
+
* below the header — commands are never clipped mid-line.
|
|
37
|
+
*/
|
|
38
|
+
export declare function shouldInlineExecuteCommand(group: TraceGroup, widthBudget: number): boolean;
|
|
39
|
+
/** Visible command-block lines for compact rendering, capped at `maxLines`. */
|
|
40
|
+
export declare function executeCommandBlock(group: TraceGroup, maxLines: number): {
|
|
41
|
+
lines: string[];
|
|
42
|
+
omitted: number;
|
|
43
|
+
};
|
package/dist/tui/trace-groups.js
CHANGED
|
@@ -65,12 +65,36 @@ export function formatElapsed(startedAt, now = Date.now()) {
|
|
|
65
65
|
return `${minutes}m${remainder.toString().padStart(2, "0")}s`;
|
|
66
66
|
}
|
|
67
67
|
export function traceGroupLabel(group) {
|
|
68
|
+
if (group.description)
|
|
69
|
+
return `${group.title} ${group.description}`;
|
|
68
70
|
if (group.command)
|
|
69
71
|
return `${group.title} ${group.command}`;
|
|
70
72
|
if (group.count !== undefined && group.noun)
|
|
71
73
|
return `${group.title} ${group.count} ${group.noun}`;
|
|
72
74
|
return group.title;
|
|
73
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* An execute command is shown inline in the header only when nothing is lost:
|
|
78
|
+
* no description competing for the slot, a single logical line, and it fits
|
|
79
|
+
* the width budget. Otherwise the full command renders as a wrapped block
|
|
80
|
+
* below the header — commands are never clipped mid-line.
|
|
81
|
+
*/
|
|
82
|
+
export function shouldInlineExecuteCommand(group, widthBudget) {
|
|
83
|
+
if (group.kind !== "execute" || !group.command)
|
|
84
|
+
return false;
|
|
85
|
+
if (group.description)
|
|
86
|
+
return false;
|
|
87
|
+
const lines = group.commandLines ?? [];
|
|
88
|
+
if (lines.length > 1)
|
|
89
|
+
return false;
|
|
90
|
+
return group.command.length <= widthBudget;
|
|
91
|
+
}
|
|
92
|
+
/** Visible command-block lines for compact rendering, capped at `maxLines`. */
|
|
93
|
+
export function executeCommandBlock(group, maxLines) {
|
|
94
|
+
const lines = group.commandLines ?? [];
|
|
95
|
+
const shown = lines.slice(0, maxLines);
|
|
96
|
+
return { lines: shown, omitted: Math.max(0, lines.length - shown.length) };
|
|
97
|
+
}
|
|
74
98
|
function classifyTool(toolCall) {
|
|
75
99
|
if (toolCall.metadata?.kind === "subagent") {
|
|
76
100
|
return { kind: "subagent", title: "Subagents", bucketKey: `subagent:${toolCall.id}`, groupable: false };
|
|
@@ -133,15 +157,17 @@ function buildTraceGroup(classifier, raw, options) {
|
|
|
133
157
|
}
|
|
134
158
|
}
|
|
135
159
|
function buildListGroup(classifier, raw, options, pending, startedAt, hasError, errorCount) {
|
|
136
|
-
const
|
|
160
|
+
const matchCount = listMatchCount(raw);
|
|
161
|
+
const resultItems = raw.flatMap((tool) => listResultItems(tool, options.homeDir));
|
|
137
162
|
const fallbackItems = raw
|
|
138
163
|
.map((tool) => String(tool.args.pattern ?? tool.args.path ?? "").trim())
|
|
139
164
|
.filter(Boolean)
|
|
140
165
|
.map((item) => formatTracePath(item, options.homeDir));
|
|
141
|
-
const
|
|
166
|
+
const hasResultData = matchCount !== undefined || resultItems.length > 0 || raw.some((tool) => isEmptyListResult(tool.result));
|
|
167
|
+
const sourceItems = hasResultData ? resultItems : fallbackItems;
|
|
142
168
|
const { shown, omitted } = take(sourceItems, options.maxItems);
|
|
143
|
-
const count =
|
|
144
|
-
const noun =
|
|
169
|
+
const count = matchCount ?? (hasResultData ? resultItems.length : sourceItems.length || raw.length);
|
|
170
|
+
const noun = hasResultData ? plural(count, "file", "files") : plural(count, "search", "searches");
|
|
145
171
|
return {
|
|
146
172
|
kind: "list",
|
|
147
173
|
title: classifier.title,
|
|
@@ -158,6 +184,40 @@ function buildListGroup(classifier, raw, options, pending, startedAt, hasError,
|
|
|
158
184
|
startedAt,
|
|
159
185
|
};
|
|
160
186
|
}
|
|
187
|
+
function listResultItems(tool, homeDir) {
|
|
188
|
+
const metadataPaths = Array.isArray(tool.metadata?.paths)
|
|
189
|
+
? tool.metadata.paths.filter((item) => typeof item === "string" && item.trim().length > 0)
|
|
190
|
+
: [];
|
|
191
|
+
if (metadataPaths.length > 0 || typeof tool.metadata?.matches === "number") {
|
|
192
|
+
return metadataPaths.map((line) => formatTracePath(line, homeDir));
|
|
193
|
+
}
|
|
194
|
+
return resultLines(tool.result)
|
|
195
|
+
.filter(isListResultLine)
|
|
196
|
+
.map((line) => formatTracePath(line, homeDir));
|
|
197
|
+
}
|
|
198
|
+
function listMatchCount(raw) {
|
|
199
|
+
let count = 0;
|
|
200
|
+
let sawMetadata = false;
|
|
201
|
+
for (const tool of raw) {
|
|
202
|
+
const matches = tool.metadata?.matches;
|
|
203
|
+
if (typeof matches === "number" && Number.isFinite(matches)) {
|
|
204
|
+
count += Math.max(0, matches);
|
|
205
|
+
sawMetadata = true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return sawMetadata ? count : undefined;
|
|
209
|
+
}
|
|
210
|
+
function isEmptyListResult(result) {
|
|
211
|
+
if (result === undefined)
|
|
212
|
+
return false;
|
|
213
|
+
const lines = resultLines(result);
|
|
214
|
+
return lines.length > 0 && lines.every((line) => !isListResultLine(line));
|
|
215
|
+
}
|
|
216
|
+
function isListResultLine(line) {
|
|
217
|
+
const normalized = line.trim();
|
|
218
|
+
return !/^No files found\.?$/i.test(normalized)
|
|
219
|
+
&& !/^\[More than \d+ files, output truncated\]$/i.test(normalized);
|
|
220
|
+
}
|
|
161
221
|
function buildPathGroup(classifier, raw, options, pending, startedAt, hasError, errorCount, nounBase) {
|
|
162
222
|
const items = unique(raw
|
|
163
223
|
.map((tool) => formatTracePath(tool.args.path ?? tool.args.file ?? "", options.homeDir))
|
|
@@ -208,11 +268,15 @@ function buildSearchGroup(classifier, raw, options, pending, startedAt, hasError
|
|
|
208
268
|
function buildExecuteGroup(classifier, tool, options, pending, startedAt, hasError, errorCount) {
|
|
209
269
|
const lines = resultLines(tool.result).map((line) => formatTracePath(line, options.homeDir));
|
|
210
270
|
const { shown, omitted } = take(lines, options.maxPreviewLines);
|
|
271
|
+
const rawCommand = String(tool.args.command ?? tool.args.cmd ?? commandFromRawArguments(tool.rawArguments) ?? "");
|
|
272
|
+
const description = String(tool.args.description ?? "").trim() || undefined;
|
|
211
273
|
return {
|
|
212
274
|
kind: "execute",
|
|
213
275
|
title: classifier.title,
|
|
214
276
|
raw: [tool],
|
|
215
|
-
command: normalizeCommand(
|
|
277
|
+
command: normalizeCommand(rawCommand),
|
|
278
|
+
description,
|
|
279
|
+
commandLines: commandLinesOf(rawCommand),
|
|
216
280
|
items: [],
|
|
217
281
|
previewLines: shown,
|
|
218
282
|
errorLines: [],
|
|
@@ -364,6 +428,19 @@ function normalizeCommand(value) {
|
|
|
364
428
|
const command = String(value ?? "").replace(/\s+/g, " ").trim();
|
|
365
429
|
return command;
|
|
366
430
|
}
|
|
431
|
+
// Preserves the command's own line structure (heredocs, && chains the model
|
|
432
|
+
// formatted across lines); only trims trailing whitespace and outer blank lines.
|
|
433
|
+
function commandLinesOf(rawCommand) {
|
|
434
|
+
const lines = rawCommand
|
|
435
|
+
.replace(/\r\n/g, "\n")
|
|
436
|
+
.split("\n")
|
|
437
|
+
.map((line) => line.trimEnd());
|
|
438
|
+
while (lines.length > 0 && lines[0].trim() === "")
|
|
439
|
+
lines.shift();
|
|
440
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "")
|
|
441
|
+
lines.pop();
|
|
442
|
+
return lines;
|
|
443
|
+
}
|
|
367
444
|
function commandFromRawArguments(rawArguments) {
|
|
368
445
|
if (!rawArguments)
|
|
369
446
|
return "";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript scroll-follow policy.
|
|
3
|
+
*
|
|
4
|
+
* The transcript snaps to the bottom ("follows") while the user is at the
|
|
5
|
+
* bottom, and stays put while they read older history. Two events override a
|
|
6
|
+
* scrolled-up position and re-engage following:
|
|
7
|
+
* - the user sends a message (explicit intent to watch the newest turn)
|
|
8
|
+
* - an approval prompt appears (requires the user's attention)
|
|
9
|
+
*
|
|
10
|
+
* Those renders set `forcePending`, which must survive until the deferred
|
|
11
|
+
* scroll actually runs: streaming redraws in the interim recompute the follow
|
|
12
|
+
* flag from the (still unscrolled) position and would otherwise cancel the
|
|
13
|
+
* snap. A user mouse scroll clears the pending force — their latest gesture
|
|
14
|
+
* always wins.
|
|
15
|
+
*/
|
|
16
|
+
export interface TranscriptScrollState {
|
|
17
|
+
/** A forceFollow render is waiting for its deferred scroll to execute. */
|
|
18
|
+
forcePending: boolean;
|
|
19
|
+
/** The viewport was at the bottom when the update was scheduled. */
|
|
20
|
+
shouldFollow: boolean;
|
|
21
|
+
/** Live follow flag, recomputed from the viewport position on each render. */
|
|
22
|
+
following: boolean;
|
|
23
|
+
}
|
|
24
|
+
export type TranscriptScrollAction = "scroll-bottom" | "sync-position";
|
|
25
|
+
export declare function resolveTranscriptScroll(state: TranscriptScrollState): TranscriptScrollAction;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript scroll-follow policy.
|
|
3
|
+
*
|
|
4
|
+
* The transcript snaps to the bottom ("follows") while the user is at the
|
|
5
|
+
* bottom, and stays put while they read older history. Two events override a
|
|
6
|
+
* scrolled-up position and re-engage following:
|
|
7
|
+
* - the user sends a message (explicit intent to watch the newest turn)
|
|
8
|
+
* - an approval prompt appears (requires the user's attention)
|
|
9
|
+
*
|
|
10
|
+
* Those renders set `forcePending`, which must survive until the deferred
|
|
11
|
+
* scroll actually runs: streaming redraws in the interim recompute the follow
|
|
12
|
+
* flag from the (still unscrolled) position and would otherwise cancel the
|
|
13
|
+
* snap. A user mouse scroll clears the pending force — their latest gesture
|
|
14
|
+
* always wins.
|
|
15
|
+
*/
|
|
16
|
+
export function resolveTranscriptScroll(state) {
|
|
17
|
+
if (state.forcePending)
|
|
18
|
+
return "scroll-bottom";
|
|
19
|
+
return state.shouldFollow && state.following ? "scroll-bottom" : "sync-position";
|
|
20
|
+
}
|
package/dist/tui/wordmark.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface BubbleWordmarkLine {
|
|
|
9
9
|
segments?: BubbleWordmarkSegment[];
|
|
10
10
|
}
|
|
11
11
|
export declare const BUBBLE_WORDMARK: BubbleWordmarkLine[];
|
|
12
|
+
export declare const BUBBLE_WORDMARK_LARGE: BubbleWordmarkLine[];
|
|
12
13
|
export declare const BUBBLE_COMPACT_WORDMARK: BubbleWordmarkLine[];
|
|
13
14
|
export declare function bubbleWordmarkLineText(line: BubbleWordmarkLine): string;
|
|
14
15
|
export declare function bubbleWordmarkMaxWidth(lines?: BubbleWordmarkLine[]): number;
|
package/dist/tui/wordmark.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
+
// Pixel-style glyphs: each cell is a half-block "pixel" (█ ▀ ▄), giving the
|
|
2
|
+
// wordmark an 8-bit look while staying one terminal row per line.
|
|
1
3
|
const LEAD_B = {
|
|
2
4
|
tone: "brand",
|
|
3
5
|
lines: [
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"╰─╯ ",
|
|
6
|
+
"█ ",
|
|
7
|
+
"█ ",
|
|
8
|
+
"█▀█ ",
|
|
9
|
+
"█ █ ",
|
|
10
|
+
"█▄█ ",
|
|
10
11
|
" ",
|
|
11
12
|
],
|
|
12
13
|
};
|
|
13
14
|
const LOWER_B = {
|
|
14
15
|
tone: "ink",
|
|
15
16
|
lines: [
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"╰─╯ ",
|
|
17
|
+
"█ ",
|
|
18
|
+
"█ ",
|
|
19
|
+
"█▀█ ",
|
|
20
|
+
"█ █ ",
|
|
21
|
+
"█▄█ ",
|
|
22
22
|
" ",
|
|
23
23
|
],
|
|
24
24
|
};
|
|
@@ -28,22 +28,20 @@ const GLYPHS = {
|
|
|
28
28
|
lines: [
|
|
29
29
|
" ",
|
|
30
30
|
" ",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"╰─╯ ",
|
|
31
|
+
"█ █ ",
|
|
32
|
+
"█ █ ",
|
|
33
|
+
"█▄█ ",
|
|
35
34
|
" ",
|
|
36
35
|
],
|
|
37
36
|
},
|
|
38
37
|
l: {
|
|
39
38
|
tone: "ink",
|
|
40
39
|
lines: [
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"╰─ ",
|
|
40
|
+
"█ ",
|
|
41
|
+
"█ ",
|
|
42
|
+
"█ ",
|
|
43
|
+
"█ ",
|
|
44
|
+
"█▄ ",
|
|
47
45
|
" ",
|
|
48
46
|
],
|
|
49
47
|
},
|
|
@@ -52,23 +50,21 @@ const GLYPHS = {
|
|
|
52
50
|
lines: [
|
|
53
51
|
" ",
|
|
54
52
|
" ",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"╰─╯ ",
|
|
53
|
+
"█▀█ ",
|
|
54
|
+
"█▀▀ ",
|
|
55
|
+
"█▄▄ ",
|
|
59
56
|
" ",
|
|
60
57
|
],
|
|
61
58
|
},
|
|
62
59
|
beta: {
|
|
63
60
|
tone: "brand",
|
|
64
61
|
lines: [
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"│ ",
|
|
62
|
+
"█▀▀▄ ",
|
|
63
|
+
"█ █ ",
|
|
64
|
+
"█▀▀▄ ",
|
|
65
|
+
"█ █ ",
|
|
66
|
+
"█▄▄▀ ",
|
|
67
|
+
"█ ",
|
|
72
68
|
],
|
|
73
69
|
},
|
|
74
70
|
r: {
|
|
@@ -76,10 +72,9 @@ const GLYPHS = {
|
|
|
76
72
|
lines: [
|
|
77
73
|
" ",
|
|
78
74
|
" ",
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"│ ",
|
|
75
|
+
"█▀▀ ",
|
|
76
|
+
"█ ",
|
|
77
|
+
"█ ",
|
|
83
78
|
" ",
|
|
84
79
|
],
|
|
85
80
|
},
|
|
@@ -88,22 +83,20 @@ const GLYPHS = {
|
|
|
88
83
|
lines: [
|
|
89
84
|
" ",
|
|
90
85
|
" ",
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"│ │ ",
|
|
86
|
+
"▀▀█ ",
|
|
87
|
+
"█▀█ ",
|
|
88
|
+
"█▄█ ",
|
|
95
89
|
" ",
|
|
96
90
|
],
|
|
97
91
|
},
|
|
98
92
|
i: {
|
|
99
93
|
tone: "ink",
|
|
100
94
|
lines: [
|
|
101
|
-
"• ",
|
|
102
95
|
" ",
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
96
|
+
"▀ ",
|
|
97
|
+
"█ ",
|
|
98
|
+
"█ ",
|
|
99
|
+
"█ ",
|
|
107
100
|
" ",
|
|
108
101
|
],
|
|
109
102
|
},
|
|
@@ -112,10 +105,9 @@ const GLYPHS = {
|
|
|
112
105
|
lines: [
|
|
113
106
|
" ",
|
|
114
107
|
" ",
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"│ │ ",
|
|
108
|
+
"█▀█ ",
|
|
109
|
+
"█ █ ",
|
|
110
|
+
"█ █ ",
|
|
119
111
|
" ",
|
|
120
112
|
],
|
|
121
113
|
},
|
|
@@ -128,11 +120,10 @@ const GLYPHS = {
|
|
|
128
120
|
" ",
|
|
129
121
|
" ",
|
|
130
122
|
" ",
|
|
131
|
-
" ",
|
|
132
123
|
],
|
|
133
124
|
},
|
|
134
125
|
};
|
|
135
|
-
|
|
126
|
+
const WORDMARK_GLYPHS = [
|
|
136
127
|
LEAD_B,
|
|
137
128
|
GLYPHS.u,
|
|
138
129
|
LOWER_B,
|
|
@@ -145,7 +136,14 @@ export const BUBBLE_WORDMARK = buildWordmark([
|
|
|
145
136
|
GLYPHS.a,
|
|
146
137
|
GLYPHS.i,
|
|
147
138
|
GLYPHS.n,
|
|
148
|
-
]
|
|
139
|
+
];
|
|
140
|
+
export const BUBBLE_WORDMARK = buildWordmark(WORDMARK_GLYPHS);
|
|
141
|
+
// Each pixel doubled horizontally: terminal cells are ~2:1 tall, so 2-char
|
|
142
|
+
// pixels render square and the wordmark reads much larger.
|
|
143
|
+
export const BUBBLE_WORDMARK_LARGE = buildWordmark(WORDMARK_GLYPHS.map((glyph) => ({
|
|
144
|
+
tone: glyph.tone,
|
|
145
|
+
lines: glyph.lines.map((line) => line.split("").map((ch) => ch + ch).join("")),
|
|
146
|
+
})));
|
|
149
147
|
export const BUBBLE_COMPACT_WORDMARK = [
|
|
150
148
|
{
|
|
151
149
|
segments: [
|
|
@@ -175,5 +173,9 @@ export function bubbleWordmarkMaxWidth(lines = BUBBLE_WORDMARK) {
|
|
|
175
173
|
return Math.max(...lines.map((line) => bubbleWordmarkLineText(line).length));
|
|
176
174
|
}
|
|
177
175
|
export function bubbleWordmarkForWidth(width) {
|
|
178
|
-
|
|
176
|
+
if (width >= bubbleWordmarkMaxWidth(BUBBLE_WORDMARK_LARGE) + 4)
|
|
177
|
+
return BUBBLE_WORDMARK_LARGE;
|
|
178
|
+
if (width >= bubbleWordmarkMaxWidth() + 4)
|
|
179
|
+
return BUBBLE_WORDMARK;
|
|
180
|
+
return BUBBLE_COMPACT_WORDMARK;
|
|
179
181
|
}
|
package/dist/tui-ink/app.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { McpManager } from "../mcp/manager.js";
|
|
|
12
12
|
import type { LspService } from "../lsp/index.js";
|
|
13
13
|
import type { QuestionController } from "../question/index.js";
|
|
14
14
|
import type { MemoryScope } from "../memory/index.js";
|
|
15
|
+
import type { ExternalHookController } from "../hooks/controller.js";
|
|
15
16
|
export interface PlanHandlerRef {
|
|
16
17
|
current?: (plan: string) => Promise<PlanDecision>;
|
|
17
18
|
}
|
|
@@ -42,11 +43,13 @@ interface AppProps {
|
|
|
42
43
|
runMemoryRefresh?: (scope?: MemoryScope) => Promise<string>;
|
|
43
44
|
/** Whether the bypassPermissions mode is reachable via Shift+Tab cycling. */
|
|
44
45
|
bypassEnabled?: boolean;
|
|
46
|
+
updateNotice?: string;
|
|
47
|
+
hookController?: ExternalHookController;
|
|
45
48
|
onExit?: (summary: ExitSummary) => void;
|
|
46
49
|
}
|
|
47
50
|
export interface ExitSummary {
|
|
48
51
|
/** Wall-clock duration of the session, in milliseconds. */
|
|
49
52
|
wallMs: number;
|
|
50
53
|
}
|
|
51
|
-
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): import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
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, updateNotice, hookController, onExit }: AppProps): import("react/jsx-runtime").JSX.Element;
|
|
52
55
|
export {};
|