@schlessera/brain-ui-react 0.4.0

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 (311) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/components/chat/ask-user-card.d.ts +12 -0
  4. package/dist/components/chat/ask-user-card.d.ts.map +1 -0
  5. package/dist/components/chat/ask-user-card.js +185 -0
  6. package/dist/components/chat/ask-user-card.js.map +1 -0
  7. package/dist/components/chat/brain-markdown.d.ts +35 -0
  8. package/dist/components/chat/brain-markdown.d.ts.map +1 -0
  9. package/dist/components/chat/brain-markdown.js +363 -0
  10. package/dist/components/chat/brain-markdown.js.map +1 -0
  11. package/dist/components/chat/chat-page.d.ts +2 -0
  12. package/dist/components/chat/chat-page.d.ts.map +1 -0
  13. package/dist/components/chat/chat-page.js +442 -0
  14. package/dist/components/chat/chat-page.js.map +1 -0
  15. package/dist/components/chat/command-palette.d.ts +5 -0
  16. package/dist/components/chat/command-palette.d.ts.map +1 -0
  17. package/dist/components/chat/command-palette.js +18 -0
  18. package/dist/components/chat/command-palette.js.map +1 -0
  19. package/dist/components/chat/markdown-content.d.ts +4 -0
  20. package/dist/components/chat/markdown-content.d.ts.map +1 -0
  21. package/dist/components/chat/markdown-content.js +6 -0
  22. package/dist/components/chat/markdown-content.js.map +1 -0
  23. package/dist/components/chat/message-bubble.d.ts +9 -0
  24. package/dist/components/chat/message-bubble.d.ts.map +1 -0
  25. package/dist/components/chat/message-bubble.js +129 -0
  26. package/dist/components/chat/message-bubble.js.map +1 -0
  27. package/dist/components/chat/message-share.d.ts +10 -0
  28. package/dist/components/chat/message-share.d.ts.map +1 -0
  29. package/dist/components/chat/message-share.js +65 -0
  30. package/dist/components/chat/message-share.js.map +1 -0
  31. package/dist/components/chat/renderers/claude-tools.d.ts +3 -0
  32. package/dist/components/chat/renderers/claude-tools.d.ts.map +1 -0
  33. package/dist/components/chat/renderers/claude-tools.js +38 -0
  34. package/dist/components/chat/renderers/claude-tools.js.map +1 -0
  35. package/dist/components/chat/renderers/generic.d.ts +9 -0
  36. package/dist/components/chat/renderers/generic.d.ts.map +1 -0
  37. package/dist/components/chat/renderers/generic.js +68 -0
  38. package/dist/components/chat/renderers/generic.js.map +1 -0
  39. package/dist/components/chat/renderers/index.d.ts +5 -0
  40. package/dist/components/chat/renderers/index.d.ts.map +1 -0
  41. package/dist/components/chat/renderers/index.js +17 -0
  42. package/dist/components/chat/renderers/index.js.map +1 -0
  43. package/dist/components/chat/risk-hints.d.ts +20 -0
  44. package/dist/components/chat/risk-hints.d.ts.map +1 -0
  45. package/dist/components/chat/risk-hints.js +81 -0
  46. package/dist/components/chat/risk-hints.js.map +1 -0
  47. package/dist/components/chat/session-drawer.d.ts +6 -0
  48. package/dist/components/chat/session-drawer.d.ts.map +1 -0
  49. package/dist/components/chat/session-drawer.js +96 -0
  50. package/dist/components/chat/session-drawer.js.map +1 -0
  51. package/dist/components/chat/share-block.d.ts +9 -0
  52. package/dist/components/chat/share-block.d.ts.map +1 -0
  53. package/dist/components/chat/share-block.js +70 -0
  54. package/dist/components/chat/share-block.js.map +1 -0
  55. package/dist/components/chat/tool-call-timeline.d.ts +8 -0
  56. package/dist/components/chat/tool-call-timeline.d.ts.map +1 -0
  57. package/dist/components/chat/tool-call-timeline.js +100 -0
  58. package/dist/components/chat/tool-call-timeline.js.map +1 -0
  59. package/dist/components/chat/tool-views.d.ts +116 -0
  60. package/dist/components/chat/tool-views.d.ts.map +1 -0
  61. package/dist/components/chat/tool-views.js +628 -0
  62. package/dist/components/chat/tool-views.js.map +1 -0
  63. package/dist/components/chat/welcome-state.d.ts +4 -0
  64. package/dist/components/chat/welcome-state.d.ts.map +1 -0
  65. package/dist/components/chat/welcome-state.js +10 -0
  66. package/dist/components/chat/welcome-state.js.map +1 -0
  67. package/dist/components/connectivity/connection-gate.d.ts +5 -0
  68. package/dist/components/connectivity/connection-gate.d.ts.map +1 -0
  69. package/dist/components/connectivity/connection-gate.js +42 -0
  70. package/dist/components/connectivity/connection-gate.js.map +1 -0
  71. package/dist/components/connectivity/login-screen.d.ts +9 -0
  72. package/dist/components/connectivity/login-screen.d.ts.map +1 -0
  73. package/dist/components/connectivity/login-screen.js +99 -0
  74. package/dist/components/connectivity/login-screen.js.map +1 -0
  75. package/dist/components/files/file-panel.d.ts +5 -0
  76. package/dist/components/files/file-panel.d.ts.map +1 -0
  77. package/dist/components/files/file-panel.js +46 -0
  78. package/dist/components/files/file-panel.js.map +1 -0
  79. package/dist/components/files/file-tree.d.ts +2 -0
  80. package/dist/components/files/file-tree.d.ts.map +1 -0
  81. package/dist/components/files/file-tree.js +63 -0
  82. package/dist/components/files/file-tree.js.map +1 -0
  83. package/dist/components/files/file-viewer-binary.d.ts +5 -0
  84. package/dist/components/files/file-viewer-binary.d.ts.map +1 -0
  85. package/dist/components/files/file-viewer-binary.js +20 -0
  86. package/dist/components/files/file-viewer-binary.js.map +1 -0
  87. package/dist/components/files/file-viewer-html.d.ts +4 -0
  88. package/dist/components/files/file-viewer-html.d.ts.map +1 -0
  89. package/dist/components/files/file-viewer-html.js +5 -0
  90. package/dist/components/files/file-viewer-html.js.map +1 -0
  91. package/dist/components/files/file-viewer-markdown.d.ts +4 -0
  92. package/dist/components/files/file-viewer-markdown.d.ts.map +1 -0
  93. package/dist/components/files/file-viewer-markdown.js +10 -0
  94. package/dist/components/files/file-viewer-markdown.js.map +1 -0
  95. package/dist/components/files/file-viewer-raw.d.ts +5 -0
  96. package/dist/components/files/file-viewer-raw.d.ts.map +1 -0
  97. package/dist/components/files/file-viewer-raw.js +15 -0
  98. package/dist/components/files/file-viewer-raw.js.map +1 -0
  99. package/dist/components/files/file-viewer.d.ts +2 -0
  100. package/dist/components/files/file-viewer.d.ts.map +1 -0
  101. package/dist/components/files/file-viewer.js +175 -0
  102. package/dist/components/files/file-viewer.js.map +1 -0
  103. package/dist/components/files/frontmatter-panel.d.ts +5 -0
  104. package/dist/components/files/frontmatter-panel.d.ts.map +1 -0
  105. package/dist/components/files/frontmatter-panel.js +12 -0
  106. package/dist/components/files/frontmatter-panel.js.map +1 -0
  107. package/dist/components/layout/app-shell.d.ts +5 -0
  108. package/dist/components/layout/app-shell.d.ts.map +1 -0
  109. package/dist/components/layout/app-shell.js +10 -0
  110. package/dist/components/layout/app-shell.js.map +1 -0
  111. package/dist/components/layout/mobile-tab-bar.d.ts +2 -0
  112. package/dist/components/layout/mobile-tab-bar.d.ts.map +1 -0
  113. package/dist/components/layout/mobile-tab-bar.js +16 -0
  114. package/dist/components/layout/mobile-tab-bar.js.map +1 -0
  115. package/dist/components/layout/side-rail.d.ts +2 -0
  116. package/dist/components/layout/side-rail.d.ts.map +1 -0
  117. package/dist/components/layout/side-rail.js +26 -0
  118. package/dist/components/layout/side-rail.js.map +1 -0
  119. package/dist/components/layout/slide-panel.d.ts +9 -0
  120. package/dist/components/layout/slide-panel.d.ts.map +1 -0
  121. package/dist/components/layout/slide-panel.js +18 -0
  122. package/dist/components/layout/slide-panel.js.map +1 -0
  123. package/dist/components/quick-actions/streaming-modal.d.ts +8 -0
  124. package/dist/components/quick-actions/streaming-modal.d.ts.map +1 -0
  125. package/dist/components/quick-actions/streaming-modal.js +92 -0
  126. package/dist/components/quick-actions/streaming-modal.js.map +1 -0
  127. package/dist/components/quick-actions/whatsup-modal.d.ts +5 -0
  128. package/dist/components/quick-actions/whatsup-modal.d.ts.map +1 -0
  129. package/dist/components/quick-actions/whatsup-modal.js +82 -0
  130. package/dist/components/quick-actions/whatsup-modal.js.map +1 -0
  131. package/dist/components/settings/passkey-panel.d.ts +10 -0
  132. package/dist/components/settings/passkey-panel.d.ts.map +1 -0
  133. package/dist/components/settings/passkey-panel.js +118 -0
  134. package/dist/components/settings/passkey-panel.js.map +1 -0
  135. package/dist/components/share/share-menu.d.ts +23 -0
  136. package/dist/components/share/share-menu.d.ts.map +1 -0
  137. package/dist/components/share/share-menu.js +58 -0
  138. package/dist/components/share/share-menu.js.map +1 -0
  139. package/dist/components/voice/dictation-sheet.d.ts +6 -0
  140. package/dist/components/voice/dictation-sheet.d.ts.map +1 -0
  141. package/dist/components/voice/dictation-sheet.js +91 -0
  142. package/dist/components/voice/dictation-sheet.js.map +1 -0
  143. package/dist/components/voice/mic-button.d.ts +6 -0
  144. package/dist/components/voice/mic-button.d.ts.map +1 -0
  145. package/dist/components/voice/mic-button.js +9 -0
  146. package/dist/components/voice/mic-button.js.map +1 -0
  147. package/dist/components/voice/review-card.d.ts +8 -0
  148. package/dist/components/voice/review-card.d.ts.map +1 -0
  149. package/dist/components/voice/review-card.js +8 -0
  150. package/dist/components/voice/review-card.js.map +1 -0
  151. package/dist/config.d.ts +19 -0
  152. package/dist/config.d.ts.map +1 -0
  153. package/dist/config.js +10 -0
  154. package/dist/config.js.map +1 -0
  155. package/dist/hooks/use-vpn-status.d.ts +2 -0
  156. package/dist/hooks/use-vpn-status.d.ts.map +1 -0
  157. package/dist/hooks/use-vpn-status.js +71 -0
  158. package/dist/hooks/use-vpn-status.js.map +1 -0
  159. package/dist/hooks/use-websocket.d.ts +8 -0
  160. package/dist/hooks/use-websocket.d.ts.map +1 -0
  161. package/dist/hooks/use-websocket.js +366 -0
  162. package/dist/hooks/use-websocket.js.map +1 -0
  163. package/dist/index.d.ts +25 -0
  164. package/dist/index.d.ts.map +1 -0
  165. package/dist/index.js +31 -0
  166. package/dist/index.js.map +1 -0
  167. package/dist/lib/api-client.d.ts +115 -0
  168. package/dist/lib/api-client.d.ts.map +1 -0
  169. package/dist/lib/api-client.js +70 -0
  170. package/dist/lib/api-client.js.map +1 -0
  171. package/dist/lib/backend.d.ts +7 -0
  172. package/dist/lib/backend.d.ts.map +1 -0
  173. package/dist/lib/backend.js +33 -0
  174. package/dist/lib/backend.js.map +1 -0
  175. package/dist/lib/frontmatter.d.ts +21 -0
  176. package/dist/lib/frontmatter.d.ts.map +1 -0
  177. package/dist/lib/frontmatter.js +88 -0
  178. package/dist/lib/frontmatter.js.map +1 -0
  179. package/dist/lib/image-attachments.d.ts +36 -0
  180. package/dist/lib/image-attachments.d.ts.map +1 -0
  181. package/dist/lib/image-attachments.js +175 -0
  182. package/dist/lib/image-attachments.js.map +1 -0
  183. package/dist/lib/passkeys.d.ts +18 -0
  184. package/dist/lib/passkeys.d.ts.map +1 -0
  185. package/dist/lib/passkeys.js +36 -0
  186. package/dist/lib/passkeys.js.map +1 -0
  187. package/dist/lib/share.d.ts +53 -0
  188. package/dist/lib/share.d.ts.map +1 -0
  189. package/dist/lib/share.js +144 -0
  190. package/dist/lib/share.js.map +1 -0
  191. package/dist/lib/strip-markdown.d.ts +10 -0
  192. package/dist/lib/strip-markdown.d.ts.map +1 -0
  193. package/dist/lib/strip-markdown.js +37 -0
  194. package/dist/lib/strip-markdown.js.map +1 -0
  195. package/dist/lib/tool-names.d.ts +17 -0
  196. package/dist/lib/tool-names.d.ts.map +1 -0
  197. package/dist/lib/tool-names.js +23 -0
  198. package/dist/lib/tool-names.js.map +1 -0
  199. package/dist/lib/utils.d.ts +3 -0
  200. package/dist/lib/utils.d.ts.map +1 -0
  201. package/dist/lib/utils.js +6 -0
  202. package/dist/lib/utils.js.map +1 -0
  203. package/dist/lib/ws-client.d.ts +26 -0
  204. package/dist/lib/ws-client.d.ts.map +1 -0
  205. package/dist/lib/ws-client.js +93 -0
  206. package/dist/lib/ws-client.js.map +1 -0
  207. package/dist/stores/chat-store.d.ts +131 -0
  208. package/dist/stores/chat-store.d.ts.map +1 -0
  209. package/dist/stores/chat-store.js +376 -0
  210. package/dist/stores/chat-store.js.map +1 -0
  211. package/dist/stores/connection-store.d.ts +11 -0
  212. package/dist/stores/connection-store.d.ts.map +1 -0
  213. package/dist/stores/connection-store.js +8 -0
  214. package/dist/stores/connection-store.js.map +1 -0
  215. package/dist/stores/file-store.d.ts +53 -0
  216. package/dist/stores/file-store.d.ts.map +1 -0
  217. package/dist/stores/file-store.js +284 -0
  218. package/dist/stores/file-store.js.map +1 -0
  219. package/dist/stores/provider-store.d.ts +24 -0
  220. package/dist/stores/provider-store.d.ts.map +1 -0
  221. package/dist/stores/provider-store.js +56 -0
  222. package/dist/stores/provider-store.js.map +1 -0
  223. package/dist/stores/ui-store.d.ts +21 -0
  224. package/dist/stores/ui-store.d.ts.map +1 -0
  225. package/dist/stores/ui-store.js +23 -0
  226. package/dist/stores/ui-store.js.map +1 -0
  227. package/dist/styles.css +2 -0
  228. package/dist/voice/asr-clients.d.ts +2 -0
  229. package/dist/voice/asr-clients.d.ts.map +1 -0
  230. package/dist/voice/asr-clients.js +33 -0
  231. package/dist/voice/asr-clients.js.map +1 -0
  232. package/dist/voice/asr-deepgram.d.ts +38 -0
  233. package/dist/voice/asr-deepgram.d.ts.map +1 -0
  234. package/dist/voice/asr-deepgram.js +307 -0
  235. package/dist/voice/asr-deepgram.js.map +1 -0
  236. package/dist/voice/asr-webspeech.d.ts +23 -0
  237. package/dist/voice/asr-webspeech.d.ts.map +1 -0
  238. package/dist/voice/asr-webspeech.js +93 -0
  239. package/dist/voice/asr-webspeech.js.map +1 -0
  240. package/dist/voice/use-dictation.d.ts +6 -0
  241. package/dist/voice/use-dictation.d.ts.map +1 -0
  242. package/dist/voice/use-dictation.js +174 -0
  243. package/dist/voice/use-dictation.js.map +1 -0
  244. package/dist/voice/voice-store.d.ts +26 -0
  245. package/dist/voice/voice-store.d.ts.map +1 -0
  246. package/dist/voice/voice-store.js +27 -0
  247. package/dist/voice/voice-store.js.map +1 -0
  248. package/package.json +71 -0
  249. package/src/components/chat/ask-user-card.tsx +451 -0
  250. package/src/components/chat/brain-markdown.tsx +547 -0
  251. package/src/components/chat/chat-page.tsx +820 -0
  252. package/src/components/chat/command-palette.tsx +68 -0
  253. package/src/components/chat/markdown-content.tsx +5 -0
  254. package/src/components/chat/message-bubble.tsx +362 -0
  255. package/src/components/chat/message-share.ts +78 -0
  256. package/src/components/chat/renderers/claude-tools.tsx +53 -0
  257. package/src/components/chat/renderers/generic.tsx +85 -0
  258. package/src/components/chat/renderers/index.ts +19 -0
  259. package/src/components/chat/risk-hints.ts +95 -0
  260. package/src/components/chat/session-drawer.tsx +213 -0
  261. package/src/components/chat/share-block.tsx +128 -0
  262. package/src/components/chat/tool-call-timeline.tsx +322 -0
  263. package/src/components/chat/tool-views.tsx +898 -0
  264. package/src/components/chat/welcome-state.tsx +105 -0
  265. package/src/components/connectivity/connection-gate.tsx +137 -0
  266. package/src/components/connectivity/login-screen.tsx +169 -0
  267. package/src/components/files/file-panel.tsx +116 -0
  268. package/src/components/files/file-tree.tsx +143 -0
  269. package/src/components/files/file-viewer-binary.tsx +63 -0
  270. package/src/components/files/file-viewer-html.tsx +10 -0
  271. package/src/components/files/file-viewer-markdown.tsx +14 -0
  272. package/src/components/files/file-viewer-raw.tsx +44 -0
  273. package/src/components/files/file-viewer.tsx +290 -0
  274. package/src/components/files/frontmatter-panel.tsx +78 -0
  275. package/src/components/layout/app-shell.tsx +25 -0
  276. package/src/components/layout/mobile-tab-bar.tsx +54 -0
  277. package/src/components/layout/side-rail.tsx +128 -0
  278. package/src/components/layout/slide-panel.tsx +64 -0
  279. package/src/components/quick-actions/streaming-modal.tsx +168 -0
  280. package/src/components/quick-actions/whatsup-modal.tsx +149 -0
  281. package/src/components/settings/passkey-panel.tsx +281 -0
  282. package/src/components/share/share-menu.tsx +121 -0
  283. package/src/components/voice/dictation-sheet.tsx +217 -0
  284. package/src/components/voice/mic-button.tsx +31 -0
  285. package/src/components/voice/review-card.tsx +66 -0
  286. package/src/config.ts +27 -0
  287. package/src/hooks/use-vpn-status.ts +75 -0
  288. package/src/hooks/use-websocket.ts +403 -0
  289. package/src/index.ts +46 -0
  290. package/src/lib/api-client.ts +173 -0
  291. package/src/lib/backend.ts +35 -0
  292. package/src/lib/frontmatter.ts +99 -0
  293. package/src/lib/image-attachments.ts +221 -0
  294. package/src/lib/passkeys.ts +50 -0
  295. package/src/lib/share.ts +167 -0
  296. package/src/lib/strip-markdown.ts +38 -0
  297. package/src/lib/tool-names.ts +27 -0
  298. package/src/lib/utils.ts +6 -0
  299. package/src/lib/ws-client.ts +109 -0
  300. package/src/stores/chat-store.ts +619 -0
  301. package/src/stores/connection-store.ts +23 -0
  302. package/src/stores/file-store.ts +344 -0
  303. package/src/stores/provider-store.ts +85 -0
  304. package/src/stores/ui-store.ts +48 -0
  305. package/src/styles.css +4 -0
  306. package/src/theme.css +425 -0
  307. package/src/voice/asr-clients.ts +45 -0
  308. package/src/voice/asr-deepgram.ts +325 -0
  309. package/src/voice/asr-webspeech.ts +135 -0
  310. package/src/voice/use-dictation.ts +187 -0
  311. package/src/voice/voice-store.ts +55 -0
