@bubblebrain-ai/bubble 0.0.24 → 0.0.26
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/README.md +5 -3
- package/dist/agent.js +1 -1
- package/dist/clipboard.d.ts +14 -0
- package/dist/clipboard.js +132 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +22 -6
- package/dist/goal/format.js +34 -4
- package/dist/goal/store.d.ts +3 -0
- package/dist/goal/store.js +14 -1
- package/dist/goal/usage.d.ts +2 -0
- package/dist/goal/usage.js +3 -0
- package/dist/main.js +23 -42
- package/dist/model-catalog.d.ts +3 -1
- package/dist/model-catalog.js +17 -28
- package/dist/prompt/compose.js +1 -1
- package/dist/provider-anthropic.d.ts +4 -0
- package/dist/provider-anthropic.js +31 -0
- package/dist/provider-ark-responses.d.ts +17 -0
- package/dist/provider-ark-responses.js +462 -0
- package/dist/provider-transform.js +7 -0
- package/dist/provider.d.ts +7 -0
- package/dist/provider.js +170 -27
- package/dist/slash-commands/commands.js +22 -0
- package/dist/tools/todo.js +22 -38
- package/dist/tui/detect-theme.d.ts +1 -0
- package/dist/tui/detect-theme.js +23 -0
- package/dist/tui/image-display.d.ts +13 -0
- package/dist/tui/image-display.js +49 -0
- package/dist/tui/input-history.d.ts +37 -6
- package/dist/tui/input-history.js +194 -23
- package/dist/tui/model-switch.d.ts +42 -0
- package/dist/tui/model-switch.js +55 -0
- package/dist/tui-ink/app.d.ts +32 -2
- package/dist/tui-ink/app.js +1409 -549
- package/dist/tui-ink/approval/select.js +10 -0
- package/dist/tui-ink/detect-theme.d.ts +1 -2
- package/dist/tui-ink/detect-theme.js +1 -87
- package/dist/tui-ink/display-history.d.ts +1 -0
- package/dist/tui-ink/display-history.js +11 -0
- package/dist/tui-ink/feedback-dialog.js +10 -0
- package/dist/tui-ink/feishu-setup-picker.js +10 -0
- package/dist/tui-ink/footer.d.ts +1 -0
- package/dist/tui-ink/footer.js +8 -2
- package/dist/tui-ink/input-box.d.ts +71 -9
- package/dist/tui-ink/input-box.js +359 -121
- package/dist/tui-ink/input-history.d.ts +1 -16
- package/dist/tui-ink/input-history.js +1 -79
- package/dist/tui-ink/input-queue.d.ts +12 -0
- package/dist/tui-ink/input-queue.js +17 -0
- package/dist/tui-ink/key-events.d.ts +9 -0
- package/dist/tui-ink/key-events.js +8 -0
- package/dist/tui-ink/markdown.js +1 -1
- package/dist/tui-ink/message-list.d.ts +19 -1
- package/dist/tui-ink/message-list.js +111 -32
- package/dist/tui-ink/model-picker.d.ts +25 -2
- package/dist/tui-ink/model-picker.js +237 -20
- package/dist/tui-ink/plan-confirm.js +10 -0
- package/dist/tui-ink/question-dialog.js +46 -10
- package/dist/tui-ink/run.d.ts +10 -1
- package/dist/tui-ink/run.js +27 -42
- package/dist/tui-ink/session-picker.js +3 -0
- package/dist/tui-ink/submit-dedupe.d.ts +5 -0
- package/dist/tui-ink/submit-dedupe.js +25 -0
- package/dist/tui-ink/terminal-mouse.d.ts +24 -1
- package/dist/tui-ink/terminal-mouse.js +76 -21
- package/dist/tui-ink/theme.d.ts +6 -3
- package/dist/tui-ink/theme.js +10 -4
- package/dist/tui-ink/welcome.d.ts +1 -0
- package/dist/tui-ink/welcome.js +34 -27
- package/dist/variant/variant-resolver.js +4 -1
- package/package.json +1 -5
- package/dist/tui/clipboard.d.ts +0 -1
- package/dist/tui/clipboard.js +0 -53
- package/dist/tui/escape-confirmation.d.ts +0 -15
- package/dist/tui/escape-confirmation.js +0 -30
- package/dist/tui/global-key-router.d.ts +0 -3
- package/dist/tui/global-key-router.js +0 -87
- package/dist/tui/markdown-inline.d.ts +0 -22
- package/dist/tui/markdown-inline.js +0 -68
- package/dist/tui/markdown-theme-rules.d.ts +0 -23
- package/dist/tui/markdown-theme-rules.js +0 -164
- package/dist/tui/markdown-theme.d.ts +0 -5
- package/dist/tui/markdown-theme.js +0 -27
- package/dist/tui/opencode-spinner.d.ts +0 -22
- package/dist/tui/opencode-spinner.js +0 -216
- package/dist/tui/prompt-keybindings.d.ts +0 -42
- package/dist/tui/prompt-keybindings.js +0 -35
- package/dist/tui/render-signature.d.ts +0 -1
- package/dist/tui/render-signature.js +0 -7
- package/dist/tui/run.d.ts +0 -67
- package/dist/tui/run.js +0 -10166
- package/dist/tui/sidebar-mcp.d.ts +0 -31
- package/dist/tui/sidebar-mcp.js +0 -62
- package/dist/tui/sidebar-state.d.ts +0 -12
- package/dist/tui/sidebar-state.js +0 -69
- package/dist/tui/streaming-tool-args.d.ts +0 -15
- package/dist/tui/streaming-tool-args.js +0 -30
- package/dist/tui/tool-renderers/fallback.d.ts +0 -2
- package/dist/tui/tool-renderers/fallback.js +0 -75
- package/dist/tui/tool-renderers/registry.d.ts +0 -3
- package/dist/tui/tool-renderers/registry.js +0 -11
- package/dist/tui/tool-renderers/subagent.d.ts +0 -2
- package/dist/tui/tool-renderers/subagent.js +0 -135
- package/dist/tui/tool-renderers/types.d.ts +0 -36
- package/dist/tui/tool-renderers/types.js +0 -1
- package/dist/tui/tool-renderers/write-preview.d.ts +0 -12
- package/dist/tui/tool-renderers/write-preview.js +0 -32
- package/dist/tui/tool-renderers/write.d.ts +0 -6
- package/dist/tui/tool-renderers/write.js +0 -88
- package/dist/tui/transcript-scroll.d.ts +0 -25
- package/dist/tui/transcript-scroll.js +0 -20
- package/dist/tui-ink/transcript-viewport-math.d.ts +0 -11
- package/dist/tui-ink/transcript-viewport-math.js +0 -17
- package/dist/tui-ink/transcript-viewport.d.ts +0 -24
- package/dist/tui-ink/transcript-viewport.js +0 -83
- package/dist/tui-opentui/app.d.ts +0 -54
- package/dist/tui-opentui/app.js +0 -1371
- package/dist/tui-opentui/approval/approval-dialog.d.ts +0 -15
- package/dist/tui-opentui/approval/approval-dialog.js +0 -155
- package/dist/tui-opentui/approval/diff-view.d.ts +0 -9
- package/dist/tui-opentui/approval/diff-view.js +0 -43
- package/dist/tui-opentui/approval/select.d.ts +0 -37
- package/dist/tui-opentui/approval/select.js +0 -91
- package/dist/tui-opentui/detect-theme.d.ts +0 -2
- package/dist/tui-opentui/detect-theme.js +0 -87
- package/dist/tui-opentui/display-history.d.ts +0 -56
- package/dist/tui-opentui/display-history.js +0 -130
- package/dist/tui-opentui/edit-diff.d.ts +0 -11
- package/dist/tui-opentui/edit-diff.js +0 -57
- package/dist/tui-opentui/feedback-dialog.d.ts +0 -21
- package/dist/tui-opentui/feedback-dialog.js +0 -164
- package/dist/tui-opentui/feishu-setup-picker.d.ts +0 -7
- package/dist/tui-opentui/feishu-setup-picker.js +0 -272
- package/dist/tui-opentui/file-mentions.d.ts +0 -29
- package/dist/tui-opentui/file-mentions.js +0 -174
- package/dist/tui-opentui/footer.d.ts +0 -26
- package/dist/tui-opentui/footer.js +0 -40
- package/dist/tui-opentui/image-paste.d.ts +0 -54
- package/dist/tui-opentui/image-paste.js +0 -288
- package/dist/tui-opentui/input-box.d.ts +0 -32
- package/dist/tui-opentui/input-box.js +0 -462
- package/dist/tui-opentui/input-history.d.ts +0 -16
- package/dist/tui-opentui/input-history.js +0 -79
- package/dist/tui-opentui/markdown.d.ts +0 -66
- package/dist/tui-opentui/markdown.js +0 -127
- package/dist/tui-opentui/message-list.d.ts +0 -31
- package/dist/tui-opentui/message-list.js +0 -131
- package/dist/tui-opentui/model-picker.d.ts +0 -63
- package/dist/tui-opentui/model-picker.js +0 -450
- package/dist/tui-opentui/plan-confirm.d.ts +0 -9
- package/dist/tui-opentui/plan-confirm.js +0 -124
- package/dist/tui-opentui/question-dialog.d.ts +0 -10
- package/dist/tui-opentui/question-dialog.js +0 -110
- package/dist/tui-opentui/recent-activity.d.ts +0 -8
- package/dist/tui-opentui/recent-activity.js +0 -71
- package/dist/tui-opentui/run-session-picker.d.ts +0 -10
- package/dist/tui-opentui/run-session-picker.js +0 -28
- package/dist/tui-opentui/run.d.ts +0 -38
- package/dist/tui-opentui/run.js +0 -48
- package/dist/tui-opentui/session-picker.d.ts +0 -12
- package/dist/tui-opentui/session-picker.js +0 -120
- package/dist/tui-opentui/theme.d.ts +0 -89
- package/dist/tui-opentui/theme.js +0 -157
- package/dist/tui-opentui/todos.d.ts +0 -9
- package/dist/tui-opentui/todos.js +0 -45
- package/dist/tui-opentui/trace-groups.d.ts +0 -27
- package/dist/tui-opentui/trace-groups.js +0 -455
- package/dist/tui-opentui/use-terminal-size.d.ts +0 -4
- package/dist/tui-opentui/use-terminal-size.js +0 -5
- package/dist/tui-opentui/welcome.d.ts +0 -25
- package/dist/tui-opentui/welcome.js +0 -77
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useState } from "react";
|
|
3
3
|
import { Box, Text, useInput, useStdout } from "ink";
|
|
4
|
+
import { isKeyReleaseEvent } from "./key-events.js";
|
|
4
5
|
import { useTheme } from "./theme.js";
|
|
5
6
|
import { formatRelativeTime } from "./recent-activity.js";
|
|
6
7
|
import { padVisual, truncateVisual } from "../text-display.js";
|
|
@@ -19,6 +20,8 @@ export function SessionPicker({ currentCwd, currentSessions, allSessions, onSele
|
|
|
19
20
|
: Math.min(selectedSessionIdx, sessionRowIndices.length - 1);
|
|
20
21
|
const selectedRowIndex = sessionRowIndices[clampedIdx] ?? -1;
|
|
21
22
|
useInput((input, key) => {
|
|
23
|
+
if (isKeyReleaseEvent(key))
|
|
24
|
+
return;
|
|
22
25
|
if (key.escape) {
|
|
23
26
|
onCancel();
|
|
24
27
|
return;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SubmitPayload } from "./input-box.js";
|
|
2
|
+
export type StartingSubmitDecision = "accept" | "ignore" | "queue";
|
|
3
|
+
export declare function submitPayloadFingerprint(payload: SubmitPayload): string;
|
|
4
|
+
export declare function decideStartingSubmit(activeFingerprint: string | null, payload: SubmitPayload): StartingSubmitDecision;
|
|
5
|
+
export declare function decideStartingSubmitFingerprint(activeFingerprint: string | null, submitFingerprint: string): StartingSubmitDecision;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
function hashValue(value) {
|
|
3
|
+
return createHash("sha256").update(value).digest("hex");
|
|
4
|
+
}
|
|
5
|
+
export function submitPayloadFingerprint(payload) {
|
|
6
|
+
return JSON.stringify({
|
|
7
|
+
text: payload.text,
|
|
8
|
+
displayText: payload.displayText ?? "",
|
|
9
|
+
images: payload.images.map((image) => ({
|
|
10
|
+
mediaType: image.mediaType,
|
|
11
|
+
bytes: image.bytes,
|
|
12
|
+
filename: image.filename ?? "",
|
|
13
|
+
sourcePath: image.sourcePath ?? "",
|
|
14
|
+
dataUrlHash: hashValue(image.dataUrl),
|
|
15
|
+
})),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export function decideStartingSubmit(activeFingerprint, payload) {
|
|
19
|
+
return decideStartingSubmitFingerprint(activeFingerprint, submitPayloadFingerprint(payload));
|
|
20
|
+
}
|
|
21
|
+
export function decideStartingSubmitFingerprint(activeFingerprint, submitFingerprint) {
|
|
22
|
+
if (!activeFingerprint)
|
|
23
|
+
return "accept";
|
|
24
|
+
return activeFingerprint === submitFingerprint ? "ignore" : "queue";
|
|
25
|
+
}
|
|
@@ -1,5 +1,28 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const ALTERNATE_SCROLL_ENABLE = "\u001B[?1007h";
|
|
2
|
+
export declare const ALTERNATE_SCROLL_DISABLE = "\u001B[?1007l";
|
|
3
|
+
export declare const MOUSE_REPORTING_ENABLE = "\u001B[?1000h\u001B[?1006h";
|
|
4
|
+
export declare const MOUSE_REPORTING_DISABLE = "\u001B[?1003l\u001B[?1002l\u001B[?1000l\u001B[?1005l\u001B[?1006l\u001B[?1015l";
|
|
5
|
+
interface TerminalMouseEnv {
|
|
6
|
+
BUBBLE_ENABLE_MOUSE?: string;
|
|
7
|
+
BUBBLE_TUI_MOUSE?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Terminal mouse reporting captures drag events before the host terminal can
|
|
11
|
+
* create a native text selection. Keep it opt-in until Bubble owns a full
|
|
12
|
+
* renderer-level selection/copy pipeline.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isTerminalMouseReportingEnabled(env?: TerminalMouseEnv): boolean;
|
|
2
15
|
export type MouseWheelDirection = "up" | "down";
|
|
16
|
+
export interface TerminalMouseInput {
|
|
17
|
+
strippedInput: string;
|
|
18
|
+
wheelDirections: MouseWheelDirection[];
|
|
19
|
+
hasMouse: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function sanitizeTerminalMouseInput(input: string): TerminalMouseInput;
|
|
22
|
+
export declare function transcriptScrollLinesFromMouseInput(mouseInput: TerminalMouseInput, options: {
|
|
23
|
+
overlayActive: boolean;
|
|
24
|
+
}): number[];
|
|
3
25
|
export declare function stripTerminalMouseSequences(input: string): string;
|
|
4
26
|
export declare function hasTerminalMouseSequence(input: string): boolean;
|
|
5
27
|
export declare function parseTerminalMouseWheel(input: string): MouseWheelDirection[];
|
|
28
|
+
export {};
|
|
@@ -1,27 +1,82 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
1
|
+
// DECSET 1007 makes terminals translate wheel gestures into Up/Down arrows
|
|
2
|
+
// while the alternate screen is active. Keep it disabled in Ink; otherwise
|
|
3
|
+
// terminals without kitty event metadata make wheel and keyboard arrows
|
|
4
|
+
// indistinguishable.
|
|
5
|
+
export const ALTERNATE_SCROLL_ENABLE = "\x1b[?1007h";
|
|
6
|
+
export const ALTERNATE_SCROLL_DISABLE = "\x1b[?1007l";
|
|
7
|
+
// Use normal mouse tracking + SGR encoding so wheel events reach the app as
|
|
8
|
+
// mouse reports instead of being translated into keyboard arrows.
|
|
9
|
+
export const MOUSE_REPORTING_ENABLE = "\x1b[?1000h\x1b[?1006h";
|
|
10
|
+
// Disable every common tracking mode defensively in case a crash or another
|
|
11
|
+
// renderer left the terminal in a reporting state.
|
|
12
|
+
export const MOUSE_REPORTING_DISABLE = "\x1b[?1003l\x1b[?1002l\x1b[?1000l\x1b[?1005l\x1b[?1006l\x1b[?1015l";
|
|
13
|
+
function envValueEnabled(value) {
|
|
14
|
+
if (!value)
|
|
15
|
+
return false;
|
|
16
|
+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Terminal mouse reporting captures drag events before the host terminal can
|
|
20
|
+
* create a native text selection. Keep it opt-in until Bubble owns a full
|
|
21
|
+
* renderer-level selection/copy pipeline.
|
|
22
|
+
*/
|
|
23
|
+
export function isTerminalMouseReportingEnabled(env = process.env) {
|
|
24
|
+
return envValueEnabled(env.BUBBLE_ENABLE_MOUSE) || envValueEnabled(env.BUBBLE_TUI_MOUSE);
|
|
25
|
+
}
|
|
26
|
+
const ESCAPED_MOUSE_SEQUENCE_RE = /\x1b(?:\[<(\d+);\d+;\d+([mM])|\[M([\s\S])[\s\S]{2})/g;
|
|
27
|
+
const RAW_SGR_MOUSE_SEQUENCE_RE = /\[?<(\d+);\d+;\d+([mM])/g;
|
|
28
|
+
const RAW_SGR_MOUSE_INPUT_RE = /^(?:\[?<\d+;\d+;\d+[mM])+$/;
|
|
29
|
+
function wheelDirectionFromButtonCode(code) {
|
|
30
|
+
if ((code & 64) !== 64)
|
|
31
|
+
return undefined;
|
|
32
|
+
const wheelButton = code & 0b11;
|
|
33
|
+
if (wheelButton === 0)
|
|
34
|
+
return "up";
|
|
35
|
+
if (wheelButton === 1)
|
|
36
|
+
return "down";
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
function collectWheelDirection(directions, code, final) {
|
|
40
|
+
if (final !== undefined && final !== "M")
|
|
41
|
+
return;
|
|
42
|
+
const direction = wheelDirectionFromButtonCode(code);
|
|
43
|
+
if (direction)
|
|
44
|
+
directions.push(direction);
|
|
45
|
+
}
|
|
46
|
+
export function sanitizeTerminalMouseInput(input) {
|
|
47
|
+
const wheelDirections = [];
|
|
48
|
+
let hasMouse = false;
|
|
49
|
+
ESCAPED_MOUSE_SEQUENCE_RE.lastIndex = 0;
|
|
50
|
+
let strippedInput = input.replace(ESCAPED_MOUSE_SEQUENCE_RE, (_sequence, sgrCode, sgrFinal, x10Button) => {
|
|
51
|
+
hasMouse = true;
|
|
52
|
+
const code = sgrCode !== undefined
|
|
53
|
+
? Number(sgrCode)
|
|
54
|
+
: (x10Button?.charCodeAt(0) ?? 32) - 32;
|
|
55
|
+
collectWheelDirection(wheelDirections, code, sgrFinal);
|
|
56
|
+
return "";
|
|
57
|
+
});
|
|
58
|
+
if (RAW_SGR_MOUSE_INPUT_RE.test(strippedInput)) {
|
|
59
|
+
hasMouse = true;
|
|
60
|
+
RAW_SGR_MOUSE_SEQUENCE_RE.lastIndex = 0;
|
|
61
|
+
strippedInput = strippedInput.replace(RAW_SGR_MOUSE_SEQUENCE_RE, (_sequence, sgrCode, sgrFinal) => {
|
|
62
|
+
collectWheelDirection(wheelDirections, Number(sgrCode), sgrFinal);
|
|
63
|
+
return "";
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return { strippedInput, wheelDirections, hasMouse };
|
|
67
|
+
}
|
|
68
|
+
export function transcriptScrollLinesFromMouseInput(mouseInput, options) {
|
|
69
|
+
if (options.overlayActive)
|
|
70
|
+
return [];
|
|
71
|
+
return mouseInput.wheelDirections.map((direction) => direction === "up" ? -1 : 1);
|
|
72
|
+
}
|
|
7
73
|
export function stripTerminalMouseSequences(input) {
|
|
8
|
-
return input.
|
|
74
|
+
return sanitizeTerminalMouseInput(input).strippedInput;
|
|
9
75
|
}
|
|
10
76
|
export function hasTerminalMouseSequence(input) {
|
|
11
|
-
|
|
12
|
-
return
|
|
77
|
+
ESCAPED_MOUSE_SEQUENCE_RE.lastIndex = 0;
|
|
78
|
+
return ESCAPED_MOUSE_SEQUENCE_RE.test(input) || RAW_SGR_MOUSE_INPUT_RE.test(input);
|
|
13
79
|
}
|
|
14
80
|
export function parseTerminalMouseWheel(input) {
|
|
15
|
-
|
|
16
|
-
SGR_MOUSE_WHEEL_RE.lastIndex = 0;
|
|
17
|
-
for (const match of input.matchAll(SGR_MOUSE_WHEEL_RE)) {
|
|
18
|
-
if (match[2] !== "M")
|
|
19
|
-
continue;
|
|
20
|
-
const code = Number(match[1]);
|
|
21
|
-
if (code === 64)
|
|
22
|
-
directions.push("up");
|
|
23
|
-
if (code === 65)
|
|
24
|
-
directions.push("down");
|
|
25
|
-
}
|
|
26
|
-
return directions;
|
|
81
|
+
return sanitizeTerminalMouseInput(input).wheelDirections;
|
|
27
82
|
}
|
package/dist/tui-ink/theme.d.ts
CHANGED
|
@@ -15,9 +15,12 @@ export interface Theme {
|
|
|
15
15
|
error: string;
|
|
16
16
|
warning: string;
|
|
17
17
|
success: string;
|
|
18
|
+
background: string;
|
|
18
19
|
accent: string;
|
|
19
20
|
border: string;
|
|
20
21
|
borderActive: string;
|
|
22
|
+
backgroundPanel: string;
|
|
23
|
+
backgroundElement: string;
|
|
21
24
|
inputBorder: string;
|
|
22
25
|
inputBorderDisabled: string;
|
|
23
26
|
inputBg: string;
|
|
@@ -49,9 +52,9 @@ export interface Theme {
|
|
|
49
52
|
}
|
|
50
53
|
export declare const darkTheme: Theme;
|
|
51
54
|
/**
|
|
52
|
-
* Light palette
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
+
* Light palette tuned for paper-neutral surfaces, blue focus/user rails, warm
|
|
56
|
+
* command accent, and semantic tool colors with readable contrast on a light
|
|
57
|
+
* terminal background.
|
|
55
58
|
*/
|
|
56
59
|
export declare const lightTheme: Theme;
|
|
57
60
|
export declare const ThemeProvider: import("react").Provider<Theme>;
|
package/dist/tui-ink/theme.js
CHANGED
|
@@ -14,9 +14,12 @@ export const darkTheme = {
|
|
|
14
14
|
error: "red",
|
|
15
15
|
warning: "yellow",
|
|
16
16
|
success: "green",
|
|
17
|
+
background: "#0A0A0A",
|
|
17
18
|
accent: "cyan",
|
|
18
19
|
border: "gray",
|
|
19
20
|
borderActive: "cyan",
|
|
21
|
+
backgroundPanel: "#141414",
|
|
22
|
+
backgroundElement: "#1c1c24",
|
|
20
23
|
inputBorder: "#8A7FC6",
|
|
21
24
|
inputBorderDisabled: "#4a4754",
|
|
22
25
|
inputBg: "#1c1c24",
|
|
@@ -47,9 +50,9 @@ export const darkTheme = {
|
|
|
47
50
|
diffRemoveFg: "#F48771",
|
|
48
51
|
};
|
|
49
52
|
/**
|
|
50
|
-
* Light palette
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
+
* Light palette tuned for paper-neutral surfaces, blue focus/user rails, warm
|
|
54
|
+
* command accent, and semantic tool colors with readable contrast on a light
|
|
55
|
+
* terminal background.
|
|
53
56
|
*/
|
|
54
57
|
export const lightTheme = {
|
|
55
58
|
user: "#356FD2",
|
|
@@ -57,13 +60,16 @@ export const lightTheme = {
|
|
|
57
60
|
error: "#B62633",
|
|
58
61
|
warning: "#8B4A00",
|
|
59
62
|
success: "#2F7D4A",
|
|
63
|
+
background: "#FCFCFA",
|
|
60
64
|
accent: "#8B4A00",
|
|
61
65
|
border: "#B9BDB8",
|
|
62
66
|
borderActive: "#356FD2",
|
|
67
|
+
backgroundPanel: "#F6F6F3",
|
|
68
|
+
backgroundElement: "#ECEDEA",
|
|
63
69
|
inputBorder: "#356FD2",
|
|
64
70
|
inputBorderDisabled: "#D7DAD4",
|
|
65
71
|
inputBg: "#F1F3F0",
|
|
66
|
-
inputBgDisabled: "#
|
|
72
|
+
inputBgDisabled: "#F6F6F3",
|
|
67
73
|
inputText: "#171717",
|
|
68
74
|
inputPlaceholder: "#6F7377",
|
|
69
75
|
muted: "#6F7377",
|
|
@@ -17,4 +17,5 @@ interface WelcomeVisibilityInput {
|
|
|
17
17
|
}
|
|
18
18
|
export declare function shouldShowWelcomeBanner({ startedWithVisibleHistory, }: WelcomeVisibilityInput): boolean;
|
|
19
19
|
export declare function WelcomeBanner({ terminalColumns, tips, updateNotice, cwd, providerId, modelLabel, thinkingLabel, }: WelcomeBannerProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare function formatModelLine({ providerId, modelLabel, thinkingLabel, tips, }: Pick<WelcomeBannerProps, "providerId" | "modelLabel" | "thinkingLabel" | "tips">): string;
|
|
20
21
|
export {};
|
package/dist/tui-ink/welcome.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React from "react";
|
|
3
2
|
import { Box, Text } from "ink";
|
|
4
3
|
import { createRequire } from "node:module";
|
|
5
4
|
import { useTheme } from "./theme.js";
|
|
6
|
-
import { bubbleWordmarkForWidth, } from "../tui/wordmark.js";
|
|
7
5
|
const require = createRequire(import.meta.url);
|
|
8
6
|
const PACKAGE_VERSION = readPackageVersion();
|
|
7
|
+
const COMPACT_LOGO = [
|
|
8
|
+
" ▄ ▄ ",
|
|
9
|
+
"██████",
|
|
10
|
+
"█ ██ █",
|
|
11
|
+
"██████",
|
|
12
|
+
" ▀ ▀ ",
|
|
13
|
+
];
|
|
9
14
|
export function shouldShowWelcomeBanner({ startedWithVisibleHistory, }) {
|
|
10
15
|
// Keep banner visibility tied to the initial history, not transient overlays,
|
|
11
16
|
// so opening and closing a picker does not move it in the transcript.
|
|
@@ -15,33 +20,35 @@ export function shouldShowWelcomeBanner({ startedWithVisibleHistory, }) {
|
|
|
15
20
|
}
|
|
16
21
|
export function WelcomeBanner({ terminalColumns, tips, updateNotice, cwd, providerId, modelLabel, thinkingLabel, }) {
|
|
17
22
|
const theme = useTheme();
|
|
18
|
-
const effectiveWidth = Math.max(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
? actionableTips.join(" · ")
|
|
27
|
-
: "Type / for commands and @ to reference files";
|
|
28
|
-
return (_jsxs(Box, { width: effectiveWidth, flexDirection: "column", alignItems: "center", marginBottom: 1, children: [_jsx(Box, { flexDirection: "column", alignItems: "center", children: logoLines.map((line, rowIndex) => (_jsx(LogoRow, { line: line }, `logo-row-${rowIndex}`))) }), _jsx(Box, { marginTop: 2, children: _jsx(Text, { bold: true, color: theme.muted, children: PACKAGE_VERSION }) }), updateNotice && (_jsx(Box, { children: _jsx(Text, { color: theme.accent, children: updateNotice }) })), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { bold: true, color: theme.userMessageText, children: "TIP: " }), _jsx(Text, { bold: true, color: theme.userMessageText, children: tip })] }), (cwd || modelLabel) && (_jsxs(Box, { marginTop: 1, children: [cwd && _jsx(Text, { color: theme.muted, children: cwd }), cwd && (providerId || modelLabel) && _jsx(Text, { children: " " }), providerId && _jsxs(Text, { color: theme.muted, dimColor: true, children: [providerId, " \u00B7 "] }), modelLabel && (_jsxs(Text, { bold: true, color: theme.toolName, children: [modelLabel, thinkingLabel ? ` ${thinkingLabel}` : ""] }))] }))] }));
|
|
23
|
+
const effectiveWidth = Math.max(24, Math.min(terminalColumns - 2, 96));
|
|
24
|
+
const modelLine = formatModelLine({
|
|
25
|
+
providerId,
|
|
26
|
+
modelLabel,
|
|
27
|
+
thinkingLabel,
|
|
28
|
+
tips,
|
|
29
|
+
});
|
|
30
|
+
return (_jsxs(Box, { width: effectiveWidth, flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { flexDirection: "column", marginRight: 2, flexShrink: 0, children: COMPACT_LOGO.map((line, rowIndex) => (_jsx(Text, { color: theme.warning, bold: true, children: line }, `logo-row-${rowIndex}`))) }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: theme.inputText, children: "Bubble" }), _jsxs(Text, { color: theme.muted, children: [" ", PACKAGE_VERSION] })] }), modelLine && (_jsx(Text, { color: theme.muted, children: modelLine })), cwd && (_jsx(Text, { color: theme.muted, children: cwd }))] })] }), updateNotice && (_jsx(Box, { children: _jsx(Text, { color: theme.accent, children: updateNotice }) }))] }));
|
|
29
31
|
}
|
|
30
|
-
function
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
case "caption": return theme.muted;
|
|
32
|
+
export function formatModelLine({ providerId, modelLabel, thinkingLabel, tips, }) {
|
|
33
|
+
const parts = [];
|
|
34
|
+
// MiniMax thinking is a binary toggle (adaptive thinking), so label it
|
|
35
|
+
// "thinking mode" rather than "<level> effort"; and its provider id
|
|
36
|
+
// ("minimax-anthropic") is redundant with the model name, so omit it.
|
|
37
|
+
const isMiniMax = (providerId || "").toLowerCase().includes("minimax");
|
|
38
|
+
if (modelLabel) {
|
|
39
|
+
if (thinkingLabel && isMiniMax)
|
|
40
|
+
parts.push(modelLabel, "thinking mode");
|
|
41
|
+
else if (thinkingLabel)
|
|
42
|
+
parts.push(`${modelLabel} with ${thinkingLabel} effort`);
|
|
43
|
+
else
|
|
44
|
+
parts.push(modelLabel);
|
|
44
45
|
}
|
|
46
|
+
const readyTip = tips.find((item) => item.startsWith("Ready with"));
|
|
47
|
+
if (!modelLabel && readyTip)
|
|
48
|
+
parts.push(readyTip.replace(/^Ready with\s+/, ""));
|
|
49
|
+
if (providerId && !isMiniMax)
|
|
50
|
+
parts.push(providerId);
|
|
51
|
+
return parts.join(" · ");
|
|
45
52
|
}
|
|
46
53
|
function readPackageVersion() {
|
|
47
54
|
try {
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { getBuiltinModel } from "../model-catalog.js";
|
|
1
|
+
import { getBuiltinModel, getModelDefaultReasoningLevel } from "../model-catalog.js";
|
|
2
2
|
import { clampThinkingLevel } from "./thinking-level.js";
|
|
3
3
|
export function getAvailableThinkingLevels(providerId, modelId) {
|
|
4
4
|
return getBuiltinModel(providerId, modelId)?.reasoningLevels ?? ["off"];
|
|
5
5
|
}
|
|
6
6
|
export function getDefaultThinkingLevel(providerId, modelId) {
|
|
7
7
|
const levels = getAvailableThinkingLevels(providerId, modelId);
|
|
8
|
+
const explicitDefault = getModelDefaultReasoningLevel(providerId, modelId);
|
|
9
|
+
if (explicitDefault && levels.includes(explicitDefault))
|
|
10
|
+
return explicitDefault;
|
|
8
11
|
return levels.includes("medium") ? "medium" : levels[0] || "off";
|
|
9
12
|
}
|
|
10
13
|
export function normalizeThinkingLevel(level, supportedLevels) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bubblebrain-ai/bubble",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "A terminal coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -25,9 +25,6 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@larksuiteoapi/node-sdk": "^1.65.0",
|
|
28
|
-
"@opentui/core": "^0.2.15",
|
|
29
|
-
"@opentui/react": "^0.2.15",
|
|
30
|
-
"@opentui/solid": "^0.2.15",
|
|
31
28
|
"@types/better-sqlite3": "^7.6.13",
|
|
32
29
|
"@types/react": "^19.2.14",
|
|
33
30
|
"@vue/language-server": "^3.2.7",
|
|
@@ -41,7 +38,6 @@
|
|
|
41
38
|
"qrcode-terminal": "^0.12.0",
|
|
42
39
|
"react": "^19.2.6",
|
|
43
40
|
"shiki": "^4.0.2",
|
|
44
|
-
"solid-js": "^1.9.12",
|
|
45
41
|
"string-width": "^8.2.1",
|
|
46
42
|
"typescript-language-server": "^5.1.3",
|
|
47
43
|
"undici": "^6.26.0",
|
package/dist/tui/clipboard.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function copyTextToClipboard(text: string): Promise<void>;
|
package/dist/tui/clipboard.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
export async function copyTextToClipboard(text) {
|
|
3
|
-
if (process.platform === "darwin") {
|
|
4
|
-
await writeToProcess("pbcopy", [], text);
|
|
5
|
-
return;
|
|
6
|
-
}
|
|
7
|
-
if (process.platform === "win32") {
|
|
8
|
-
await writeToProcess("powershell", [
|
|
9
|
-
"-NoProfile",
|
|
10
|
-
"-Command",
|
|
11
|
-
"Set-Clipboard -Value ([Console]::In.ReadToEnd())",
|
|
12
|
-
], text);
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
const candidates = [
|
|
16
|
-
["wl-copy", []],
|
|
17
|
-
["xclip", ["-selection", "clipboard"]],
|
|
18
|
-
["xsel", ["--clipboard", "--input"]],
|
|
19
|
-
];
|
|
20
|
-
let lastError;
|
|
21
|
-
for (const [command, args] of candidates) {
|
|
22
|
-
try {
|
|
23
|
-
await writeToProcess(command, args, text);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
lastError = error;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
throw lastError instanceof Error ? lastError : new Error("No clipboard command available");
|
|
31
|
-
}
|
|
32
|
-
function writeToProcess(command, args, input) {
|
|
33
|
-
return new Promise((resolve, reject) => {
|
|
34
|
-
const child = spawn(command, args, {
|
|
35
|
-
stdio: ["pipe", "ignore", "pipe"],
|
|
36
|
-
windowsHide: true,
|
|
37
|
-
});
|
|
38
|
-
let stderr = "";
|
|
39
|
-
child.stderr.setEncoding("utf8");
|
|
40
|
-
child.stderr.on("data", (chunk) => {
|
|
41
|
-
stderr += chunk;
|
|
42
|
-
});
|
|
43
|
-
child.on("error", reject);
|
|
44
|
-
child.on("close", (code) => {
|
|
45
|
-
if (code === 0) {
|
|
46
|
-
resolve();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
reject(new Error(stderr.trim() || `${command} exited with code ${code}`));
|
|
50
|
-
});
|
|
51
|
-
child.stdin.end(input);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export type EscapeConfirmationDecision = {
|
|
2
|
-
action: "arm";
|
|
3
|
-
expiresAt: number;
|
|
4
|
-
} | {
|
|
5
|
-
action: "confirm";
|
|
6
|
-
};
|
|
7
|
-
export declare class EscapeConfirmationGate {
|
|
8
|
-
private readonly windowMs;
|
|
9
|
-
private armedRunId;
|
|
10
|
-
private deadline;
|
|
11
|
-
constructor(windowMs: number);
|
|
12
|
-
press(runId: number, now?: number): EscapeConfirmationDecision;
|
|
13
|
-
isArmed(runId: number, now?: number): boolean;
|
|
14
|
-
clear(): void;
|
|
15
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export class EscapeConfirmationGate {
|
|
2
|
-
windowMs;
|
|
3
|
-
armedRunId;
|
|
4
|
-
deadline = 0;
|
|
5
|
-
constructor(windowMs) {
|
|
6
|
-
this.windowMs = windowMs;
|
|
7
|
-
}
|
|
8
|
-
press(runId, now = Date.now()) {
|
|
9
|
-
if (this.armedRunId === runId && now <= this.deadline) {
|
|
10
|
-
this.clear();
|
|
11
|
-
return { action: "confirm" };
|
|
12
|
-
}
|
|
13
|
-
this.armedRunId = runId;
|
|
14
|
-
this.deadline = now + this.windowMs;
|
|
15
|
-
return { action: "arm", expiresAt: this.deadline };
|
|
16
|
-
}
|
|
17
|
-
isArmed(runId, now = Date.now()) {
|
|
18
|
-
if (this.armedRunId !== runId)
|
|
19
|
-
return false;
|
|
20
|
-
if (now > this.deadline) {
|
|
21
|
-
this.clear();
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
clear() {
|
|
27
|
-
this.armedRunId = undefined;
|
|
28
|
-
this.deadline = 0;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { isEscapeSequence } from "./prompt-keybindings.js";
|
|
2
|
-
export function normalizeKeyName(name) {
|
|
3
|
-
const rawName = String(name || "").toLowerCase();
|
|
4
|
-
if (["arrowleft", "left_arrow", "leftarrow", "cursorleft", "left"].includes(rawName))
|
|
5
|
-
return "left";
|
|
6
|
-
if (["arrowright", "right_arrow", "rightarrow", "cursorright", "right"].includes(rawName))
|
|
7
|
-
return "right";
|
|
8
|
-
if (["arrowup", "up_arrow", "uparrow", "cursorup", "up"].includes(rawName))
|
|
9
|
-
return "up";
|
|
10
|
-
if (["arrowdown", "down_arrow", "downarrow", "cursordown", "down"].includes(rawName))
|
|
11
|
-
return "down";
|
|
12
|
-
if (rawName === "return" || rawName === "enter")
|
|
13
|
-
return "enter";
|
|
14
|
-
if (rawName === "esc" || rawName === "escape")
|
|
15
|
-
return "escape";
|
|
16
|
-
if (rawName === "tab")
|
|
17
|
-
return "tab";
|
|
18
|
-
return rawName;
|
|
19
|
-
}
|
|
20
|
-
export function keyNameFromSequence(sequence) {
|
|
21
|
-
if (!sequence)
|
|
22
|
-
return "";
|
|
23
|
-
const kittyName = keyNameFromKittySequence(sequence);
|
|
24
|
-
if (kittyName)
|
|
25
|
-
return kittyName;
|
|
26
|
-
if (sequence === "\x1b[D" || /^\x1b\[[0-9;]*D$/.test(sequence))
|
|
27
|
-
return "left";
|
|
28
|
-
if (sequence === "\x1b[C" || /^\x1b\[[0-9;]*C$/.test(sequence))
|
|
29
|
-
return "right";
|
|
30
|
-
if (sequence === "\x1b[A" || /^\x1b\[[0-9;]*A$/.test(sequence))
|
|
31
|
-
return "up";
|
|
32
|
-
if (sequence === "\x1b[B" || /^\x1b\[[0-9;]*B$/.test(sequence))
|
|
33
|
-
return "down";
|
|
34
|
-
if (sequence === "\x1bOD")
|
|
35
|
-
return "left";
|
|
36
|
-
if (sequence === "\x1bOC")
|
|
37
|
-
return "right";
|
|
38
|
-
if (sequence === "\x1bOA")
|
|
39
|
-
return "up";
|
|
40
|
-
if (sequence === "\x1bOB")
|
|
41
|
-
return "down";
|
|
42
|
-
if (sequence === "\t")
|
|
43
|
-
return "tab";
|
|
44
|
-
if (sequence === "\r" || sequence === "\n")
|
|
45
|
-
return "enter";
|
|
46
|
-
if (isEscapeSequence(sequence))
|
|
47
|
-
return "escape";
|
|
48
|
-
return "";
|
|
49
|
-
}
|
|
50
|
-
function keyNameFromKittySequence(sequence) {
|
|
51
|
-
const kittyMatch = /^\x1b\[(\d+)(?:;[1-9]\d*(?::[1-3])?)?u$/.exec(sequence);
|
|
52
|
-
const kittyCode = kittyMatch?.[1] ? Number(kittyMatch[1]) : NaN;
|
|
53
|
-
if (!Number.isNaN(kittyCode))
|
|
54
|
-
return keyNameFromKittyCode(kittyCode);
|
|
55
|
-
const modifyOtherKeysMatch = /^\x1b\[27;[1-9]\d*(?::[1-3])?;(\d+)~$/.exec(sequence);
|
|
56
|
-
const modifyOtherKeysCode = modifyOtherKeysMatch?.[1] ? Number(modifyOtherKeysMatch[1]) : NaN;
|
|
57
|
-
if (!Number.isNaN(modifyOtherKeysCode))
|
|
58
|
-
return keyNameFromKittyCode(modifyOtherKeysCode);
|
|
59
|
-
return "";
|
|
60
|
-
}
|
|
61
|
-
function keyNameFromKittyCode(code) {
|
|
62
|
-
switch (code) {
|
|
63
|
-
case 27:
|
|
64
|
-
case 57344:
|
|
65
|
-
return "escape";
|
|
66
|
-
case 9:
|
|
67
|
-
case 57346:
|
|
68
|
-
return "tab";
|
|
69
|
-
case 13:
|
|
70
|
-
case 57345:
|
|
71
|
-
return "enter";
|
|
72
|
-
case 57350:
|
|
73
|
-
return "left";
|
|
74
|
-
case 57351:
|
|
75
|
-
return "right";
|
|
76
|
-
case 57352:
|
|
77
|
-
return "up";
|
|
78
|
-
case 57353:
|
|
79
|
-
return "down";
|
|
80
|
-
default:
|
|
81
|
-
return "";
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
export function keyNameFromEvent(event) {
|
|
85
|
-
const rawName = normalizeKeyName(event?.name || event?.key || event?.input);
|
|
86
|
-
return rawName || keyNameFromSequence(event?.raw || event?.sequence);
|
|
87
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export interface MarkdownInlineSegment {
|
|
2
|
-
text: string;
|
|
3
|
-
color?: "text" | "textMuted" | "success" | "warning" | "secondary";
|
|
4
|
-
bold?: boolean;
|
|
5
|
-
italic?: boolean;
|
|
6
|
-
dim?: boolean;
|
|
7
|
-
}
|
|
8
|
-
type InlineToken = {
|
|
9
|
-
type?: string;
|
|
10
|
-
text?: string;
|
|
11
|
-
raw?: string;
|
|
12
|
-
href?: string;
|
|
13
|
-
tokens?: InlineToken[];
|
|
14
|
-
};
|
|
15
|
-
interface InlineStyle {
|
|
16
|
-
bold?: boolean;
|
|
17
|
-
italic?: boolean;
|
|
18
|
-
dim?: boolean;
|
|
19
|
-
color?: MarkdownInlineSegment["color"];
|
|
20
|
-
}
|
|
21
|
-
export declare function markdownInlineSegments(tokens: InlineToken[] | undefined, fallback?: string, style?: InlineStyle): MarkdownInlineSegment[];
|
|
22
|
-
export {};
|