@bubblebrain-ai/bubble 0.0.23 → 0.0.25

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.
Files changed (168) hide show
  1. package/README.md +1 -1
  2. package/dist/config.d.ts +3 -0
  3. package/dist/config.js +22 -6
  4. package/dist/goal/command.d.ts +20 -0
  5. package/dist/goal/command.js +71 -0
  6. package/dist/goal/engine.d.ts +33 -0
  7. package/dist/goal/engine.js +65 -0
  8. package/dist/goal/format.d.ts +18 -0
  9. package/dist/goal/format.js +112 -0
  10. package/dist/goal/prompts.d.ts +13 -0
  11. package/dist/goal/prompts.js +84 -0
  12. package/dist/goal/store.d.ts +64 -0
  13. package/dist/goal/store.js +174 -0
  14. package/dist/goal/tools.d.ts +10 -0
  15. package/dist/goal/tools.js +70 -0
  16. package/dist/goal/usage.d.ts +2 -0
  17. package/dist/goal/usage.js +3 -0
  18. package/dist/main.js +29 -42
  19. package/dist/model-catalog.js +11 -0
  20. package/dist/provider-transform.js +17 -0
  21. package/dist/provider.js +20 -5
  22. package/dist/session-types.d.ts +3 -0
  23. package/dist/tools/index.d.ts +3 -0
  24. package/dist/tools/index.js +2 -0
  25. package/dist/tui/detect-theme.d.ts +1 -0
  26. package/dist/tui/detect-theme.js +23 -0
  27. package/dist/tui/image-display.d.ts +13 -0
  28. package/dist/tui/image-display.js +49 -0
  29. package/dist/tui/input-history.d.ts +37 -6
  30. package/dist/tui/input-history.js +194 -23
  31. package/dist/tui/model-switch.d.ts +42 -0
  32. package/dist/tui/model-switch.js +55 -0
  33. package/dist/tui-ink/app.d.ts +32 -2
  34. package/dist/tui-ink/app.js +1360 -522
  35. package/dist/tui-ink/approval/select.js +10 -0
  36. package/dist/tui-ink/detect-theme.d.ts +1 -2
  37. package/dist/tui-ink/detect-theme.js +1 -87
  38. package/dist/tui-ink/display-history.d.ts +1 -0
  39. package/dist/tui-ink/display-history.js +11 -0
  40. package/dist/tui-ink/feedback-dialog.js +10 -0
  41. package/dist/tui-ink/feishu-setup-picker.js +10 -0
  42. package/dist/tui-ink/footer.d.ts +1 -0
  43. package/dist/tui-ink/footer.js +8 -2
  44. package/dist/tui-ink/input-box.d.ts +70 -9
  45. package/dist/tui-ink/input-box.js +354 -120
  46. package/dist/tui-ink/input-history.d.ts +1 -16
  47. package/dist/tui-ink/input-history.js +1 -79
  48. package/dist/tui-ink/input-queue.d.ts +12 -0
  49. package/dist/tui-ink/input-queue.js +17 -0
  50. package/dist/tui-ink/key-events.d.ts +9 -0
  51. package/dist/tui-ink/key-events.js +8 -0
  52. package/dist/tui-ink/markdown.js +1 -1
  53. package/dist/tui-ink/message-list.d.ts +3 -1
  54. package/dist/tui-ink/message-list.js +42 -24
  55. package/dist/tui-ink/model-picker.d.ts +24 -2
  56. package/dist/tui-ink/model-picker.js +224 -20
  57. package/dist/tui-ink/plan-confirm.js +10 -0
  58. package/dist/tui-ink/question-dialog.js +10 -0
  59. package/dist/tui-ink/run.d.ts +11 -0
  60. package/dist/tui-ink/run.js +21 -28
  61. package/dist/tui-ink/session-picker.js +3 -0
  62. package/dist/tui-ink/submit-dedupe.d.ts +5 -0
  63. package/dist/tui-ink/submit-dedupe.js +25 -0
  64. package/dist/tui-ink/terminal-mouse.d.ts +13 -1
  65. package/dist/tui-ink/terminal-mouse.js +63 -21
  66. package/dist/tui-ink/theme.d.ts +6 -3
  67. package/dist/tui-ink/theme.js +10 -4
  68. package/dist/tui-ink/transcript-input.d.ts +8 -0
  69. package/dist/tui-ink/transcript-input.js +9 -0
  70. package/dist/tui-ink/transcript-viewport-math.d.ts +1 -2
  71. package/dist/tui-ink/transcript-viewport-math.js +1 -2
  72. package/dist/tui-ink/welcome.d.ts +1 -0
  73. package/dist/tui-ink/welcome.js +25 -28
  74. package/package.json +1 -5
  75. package/dist/tui/clipboard.d.ts +0 -1
  76. package/dist/tui/clipboard.js +0 -53
  77. package/dist/tui/escape-confirmation.d.ts +0 -15
  78. package/dist/tui/escape-confirmation.js +0 -30
  79. package/dist/tui/global-key-router.d.ts +0 -3
  80. package/dist/tui/global-key-router.js +0 -87
  81. package/dist/tui/markdown-inline.d.ts +0 -22
  82. package/dist/tui/markdown-inline.js +0 -68
  83. package/dist/tui/markdown-theme-rules.d.ts +0 -23
  84. package/dist/tui/markdown-theme-rules.js +0 -164
  85. package/dist/tui/markdown-theme.d.ts +0 -5
  86. package/dist/tui/markdown-theme.js +0 -27
  87. package/dist/tui/opencode-spinner.d.ts +0 -22
  88. package/dist/tui/opencode-spinner.js +0 -216
  89. package/dist/tui/prompt-keybindings.d.ts +0 -42
  90. package/dist/tui/prompt-keybindings.js +0 -35
  91. package/dist/tui/render-signature.d.ts +0 -1
  92. package/dist/tui/render-signature.js +0 -7
  93. package/dist/tui/run.d.ts +0 -65
  94. package/dist/tui/run.js +0 -9934
  95. package/dist/tui/sidebar-mcp.d.ts +0 -31
  96. package/dist/tui/sidebar-mcp.js +0 -62
  97. package/dist/tui/sidebar-state.d.ts +0 -12
  98. package/dist/tui/sidebar-state.js +0 -69
  99. package/dist/tui/streaming-tool-args.d.ts +0 -15
  100. package/dist/tui/streaming-tool-args.js +0 -30
  101. package/dist/tui/tool-renderers/fallback.d.ts +0 -2
  102. package/dist/tui/tool-renderers/fallback.js +0 -75
  103. package/dist/tui/tool-renderers/registry.d.ts +0 -3
  104. package/dist/tui/tool-renderers/registry.js +0 -11
  105. package/dist/tui/tool-renderers/subagent.d.ts +0 -2
  106. package/dist/tui/tool-renderers/subagent.js +0 -135
  107. package/dist/tui/tool-renderers/types.d.ts +0 -36
  108. package/dist/tui/tool-renderers/types.js +0 -1
  109. package/dist/tui/tool-renderers/write-preview.d.ts +0 -12
  110. package/dist/tui/tool-renderers/write-preview.js +0 -32
  111. package/dist/tui/tool-renderers/write.d.ts +0 -6
  112. package/dist/tui/tool-renderers/write.js +0 -88
  113. package/dist/tui-opentui/app.d.ts +0 -54
  114. package/dist/tui-opentui/app.js +0 -1371
  115. package/dist/tui-opentui/approval/approval-dialog.d.ts +0 -15
  116. package/dist/tui-opentui/approval/approval-dialog.js +0 -155
  117. package/dist/tui-opentui/approval/diff-view.d.ts +0 -9
  118. package/dist/tui-opentui/approval/diff-view.js +0 -43
  119. package/dist/tui-opentui/approval/select.d.ts +0 -37
  120. package/dist/tui-opentui/approval/select.js +0 -91
  121. package/dist/tui-opentui/detect-theme.d.ts +0 -2
  122. package/dist/tui-opentui/detect-theme.js +0 -87
  123. package/dist/tui-opentui/display-history.d.ts +0 -56
  124. package/dist/tui-opentui/display-history.js +0 -130
  125. package/dist/tui-opentui/edit-diff.d.ts +0 -11
  126. package/dist/tui-opentui/edit-diff.js +0 -57
  127. package/dist/tui-opentui/feedback-dialog.d.ts +0 -21
  128. package/dist/tui-opentui/feedback-dialog.js +0 -164
  129. package/dist/tui-opentui/feishu-setup-picker.d.ts +0 -7
  130. package/dist/tui-opentui/feishu-setup-picker.js +0 -272
  131. package/dist/tui-opentui/file-mentions.d.ts +0 -29
  132. package/dist/tui-opentui/file-mentions.js +0 -174
  133. package/dist/tui-opentui/footer.d.ts +0 -26
  134. package/dist/tui-opentui/footer.js +0 -40
  135. package/dist/tui-opentui/image-paste.d.ts +0 -54
  136. package/dist/tui-opentui/image-paste.js +0 -288
  137. package/dist/tui-opentui/input-box.d.ts +0 -32
  138. package/dist/tui-opentui/input-box.js +0 -462
  139. package/dist/tui-opentui/input-history.d.ts +0 -16
  140. package/dist/tui-opentui/input-history.js +0 -79
  141. package/dist/tui-opentui/markdown.d.ts +0 -66
  142. package/dist/tui-opentui/markdown.js +0 -127
  143. package/dist/tui-opentui/message-list.d.ts +0 -31
  144. package/dist/tui-opentui/message-list.js +0 -131
  145. package/dist/tui-opentui/model-picker.d.ts +0 -63
  146. package/dist/tui-opentui/model-picker.js +0 -450
  147. package/dist/tui-opentui/plan-confirm.d.ts +0 -9
  148. package/dist/tui-opentui/plan-confirm.js +0 -124
  149. package/dist/tui-opentui/question-dialog.d.ts +0 -10
  150. package/dist/tui-opentui/question-dialog.js +0 -110
  151. package/dist/tui-opentui/recent-activity.d.ts +0 -8
  152. package/dist/tui-opentui/recent-activity.js +0 -71
  153. package/dist/tui-opentui/run-session-picker.d.ts +0 -10
  154. package/dist/tui-opentui/run-session-picker.js +0 -28
  155. package/dist/tui-opentui/run.d.ts +0 -38
  156. package/dist/tui-opentui/run.js +0 -48
  157. package/dist/tui-opentui/session-picker.d.ts +0 -12
  158. package/dist/tui-opentui/session-picker.js +0 -120
  159. package/dist/tui-opentui/theme.d.ts +0 -89
  160. package/dist/tui-opentui/theme.js +0 -157
  161. package/dist/tui-opentui/todos.d.ts +0 -9
  162. package/dist/tui-opentui/todos.js +0 -45
  163. package/dist/tui-opentui/trace-groups.d.ts +0 -27
  164. package/dist/tui-opentui/trace-groups.js +0 -455
  165. package/dist/tui-opentui/use-terminal-size.d.ts +0 -4
  166. package/dist/tui-opentui/use-terminal-size.js +0 -5
  167. package/dist/tui-opentui/welcome.d.ts +0 -25
  168. package/dist/tui-opentui/welcome.js +0 -77