@@ -0,0 +1,129 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { uiConfig } from "../../config.js";
3
+ import { useRef, useState } from "react";
4
+ import { ChevronDown, Sparkles, Mic, Image as ImageIcon } from "lucide-react";
5
+ import { ToolCallTimeline } from "./tool-call-timeline.js";
6
+ import { MarkdownContent } from "./markdown-content.js";
7
+ import { linkifyPaths } from "./brain-markdown.js";
8
+ import { AskUserCard } from "./ask-user-card.js";
9
+ import { isAskUserTool } from "../../lib/tool-names.js";
10
+ import { motion } from "framer-motion";
11
+ import { buildMessageShareOptions } from "./message-share.js";
12
+ import { ShareMenu } from "../share/share-menu.js";
13
+ export function MessageBubble({ message, onToolApproval, onAskUserSubmit, onAskUserCancel, }) {
14
+ const isUser = message.role === "user";
15
+ return (_jsxs(motion.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.2, ease: "easeOut" }, className: "py-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-3", children: [_jsx("span", { className: isUser
16
+ ? "select-none font-[family-name:var(--font-mono)] text-xs font-semibold uppercase tracking-widest text-primary/80"
17
+ : "select-none font-[family-name:var(--font-mono)] text-xs font-semibold uppercase tracking-widest text-accent/80", children: isUser ? "You" : uiConfig.assistantName }), _jsx("div", { className: "h-px flex-1 bg-border/40" }), isUser && message.source && message.source !== "typed" && (_jsx(Mic, { className: message.source === "voice-conversation"
18
+ ? "h-3 w-3 text-primary/70"
19
+ : "h-3 w-3 text-primary/50", "aria-label": message.source === "voice-conversation"
20
+ ? "Voice conversation"
21
+ : "Voice dictation" })), _jsx("span", { className: "font-[family-name:var(--font-mono)] text-[10px] text-muted-foreground/40", children: formatTime(message.timestamp) })] }), isUser ? (_jsxs("div", { className: "space-y-2", children: [_jsx(UserAttachments, { message: message }), message.content && (_jsx("div", { className: "text-sm leading-relaxed text-foreground whitespace-pre-wrap", children: linkifyPaths(message.content) }))] })) : (_jsx(AssistantContent, { message: message, onToolApproval: onToolApproval, onAskUserSubmit: onAskUserSubmit, onAskUserCancel: onAskUserCancel }))] }));
22
+ }
23
+ /**
24
+ * User-message image attachments. Live messages render real thumbnails from
25
+ * their local object URLs; resumed-from-history messages carry only a count,
26
+ * so they get a neutral "N image(s)" chip instead.
27
+ */
28
+ function UserAttachments({ message }) {
29
+ if (message.attachments && message.attachments.length > 0) {
30
+ return (_jsx("div", { className: "flex flex-wrap gap-2", children: message.attachments.map((a, i) => (_jsx("img", { src: a.previewUrl, alt: "Attached image", className: "h-20 w-20 rounded-lg border border-border/60 object-cover" }, i))) }));
31
+ }
32
+ if (message.attachmentCount && message.attachmentCount > 0) {
33
+ return (_jsxs("div", { className: "inline-flex items-center gap-1.5 rounded-lg border border-border/40 bg-surface/50 px-2.5 py-1 text-xs text-muted-foreground", children: [_jsx(ImageIcon, { className: "h-3.5 w-3.5" }), message.attachmentCount, " image", message.attachmentCount === 1 ? "" : "s"] }));
34
+ }
35
+ return null;
36
+ }
37
+ function groupParts(parts, toolCalls, askUserExchanges) {
38
+ const groups = [];
39
+ // ask_user tool parts don't join the tool timeline — each maps (in order)
40
+ // to its exchange and renders as its own AskUserCard at that chronological
41
+ // spot, so it collapses and scrolls away like the surrounding events.
42
+ let askUserSeen = 0;
43
+ for (const part of parts) {
44
+ if (part.kind === "tool") {
45
+ const tool = toolCalls[part.toolIndex];
46
+ if (!tool)
47
+ continue;
48
+ if (isAskUserTool(tool.name)) {
49
+ const exchange = askUserExchanges?.[askUserSeen];
50
+ askUserSeen++;
51
+ // The exchange may not have arrived yet mid-stream — skip until it does.
52
+ if (exchange) {
53
+ groups.push({ kind: "askUser", exchange, isLast: false });
54
+ }
55
+ continue;
56
+ }
57
+ const last = groups[groups.length - 1];
58
+ if (last?.kind === "tools") {
59
+ last.toolCalls.push(tool);
60
+ }
61
+ else {
62
+ groups.push({ kind: "tools", toolCalls: [tool], isLast: false });
63
+ }
64
+ }
65
+ else if (part.text.trim()) {
66
+ groups.push({ kind: part.kind, text: part.text, isLast: false, isLastText: false });
67
+ }
68
+ }
69
+ const last = groups[groups.length - 1];
70
+ if (last)
71
+ last.isLast = true;
72
+ for (let i = groups.length - 1; i >= 0; i--) {
73
+ const g = groups[i];
74
+ if (g.kind === "text") {
75
+ g.isLastText = true;
76
+ break;
77
+ }
78
+ }
79
+ return groups;
80
+ }
81
+ function AssistantContent({ message, onToolApproval, onAskUserSubmit, onAskUserCancel, }) {
82
+ const contentRef = useRef(null);
83
+ const showShare = !message.isStreaming && !!message.content && message.content.trim().length > 0;
84
+ const shareOptions = showShare
85
+ ? buildMessageShareOptions({
86
+ content: message.content,
87
+ renderedRef: contentRef,
88
+ })
89
+ : [];
90
+ const groups = groupParts(message.parts, message.toolCalls, message.askUserExchanges);
91
+ // Safety net: each ask_user tool part consumes one exchange (by order) into a
92
+ // chronological group above. Any exchange without a matching tool part — e.g.
93
+ // a stream that missed the tool_use_start — would otherwise vanish, stranding
94
+ // an unanswerable prompt. Render those trailing so a prompt is never lost.
95
+ const askUserSlots = message.parts.reduce((n, p) => p.kind === "tool" && isAskUserTool(message.toolCalls[p.toolIndex]?.name)
96
+ ? n + 1
97
+ : n, 0);
98
+ const unmatchedExchanges = (message.askUserExchanges ?? []).slice(askUserSlots);
99
+ return (_jsxs("div", { className: "space-y-3", children: [groups.map((group, i) => {
100
+ switch (group.kind) {
101
+ case "thinking":
102
+ return (_jsx(ThinkingSection, { content: group.text, isStreaming: message.isStreaming && group.isLast }, i));
103
+ case "tools":
104
+ return (_jsx(ToolCallTimeline, { toolCalls: group.toolCalls, onApproval: onToolApproval, live: message.isStreaming }, i));
105
+ case "askUser":
106
+ return (_jsx(AskUserCard, { requestId: group.exchange.requestId, questions: group.exchange.questions, answered: group.exchange.answers, cancelled: group.exchange.cancelled, live: message.isStreaming, onSubmit: onAskUserSubmit, onCancel: onAskUserCancel }, i));
107
+ case "text":
108
+ // The final text block carries the share affordance; text/markdown
109
+ // share formats still use the full message content.
110
+ return group.isLastText ? (_jsxs("div", { className: "group relative", ref: contentRef, children: [_jsx(MarkdownContent, { content: group.text }), showShare && shareOptions.length > 0 && (_jsx("div", { className: "mt-1 flex justify-end opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100", children: _jsx(ShareMenu, { options: shareOptions, title: "Share message" }) }))] }, i)) : (_jsx(MarkdownContent, { content: group.text }, i));
111
+ }
112
+ }), unmatchedExchanges.map((ex) => (_jsx(AskUserCard, { requestId: ex.requestId, questions: ex.questions, answered: ex.answers, cancelled: ex.cancelled, live: message.isStreaming, onSubmit: onAskUserSubmit, onCancel: onAskUserCancel }, ex.requestId))), message.isStreaming && groups.length === 0 && unmatchedExchanges.length === 0 && (_jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [_jsx("span", { className: "inline-block h-1.5 w-1.5 rounded-full bg-primary", style: { animation: "breathe 2s ease-in-out infinite" } }), "Thinking..."] }))] }));
113
+ }
114
+ function ThinkingSection({ content, isStreaming, }) {
115
+ const [expanded, setExpanded] = useState(isStreaming);
116
+ if (!isStreaming && !expanded) {
117
+ // Collapsed: minimal clickable text
118
+ const estimatedTokens = Math.round(content.length / 4);
119
+ return (_jsxs("button", { onClick: () => setExpanded(true), className: "flex items-center gap-1.5 text-xs text-muted-foreground/50 transition-colors hover:text-muted-foreground", children: [_jsx(Sparkles, { className: "h-3 w-3" }), _jsxs("span", { className: "font-[family-name:var(--font-mono)]", children: ["Thought for ~", estimatedTokens, " tokens"] }), _jsx(ChevronDown, { className: "h-3 w-3" })] }));
120
+ }
121
+ return (_jsxs("div", { className: "rounded-lg bg-surface/50 px-4 py-3", children: [!isStreaming && (_jsx("button", { onClick: () => setExpanded(false), className: "mb-2 text-[11px] font-medium text-muted-foreground/50 transition-colors hover:text-muted-foreground", children: "Hide thinking" })), _jsxs("div", { className: "font-[family-name:var(--font-mono)] text-xs leading-relaxed text-muted-foreground/60 italic whitespace-pre-wrap", children: [linkifyPaths(content), isStreaming && (_jsx("span", { className: "inline-block h-1.5 w-1.5 rounded-full bg-primary ml-1", style: { animation: "breathe 2s ease-in-out infinite" } }))] })] }));
122
+ }
123
+ function formatTime(timestamp) {
124
+ return new Date(timestamp).toLocaleTimeString([], {
125
+ hour: "2-digit",
126
+ minute: "2-digit",
127
+ });
128
+ }
129
+ //# sourceMappingURL=message-bubble.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-bubble.js","sourceRoot":"","sources":["../../../src/components/chat/message-bubble.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { RefObject } from "react";
2
+ import type { ShareOption } from "../share/share-menu.js";
3
+ interface BuildOpts {
4
+ content: string;
5
+ /** Ref to the rendered message DOM. Used to grab outerHTML for rich-text copy. */
6
+ renderedRef: RefObject<HTMLElement | null>;
7
+ }
8
+ export declare function buildMessageShareOptions({ content, renderedRef }: BuildOpts): ShareOption[];
9
+ export {};
10
+ //# sourceMappingURL=message-share.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-share.d.ts","sourceRoot":"","sources":["../../../src/components/chat/message-share.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,WAAW,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC5C;AAED,wBAAgB,wBAAwB,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,SAAS,GAAG,WAAW,EAAE,CAwD3F"}
@@ -0,0 +1,65 @@
1
+ import { uiConfig } from "../../config.js";
2
+ import { renderAndShare, shareFile, shareText, copyRichText } from "../../lib/share.js";
3
+ import { stripMarkdown } from "../../lib/strip-markdown.js";
4
+ export function buildMessageShareOptions({ content, renderedRef }) {
5
+ const plain = () => stripMarkdown(content);
6
+ return [
7
+ {
8
+ id: "image",
9
+ label: "Image (PNG)",
10
+ hint: "Rendered snapshot",
11
+ run: () => renderAndShare({
12
+ content,
13
+ contentType: "markdown",
14
+ format: "png",
15
+ filename: "message",
16
+ title: uiConfig.shareTitle,
17
+ }),
18
+ },
19
+ {
20
+ id: "pdf",
21
+ label: "PDF",
22
+ hint: "Vector PDF, A4",
23
+ run: () => renderAndShare({
24
+ content,
25
+ contentType: "markdown",
26
+ format: "pdf",
27
+ filename: "message",
28
+ title: uiConfig.shareTitle,
29
+ }),
30
+ },
31
+ {
32
+ id: "text",
33
+ label: "Plain text",
34
+ hint: "Markdown stripped",
35
+ run: () => shareText({ text: plain() }),
36
+ },
37
+ {
38
+ id: "markdown",
39
+ label: "Markdown source",
40
+ hint: "Raw markdown verbatim",
41
+ run: () => shareFile(new File([content], "message.md", { type: "text/markdown" }), {
42
+ title: uiConfig.shareTitle,
43
+ }),
44
+ },
45
+ {
46
+ id: "richtext",
47
+ label: "Copy as rich text",
48
+ hint: "Paste into Gmail / Docs",
49
+ run: async () => {
50
+ const node = renderedRef.current;
51
+ const html = node?.innerHTML ?? `<p>${escapeHtml(plain())}</p>`;
52
+ return copyRichText(html, plain());
53
+ },
54
+ },
55
+ ];
56
+ }
57
+ function escapeHtml(s) {
58
+ return s
59
+ .replace(/&/g, "&amp;")
60
+ .replace(/</g, "&lt;")
61
+ .replace(/>/g, "&gt;")
62
+ .replace(/"/g, "&quot;")
63
+ .replace(/'/g, "&#39;");
64
+ }
65
+ //# sourceMappingURL=message-share.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-share.js","sourceRoot":"","sources":["../../../src/components/chat/message-share.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAS5D,MAAM,UAAU,wBAAwB,CAAC,EAAE,OAAO,EAAE,WAAW,EAAa;IAC1E,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAE3C,OAAO;QACL;YACE,EAAE,EAAE,OAAO;YACX,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,mBAAmB;YACzB,GAAG,EAAE,GAAG,EAAE,CACR,cAAc,CAAC;gBACb,OAAO;gBACP,WAAW,EAAE,UAAU;gBACvB,MAAM,EAAE,KAAK;gBACb,QAAQ,EAAE,SAAS;gBACnB,KAAK,EAAE,QAAQ,CAAC,UAAU;aAC3B,CAAC;SACL;QACD;YACE,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,KAAK;YACZ,IAAI,EAAE,gBAAgB;YACtB,GAAG,EAAE,GAAG,EAAE,CACR,cAAc,CAAC;gBACb,OAAO;gBACP,WAAW,EAAE,UAAU;gBACvB,MAAM,EAAE,KAAK;gBACb,QAAQ,EAAE,SAAS;gBACnB,KAAK,EAAE,QAAQ,CAAC,UAAU;aAC3B,CAAC;SACL;QACD;YACE,EAAE,EAAE,MAAM;YACV,KAAK,EAAE,YAAY;YACnB,IAAI,EAAE,mBAAmB;YACzB,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;SACxC;QACD;YACE,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,iBAAiB;YACxB,IAAI,EAAE,uBAAuB;YAC7B,GAAG,EAAE,GAAG,EAAE,CACR,SAAS,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,EAAE;gBACtE,KAAK,EAAE,QAAQ,CAAC,UAAU;aAC3B,CAAC;SACL;QACD;YACE,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,mBAAmB;YAC1B,IAAI,EAAE,yBAAyB;YAC/B,GAAG,EAAE,KAAK,IAAI,EAAE;gBACd,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC;gBACjC,MAAM,IAAI,GAAG,IAAI,EAAE,SAAS,IAAI,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC;gBAChE,OAAO,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACrC,CAAC;SACF;KACF,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { RendererPack } from "@schlessera/brain-ui-sdk/client";
2
+ export declare const claudeToolPack: RendererPack;
3
+ //# sourceMappingURL=claude-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-tools.d.ts","sourceRoot":"","sources":["../../../../src/components/chat/renderers/claude-tools.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,iCAAiC,CAAC;AA4ChG,eAAO,MAAM,cAAc,EAAE,YAG5B,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { GET_LOCATION_TOOL_NAME } from "../../../lib/tool-names.js";
3
+ import { getToolIcon, getToolSummary, getOutputMeta, ToolInputView, ToolOutputView, } from "../tool-views.js";
4
+ /** Claude tool names with dedicated iconography / summaries. */
5
+ const CLAUDE_TOOL_NAMES = [
6
+ "Bash",
7
+ "Edit",
8
+ "Write",
9
+ "Read",
10
+ "Grep",
11
+ "Glob",
12
+ "WebSearch",
13
+ "WebFetch",
14
+ "Agent",
15
+ "Skill",
16
+ "NotebookEdit",
17
+ "LSP",
18
+ GET_LOCATION_TOOL_NAME,
19
+ ];
20
+ // The shared views read brain-ui's richer ToolCall (status, inputJson, timing).
21
+ // The timeline passes the full ToolCall at runtime, so widening the neutral
22
+ // ToolCallView back to ToolCall here is safe.
23
+ const asToolCall = (tool) => tool;
24
+ function claudeRenderer(name) {
25
+ return {
26
+ match: name,
27
+ icon: getToolIcon(name),
28
+ summary: (tool) => getToolSummary(asToolCall(tool)),
29
+ meta: (tool) => getOutputMeta(asToolCall(tool)),
30
+ Input: ({ tool }) => _jsx(ToolInputView, { tool: asToolCall(tool) }),
31
+ Output: ({ tool }) => _jsx(ToolOutputView, { tool: asToolCall(tool) }),
32
+ };
33
+ }
34
+ export const claudeToolPack = {
35
+ backend: "claude",
36
+ renderers: CLAUDE_TOOL_NAMES.map(claudeRenderer),
37
+ };
38
+ //# sourceMappingURL=claude-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-tools.js","sourceRoot":"","sources":["../../../../src/components/chat/renderers/claude-tools.tsx"],"names":[],"mappings":";AAOA,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EACL,WAAW,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,gEAAgE;AAChE,MAAM,iBAAiB,GAAG;IACxB,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,WAAW;IACX,UAAU;IACV,OAAO;IACP,OAAO;IACP,cAAc;IACd,KAAK;IACL,sBAAsB;CACvB,CAAC;AAEF,gFAAgF;AAChF,4EAA4E;AAC5E,8CAA8C;AAC9C,MAAM,UAAU,GAAG,CAAC,IAAkB,EAAE,EAAE,CAAC,IAA2B,CAAC;AAEvE,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO;QACL,KAAK,EAAE,IAAI;QACX,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;QACvB,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/C,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,KAAC,aAAa,IAAC,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,GAAI;QAC9D,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,KAAC,cAAc,IAAC,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,GAAI;KACjE,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,OAAO,EAAE,QAAQ;IACjB,SAAS,EAAE,iBAAiB,CAAC,GAAG,CAAC,cAAc,CAAC;CACjD,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { RendererPack, ToolRenderer } from "@schlessera/brain-ui-sdk/client";
2
+ /**
3
+ * The catch-all renderer. Registered as a low-scoring predicate so it applies
4
+ * to any tool no more specific renderer claimed, and exported for the timeline
5
+ * to use as an explicit fallback.
6
+ */
7
+ export declare const GENERIC_RENDERER: ToolRenderer;
8
+ export declare const genericToolPack: RendererPack;
9
+ //# sourceMappingURL=generic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generic.d.ts","sourceRoot":"","sources":["../../../../src/components/chat/renderers/generic.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAgB,MAAM,iCAAiC,CAAC;AAgEhG;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAM9B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,YAE7B,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { FileText } from "lucide-react";
3
+ import { getOutputMeta, EditDiffView, WriteFileView, BashCommandView, KeyValueView, ClampedPre, FileRowsView, splitGrepRow, } from "../tool-views.js";
4
+ const asToolCall = (tool) => tool;
5
+ function str(v) {
6
+ return typeof v === "string" && v.length > 0;
7
+ }
8
+ /** Input rendered by input SHAPE: diff, written file, terminal command, else key/value. */
9
+ function GenericInput({ tool }) {
10
+ const t = asToolCall(tool);
11
+ const input = t.input ?? {};
12
+ if (str(input.old_string) && str(input.new_string)) {
13
+ return _jsx(EditDiffView, { tool: t });
14
+ }
15
+ if ((str(input.file_path) || str(input.path)) && str(input.content)) {
16
+ // WriteFileView reads `file_path`; alias `path` so `{path, content}` tools render too.
17
+ const aliased = str(input.file_path)
18
+ ? t
19
+ : { ...t, input: { ...input, file_path: input.path } };
20
+ return _jsx(WriteFileView, { tool: aliased });
21
+ }
22
+ if (str(input.command) || str(input.cmd)) {
23
+ const aliased = str(input.command)
24
+ ? t
25
+ : { ...t, input: { ...input, command: input.cmd } };
26
+ return _jsx(BashCommandView, { tool: aliased });
27
+ }
28
+ return _jsx(KeyValueView, { tool: t });
29
+ }
30
+ /** Output rendered by output SHAPE: error, `path:line:` rows, else clamped text. */
31
+ function GenericOutput({ tool }) {
32
+ const t = asToolCall(tool);
33
+ if (!t.output)
34
+ return null;
35
+ if (t.isError)
36
+ return _jsx(ClampedPre, { text: t.output, isError: true });
37
+ if (looksLikeFileRows(t.output))
38
+ return _jsx(FileRowsView, { output: t.output });
39
+ return _jsx(ClampedPre, { text: t.output });
40
+ }
41
+ /** True when most non-empty lines look like ripgrep-style `path:line:content`. */
42
+ function looksLikeFileRows(output) {
43
+ const lines = output
44
+ .split("\n")
45
+ .map((l) => l.trim())
46
+ .filter(Boolean)
47
+ .slice(0, 20);
48
+ if (lines.length === 0)
49
+ return false;
50
+ const matches = lines.filter((l) => splitGrepRow(l) !== null).length;
51
+ return matches >= Math.ceil(lines.length / 2);
52
+ }
53
+ /**
54
+ * The catch-all renderer. Registered as a low-scoring predicate so it applies
55
+ * to any tool no more specific renderer claimed, and exported for the timeline
56
+ * to use as an explicit fallback.
57
+ */
58
+ export const GENERIC_RENDERER = {
59
+ match: () => 0.1,
60
+ icon: FileText,
61
+ meta: (tool) => getOutputMeta(asToolCall(tool)),
62
+ Input: GenericInput,
63
+ Output: GenericOutput,
64
+ };
65
+ export const genericToolPack = {
66
+ renderers: [GENERIC_RENDERER],
67
+ };
68
+ //# sourceMappingURL=generic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generic.js","sourceRoot":"","sources":["../../../../src/components/chat/renderers/generic.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { GENERIC_RENDERER } from "./generic.js";
2
+ /** Register the built-in packs once (idempotent). */
3
+ export declare function registerBuiltinRenderers(): void;
4
+ export { GENERIC_RENDERER };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/chat/renderers/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIjE,qDAAqD;AACrD,wBAAgB,wBAAwB,IAAI,IAAI,CAK/C;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,17 @@
1
+ // Build-time registration of tool-renderer packs. A contributor adds one
2
+ // import line + registerToolRenderers call here (React.lazy for heavy packs).
3
+ // Runtime plugin loading into the compiled PWA is deliberately not supported.
4
+ import { registerToolRenderers } from "@schlessera/brain-ui-sdk/client";
5
+ import { claudeToolPack } from "./claude-tools.js";
6
+ import { genericToolPack, GENERIC_RENDERER } from "./generic.js";
7
+ let registered = false;
8
+ /** Register the built-in packs once (idempotent). */
9
+ export function registerBuiltinRenderers() {
10
+ if (registered)
11
+ return;
12
+ registered = true;
13
+ registerToolRenderers(claudeToolPack);
14
+ registerToolRenderers(genericToolPack);
15
+ }
16
+ export { GENERIC_RENDERER };
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/chat/renderers/index.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,8EAA8E;AAC9E,8EAA8E;AAE9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEjE,IAAI,UAAU,GAAG,KAAK,CAAC;AAEvB,qDAAqD;AACrD,MAAM,UAAU,wBAAwB;IACtC,IAAI,UAAU;QAAE,OAAO;IACvB,UAAU,GAAG,IAAI,CAAC;IAClB,qBAAqB,CAAC,cAAc,CAAC,CAAC;IACtC,qBAAqB,CAAC,eAAe,CAAC,CAAC;AACzC,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { ToolCall } from "../../stores/chat-store.js";
2
+ /**
3
+ * Does this path resolve inside the brain repo?
4
+ *
5
+ * Deliberately STRICTER than tool-views.tsx's `toRepoRelative`: this drives a
6
+ * *risk advisory*, so it must err toward warning — a bare `/brain/` substring
7
+ * match would wrongly clear `/tmp/brain/secrets`. Inside means either
8
+ * repo-relative (not absolute, no `..` escape) or anchored at a known brain
9
+ * root: the container path `/data/brain/` or a home checkout `/home/<user>/brain/`
10
+ * (`~/brain`).
11
+ */
12
+ export declare function isInsideBrainRepo(path: string): boolean;
13
+ export type RiskRule = {
14
+ test: (tool: ToolCall) => boolean;
15
+ message: string;
16
+ };
17
+ export declare const RISK_RULES: RiskRule[];
18
+ /** Advisory messages for every risk rule that fires. Never throws. */
19
+ export declare function riskHints(tool: ToolCall): string[];
20
+ //# sourceMappingURL=risk-hints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-hints.d.ts","sourceRoot":"","sources":["../../../src/components/chat/risk-hints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAU3D;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOvD;AAMD,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,QAAQ,EA2ChC,CAAC;AAEF,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,CAUlD"}
@@ -0,0 +1,81 @@
1
+ // ============================================================
2
+ // Approval-time risk hints
3
+ // ============================================================
4
+ //
5
+ // Non-blocking advisories surfaced in the pending-approval detail so a human
6
+ // notices known-risky shapes before hitting Allow. Detection only — the human
7
+ // still decides. Each rule is { test, message } so the list is easy to extend.
8
+ /**
9
+ * Does this path resolve inside the brain repo?
10
+ *
11
+ * Deliberately STRICTER than tool-views.tsx's `toRepoRelative`: this drives a
12
+ * *risk advisory*, so it must err toward warning — a bare `/brain/` substring
13
+ * match would wrongly clear `/tmp/brain/secrets`. Inside means either
14
+ * repo-relative (not absolute, no `..` escape) or anchored at a known brain
15
+ * root: the container path `/data/brain/` or a home checkout `/home/<user>/brain/`
16
+ * (`~/brain`).
17
+ */
18
+ export function isInsideBrainRepo(path) {
19
+ if (/^\/data\/brain\//.test(path) || /^\/home\/[^/]+\/brain\//.test(path)) {
20
+ return true;
21
+ }
22
+ // Repo-relative: not absolute and not escaping upward.
23
+ if (!path.startsWith("/") && !path.split("/").includes(".."))
24
+ return true;
25
+ return false;
26
+ }
27
+ function asString(v) {
28
+ return typeof v === "string" ? v : "";
29
+ }
30
+ export const RISK_RULES = [
31
+ {
32
+ // Note: the Bash input view already badges this red, so this line is
33
+ // secondary. Kept for consistency so the advisory row reflects every known
34
+ // risk in one place rather than the human having to cross-reference badges.
35
+ test: (t) => t.name === "Bash" && Boolean(t.input?.dangerouslyDisableSandbox),
36
+ message: "runs without the sandbox",
37
+ },
38
+ {
39
+ // Recursive + force in either flag order (-rf, -fr) or as separate flags
40
+ // (rm -r -f). Lookaheads match a `-…r` flag and a `-…f` flag in any order.
41
+ test: (t) => t.name === "Bash" &&
42
+ /\brm\b(?=[^\n]*\s-[a-zA-Z]*r)(?=[^\n]*\s-[a-zA-Z]*f)/.test(asString(t.input?.command)),
43
+ message: "removes files recursively (rm -rf)",
44
+ },
45
+ {
46
+ test: (t) => t.name === "Bash" &&
47
+ /git\s+push\b[^\n]*(?:--force\b|--force-with-lease\b|\s-f\b)/.test(asString(t.input?.command)),
48
+ message: "force-pushes a git branch",
49
+ },
50
+ {
51
+ test: (t) => t.name === "Bash" &&
52
+ /\b(?:curl|wget)\b[^|]*\|\s*(?:ba)?sh\b/.test(asString(t.input?.command)),
53
+ message: "pipes a remote script into a shell",
54
+ },
55
+ {
56
+ test: (t) => {
57
+ if (t.name !== "Write" && t.name !== "Edit" && t.name !== "NotebookEdit")
58
+ return false;
59
+ const path = asString(t.input?.file_path) || asString(t.input?.notebook_path);
60
+ if (!path)
61
+ return false;
62
+ return !isInsideBrainRepo(path);
63
+ },
64
+ message: "writes outside the brain repo",
65
+ },
66
+ ];
67
+ /** Advisory messages for every risk rule that fires. Never throws. */
68
+ export function riskHints(tool) {
69
+ const hints = [];
70
+ for (const rule of RISK_RULES) {
71
+ try {
72
+ if (rule.test(tool))
73
+ hints.push(rule.message);
74
+ }
75
+ catch {
76
+ // Defensive: never let a malformed input break the approval UI.
77
+ }
78
+ }
79
+ return hints;
80
+ }
81
+ //# sourceMappingURL=risk-hints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-hints.js","sourceRoot":"","sources":["../../../src/components/chat/risk-hints.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function SessionDrawer({ open, onClose, onResume, }: {
2
+ open: boolean;
3
+ onClose: () => void;
4
+ onResume: (sessionId: string) => void;
5
+ }): import("react").JSX.Element;
6
+ //# sourceMappingURL=session-drawer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-drawer.d.ts","sourceRoot":"","sources":["../../../src/components/chat/session-drawer.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,OAAO,EACP,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC,+BA4HA"}