@blade-hq/agent-react 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -0
- package/dist/components/AgentLoopBlock.d.ts +10 -0
- package/dist/components/AskUserQuestionBlock.d.ts +31 -0
- package/dist/components/AssistantTurnBlock.d.ts +16 -0
- package/dist/components/ChatInput.d.ts +18 -0
- package/dist/components/ChatView.d.ts +41 -0
- package/dist/components/ConnectionBanner.d.ts +8 -0
- package/dist/components/MarkdownContent.d.ts +16 -0
- package/dist/components/MessageList.d.ts +19 -0
- package/dist/components/RenderErrorBoundary.d.ts +20 -0
- package/dist/components/Shimmer.d.ts +7 -0
- package/dist/components/ToolCallBlock.d.ts +18 -0
- package/dist/components/UserMessageBubble.d.ts +21 -0
- package/dist/components/display-utils.d.ts +16 -0
- package/dist/context.d.ts +13 -0
- package/dist/embed/blade-chat-element.d.ts +33 -0
- package/dist/embed/entry.d.ts +94 -0
- package/dist/hooks/use-agent-session.d.ts +29 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1527 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/style.css +391 -0
- package/package.json +37 -0
- package/public-api.md +1206 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context.tsx","../src/hooks/use-agent-session.ts","../src/components/ChatView.tsx","../src/lib/utils.ts","../src/components/ChatInput.tsx","../src/components/ConnectionBanner.tsx","../src/components/MessageList.tsx","../src/components/AssistantTurnBlock.tsx","../src/components/AgentLoopBlock.tsx","../src/components/display-utils.ts","../src/components/MarkdownContent.tsx","../src/components/Shimmer.tsx","../src/components/ToolCallBlock.tsx","../src/components/AskUserQuestionBlock.tsx","../src/components/RenderErrorBoundary.tsx","../src/components/UserMessageBubble.tsx","../src/index.ts"],"sourcesContent":["import type { BladeClient } from \"@blade-hq/agent-client\"\nimport { createContext, useContext, type ReactNode } from \"react\"\n\nconst BladeClientContext = createContext<BladeClient | null>(null)\n\nexport interface BladeProviderProps {\n client: BladeClient\n children: ReactNode\n}\n\n/**\n * 提供 client 级共享(一条连接、一份鉴权)。不含任何全局状态:\n * 同一 Provider 下多个 ChatView / useAgentSession 各自独立。\n */\nexport function BladeProvider({ client, children }: BladeProviderProps) {\n return <BladeClientContext.Provider value={client}>{children}</BladeClientContext.Provider>\n}\n\n/** 读取当前 BladeProvider 提供的 client。不在 Provider 内使用时抛错。 */\nexport function useBladeClient(): BladeClient {\n const client = useContext(BladeClientContext)\n if (!client) {\n throw new Error(\"useBladeClient 必须在 <BladeProvider> 内使用\")\n }\n return client\n}\n","import type {\n AgentSession,\n CreateSessionRequest,\n SessionState,\n} from \"@blade-hq/agent-client\"\nimport { useEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\"\nimport { useBladeClient } from \"../context\"\n\nexport interface UseAgentSessionOptions {\n /** 不传 sessionId 时按此配置新建会话;两者都不传则新建默认对话。 */\n createOptions?: CreateSessionRequest\n /** 自动创建出的 sessionId 通过此回调交还调用方持有(重挂载复用的依据)。 */\n onSessionCreated?: (sessionId: string) => void\n}\n\nexport interface UseAgentSessionResult {\n /** 连接/创建完成前为 null。 */\n session: AgentSession | null\n /** 会话状态快照;session 未就绪时为 null。 */\n state: SessionState | null\n /** 连接或创建失败的错误(含 VersionMismatchError)。 */\n error: Error | null\n}\n\nconst EMPTY_SUBSCRIBE = () => () => {}\n\n/** 卸载后延迟释放引用:StrictMode/路由抖动的立即重挂载会在窗口内重新\n * connect(引用计数 +1),延迟 dispose 只把计数减回去,实例得以复用;\n * 真正的卸载则在窗口结束后正常释放订阅。 */\nconst DISPOSE_DELAY_MS = 1000\n\n/**\n * 连接(或创建)一个会话并订阅其状态。\n *\n * - 优先级:sessionId(连接既有会话)> createOptions(按配置新建)> 默认新建。\n * - 同一 sessionId 重复调用复用同一 AgentSession 实例(SessionHub 引用计数)。\n * - 自动创建只发生一次(含 StrictMode 双跑);创建出的 id 经 onSessionCreated\n * 交还调用方。\n *\n * **连接与 sessionId 变更分离**:连接在挂载时建立、仅在卸载时释放;只有\n * 当 sessionId 明确切到一个「与当前已连接会话不同」的 id 时才重连。这样宿主\n * (如 Vue 把自动创建的 id 写回 `session-id` 属性)回填同一个 id 不会触发\n * 拆–重连——否则会把正在流式的会话换成新实例、当前渲染整个丢失。\n */\nexport function useAgentSession(\n sessionId?: string,\n options: UseAgentSessionOptions = {},\n): UseAgentSessionResult {\n const client = useBladeClient()\n const [session, setSession] = useState<AgentSession | null>(null)\n const [error, setError] = useState<Error | null>(null)\n\n // 当前连接:id + 实例 + 一个递增的「代」号,用于让过期的异步连接结果作废。\n const connRef = useRef<{ id: string | null; session: AgentSession | null; gen: number }>({\n id: null,\n session: null,\n gen: 0,\n })\n // 自动创建去重:StrictMode 双跑时同一组件实例只创建一次会话。\n const createdIdPromiseRef = useRef<Promise<string> | null>(null)\n const onCreatedRef = useRef(options.onSessionCreated)\n onCreatedRef.current = options.onSessionCreated\n const createOptionsRef = useRef(options.createOptions)\n createOptionsRef.current = options.createOptions\n // 最新的目标 sessionId,供挂载 effect 的异步链读取(不进依赖,避免重连)。\n const sessionIdRef = useRef(sessionId)\n sessionIdRef.current = sessionId\n\n // 把「连接到某个 id / 自动创建」封成一次操作;gen 作废过期结果。\n const connect = useMemo(() => {\n return (targetId: string | undefined) => {\n const gen = ++connRef.current.gen\n const idPromise = targetId\n ? Promise.resolve(targetId)\n : // biome-ignore lint/suspicious/noAssignInExpressions: ??= 挂 ref 是 StrictMode 下\"只创建一次\"的关键\n (createdIdPromiseRef.current ??= client.sessions\n .create(createOptionsRef.current ?? {})\n .then((created) => {\n // create() 的实例引用立即归还,由下面的 connect() 统一取,避免多挂一份计数\n const id = created.sessionId\n created.dispose()\n onCreatedRef.current?.(id)\n return id\n }))\n\n idPromise\n .then((id) => client.hub.connect(id))\n .then((next) => {\n if (connRef.current.gen !== gen) {\n next.dispose() // 已被更新的连接请求取代\n return\n }\n connRef.current.id = next.sessionId\n connRef.current.session = next\n setSession(next)\n setError(null)\n })\n .catch((err) => {\n if (connRef.current.gen !== gen) return\n createdIdPromiseRef.current = null // 创建失败允许下次重试\n setError(err instanceof Error ? err : new Error(String(err)))\n })\n }\n }, [client])\n\n // 挂载时建立连接,仅在卸载(或 client 变化)时释放。\n useEffect(() => {\n connect(sessionIdRef.current)\n return () => {\n connRef.current.gen++ // 作废在途的连接结果\n const toRelease = connRef.current.session\n connRef.current.id = null\n connRef.current.session = null\n setSession(null)\n if (toRelease) setTimeout(() => toRelease.dispose(), DISPOSE_DELAY_MS)\n }\n }, [connect])\n\n // 仅当 sessionId 明确切到「与已连接会话不同」的 id 时才重连。\n // 回填自动创建的 id(sessionId === 已连接 id)在此被跳过。\n useEffect(() => {\n if (!sessionId) return\n if (connRef.current.id === null) return // 首连仍在进行,交给挂载 effect\n if (sessionId === connRef.current.id) return\n const previous = connRef.current.session\n connRef.current.id = null\n connRef.current.session = null\n if (previous) setTimeout(() => previous.dispose(), DISPOSE_DELAY_MS)\n connect(sessionId)\n }, [sessionId, connect])\n\n const store = useMemo(\n () => ({\n subscribe: session ? (listener: () => void) => session.subscribe(listener) : EMPTY_SUBSCRIBE,\n getSnapshot: () => (session ? session.getState() : null),\n }),\n [session],\n )\n const state = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n\n return { session, state, error }\n}\n","import type { AgentSession, BladeClient, CreateSessionRequest } from \"@blade-hq/agent-client\"\nimport { BladeApiError, VersionMismatchError } from \"@blade-hq/agent-client\"\nimport { CircleAlert, LockKeyhole } from \"lucide-react\"\nimport { useEffect, useState, type ReactNode } from \"react\"\nimport { useBladeClient } from \"../context\"\nimport { useAgentSession } from \"../hooks/use-agent-session\"\nimport { cn } from \"../lib/utils\"\nimport { ChatInput } from \"./ChatInput\"\nimport { ConnectionBanner } from \"./ConnectionBanner\"\nimport { MessageList } from \"./MessageList\"\nimport type { ToolCallRenderer } from \"./ToolCallBlock\"\n\nexport interface ChatViewClassNames {\n root?: string\n banner?: string\n messageList?: string\n chatInput?: string\n}\n\nexport interface ChatViewRenderers {\n /** 自定义工具调用渲染;返回 null 走默认渲染。 */\n toolCall?: ToolCallRenderer\n}\n\nexport interface ChatViewSlots {\n /** 消息列表上方的自定义区域。 */\n header?: ReactNode\n /** 无消息时的空状态。 */\n emptyState?: ReactNode\n /** 输入框下方的自定义区域。 */\n footer?: ReactNode\n}\n\nexport interface ChatViewProps {\n /** 连接既有会话;不传则按 createOptions 自动新建。 */\n sessionId?: string\n createOptions?: CreateSessionRequest\n /** 自动创建出的 sessionId 通过此回调交还调用方持有。 */\n onSessionCreated?: (sessionId: string) => void\n /** 会话连接完成后回调(页面协作场景拿 AgentSession 实例用)。 */\n onSessionReady?: (session: AgentSession) => void\n /** 智能体下发给宿主页面的指令处理器(action → handler)。 */\n commands?: Record<string, (data: unknown) => void>\n classNames?: ChatViewClassNames\n renderers?: ChatViewRenderers\n slots?: ChatViewSlots\n placeholder?: string\n}\n\nfunction isUnauthorizedError(error: Error | null): boolean {\n return error instanceof BladeApiError && error.status === 401\n}\n\n/** 登录引导卡片:未持有令牌或令牌失效时展示,按钮走弹窗授权。 */\nfunction LoginCard({ client, onLoggedIn }: { client: BladeClient; onLoggedIn: () => void }) {\n const [loggingIn, setLoggingIn] = useState(false)\n const [loginError, setLoginError] = useState<string | null>(null)\n\n const handleLogin = async () => {\n setLoggingIn(true)\n setLoginError(null)\n try {\n await client.login()\n onLoggedIn()\n } catch (error) {\n setLoginError(error instanceof Error && error.message ? error.message : \"登录失败,请重试\")\n } finally {\n setLoggingIn(false)\n }\n }\n\n return (\n <div className=\"blade-chat-login flex flex-1 items-center justify-center p-6\">\n <div className=\"flex w-full max-w-sm flex-col items-center gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-6 py-8 text-center\">\n <LockKeyhole size={28} className=\"text-[hsl(var(--muted-foreground))]\" />\n <div className=\"text-base font-medium text-[hsl(var(--foreground))]\">需要登录后使用</div>\n <div className=\"text-sm text-[hsl(var(--muted-foreground))]\">\n 登录后即可与智能体对话,你的会话内容仅自己可见。\n </div>\n <button\n type=\"button\"\n onClick={handleLogin}\n disabled={loggingIn}\n className=\"w-full rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-semibold text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:opacity-60\"\n >\n {loggingIn ? \"登录中…\" : \"登录\"}\n </button>\n {loginError && <div className=\"text-xs text-[hsl(var(--destructive))]\">{loginError}</div>}\n </div>\n </div>\n )\n}\n\n/** 版本不匹配提示:SDK 与 Server 需要配套升级,展示可读的错误说明。 */\nfunction VersionMismatchCard({ error }: { error: VersionMismatchError }) {\n return (\n <div className=\"blade-chat-version-mismatch flex flex-1 items-center justify-center p-6\">\n <div className=\"flex w-full max-w-md flex-col gap-3 rounded-2xl border border-amber-500/30 bg-amber-500/10 px-6 py-6\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-amber-300\">\n <CircleAlert size={16} />\n <span>版本不匹配</span>\n </div>\n <div className=\"break-words text-sm leading-6 text-[hsl(var(--foreground))]\">\n {error.message}\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * 开箱即用的聊天界面:连接(或新建)一个会话,渲染消息流与输入框。\n * 状态全部来自 useAgentSession,不依赖任何全局 store。\n */\nexport function ChatView(props: ChatViewProps) {\n const client = useBladeClient()\n // attempt 作为会话子树的 key:登录成功后整体重挂载,重新走连接/创建流程\n const [attempt, setAttempt] = useState(0)\n const [needLogin, setNeedLogin] = useState(() => !client.hasToken())\n\n if (needLogin) {\n return (\n <div className={cn(\"blade-chat flex min-h-0 flex-1 flex-col\", props.classNames?.root)}>\n <LoginCard\n client={client}\n onLoggedIn={() => {\n setNeedLogin(false)\n setAttempt((value) => value + 1)\n }}\n />\n </div>\n )\n }\n\n return (\n <ChatSessionView key={attempt} {...props} onUnauthorized={() => setNeedLogin(true)} />\n )\n}\n\nfunction ChatSessionView({\n sessionId,\n createOptions,\n onSessionCreated,\n onSessionReady,\n commands,\n classNames,\n renderers,\n slots,\n placeholder,\n onUnauthorized,\n}: ChatViewProps & { onUnauthorized: () => void }) {\n const { session, state, error } = useAgentSession(sessionId, {\n createOptions,\n onSessionCreated,\n })\n const [stopRequested, setStopRequested] = useState(false)\n const [inputText, setInputText] = useState(\"\")\n\n // 会话就绪后交还实例\n useEffect(() => {\n if (session) {\n onSessionReady?.(session)\n }\n }, [session, onSessionReady])\n\n // session.attach() / insertText() 注入到输入框:页面把业务数据(如地图选中\n // 的点位)塞进输入框,随下一条消息发给智能体。\n useEffect(() => {\n if (!session) return\n const offAttach = session.on(\"attachRequested\", ({ label, content }) => {\n setInputText((prev) => `${prev ? `${prev}\\n\\n` : \"\"}[${label}]\\n${content}`)\n })\n const offInsert = session.on(\"insertTextRequested\", ({ text }) => {\n setInputText((prev) => prev + text)\n })\n return () => {\n offAttach()\n offInsert()\n }\n }, [session])\n\n // 令牌失效(401)时回到登录引导\n useEffect(() => {\n if (isUnauthorizedError(error)) {\n onUnauthorized()\n }\n }, [error, onUnauthorized])\n\n // 注册宿主页面指令:卸载或 commands 变化时反注册\n useEffect(() => {\n if (!session || !commands) return\n const unsubscribes = Object.entries(commands).map(([action, handler]) =>\n session.onCommand(action, (payload) => handler(payload)),\n )\n return () => {\n for (const unsubscribe of unsubscribes) unsubscribe()\n }\n }, [session, commands])\n\n if (error instanceof VersionMismatchError) {\n return (\n <div className={cn(\"blade-chat flex min-h-0 flex-1 flex-col\", classNames?.root)}>\n <VersionMismatchCard error={error} />\n </div>\n )\n }\n\n const isStreaming = state?.isStreaming ?? false\n const isStopping = stopRequested && isStreaming\n const connectError =\n error && !isUnauthorizedError(error)\n ? error.message || \"连接失败,请稍后重试\"\n : null\n const errorMessage = connectError ?? state?.errorMessage ?? null\n\n const handleSend = (text: string) => {\n setStopRequested(false)\n void session?.send(text, { mode: state?.mode ?? undefined })\n }\n\n const handleStop = () => {\n setStopRequested(true)\n void session?.stop()\n }\n\n return (\n <div\n className={cn(\n \"blade-chat flex min-h-0 flex-1 flex-col overflow-hidden\",\n classNames?.root,\n )}\n >\n <ConnectionBanner\n connection={state?.connection ?? \"connecting\"}\n className={classNames?.banner}\n />\n {errorMessage && (\n <div className=\"blade-chat-error-bar flex items-start gap-2 border-b border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800 dark:border-red-800/30 dark:bg-red-950/30 dark:text-red-300\">\n <CircleAlert size={16} className=\"mt-0.5 shrink-0\" />\n <span>{errorMessage}</span>\n </div>\n )}\n {slots?.header}\n <MessageList\n messages={state?.messages ?? []}\n isStreaming={isStreaming}\n sessionStatus={state?.status ?? undefined}\n askAnswers={state?.askAnswers}\n onAnswer={(answer, toolCallId, answerData) => {\n void session?.send(answer, {\n mode: state?.mode ?? undefined,\n askUserAnswer: { tool_call_id: toolCallId, ...answerData },\n })\n }}\n toolCallRenderer={renderers?.toolCall}\n emptyState={slots?.emptyState}\n className={classNames?.messageList}\n />\n <ChatInput\n value={inputText}\n onValueChange={setInputText}\n onSend={handleSend}\n onStop={handleStop}\n isStreaming={isStreaming}\n isStopping={isStopping}\n placeholder={placeholder}\n className={classNames?.chatInput}\n />\n {slots?.footer}\n </div>\n )\n}\n","import { clsx, type ClassValue } from \"clsx\"\n\n/** 合并 className(SDK 内不引入 tailwind-merge,按序拼接即可)。 */\nexport function cn(...inputs: ClassValue[]): string {\n return clsx(inputs)\n}\n\n/** 复制文本到剪贴板,返回是否成功。 */\nexport async function copyToClipboard(text: string): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text)\n return true\n } catch {\n return false\n }\n}\n","import { ArrowUp, Loader2, Square } from \"lucide-react\"\nimport type { KeyboardEvent } from \"react\"\nimport { cn } from \"../lib/utils\"\n\ninterface Props {\n /** 受控输入内容(由 ChatView 持有,便于 attach/insertText 注入)。 */\n value: string\n onValueChange: (value: string) => void\n onSend: (text: string) => void\n onStop: () => void\n isStreaming: boolean\n isStopping?: boolean\n placeholder?: string\n className?: string\n}\n\n/**\n * 轻量聊天输入框:textarea(Enter 发送 / Shift+Enter 换行)+ 发送/停止按钮。\n * 不含第一方版本的富文本编辑、@ 补全、技能菜单、语音输入与模型选择。\n * 内容受控,session.attach() / insertText() 通过 ChatView 注入到这里。\n */\nexport function ChatInput({\n value,\n onValueChange,\n onSend,\n onStop,\n isStreaming,\n isStopping = false,\n placeholder = \"输入消息…\",\n className,\n}: Props) {\n const trimmed = value.trim()\n const canSend = trimmed.length > 0 && !isStreaming\n\n const handleSend = () => {\n if (!canSend) return\n onSend(trimmed)\n onValueChange(\"\")\n }\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n // 输入法组合中回车用于选字,不能触发发送\n if (event.key === \"Enter\" && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault()\n handleSend()\n }\n }\n\n return (\n <div className={cn(\"blade-chat-input border-t border-[hsl(var(--border))] px-4 py-3\", className)}>\n <div className=\"blade-chat-input-inner mx-auto flex max-w-[748px] items-end gap-2 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2\">\n <textarea\n value={value}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={handleKeyDown}\n onInput={(event) => {\n // 内容驱动的高度自适应:先复位再按 scrollHeight 撑开,上限约 8 行\n const el = event.currentTarget\n el.style.height = \"auto\"\n el.style.height = `${Math.min(el.scrollHeight, 192)}px`\n }}\n rows={1}\n placeholder={placeholder}\n aria-label=\"聊天输入\"\n className=\"blade-chat-textarea max-h-48 min-h-[28px] flex-1 resize-none bg-transparent py-1 text-sm leading-6 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.6)]\"\n />\n {isStreaming ? (\n <button\n type=\"button\"\n onClick={onStop}\n disabled={isStopping}\n aria-label={isStopping ? \"正在停止\" : \"停止回复\"}\n title={isStopping ? \"正在停止\" : \"停止回复\"}\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--muted))] text-[hsl(var(--foreground))] transition-opacity hover:opacity-90 disabled:opacity-60\"\n >\n {isStopping ? (\n <Loader2 size={14} className=\"animate-spin\" />\n ) : (\n <Square size={12} fill=\"currentColor\" />\n )}\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={handleSend}\n disabled={!canSend}\n aria-label=\"发送消息\"\n title=\"发送消息\"\n className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ArrowUp size={15} />\n </button>\n )}\n </div>\n </div>\n )\n}\n","import type { ConnectionStatus } from \"@blade-hq/agent-client\"\nimport { AlertTriangle, LoaderCircle } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\ninterface Props {\n connection: ConnectionStatus\n className?: string\n}\n\n/** 连接状态提示条:数据来自 SessionState.connection,正常连接时不渲染。 */\nexport function ConnectionBanner({ connection, className }: Props) {\n if (connection === \"connected\" || connection === \"connecting\") {\n return null\n }\n\n const reconnecting = connection === \"reconnecting\"\n\n return (\n <div className={cn(\"blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3\", className)}>\n <div\n className={cn(\n \"mx-auto flex max-w-3xl items-start gap-3 rounded-2xl border px-4 py-3\",\n reconnecting\n ? \"border-amber-500/25 bg-amber-500/10 text-amber-100\"\n : \"border-rose-500/25 bg-rose-500/10 text-rose-100\",\n )}\n >\n <span className=\"mt-0.5 shrink-0\">\n {reconnecting ? (\n <LoaderCircle size={14} className=\"animate-spin\" />\n ) : (\n <AlertTriangle size={14} />\n )}\n </span>\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium\">\n {reconnecting ? \"连接已断开,正在重连…\" : \"连接已断开\"}\n </div>\n <div className=\"text-xs opacity-80\">消息同步可能会延迟,系统会继续自动重试</div>\n </div>\n </div>\n </div>\n )\n}\n","import type { AskUserAnswerData, ChatMessage } from \"@blade-hq/agent-client\"\nimport { isHiddenInternalMessage } from \"@blade-hq/agent-client\"\nimport { ChevronDown, Layers, Lightbulb, MessageSquare } from \"lucide-react\"\nimport { useEffect, useMemo, useRef, type ReactNode } from \"react\"\nimport { StickToBottom, useStickToBottomContext } from \"use-stick-to-bottom\"\nimport { cn } from \"../lib/utils\"\nimport { AssistantTurnBlock } from \"./AssistantTurnBlock\"\nimport { RenderErrorBoundary } from \"./RenderErrorBoundary\"\nimport type { ToolCallRenderer } from \"./ToolCallBlock\"\nimport {\n ErrorMessageBlock,\n UserMessageBubble,\n isErrorMessage,\n isUserMessage,\n} from \"./UserMessageBubble\"\n\ntype RenderBlock =\n | { type: \"message\"; message: ChatMessage; key: string }\n | { type: \"assistant_turn\"; messages: ChatMessage[]; key: string; isStreaming: boolean }\n | { type: \"compaction\"; key: string }\n | { type: \"planning_divider\"; kind: \"enter\" | \"exit\"; key: string }\n\nfunction parseModeChange(message: ChatMessage): { from: string; to: string } | null {\n if (message.kind !== \"mode_change\" || typeof message.content !== \"string\") {\n return null\n }\n try {\n const parsed = JSON.parse(message.content) as { from?: unknown; to?: unknown }\n if (typeof parsed.from === \"string\" && typeof parsed.to === \"string\") {\n return { from: parsed.from, to: parsed.to }\n }\n } catch {}\n return null\n}\n\nfunction getPlanningDividerKind(message: ChatMessage): \"enter\" | \"exit\" | null {\n const modeChange = parseModeChange(message)\n if (modeChange?.to === \"planning\") return \"enter\"\n if (modeChange?.from === \"planning\") return \"exit\"\n return null\n}\n\nfunction getMessageResetSignature(messages: ChatMessage[]): string {\n return messages\n .map((message) => {\n const contentSize =\n typeof message.content === \"string\" ? message.content.length : message.content.length\n const toolSignature =\n message.tool_calls\n ?.map((toolCall) => `${toolCall.id}:${toolCall.status ?? \"\"}`)\n .join(\",\") ?? \"\"\n return [\n message.entry_id ?? message.timestamp ?? message.role,\n message.status ?? \"\",\n contentSize,\n toolSignature,\n ].join(\":\")\n })\n .join(\"|\")\n}\n\ninterface Props {\n messages: ChatMessage[]\n isStreaming: boolean\n sessionStatus?: string\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n toolCallRenderer?: ToolCallRenderer\n emptyState?: ReactNode\n className?: string\n}\n\n/**\n * 消息列表:use-stick-to-bottom 自动滚动 + 按轮次分组渲染。\n * 相比第一方版本去掉了轮次导航栏、吸顶状态条与规划摘要卡片。\n */\nexport function MessageList({\n messages,\n isStreaming,\n sessionStatus,\n askAnswers,\n onAnswer,\n toolCallRenderer,\n emptyState,\n className,\n}: Props) {\n const renderBlocks = useMemo(() => {\n const visible = messages.filter((message) => {\n if ((message.loop_name ?? \"root\") !== \"root\") return false\n if (isHiddenInternalMessage(message)) return false\n if (message.kind === \"compaction\") return true\n return message.role !== \"tool\" || getPlanningDividerKind(message) !== null\n })\n\n const blocks: RenderBlock[] = []\n let assistantBuffer: ChatMessage[] = []\n let assistantTurnCount = 0\n\n const flushAssistant = () => {\n if (assistantBuffer.length === 0) return\n assistantTurnCount += 1\n const first = assistantBuffer[0]\n blocks.push({\n type: \"assistant_turn\",\n messages: assistantBuffer,\n key: first.entry_id ?? `assistant-${assistantTurnCount}`,\n isStreaming: assistantBuffer.some((message) => message.status === \"streaming\"),\n })\n assistantBuffer = []\n }\n\n for (const message of visible) {\n const dividerKind = getPlanningDividerKind(message)\n if (dividerKind) {\n flushAssistant()\n blocks.push({\n type: \"planning_divider\",\n kind: dividerKind,\n key: message.entry_id ?? `${dividerKind}-${blocks.length}`,\n })\n continue\n }\n\n if (message.kind === \"compaction\") {\n flushAssistant()\n blocks.push({ type: \"compaction\", key: message.entry_id ?? `compaction-${blocks.length}` })\n continue\n }\n\n if (message.role === \"assistant\") {\n assistantBuffer.push(message)\n continue\n }\n\n flushAssistant()\n blocks.push({\n type: \"message\",\n message,\n key: message.entry_id ?? `${message.role}-${blocks.length}`,\n })\n }\n\n flushAssistant()\n\n // 流式中但最后一块还是用户消息:补一个空的助手轮占位,立即出现\"正在生成\"\n const last = blocks[blocks.length - 1]\n if (isStreaming) {\n if (last?.type === \"assistant_turn\") {\n last.isStreaming = true\n } else if (last?.type === \"message\" && isUserMessage(last.message)) {\n blocks.push({\n type: \"assistant_turn\",\n messages: [\n {\n role: \"assistant\",\n content: \"\",\n entry_id: `pending-assistant-${last.key}`,\n status: \"streaming\",\n loop_name: \"root\",\n },\n ],\n key: `pending-assistant-${last.key}`,\n isStreaming: true,\n })\n }\n }\n\n return blocks\n }, [messages, isStreaming])\n\n return (\n <div className={cn(\"blade-chat-messages relative min-h-0 flex-1\", className)}>\n <StickToBottom className=\"h-full overflow-y-hidden\" initial=\"instant\" resize=\"instant\">\n <StickToBottom.Content className=\"px-5 pb-7 pt-8 sm:px-10 sm:pt-10\">\n <div className=\"blade-chat-messages-content mx-auto max-w-[748px]\">\n <div className=\"flex min-w-0 flex-col gap-9\">\n {renderBlocks.length === 0\n ? (emptyState ?? (\n <div className=\"flex flex-col items-center justify-center gap-3 py-24 text-[hsl(var(--muted-foreground))]\">\n <MessageSquare size={40} strokeWidth={1.5} />\n <span className=\"text-base font-medium\">开始对话</span>\n <span className=\"text-sm opacity-60\">在下方输入消息开始聊天</span>\n </div>\n ))\n : renderBlocks.map((block) => {\n if (block.type === \"message\") {\n return (\n <div key={block.key} data-entry-id={block.message.entry_id}>\n {isUserMessage(block.message) ? (\n <UserMessageBubble message={block.message} />\n ) : isErrorMessage(block.message) ? (\n <ErrorMessageBlock message={block.message} />\n ) : null}\n </div>\n )\n }\n\n if (block.type === \"assistant_turn\") {\n return (\n <div key={block.key} data-entry-id={block.messages[0]?.entry_id}>\n <RenderErrorBoundary\n label=\"助手消息\"\n details={block.key}\n resetKey={getMessageResetSignature(block.messages)}\n >\n <AssistantTurnBlock\n messages={block.messages}\n isStreaming={block.isStreaming}\n askAnswers={askAnswers}\n onAnswer={onAnswer}\n sessionStatus={sessionStatus}\n toolCallRenderer={toolCallRenderer}\n />\n </RenderErrorBoundary>\n </div>\n )\n }\n\n if (block.type === \"compaction\") {\n return (\n <div\n key={block.key}\n className=\"flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]\"\n >\n <Layers size={12} />\n <span>上下文已压缩</span>\n </div>\n )\n }\n\n return <PlanningDivider key={block.key} kind={block.kind} />\n })}\n\n {sessionStatus === \"interrupted\" && !isStreaming ? (\n <div className=\"flex\">\n <div className=\"rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300\">\n 已中断\n </div>\n </div>\n ) : null}\n </div>\n </div>\n </StickToBottom.Content>\n <AutoScrollOnUserSend userMessageCount={messages.filter((m) => isUserMessage(m)).length} />\n <ScrollToBottomButton />\n </StickToBottom>\n </div>\n )\n}\n\n/** 用户每发一条消息就滚到底部;流式期间用户向上翻阅历史则不打扰。 */\nfunction AutoScrollOnUserSend({ userMessageCount }: { userMessageCount: number }) {\n const { scrollToBottom } = useStickToBottomContext()\n const previousCountRef = useRef(userMessageCount)\n useEffect(() => {\n if (userMessageCount > previousCountRef.current) {\n scrollToBottom(\"instant\")\n }\n previousCountRef.current = userMessageCount\n }, [userMessageCount, scrollToBottom])\n return null\n}\n\nfunction ScrollToBottomButton() {\n const { isAtBottom, scrollToBottom } = useStickToBottomContext()\n if (isAtBottom) return null\n\n return (\n <button\n type=\"button\"\n onClick={() => scrollToBottom()}\n className=\"absolute bottom-4 right-4 flex items-center gap-1 rounded-full border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-1.5 text-xs text-[hsl(var(--muted-foreground))] shadow-lg transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\"\n >\n <ChevronDown size={14} />\n <span>滚动到底部</span>\n </button>\n )\n}\n\nfunction PlanningDivider({ kind }: { kind: \"enter\" | \"exit\" }) {\n return (\n <div className=\"flex items-center gap-3 py-1\">\n <div className=\"h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent\" />\n <div className=\"inline-flex items-center gap-1.5 rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-[11px] text-amber-300\">\n <Lightbulb size={12} />\n <span>{kind === \"enter\" ? \"进入规划模式\" : \"规划完成\"}</span>\n </div>\n <div className=\"h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent\" />\n </div>\n )\n}\n","import type { AskUserAnswerData, ChatMessage } from \"@blade-hq/agent-client\"\nimport { getTextContent, normalizeMessageContent } from \"@blade-hq/agent-client\"\nimport { Brain, ChevronDown } from \"lucide-react\"\nimport { useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { AgentLoopBlock } from \"./AgentLoopBlock\"\nimport { MarkdownContent } from \"./MarkdownContent\"\nimport { Shimmer } from \"./Shimmer\"\nimport { ToolCallBlock, type ToolCallRenderer } from \"./ToolCallBlock\"\nimport { formatToolName } from \"./display-utils\"\n\ninterface Props {\n messages: ChatMessage[]\n isStreaming?: boolean\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n sessionStatus?: string\n toolCallRenderer?: ToolCallRenderer\n}\n\n/** 思考折叠块:折叠状态归组件自身,不依赖全局 store。 */\nfunction ThinkingBlock({ reasoning, isStreaming }: { reasoning: string; isStreaming: boolean }) {\n const [open, setOpen] = useState(false)\n\n return (\n <div className=\"blade-chat-thinking ml-4 text-sm\">\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n aria-expanded={open}\n className=\"inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]\"\n >\n <Brain size={12} className=\"shrink-0\" />\n {isStreaming ? <Shimmer className=\"text-xs\">正在思考...</Shimmer> : <span>思考过程</span>}\n <ChevronDown\n size={12}\n className={cn(\"shrink-0 transition-transform\", open && \"rotate-180\")}\n />\n </button>\n {open && (\n <div className=\"mt-1.5 whitespace-pre-wrap border-l-2 border-[hsl(var(--border))] pl-3 text-[11px] leading-5 text-[hsl(var(--muted-foreground))]\">\n {reasoning}\n </div>\n )}\n </div>\n )\n}\n\nfunction getMessageText(message: ChatMessage): string {\n return getTextContent(normalizeMessageContent(message.content)).trim()\n}\n\n/**\n * 一轮助手回复:按消息顺序渲染 thinking 折叠块、正文 Markdown 与工具调用。\n * 相比第一方版本去掉了紧凑/详细模式切换、文件交付卡片与内联资源 iframe。\n */\nexport function AssistantTurnBlock({\n messages,\n isStreaming = false,\n askAnswers,\n onAnswer,\n sessionStatus,\n toolCallRenderer,\n}: Props) {\n const hasInterrupted = messages.some((message) => message.status === \"interrupted\")\n const hasAnyContent = messages.some(\n (message) =>\n getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0,\n )\n\n return (\n <div className=\"blade-chat-assistant-turn flex flex-col gap-3\">\n {hasInterrupted && (\n <div className=\"ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300\">\n 已中断\n </div>\n )}\n {messages.map((message, index) => {\n const isLast = index === messages.length - 1\n const streamingThis = isStreaming && isLast\n const text = getMessageText(message)\n const toolCalls = message.tool_calls ?? []\n\n return (\n <div\n key={message.entry_id ?? `${message.timestamp ?? \"assistant\"}-${index}`}\n className=\"flex flex-col gap-3\"\n >\n {message.reasoning && (\n <ThinkingBlock reasoning={message.reasoning} isStreaming={streamingThis && !text} />\n )}\n {text && (\n <div className=\"blade-chat-assistant-text text-[15px] leading-8 text-[hsl(var(--foreground))]\">\n <MarkdownContent\n mode={streamingThis ? \"streaming\" : \"static\"}\n className=\"blade-chat-prose\"\n >\n {text}\n </MarkdownContent>\n </div>\n )}\n {toolCalls.length > 0 && (\n <div className=\"flex flex-col gap-2\">\n {toolCalls.map((toolCall) =>\n formatToolName(toolCall.name) === \"Agent\" ? (\n <AgentLoopBlock key={toolCall.id} toolCall={toolCall} />\n ) : (\n <ToolCallBlock\n key={toolCall.id}\n toolCall={toolCall}\n answerData={askAnswers?.[toolCall.id]}\n onAnswer={onAnswer}\n answered={sessionStatus !== \"waiting_for_input\"}\n sessionStatus={sessionStatus}\n renderer={toolCallRenderer}\n />\n ),\n )}\n </div>\n )}\n </div>\n )\n })}\n {isStreaming && !hasAnyContent && <Shimmer className=\"ml-4\">正在生成...</Shimmer>}\n </div>\n )\n}\n","import type { ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { Bot, Check, ChevronRight, Loader2, X } from \"lucide-react\"\nimport { useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { formatToolDuration, formatToolResult } from \"./display-utils\"\n\ninterface Props {\n toolCall: ToolCallInfo\n}\n\nfunction parseAgentDescription(argumentsJson: string): string {\n try {\n const parsed = JSON.parse(argumentsJson)\n return typeof parsed?.description === \"string\" && parsed.description\n ? parsed.description\n : \"子智能体\"\n } catch {\n return \"子智能体\"\n }\n}\n\n/**\n * 子智能体(fork Agent)状态块的 SDK 简化版:只展示描述、状态与最终结果,\n * 不再嵌套渲染子循环的完整消息流(那是第一方界面的能力)。\n */\nexport function AgentLoopBlock({ toolCall }: Props) {\n const [expanded, setExpanded] = useState(false)\n const description = parseAgentDescription(toolCall.arguments)\n const running = toolCall.status === \"pending\" || toolCall.status === \"awaiting_answer\"\n const failed = toolCall.status === \"error\" || toolCall.status === \"cancelled\"\n\n return (\n <div className=\"blade-chat-agent-loop ml-4 text-xs\">\n <div\n className={cn(\n \"border-l-[3px] flex items-center gap-2 px-3 py-2\",\n failed\n ? \"border-l-[hsl(var(--muted-foreground)/0.5)]\"\n : running\n ? \"border-l-blue-500\"\n : \"border-l-[hsl(var(--primary))]\",\n )}\n >\n <button\n type=\"button\"\n onClick={() => setExpanded(!expanded)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none\"\n aria-expanded={expanded}\n >\n <ChevronRight\n size={11}\n className={cn(\n \"shrink-0 text-[hsl(var(--muted-foreground))] transition-transform\",\n expanded && \"rotate-90\",\n )}\n />\n <Bot size={12} className=\"shrink-0 text-[hsl(var(--muted-foreground))]\" />\n <span\n className={cn(\n \"flex shrink-0 items-center gap-1 text-[10px]\",\n failed\n ? \"text-[hsl(var(--muted-foreground))]\"\n : running\n ? \"text-blue-300\"\n : \"text-[hsl(var(--primary))]\",\n )}\n >\n {running ? (\n <Loader2 size={11} className=\"animate-spin\" />\n ) : failed ? (\n <X size={11} />\n ) : (\n <Check size={11} />\n )}\n <span>{running ? \"执行中\" : failed ? \"已终止\" : \"完成\"}</span>\n </span>\n <span className=\"min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]\">\n 子智能体:{description}\n </span>\n </button>\n {typeof toolCall.duration_ms === \"number\" && toolCall.duration_ms > 0 && (\n <span className=\"shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]\">\n {formatToolDuration(toolCall.duration_ms)}\n </span>\n )}\n </div>\n {expanded && toolCall.result != null && (\n <div className=\"ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3\">\n <div className=\"mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 结果\n </div>\n <pre className=\"max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {formatToolResult(toolCall.result)}\n </pre>\n </div>\n )}\n </div>\n )\n}\n","import type { ToolCallInfo } from \"@blade-hq/agent-client\"\n\n/** 工具名归一化别名表:把后端各种命名风格统一为展示用的驼峰名。 */\nconst TOOL_NAME_ALIASES: Record<string, string> = {\n agent: \"Agent\",\n ask_user_question: \"AskUserQuestion\",\n bash: \"Bash\",\n bg_bash: \"BgBash\",\n edit: \"Edit\",\n exit_plan_mode: \"ExitPlanMode\",\n file_edit: \"Edit\",\n file_read: \"Read\",\n file_write: \"Write\",\n finish_task: \"FinishTask\",\n glob: \"Glob\",\n grep: \"Grep\",\n ls: \"Ls\",\n read: \"Read\",\n read_skill: \"ReadSkill\",\n web_fetch: \"WebFetch\",\n web_search: \"WebSearch\",\n write: \"Write\",\n}\n\n/** 系统工具的中文展示名。 */\nconst TOOL_DISPLAY_LABELS: Record<string, string> = {\n Bash: \"执行命令\",\n BgBash: \"后台执行命令\",\n Read: \"读取文件\",\n Write: \"写入文件\",\n Edit: \"编辑文件\",\n Ls: \"列出目录\",\n Glob: \"匹配文件\",\n Grep: \"搜索文本\",\n WebSearch: \"搜索网页\",\n WebFetch: \"整理网页内容\",\n Agent: \"派生子智能体\",\n AskUserQuestion: \"向用户提问\",\n ReadSkill: \"读取技能\",\n FinishTask: \"任务完成\",\n ExitPlanMode: \"提交计划\",\n ListSessions: \"列出历史会话\",\n GetSessionHistory: \"读取会话历史\",\n}\n\nexport type ToolTone = \"emerald\" | \"blue\" | \"amber\" | \"red\"\n\nfunction safeParseJson(value: string | null | undefined): unknown {\n if (!value) return null\n try {\n return JSON.parse(value)\n } catch {\n return null\n }\n}\n\nfunction getStringArgValue(args: Record<string, unknown> | null, key: string): string {\n const value = args?.[key]\n return typeof value === \"string\" ? value.trim() : \"\"\n}\n\n/** 归一化工具名:去掉命名空间前缀并映射到别名表(如 \"fork:Agent\" → \"Agent\")。 */\nexport function formatToolName(name: string): string {\n const trimmed = name.trim()\n if (!trimmed) return name\n\n const stripped = trimmed.split(\":\").pop()?.split(\"/\").pop()?.split(\".\").pop()?.trim() || trimmed\n const normalized = stripped\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\n .replace(/[^a-zA-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .toLowerCase()\n\n return TOOL_NAME_ALIASES[normalized] ?? stripped\n}\n\n/**\n * 返回工具调用的中文展示标签:优先取参数里的 description,\n * 其次取后端下发的 display_name / 系统工具中文名。\n */\nexport function getToolDisplayLabel(toolCall: ToolCallInfo): string {\n const normalized = formatToolName(toolCall.name)\n const args = safeParseJson(toolCall.arguments) as Record<string, unknown> | null\n const displayName = toolCall.display_name?.trim() ?? \"\"\n const baseLabel = displayName || TOOL_DISPLAY_LABELS[normalized] || normalized\n const metaDisplayName = getStringArgValue(args, \"_meta_display_name\")\n if (metaDisplayName) {\n return metaDisplayName\n }\n\n const description = getStringArgValue(args, \"description\")\n\n if (normalized === \"BgBash\") {\n return description ? `后台执行:${description}` : \"后台执行命令\"\n }\n if (normalized === \"ReadSkill\") {\n const skillName = getStringArgValue(args, \"skill\") || getStringArgValue(args, \"skill_name\")\n return skillName ? `${baseLabel}「${skillName}」` : baseLabel\n }\n if (normalized === \"FinishTask\") {\n const title = getStringArgValue(args, \"title\")\n return title ? `${baseLabel}:${title}` : baseLabel\n }\n\n return description || baseLabel\n}\n\nexport function getToolTone(status?: ToolCallInfo[\"status\"]): ToolTone {\n if (status === \"error\" || status === \"cancelled\") return \"red\"\n if (status === \"awaiting_answer\") return \"amber\"\n if (status === \"pending\") return \"blue\"\n return \"emerald\"\n}\n\nexport function getToolStatusLabel(status?: ToolCallInfo[\"status\"]): string {\n if (status === \"pending\") return \"运行中\"\n if (status === \"awaiting_answer\") return \"等待回答\"\n if (status === \"error\") return \"错误\"\n if (status === \"cancelled\") return \"已取消\"\n return \"完成\"\n}\n\nexport function formatToolDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`\n const seconds = ms / 1000\n if (seconds < 60) return `${seconds.toFixed(1)}s`\n const minutes = Math.floor(seconds / 60)\n const remainingSeconds = Math.round(seconds % 60)\n return remainingSeconds > 0 ? `${minutes}m${remainingSeconds}s` : `${minutes}m`\n}\n\n/** 参数 JSON 美化:无法解析时按原文展示。 */\nexport function formatToolArgs(args: string): string {\n try {\n return JSON.stringify(JSON.parse(args), null, 2)\n } catch {\n return args\n }\n}\n\nconst RESULT_PREVIEW_LIMIT = 4000\n\n/** 工具结果的文本化 + 截断:长结果只在折叠面板里展示前若干字符。 */\nexport function formatToolResult(result: unknown): string {\n const text = typeof result === \"string\" ? result : JSON.stringify(result, null, 2)\n if (text == null) return \"\"\n if (text.length <= RESULT_PREVIEW_LIMIT) return text\n return `${text.slice(0, RESULT_PREVIEW_LIMIT)}\\n…(结果过长,已截断)`\n}\n","import { Check, Copy } from \"lucide-react\"\nimport {\n type ComponentProps,\n type ComponentPropsWithRef,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\"\nimport { type ExtraProps, Streamdown } from \"streamdown\"\nimport { cn, copyToClipboard } from \"../lib/utils\"\n\ntype StreamdownProps = ComponentProps<typeof Streamdown>\n\n/** 平台内部提醒标记:不属于给用户看的内容,渲染前整段剥掉。 */\nconst SYSTEM_REMINDER_RE = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/gi\n\ntype PreProps = ComponentPropsWithRef<\"pre\"> & ExtraProps\n\n/** 代码块外壳:常驻头部栏(语言标签 + 复制按钮),代码区浅底。 */\nfunction CodeBlockPre({ children, node: _node, ...props }: PreProps) {\n const preRef = useRef<HTMLPreElement>(null)\n const [copied, setCopied] = useState(false)\n const [language, setLanguage] = useState(\"\")\n\n // DOM 测量:语言标签写在内层 code 的 className 上,只能挂载后读取。\n useEffect(() => {\n const codeEl = preRef.current?.querySelector(\"code\")\n setLanguage(codeEl?.className.match(/language-(\\S+)/)?.[1] ?? \"\")\n }, [])\n\n const handleCopy = async () => {\n const ok = await copyToClipboard(preRef.current?.querySelector(\"code\")?.textContent ?? \"\")\n if (ok) {\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n }\n }\n\n return (\n <div className=\"blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]\">\n <div className=\"blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5\">\n <span className=\"font-mono text-[12px] text-[hsl(var(--muted-foreground))]\">\n {language || \"code\"}\n </span>\n <button\n type=\"button\"\n onClick={handleCopy}\n className={cn(\n \"flex h-6 items-center gap-1 rounded-md px-1.5 text-[11px] transition-colors\",\n copied\n ? \"text-[hsl(var(--primary))]\"\n : \"text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n )}\n >\n {copied ? <Check size={12} /> : <Copy size={12} />}\n <span>{copied ? \"已复制\" : \"复制\"}</span>\n </button>\n </div>\n <pre\n ref={preRef}\n {...props}\n className={cn(props.className, \"blade-chat-codeblock-body m-0 overflow-x-auto\")}\n >\n {children}\n </pre>\n </div>\n )\n}\n\ntype AnchorProps = ComponentPropsWithRef<\"a\"> & ExtraProps\n\n/** 链接一律新窗口打开并断开 opener,SDK 不接管站内跳转。 */\nfunction ExternalAnchor({ node: _node, children, ...props }: AnchorProps) {\n return (\n <a {...props} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n )\n}\n\nconst MARKDOWN_COMPONENTS = {\n pre: CodeBlockPre,\n a: ExternalAnchor,\n} as NonNullable<StreamdownProps[\"components\"]>\n\nexport interface MarkdownContentProps {\n children: string\n className?: string\n /** 流式渲染传 \"streaming\",历史静态内容默认 \"static\"。 */\n mode?: StreamdownProps[\"mode\"]\n}\n\n/**\n * SDK 版 Markdown 渲染:Streamdown 默认转义/消毒(不开启任何 raw HTML),\n * 附带代码块复制按钮;相比第一方版本去掉了 mermaid、外链确认弹窗、\n * 会话文件卡片等第一方能力。\n */\nexport function MarkdownContent({ children, className, mode }: MarkdownContentProps) {\n const resolvedChildren = useMemo(() => children.replace(SYSTEM_REMINDER_RE, \"\"), [children])\n\n return (\n <Streamdown\n className={cn(\"blade-chat-markdown break-words\", className)}\n mode={mode ?? \"static\"}\n components={MARKDOWN_COMPONENTS}\n >\n {resolvedChildren}\n </Streamdown>\n )\n}\n","import { cn } from \"../lib/utils\"\n\ninterface ShimmerProps {\n children?: string\n className?: string\n}\n\n/** 流式等待时的呼吸文字效果(样式见 style.css 的 .blade-shimmer-text)。 */\nexport function Shimmer({ children = \"正在思考...\", className }: ShimmerProps) {\n return (\n <span className={cn(\"blade-shimmer-text text-sm font-medium\", className)}>{children}</span>\n )\n}\n","import type { AskUserAnswerData, ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { Check, ChevronRight, Loader2, MessageSquareMore, X } from \"lucide-react\"\nimport { useState, type ReactNode } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { AskUserQuestionBlock, parseAskUserQuestion } from \"./AskUserQuestionBlock\"\nimport {\n formatToolArgs,\n formatToolDuration,\n formatToolName,\n formatToolResult,\n getToolDisplayLabel,\n getToolStatusLabel,\n getToolTone,\n} from \"./display-utils\"\n\n/** 接入方自定义渲染器:返回 null 时回落到默认渲染。 */\nexport type ToolCallRenderer = (info: ToolCallInfo) => ReactNode | null\n\ninterface Props {\n toolCall: ToolCallInfo\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n answered?: boolean\n answerData?: AskUserAnswerData\n sessionStatus?: string\n renderer?: ToolCallRenderer\n}\n\n/**\n * 极简工具调用块:状态 + 展示名一行,点开后是参数/结果的 <pre> 折叠展示。\n * AskUserQuestion 特殊处理为提问选项卡片。\n */\nexport function ToolCallBlock({\n toolCall,\n onAnswer,\n answered,\n answerData,\n sessionStatus,\n renderer,\n}: Props) {\n const [expanded, setExpanded] = useState(false)\n const normalizedName = formatToolName(toolCall.name)\n\n if (renderer) {\n const custom = renderer(toolCall)\n if (custom !== null && custom !== undefined) {\n return <>{custom}</>\n }\n }\n\n // AskUserQuestion:渲染成可作答的提问卡片\n if (normalizedName === \"AskUserQuestion\") {\n const askData =\n parseAskUserQuestion(typeof toolCall.result === \"string\" ? toolCall.result : null) ??\n parseAskUserQuestion(buildAskUserPayload(toolCall.arguments))\n const canAnswer =\n toolCall.status === \"awaiting_answer\" &&\n sessionStatus === \"waiting_for_input\" &&\n Boolean(onAnswer)\n if (askData) {\n return (\n <AskUserQuestionBlock\n data={askData}\n answered={Boolean(answered) || toolCall.status === \"done\"}\n toolCallId={toolCall.id}\n sessionStatus={sessionStatus ?? \"\"}\n answerData={answerData}\n onAnswer={canAnswer ? onAnswer : undefined}\n />\n )\n }\n if (toolCall.status === \"pending\") {\n return (\n <div className=\"ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]\">\n <Loader2 size={14} className=\"animate-spin\" />\n <span>正在准备问题…</span>\n </div>\n )\n }\n return (\n <div className=\"ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]\">\n <div className=\"font-semibold\">选择题内容暂时无法显示</div>\n <div className=\"mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]\">\n 收到的交互数据不完整。请让智能体重新提问。\n </div>\n </div>\n )\n }\n\n const tone = getToolTone(toolCall.status)\n const displayName = getToolDisplayLabel(toolCall)\n const toneClass =\n tone === \"red\"\n ? \"border-l-[hsl(var(--muted-foreground)/0.5)]\"\n : tone === \"amber\"\n ? \"border-l-amber-400\"\n : tone === \"blue\"\n ? \"border-l-blue-500\"\n : \"border-l-[hsl(var(--primary))]\"\n\n const statusIcon =\n toolCall.status === \"pending\" ? (\n <Loader2 size={11} className=\"animate-spin\" />\n ) : toolCall.status === \"awaiting_answer\" ? (\n <MessageSquareMore size={11} />\n ) : toolCall.status === \"cancelled\" || toolCall.status === \"error\" ? (\n <X size={11} />\n ) : (\n <Check size={11} />\n )\n\n const statusTextClass =\n tone === \"red\"\n ? \"text-[hsl(var(--muted-foreground))]\"\n : tone === \"amber\"\n ? \"text-amber-300\"\n : tone === \"blue\"\n ? \"text-blue-300\"\n : \"text-[hsl(var(--primary))]\"\n\n return (\n <div className=\"blade-chat-tool ml-4 text-xs\">\n <div className={cn(\"border-l-[3px] flex items-center gap-2 px-3 py-2\", toneClass)}>\n <button\n type=\"button\"\n onClick={() => setExpanded(!expanded)}\n className=\"flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none\"\n aria-expanded={expanded}\n >\n <ChevronRight\n size={11}\n className={cn(\n \"shrink-0 text-[hsl(var(--muted-foreground))] transition-transform\",\n expanded && \"rotate-90\",\n )}\n />\n <span className={cn(\"flex shrink-0 items-center gap-1 text-[10px]\", statusTextClass)}>\n {statusIcon}\n <span>{getToolStatusLabel(toolCall.status)}</span>\n </span>\n <span className=\"min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]\">\n {displayName}\n </span>\n </button>\n {typeof toolCall.duration_ms === \"number\" && toolCall.duration_ms > 0 && (\n <span className=\"shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]\">\n {formatToolDuration(toolCall.duration_ms)}\n </span>\n )}\n </div>\n {expanded && (\n <div className=\"blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3\">\n <div className=\"mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 工具\n </div>\n <div className=\"mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {normalizedName}\n </div>\n <div className=\"mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 参数\n </div>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {formatToolArgs(toolCall.arguments)}\n </pre>\n {toolCall.result != null && (\n <>\n <div className=\"mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]\">\n 结果\n </div>\n <pre className=\"max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]\">\n {formatToolResult(toolCall.result)}\n </pre>\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n\nfunction buildAskUserPayload(argumentsJson: string): string | null {\n try {\n const parsed = JSON.parse(argumentsJson)\n if (\n parsed &&\n typeof parsed === \"object\" &&\n Array.isArray((parsed as { questions?: unknown }).questions)\n ) {\n return JSON.stringify({ questions: (parsed as { questions: unknown[] }).questions })\n }\n } catch {\n return null\n }\n return null\n}\n","import type { AskUserAnswerData } from \"@blade-hq/agent-client\"\nimport { Check, Loader2, MessageSquareMore } from \"lucide-react\"\nimport { useEffect, useMemo, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { MarkdownContent } from \"./MarkdownContent\"\n\ninterface OptionItem {\n label: string\n description: string\n}\n\ninterface QuestionItem {\n question: string\n options: OptionItem[]\n multiSelect?: boolean\n}\n\ninterface SourceLoopInfo {\n loop_name: string\n description: string\n}\n\nexport interface AskUserQuestionData {\n questions: QuestionItem[]\n source_loop?: SourceLoopInfo | null\n}\n\ninterface Props {\n data: AskUserQuestionData\n answered: boolean\n toolCallId: string\n sessionStatus: string\n answerData?: AskUserAnswerData | undefined\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n}\n\n/** 提问选项卡片:单选/多选 + 自定义输入,回答通过 onAnswer 回调交还 ChatView。 */\nexport function AskUserQuestionBlock({\n data,\n answered,\n toolCallId,\n sessionStatus,\n answerData,\n onAnswer,\n}: Props) {\n // 每个问题独立的选择状态:选项下标集合或自定义文本\n const [selections, setSelections] = useState<Map<number, Set<number>>>(new Map())\n const [customTexts, setCustomTexts] = useState<Map<number, string>>(new Map())\n const [usingCustom, setUsingCustom] = useState<Set<number>>(new Set())\n const [submitted, setSubmitted] = useState(false)\n\n useEffect(() => {\n if (sessionStatus === \"failed\" || sessionStatus === \"interrupted\") {\n setSubmitted(false)\n }\n }, [sessionStatus])\n\n // 已作答时按历史答案展示,未作答时展示本地编辑状态\n const displayAnswerState = useMemo(() => {\n if (!(answered && answerData)) {\n return { selections, customTexts, usingCustom }\n }\n\n const nextSelections = new Map<number, Set<number>>()\n const nextCustomTexts = new Map<number, string>()\n const nextUsingCustom = new Set<number>()\n\n for (const [questionKey, optionIndexes] of Object.entries(answerData.selections)) {\n nextSelections.set(Number(questionKey), new Set(optionIndexes))\n }\n\n for (const [questionKey, text] of Object.entries(answerData.custom)) {\n const qIdx = Number(questionKey)\n nextCustomTexts.set(qIdx, text)\n nextUsingCustom.add(qIdx)\n }\n\n return {\n selections: nextSelections,\n customTexts: nextCustomTexts,\n usingCustom: nextUsingCustom,\n }\n }, [answerData, answered, customTexts, selections, usingCustom])\n\n const toggleOption = (qIdx: number, optIdx: number, multi: boolean) => {\n if (answered || submitted) return\n setSelections((prev) => {\n const next = new Map(prev)\n const current = new Set(next.get(qIdx) ?? [])\n if (multi) {\n if (current.has(optIdx)) current.delete(optIdx)\n else current.add(optIdx)\n } else {\n current.clear()\n current.add(optIdx)\n }\n next.set(qIdx, current)\n return next\n })\n setUsingCustom((prev) => {\n const next = new Set(prev)\n next.delete(qIdx)\n return next\n })\n }\n\n const handleCustomFocus = (qIdx: number) => {\n if (answered || submitted) return\n setSelections((prev) => {\n const next = new Map(prev)\n next.delete(qIdx)\n return next\n })\n setUsingCustom((prev) => new Set(prev).add(qIdx))\n }\n\n const setCustomText = (qIdx: number, text: string) => {\n if (answered || submitted) return\n setCustomTexts((prev) => new Map(prev).set(qIdx, text))\n }\n\n const getAnswer = (qIdx: number): string | null => {\n const q = data.questions[qIdx]\n if (usingCustom.has(qIdx)) {\n const text = (customTexts.get(qIdx) ?? \"\").trim()\n return text || null\n }\n const sel = selections.get(qIdx)\n if (!sel || sel.size === 0) return null\n return [...sel]\n .sort()\n .map((i) => q.options[i].label)\n .join(\", \")\n }\n\n const allAnswered = data.questions.every((_, i) => getAnswer(i) !== null)\n\n const handleSubmit = () => {\n if (answered || submitted || !allAnswered || !onAnswer) return\n\n const nextAnswerData: AskUserAnswerData = {\n selections: Object.fromEntries(\n Array.from(selections.entries()).map(([qIdx, optionIndexes]) => [\n qIdx,\n Array.from(optionIndexes).sort((a, b) => a - b),\n ]),\n ) as Record<number, number[]>,\n custom: Object.fromEntries(\n Array.from(usingCustom)\n .map((qIdx) => [qIdx, (customTexts.get(qIdx) ?? \"\").trim()] as const)\n .filter(([, text]) => text.length > 0),\n ) as Record<number, string>,\n }\n\n const parts = data.questions.map((q, i) => `- ${q.question} -> ${getAnswer(i)}`)\n const text = `关于需要确认的问题,用户的回答如下:\\n${parts.join(\"\\n\")}`\n setSubmitted(true)\n onAnswer(text, toolCallId, nextAnswerData)\n }\n\n return (\n <div\n className={cn(\n \"blade-chat-ask-card ml-4 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))]\",\n answered\n ? \"max-w-2xl space-y-3 p-3 text-xs text-[hsl(var(--muted-foreground))] opacity-80\"\n : \"max-w-lg space-y-5 p-4 text-sm\",\n )}\n >\n {data.source_loop?.description && (\n <div className=\"rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]\">\n 子智能体「{data.source_loop.description}」在等待你的回答\n </div>\n )}\n {data.questions.map((q, qIdx) => (\n <QuestionCard\n key={q.question}\n question={q}\n qIdx={qIdx}\n answered={answered}\n selected={displayAnswerState.selections.get(qIdx) ?? new Set()}\n isCustom={displayAnswerState.usingCustom.has(qIdx)}\n customText={displayAnswerState.customTexts.get(qIdx) ?? \"\"}\n onToggle={toggleOption}\n onCustomFocus={handleCustomFocus}\n onCustomChange={setCustomText}\n />\n ))}\n\n {!answered && !submitted && onAnswer && (\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!allAnswered}\n className=\"w-full rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {allAnswered ? \"确认\" : \"请先选择一个选项\"}\n </button>\n )}\n\n {submitted && !answered && (\n <button\n type=\"button\"\n disabled\n className=\"flex w-full items-center justify-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] opacity-80\"\n >\n <Loader2 size={14} className=\"animate-spin\" />\n 确认中\n </button>\n )}\n </div>\n )\n}\n\nfunction QuestionCard({\n question,\n qIdx,\n answered,\n selected,\n isCustom,\n customText,\n onToggle,\n onCustomFocus,\n onCustomChange,\n}: {\n question: QuestionItem\n qIdx: number\n answered: boolean\n selected: Set<number>\n isCustom: boolean\n customText: string\n onToggle: (qIdx: number, optIdx: number, multi: boolean) => void\n onCustomFocus: (qIdx: number) => void\n onCustomChange: (qIdx: number, text: string) => void\n}) {\n const multi = question.multiSelect ?? false\n\n return (\n <div>\n <div className={cn(\"flex items-start gap-2\", answered ? \"mb-2\" : \"mb-3\")}>\n <MessageSquareMore\n size={answered ? 12 : 13}\n className=\"mt-0.5 shrink-0 text-[hsl(var(--primary))]\"\n />\n <div\n className={cn(\n \"min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]\",\n answered ? \"text-xs\" : \"text-sm\",\n )}\n >\n <MarkdownContent className=\"blade-chat-prose\">{question.question}</MarkdownContent>\n </div>\n </div>\n\n <div className={cn(\"flex flex-col pl-5\", answered ? \"gap-1\" : \"gap-1.5\")}>\n {question.options.map((opt, optIdx) => {\n const isSel = selected.has(optIdx)\n return (\n <button\n key={opt.label}\n type=\"button\"\n disabled={answered}\n onClick={() => onToggle(qIdx, optIdx, multi)}\n className={cn(\n \"flex items-start gap-2.5 rounded-lg border text-left transition-all\",\n answered ? \"px-2.5 py-1.5\" : \"px-3 py-2.5\",\n isSel && !answered\n ? \"border-[hsl(var(--primary)/0.8)] bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]\"\n : isSel\n ? \"border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.08)] text-[hsl(var(--foreground))]\"\n : \"border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)]\",\n !answered &&\n !isSel &&\n \"hover:border-[hsl(var(--ring)/0.4)] hover:bg-[hsl(var(--accent))]\",\n answered && \"cursor-default opacity-70\",\n )}\n >\n {multi && (\n <div\n className={cn(\n \"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors\",\n isSel && !answered\n ? \"border-[hsl(var(--primary-foreground)/0.6)] bg-[hsl(var(--primary-foreground)/0.2)]\"\n : isSel\n ? \"border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.12)]\"\n : \"border-[hsl(var(--border))]\",\n )}\n >\n {isSel && (\n <Check\n size={9}\n className={\n answered\n ? \"text-[hsl(var(--primary))]\"\n : \"text-[hsl(var(--primary-foreground))]\"\n }\n />\n )}\n </div>\n )}\n <div className=\"min-w-0\">\n <div className={cn(\"font-medium\", answered ? \"text-xs\" : \"text-[13px]\")}>\n {opt.label}\n </div>\n {opt.description && (\n <div\n className={cn(\n \"mt-0.5\",\n answered ? \"text-[11px]\" : \"text-xs\",\n isSel && !answered ? \"opacity-75\" : \"text-[hsl(var(--muted-foreground))]\",\n )}\n >\n {opt.description}\n </div>\n )}\n </div>\n </button>\n )\n })}\n\n {/* 「其他」内联输入 */}\n {answered && !isCustom ? null : (\n <div\n className={cn(\n \"flex items-center gap-2 rounded-lg border transition-all\",\n answered ? \"px-2.5 py-1.5\" : \"px-3 py-2.5\",\n isCustom\n ? \"border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]\"\n : \"border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]\",\n answered && \"cursor-default opacity-70\",\n )}\n >\n <span className=\"shrink-0 text-xs text-[hsl(var(--muted-foreground))]\">其他:</span>\n <input\n type=\"text\"\n value={customText}\n disabled={answered}\n onChange={(e) => onCustomChange(qIdx, e.target.value)}\n onFocus={() => onCustomFocus(qIdx)}\n aria-label=\"自定义回答\"\n placeholder=\"输入你的答案...\"\n className={cn(\n \"min-w-0 flex-1 bg-transparent text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]\",\n answered ? \"text-xs\" : \"text-sm\",\n )}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/** 解析 AskUserQuestion 的工具结果/参数,容错 LLM 把 questions 输出为字符串的情况。 */\nexport function parseAskUserQuestion(\n toolResult: string | null | undefined,\n): AskUserQuestionData | null {\n if (!toolResult) return null\n try {\n const parsed = JSON.parse(toolResult)\n let questions = parsed?.questions\n if (typeof questions === \"string\") {\n try {\n questions = JSON.parse(questions)\n } catch {\n return null\n }\n }\n if (Array.isArray(questions) && questions.length > 0) {\n const normalized = questions.map(normalizeQuestionItem)\n if (normalized.every((question): question is QuestionItem => question !== null)) {\n return { ...parsed, questions: normalized } as AskUserQuestionData\n }\n }\n } catch {\n // 非 JSON,忽略\n }\n return null\n}\n\nfunction normalizeQuestionItem(value: unknown): QuestionItem | null {\n if (!value || typeof value !== \"object\") return null\n const item = value as {\n question?: unknown\n options?: unknown\n multiSelect?: unknown\n multi_select?: unknown\n }\n if (typeof item.question !== \"string\" || !Array.isArray(item.options)) return null\n const options = item.options.map(normalizeOptionItem)\n if (!options.every((option): option is OptionItem => option !== null)) return null\n return {\n question: item.question,\n options,\n multiSelect: item.multiSelect === true || item.multi_select === true,\n }\n}\n\nfunction normalizeOptionItem(value: unknown): OptionItem | null {\n if (typeof value === \"string\") return { label: value, description: \"\" }\n if (!value || typeof value !== \"object\") return null\n const option = value as { label?: unknown; description?: unknown }\n if (typeof option.label !== \"string\") return null\n return {\n label: option.label,\n description: typeof option.description === \"string\" ? option.description : \"\",\n }\n}\n","import { AlertTriangle } from \"lucide-react\"\nimport { Component, type ErrorInfo, type ReactNode } from \"react\"\n\ninterface Props {\n label: string\n details?: string\n resetKey?: string\n children: ReactNode\n}\n\ninterface State {\n componentStack: string\n error: Error | null\n}\n\nfunction getFirstComponentName(componentStack: string): string | null {\n const match = componentStack.match(/\\n\\s+at\\s+([^\\s(]+)/)\n return match?.[1] ?? null\n}\n\n/** 消息级渲染错误边界:单条消息渲染崩溃时降级为提示卡片,不拖垮整个列表。 */\nexport class RenderErrorBoundary extends Component<Props, State> {\n state: State = {\n componentStack: \"\",\n error: null,\n }\n\n static getDerivedStateFromError(error: Error): State {\n return {\n componentStack: \"\",\n error,\n }\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n const componentStack = info.componentStack ?? \"\"\n console.error(`${this.props.label} render error:`, error, info)\n this.setState({ componentStack })\n }\n\n componentDidUpdate(prevProps: Props) {\n if (this.state.error && prevProps.resetKey !== this.props.resetKey) {\n this.setState({ componentStack: \"\", error: null })\n }\n }\n\n render() {\n const { children, details, label } = this.props\n const { componentStack, error } = this.state\n\n if (!error) {\n return children\n }\n\n const componentName = getFirstComponentName(componentStack)\n\n return (\n <div className=\"blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100\">\n <div className=\"flex items-start gap-2\">\n <AlertTriangle className=\"mt-0.5 h-4 w-4 shrink-0 text-amber-300\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"font-medium\">{label}渲染失败</div>\n <div className=\"mt-1 break-words text-xs leading-5 text-amber-100/75\">\n {componentName ? `组件:${componentName}。` : null}\n {error.message || \"发生了未预期的渲染错误。\"}\n </div>\n {details ? (\n <div className=\"mt-1 truncate text-xs text-amber-100/55\">{details}</div>\n ) : null}\n </div>\n </div>\n </div>\n )\n }\n}\n","import type { ChatMessage } from \"@blade-hq/agent-client\"\nimport { getFileParts, getImageParts, getTextContent } from \"@blade-hq/agent-client\"\nimport { FileText, Loader2 } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { MarkdownContent } from \"./MarkdownContent\"\n\nexport type UserChatMessage = ChatMessage & { role: \"user\" }\nexport type ErrorChatMessage = ChatMessage & { role: \"error\" }\n\nexport function isUserMessage(message: ChatMessage): message is UserChatMessage {\n return message.role === \"user\"\n}\n\nexport function isErrorMessage(message: ChatMessage): message is ErrorChatMessage {\n return message.role === \"error\"\n}\n\ninterface UserMessageProps {\n message: UserChatMessage\n className?: string\n}\n\nconst isSending = (message: ChatMessage) => message.status === \"streaming\"\n\n/** 用户消息气泡:文本 + 图片/文件附件展示(不含第一方的预览弹窗与 what-if 徽章)。 */\nexport function UserMessageBubble({ message, className }: UserMessageProps) {\n const text = getTextContent(message.content).trim()\n const fileParts = getFileParts(message.content)\n const imageParts = getImageParts(message.content)\n\n return (\n <div className={cn(\"blade-chat-user-row flex justify-end\", className)}>\n <div className=\"blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3\">\n {imageParts.length > 0 && (\n <div className=\"grid gap-2\">\n {imageParts.map((part) => (\n <img\n key={part.image_url.url}\n src={part.image_url.url}\n alt=\"用户上传的图片\"\n className=\"max-h-64 rounded-xl border border-[hsl(var(--user-msg-border))] object-cover\"\n />\n ))}\n </div>\n )}\n {fileParts.length > 0 && (\n <div className=\"flex flex-col items-end gap-1.5\">\n {fileParts.map((part) => (\n <div\n key={`${part.name}-${part.data.length}`}\n className=\"flex items-center gap-1.5 rounded-lg border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--muted)/0.3)] px-2.5 py-1.5 text-xs text-[hsl(var(--muted-foreground))]\"\n >\n <FileText size={12} className=\"shrink-0\" />\n <span className=\"max-w-56 truncate\" title={part.name}>\n {part.name}\n </span>\n </div>\n ))}\n </div>\n )}\n {text && (\n <div className=\"blade-chat-user-bubble max-w-full rounded-[20px] rounded-br-[6px] border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-[18px] py-[13px] text-sm leading-[1.65] text-[hsl(var(--user-msg-fg))]\">\n <MarkdownContent className=\"blade-chat-prose\">{text}</MarkdownContent>\n </div>\n )}\n {text && isSending(message) && (\n <div className=\"flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]\">\n <Loader2 size={11} className=\"animate-spin\" aria-hidden=\"true\" />\n <span>发送中</span>\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/** 错误消息:居中红色提示条。 */\nexport function ErrorMessageBlock({\n message,\n className,\n}: {\n message: ErrorChatMessage\n className?: string\n}) {\n const text = getTextContent(message.content)\n return (\n <div className={cn(\"blade-chat-error-row flex justify-center\", className)}>\n <div className=\"blade-chat-error-block max-w-[85%] border-l-[3px] border-[hsl(var(--destructive))] px-4 py-1 text-sm leading-7 text-[hsl(var(--destructive))]\">\n {text}\n </div>\n </div>\n )\n}\n","// ============================== React 绑定 ==============================\nexport { BladeProvider, useBladeClient } from \"./context\"\nexport type { BladeProviderProps } from \"./context\"\nexport { useAgentSession } from \"./hooks/use-agent-session\"\nexport type { UseAgentSessionOptions, UseAgentSessionResult } from \"./hooks/use-agent-session\"\n\n// ============================== 聊天界面 ==============================\nexport { ChatView } from \"./components/ChatView\"\nexport type {\n ChatViewClassNames,\n ChatViewProps,\n ChatViewRenderers,\n ChatViewSlots,\n} from \"./components/ChatView\"\nexport { MarkdownContent } from \"./components/MarkdownContent\"\nexport type { MarkdownContentProps } from \"./components/MarkdownContent\"\nexport type { ToolCallRenderer } from \"./components/ToolCallBlock\"\n\n// ============================== 客户端全量转出 ==============================\n// 方便接入方只装一个包:agent-client 的全部公共面从这里原样可用。\nexport * from \"@blade-hq/agent-client\"\n"],"mappings":"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","names":["useEffect","useState","jsx","jsx","jsxs","ChevronDown","useEffect","useMemo","useRef","useState","Loader2","useState","jsx","jsxs","useState","Loader2","Check","useEffect","useMemo","useRef","useState","jsx","jsxs","useRef","useState","useEffect","Check","useMemo","jsx","Check","ChevronRight","Loader2","MessageSquareMore","X","useState","Check","Loader2","useEffect","useMemo","useState","jsx","jsxs","useState","useEffect","useMemo","text","Loader2","Check","jsx","jsxs","useState","Loader2","MessageSquareMore","X","Check","ChevronRight","jsx","jsxs","useState","AlertTriangle","jsx","jsxs","getTextContent","Loader2","jsx","jsxs","getTextContent","Loader2","jsx","jsxs","useMemo","useRef","useEffect","ChevronDown","jsx","jsxs","useState","useEffect"]}
|