@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.
- package/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/components/chat/ask-user-card.d.ts +12 -0
- package/dist/components/chat/ask-user-card.d.ts.map +1 -0
- package/dist/components/chat/ask-user-card.js +185 -0
- package/dist/components/chat/ask-user-card.js.map +1 -0
- package/dist/components/chat/brain-markdown.d.ts +35 -0
- package/dist/components/chat/brain-markdown.d.ts.map +1 -0
- package/dist/components/chat/brain-markdown.js +363 -0
- package/dist/components/chat/brain-markdown.js.map +1 -0
- package/dist/components/chat/chat-page.d.ts +2 -0
- package/dist/components/chat/chat-page.d.ts.map +1 -0
- package/dist/components/chat/chat-page.js +442 -0
- package/dist/components/chat/chat-page.js.map +1 -0
- package/dist/components/chat/command-palette.d.ts +5 -0
- package/dist/components/chat/command-palette.d.ts.map +1 -0
- package/dist/components/chat/command-palette.js +18 -0
- package/dist/components/chat/command-palette.js.map +1 -0
- package/dist/components/chat/markdown-content.d.ts +4 -0
- package/dist/components/chat/markdown-content.d.ts.map +1 -0
- package/dist/components/chat/markdown-content.js +6 -0
- package/dist/components/chat/markdown-content.js.map +1 -0
- package/dist/components/chat/message-bubble.d.ts +9 -0
- package/dist/components/chat/message-bubble.d.ts.map +1 -0
- package/dist/components/chat/message-bubble.js +129 -0
- package/dist/components/chat/message-bubble.js.map +1 -0
- package/dist/components/chat/message-share.d.ts +10 -0
- package/dist/components/chat/message-share.d.ts.map +1 -0
- package/dist/components/chat/message-share.js +65 -0
- package/dist/components/chat/message-share.js.map +1 -0
- package/dist/components/chat/renderers/claude-tools.d.ts +3 -0
- package/dist/components/chat/renderers/claude-tools.d.ts.map +1 -0
- package/dist/components/chat/renderers/claude-tools.js +38 -0
- package/dist/components/chat/renderers/claude-tools.js.map +1 -0
- package/dist/components/chat/renderers/generic.d.ts +9 -0
- package/dist/components/chat/renderers/generic.d.ts.map +1 -0
- package/dist/components/chat/renderers/generic.js +68 -0
- package/dist/components/chat/renderers/generic.js.map +1 -0
- package/dist/components/chat/renderers/index.d.ts +5 -0
- package/dist/components/chat/renderers/index.d.ts.map +1 -0
- package/dist/components/chat/renderers/index.js +17 -0
- package/dist/components/chat/renderers/index.js.map +1 -0
- package/dist/components/chat/risk-hints.d.ts +20 -0
- package/dist/components/chat/risk-hints.d.ts.map +1 -0
- package/dist/components/chat/risk-hints.js +81 -0
- package/dist/components/chat/risk-hints.js.map +1 -0
- package/dist/components/chat/session-drawer.d.ts +6 -0
- package/dist/components/chat/session-drawer.d.ts.map +1 -0
- package/dist/components/chat/session-drawer.js +96 -0
- package/dist/components/chat/session-drawer.js.map +1 -0
- package/dist/components/chat/share-block.d.ts +9 -0
- package/dist/components/chat/share-block.d.ts.map +1 -0
- package/dist/components/chat/share-block.js +70 -0
- package/dist/components/chat/share-block.js.map +1 -0
- package/dist/components/chat/tool-call-timeline.d.ts +8 -0
- package/dist/components/chat/tool-call-timeline.d.ts.map +1 -0
- package/dist/components/chat/tool-call-timeline.js +100 -0
- package/dist/components/chat/tool-call-timeline.js.map +1 -0
- package/dist/components/chat/tool-views.d.ts +116 -0
- package/dist/components/chat/tool-views.d.ts.map +1 -0
- package/dist/components/chat/tool-views.js +628 -0
- package/dist/components/chat/tool-views.js.map +1 -0
- package/dist/components/chat/welcome-state.d.ts +4 -0
- package/dist/components/chat/welcome-state.d.ts.map +1 -0
- package/dist/components/chat/welcome-state.js +10 -0
- package/dist/components/chat/welcome-state.js.map +1 -0
- package/dist/components/connectivity/connection-gate.d.ts +5 -0
- package/dist/components/connectivity/connection-gate.d.ts.map +1 -0
- package/dist/components/connectivity/connection-gate.js +42 -0
- package/dist/components/connectivity/connection-gate.js.map +1 -0
- package/dist/components/connectivity/login-screen.d.ts +9 -0
- package/dist/components/connectivity/login-screen.d.ts.map +1 -0
- package/dist/components/connectivity/login-screen.js +99 -0
- package/dist/components/connectivity/login-screen.js.map +1 -0
- package/dist/components/files/file-panel.d.ts +5 -0
- package/dist/components/files/file-panel.d.ts.map +1 -0
- package/dist/components/files/file-panel.js +46 -0
- package/dist/components/files/file-panel.js.map +1 -0
- package/dist/components/files/file-tree.d.ts +2 -0
- package/dist/components/files/file-tree.d.ts.map +1 -0
- package/dist/components/files/file-tree.js +63 -0
- package/dist/components/files/file-tree.js.map +1 -0
- package/dist/components/files/file-viewer-binary.d.ts +5 -0
- package/dist/components/files/file-viewer-binary.d.ts.map +1 -0
- package/dist/components/files/file-viewer-binary.js +20 -0
- package/dist/components/files/file-viewer-binary.js.map +1 -0
- package/dist/components/files/file-viewer-html.d.ts +4 -0
- package/dist/components/files/file-viewer-html.d.ts.map +1 -0
- package/dist/components/files/file-viewer-html.js +5 -0
- package/dist/components/files/file-viewer-html.js.map +1 -0
- package/dist/components/files/file-viewer-markdown.d.ts +4 -0
- package/dist/components/files/file-viewer-markdown.d.ts.map +1 -0
- package/dist/components/files/file-viewer-markdown.js +10 -0
- package/dist/components/files/file-viewer-markdown.js.map +1 -0
- package/dist/components/files/file-viewer-raw.d.ts +5 -0
- package/dist/components/files/file-viewer-raw.d.ts.map +1 -0
- package/dist/components/files/file-viewer-raw.js +15 -0
- package/dist/components/files/file-viewer-raw.js.map +1 -0
- package/dist/components/files/file-viewer.d.ts +2 -0
- package/dist/components/files/file-viewer.d.ts.map +1 -0
- package/dist/components/files/file-viewer.js +175 -0
- package/dist/components/files/file-viewer.js.map +1 -0
- package/dist/components/files/frontmatter-panel.d.ts +5 -0
- package/dist/components/files/frontmatter-panel.d.ts.map +1 -0
- package/dist/components/files/frontmatter-panel.js +12 -0
- package/dist/components/files/frontmatter-panel.js.map +1 -0
- package/dist/components/layout/app-shell.d.ts +5 -0
- package/dist/components/layout/app-shell.d.ts.map +1 -0
- package/dist/components/layout/app-shell.js +10 -0
- package/dist/components/layout/app-shell.js.map +1 -0
- package/dist/components/layout/mobile-tab-bar.d.ts +2 -0
- package/dist/components/layout/mobile-tab-bar.d.ts.map +1 -0
- package/dist/components/layout/mobile-tab-bar.js +16 -0
- package/dist/components/layout/mobile-tab-bar.js.map +1 -0
- package/dist/components/layout/side-rail.d.ts +2 -0
- package/dist/components/layout/side-rail.d.ts.map +1 -0
- package/dist/components/layout/side-rail.js +26 -0
- package/dist/components/layout/side-rail.js.map +1 -0
- package/dist/components/layout/slide-panel.d.ts +9 -0
- package/dist/components/layout/slide-panel.d.ts.map +1 -0
- package/dist/components/layout/slide-panel.js +18 -0
- package/dist/components/layout/slide-panel.js.map +1 -0
- package/dist/components/quick-actions/streaming-modal.d.ts +8 -0
- package/dist/components/quick-actions/streaming-modal.d.ts.map +1 -0
- package/dist/components/quick-actions/streaming-modal.js +92 -0
- package/dist/components/quick-actions/streaming-modal.js.map +1 -0
- package/dist/components/quick-actions/whatsup-modal.d.ts +5 -0
- package/dist/components/quick-actions/whatsup-modal.d.ts.map +1 -0
- package/dist/components/quick-actions/whatsup-modal.js +82 -0
- package/dist/components/quick-actions/whatsup-modal.js.map +1 -0
- package/dist/components/settings/passkey-panel.d.ts +10 -0
- package/dist/components/settings/passkey-panel.d.ts.map +1 -0
- package/dist/components/settings/passkey-panel.js +118 -0
- package/dist/components/settings/passkey-panel.js.map +1 -0
- package/dist/components/share/share-menu.d.ts +23 -0
- package/dist/components/share/share-menu.d.ts.map +1 -0
- package/dist/components/share/share-menu.js +58 -0
- package/dist/components/share/share-menu.js.map +1 -0
- package/dist/components/voice/dictation-sheet.d.ts +6 -0
- package/dist/components/voice/dictation-sheet.d.ts.map +1 -0
- package/dist/components/voice/dictation-sheet.js +91 -0
- package/dist/components/voice/dictation-sheet.js.map +1 -0
- package/dist/components/voice/mic-button.d.ts +6 -0
- package/dist/components/voice/mic-button.d.ts.map +1 -0
- package/dist/components/voice/mic-button.js +9 -0
- package/dist/components/voice/mic-button.js.map +1 -0
- package/dist/components/voice/review-card.d.ts +8 -0
- package/dist/components/voice/review-card.d.ts.map +1 -0
- package/dist/components/voice/review-card.js +8 -0
- package/dist/components/voice/review-card.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +10 -0
- package/dist/config.js.map +1 -0
- package/dist/hooks/use-vpn-status.d.ts +2 -0
- package/dist/hooks/use-vpn-status.d.ts.map +1 -0
- package/dist/hooks/use-vpn-status.js +71 -0
- package/dist/hooks/use-vpn-status.js.map +1 -0
- package/dist/hooks/use-websocket.d.ts +8 -0
- package/dist/hooks/use-websocket.d.ts.map +1 -0
- package/dist/hooks/use-websocket.js +366 -0
- package/dist/hooks/use-websocket.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-client.d.ts +115 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +70 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/backend.d.ts +7 -0
- package/dist/lib/backend.d.ts.map +1 -0
- package/dist/lib/backend.js +33 -0
- package/dist/lib/backend.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +21 -0
- package/dist/lib/frontmatter.d.ts.map +1 -0
- package/dist/lib/frontmatter.js +88 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/image-attachments.d.ts +36 -0
- package/dist/lib/image-attachments.d.ts.map +1 -0
- package/dist/lib/image-attachments.js +175 -0
- package/dist/lib/image-attachments.js.map +1 -0
- package/dist/lib/passkeys.d.ts +18 -0
- package/dist/lib/passkeys.d.ts.map +1 -0
- package/dist/lib/passkeys.js +36 -0
- package/dist/lib/passkeys.js.map +1 -0
- package/dist/lib/share.d.ts +53 -0
- package/dist/lib/share.d.ts.map +1 -0
- package/dist/lib/share.js +144 -0
- package/dist/lib/share.js.map +1 -0
- package/dist/lib/strip-markdown.d.ts +10 -0
- package/dist/lib/strip-markdown.d.ts.map +1 -0
- package/dist/lib/strip-markdown.js +37 -0
- package/dist/lib/strip-markdown.js.map +1 -0
- package/dist/lib/tool-names.d.ts +17 -0
- package/dist/lib/tool-names.d.ts.map +1 -0
- package/dist/lib/tool-names.js +23 -0
- package/dist/lib/tool-names.js.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +6 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/ws-client.d.ts +26 -0
- package/dist/lib/ws-client.d.ts.map +1 -0
- package/dist/lib/ws-client.js +93 -0
- package/dist/lib/ws-client.js.map +1 -0
- package/dist/stores/chat-store.d.ts +131 -0
- package/dist/stores/chat-store.d.ts.map +1 -0
- package/dist/stores/chat-store.js +376 -0
- package/dist/stores/chat-store.js.map +1 -0
- package/dist/stores/connection-store.d.ts +11 -0
- package/dist/stores/connection-store.d.ts.map +1 -0
- package/dist/stores/connection-store.js +8 -0
- package/dist/stores/connection-store.js.map +1 -0
- package/dist/stores/file-store.d.ts +53 -0
- package/dist/stores/file-store.d.ts.map +1 -0
- package/dist/stores/file-store.js +284 -0
- package/dist/stores/file-store.js.map +1 -0
- package/dist/stores/provider-store.d.ts +24 -0
- package/dist/stores/provider-store.d.ts.map +1 -0
- package/dist/stores/provider-store.js +56 -0
- package/dist/stores/provider-store.js.map +1 -0
- package/dist/stores/ui-store.d.ts +21 -0
- package/dist/stores/ui-store.d.ts.map +1 -0
- package/dist/stores/ui-store.js +23 -0
- package/dist/stores/ui-store.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/voice/asr-clients.d.ts +2 -0
- package/dist/voice/asr-clients.d.ts.map +1 -0
- package/dist/voice/asr-clients.js +33 -0
- package/dist/voice/asr-clients.js.map +1 -0
- package/dist/voice/asr-deepgram.d.ts +38 -0
- package/dist/voice/asr-deepgram.d.ts.map +1 -0
- package/dist/voice/asr-deepgram.js +307 -0
- package/dist/voice/asr-deepgram.js.map +1 -0
- package/dist/voice/asr-webspeech.d.ts +23 -0
- package/dist/voice/asr-webspeech.d.ts.map +1 -0
- package/dist/voice/asr-webspeech.js +93 -0
- package/dist/voice/asr-webspeech.js.map +1 -0
- package/dist/voice/use-dictation.d.ts +6 -0
- package/dist/voice/use-dictation.d.ts.map +1 -0
- package/dist/voice/use-dictation.js +174 -0
- package/dist/voice/use-dictation.js.map +1 -0
- package/dist/voice/voice-store.d.ts +26 -0
- package/dist/voice/voice-store.d.ts.map +1 -0
- package/dist/voice/voice-store.js +27 -0
- package/dist/voice/voice-store.js.map +1 -0
- package/package.json +71 -0
- package/src/components/chat/ask-user-card.tsx +451 -0
- package/src/components/chat/brain-markdown.tsx +547 -0
- package/src/components/chat/chat-page.tsx +820 -0
- package/src/components/chat/command-palette.tsx +68 -0
- package/src/components/chat/markdown-content.tsx +5 -0
- package/src/components/chat/message-bubble.tsx +362 -0
- package/src/components/chat/message-share.ts +78 -0
- package/src/components/chat/renderers/claude-tools.tsx +53 -0
- package/src/components/chat/renderers/generic.tsx +85 -0
- package/src/components/chat/renderers/index.ts +19 -0
- package/src/components/chat/risk-hints.ts +95 -0
- package/src/components/chat/session-drawer.tsx +213 -0
- package/src/components/chat/share-block.tsx +128 -0
- package/src/components/chat/tool-call-timeline.tsx +322 -0
- package/src/components/chat/tool-views.tsx +898 -0
- package/src/components/chat/welcome-state.tsx +105 -0
- package/src/components/connectivity/connection-gate.tsx +137 -0
- package/src/components/connectivity/login-screen.tsx +169 -0
- package/src/components/files/file-panel.tsx +116 -0
- package/src/components/files/file-tree.tsx +143 -0
- package/src/components/files/file-viewer-binary.tsx +63 -0
- package/src/components/files/file-viewer-html.tsx +10 -0
- package/src/components/files/file-viewer-markdown.tsx +14 -0
- package/src/components/files/file-viewer-raw.tsx +44 -0
- package/src/components/files/file-viewer.tsx +290 -0
- package/src/components/files/frontmatter-panel.tsx +78 -0
- package/src/components/layout/app-shell.tsx +25 -0
- package/src/components/layout/mobile-tab-bar.tsx +54 -0
- package/src/components/layout/side-rail.tsx +128 -0
- package/src/components/layout/slide-panel.tsx +64 -0
- package/src/components/quick-actions/streaming-modal.tsx +168 -0
- package/src/components/quick-actions/whatsup-modal.tsx +149 -0
- package/src/components/settings/passkey-panel.tsx +281 -0
- package/src/components/share/share-menu.tsx +121 -0
- package/src/components/voice/dictation-sheet.tsx +217 -0
- package/src/components/voice/mic-button.tsx +31 -0
- package/src/components/voice/review-card.tsx +66 -0
- package/src/config.ts +27 -0
- package/src/hooks/use-vpn-status.ts +75 -0
- package/src/hooks/use-websocket.ts +403 -0
- package/src/index.ts +46 -0
- package/src/lib/api-client.ts +173 -0
- package/src/lib/backend.ts +35 -0
- package/src/lib/frontmatter.ts +99 -0
- package/src/lib/image-attachments.ts +221 -0
- package/src/lib/passkeys.ts +50 -0
- package/src/lib/share.ts +167 -0
- package/src/lib/strip-markdown.ts +38 -0
- package/src/lib/tool-names.ts +27 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/ws-client.ts +109 -0
- package/src/stores/chat-store.ts +619 -0
- package/src/stores/connection-store.ts +23 -0
- package/src/stores/file-store.ts +344 -0
- package/src/stores/provider-store.ts +85 -0
- package/src/stores/ui-store.ts +48 -0
- package/src/styles.css +4 -0
- package/src/theme.css +425 -0
- package/src/voice/asr-clients.ts +45 -0
- package/src/voice/asr-deepgram.ts +325 -0
- package/src/voice/asr-webspeech.ts +135 -0
- package/src/voice/use-dictation.ts +187 -0
- 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, "&")
|
|
60
|
+
.replace(/</g, "<")
|
|
61
|
+
.replace(/>/g, ">")
|
|
62
|
+
.replace(/"/g, """)
|
|
63
|
+
.replace(/'/g, "'");
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|