@@ -1,27 +1,69 @@
1
- // Bubble does NOT enable mouse reporting plain drag-select and copy keep
2
- // their native terminal behavior. This disable sequence is written
3
- // defensively on teardown in case a previous crash left reporting on.
4
- export const MOUSE_REPORTING_DISABLE = "\x1b[?1006l\x1b[?1000l";
5
- const SGR_MOUSE_SEQUENCE_RE = /\x1b?\[?<\d+;\d+;\d+[mM]/g;
6
- const SGR_MOUSE_WHEEL_RE = /\x1b?\[?<(\d+);\d+;\d+([mM])/g;
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
+ const ESCAPED_MOUSE_SEQUENCE_RE = /\x1b(?:\[<(\d+);\d+;\d+([mM])|\[M([\s\S])[\s\S]{2})/g;
14
+ const RAW_SGR_MOUSE_SEQUENCE_RE = /\[?<(\d+);\d+;\d+([mM])/g;
15
+ const RAW_SGR_MOUSE_INPUT_RE = /^(?:\[?<\d+;\d+;\d+[mM])+$/;
16
+ function wheelDirectionFromButtonCode(code) {
17
+ if ((code & 64) !== 64)
18
+ return undefined;
19
+ const wheelButton = code & 0b11;
20
+ if (wheelButton === 0)
21
+ return "up";
22
+ if (wheelButton === 1)
23
+ return "down";
24
+ return undefined;
25
+ }
26
+ function collectWheelDirection(directions, code, final) {
27
+ if (final !== undefined && final !== "M")
28
+ return;
29
+ const direction = wheelDirectionFromButtonCode(code);
30
+ if (direction)
31
+ directions.push(direction);
32
+ }
33
+ export function sanitizeTerminalMouseInput(input) {
34
+ const wheelDirections = [];
35
+ let hasMouse = false;
36
+ ESCAPED_MOUSE_SEQUENCE_RE.lastIndex = 0;
37
+ let strippedInput = input.replace(ESCAPED_MOUSE_SEQUENCE_RE, (_sequence, sgrCode, sgrFinal, x10Button) => {
38
+ hasMouse = true;
39
+ const code = sgrCode !== undefined
40
+ ? Number(sgrCode)
41
+ : (x10Button?.charCodeAt(0) ?? 32) - 32;
42
+ collectWheelDirection(wheelDirections, code, sgrFinal);
43
+ return "";
44
+ });
45
+ if (RAW_SGR_MOUSE_INPUT_RE.test(strippedInput)) {
46
+ hasMouse = true;
47
+ RAW_SGR_MOUSE_SEQUENCE_RE.lastIndex = 0;
48
+ strippedInput = strippedInput.replace(RAW_SGR_MOUSE_SEQUENCE_RE, (_sequence, sgrCode, sgrFinal) => {
49
+ collectWheelDirection(wheelDirections, Number(sgrCode), sgrFinal);
50
+ return "";
51
+ });
52
+ }
53
+ return { strippedInput, wheelDirections, hasMouse };
54
+ }
55
+ export function transcriptScrollLinesFromMouseInput(mouseInput, options) {
56
+ if (options.overlayActive)
57
+ return [];
58
+ return mouseInput.wheelDirections.map((direction) => direction === "up" ? -1 : 1);
59
+ }
7
60
  export function stripTerminalMouseSequences(input) {
8
- return input.replace(SGR_MOUSE_SEQUENCE_RE, "");
61
+ return sanitizeTerminalMouseInput(input).strippedInput;
9
62
  }
10
63
  export function hasTerminalMouseSequence(input) {
11
- SGR_MOUSE_SEQUENCE_RE.lastIndex = 0;
12
- return SGR_MOUSE_SEQUENCE_RE.test(input);
64
+ ESCAPED_MOUSE_SEQUENCE_RE.lastIndex = 0;
65
+ return ESCAPED_MOUSE_SEQUENCE_RE.test(input) || RAW_SGR_MOUSE_INPUT_RE.test(input);
13
66
  }
14
67
  export function parseTerminalMouseWheel(input) {
15
- const directions = [];
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;
68
+ return sanitizeTerminalMouseInput(input).wheelDirections;
27
69
  }
@@ -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 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.
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>;
@@ -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 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.
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: "#E6E8E3",
72
+ inputBgDisabled: "#F6F6F3",
67
73
  inputText: "#171717",
68
74
  inputPlaceholder: "#6F7377",
69
75
  muted: "#6F7377",
@@ -0,0 +1,8 @@
1
+ export type TranscriptPageScrollDirection = "up" | "down";
2
+ export interface TranscriptKeyEvent {
3
+ pageUp?: boolean;
4
+ pageDown?: boolean;
5
+ }
6
+ export declare function transcriptPageScrollDirection(key: TranscriptKeyEvent, options: {
7
+ overlayActive: boolean;
8
+ }): TranscriptPageScrollDirection | undefined;
@@ -0,0 +1,9 @@
1
+ export function transcriptPageScrollDirection(key, options) {
2
+ if (options.overlayActive)
3
+ return undefined;
4
+ if (key.pageUp)
5
+ return "up";
6
+ if (key.pageDown)
7
+ return "down";
8
+ return undefined;
9
+ }
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Pure scroll arithmetic for the alt-screen transcript viewport.
3
3
  *
4
- * Mirrors the live OpenTUI scrollbox semantics (src/tui/run.ts
5
- * transcriptMaxScrollTop / isTranscriptAtBottom): "at bottom" tolerates a
4
+ * Mirrors the historical transcript viewport semantics: "at bottom" tolerates a
6
5
  * one-line slack so sub-line rounding never flips the follow flag while the
7
6
  * user sits at the end of the transcript.
8
7
  */
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Pure scroll arithmetic for the alt-screen transcript viewport.
3
3
  *
4
- * Mirrors the live OpenTUI scrollbox semantics (src/tui/run.ts
5
- * transcriptMaxScrollTop / isTranscriptAtBottom): "at bottom" tolerates a
4
+ * Mirrors the historical transcript viewport semantics: "at bottom" tolerates a
6
5
  * one-line slack so sub-line rounding never flips the follow flag while the
7
6
  * user sits at the end of the transcript.
8
7
  */
@@ -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 {};
@@ -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,25 @@ 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(20, Math.min(terminalColumns - 2, 118));
19
- // Adaptive sizing: large pixel logo on wide terminals, standard, then the
20
- // single-line compact mark — same thresholds as the OpenTUI home screen.
21
- const logoLines = bubbleWordmarkForWidth(effectiveWidth);
22
- const actionableTips = tips
23
- .filter((item) => !item.startsWith("Ready with") && item.trim().length > 0)
24
- .slice(0, 2);
25
- const tip = actionableTips.length > 0
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 LogoRow({ line }) {
31
- const theme = useTheme();
32
- if (!line.segments) {
33
- return _jsx(Text, { bold: true, color: logoColor(theme, line.tone ?? "caption"), children: line.text ?? "" });
34
- }
35
- 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}`))) }));
36
- }
37
- function logoColor(theme, tone) {
38
- switch (tone) {
39
- case "brand": return theme.warning;
40
- case "ink": return theme.userMessageText;
41
- case "stone": return theme.muted;
42
- case "soft": return theme.dim;
43
- case "caption": return theme.muted;
44
- }
32
+ export function formatModelLine({ providerId, modelLabel, thinkingLabel, tips, }) {
33
+ const parts = [];
34
+ if (modelLabel)
35
+ parts.push(thinkingLabel ? `${modelLabel} with ${thinkingLabel} effort` : modelLabel);
36
+ const readyTip = tips.find((item) => item.startsWith("Ready with"));
37
+ if (!modelLabel && readyTip)
38
+ parts.push(readyTip.replace(/^Ready with\s+/, ""));
39
+ if (providerId)
40
+ parts.push(providerId);
41
+ return parts.join(" · ");
45
42
  }
46
43
  function readPackageVersion() {
47
44
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bubblebrain-ai/bubble",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
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",
@@ -1 +0,0 @@
1
- export declare function copyTextToClipboard(text: string): Promise<void>;
@@ -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,3 +0,0 @@
1
- export declare function normalizeKeyName(name?: string): string;
2
- export declare function keyNameFromSequence(sequence?: string): string;
3
- export declare function keyNameFromEvent(event: any): string;
@@ -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 {};
@@ -1,68 +0,0 @@
1
- export function markdownInlineSegments(tokens, fallback = "", style = {}) {
2
- const segments = [];
3
- for (const token of tokens ?? []) {
4
- appendInlineToken(segments, token, style);
5
- }
6
- if (segments.length === 0 && fallback) {
7
- appendStyled(segments, fallback, style);
8
- }
9
- return segments;
10
- }
11
- function appendInlineToken(segments, token, style) {
12
- switch (token.type) {
13
- case "strong":
14
- appendInlineTokens(segments, token.tokens, { ...style, bold: true });
15
- return;
16
- case "em":
17
- appendInlineTokens(segments, token.tokens, { ...style, italic: true, color: style.color ?? "warning" });
18
- return;
19
- case "del":
20
- appendInlineTokens(segments, token.tokens, { ...style, dim: true, color: style.color ?? "textMuted" });
21
- return;
22
- case "codespan":
23
- appendStyled(segments, token.text ?? "", { ...style, color: "success" });
24
- return;
25
- case "link":
26
- appendInlineTokens(segments, token.tokens, { ...style, color: style.color ?? "secondary" });
27
- return;
28
- case "br":
29
- appendStyled(segments, "\n", style);
30
- return;
31
- case "text":
32
- case "paragraph":
33
- case "list_item":
34
- case "heading":
35
- if (token.tokens?.length) {
36
- appendInlineTokens(segments, token.tokens, style);
37
- }
38
- else {
39
- appendStyled(segments, token.text ?? token.raw ?? "", style);
40
- }
41
- return;
42
- case "space":
43
- return;
44
- default:
45
- if (token.tokens?.length) {
46
- appendInlineTokens(segments, token.tokens, style);
47
- }
48
- else {
49
- appendStyled(segments, token.text ?? token.raw ?? "", style);
50
- }
51
- }
52
- }
53
- function appendInlineTokens(segments, tokens, style) {
54
- for (const child of tokens ?? []) {
55
- appendInlineToken(segments, child, style);
56
- }
57
- }
58
- function appendStyled(segments, text, style) {
59
- if (!text)
60
- return;
61
- segments.push({
62
- text,
63
- color: style.color ?? "text",
64
- bold: style.bold,
65
- italic: style.italic,
66
- dim: style.dim,
67
- });
68
- }
@@ -1,23 +0,0 @@
1
- export interface MarkdownThemePalette {
2
- text: string;
3
- textMuted: string;
4
- background: string;
5
- secondary: string;
6
- info: string;
7
- success: string;
8
- warning: string;
9
- accent: string;
10
- error: string;
11
- }
12
- export interface SyntaxThemeRule {
13
- scope: string[];
14
- style: {
15
- foreground?: string;
16
- background?: string;
17
- bold?: boolean;
18
- italic?: boolean;
19
- underline?: boolean;
20
- dim?: boolean;
21
- };
22
- }
23
- export declare function buildMarkdownThemeRules(theme: MarkdownThemePalette): SyntaxThemeRule[];