@blade-hq/agent-react 2610.0.0-beta.1 → 2610.0.1-alpha.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 +3 -5
- package/dist/components/AgentChat.d.ts +0 -3
- package/dist/components/AssistantTurnBlock.d.ts +1 -1
- package/dist/components/ChatSurface.d.ts +2 -11
- package/dist/components/ChatView.d.ts +0 -1
- package/dist/components/FileCard.d.ts +16 -0
- package/dist/components/MarkdownContent.d.ts +6 -3
- package/dist/components/MessageList.d.ts +3 -10
- package/dist/embed/entry.d.ts +0 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +312 -636
- package/dist/index.js.map +1 -1
- package/dist/lib/media-tags.d.ts +24 -0
- package/dist/style.full.css +1 -1
- package/package.json +2 -2
- package/public-api.md +1 -145
- package/dist/components/PostChatFollowupBlock.d.ts +0 -59
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.tsx","../src/hooks/use-agent-session.ts","../src/hooks/use-replay.ts","../src/hooks/use-llm-chat.ts","../src/components/AgentChat.tsx","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/shared/src/utils.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/defaultAttributes.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/Icon.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/createLucideIcon.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-up-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-up.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/bot.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/brain.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/check.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/chevron-down.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/chevron-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/circle-alert.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/copy.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/download.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file-text.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/film.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/globe.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/layers.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/lightbulb.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/loader-circle.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/lock-keyhole.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/message-square-more.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/message-square.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/play.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/settings-2.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/sparkles.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/square.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/triangle-alert.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/x.ts","../src/lib/utils.ts","../src/components/ReplayBar.tsx","../src/components/ReplayMismatchPrompt.tsx","../src/components/ChatInput.tsx","../src/components/ConnectionBanner.tsx","../src/components/MessageList.tsx","../../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js","../../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js","../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/PostChatFollowupBlock.tsx","../src/components/UserMessageBubble.tsx","../src/components/ChatSurface.tsx","../src/components/LlmChat.tsx","../src/components/LlmAdvancedSettings.tsx","../src/components/ChatView.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 /** 连接或创建失败的错误。 */\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 { DEFAULT_REPLAY_SPEED } from \"@blade-hq/agent-client\"\nimport type { AgentSession, ReplaySpeed, SessionState } from \"@blade-hq/agent-client\"\nimport { useCallback, useEffect, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\n\n/** 回放中用户说的话和录制内容对不上,需要当场决定往哪走。 */\nexport interface ReplayMismatch {\n /** 用户这次实际发出的内容。 */\n actualMessage: string\n /** 源会话里录制的下一句。 */\n expectedMessage: string\n /** keep_replay:按录制内容继续;continue_replay:从这里转为真实运行。 */\n resolve: (decision: \"keep_replay\" | \"continue_replay\") => void\n}\n\nexport interface UseReplayResult {\n /** 当前会话是不是正在回放(转自主运行后为 false)。 */\n isReplay: boolean\n /** 当前播放倍速,非回放会话固定为 1。 */\n speed: ReplaySpeed\n setSpeed: (speed: ReplaySpeed) => Promise<void>\n /** 退出回放,之后的对话交给真实模型运行。 */\n exitToAutonomous: () => Promise<void>\n /** 待决策的输入冲突;处理完自动清空。 */\n mismatch: ReplayMismatch | null\n /** 能不能改回放(改回放是 owner 专属)。只读身份下仍然显示\"在回放\",但不能操作。 */\n canControl: boolean\n\n /** 当前会话能否作为回放来源(并行子智能体等场景不支持)。 */\n canReplay: boolean\n /** canReplay 为 false 时的原因,可直接展示。 */\n unsupportedReason: string | null\n /** 从当前会话派生一个回放会话,返回新会话 id。 */\n startReplay: (speed?: ReplaySpeed) => Promise<string>\n isStarting: boolean\n /** 最近一次回放操作失败的原因。 */\n error: Error | null\n}\n\n/** 正在创建回放会话的源会话;按会话共享,多个 useReplay 实例也只会建出一个。 */\nconst replayCreating = new Set<string>()\n\n/**\n * 会话回放(演示 / 彩排模式)。\n *\n * 回放会话不会自动播放:用户照常发消息,只要这句和源会话录制的下一句意图一致,\n * 服务端就按倍速重放当时的回复和工具调用,完全不调用模型;对不上就抛出\n * `mismatch` 交给界面决定。\n *\n * 同一个 hook 覆盖两种身份——传入的会话既可能是回放会话(isReplay / setSpeed /\n * exitToAutonomous / mismatch),也可能是准备被回放的源会话(canReplay /\n * startReplay)。\n *\n * 回放状态本身住在 `SessionState.replay` 里(非 React 宿主直接订阅它即可),\n * 这里只做 React 绑定:订阅快照 + 接管冲突 + 拉一次 preview。\n *\n * **注册即接管**:底层的 `replayMismatch` 只交给最先注册的处理器。调用本 hook\n * 就意味着由它接管冲突;不调用则维持 SDK 默认行为——直接转为真实运行。\n */\nexport function useReplay(session: AgentSession | null): UseReplayResult {\n const client = useBladeClient()\n const sessionId = session?.sessionId ?? null\n\n const [replay, setReplay] = useState<SessionState[\"replay\"]>(null)\n const [viewerRole, setViewerRole] = useState<SessionState[\"viewerRole\"]>(null)\n // 运行中的会话不能当回放素材(服务端也这么判),跑完要重新问一次能不能回放。\n const [sourceRunning, setSourceRunning] = useState(false)\n const [mismatch, setMismatch] = useState<ReplayMismatch | null>(null)\n // 加载中不能当\"支持\"用:慢网络下一打开就点,会绕过这道检查发出必然失败的请求。\n const [preview, setPreview] = useState<\n { status: \"loading\" } | { status: \"ready\"; supported: boolean; reason: string | null }\n >({ status: \"loading\" })\n const [isStarting, setIsStarting] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n // 还没开始回放时倍速只是个本地选择,startReplay 时才带给服务端。\n const [draftSpeed, setDraftSpeed] = useState<ReplaySpeed>(DEFAULT_REPLAY_SPEED)\n\n // 回放状态跟着会话快照走;AgentSession 在 bootstrap 时就填好了初始值。\n useEffect(() => {\n if (!session) {\n setReplay(null)\n setViewerRole(null)\n setSourceRunning(false)\n return\n }\n const pull = () => {\n const state = session.getState()\n setReplay(state.replay)\n setViewerRole(state.viewerRole)\n setSourceRunning(state.isStreaming || state.status === \"running\")\n }\n pull()\n return session.subscribe(pull)\n }, [session])\n\n // 换会话才重置这些:倍速是跟着源会话走的选择,上一个会话选的 1x 不该带过来;\n // 冲突和错误同理。在渲染期比较而不是用 effect——effect 会先渲染一帧旧值,\n // 而且不能和下面的 preview 查询合并(那个还要跟着运行状态重跑,会把用户\n // 刚选的倍速冲掉)。\n const [lastSessionId, setLastSessionId] = useState(sessionId)\n if (sessionId !== lastSessionId) {\n setLastSessionId(sessionId)\n setMismatch(null)\n setError(null)\n setDraftSpeed(DEFAULT_REPLAY_SPEED)\n }\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: sourceRunning 跨过\"运行中\"这条线就是重新查询的触发条件——这一轮跑完了本该能回放,新一轮开始了就不该再放行\n useEffect(() => {\n setPreview({ status: \"loading\" })\n if (!sessionId) return\n let alive = true\n client.sessions\n .getReplayPreview(sessionId)\n .then((result) => {\n if (!alive) return\n setPreview({\n status: \"ready\",\n supported: result.supported,\n reason: result.reason ?? null,\n })\n })\n .catch(() => {\n // 查不到就放行,让服务端在真正创建时把关——但那是\"问过了没结论\",\n // 和\"还没问\"不是一回事,后者必须继续禁用。\n if (alive) setPreview({ status: \"ready\", supported: true, reason: null })\n })\n return () => {\n alive = false\n }\n }, [client, sessionId, sourceRunning])\n\n useEffect(() => {\n if (!session) return\n return session.on(\"replayMismatch\", ({ actualMessage, expectedMessage, respond }) => {\n setMismatch({\n actualMessage: actualMessage ?? \"\",\n expectedMessage: expectedMessage ?? \"\",\n resolve: (decision) => {\n setMismatch(null)\n respond(decision)\n },\n })\n })\n }, [session])\n\n const isReplay = replay?.isReplay ?? false\n\n const setSpeed = useCallback(\n async (next: ReplaySpeed) => {\n setDraftSpeed(next)\n if (!session) return\n setError(null)\n try {\n await session.setReplaySpeed(next)\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n },\n [session],\n )\n\n const exitToAutonomous = useCallback(async () => {\n if (!session) return\n setError(null)\n // 冲突还没决定就点退出:那条消息已经发出去、输入框锁着,只改回放状态的话\n // 卡片会留在屏幕上、对话卡死。\"不回放了\"对应的决定就是让它真的运行,\n // 而 continue_replay 本身就会转自主,不用再 PATCH 一次。\n const pending = mismatch\n if (pending) {\n pending.resolve(\"continue_replay\")\n return\n }\n try {\n await session.exitReplay()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n }, [session, mismatch])\n\n const startReplay = useCallback(\n async (speed?: ReplaySpeed) => {\n if (!sessionId) throw new Error(\"当前没有可回放的会话\")\n // 不能只看 isStarting:state 在同一次渲染里还没更新,多个组件各挂一个\n // hook 实例时也各有各的 state。\n if (replayCreating.has(sessionId)) throw new Error(\"正在创建回放会话,请稍候\")\n replayCreating.add(sessionId)\n setIsStarting(true)\n setError(null)\n try {\n const result = await client.sessions.startReplaySession(sessionId, speed ?? draftSpeed)\n return result.session_id\n } catch (err) {\n const failure = err instanceof Error ? err : new Error(String(err))\n setError(failure)\n throw failure\n } finally {\n replayCreating.delete(sessionId)\n setIsStarting(false)\n }\n },\n [client, sessionId, draftSpeed],\n )\n\n return {\n isReplay,\n speed: isReplay ? (replay?.speed ?? 1) : draftSpeed,\n setSpeed,\n exitToAutonomous,\n mismatch,\n canControl: viewerRole !== \"viewer\",\n canReplay:\n Boolean(sessionId) &&\n preview.status === \"ready\" &&\n preview.supported &&\n viewerRole !== \"viewer\",\n unsupportedReason:\n preview.status === \"ready\" && !preview.supported ? preview.reason : null,\n startReplay,\n isStarting,\n error,\n }\n}\n","import type { ChatMessage, ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { useCallback, useMemo, useRef, useState } from \"react\"\n\n/** OpenAI 标准的工具声明,原样发给模型服务。 */\nexport interface ChatCompletionTool {\n type: \"function\"\n function: {\n name: string\n description?: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface LlmToolCall {\n id: string\n name: string\n /** 模型生成的 JSON 字符串,是不可信输入,用之前自己校验。 */\n arguments: string\n}\n\nexport interface LlmChatOptions {\n /** OpenAI 兼容根地址,如 `/api/llm`(自己后端的透传路径)或 `http://host:30000/v1`。 */\n baseURL: string\n model: string\n /**\n * 直连模型服务时才需要。默认形态是让 `baseURL` 指向自己的后端,由后端补密钥转发,\n * 浏览器不持有密钥。\n */\n apiKey?: string\n headers?: Record<string, string>\n /** 系统提示词,合成到消息最前面。 */\n system?: string\n /** 每次带上最近多少轮,默认 12。越长越贵,也越容易超上下文。 */\n historyTurns?: number\n temperature?: number\n /** 透传给模型服务的额外字段。 */\n extraBody?: Record<string, unknown>\n tools?: ChatCompletionTool[]\n /** 执行模型请求的工具,返回值会被序列化后回喂给模型。 */\n onToolCall?: (call: LlmToolCall) => Promise<unknown>\n /** 工具最多来回几轮,默认 4,防止模型绕圈烧 token。 */\n maxToolRounds?: number\n fetchImpl?: typeof fetch\n}\n\nexport interface UseLlmChatResult {\n messages: ChatMessage[]\n isStreaming: boolean\n /** 出错原因;下一次发送会清掉。 */\n error: string | null\n send: (text: string) => Promise<boolean>\n stop: () => void\n reset: () => void\n}\n\ninterface OpenAiToolCall {\n id: string\n type: \"function\"\n function: { name: string; arguments: string }\n}\n\ntype OpenAiMessage =\n | { role: \"user\" | \"assistant\"; content: string; tool_calls?: OpenAiToolCall[] }\n | { role: \"tool\"; tool_call_id: string; content: string }\n\nconst DEFAULT_HISTORY_TURNS = 12\nconst DEFAULT_MAX_TOOL_ROUNDS = 4\n\n/** 补 id 用的序号。全局递增即可,只要同一段对话里不重复。 */\nlet syntheticIdSeq = 0\n\n/**\n * 纯 LLM 对话的状态机,协议就是 **OpenAI Chat Completions**——任何兼容实现都能直接接,\n * 不需要为这个组件专门写后端。\n *\n * 工具调用走 OpenAI 标准的多轮流程:模型返回 tool_calls → 调 onToolCall 执行 →\n * 结果作为 role:\"tool\" 消息回喂 → 再请求一次。\n */\nexport function useLlmChat(options: LlmChatOptions): UseLlmChatResult {\n const [history, setHistory] = useState<OpenAiMessage[]>([])\n const [streamingText, setStreamingText] = useState<string | null>(null)\n const [streamingCalls, setStreamingCalls] = useState<OpenAiToolCall[]>([])\n const [failedToolIds, setFailedToolIds] = useState<string[]>([])\n const [error, setError] = useState<string | null>(null)\n const [isStreaming, setIsStreaming] = useState(false)\n const abortRef = useRef<AbortController | null>(null)\n // reset 会把对话清空;正在跑的那轮不能事后再往里补消息\n const generationRef = useRef(0)\n // 用 ref 读:send 不该因为消息或配置变化而换引用,否则宿主的 useEffect 会连环重跑\n const historyRef = useRef<OpenAiMessage[]>([])\n const optionsRef = useRef(options)\n optionsRef.current = options\n historyRef.current = history\n\n const stop = useCallback(() => {\n abortRef.current?.abort()\n abortRef.current = null\n }, [])\n\n const reset = useCallback(() => {\n generationRef.current += 1\n stop()\n setHistory([])\n setStreamingText(null)\n setStreamingCalls([])\n setFailedToolIds([])\n setError(null)\n setIsStreaming(false)\n }, [stop])\n\n const send = useCallback(async (text: string): Promise<boolean> => {\n const content = text.trim()\n if (!content || abortRef.current) return false\n const opts = optionsRef.current\n const maxRounds = opts.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS\n\n const commit = (message: OpenAiMessage) => {\n historyRef.current = [...historyRef.current, message]\n setHistory(historyRef.current)\n }\n\n setError(null)\n setIsStreaming(true)\n setStreamingText(\"\")\n setStreamingCalls([])\n commit({ role: \"user\", content })\n // 停止时要把已经吐出来的部分留在对话里,所以本地也累一份——state 在闭包里读不到\n let partial = \"\"\n\n const controller = new AbortController()\n abortRef.current = controller\n const generation = generationRef.current\n try {\n for (let round = 0; ; round += 1) {\n const result = await streamOnce(opts, historyRef.current, controller.signal, {\n onText: (chunk) => {\n partial += chunk\n setStreamingText((prev) => `${prev ?? \"\"}${chunk}`)\n },\n onToolCalls: (calls) => setStreamingCalls(calls),\n })\n\n const assistant: OpenAiMessage = {\n role: \"assistant\",\n content: result.content,\n ...(result.toolCalls.length ? { tool_calls: result.toolCalls } : {}),\n }\n setStreamingText(null)\n setStreamingCalls([])\n commit(assistant)\n\n if (!result.toolCalls.length) return true\n // 提交过的 assistant 带着 tool_calls,就必须有对应的 role:\"tool\" 回复,\n // 否则下一轮请求里是一组没人应答的工具调用,严格的 OpenAI 兼容服务直接 400,\n // 这段对话从此就废了。提前收尾时补上说明,让模型知道没执行。\n // 没执行成功的要标成失败,否则界面给个绿勾,等于骗人说查过了\n const bail = (reason: string) => {\n for (const call of result.toolCalls) {\n commit({ role: \"tool\", tool_call_id: call.id, content: JSON.stringify({ error: reason }) })\n }\n setFailedToolIds((prev) => [...prev, ...result.toolCalls.map((call) => call.id)])\n }\n if (!opts.onToolCall) {\n bail(\"这个应用没有提供工具执行入口\")\n return true\n }\n if (round >= maxRounds) {\n bail(`工具调用已达上限 ${maxRounds} 轮,没有执行`)\n setError(`工具调用超过 ${maxRounds} 轮仍未给出结论,已停下。`)\n return false\n }\n\n for (const call of result.toolCalls) {\n let output: string\n try {\n const value = await opts.onToolCall({\n id: call.id,\n name: call.function.name,\n arguments: call.function.arguments,\n })\n output = typeof value === \"string\" ? value : JSON.stringify(value ?? null)\n } catch (err) {\n // 执行失败也要回喂给模型,让它自己决定换个方式还是告诉用户\n output = JSON.stringify({ error: err instanceof Error ? err.message : String(err) })\n setFailedToolIds((prev) => [...prev, call.id])\n }\n commit({ role: \"tool\", tool_call_id: call.id, content: output })\n }\n setStreamingText(\"\")\n }\n } catch (err) {\n setStreamingText(null)\n setStreamingCalls([])\n if (controller.signal.aborted) {\n // reset 也走 abort,但它是\"把这段对话清空\",不能再往里补消息\n if (generation !== generationRef.current) return false\n // 保留已经看到的内容:清成\"(已停止)\"等于把用户刚读到的答案抹掉,\n // 下一轮的上下文也跟着丢了\n commit({ role: \"assistant\", content: partial ? `${partial}(已停止)` : \"(已停止)\" })\n return false\n }\n // 和\"停止\"一样保留已经吐出来的部分:网关在流中途下发 error(限流、上游断连)\n // 时把半屏答案抹掉,用户刚读的内容没了,重试时模型也看不到这段上下文\n if (partial && generation === generationRef.current) {\n commit({ role: \"assistant\", content: partial })\n }\n setError(err instanceof Error && err.message ? err.message : \"对话失败,请重试\")\n return false\n } finally {\n if (abortRef.current === controller) abortRef.current = null\n setIsStreaming(false)\n }\n }, [])\n\n const messages = useMemo(\n () => toChatMessages(history, streamingText, streamingCalls, failedToolIds),\n [history, streamingText, streamingCalls, failedToolIds],\n )\n\n return { messages, isStreaming, error, send, stop, reset }\n}\n\ninterface StreamResult {\n content: string\n toolCalls: OpenAiToolCall[]\n}\n\n/** 发一次请求并消费流;返回这一轮模型给出的完整内容与工具调用。 */\nasync function streamOnce(\n options: LlmChatOptions,\n history: OpenAiMessage[],\n signal: AbortSignal,\n handlers: { onText: (chunk: string) => void; onToolCalls: (calls: OpenAiToolCall[]) => void },\n): Promise<StreamResult> {\n const {\n baseURL,\n model,\n apiKey,\n headers,\n system,\n historyTurns = DEFAULT_HISTORY_TURNS,\n temperature,\n extraBody,\n tools,\n fetchImpl,\n } = options\n\n const trimmed = trimHistory(history, historyTurns)\n const payload = {\n model,\n stream: true,\n messages: system ? [{ role: \"system\", content: system }, ...trimmed] : trimmed,\n ...(tools?.length ? { tools } : {}),\n ...(temperature === undefined ? {} : { temperature }),\n ...extraBody,\n }\n\n const doFetch = fetchImpl ?? globalThis.fetch\n const url = `${baseURL.replace(/\\/$/, \"\")}/chat/completions`\n let response: Response\n try {\n response = await doFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n ...headers,\n },\n body: JSON.stringify(payload),\n signal,\n })\n } catch (err) {\n if (signal.aborted) throw err\n // 浏览器给的原始信息是 \"Failed to fetch\",看不出是哪台服务连不上。\n // 地址填错是最常见的故障,必须把地址显示出来。\n throw new Error(`连不上模型服务 ${url}:${err instanceof Error ? err.message : String(err)}`)\n }\n if (!response.ok) {\n throw new Error(await describeHttpError(response))\n }\n if (!response.body) {\n throw new Error(\"模型服务没有返回流式响应\")\n }\n\n let content = \"\"\n const calls: OpenAiToolCall[] = []\n for await (const chunk of readSse(response.body, signal)) {\n if (chunk.error) {\n throw new Error(chunk.error)\n }\n if (chunk.text) {\n content += chunk.text\n handlers.onText(chunk.text)\n }\n if (chunk.toolCallDeltas) {\n for (const delta of chunk.toolCallDeltas) {\n // 工具调用按 index 分片下发:id 与函数名通常只在第一片,参数逐片拼\n const index = delta.index ?? 0\n const existing = calls[index] ?? { id: \"\", type: \"function\" as const, function: { name: \"\", arguments: \"\" } }\n calls[index] = {\n id: delta.id ?? existing.id,\n type: \"function\",\n function: {\n name: delta.function?.name ?? existing.function.name,\n arguments: `${existing.function.arguments}${delta.function?.arguments ?? \"\"}`,\n },\n }\n }\n handlers.onToolCalls(calls.filter(Boolean).map((call) => ({ ...call })))\n }\n }\n const normalized = calls\n .filter(Boolean)\n // 流里会出现只有 arguments、没有函数名的分片;累积后如果仍然没名字,说明它不是\n // 一次真的调用(网关分片的产物)。留着会让宿主的 onToolCall 收到空名字,\n // 只能抛\"没有这个函数\",整轮就废了。\n .filter((call) => call.function.name)\n // 有的实现不给 id,补一个,否则回喂时对不上。序号必须跨轮唯一:\n // 每轮都从 call_0 开始的话,工具结果和失败标记会串到上一轮的卡片上。\n .map((call) => {\n if (call.id) return call\n syntheticIdSeq += 1\n return { ...call, id: `call_auto_${syntheticIdSeq}` }\n })\n return { content, toolCalls: normalized }\n}\n\n/** 按轮数截断历史,但不能把工具消息与它所属的助手消息切散。 */\nfunction trimHistory(history: OpenAiMessage[], historyTurns: number): OpenAiMessage[] {\n const userIndexes = history.reduce<number[]>((acc, msg, i) => {\n if (msg.role === \"user\") acc.push(i)\n return acc\n }, [])\n if (userIndexes.length <= historyTurns) return history\n return history.slice(userIndexes[userIndexes.length - historyTurns])\n}\n\nasync function describeHttpError(response: Response): Promise<string> {\n const raw = await response.text().catch(() => \"\")\n try {\n const parsed = JSON.parse(raw) as { error?: { message?: string } | string; detail?: unknown }\n const message =\n typeof parsed.error === \"string\"\n ? parsed.error\n : (parsed.error?.message ??\n (typeof parsed.detail === \"string\" ? parsed.detail : undefined))\n if (message) return `模型服务返回 ${response.status}:${message}`\n } catch {\n // 不是 JSON 就用原文\n }\n const snippet = raw.trim().slice(0, 120)\n return snippet ? `模型服务返回 ${response.status}:${snippet}` : `模型服务返回 ${response.status}`\n}\n\ninterface StreamChunk {\n text?: string\n toolCallDeltas?: Array<{\n index?: number\n id?: string\n function?: { name?: string; arguments?: string }\n }>\n error?: string\n}\n\n/**\n * 读 OpenAI 的流式响应。事件之间用空行分隔,`data: [DONE]` 收尾。\n * 必须按 `\\n\\n` 缓冲——一个事件可能横跨两个网络分片,这是实测踩过的。\n */\nasync function* readSse(\n stream: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n): AsyncGenerator<StreamChunk> {\n const reader = stream.getReader()\n const decoder = new TextDecoder()\n let buffer = \"\"\n try {\n while (!signal.aborted) {\n const { done, value } = await reader.read()\n if (done) {\n // 不少兼容网关最后一个 data: 后面不补空行、也不发 [DONE]。不收尾解析的话,\n // 回答的最后一段会静默丢失;丢的要是工具参数的最后一片,宿主拿到的就是\n // 截断的 JSON。\n const tail = parseSse(buffer)\n if (tail && tail !== \"done\") yield tail\n break\n }\n // SSE 规范允许 CRLF;只认 \\n\\n 的话,用 CRLF 的服务永远解析不出事件。\n // 归一必须在拼接之后做:网络分片可能正好切在 \\r 和 \\n 中间,先替换再拼会\n // 留下一个 \\n\\r\\n,边界就再也认不出来了。\n buffer = `${buffer}${decoder.decode(value, { stream: true })}`.replace(/\\r\\n/g, \"\\n\")\n let boundary = buffer.indexOf(\"\\n\\n\")\n while (boundary !== -1) {\n const chunk = parseSse(buffer.slice(0, boundary))\n buffer = buffer.slice(boundary + 2)\n if (chunk === \"done\") return\n if (chunk) yield chunk\n boundary = buffer.indexOf(\"\\n\\n\")\n }\n }\n } finally {\n reader.cancel().catch(() => {})\n }\n}\n\nfunction parseSse(raw: string): StreamChunk | \"done\" | null {\n const dataLines: string[] = []\n for (const line of raw.split(\"\\n\")) {\n if (line.startsWith(\"data:\")) dataLines.push(line.slice(5).trim())\n }\n if (!dataLines.length) return null\n const data = dataLines.join(\"\\n\")\n if (data === \"[DONE]\") return \"done\"\n let parsed: {\n choices?: Array<{ delta?: { content?: string; tool_calls?: StreamChunk[\"toolCallDeltas\"] } }>\n error?: { message?: string } | string\n }\n try {\n parsed = JSON.parse(data)\n } catch {\n return null\n }\n if (parsed.error) {\n const message = typeof parsed.error === \"string\" ? parsed.error : parsed.error.message\n return { error: message || \"模型服务返回了错误\" }\n }\n const delta = parsed.choices?.[0]?.delta\n if (!delta) return null\n return { text: delta.content, toolCallDeltas: delta.tool_calls }\n}\n\n/** OpenAI 形状 → SDK 的 ChatMessage:工具结果并进对应的 tool_calls,不单独成条。 */\nfunction toChatMessages(\n history: OpenAiMessage[],\n streamingText: string | null,\n streamingCalls: OpenAiToolCall[],\n failedToolIds: string[],\n): ChatMessage[] {\n const results = new Map<string, string>()\n for (const msg of history) {\n if (msg.role === \"tool\") results.set(msg.tool_call_id, msg.content)\n }\n\n const messages: ChatMessage[] = []\n for (const msg of history) {\n if (msg.role === \"tool\") continue\n if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: msg.content, status: \"completed\" })\n continue\n }\n messages.push({\n role: \"assistant\",\n content: msg.content,\n status: \"completed\",\n ...(msg.tool_calls?.length\n ? { tool_calls: msg.tool_calls.map((call) => toToolCallInfo(call, results, failedToolIds)) }\n : {}),\n })\n }\n\n if (streamingText !== null) {\n messages.push({\n role: \"assistant\",\n content: streamingText,\n status: \"streaming\",\n ...(streamingCalls.length\n ? { tool_calls: streamingCalls.map((call) => toToolCallInfo(call, results, failedToolIds)) }\n : {}),\n })\n }\n return messages\n}\n\nfunction toToolCallInfo(\n call: OpenAiToolCall,\n results: Map<string, string>,\n failedToolIds: string[],\n): ToolCallInfo {\n const result = results.get(call.id)\n const failed = failedToolIds.includes(call.id)\n return {\n id: call.id,\n name: call.function.name,\n arguments: call.function.arguments,\n ...(result === undefined ? {} : { result }),\n status: result === undefined ? \"pending\" : failed ? \"error\" : \"done\",\n }\n}\n","import type {\n AgentSession,\n BladeClient,\n CreateSessionRequest,\n ResultFeedback,\n} from \"@blade-hq/agent-client\"\nimport { BladeApiError, latestPostChatFollowup } from \"@blade-hq/agent-client\"\nimport { LockKeyhole } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\nimport { useAgentSession } from \"../hooks/use-agent-session\"\nimport { useReplay } from \"../hooks/use-replay\"\nimport { ReplayBar } from \"./ReplayBar\"\nimport { ReplayMismatchPrompt } from \"./ReplayMismatchPrompt\"\nimport { cn } from \"../lib/utils\"\nimport { ChatSurface, themeAttr, type ChatPresentationProps } from \"./ChatSurface\"\nimport type { FollowupInteractionEvent } from \"./PostChatFollowupBlock\"\n\nexport interface AgentChatProps extends ChatPresentationProps {\n /** follow-up、成果和评分交互;SDK 不绑定任何分析厂商。 */\n onFollowupInteraction?: (event: FollowupInteractionEvent) => void\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}\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/**\n * 智能体模式的聊天界面:连接(或新建)一个 Blade 会话,渲染消息流与输入框。\n * 状态全部来自 useAgentSession,不依赖任何全局 store。\n */\nexport function AgentChat(props: AgentChatProps) {\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\n data-theme={themeAttr(props.theme)}\n className={cn(\"blade-chat flex min-h-0 flex-1 flex-col\", props.classNames?.root)}\n >\n <LoginCard\n client={client}\n onLoggedIn={() => {\n setNeedLogin(false)\n setAttempt((value) => value + 1)\n }}\n />\n </div>\n )\n }\n\n return <ChatSessionView key={attempt} {...props} onUnauthorized={() => setNeedLogin(true)} />\n}\n\nfunction ChatSessionView({\n sessionId,\n createOptions,\n onSessionCreated,\n onSessionReady,\n commands,\n classNames,\n renderers,\n slots,\n placeholder,\n theme,\n onFollowupInteraction,\n onUnauthorized,\n}: AgentChatProps & { onUnauthorized: () => void }) {\n const client = useBladeClient()\n const { session, state, error } = useAgentSession(sessionId, {\n createOptions,\n onSessionCreated,\n })\n // 开箱即用的聊天界面自带回放处理:不接的话,用它打开回放会话就是个普通聊天\n // ——看不出在回放、退不出去,说错话还会走\"无人监听\"兜底静默转成真实运行。\n const replay = useReplay(session)\n const [stopRequested, setStopRequested] = useState(false)\n const [inputText, setInputText] = useState(\"\")\n const [resultFeedback, setResultFeedback] = useState<ResultFeedback[]>([])\n const resolvedSessionId = session?.sessionId\n const isViewer = state?.viewerRole === \"viewer\"\n\n useEffect(() => {\n setResultFeedback([])\n if (!resolvedSessionId || isViewer) return\n let cancelled = false\n void client.sessions.listResultFeedback(resolvedSessionId).then(\n (items) => {\n if (cancelled) return\n setResultFeedback((current) => {\n const merged = new Map(items.map((item) => [item.assistant_entry_id, item]))\n for (const item of current) {\n const fetched = merged.get(item.assistant_entry_id)\n if (!fetched || fetched.updated_at < item.updated_at) {\n merged.set(item.assistant_entry_id, item)\n }\n }\n return [...merged.values()]\n })\n },\n () => {},\n )\n return () => {\n cancelled = true\n }\n }, [client, isViewer, resolvedSessionId])\n const resultFeedbackByEntry = useMemo(\n () => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),\n [resultFeedback],\n )\n const handleResultFeedbackSaved = useCallback((saved: ResultFeedback) => {\n setResultFeedback((current) => [\n ...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),\n saved,\n ])\n }, [])\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 const isStreaming = state?.isStreaming ?? false\n const isStopping = stopRequested && isStreaming\n const connectError =\n error && !isUnauthorizedError(error)\n ? error.message || \"连接失败,请稍后重试\"\n : null\n // 回放控制失败(改倍速 / 退出)也要让人看见,否则点了毫无反馈。\n const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null\n\n const handleSend = (text: string): Promise<boolean> => {\n setStopRequested(false)\n return session?.send(text, { mode: state?.mode ?? undefined }) ?? Promise.resolve(false)\n }\n\n const handleStop = () => {\n setStopRequested(true)\n void session?.stop()\n }\n\n return (\n <ChatSurface\n theme={theme}\n onFollowupInteraction={onFollowupInteraction}\n classNames={classNames}\n renderers={renderers}\n slots={slots}\n placeholder={placeholder}\n connection={state?.connection ?? \"connecting\"}\n banner={\n <>\n <ReplayBar\n isReplay={replay.isReplay}\n speed={replay.speed}\n canControl={replay.canControl}\n onSpeedChange={(next) => void replay.setSpeed(next)}\n onExit={() => void replay.exitToAutonomous()}\n />\n <ReplayMismatchPrompt mismatch={replay.mismatch} />\n </>\n }\n errorMessage={errorMessage}\n messages={state?.messages ?? []}\n postChatFollowup={latestPostChatFollowup(state?.turns ?? [])}\n resultFeedbackByEntry={resultFeedbackByEntry}\n onResultFeedbackSaved={handleResultFeedbackSaved}\n isStreaming={isStreaming}\n isStopping={isStopping}\n inputText={inputText}\n onInputChange={setInputText}\n onSuggestion={setInputText}\n onSend={handleSend}\n onStop={handleStop}\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 // 用 session 自己持有的 id,不是 sessionId prop——后者是可选的,\n // 自动建会话时一直是 undefined,文件卡片会永远不可用。\n sessionId={resolvedSessionId}\n isViewer={isViewer}\n />\n )\n}\n","import { CamelToPascal } from './utility-types';\n\n/**\n * Converts string to kebab case\n *\n * @param {string} string\n * @returns {string} A kebabized string\n */\nexport const toKebabCase = (string: string) =>\n string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n\n/**\n * Converts string to camel case\n *\n * @param {string} string\n * @returns {string} A camelized string\n */\nexport const toCamelCase = <T extends string>(string: T) =>\n string.replace(/^([A-Z])|[\\s-_]+(\\w)/g, (match, p1, p2) =>\n p2 ? p2.toUpperCase() : p1.toLowerCase(),\n );\n\n/**\n * Converts string to pascal case\n *\n * @param {string} string\n * @returns {string} A pascalized string\n */\nexport const toPascalCase = <T extends string>(string: T): CamelToPascal<T> => {\n const camelCase = toCamelCase(string);\n\n return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal<T>;\n};\n\n/**\n * Merges classes into a single string\n *\n * @param {array} classes\n * @returns {string} A string of classes\n */\nexport const mergeClasses = <ClassType = string | undefined | null>(...classes: ClassType[]) =>\n classes\n .filter((className, index, array) => {\n return (\n Boolean(className) &&\n (className as string).trim() !== '' &&\n array.indexOf(className) === index\n );\n })\n .join(' ')\n .trim();\n","export default {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n};\n","import { createElement, forwardRef } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport { IconNode, LucideProps } from './types';\nimport { mergeClasses } from '@lucide/shared';\n\ninterface IconComponentProps extends LucideProps {\n iconNode: IconNode;\n}\n\n/**\n * Lucide icon component\n *\n * @component Icon\n * @param {object} props\n * @param {string} props.color - The color of the icon\n * @param {number} props.size - The size of the icon\n * @param {number} props.strokeWidth - The stroke width of the icon\n * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width\n * @param {string} props.className - The class name of the icon\n * @param {IconNode} props.children - The children of the icon\n * @param {IconNode} props.iconNode - The icon node of the icon\n *\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst Icon = forwardRef<SVGSVGElement, IconComponentProps>(\n (\n {\n color = 'currentColor',\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = '',\n children,\n iconNode,\n ...rest\n },\n ref,\n ) => {\n return createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,\n className: mergeClasses('lucide', className),\n ...rest,\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n );\n },\n);\n\nexport default Icon;\n","import { createElement, forwardRef } from 'react';\nimport { mergeClasses, toKebabCase } from '@lucide/shared';\nimport { IconNode, LucideProps } from './types';\nimport Icon from './Icon';\n\n/**\n * Create a Lucide icon component\n * @param {string} iconName\n * @param {array} iconNode\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst createLucideIcon = (iconName: string, iconNode: IconNode) => {\n const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>\n createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),\n ...props,\n }),\n );\n\n Component.displayName = `${iconName}`;\n\n return Component;\n};\n\nexport default createLucideIcon;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowRight = createLucideIcon('ArrowRight', [\n ['path', { d: 'M5 12h14', key: '1ays0h' }],\n ['path', { d: 'm12 5 7 7-7 7', key: 'xquz4c' }],\n]);\n\nexport default ArrowRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowUpRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-up-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowUpRight = createLucideIcon('ArrowUpRight', [\n ['path', { d: 'M7 7h10v10', key: '1tivn9' }],\n ['path', { d: 'M7 17 17 7', key: '1vkiza' }],\n]);\n\nexport default ArrowUpRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowUp\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-up\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowUp = createLucideIcon('ArrowUp', [\n ['path', { d: 'm5 12 7-7 7 7', key: 'hav0vg' }],\n ['path', { d: 'M12 19V5', key: 'x0mq9r' }],\n]);\n\nexport default ArrowUp;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Bot\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/bot\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Bot = createLucideIcon('Bot', [\n ['path', { d: 'M12 8V4H8', key: 'hb8ula' }],\n ['rect', { width: '16', height: '12', x: '4', y: '8', rx: '2', key: 'enze0r' }],\n ['path', { d: 'M2 14h2', key: 'vft8re' }],\n ['path', { d: 'M20 14h2', key: '4cs60a' }],\n ['path', { d: 'M15 13v2', key: '1xurst' }],\n ['path', { d: 'M9 13v2', key: 'rq6x2g' }],\n]);\n\nexport default Bot;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Brain\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/brain\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Brain = createLucideIcon('Brain', [\n [\n 'path',\n {\n d: 'M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z',\n key: 'l5xja',\n },\n ],\n [\n 'path',\n {\n d: 'M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z',\n key: 'ep3f8r',\n },\n ],\n ['path', { d: 'M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4', key: '1p4c4q' }],\n ['path', { d: 'M17.599 6.5a3 3 0 0 0 .399-1.375', key: 'tmeiqw' }],\n ['path', { d: 'M6.003 5.125A3 3 0 0 0 6.401 6.5', key: '105sqy' }],\n ['path', { d: 'M3.477 10.896a4 4 0 0 1 .585-.396', key: 'ql3yin' }],\n ['path', { d: 'M19.938 10.5a4 4 0 0 1 .585.396', key: '1qfode' }],\n ['path', { d: 'M6 18a4 4 0 0 1-1.967-.516', key: '2e4loj' }],\n ['path', { d: 'M19.967 17.484A4 4 0 0 1 18 18', key: '159ez6' }],\n]);\n\nexport default Brain;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Check\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/check\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Check = createLucideIcon('Check', [['path', { d: 'M20 6 9 17l-5-5', key: '1gmf2c' }]]);\n\nexport default Check;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ChevronDown\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-down\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronDown = createLucideIcon('ChevronDown', [\n ['path', { d: 'm6 9 6 6 6-6', key: 'qrunsl' }],\n]);\n\nexport default ChevronDown;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ChevronRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronRight = createLucideIcon('ChevronRight', [\n ['path', { d: 'm9 18 6-6-6-6', key: 'mthhwq' }],\n]);\n\nexport default ChevronRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name CircleAlert\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/circle-alert\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst CircleAlert = createLucideIcon('CircleAlert', [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n ['line', { x1: '12', x2: '12', y1: '8', y2: '12', key: '1pkeuh' }],\n ['line', { x1: '12', x2: '12.01', y1: '16', y2: '16', key: '4dfq90' }],\n]);\n\nexport default CircleAlert;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Copy\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/copy\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Copy = createLucideIcon('Copy', [\n ['rect', { width: '14', height: '14', x: '8', y: '8', rx: '2', ry: '2', key: '17jyea' }],\n ['path', { d: 'M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2', key: 'zix9uf' }],\n]);\n\nexport default Copy;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Download\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/download\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Download = createLucideIcon('Download', [\n ['path', { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4', key: 'ih7n3h' }],\n ['polyline', { points: '7 10 12 15 17 10', key: '2ggqvy' }],\n ['line', { x1: '12', x2: '12', y1: '15', y2: '3', key: '1vk2je' }],\n]);\n\nexport default Download;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name FileText\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file-text\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst FileText = createLucideIcon('FileText', [\n ['path', { d: 'M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z', key: '1rqfz7' }],\n ['path', { d: 'M14 2v4a2 2 0 0 0 2 2h4', key: 'tnqrlb' }],\n ['path', { d: 'M10 9H8', key: 'b1mrlr' }],\n ['path', { d: 'M16 13H8', key: 't4e002' }],\n ['path', { d: 'M16 17H8', key: 'z1uh3a' }],\n]);\n\nexport default FileText;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name File\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst File = createLucideIcon('File', [\n ['path', { d: 'M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z', key: '1rqfz7' }],\n ['path', { d: 'M14 2v4a2 2 0 0 0 2 2h4', key: 'tnqrlb' }],\n]);\n\nexport default File;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Film\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/film\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Film = createLucideIcon('Film', [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n ['path', { d: 'M7 3v18', key: 'bbkbws' }],\n ['path', { d: 'M3 7.5h4', key: 'zfgn84' }],\n ['path', { d: 'M3 12h18', key: '1i2n21' }],\n ['path', { d: 'M3 16.5h4', key: '1230mu' }],\n ['path', { d: 'M17 3v18', key: 'in4fa5' }],\n ['path', { d: 'M17 7.5h4', key: 'myr1c1' }],\n ['path', { d: 'M17 16.5h4', key: 'go4c1d' }],\n]);\n\nexport default Film;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Globe\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/globe\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Globe = createLucideIcon('Globe', [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n ['path', { d: 'M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20', key: '13o1zl' }],\n ['path', { d: 'M2 12h20', key: '9i4pu4' }],\n]);\n\nexport default Globe;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Layers\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/layers\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Layers = createLucideIcon('Layers', [\n [\n 'path',\n {\n d: 'M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z',\n key: 'zw3jo',\n },\n ],\n [\n 'path',\n {\n d: 'M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12',\n key: '1wduqc',\n },\n ],\n [\n 'path',\n {\n d: 'M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17',\n key: 'kqbvx6',\n },\n ],\n]);\n\nexport default Layers;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Lightbulb\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/lightbulb\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Lightbulb = createLucideIcon('Lightbulb', [\n [\n 'path',\n {\n d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5',\n key: '1gvzjb',\n },\n ],\n ['path', { d: 'M9 18h6', key: 'x1upvd' }],\n ['path', { d: 'M10 22h4', key: 'ceow96' }],\n]);\n\nexport default Lightbulb;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name LoaderCircle\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/loader-circle\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst LoaderCircle = createLucideIcon('LoaderCircle', [\n ['path', { d: 'M21 12a9 9 0 1 1-6.219-8.56', key: '13zald' }],\n]);\n\nexport default LoaderCircle;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name LockKeyhole\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/lock-keyhole\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst LockKeyhole = createLucideIcon('LockKeyhole', [\n ['circle', { cx: '12', cy: '16', r: '1', key: '1au0dj' }],\n ['rect', { x: '3', y: '10', width: '18', height: '12', rx: '2', key: '6s8ecr' }],\n ['path', { d: 'M7 10V7a5 5 0 0 1 10 0v3', key: '1pqi11' }],\n]);\n\nexport default LockKeyhole;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name MessageSquareMore\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/message-square-more\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst MessageSquareMore = createLucideIcon('MessageSquareMore', [\n ['path', { d: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', key: '1lielz' }],\n ['path', { d: 'M8 10h.01', key: '19clt8' }],\n ['path', { d: 'M12 10h.01', key: '1nrarc' }],\n ['path', { d: 'M16 10h.01', key: '1m94wz' }],\n]);\n\nexport default MessageSquareMore;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name MessageSquare\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/message-square\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst MessageSquare = createLucideIcon('MessageSquare', [\n ['path', { d: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', key: '1lielz' }],\n]);\n\nexport default MessageSquare;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Play\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/play\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Play = createLucideIcon('Play', [\n ['polygon', { points: '6 3 20 12 6 21 6 3', key: '1oa8hb' }],\n]);\n\nexport default Play;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Settings2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/settings-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Settings2 = createLucideIcon('Settings2', [\n ['path', { d: 'M20 7h-9', key: '3s1dr2' }],\n ['path', { d: 'M14 17H5', key: 'gfn3mx' }],\n ['circle', { cx: '17', cy: '17', r: '3', key: '18b49y' }],\n ['circle', { cx: '7', cy: '7', r: '3', key: 'dfmy0x' }],\n]);\n\nexport default Settings2;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Sparkles\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/sparkles\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Sparkles = createLucideIcon('Sparkles', [\n [\n 'path',\n {\n d: 'M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z',\n key: '4pj2yx',\n },\n ],\n ['path', { d: 'M20 3v4', key: '1olli1' }],\n ['path', { d: 'M22 5h-4', key: '1gvqau' }],\n ['path', { d: 'M4 17v2', key: 'vumght' }],\n ['path', { d: 'M5 18H3', key: 'zchphs' }],\n]);\n\nexport default Sparkles;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Square\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/square\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Square = createLucideIcon('Square', [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n]);\n\nexport default Square;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name TriangleAlert\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/triangle-alert\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst TriangleAlert = createLucideIcon('TriangleAlert', [\n [\n 'path',\n {\n d: 'm21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3',\n key: 'wmoenq',\n },\n ],\n ['path', { d: 'M12 9v4', key: 'juzpu7' }],\n ['path', { d: 'M12 17h.01', key: 'p32p05' }],\n]);\n\nexport default TriangleAlert;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name X\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/x\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst X = createLucideIcon('X', [\n ['path', { d: 'M18 6 6 18', key: '1bl5f8' }],\n ['path', { d: 'm6 6 12 12', key: 'd8bk6v' }],\n]);\n\nexport default X;\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 type { ReplaySpeed } from \"@blade-hq/agent-client\"\nimport { Play } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nconst SPEED_OPTIONS: ReplaySpeed[] = [1, 2, 5]\n\nexport interface ReplayBarProps {\n /** 非回放会话传 false 时整条不渲染。 */\n isReplay: boolean\n speed: ReplaySpeed\n onSpeedChange: (speed: ReplaySpeed) => void\n onExit: () => void\n /** 传 useReplay().canControl。只读身份下仍然显示\"在回放\",但操作按钮禁用。 */\n canControl?: boolean\n className?: string\n}\n\n/** 回放会话的顶部状态条:说明当前在回放、切换倍速、随时退出回放。 */\nexport function ReplayBar({\n isReplay,\n speed,\n onSpeedChange,\n onExit,\n canControl = true,\n className,\n}: ReplayBarProps) {\n if (!isReplay) return null\n\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-2 border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))]/40 px-4 py-2 text-xs\",\n className,\n )}\n >\n <span className=\"inline-flex items-center gap-1.5 font-medium text-[hsl(var(--foreground))]\">\n <Play size={13} />\n 回放模式\n </span>\n <span className=\"text-[hsl(var(--muted-foreground))]\">\n 正在重现之前的对话\n </span>\n <div className=\"ml-auto flex items-center gap-2\">\n <div className=\"flex items-center gap-0.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-0.5\">\n {SPEED_OPTIONS.map((option) => (\n <button\n key={option}\n type=\"button\"\n onClick={() => onSpeedChange(option)}\n aria-pressed={speed === option}\n disabled={!canControl}\n className={cn(\n \"h-6 rounded px-2 font-medium transition-colors\",\n speed === option\n ? \"bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]\"\n : \"text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n !canControl && \"cursor-not-allowed opacity-50\",\n )}\n >\n {option}x\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={onExit}\n disabled={!canControl}\n className={cn(\n \"h-7 rounded-md border border-[hsl(var(--border))] px-2.5 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n !canControl && \"cursor-not-allowed opacity-50 hover:bg-transparent\",\n )}\n >\n 退出回放\n </button>\n </div>\n </div>\n )\n}\n","import type { ReplayMismatch } from \"../hooks/use-replay\"\nimport { cn } from \"../lib/utils\"\n\nexport interface ReplayMismatchPromptProps {\n /** 来自 useReplay().mismatch;为 null 时不渲染。 */\n mismatch: ReplayMismatch | null\n className?: string\n}\n\n/** 回放中输入和录制内容对不上时的选择卡:按录制继续,还是从这里真跑。 */\nexport function ReplayMismatchPrompt({ mismatch, className }: ReplayMismatchPromptProps) {\n if (!mismatch) return null\n\n return (\n <div\n className={cn(\n \"mx-auto my-3 max-w-3xl rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-sm\",\n className,\n )}\n >\n <div className=\"font-medium text-[hsl(var(--foreground))]\">\n 这句话和之前录制的不一样\n </div>\n <dl className=\"mt-2 space-y-1 text-xs text-[hsl(var(--muted-foreground))]\">\n <div className=\"flex gap-2\">\n <dt className=\"shrink-0\">录制的是</dt>\n <dd className=\"min-w-0 break-words text-[hsl(var(--foreground))]\">\n {mismatch.expectedMessage || \"(空)\"}\n </dd>\n </div>\n <div className=\"flex gap-2\">\n <dt className=\"shrink-0\">你输入的</dt>\n <dd className=\"min-w-0 break-words text-[hsl(var(--foreground))]\">\n {mismatch.actualMessage || \"(空)\"}\n </dd>\n </div>\n </dl>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => mismatch.resolve(\"keep_replay\")}\n className=\"h-8 rounded-md bg-[hsl(var(--primary))] px-3 text-xs font-medium text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90\"\n >\n 按录制内容继续\n </button>\n <button\n type=\"button\"\n onClick={() => mismatch.resolve(\"continue_replay\")}\n className=\"h-8 rounded-md border border-[hsl(var(--border))] px-3 text-xs text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\"\n >\n 从这里开始真的运行\n </button>\n </div>\n </div>\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) => boolean | Promise<boolean>\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 = async () => {\n if (!canSend) return\n const accepted = await onSend(trimmed)\n if (!accepted) return\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 void handleSend()\n }\n }\n\n return (\n <div className={cn(\"blade-chat-input border-t border-[hsl(var(--border))] 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 {\n AskUserAnswerData,\n ChatMessage,\n PostChatFollowup,\n ResultFeedback,\n} from \"@blade-hq/agent-client\"\nimport { isHiddenInternalMessage } from \"@blade-hq/agent-client\"\nimport { ChevronDown, Layers, Lightbulb, MessageSquare } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useRef, useState, 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 {\n HistoricalResultFeedback,\n PostChatFollowupBlock,\n type FollowupInteractionEvent,\n} from \"./PostChatFollowupBlock\"\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 postChatFollowup?: PostChatFollowup | null\n onSuggestion?: (value: string) => void\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 /** 会话 id;传给助手消息的统一上下文。 */\n sessionId?: string\n isViewer?: boolean\n onFollowupInteraction?: (event: FollowupInteractionEvent) => void\n resultFeedbackByEntry?: ReadonlyMap<string, ResultFeedback>\n onResultFeedbackSaved?: (feedback: ResultFeedback) => void\n}\n\n/**\n * 消息列表:use-stick-to-bottom 自动滚动 + 按轮次分组渲染。\n * 相比第一方版本去掉了轮次导航栏、吸顶状态条与规划摘要卡片。\n */\nexport function MessageList({\n messages,\n postChatFollowup,\n onSuggestion,\n isStreaming,\n sessionStatus,\n askAnswers,\n onAnswer,\n toolCallRenderer,\n emptyState,\n className,\n sessionId,\n isViewer = false,\n onFollowupInteraction,\n resultFeedbackByEntry = new Map(),\n onResultFeedbackSaved,\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=\"blade-chat-messages-scroll\">\n <div className=\"blade-chat-messages-content mx-auto max-w-[748px]\">\n <div className=\"flex min-w-0 flex-col\">\n {renderBlocks.length === 0\n ? (emptyState ?? (\n <div className=\"blade-chat-empty\">\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 const blockFeedback = block.messages\n .map((message) =>\n message.entry_id\n ? resultFeedbackByEntry.get(message.entry_id)\n : undefined,\n )\n .find((feedback): feedback is ResultFeedback => feedback != null)\n const hasActiveFollowup = Boolean(\n postChatFollowup &&\n block.messages.some(\n (message) => message.entry_id === postChatFollowup.assistant_entry_id,\n ),\n )\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 sessionId={sessionId}\n />\n {blockFeedback && !hasActiveFollowup ? (\n <HistoricalResultFeedback feedback={blockFeedback} />\n ) : null}\n {hasActiveFollowup && postChatFollowup ? (\n <PostChatFollowupBlock\n followup={postChatFollowup}\n sessionId={sessionId}\n onSuggestion={onSuggestion}\n isViewer={isViewer}\n onInteraction={onFollowupInteraction}\n savedFeedback={blockFeedback}\n onFeedbackSaved={onResultFeedbackSaved}\n />\n ) : null}\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 const [visible, setVisible] = useState(false)\n const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n // 防抖:isAtBottom 变为 true 时不立即隐藏,等 300ms 确认稳定后再隐藏\n // 避免流式内容高度变化导致 isAtBottom 在 true/false 之间快速切换闪烁\n useEffect(() => {\n if (isAtBottom) {\n // 启动延迟隐藏定时器\n if (!hideTimerRef.current) {\n hideTimerRef.current = setTimeout(() => {\n setVisible(false)\n hideTimerRef.current = null\n }, 300)\n }\n } else {\n // 不在底部,取消所有待执行的隐藏,立即显示\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n setVisible(true)\n }\n\n return () => {\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n }\n }, [isAtBottom])\n\n // hook 必须在任何早退之前调用,否则 visible 变化时 hook 顺序不一致\n const handleClick = useCallback(() => {\n // 点击滚动到底部时立即隐藏按钮,避免闪烁\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n setVisible(false)\n scrollToBottom()\n }, [scrollToBottom])\n\n if (!visible) return null\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n aria-label=\"滚动到底部\"\n className=\"blade-chat-scroll-bottom 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 {/* 窄容器里这行字会横跨近半个宽度,收起来只留图标;可访问名由 aria-label 保住。 */}\n <span className=\"blade-chat-scroll-bottom-label\">滚动到底部</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","/*!---------------------------------------------------------------------------------------------\n * Copyright (c) StackBlitz. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nimport { useCallback, useMemo, useRef, useState, } from \"react\";\nconst DEFAULT_SPRING_ANIMATION = {\n /**\n * A value from 0 to 1, on how much to damp the animation.\n * 0 means no damping, 1 means full damping.\n *\n * @default 0.7\n */\n damping: 0.7,\n /**\n * The stiffness of how fast/slow the animation gets up to speed.\n *\n * @default 0.05\n */\n stiffness: 0.05,\n /**\n * The inertial mass associated with the animation.\n * Higher numbers make the animation slower.\n *\n * @default 1.25\n */\n mass: 1.25,\n};\nconst STICK_TO_BOTTOM_OFFSET_PX = 70;\nconst SIXTY_FPS_INTERVAL_MS = 1000 / 60;\nconst RETAIN_ANIMATION_DURATION_MS = 350;\nlet mouseDown = false;\nglobalThis.document?.addEventListener(\"mousedown\", () => {\n mouseDown = true;\n});\nglobalThis.document?.addEventListener(\"mouseup\", () => {\n mouseDown = false;\n});\nglobalThis.document?.addEventListener(\"click\", () => {\n mouseDown = false;\n});\nexport const useStickToBottom = (options = {}) => {\n const [escapedFromLock, updateEscapedFromLock] = useState(false);\n const [isAtBottom, updateIsAtBottom] = useState(options.initial !== false);\n const [isNearBottom, setIsNearBottom] = useState(false);\n const optionsRef = useRef(null);\n optionsRef.current = options;\n const isSelecting = useCallback(() => {\n if (!mouseDown) {\n return false;\n }\n const selection = window.getSelection();\n if (!selection || !selection.rangeCount) {\n return false;\n }\n const range = selection.getRangeAt(0);\n return (range.commonAncestorContainer.contains(scrollRef.current) ||\n scrollRef.current?.contains(range.commonAncestorContainer));\n }, []);\n const setIsAtBottom = useCallback((isAtBottom) => {\n state.isAtBottom = isAtBottom;\n updateIsAtBottom(isAtBottom);\n }, []);\n const setEscapedFromLock = useCallback((escapedFromLock) => {\n state.escapedFromLock = escapedFromLock;\n updateEscapedFromLock(escapedFromLock);\n }, []);\n // biome-ignore lint/correctness/useExhaustiveDependencies: not needed\n const state = useMemo(() => {\n let lastCalculation;\n return {\n escapedFromLock,\n isAtBottom,\n resizeDifference: 0,\n accumulated: 0,\n velocity: 0,\n listeners: new Set(),\n get scrollTop() {\n return scrollRef.current?.scrollTop ?? 0;\n },\n set scrollTop(scrollTop) {\n if (scrollRef.current) {\n scrollRef.current.scrollTop = scrollTop;\n state.ignoreScrollToTop = scrollRef.current.scrollTop;\n }\n },\n get targetScrollTop() {\n if (!scrollRef.current || !contentRef.current) {\n return 0;\n }\n return (scrollRef.current.scrollHeight - 1 - scrollRef.current.clientHeight);\n },\n get calculatedTargetScrollTop() {\n if (!scrollRef.current || !contentRef.current) {\n return 0;\n }\n const { targetScrollTop } = this;\n if (!options.targetScrollTop) {\n return targetScrollTop;\n }\n if (lastCalculation?.targetScrollTop === targetScrollTop) {\n return lastCalculation.calculatedScrollTop;\n }\n const calculatedScrollTop = Math.max(Math.min(options.targetScrollTop(targetScrollTop, {\n scrollElement: scrollRef.current,\n contentElement: contentRef.current,\n }), targetScrollTop), 0);\n lastCalculation = { targetScrollTop, calculatedScrollTop };\n requestAnimationFrame(() => {\n lastCalculation = undefined;\n });\n return calculatedScrollTop;\n },\n get scrollDifference() {\n return this.calculatedTargetScrollTop - this.scrollTop;\n },\n get isNearBottom() {\n return this.scrollDifference <= STICK_TO_BOTTOM_OFFSET_PX;\n },\n };\n }, []);\n const scrollToBottom = useCallback((scrollOptions = {}) => {\n if (typeof scrollOptions === \"string\") {\n scrollOptions = { animation: scrollOptions };\n }\n if (!scrollOptions.preserveScrollPosition) {\n setIsAtBottom(true);\n }\n const waitElapsed = Date.now() + (Number(scrollOptions.wait) || 0);\n const behavior = mergeAnimations(optionsRef.current, scrollOptions.animation);\n const { ignoreEscapes = false } = scrollOptions;\n let durationElapsed;\n let startTarget = state.calculatedTargetScrollTop;\n if (scrollOptions.duration instanceof Promise) {\n scrollOptions.duration.finally(() => {\n durationElapsed = Date.now();\n });\n }\n else {\n durationElapsed = waitElapsed + (scrollOptions.duration ?? 0);\n }\n const next = async () => {\n const promise = new Promise(requestAnimationFrame).then(() => {\n if (!state.isAtBottom) {\n state.animation = undefined;\n return false;\n }\n const { scrollTop } = state;\n const tick = performance.now();\n const tickDelta = (tick - (state.lastTick ?? tick)) / SIXTY_FPS_INTERVAL_MS;\n state.animation || (state.animation = { behavior, promise, ignoreEscapes });\n if (state.animation.behavior === behavior) {\n state.lastTick = tick;\n }\n if (isSelecting()) {\n return next();\n }\n if (waitElapsed > Date.now()) {\n return next();\n }\n if (scrollTop < Math.min(startTarget, state.calculatedTargetScrollTop)) {\n if (state.animation?.behavior === behavior) {\n if (behavior === \"instant\") {\n state.scrollTop = state.calculatedTargetScrollTop;\n return next();\n }\n state.velocity =\n (behavior.damping * state.velocity +\n behavior.stiffness * state.scrollDifference) /\n behavior.mass;\n state.accumulated += state.velocity * tickDelta;\n state.scrollTop += state.accumulated;\n if (state.scrollTop !== scrollTop) {\n state.accumulated = 0;\n }\n }\n return next();\n }\n if (durationElapsed > Date.now()) {\n startTarget = state.calculatedTargetScrollTop;\n return next();\n }\n state.animation = undefined;\n /**\n * If we're still below the target, then queue\n * up another scroll to the bottom with the last\n * requested animatino.\n */\n if (state.scrollTop < state.calculatedTargetScrollTop) {\n return scrollToBottom({\n animation: mergeAnimations(optionsRef.current, optionsRef.current.resize),\n ignoreEscapes,\n duration: Math.max(0, durationElapsed - Date.now()) || undefined,\n });\n }\n return state.isAtBottom;\n });\n return promise.then((isAtBottom) => {\n requestAnimationFrame(() => {\n if (!state.animation) {\n state.lastTick = undefined;\n state.velocity = 0;\n }\n });\n return isAtBottom;\n });\n };\n if (scrollOptions.wait !== true) {\n state.animation = undefined;\n }\n if (state.animation?.behavior === behavior) {\n return state.animation.promise;\n }\n return next();\n }, [setIsAtBottom, isSelecting, state]);\n const stopScroll = useCallback(() => {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }, [setEscapedFromLock, setIsAtBottom]);\n const handleScroll = useCallback(({ target }) => {\n if (target !== scrollRef.current) {\n return;\n }\n const { scrollTop, ignoreScrollToTop } = state;\n let { lastScrollTop = scrollTop } = state;\n state.lastScrollTop = scrollTop;\n state.ignoreScrollToTop = undefined;\n if (ignoreScrollToTop && ignoreScrollToTop > scrollTop) {\n /**\n * When the user scrolls up while the animation plays, the `scrollTop` may\n * not come in separate events; if this happens, to make sure `isScrollingUp`\n * is correct, set the lastScrollTop to the ignored event.\n */\n lastScrollTop = ignoreScrollToTop;\n }\n setIsNearBottom(state.isNearBottom);\n /**\n * Scroll events may come before a ResizeObserver event,\n * so in order to ignore resize events correctly we use a\n * timeout.\n *\n * @see https://github.com/WICG/resize-observer/issues/25#issuecomment-248757228\n */\n setTimeout(() => {\n /**\n * When theres a resize difference ignore the resize event.\n */\n if (state.resizeDifference || scrollTop === ignoreScrollToTop) {\n return;\n }\n if (isSelecting()) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n return;\n }\n const isScrollingDown = scrollTop > lastScrollTop;\n const isScrollingUp = scrollTop < lastScrollTop;\n if (state.animation?.ignoreEscapes) {\n state.scrollTop = lastScrollTop;\n return;\n }\n if (isScrollingUp) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }\n if (isScrollingDown) {\n setEscapedFromLock(false);\n }\n if (!state.escapedFromLock && state.isNearBottom) {\n setIsAtBottom(true);\n }\n }, 1);\n }, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);\n const handleWheel = useCallback(({ target, deltaY }) => {\n let element = target;\n while (![\"scroll\", \"auto\"].includes(getComputedStyle(element).overflow)) {\n if (!element.parentElement) {\n return;\n }\n element = element.parentElement;\n }\n /**\n * The browser may cancel the scrolling from the mouse wheel\n * if we update it from the animation in meantime.\n * To prevent this, always escape when the wheel is scrolled up.\n */\n if (element === scrollRef.current &&\n deltaY < 0 &&\n scrollRef.current.scrollHeight > scrollRef.current.clientHeight &&\n !state.animation?.ignoreEscapes) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }\n }, [setEscapedFromLock, setIsAtBottom, state]);\n const scrollRef = useRefCallback((scroll) => {\n scrollRef.current?.removeEventListener(\"scroll\", handleScroll);\n scrollRef.current?.removeEventListener(\"wheel\", handleWheel);\n scroll?.addEventListener(\"scroll\", handleScroll, { passive: true });\n scroll?.addEventListener(\"wheel\", handleWheel, { passive: true });\n }, []);\n const contentRef = useRefCallback((content) => {\n state.resizeObserver?.disconnect();\n if (!content) {\n return;\n }\n let previousHeight;\n state.resizeObserver = new ResizeObserver(([entry]) => {\n const { height } = entry.contentRect;\n const difference = height - (previousHeight ?? height);\n state.resizeDifference = difference;\n /**\n * Sometimes the browser can overscroll past the target,\n * so check for this and adjust appropriately.\n */\n if (state.scrollTop > state.targetScrollTop) {\n state.scrollTop = state.targetScrollTop;\n }\n setIsNearBottom(state.isNearBottom);\n if (difference >= 0) {\n /**\n * If it's a positive resize, scroll to the bottom when\n * we're already at the bottom.\n */\n const animation = mergeAnimations(optionsRef.current, previousHeight\n ? optionsRef.current.resize\n : optionsRef.current.initial);\n scrollToBottom({\n animation,\n wait: true,\n preserveScrollPosition: true,\n duration: animation === \"instant\" ? undefined : RETAIN_ANIMATION_DURATION_MS,\n });\n }\n else {\n /**\n * Else if it's a negative resize, check if we're near the bottom\n * if we are want to un-escape from the lock, because the resize\n * could have caused the container to be at the bottom.\n */\n if (state.isNearBottom) {\n setEscapedFromLock(false);\n setIsAtBottom(true);\n }\n }\n previousHeight = height;\n /**\n * Reset the resize difference after the scroll event\n * has fired. Requires a rAF to wait for the scroll event,\n * and a setTimeout to wait for the other timeout we have in\n * resizeObserver in case the scroll event happens after the\n * resize event.\n */\n requestAnimationFrame(() => {\n setTimeout(() => {\n if (state.resizeDifference === difference) {\n state.resizeDifference = 0;\n }\n }, 1);\n });\n });\n state.resizeObserver?.observe(content);\n }, []);\n return {\n contentRef,\n scrollRef,\n scrollToBottom,\n stopScroll,\n isAtBottom: isAtBottom || isNearBottom,\n isNearBottom,\n escapedFromLock,\n state,\n };\n};\nfunction useRefCallback(callback, deps) {\n // biome-ignore lint/correctness/useExhaustiveDependencies: not needed\n const result = useCallback((ref) => {\n result.current = ref;\n return callback(ref);\n }, deps);\n return result;\n}\nconst animationCache = new Map();\nfunction mergeAnimations(...animations) {\n const result = { ...DEFAULT_SPRING_ANIMATION };\n let instant = false;\n for (const animation of animations) {\n if (animation === \"instant\") {\n instant = true;\n continue;\n }\n if (typeof animation !== \"object\") {\n continue;\n }\n instant = false;\n result.damping = animation.damping ?? result.damping;\n result.stiffness = animation.stiffness ?? result.stiffness;\n result.mass = animation.mass ?? result.mass;\n }\n const key = JSON.stringify(result);\n if (!animationCache.has(key)) {\n animationCache.set(key, Object.freeze(result));\n }\n return instant ? \"instant\" : animationCache.get(key);\n}\n","/*!---------------------------------------------------------------------------------------------\n * Copyright (c) StackBlitz. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nimport * as React from \"react\";\nimport { createContext, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, } from \"react\";\nimport { useStickToBottom, } from \"./useStickToBottom.js\";\nconst StickToBottomContext = createContext(null);\nconst useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\nexport function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {\n const customTargetScrollTop = useRef(null);\n const targetScrollTop = React.useCallback((target, elements) => {\n const get = context?.targetScrollTop ?? currentTargetScrollTop;\n return get?.(target, elements) ?? target;\n }, [currentTargetScrollTop]);\n const defaultInstance = useStickToBottom({\n mass,\n damping,\n stiffness,\n resize,\n initial,\n targetScrollTop,\n });\n const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state, } = instance ?? defaultInstance;\n const context = useMemo(() => ({\n scrollToBottom,\n stopScroll,\n scrollRef,\n isAtBottom,\n escapedFromLock,\n contentRef,\n state,\n get targetScrollTop() {\n return customTargetScrollTop.current;\n },\n set targetScrollTop(targetScrollTop) {\n customTargetScrollTop.current = targetScrollTop;\n },\n }), [\n scrollToBottom,\n isAtBottom,\n contentRef,\n scrollRef,\n stopScroll,\n escapedFromLock,\n state,\n ]);\n useImperativeHandle(contextRef, () => context, [context]);\n useIsomorphicLayoutEffect(() => {\n if (!scrollRef.current) {\n return;\n }\n if (getComputedStyle(scrollRef.current).overflow === \"visible\") {\n scrollRef.current.style.overflow = \"auto\";\n }\n }, []);\n return (React.createElement(StickToBottomContext.Provider, { value: context },\n React.createElement(\"div\", { ...props }, typeof children === \"function\" ? children(context) : children)));\n}\n(function (StickToBottom) {\n function Content({ children, scrollClassName, ...props }) {\n const context = useStickToBottomContext();\n return (React.createElement(\"div\", { ref: context.scrollRef, style: {\n height: \"100%\",\n width: \"100%\",\n scrollbarGutter: \"stable both-edges\",\n }, className: scrollClassName },\n React.createElement(\"div\", { ...props, ref: context.contentRef }, typeof children === \"function\" ? children(context) : children)));\n }\n StickToBottom.Content = Content;\n})(StickToBottom || (StickToBottom = {}));\n/**\n * Use this hook inside a <StickToBottom> component to gain access to whether the component is at the bottom of the scrollable area.\n */\nexport function useStickToBottomContext() {\n const context = useContext(StickToBottomContext);\n if (!context) {\n throw new Error(\"use-stick-to-bottom component context must be used within a StickToBottom component\");\n }\n return context;\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 /** 会话 id;传给 Markdown 渲染的统一上下文。 */\n sessionId?: string\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 <span className=\"text-[hsl(var(--muted-foreground))]/70\">\n · {new Intl.NumberFormat(\"zh-CN\").format(reasoning.length)} 字\n </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\nfunction findLatestReasoningMessageIndex(messages: readonly ChatMessage[]): number {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n if (messages[index].reasoning) return index\n }\n return -1\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 sessionId,\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 const latestReasoningIndex = isStreaming\n ? findLatestReasoningMessageIndex(messages)\n : -1\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 const showReasoning =\n !!message.reasoning && (!isStreaming || index === latestReasoningIndex)\n\n return (\n <div\n key={message.entry_id ?? `${message.timestamp ?? \"assistant\"}-${index}`}\n className=\"flex flex-col gap-3\"\n >\n {showReasoning && 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 sessionId={sessionId}\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?: \"streaming\" | \"static\"\n /** 会话 id;保留给聊天组件统一透传上下文。 */\n sessionId?: string\n}\n\n/**\n * SDK 版 Markdown 渲染:附带代码块复制按钮;相比第一方版本去掉了\n * mermaid、外链确认弹窗与文件预览。\n */\nexport function MarkdownContent({ children, className, mode, sessionId }: MarkdownContentProps) {\n const resolvedChildren = useMemo(() => {\n return children.replace(SYSTEM_REMINDER_RE, \"\")\n }, [children])\n\n return (\n <Streamdown\n // Streamdown 外层 memo 不比较 components,光换 sessionId 可能留着旧闭包,\n // 卡片就会拿旧会话去下载。挂 key 强制重建,切会话本来也要换内容。\n key={sessionId ?? \"no-session\"}\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 * 判断提问卡片该等待作答还是展示成已作答,只认工具状态。\n * 与 apps/web 的 ToolCallBlock 里同名函数保持一致:两处算出不同结果时,\n * 同一份会话在内置页面能点、在 SDK 里点不动。\n *\n * 不要再拿 sessionStatus 兜底。历史投影算错时用 waiting_for_input 补救,\n * 会同时把已经回答过的旧提问重新变成可作答(历史加载不会带回 answerData),\n * 而且会盖住投影层的错误——这个 bug 就是这么藏住的。\n */\nexport function resolveAskQuestionState({\n toolStatus,\n hasAnswerData,\n fallbackAnswered,\n}: {\n toolStatus: ToolCallInfo[\"status\"]\n hasAnswerData: boolean\n fallbackAnswered?: boolean\n}) {\n const awaitingAnswer = !hasAnswerData && toolStatus === \"awaiting_answer\"\n return {\n awaitingAnswer,\n answered:\n hasAnswerData ||\n (!awaitingAnswer &&\n (Boolean(fallbackAnswered) ||\n toolStatus === \"done\" ||\n toolStatus === \"cancelled\" ||\n toolStatus === \"error\")),\n }\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 questionState = resolveAskQuestionState({\n toolStatus: toolCall.status,\n hasAnswerData: Boolean(answerData),\n fallbackAnswered: answered,\n })\n const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer)\n if (askData) {\n return (\n <AskUserQuestionBlock\n data={askData}\n answered={questionState.answered}\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 { FinalArtifact, PostChatFollowup, ResultFeedback as SavedResultFeedback, ResultFeedbackReason } from \"@blade-hq/agent-client\"\nimport { ArrowRight, ArrowUpRight, ChevronDown, Download, File, Film, Globe, Sparkles } from \"lucide-react\"\nimport { useCallback, useEffect, useRef, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\n\nexport type FollowupInteractionEvent =\n | {\n type: \"suggestions_shown\"\n sessionId?: string\n assistantEntryId: string\n count: number\n }\n | {\n type: \"suggestion_adopted\"\n sessionId?: string\n assistantEntryId: string\n suggestionIndex: number\n }\n | {\n type: \"artifact_shown\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: FinalArtifact[\"kind\"]\n }\n | {\n type: \"artifact_opened\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: FinalArtifact[\"kind\"]\n }\n | {\n type: \"artifact_download_started\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: \"file\"\n }\n | {\n type: \"artifact_download_succeeded\"\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n artifactKind: \"file\"\n }\n | {\n type: \"result_feedback_shown\"\n sessionId?: string\n assistantEntryId: string\n }\n | {\n type: \"result_feedback_submitted\"\n sessionId?: string\n assistantEntryId: string\n helpful: boolean\n reason: ResultFeedbackReason | null\n updatedAt: string\n }\n\nfunction emitInteraction(\n callback: ((event: FollowupInteractionEvent) => void) | undefined,\n event: FollowupInteractionEvent,\n) {\n try {\n callback?.(event)\n } catch {\n // 宿主的分析回调不得阻断 SDK 交互。\n }\n}\n\nfunction basename(path: string): string {\n return path.split(/[\\\\/]/).filter(Boolean).pop() || path\n}\n\nfunction isVideo(path: string): boolean {\n return /\\.(?:mp4|mov|webm|mkv|avi|m4v)$/i.test(path)\n}\n\nfunction ArtifactCard({\n artifact,\n sessionId,\n assistantEntryId,\n artifactIndex,\n onInteraction,\n onArtifactOpened,\n}: {\n artifact: FinalArtifact\n sessionId?: string\n assistantEntryId: string\n artifactIndex: number\n onInteraction?: (event: FollowupInteractionEvent) => void\n onArtifactOpened: (artifactIndex: number, artifactKind: FinalArtifact[\"kind\"]) => void\n}) {\n const client = useBladeClient()\n const [downloading, setDownloading] = useState(false)\n const name = artifact.label || basename(artifact.target)\n\n if (artifact.kind === \"link\") {\n return (\n <a\n href={artifact.target}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n onClick={() => onArtifactOpened(artifactIndex, \"link\")}\n title={`${name}\\n${artifact.target}`}\n className=\"group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))]\"\n >\n <Globe size={15} className=\"shrink-0 text-[hsl(var(--primary))]\" />\n <span className=\"min-w-0 flex-1 truncate font-medium\">{name}</span>\n <ArrowUpRight size={13} className=\"absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100\" />\n </a>\n )\n }\n\n const Icon = isVideo(artifact.target) ? Film : File\n return (\n <button\n type=\"button\"\n disabled={!sessionId || downloading}\n onClick={async () => {\n if (!sessionId || downloading) return\n setDownloading(true)\n emitInteraction(onInteraction, {\n type: \"artifact_download_started\",\n sessionId,\n assistantEntryId,\n artifactIndex,\n artifactKind: \"file\",\n })\n try {\n await client.sessions.downloadFile(sessionId, artifact.target, basename(artifact.target))\n emitInteraction(onInteraction, {\n type: \"artifact_download_succeeded\",\n sessionId,\n assistantEntryId,\n artifactIndex,\n artifactKind: \"file\",\n })\n } catch {\n // 下载失败不能误报成功,也不影响其余聊天交互。\n } finally {\n setDownloading(false)\n }\n }}\n title={name}\n aria-label={`下载 ${name}`}\n className=\"group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-left text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))] disabled:opacity-60\"\n >\n <Icon size={15} className=\"shrink-0 text-[hsl(var(--primary))]\" />\n <span className=\"min-w-0 flex-1 truncate font-medium\">{name}</span>\n <Download size={13} className=\"absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100\" />\n </button>\n )\n}\n\nconst FEEDBACK_REASONS: Array<{ value: ResultFeedbackReason; label: string }> = [\n { value: \"not_solved\", label: \"没解决问题\" },\n { value: \"inaccurate\", label: \"内容不准确\" },\n { value: \"incomplete\", label: \"内容不完整\" },\n { value: \"needs_major_changes\", label: \"需要大量修改\" },\n { value: \"too_slow\", label: \"太慢\" },\n { value: \"other\", label: \"其他\" },\n]\n\nfunction feedbackReasonLabel(reason: ResultFeedbackReason | null): string | null {\n return FEEDBACK_REASONS.find((item) => item.value === reason)?.label ?? null\n}\n\nexport function HistoricalResultFeedback({ feedback }: { feedback: SavedResultFeedback }) {\n const label = feedbackReasonLabel(feedback.reason)\n return (\n <section\n aria-label=\"历史结果反馈\"\n className=\"mt-3 w-fit max-w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]\"\n >\n <span>你对此轮结果的评价:{feedback.helpful ? \"有帮助\" : \"没帮助\"}</span>\n {label ? <span> · {label}</span> : null}\n </section>\n )\n}\n\nfunction ResultFeedback({\n followup,\n sessionId,\n isViewer,\n onInteraction,\n savedFeedback,\n onFeedbackSaved,\n}: {\n followup: PostChatFollowup\n sessionId?: string\n isViewer: boolean\n onInteraction?: (event: FollowupInteractionEvent) => void\n savedFeedback?: SavedResultFeedback\n onFeedbackSaved?: (feedback: SavedResultFeedback) => void\n}) {\n const client = useBladeClient()\n const [saved, setSaved] = useState<SavedResultFeedback | null>(savedFeedback ?? null)\n const [helpful, setHelpful] = useState<boolean | null>(savedFeedback?.helpful ?? null)\n const [reason, setReason] = useState<ResultFeedbackReason | null>(savedFeedback?.reason ?? null)\n const [saving, setSaving] = useState(false)\n const [saveError, setSaveError] = useState(false)\n const reportedShown = useRef(false)\n const latestChoice = useRef<{\n helpful: boolean\n reason: ResultFeedbackReason | null\n } | null>(null)\n const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer\n\n useEffect(() => {\n if (!eligible || reportedShown.current) return\n reportedShown.current = true\n emitInteraction(onInteraction, {\n type: \"result_feedback_shown\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n })\n }, [eligible, followup.assistant_entry_id, onInteraction, sessionId])\n\n useEffect(() => {\n if (!savedFeedback || latestChoice.current) return\n setSaved(savedFeedback)\n setHelpful(savedFeedback.helpful)\n setReason(savedFeedback.reason)\n }, [savedFeedback])\n\n const submit = useCallback(\n async (nextHelpful: boolean, nextReason: ResultFeedbackReason | null) => {\n if (!sessionId) return\n const choice = { helpful: nextHelpful, reason: nextReason }\n latestChoice.current = choice\n setHelpful(nextHelpful)\n setReason(nextReason)\n setSaving(true)\n setSaveError(false)\n try {\n const result = await client.sessions.putResultFeedback(\n sessionId,\n followup.assistant_entry_id,\n choice,\n )\n if (latestChoice.current !== choice) return\n setSaved(result)\n onFeedbackSaved?.(result)\n emitInteraction(onInteraction, {\n type: \"result_feedback_submitted\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n helpful: result.helpful,\n reason: result.reason,\n updatedAt: result.updated_at,\n })\n } catch {\n if (latestChoice.current === choice) setSaveError(true)\n } finally {\n if (latestChoice.current === choice) setSaving(false)\n }\n },\n [client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId],\n )\n\n if (!eligible) return null\n return (\n <section\n aria-label=\"结果反馈\"\n className=\"flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3\"\n >\n <div className=\"text-xs font-medium text-[hsl(var(--muted-foreground))]\">\n 目前的整体结果有帮助吗?\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n <button\n type=\"button\"\n aria-pressed={helpful === true}\n disabled={saving}\n onClick={() => void submit(true, null)}\n className=\"rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60\"\n >\n 有帮助\n </button>\n <button\n type=\"button\"\n aria-pressed={helpful === false}\n disabled={saving}\n onClick={() => void submit(false, reason)}\n className=\"rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60\"\n >\n 没帮助\n </button>\n </div>\n {helpful === false ? (\n <div className=\"flex flex-wrap gap-1.5\" aria-label=\"没帮助的主要原因\">\n {FEEDBACK_REASONS.map((item) => (\n <button\n key={item.value}\n type=\"button\"\n aria-pressed={reason === item.value}\n disabled={saving}\n onClick={() => void submit(false, item.value)}\n className=\"rounded-full bg-[hsl(var(--muted)/0.62)] px-2.5 py-1 text-[11px] aria-pressed:bg-[hsl(var(--primary)/0.14)] disabled:opacity-60\"\n >\n {item.label}\n </button>\n ))}\n </div>\n ) : null}\n {saveError ? (\n <div className=\"flex items-center gap-2 text-xs text-[hsl(var(--destructive))]\">\n <span>反馈暂未保存,可重试</span>\n <button\n type=\"button\"\n className=\"underline\"\n onClick={() => {\n const choice = latestChoice.current\n if (choice) void submit(choice.helpful, choice.reason)\n }}\n >\n 重试\n </button>\n </div>\n ) : saved ? (\n <div className=\"text-[11px] text-[hsl(var(--muted-foreground))]\">已保存,可随时修改</div>\n ) : null}\n </section>\n )\n}\n\nexport function PostChatFollowupBlock({\n followup,\n sessionId,\n onSuggestion,\n isViewer = false,\n onInteraction,\n savedFeedback,\n onFeedbackSaved,\n}: {\n followup: PostChatFollowup\n sessionId?: string\n onSuggestion?: (value: string) => void\n isViewer?: boolean\n onInteraction?: (event: FollowupInteractionEvent) => void\n savedFeedback?: SavedResultFeedback\n onFeedbackSaved?: (feedback: SavedResultFeedback) => void\n}) {\n const [expanded, setExpanded] = useState(false)\n const adopted = useRef(new Set<number>())\n const reportedSuggestions = useRef(false)\n const reportedArtifacts = useRef(new Set<number>())\n const openedArtifacts = useRef(new Set<number>())\n const artifacts = followup.final_artifacts ?? []\n const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3)\n useEffect(() => {\n if (!reportedSuggestions.current && followup.suggestions.length > 0) {\n reportedSuggestions.current = true\n emitInteraction(onInteraction, {\n type: \"suggestions_shown\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n count: followup.suggestions.length,\n })\n }\n for (let artifactIndex = 0; artifactIndex < visibleArtifacts.length; artifactIndex += 1) {\n if (reportedArtifacts.current.has(artifactIndex)) continue\n const artifact = visibleArtifacts[artifactIndex]\n if (!artifact) continue\n reportedArtifacts.current.add(artifactIndex)\n emitInteraction(onInteraction, {\n type: \"artifact_shown\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n artifactIndex,\n artifactKind: artifact.kind,\n })\n }\n }, [\n followup.assistant_entry_id,\n followup.suggestions.length,\n onInteraction,\n sessionId,\n visibleArtifacts,\n ])\n const reportArtifactOpened = useCallback(\n (artifactIndex: number, artifactKind: FinalArtifact[\"kind\"]) => {\n if (openedArtifacts.current.has(artifactIndex)) return\n openedArtifacts.current.add(artifactIndex)\n emitInteraction(onInteraction, {\n type: \"artifact_opened\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n artifactIndex,\n artifactKind,\n })\n },\n [followup.assistant_entry_id, onInteraction, sessionId],\n )\n if (\n !followup.recaption &&\n artifacts.length === 0 &&\n followup.suggestions.length === 0 &&\n !followup.feedback_eligible\n )\n return null\n\n return (\n <div className=\"mt-3 flex w-fit max-w-full flex-col gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.28)] p-3 sm:max-w-[680px]\">\n {followup.recaption || artifacts.length > 0 ? (\n <section aria-label=\"本轮小结\" className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]\">\n <Sparkles size={14} />\n 本轮小结\n </div>\n {followup.recaption ? <p className=\"text-[13px] leading-5\">{followup.recaption}</p> : null}\n {artifacts.length > 0 ? (\n <>\n <div className=\"grid max-w-full grid-cols-3 gap-1.5\">\n {visibleArtifacts.map((artifact, artifactIndex) => (\n <ArtifactCard\n key={`${artifact.kind}:${artifactIndex}`}\n artifact={artifact}\n sessionId={sessionId}\n assistantEntryId={followup.assistant_entry_id}\n artifactIndex={artifactIndex}\n onInteraction={onInteraction}\n onArtifactOpened={reportArtifactOpened}\n />\n ))}\n </div>\n {artifacts.length > 3 ? (\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n aria-expanded={expanded}\n className=\"flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]\"\n >\n {expanded ? \"收起\" : `展开 ${artifacts.length - 3} 个`}\n <ChevronDown size={13} className={expanded ? \"rotate-180\" : undefined} />\n </button>\n ) : null}\n </>\n ) : null}\n </section>\n ) : null}\n {followup.suggestions.length > 0 ? (\n <section aria-label=\"下一步建议\" className=\"flex flex-col gap-1.5\">\n <div className=\"text-xs font-medium text-[hsl(var(--muted-foreground))]\">下一步可以</div>\n {followup.suggestions.map((suggestion, suggestionIndex) => (\n <button\n key={suggestion}\n type=\"button\"\n disabled={isViewer}\n onClick={() => {\n if (!adopted.current.has(suggestionIndex)) {\n adopted.current.add(suggestionIndex)\n emitInteraction(onInteraction, {\n type: \"suggestion_adopted\",\n sessionId,\n assistantEntryId: followup.assistant_entry_id,\n suggestionIndex,\n })\n }\n onSuggestion?.(suggestion)\n }}\n className=\"group flex items-center gap-2 rounded-xl bg-[hsl(var(--muted)/0.62)] px-3 py-2 text-left text-[13px] disabled:cursor-default disabled:opacity-60\"\n >\n <span>{suggestion}</span>\n <ArrowRight size={14} className=\"ml-auto shrink-0\" />\n </button>\n ))}\n </section>\n ) : null}\n <ResultFeedback\n followup={followup}\n sessionId={sessionId}\n isViewer={isViewer}\n onInteraction={onInteraction}\n savedFeedback={savedFeedback}\n onFeedbackSaved={onFeedbackSaved}\n />\n </div>\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 {/* 与内置 Web 的 ErrorMessageBlock 保持一致:消息流里的错误不用红色。 */}\n <div className=\"blade-chat-error-block max-w-[85%] border-l-[3px] border-[hsl(var(--border))] px-4 py-1 text-sm leading-7 text-[hsl(var(--muted-foreground))]\">\n {text}\n </div>\n </div>\n )\n}\n","import type {\n AskUserAnswerData,\n ChatMessage,\n ConnectionStatus,\n PostChatFollowup,\n} from \"@blade-hq/agent-client\"\nimport { CircleAlert } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { cn } from \"../lib/utils\"\nimport { ChatInput } from \"./ChatInput\"\nimport { ConnectionBanner } from \"./ConnectionBanner\"\nimport { MessageList } from \"./MessageList\"\nimport type { ToolCallRenderer } from \"./ToolCallBlock\"\nimport type { FollowupInteractionEvent } from \"./PostChatFollowupBlock\"\nimport type { ResultFeedback } from \"@blade-hq/agent-client\"\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\n/** 两种模式共用的外观属性。 */\nexport interface ChatPresentationProps {\n classNames?: ChatViewClassNames\n renderers?: ChatViewRenderers\n slots?: ChatViewSlots\n placeholder?: string\n /** 配色主题,默认浅色。宿主已经在祖先元素上写了 data-theme 时不用传。 */\n theme?: \"light\" | \"dark\"\n}\n\n/** 主题落到聊天根节点上:深色变量挂在 [data-theme=\"dark\"] 上,由它继承给整棵子树。 */\nexport function themeAttr(theme: ChatPresentationProps[\"theme\"]): \"dark\" | undefined {\n return theme === \"dark\" ? \"dark\" : undefined\n}\n\ninterface ChatSurfaceProps extends ChatPresentationProps {\n /** follow-up、成果和评分交互;SDK 不绑定任何分析厂商。 */\n onFollowupInteraction?: (event: FollowupInteractionEvent) => void\n connection: ConnectionStatus\n errorMessage: string | null\n messages: ChatMessage[]\n postChatFollowup?: PostChatFollowup | null\n isStreaming: boolean\n isStopping: boolean\n inputText: string\n onInputChange: (value: string) => void\n onSuggestion?: (value: string) => void\n onSend: (text: string) => Promise<boolean>\n onStop: () => void\n sessionStatus?: string\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n sessionId?: string\n isViewer?: boolean\n resultFeedbackByEntry?: ReadonlyMap<string, ResultFeedback>\n onResultFeedbackSaved?: (feedback: ResultFeedback) => void\n /** 输入框上方的内部区域(纯 LLM 模式的高级设置用)。不传时不渲染任何节点。 */\n beforeInput?: ReactNode\n /** 连接状态条下方的横幅(回放模式条用)。不传时不渲染任何节点。 */\n banner?: ReactNode\n}\n\n/**\n * 消息流 + 输入框:智能体模式与纯 LLM 模式共用同一套外观,差别只在谁喂数据。\n *\n * 这里的 DOM 结构受 tests/chat-view-compat.test.tsx 的快照保护——接入方拿\n * .blade-chat-* 类名写了自己的样式,结构一动他们的页面就歪。\n */\nexport function ChatSurface({\n theme,\n classNames,\n renderers,\n slots,\n placeholder,\n connection,\n errorMessage,\n messages,\n postChatFollowup,\n isStreaming,\n isStopping,\n inputText,\n onInputChange,\n onSuggestion,\n onSend,\n onStop,\n sessionStatus,\n askAnswers,\n onAnswer,\n sessionId,\n isViewer,\n resultFeedbackByEntry,\n onResultFeedbackSaved,\n onFollowupInteraction,\n beforeInput,\n banner,\n}: ChatSurfaceProps) {\n return (\n <div\n data-theme={themeAttr(theme)}\n className={cn(\n \"blade-chat flex min-h-0 flex-1 flex-col overflow-hidden\",\n classNames?.root,\n )}\n >\n <ConnectionBanner connection={connection} className={classNames?.banner} />\n {banner}\n {/* 出错条的配色由 .blade-chat-error-bar 的 --destructive 变量给,别写死 red-*:\n Tailwind 的 dark: 变体跟的是系统偏好,会和 data-theme 主题打架。 */}\n {errorMessage && (\n <div className=\"blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm\">\n <CircleAlert size={16} className=\"mt-0.5 shrink-0\" />\n <span>{errorMessage}</span>\n </div>\n )}\n {slots?.header}\n <MessageList\n messages={messages}\n postChatFollowup={postChatFollowup}\n onSuggestion={onSuggestion}\n isStreaming={isStreaming}\n sessionStatus={sessionStatus}\n askAnswers={askAnswers}\n onAnswer={onAnswer}\n toolCallRenderer={renderers?.toolCall}\n emptyState={slots?.emptyState}\n className={classNames?.messageList}\n sessionId={sessionId}\n isViewer={isViewer}\n resultFeedbackByEntry={resultFeedbackByEntry}\n onResultFeedbackSaved={onResultFeedbackSaved}\n onFollowupInteraction={onFollowupInteraction}\n />\n {beforeInput}\n <ChatInput\n value={inputText}\n onValueChange={onInputChange}\n onSend={onSend}\n onStop={onStop}\n isStreaming={isStreaming}\n isStopping={isStopping}\n placeholder={placeholder}\n className={classNames?.chatInput}\n />\n {slots?.footer}\n </div>\n )\n}\n","import { useEffect, useMemo, useState } from \"react\"\nimport { useLlmChat, type LlmChatOptions } from \"../hooks/use-llm-chat\"\nimport { ChatSurface, type ChatPresentationProps } from \"./ChatSurface\"\nimport {\n LlmAdvancedSettingsBar,\n readOverride,\n writeOverride,\n type LlmAdvancedSettings,\n type LlmOverride,\n} from \"./LlmAdvancedSettings\"\n\n/** 纯 LLM 模式的操作句柄,对应智能体模式的 session.insertText() / send()。 */\nexport interface LlmChatHandle {\n /** 把文本放进输入框交给用户确认,不直接发出去。 */\n insertText: (text: string) => void\n send: (text: string) => Promise<boolean>\n /** 清空这段对话。 */\n reset: () => void\n}\n\nexport interface LlmChatProps extends LlmChatOptions, ChatPresentationProps {\n /** 拿到操作句柄:业务界面把选中的数据塞进输入框,或直接发一句。 */\n onReady?: (handle: LlmChatHandle) => void\n /**\n * 打开输入框上方的「高级设置」,让用户自己改模型服务地址(默认还能改模型名)。\n * 不传就一个节点都不渲染。传 `true` 用默认配置,或用对象细调。\n */\n advanced?: boolean | LlmAdvancedSettings\n /** 用户改了覆盖值时回调,方便宿主自己也记一份。 */\n onOverrideChange?: (override: LlmOverride) => void\n}\n\n/**\n * 纯 LLM 模式的聊天界面:不建会话、不连实时通道、也不要求登录 Blade,\n * 只按 **OpenAI Chat Completions** 标准协议跟模型服务一问一答。\n *\n * 默认形态是把 `baseURL` 指向自己后端的透传路径,由后端补密钥再转发,浏览器不持有密钥。\n */\nexport function LlmChat({\n classNames,\n renderers,\n slots,\n placeholder,\n theme,\n onReady,\n advanced,\n onOverrideChange,\n ...options\n}: LlmChatProps) {\n // 用户在高级设置里填的值盖住调用方传进来的默认值;本轮请求用哪套由这里决定\n const [override, setOverride] = useState<LlmOverride>(() => readOverride(advanced, options.baseURL))\n // 每次渲染新建对象也没关系:useLlmChat 每轮都把配置写进 ref,send 用的是调用那一刻的值\n const effective = { ...options, ...override }\n const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective)\n const [inputText, setInputText] = useState(\"\")\n const [stopRequested, setStopRequested] = useState(false)\n\n // 句柄引用要稳定:随渲染换新对象的话,宿主放进 useEffect 依赖里会连环重跑\n const handle = useMemo<LlmChatHandle>(\n () => ({\n insertText: (text) => setInputText((prev) => (prev ? `${prev}\\n${text}` : text)),\n send,\n reset,\n }),\n [send, reset],\n )\n useEffect(() => {\n onReady?.(handle)\n }, [handle, onReady])\n\n return (\n <ChatSurface\n theme={theme}\n classNames={classNames}\n renderers={renderers}\n slots={slots}\n placeholder={placeholder}\n // 没有\"连接\"这回事:不建会话也不连实时通道,状态条恒为已连接\n connection=\"connected\"\n errorMessage={error}\n messages={messages}\n isStreaming={isStreaming}\n isStopping={stopRequested && isStreaming}\n inputText={inputText}\n onInputChange={setInputText}\n onSend={async (text) => {\n setStopRequested(false)\n if (!text.trim() || isStreaming) return false\n // 只等\"这条已经收下\",不等整段流跑完:等下去的话输入框在整个生成期间都清不掉,\n // 用户中途敲的下一句还会被清掉。失败通过出错条反馈,不靠输入框。\n void send(text)\n return true\n }}\n onStop={() => {\n setStopRequested(true)\n stop()\n }}\n beforeInput={\n advanced ? (\n <LlmAdvancedSettingsBar\n settings={advanced}\n defaults={{ baseURL: options.baseURL, model: options.model }}\n override={override}\n onChange={(next) => {\n setOverride(next)\n writeOverride(advanced, options.baseURL, next)\n onOverrideChange?.(next)\n }}\n />\n ) : undefined\n }\n />\n )\n}\n","import { Settings2 } from \"lucide-react\"\nimport { useState } from \"react\"\n\n/** 纯 LLM 模式的高级设置:让用户自己改模型服务地址。 */\nexport interface LlmAdvancedSettings {\n /** 允许改模型服务地址,默认允许。 */\n baseURL?: boolean\n /** 允许改模型名,默认允许。 */\n model?: boolean\n /**\n * 允许在界面上填密钥,默认**不允许**。\n * 打开意味着密钥存在浏览器里,只适合受控内网、演示,或密钥本身就属于当前用户的场景。\n */\n apiKey?: boolean\n /** 覆盖值存哪:`local` 记住用户的选择(默认),`memory` 只在本次会话有效。 */\n storage?: \"local\" | \"memory\"\n /** localStorage 的键,默认按组件拿到的 baseURL 生成。 */\n storageKey?: string\n}\n\nexport interface LlmOverride {\n baseURL?: string\n model?: string\n apiKey?: string\n}\n\nconst FIELDS: Array<{ id: keyof LlmOverride; label: string; placeholder: string; secret?: boolean }> = [\n { id: \"baseURL\", label: \"模型服务地址\", placeholder: \"http://内网地址/v1\" },\n { id: \"model\", label: \"模型\", placeholder: \"模型名称\" },\n { id: \"apiKey\", label: \"密钥\", placeholder: \"留空表示不改\", secret: true },\n]\n\nexport function normalizeAdvanced(\n value: boolean | LlmAdvancedSettings | undefined,\n): Required<Pick<LlmAdvancedSettings, \"baseURL\" | \"model\" | \"apiKey\" | \"storage\">> | null {\n if (!value) return null\n const config = value === true ? {} : value\n return {\n baseURL: config.baseURL ?? true,\n model: config.model ?? true,\n apiKey: config.apiKey ?? false,\n storage: config.storage ?? \"local\",\n }\n}\n\nfunction storageKeyFor(settings: boolean | LlmAdvancedSettings | undefined, baseURL: string): string {\n const explicit = typeof settings === \"object\" ? settings.storageKey : undefined\n return `blade-llm-override:${explicit ?? baseURL}`\n}\n\nexport function readOverride(\n settings: boolean | LlmAdvancedSettings | undefined,\n baseURL: string,\n): LlmOverride {\n const normalized = normalizeAdvanced(settings)\n if (!normalized || normalized.storage !== \"local\" || typeof localStorage === \"undefined\") return {}\n try {\n const raw = localStorage.getItem(storageKeyFor(settings, baseURL))\n if (!raw) return {}\n const stored = JSON.parse(raw) as LlmOverride\n // 只认当前还允许改的字段:宿主把密钥字段关掉之后,之前存下的密钥不能继续\n // 被悄悄发出去(同一个 storageKey 被别处写过也一样)。\n return Object.fromEntries(\n Object.entries(stored).filter(([key]) => normalized[key as keyof LlmOverride]),\n ) as LlmOverride\n } catch {\n // 隐私模式等场景读不到就当没设过\n return {}\n }\n}\n\nfunction writeOverride(\n settings: boolean | LlmAdvancedSettings | undefined,\n baseURL: string,\n override: LlmOverride,\n): void {\n const normalized = normalizeAdvanced(settings)\n if (!normalized || normalized.storage !== \"local\" || typeof localStorage === \"undefined\") return\n try {\n const key = storageKeyFor(settings, baseURL)\n if (Object.keys(override).length === 0) localStorage.removeItem(key)\n else localStorage.setItem(key, JSON.stringify(override))\n } catch {\n // 写不进去就退化成只在本次会话有效\n }\n}\n\ninterface Props {\n settings: boolean | LlmAdvancedSettings\n /** 组件拿到的默认值,用来做占位提示与\"恢复默认\" */\n defaults: { baseURL: string; model: string }\n override: LlmOverride\n onChange: (next: LlmOverride) => void\n}\n\n/**\n * 输入框上方的一行「高级设置」:默认收起,展开后可以改模型服务地址与模型。\n * 只有调用方显式打开才渲染——不传 advanced 时这里一个节点都不会出现。\n */\nexport function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }: Props) {\n const normalized = normalizeAdvanced(settings)\n const [open, setOpen] = useState(false)\n const [draft, setDraft] = useState<LlmOverride>(override)\n if (!normalized) return null\n\n const fields = FIELDS.filter((field) => normalized[field.id])\n const dirty = Object.keys(override).length > 0\n\n return (\n <div className=\"blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs\">\n <button\n type=\"button\"\n onClick={() => {\n setDraft(override)\n setOpen((value) => !value)\n }}\n className=\"flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]\"\n >\n <Settings2 size={13} />\n 高级设置\n {dirty && <span className=\"text-[hsl(var(--primary))]\">(已自定义)</span>}\n </button>\n {open && (\n <div className=\"mt-2 flex flex-col gap-2\">\n {fields.map((field) => (\n <label key={field.id} className=\"flex flex-col gap-1\">\n <span className=\"text-[hsl(var(--muted-foreground))]\">{field.label}</span>\n <input\n type={field.secret ? \"password\" : \"text\"}\n value={draft[field.id] ?? \"\"}\n placeholder={\n field.id === \"apiKey\"\n ? field.placeholder\n : (defaults[field.id as \"baseURL\" | \"model\"] || field.placeholder)\n }\n onChange={(event) => setDraft({ ...draft, [field.id]: event.target.value })}\n className=\"rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-2 py-1 text-xs text-[hsl(var(--foreground))] outline-none\"\n />\n </label>\n ))}\n {normalized.apiKey && (\n <p className=\"text-[hsl(var(--muted-foreground))]\">\n 密钥会存在这台浏览器里。只在你信得过这台机器时填。\n </p>\n )}\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n // 空字符串等于没改,不要把空值当成覆盖发出去\n const next = Object.fromEntries(\n Object.entries(draft).filter(([, value]) => String(value ?? \"\").trim() !== \"\"),\n ) as LlmOverride\n onChange(next)\n setOpen(false)\n }}\n className=\"rounded-md bg-[hsl(var(--primary))] px-2.5 py-1 font-medium text-[hsl(var(--primary-foreground))]\"\n >\n 保存\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setDraft({})\n onChange({})\n setOpen(false)\n }}\n className=\"rounded-md border border-[hsl(var(--border))] px-2.5 py-1 text-[hsl(var(--foreground))]\"\n >\n 恢复默认\n </button>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport { writeOverride }\n","import { AgentChat, type AgentChatProps } from \"./AgentChat\"\nimport { LlmChat, type LlmChatHandle } from \"./LlmChat\"\nimport type { LlmAdvancedSettings, LlmOverride } from \"./LlmAdvancedSettings\"\nimport type { LlmChatOptions } from \"../hooks/use-llm-chat\"\n\nexport type {\n ChatViewClassNames,\n ChatViewRenderers,\n ChatViewSlots,\n} from \"./ChatSurface\"\nexport type { FollowupInteractionEvent } from \"./PostChatFollowupBlock\"\n\nexport interface ChatViewProps extends AgentChatProps {\n /** 挂哪个下层组件。默认 `agent`——不传就是接 Blade 智能体会话的原有行为。 */\n mode?: \"agent\" | \"llm\"\n /** `mode=\"llm\"` 时的模型服务配置;`advanced` 打开输入框上方的「高级设置」。 */\n llm?: LlmChatOptions & {\n advanced?: boolean | LlmAdvancedSettings\n onOverrideChange?: (override: LlmOverride) => void\n }\n /** `mode=\"llm\"` 时拿到操作句柄;智能体模式请用 `onSessionReady`。 */\n onLlmReady?: (handle: LlmChatHandle) => void\n}\n\n/**\n * 聊天界面。默认接 Blade 智能体会话,`mode=\"llm\"` 时改接标准 OpenAI 对话接口。\n *\n * 这一层只做分流:两种模式各自是独立组件,已经确定要哪一种的可以直接用\n * `<AgentChat>` 或 `<LlmChat>`。\n */\nexport function ChatView(props: ChatViewProps) {\n // 参数保持单个 props、在函数体里解构:公共面报告会把参数列表原样印出来,\n // 在参数位置解构会让签名行发生变化,而这一行是接入方看兼容性的依据。\n const { mode, llm, onLlmReady, ...rest } = props\n if (mode === \"llm\") {\n if (!llm) {\n throw new Error('ChatView: mode=\"llm\" 需要同时传 llm={{ baseURL, model }}')\n }\n return (\n <LlmChat\n {...llm}\n classNames={rest.classNames}\n renderers={rest.renderers}\n slots={rest.slots}\n placeholder={rest.placeholder}\n theme={rest.theme}\n onReady={onLlmReady}\n />\n )\n }\n return <AgentChat {...rest} />\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\"\nexport { useReplay } from \"./hooks/use-replay\"\nexport type { ReplayMismatch, UseReplayResult } from \"./hooks/use-replay\"\nexport { useLlmChat } from \"./hooks/use-llm-chat\"\nexport type {\n ChatCompletionTool,\n LlmChatOptions,\n LlmToolCall,\n UseLlmChatResult,\n} from \"./hooks/use-llm-chat\"\n\n// ============================== 聊天界面 ==============================\nexport { ChatView } from \"./components/ChatView\"\nexport type {\n ChatViewClassNames,\n ChatViewProps,\n ChatViewRenderers,\n ChatViewSlots,\n FollowupInteractionEvent,\n} from \"./components/ChatView\"\n// 下层组件:已经确定要哪种模式的可以直接用,不必经 ChatView 分流\nexport { AgentChat } from \"./components/AgentChat\"\nexport type { AgentChatProps } from \"./components/AgentChat\"\nexport { LlmChat } from \"./components/LlmChat\"\nexport type { LlmChatHandle, LlmChatProps } from \"./components/LlmChat\"\nexport type { LlmAdvancedSettings, LlmOverride } from \"./components/LlmAdvancedSettings\"\nexport { MarkdownContent } from \"./components/MarkdownContent\"\nexport type { MarkdownContentProps } from \"./components/MarkdownContent\"\nexport { ReplayBar } from \"./components/ReplayBar\"\nexport type { ReplayBarProps } from \"./components/ReplayBar\"\nexport { ReplayMismatchPrompt } from \"./components/ReplayMismatchPrompt\"\nexport type { ReplayMismatchPromptProps } from \"./components/ReplayMismatchPrompt\"\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","useState","useEffect","useCallback","useMemo","useRef","useState","Component","forwardRef","createElement","useCallback","useEffect","useMemo","useState","jsx","jsx","jsxs","jsx","jsxs","jsx","jsxs","useCallback","useEffect","useMemo","useRef","useState","useCallback","useMemo","useRef","useState","isAtBottom","escapedFromLock","createContext","useContext","useEffect","useMemo","useRef","createContext","useEffect","useRef","useMemo","targetScrollTop","StickToBottom","useContext","useState","useState","jsx","jsxs","useState","useEffect","useMemo","useRef","useState","jsx","jsxs","useRef","useState","useEffect","useMemo","jsx","useState","useEffect","useMemo","useState","jsx","jsxs","useState","useEffect","useMemo","text","jsx","jsxs","useState","jsx","jsxs","useState","jsx","jsxs","useCallback","useEffect","useRef","useState","Fragment","jsx","jsxs","useState","Icon","useRef","useEffect","useCallback","getTextContent","jsx","jsxs","getTextContent","jsx","jsxs","useMemo","useRef","useEffect","useState","useCallback","jsx","jsxs","Fragment","jsx","jsxs","useState","useEffect","useMemo","useCallback","useEffect","useMemo","useState","useState","jsx","jsxs","jsx","useState","useMemo","useEffect","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.tsx","../src/hooks/use-agent-session.ts","../src/hooks/use-replay.ts","../src/hooks/use-llm-chat.ts","../src/components/AgentChat.tsx","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/shared/src/utils.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/defaultAttributes.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/Icon.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/createLucideIcon.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/archive.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/arrow-up.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/bot.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/brain.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/check.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/chevron-down.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/chevron-right.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/circle-alert.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/copy.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/download.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file-code-2.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file-spreadsheet.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file-text.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/file.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/film.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/image.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/layers.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/lightbulb.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/loader-circle.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/lock-keyhole.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/message-square-more.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/message-square.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/music.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/play.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/settings-2.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/square.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/triangle-alert.ts","../../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/src/icons/x.ts","../src/lib/utils.ts","../src/components/ReplayBar.tsx","../src/components/ReplayMismatchPrompt.tsx","../src/components/ChatInput.tsx","../src/components/ConnectionBanner.tsx","../src/components/MessageList.tsx","../../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js","../../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js","../src/components/AssistantTurnBlock.tsx","../src/components/AgentLoopBlock.tsx","../src/components/display-utils.ts","../src/components/MarkdownContent.tsx","../src/lib/media-tags.ts","../src/components/FileCard.tsx","../src/components/Shimmer.tsx","../src/components/ToolCallBlock.tsx","../src/components/AskUserQuestionBlock.tsx","../src/components/RenderErrorBoundary.tsx","../src/components/UserMessageBubble.tsx","../src/components/ChatSurface.tsx","../src/components/LlmChat.tsx","../src/components/LlmAdvancedSettings.tsx","../src/components/ChatView.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 /** 连接或创建失败的错误。 */\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 { DEFAULT_REPLAY_SPEED } from \"@blade-hq/agent-client\"\nimport type { AgentSession, ReplaySpeed, SessionState } from \"@blade-hq/agent-client\"\nimport { useCallback, useEffect, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\n\n/** 回放中用户说的话和录制内容对不上,需要当场决定往哪走。 */\nexport interface ReplayMismatch {\n /** 用户这次实际发出的内容。 */\n actualMessage: string\n /** 源会话里录制的下一句。 */\n expectedMessage: string\n /** keep_replay:按录制内容继续;continue_replay:从这里转为真实运行。 */\n resolve: (decision: \"keep_replay\" | \"continue_replay\") => void\n}\n\nexport interface UseReplayResult {\n /** 当前会话是不是正在回放(转自主运行后为 false)。 */\n isReplay: boolean\n /** 当前播放倍速,非回放会话固定为 1。 */\n speed: ReplaySpeed\n setSpeed: (speed: ReplaySpeed) => Promise<void>\n /** 退出回放,之后的对话交给真实模型运行。 */\n exitToAutonomous: () => Promise<void>\n /** 待决策的输入冲突;处理完自动清空。 */\n mismatch: ReplayMismatch | null\n /** 能不能改回放(改回放是 owner 专属)。只读身份下仍然显示\"在回放\",但不能操作。 */\n canControl: boolean\n\n /** 当前会话能否作为回放来源(并行子智能体等场景不支持)。 */\n canReplay: boolean\n /** canReplay 为 false 时的原因,可直接展示。 */\n unsupportedReason: string | null\n /** 从当前会话派生一个回放会话,返回新会话 id。 */\n startReplay: (speed?: ReplaySpeed) => Promise<string>\n isStarting: boolean\n /** 最近一次回放操作失败的原因。 */\n error: Error | null\n}\n\n/** 正在创建回放会话的源会话;按会话共享,多个 useReplay 实例也只会建出一个。 */\nconst replayCreating = new Set<string>()\n\n/**\n * 会话回放(演示 / 彩排模式)。\n *\n * 回放会话不会自动播放:用户照常发消息,只要这句和源会话录制的下一句意图一致,\n * 服务端就按倍速重放当时的回复和工具调用,完全不调用模型;对不上就抛出\n * `mismatch` 交给界面决定。\n *\n * 同一个 hook 覆盖两种身份——传入的会话既可能是回放会话(isReplay / setSpeed /\n * exitToAutonomous / mismatch),也可能是准备被回放的源会话(canReplay /\n * startReplay)。\n *\n * 回放状态本身住在 `SessionState.replay` 里(非 React 宿主直接订阅它即可),\n * 这里只做 React 绑定:订阅快照 + 接管冲突 + 拉一次 preview。\n *\n * **注册即接管**:底层的 `replayMismatch` 只交给最先注册的处理器。调用本 hook\n * 就意味着由它接管冲突;不调用则维持 SDK 默认行为——直接转为真实运行。\n */\nexport function useReplay(session: AgentSession | null): UseReplayResult {\n const client = useBladeClient()\n const sessionId = session?.sessionId ?? null\n\n const [replay, setReplay] = useState<SessionState[\"replay\"]>(null)\n const [viewerRole, setViewerRole] = useState<SessionState[\"viewerRole\"]>(null)\n // 运行中的会话不能当回放素材(服务端也这么判),跑完要重新问一次能不能回放。\n const [sourceRunning, setSourceRunning] = useState(false)\n const [mismatch, setMismatch] = useState<ReplayMismatch | null>(null)\n // 加载中不能当\"支持\"用:慢网络下一打开就点,会绕过这道检查发出必然失败的请求。\n const [preview, setPreview] = useState<\n { status: \"loading\" } | { status: \"ready\"; supported: boolean; reason: string | null }\n >({ status: \"loading\" })\n const [isStarting, setIsStarting] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n // 还没开始回放时倍速只是个本地选择,startReplay 时才带给服务端。\n const [draftSpeed, setDraftSpeed] = useState<ReplaySpeed>(DEFAULT_REPLAY_SPEED)\n\n // 回放状态跟着会话快照走;AgentSession 在 bootstrap 时就填好了初始值。\n useEffect(() => {\n if (!session) {\n setReplay(null)\n setViewerRole(null)\n setSourceRunning(false)\n return\n }\n const pull = () => {\n const state = session.getState()\n setReplay(state.replay)\n setViewerRole(state.viewerRole)\n setSourceRunning(state.isStreaming || state.status === \"running\")\n }\n pull()\n return session.subscribe(pull)\n }, [session])\n\n // 换会话才重置这些:倍速是跟着源会话走的选择,上一个会话选的 1x 不该带过来;\n // 冲突和错误同理。在渲染期比较而不是用 effect——effect 会先渲染一帧旧值,\n // 而且不能和下面的 preview 查询合并(那个还要跟着运行状态重跑,会把用户\n // 刚选的倍速冲掉)。\n const [lastSessionId, setLastSessionId] = useState(sessionId)\n if (sessionId !== lastSessionId) {\n setLastSessionId(sessionId)\n setMismatch(null)\n setError(null)\n setDraftSpeed(DEFAULT_REPLAY_SPEED)\n }\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: sourceRunning 跨过\"运行中\"这条线就是重新查询的触发条件——这一轮跑完了本该能回放,新一轮开始了就不该再放行\n useEffect(() => {\n setPreview({ status: \"loading\" })\n if (!sessionId) return\n let alive = true\n client.sessions\n .getReplayPreview(sessionId)\n .then((result) => {\n if (!alive) return\n setPreview({\n status: \"ready\",\n supported: result.supported,\n reason: result.reason ?? null,\n })\n })\n .catch(() => {\n // 查不到就放行,让服务端在真正创建时把关——但那是\"问过了没结论\",\n // 和\"还没问\"不是一回事,后者必须继续禁用。\n if (alive) setPreview({ status: \"ready\", supported: true, reason: null })\n })\n return () => {\n alive = false\n }\n }, [client, sessionId, sourceRunning])\n\n useEffect(() => {\n if (!session) return\n return session.on(\"replayMismatch\", ({ actualMessage, expectedMessage, respond }) => {\n setMismatch({\n actualMessage: actualMessage ?? \"\",\n expectedMessage: expectedMessage ?? \"\",\n resolve: (decision) => {\n setMismatch(null)\n respond(decision)\n },\n })\n })\n }, [session])\n\n const isReplay = replay?.isReplay ?? false\n\n const setSpeed = useCallback(\n async (next: ReplaySpeed) => {\n setDraftSpeed(next)\n if (!session) return\n setError(null)\n try {\n await session.setReplaySpeed(next)\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n },\n [session],\n )\n\n const exitToAutonomous = useCallback(async () => {\n if (!session) return\n setError(null)\n // 冲突还没决定就点退出:那条消息已经发出去、输入框锁着,只改回放状态的话\n // 卡片会留在屏幕上、对话卡死。\"不回放了\"对应的决定就是让它真的运行,\n // 而 continue_replay 本身就会转自主,不用再 PATCH 一次。\n const pending = mismatch\n if (pending) {\n pending.resolve(\"continue_replay\")\n return\n }\n try {\n await session.exitReplay()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n }, [session, mismatch])\n\n const startReplay = useCallback(\n async (speed?: ReplaySpeed) => {\n if (!sessionId) throw new Error(\"当前没有可回放的会话\")\n // 不能只看 isStarting:state 在同一次渲染里还没更新,多个组件各挂一个\n // hook 实例时也各有各的 state。\n if (replayCreating.has(sessionId)) throw new Error(\"正在创建回放会话,请稍候\")\n replayCreating.add(sessionId)\n setIsStarting(true)\n setError(null)\n try {\n const result = await client.sessions.startReplaySession(sessionId, speed ?? draftSpeed)\n return result.session_id\n } catch (err) {\n const failure = err instanceof Error ? err : new Error(String(err))\n setError(failure)\n throw failure\n } finally {\n replayCreating.delete(sessionId)\n setIsStarting(false)\n }\n },\n [client, sessionId, draftSpeed],\n )\n\n return {\n isReplay,\n speed: isReplay ? (replay?.speed ?? 1) : draftSpeed,\n setSpeed,\n exitToAutonomous,\n mismatch,\n canControl: viewerRole !== \"viewer\",\n canReplay:\n Boolean(sessionId) &&\n preview.status === \"ready\" &&\n preview.supported &&\n viewerRole !== \"viewer\",\n unsupportedReason:\n preview.status === \"ready\" && !preview.supported ? preview.reason : null,\n startReplay,\n isStarting,\n error,\n }\n}\n","import type { ChatMessage, ToolCallInfo } from \"@blade-hq/agent-client\"\nimport { useCallback, useMemo, useRef, useState } from \"react\"\n\n/** OpenAI 标准的工具声明,原样发给模型服务。 */\nexport interface ChatCompletionTool {\n type: \"function\"\n function: {\n name: string\n description?: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface LlmToolCall {\n id: string\n name: string\n /** 模型生成的 JSON 字符串,是不可信输入,用之前自己校验。 */\n arguments: string\n}\n\nexport interface LlmChatOptions {\n /** OpenAI 兼容根地址,如 `/api/llm`(自己后端的透传路径)或 `http://host:30000/v1`。 */\n baseURL: string\n model: string\n /**\n * 直连模型服务时才需要。默认形态是让 `baseURL` 指向自己的后端,由后端补密钥转发,\n * 浏览器不持有密钥。\n */\n apiKey?: string\n headers?: Record<string, string>\n /** 系统提示词,合成到消息最前面。 */\n system?: string\n /** 每次带上最近多少轮,默认 12。越长越贵,也越容易超上下文。 */\n historyTurns?: number\n temperature?: number\n /** 透传给模型服务的额外字段。 */\n extraBody?: Record<string, unknown>\n tools?: ChatCompletionTool[]\n /** 执行模型请求的工具,返回值会被序列化后回喂给模型。 */\n onToolCall?: (call: LlmToolCall) => Promise<unknown>\n /** 工具最多来回几轮,默认 4,防止模型绕圈烧 token。 */\n maxToolRounds?: number\n fetchImpl?: typeof fetch\n}\n\nexport interface UseLlmChatResult {\n messages: ChatMessage[]\n isStreaming: boolean\n /** 出错原因;下一次发送会清掉。 */\n error: string | null\n send: (text: string) => Promise<boolean>\n stop: () => void\n reset: () => void\n}\n\ninterface OpenAiToolCall {\n id: string\n type: \"function\"\n function: { name: string; arguments: string }\n}\n\ntype OpenAiMessage =\n | { role: \"user\" | \"assistant\"; content: string; tool_calls?: OpenAiToolCall[] }\n | { role: \"tool\"; tool_call_id: string; content: string }\n\nconst DEFAULT_HISTORY_TURNS = 12\nconst DEFAULT_MAX_TOOL_ROUNDS = 4\n\n/** 补 id 用的序号。全局递增即可,只要同一段对话里不重复。 */\nlet syntheticIdSeq = 0\n\n/**\n * 纯 LLM 对话的状态机,协议就是 **OpenAI Chat Completions**——任何兼容实现都能直接接,\n * 不需要为这个组件专门写后端。\n *\n * 工具调用走 OpenAI 标准的多轮流程:模型返回 tool_calls → 调 onToolCall 执行 →\n * 结果作为 role:\"tool\" 消息回喂 → 再请求一次。\n */\nexport function useLlmChat(options: LlmChatOptions): UseLlmChatResult {\n const [history, setHistory] = useState<OpenAiMessage[]>([])\n const [streamingText, setStreamingText] = useState<string | null>(null)\n const [streamingCalls, setStreamingCalls] = useState<OpenAiToolCall[]>([])\n const [failedToolIds, setFailedToolIds] = useState<string[]>([])\n const [error, setError] = useState<string | null>(null)\n const [isStreaming, setIsStreaming] = useState(false)\n const abortRef = useRef<AbortController | null>(null)\n // reset 会把对话清空;正在跑的那轮不能事后再往里补消息\n const generationRef = useRef(0)\n // 用 ref 读:send 不该因为消息或配置变化而换引用,否则宿主的 useEffect 会连环重跑\n const historyRef = useRef<OpenAiMessage[]>([])\n const optionsRef = useRef(options)\n optionsRef.current = options\n historyRef.current = history\n\n const stop = useCallback(() => {\n abortRef.current?.abort()\n abortRef.current = null\n }, [])\n\n const reset = useCallback(() => {\n generationRef.current += 1\n stop()\n setHistory([])\n setStreamingText(null)\n setStreamingCalls([])\n setFailedToolIds([])\n setError(null)\n setIsStreaming(false)\n }, [stop])\n\n const send = useCallback(async (text: string): Promise<boolean> => {\n const content = text.trim()\n if (!content || abortRef.current) return false\n const opts = optionsRef.current\n const maxRounds = opts.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS\n\n const commit = (message: OpenAiMessage) => {\n historyRef.current = [...historyRef.current, message]\n setHistory(historyRef.current)\n }\n\n setError(null)\n setIsStreaming(true)\n setStreamingText(\"\")\n setStreamingCalls([])\n commit({ role: \"user\", content })\n // 停止时要把已经吐出来的部分留在对话里,所以本地也累一份——state 在闭包里读不到\n let partial = \"\"\n\n const controller = new AbortController()\n abortRef.current = controller\n const generation = generationRef.current\n try {\n for (let round = 0; ; round += 1) {\n const result = await streamOnce(opts, historyRef.current, controller.signal, {\n onText: (chunk) => {\n partial += chunk\n setStreamingText((prev) => `${prev ?? \"\"}${chunk}`)\n },\n onToolCalls: (calls) => setStreamingCalls(calls),\n })\n\n const assistant: OpenAiMessage = {\n role: \"assistant\",\n content: result.content,\n ...(result.toolCalls.length ? { tool_calls: result.toolCalls } : {}),\n }\n setStreamingText(null)\n setStreamingCalls([])\n commit(assistant)\n\n if (!result.toolCalls.length) return true\n // 提交过的 assistant 带着 tool_calls,就必须有对应的 role:\"tool\" 回复,\n // 否则下一轮请求里是一组没人应答的工具调用,严格的 OpenAI 兼容服务直接 400,\n // 这段对话从此就废了。提前收尾时补上说明,让模型知道没执行。\n // 没执行成功的要标成失败,否则界面给个绿勾,等于骗人说查过了\n const bail = (reason: string) => {\n for (const call of result.toolCalls) {\n commit({ role: \"tool\", tool_call_id: call.id, content: JSON.stringify({ error: reason }) })\n }\n setFailedToolIds((prev) => [...prev, ...result.toolCalls.map((call) => call.id)])\n }\n if (!opts.onToolCall) {\n bail(\"这个应用没有提供工具执行入口\")\n return true\n }\n if (round >= maxRounds) {\n bail(`工具调用已达上限 ${maxRounds} 轮,没有执行`)\n setError(`工具调用超过 ${maxRounds} 轮仍未给出结论,已停下。`)\n return false\n }\n\n for (const call of result.toolCalls) {\n let output: string\n try {\n const value = await opts.onToolCall({\n id: call.id,\n name: call.function.name,\n arguments: call.function.arguments,\n })\n output = typeof value === \"string\" ? value : JSON.stringify(value ?? null)\n } catch (err) {\n // 执行失败也要回喂给模型,让它自己决定换个方式还是告诉用户\n output = JSON.stringify({ error: err instanceof Error ? err.message : String(err) })\n setFailedToolIds((prev) => [...prev, call.id])\n }\n commit({ role: \"tool\", tool_call_id: call.id, content: output })\n }\n setStreamingText(\"\")\n }\n } catch (err) {\n setStreamingText(null)\n setStreamingCalls([])\n if (controller.signal.aborted) {\n // reset 也走 abort,但它是\"把这段对话清空\",不能再往里补消息\n if (generation !== generationRef.current) return false\n // 保留已经看到的内容:清成\"(已停止)\"等于把用户刚读到的答案抹掉,\n // 下一轮的上下文也跟着丢了\n commit({ role: \"assistant\", content: partial ? `${partial}(已停止)` : \"(已停止)\" })\n return false\n }\n // 和\"停止\"一样保留已经吐出来的部分:网关在流中途下发 error(限流、上游断连)\n // 时把半屏答案抹掉,用户刚读的内容没了,重试时模型也看不到这段上下文\n if (partial && generation === generationRef.current) {\n commit({ role: \"assistant\", content: partial })\n }\n setError(err instanceof Error && err.message ? err.message : \"对话失败,请重试\")\n return false\n } finally {\n if (abortRef.current === controller) abortRef.current = null\n setIsStreaming(false)\n }\n }, [])\n\n const messages = useMemo(\n () => toChatMessages(history, streamingText, streamingCalls, failedToolIds),\n [history, streamingText, streamingCalls, failedToolIds],\n )\n\n return { messages, isStreaming, error, send, stop, reset }\n}\n\ninterface StreamResult {\n content: string\n toolCalls: OpenAiToolCall[]\n}\n\n/** 发一次请求并消费流;返回这一轮模型给出的完整内容与工具调用。 */\nasync function streamOnce(\n options: LlmChatOptions,\n history: OpenAiMessage[],\n signal: AbortSignal,\n handlers: { onText: (chunk: string) => void; onToolCalls: (calls: OpenAiToolCall[]) => void },\n): Promise<StreamResult> {\n const {\n baseURL,\n model,\n apiKey,\n headers,\n system,\n historyTurns = DEFAULT_HISTORY_TURNS,\n temperature,\n extraBody,\n tools,\n fetchImpl,\n } = options\n\n const trimmed = trimHistory(history, historyTurns)\n const payload = {\n model,\n stream: true,\n messages: system ? [{ role: \"system\", content: system }, ...trimmed] : trimmed,\n ...(tools?.length ? { tools } : {}),\n ...(temperature === undefined ? {} : { temperature }),\n ...extraBody,\n }\n\n const doFetch = fetchImpl ?? globalThis.fetch\n const url = `${baseURL.replace(/\\/$/, \"\")}/chat/completions`\n let response: Response\n try {\n response = await doFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n ...headers,\n },\n body: JSON.stringify(payload),\n signal,\n })\n } catch (err) {\n if (signal.aborted) throw err\n // 浏览器给的原始信息是 \"Failed to fetch\",看不出是哪台服务连不上。\n // 地址填错是最常见的故障,必须把地址显示出来。\n throw new Error(`连不上模型服务 ${url}:${err instanceof Error ? err.message : String(err)}`)\n }\n if (!response.ok) {\n throw new Error(await describeHttpError(response))\n }\n if (!response.body) {\n throw new Error(\"模型服务没有返回流式响应\")\n }\n\n let content = \"\"\n const calls: OpenAiToolCall[] = []\n for await (const chunk of readSse(response.body, signal)) {\n if (chunk.error) {\n throw new Error(chunk.error)\n }\n if (chunk.text) {\n content += chunk.text\n handlers.onText(chunk.text)\n }\n if (chunk.toolCallDeltas) {\n for (const delta of chunk.toolCallDeltas) {\n // 工具调用按 index 分片下发:id 与函数名通常只在第一片,参数逐片拼\n const index = delta.index ?? 0\n const existing = calls[index] ?? { id: \"\", type: \"function\" as const, function: { name: \"\", arguments: \"\" } }\n calls[index] = {\n id: delta.id ?? existing.id,\n type: \"function\",\n function: {\n name: delta.function?.name ?? existing.function.name,\n arguments: `${existing.function.arguments}${delta.function?.arguments ?? \"\"}`,\n },\n }\n }\n handlers.onToolCalls(calls.filter(Boolean).map((call) => ({ ...call })))\n }\n }\n const normalized = calls\n .filter(Boolean)\n // 流里会出现只有 arguments、没有函数名的分片;累积后如果仍然没名字,说明它不是\n // 一次真的调用(网关分片的产物)。留着会让宿主的 onToolCall 收到空名字,\n // 只能抛\"没有这个函数\",整轮就废了。\n .filter((call) => call.function.name)\n // 有的实现不给 id,补一个,否则回喂时对不上。序号必须跨轮唯一:\n // 每轮都从 call_0 开始的话,工具结果和失败标记会串到上一轮的卡片上。\n .map((call) => {\n if (call.id) return call\n syntheticIdSeq += 1\n return { ...call, id: `call_auto_${syntheticIdSeq}` }\n })\n return { content, toolCalls: normalized }\n}\n\n/** 按轮数截断历史,但不能把工具消息与它所属的助手消息切散。 */\nfunction trimHistory(history: OpenAiMessage[], historyTurns: number): OpenAiMessage[] {\n const userIndexes = history.reduce<number[]>((acc, msg, i) => {\n if (msg.role === \"user\") acc.push(i)\n return acc\n }, [])\n if (userIndexes.length <= historyTurns) return history\n return history.slice(userIndexes[userIndexes.length - historyTurns])\n}\n\nasync function describeHttpError(response: Response): Promise<string> {\n const raw = await response.text().catch(() => \"\")\n try {\n const parsed = JSON.parse(raw) as { error?: { message?: string } | string; detail?: unknown }\n const message =\n typeof parsed.error === \"string\"\n ? parsed.error\n : (parsed.error?.message ??\n (typeof parsed.detail === \"string\" ? parsed.detail : undefined))\n if (message) return `模型服务返回 ${response.status}:${message}`\n } catch {\n // 不是 JSON 就用原文\n }\n const snippet = raw.trim().slice(0, 120)\n return snippet ? `模型服务返回 ${response.status}:${snippet}` : `模型服务返回 ${response.status}`\n}\n\ninterface StreamChunk {\n text?: string\n toolCallDeltas?: Array<{\n index?: number\n id?: string\n function?: { name?: string; arguments?: string }\n }>\n error?: string\n}\n\n/**\n * 读 OpenAI 的流式响应。事件之间用空行分隔,`data: [DONE]` 收尾。\n * 必须按 `\\n\\n` 缓冲——一个事件可能横跨两个网络分片,这是实测踩过的。\n */\nasync function* readSse(\n stream: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n): AsyncGenerator<StreamChunk> {\n const reader = stream.getReader()\n const decoder = new TextDecoder()\n let buffer = \"\"\n try {\n while (!signal.aborted) {\n const { done, value } = await reader.read()\n if (done) {\n // 不少兼容网关最后一个 data: 后面不补空行、也不发 [DONE]。不收尾解析的话,\n // 回答的最后一段会静默丢失;丢的要是工具参数的最后一片,宿主拿到的就是\n // 截断的 JSON。\n const tail = parseSse(buffer)\n if (tail && tail !== \"done\") yield tail\n break\n }\n // SSE 规范允许 CRLF;只认 \\n\\n 的话,用 CRLF 的服务永远解析不出事件。\n // 归一必须在拼接之后做:网络分片可能正好切在 \\r 和 \\n 中间,先替换再拼会\n // 留下一个 \\n\\r\\n,边界就再也认不出来了。\n buffer = `${buffer}${decoder.decode(value, { stream: true })}`.replace(/\\r\\n/g, \"\\n\")\n let boundary = buffer.indexOf(\"\\n\\n\")\n while (boundary !== -1) {\n const chunk = parseSse(buffer.slice(0, boundary))\n buffer = buffer.slice(boundary + 2)\n if (chunk === \"done\") return\n if (chunk) yield chunk\n boundary = buffer.indexOf(\"\\n\\n\")\n }\n }\n } finally {\n reader.cancel().catch(() => {})\n }\n}\n\nfunction parseSse(raw: string): StreamChunk | \"done\" | null {\n const dataLines: string[] = []\n for (const line of raw.split(\"\\n\")) {\n if (line.startsWith(\"data:\")) dataLines.push(line.slice(5).trim())\n }\n if (!dataLines.length) return null\n const data = dataLines.join(\"\\n\")\n if (data === \"[DONE]\") return \"done\"\n let parsed: {\n choices?: Array<{ delta?: { content?: string; tool_calls?: StreamChunk[\"toolCallDeltas\"] } }>\n error?: { message?: string } | string\n }\n try {\n parsed = JSON.parse(data)\n } catch {\n return null\n }\n if (parsed.error) {\n const message = typeof parsed.error === \"string\" ? parsed.error : parsed.error.message\n return { error: message || \"模型服务返回了错误\" }\n }\n const delta = parsed.choices?.[0]?.delta\n if (!delta) return null\n return { text: delta.content, toolCallDeltas: delta.tool_calls }\n}\n\n/** OpenAI 形状 → SDK 的 ChatMessage:工具结果并进对应的 tool_calls,不单独成条。 */\nfunction toChatMessages(\n history: OpenAiMessage[],\n streamingText: string | null,\n streamingCalls: OpenAiToolCall[],\n failedToolIds: string[],\n): ChatMessage[] {\n const results = new Map<string, string>()\n for (const msg of history) {\n if (msg.role === \"tool\") results.set(msg.tool_call_id, msg.content)\n }\n\n const messages: ChatMessage[] = []\n for (const msg of history) {\n if (msg.role === \"tool\") continue\n if (msg.role === \"user\") {\n messages.push({ role: \"user\", content: msg.content, status: \"completed\" })\n continue\n }\n messages.push({\n role: \"assistant\",\n content: msg.content,\n status: \"completed\",\n ...(msg.tool_calls?.length\n ? { tool_calls: msg.tool_calls.map((call) => toToolCallInfo(call, results, failedToolIds)) }\n : {}),\n })\n }\n\n if (streamingText !== null) {\n messages.push({\n role: \"assistant\",\n content: streamingText,\n status: \"streaming\",\n ...(streamingCalls.length\n ? { tool_calls: streamingCalls.map((call) => toToolCallInfo(call, results, failedToolIds)) }\n : {}),\n })\n }\n return messages\n}\n\nfunction toToolCallInfo(\n call: OpenAiToolCall,\n results: Map<string, string>,\n failedToolIds: string[],\n): ToolCallInfo {\n const result = results.get(call.id)\n const failed = failedToolIds.includes(call.id)\n return {\n id: call.id,\n name: call.function.name,\n arguments: call.function.arguments,\n ...(result === undefined ? {} : { result }),\n status: result === undefined ? \"pending\" : failed ? \"error\" : \"done\",\n }\n}\n","import type { AgentSession, BladeClient, CreateSessionRequest } from \"@blade-hq/agent-client\"\nimport { BladeApiError } from \"@blade-hq/agent-client\"\nimport { LockKeyhole } from \"lucide-react\"\nimport { useEffect, useState } from \"react\"\nimport { useBladeClient } from \"../context\"\nimport { useAgentSession } from \"../hooks/use-agent-session\"\nimport { useReplay } from \"../hooks/use-replay\"\nimport { ReplayBar } from \"./ReplayBar\"\nimport { ReplayMismatchPrompt } from \"./ReplayMismatchPrompt\"\nimport { cn } from \"../lib/utils\"\nimport { ChatSurface, themeAttr, type ChatPresentationProps } from \"./ChatSurface\"\n\nexport interface AgentChatProps extends ChatPresentationProps {\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}\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/**\n * 智能体模式的聊天界面:连接(或新建)一个 Blade 会话,渲染消息流与输入框。\n * 状态全部来自 useAgentSession,不依赖任何全局 store。\n */\nexport function AgentChat(props: AgentChatProps) {\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\n data-theme={themeAttr(props.theme)}\n className={cn(\"blade-chat flex min-h-0 flex-1 flex-col\", props.classNames?.root)}\n >\n <LoginCard\n client={client}\n onLoggedIn={() => {\n setNeedLogin(false)\n setAttempt((value) => value + 1)\n }}\n />\n </div>\n )\n }\n\n return <ChatSessionView key={attempt} {...props} onUnauthorized={() => setNeedLogin(true)} />\n}\n\nfunction ChatSessionView({\n sessionId,\n createOptions,\n onSessionCreated,\n onSessionReady,\n commands,\n classNames,\n renderers,\n slots,\n placeholder,\n theme,\n onUnauthorized,\n}: AgentChatProps & { onUnauthorized: () => void }) {\n const { session, state, error } = useAgentSession(sessionId, {\n createOptions,\n onSessionCreated,\n })\n // 开箱即用的聊天界面自带回放处理:不接的话,用它打开回放会话就是个普通聊天\n // ——看不出在回放、退不出去,说错话还会走\"无人监听\"兜底静默转成真实运行。\n const replay = useReplay(session)\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 const isStreaming = state?.isStreaming ?? false\n const isStopping = stopRequested && isStreaming\n const connectError =\n error && !isUnauthorizedError(error)\n ? error.message || \"连接失败,请稍后重试\"\n : null\n // 回放控制失败(改倍速 / 退出)也要让人看见,否则点了毫无反馈。\n const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null\n\n const handleSend = (text: string): Promise<boolean> => {\n setStopRequested(false)\n return session?.send(text, { mode: state?.mode ?? undefined }) ?? Promise.resolve(false)\n }\n\n const handleStop = () => {\n setStopRequested(true)\n void session?.stop()\n }\n\n return (\n <ChatSurface\n theme={theme}\n classNames={classNames}\n renderers={renderers}\n slots={slots}\n placeholder={placeholder}\n connection={state?.connection ?? \"connecting\"}\n banner={\n <>\n <ReplayBar\n isReplay={replay.isReplay}\n speed={replay.speed}\n canControl={replay.canControl}\n onSpeedChange={(next) => void replay.setSpeed(next)}\n onExit={() => void replay.exitToAutonomous()}\n />\n <ReplayMismatchPrompt mismatch={replay.mismatch} />\n </>\n }\n errorMessage={errorMessage}\n messages={state?.messages ?? []}\n isStreaming={isStreaming}\n isStopping={isStopping}\n inputText={inputText}\n onInputChange={setInputText}\n onSend={handleSend}\n onStop={handleStop}\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 // 用 session 自己持有的 id,不是 sessionId prop——后者是可选的,\n // 自动建会话时一直是 undefined,文件卡片会永远不可用。\n sessionId={session?.sessionId}\n />\n )\n}\n","import { CamelToPascal } from './utility-types';\n\n/**\n * Converts string to kebab case\n *\n * @param {string} string\n * @returns {string} A kebabized string\n */\nexport const toKebabCase = (string: string) =>\n string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n\n/**\n * Converts string to camel case\n *\n * @param {string} string\n * @returns {string} A camelized string\n */\nexport const toCamelCase = <T extends string>(string: T) =>\n string.replace(/^([A-Z])|[\\s-_]+(\\w)/g, (match, p1, p2) =>\n p2 ? p2.toUpperCase() : p1.toLowerCase(),\n );\n\n/**\n * Converts string to pascal case\n *\n * @param {string} string\n * @returns {string} A pascalized string\n */\nexport const toPascalCase = <T extends string>(string: T): CamelToPascal<T> => {\n const camelCase = toCamelCase(string);\n\n return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal<T>;\n};\n\n/**\n * Merges classes into a single string\n *\n * @param {array} classes\n * @returns {string} A string of classes\n */\nexport const mergeClasses = <ClassType = string | undefined | null>(...classes: ClassType[]) =>\n classes\n .filter((className, index, array) => {\n return (\n Boolean(className) &&\n (className as string).trim() !== '' &&\n array.indexOf(className) === index\n );\n })\n .join(' ')\n .trim();\n","export default {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n};\n","import { createElement, forwardRef } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport { IconNode, LucideProps } from './types';\nimport { mergeClasses } from '@lucide/shared';\n\ninterface IconComponentProps extends LucideProps {\n iconNode: IconNode;\n}\n\n/**\n * Lucide icon component\n *\n * @component Icon\n * @param {object} props\n * @param {string} props.color - The color of the icon\n * @param {number} props.size - The size of the icon\n * @param {number} props.strokeWidth - The stroke width of the icon\n * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width\n * @param {string} props.className - The class name of the icon\n * @param {IconNode} props.children - The children of the icon\n * @param {IconNode} props.iconNode - The icon node of the icon\n *\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst Icon = forwardRef<SVGSVGElement, IconComponentProps>(\n (\n {\n color = 'currentColor',\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = '',\n children,\n iconNode,\n ...rest\n },\n ref,\n ) => {\n return createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,\n className: mergeClasses('lucide', className),\n ...rest,\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n );\n },\n);\n\nexport default Icon;\n","import { createElement, forwardRef } from 'react';\nimport { mergeClasses, toKebabCase } from '@lucide/shared';\nimport { IconNode, LucideProps } from './types';\nimport Icon from './Icon';\n\n/**\n * Create a Lucide icon component\n * @param {string} iconName\n * @param {array} iconNode\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst createLucideIcon = (iconName: string, iconNode: IconNode) => {\n const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>\n createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(`lucide-${toKebabCase(iconName)}`, className),\n ...props,\n }),\n );\n\n Component.displayName = `${iconName}`;\n\n return Component;\n};\n\nexport default createLucideIcon;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Archive\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/archive\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Archive = createLucideIcon('Archive', [\n ['rect', { width: '20', height: '5', x: '2', y: '3', rx: '1', key: '1wp1u1' }],\n ['path', { d: 'M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8', key: '1s80jp' }],\n ['path', { d: 'M10 12h4', key: 'a56b0p' }],\n]);\n\nexport default Archive;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ArrowUp\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-up\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowUp = createLucideIcon('ArrowUp', [\n ['path', { d: 'm5 12 7-7 7 7', key: 'hav0vg' }],\n ['path', { d: 'M12 19V5', key: 'x0mq9r' }],\n]);\n\nexport default ArrowUp;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Bot\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/bot\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Bot = createLucideIcon('Bot', [\n ['path', { d: 'M12 8V4H8', key: 'hb8ula' }],\n ['rect', { width: '16', height: '12', x: '4', y: '8', rx: '2', key: 'enze0r' }],\n ['path', { d: 'M2 14h2', key: 'vft8re' }],\n ['path', { d: 'M20 14h2', key: '4cs60a' }],\n ['path', { d: 'M15 13v2', key: '1xurst' }],\n ['path', { d: 'M9 13v2', key: 'rq6x2g' }],\n]);\n\nexport default Bot;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Brain\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/brain\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Brain = createLucideIcon('Brain', [\n [\n 'path',\n {\n d: 'M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z',\n key: 'l5xja',\n },\n ],\n [\n 'path',\n {\n d: 'M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z',\n key: 'ep3f8r',\n },\n ],\n ['path', { d: 'M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4', key: '1p4c4q' }],\n ['path', { d: 'M17.599 6.5a3 3 0 0 0 .399-1.375', key: 'tmeiqw' }],\n ['path', { d: 'M6.003 5.125A3 3 0 0 0 6.401 6.5', key: '105sqy' }],\n ['path', { d: 'M3.477 10.896a4 4 0 0 1 .585-.396', key: 'ql3yin' }],\n ['path', { d: 'M19.938 10.5a4 4 0 0 1 .585.396', key: '1qfode' }],\n ['path', { d: 'M6 18a4 4 0 0 1-1.967-.516', key: '2e4loj' }],\n ['path', { d: 'M19.967 17.484A4 4 0 0 1 18 18', key: '159ez6' }],\n]);\n\nexport default Brain;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Check\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/check\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Check = createLucideIcon('Check', [['path', { d: 'M20 6 9 17l-5-5', key: '1gmf2c' }]]);\n\nexport default Check;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ChevronDown\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-down\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronDown = createLucideIcon('ChevronDown', [\n ['path', { d: 'm6 9 6 6 6-6', key: 'qrunsl' }],\n]);\n\nexport default ChevronDown;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name ChevronRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronRight = createLucideIcon('ChevronRight', [\n ['path', { d: 'm9 18 6-6-6-6', key: 'mthhwq' }],\n]);\n\nexport default ChevronRight;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name CircleAlert\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/circle-alert\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst CircleAlert = createLucideIcon('CircleAlert', [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n ['line', { x1: '12', x2: '12', y1: '8', y2: '12', key: '1pkeuh' }],\n ['line', { x1: '12', x2: '12.01', y1: '16', y2: '16', key: '4dfq90' }],\n]);\n\nexport default CircleAlert;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Copy\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/copy\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Copy = createLucideIcon('Copy', [\n ['rect', { width: '14', height: '14', x: '8', y: '8', rx: '2', ry: '2', key: '17jyea' }],\n ['path', { d: 'M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2', key: 'zix9uf' }],\n]);\n\nexport default Copy;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Download\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/download\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Download = createLucideIcon('Download', [\n ['path', { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4', key: 'ih7n3h' }],\n ['polyline', { points: '7 10 12 15 17 10', key: '2ggqvy' }],\n ['line', { x1: '12', x2: '12', y1: '15', y2: '3', key: '1vk2je' }],\n]);\n\nexport default Download;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name FileCode2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file-code-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst FileCode2 = createLucideIcon('FileCode2', [\n ['path', { d: 'M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4', key: '1pf5j1' }],\n ['path', { d: 'M14 2v4a2 2 0 0 0 2 2h4', key: 'tnqrlb' }],\n ['path', { d: 'm5 12-3 3 3 3', key: 'oke12k' }],\n ['path', { d: 'm9 18 3-3-3-3', key: '112psh' }],\n]);\n\nexport default FileCode2;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name FileSpreadsheet\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file-spreadsheet\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst FileSpreadsheet = createLucideIcon('FileSpreadsheet', [\n ['path', { d: 'M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z', key: '1rqfz7' }],\n ['path', { d: 'M14 2v4a2 2 0 0 0 2 2h4', key: 'tnqrlb' }],\n ['path', { d: 'M8 13h2', key: 'yr2amv' }],\n ['path', { d: 'M14 13h2', key: 'un5t4a' }],\n ['path', { d: 'M8 17h2', key: '2yhykz' }],\n ['path', { d: 'M14 17h2', key: '10kma7' }],\n]);\n\nexport default FileSpreadsheet;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name FileText\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file-text\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst FileText = createLucideIcon('FileText', [\n ['path', { d: 'M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z', key: '1rqfz7' }],\n ['path', { d: 'M14 2v4a2 2 0 0 0 2 2h4', key: 'tnqrlb' }],\n ['path', { d: 'M10 9H8', key: 'b1mrlr' }],\n ['path', { d: 'M16 13H8', key: 't4e002' }],\n ['path', { d: 'M16 17H8', key: 'z1uh3a' }],\n]);\n\nexport default FileText;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name File\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst File = createLucideIcon('File', [\n ['path', { d: 'M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z', key: '1rqfz7' }],\n ['path', { d: 'M14 2v4a2 2 0 0 0 2 2h4', key: 'tnqrlb' }],\n]);\n\nexport default File;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Film\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/film\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Film = createLucideIcon('Film', [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n ['path', { d: 'M7 3v18', key: 'bbkbws' }],\n ['path', { d: 'M3 7.5h4', key: 'zfgn84' }],\n ['path', { d: 'M3 12h18', key: '1i2n21' }],\n ['path', { d: 'M3 16.5h4', key: '1230mu' }],\n ['path', { d: 'M17 3v18', key: 'in4fa5' }],\n ['path', { d: 'M17 7.5h4', key: 'myr1c1' }],\n ['path', { d: 'M17 16.5h4', key: 'go4c1d' }],\n]);\n\nexport default Film;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Image\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/image\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Image = createLucideIcon('Image', [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', ry: '2', key: '1m3agn' }],\n ['circle', { cx: '9', cy: '9', r: '2', key: 'af1f0g' }],\n ['path', { d: 'm21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21', key: '1xmnt7' }],\n]);\n\nexport default Image;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Layers\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/layers\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Layers = createLucideIcon('Layers', [\n [\n 'path',\n {\n d: 'M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z',\n key: 'zw3jo',\n },\n ],\n [\n 'path',\n {\n d: 'M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12',\n key: '1wduqc',\n },\n ],\n [\n 'path',\n {\n d: 'M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17',\n key: 'kqbvx6',\n },\n ],\n]);\n\nexport default Layers;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Lightbulb\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/lightbulb\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Lightbulb = createLucideIcon('Lightbulb', [\n [\n 'path',\n {\n d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5',\n key: '1gvzjb',\n },\n ],\n ['path', { d: 'M9 18h6', key: 'x1upvd' }],\n ['path', { d: 'M10 22h4', key: 'ceow96' }],\n]);\n\nexport default Lightbulb;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name LoaderCircle\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/loader-circle\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst LoaderCircle = createLucideIcon('LoaderCircle', [\n ['path', { d: 'M21 12a9 9 0 1 1-6.219-8.56', key: '13zald' }],\n]);\n\nexport default LoaderCircle;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name LockKeyhole\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/lock-keyhole\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst LockKeyhole = createLucideIcon('LockKeyhole', [\n ['circle', { cx: '12', cy: '16', r: '1', key: '1au0dj' }],\n ['rect', { x: '3', y: '10', width: '18', height: '12', rx: '2', key: '6s8ecr' }],\n ['path', { d: 'M7 10V7a5 5 0 0 1 10 0v3', key: '1pqi11' }],\n]);\n\nexport default LockKeyhole;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name MessageSquareMore\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/message-square-more\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst MessageSquareMore = createLucideIcon('MessageSquareMore', [\n ['path', { d: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', key: '1lielz' }],\n ['path', { d: 'M8 10h.01', key: '19clt8' }],\n ['path', { d: 'M12 10h.01', key: '1nrarc' }],\n ['path', { d: 'M16 10h.01', key: '1m94wz' }],\n]);\n\nexport default MessageSquareMore;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name MessageSquare\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/message-square\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst MessageSquare = createLucideIcon('MessageSquare', [\n ['path', { d: 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z', key: '1lielz' }],\n]);\n\nexport default MessageSquare;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Music\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/music\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Music = createLucideIcon('Music', [\n ['path', { d: 'M9 18V5l12-2v13', key: '1jmyc2' }],\n ['circle', { cx: '6', cy: '18', r: '3', key: 'fqmcym' }],\n ['circle', { cx: '18', cy: '16', r: '3', key: '1hluhg' }],\n]);\n\nexport default Music;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Play\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/play\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Play = createLucideIcon('Play', [\n ['polygon', { points: '6 3 20 12 6 21 6 3', key: '1oa8hb' }],\n]);\n\nexport default Play;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Settings2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/settings-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Settings2 = createLucideIcon('Settings2', [\n ['path', { d: 'M20 7h-9', key: '3s1dr2' }],\n ['path', { d: 'M14 17H5', key: 'gfn3mx' }],\n ['circle', { cx: '17', cy: '17', r: '3', key: '18b49y' }],\n ['circle', { cx: '7', cy: '7', r: '3', key: 'dfmy0x' }],\n]);\n\nexport default Settings2;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name Square\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/square\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Square = createLucideIcon('Square', [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n]);\n\nexport default Square;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name TriangleAlert\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/triangle-alert\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst TriangleAlert = createLucideIcon('TriangleAlert', [\n [\n 'path',\n {\n d: 'm21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3',\n key: 'wmoenq',\n },\n ],\n ['path', { d: 'M12 9v4', key: 'juzpu7' }],\n ['path', { d: 'M12 17h.01', key: 'p32p05' }],\n]);\n\nexport default TriangleAlert;\n","import createLucideIcon from '../createLucideIcon';\n\n/**\n * @component @name X\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/x\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst X = createLucideIcon('X', [\n ['path', { d: 'M18 6 6 18', key: '1bl5f8' }],\n ['path', { d: 'm6 6 12 12', key: 'd8bk6v' }],\n]);\n\nexport default X;\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 type { ReplaySpeed } from \"@blade-hq/agent-client\"\nimport { Play } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nconst SPEED_OPTIONS: ReplaySpeed[] = [1, 2, 5]\n\nexport interface ReplayBarProps {\n /** 非回放会话传 false 时整条不渲染。 */\n isReplay: boolean\n speed: ReplaySpeed\n onSpeedChange: (speed: ReplaySpeed) => void\n onExit: () => void\n /** 传 useReplay().canControl。只读身份下仍然显示\"在回放\",但操作按钮禁用。 */\n canControl?: boolean\n className?: string\n}\n\n/** 回放会话的顶部状态条:说明当前在回放、切换倍速、随时退出回放。 */\nexport function ReplayBar({\n isReplay,\n speed,\n onSpeedChange,\n onExit,\n canControl = true,\n className,\n}: ReplayBarProps) {\n if (!isReplay) return null\n\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-2 border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))]/40 px-4 py-2 text-xs\",\n className,\n )}\n >\n <span className=\"inline-flex items-center gap-1.5 font-medium text-[hsl(var(--foreground))]\">\n <Play size={13} />\n 回放模式\n </span>\n <span className=\"text-[hsl(var(--muted-foreground))]\">\n 正在重现之前的对话\n </span>\n <div className=\"ml-auto flex items-center gap-2\">\n <div className=\"flex items-center gap-0.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-0.5\">\n {SPEED_OPTIONS.map((option) => (\n <button\n key={option}\n type=\"button\"\n onClick={() => onSpeedChange(option)}\n aria-pressed={speed === option}\n disabled={!canControl}\n className={cn(\n \"h-6 rounded px-2 font-medium transition-colors\",\n speed === option\n ? \"bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]\"\n : \"text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n !canControl && \"cursor-not-allowed opacity-50\",\n )}\n >\n {option}x\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={onExit}\n disabled={!canControl}\n className={cn(\n \"h-7 rounded-md border border-[hsl(var(--border))] px-2.5 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\",\n !canControl && \"cursor-not-allowed opacity-50 hover:bg-transparent\",\n )}\n >\n 退出回放\n </button>\n </div>\n </div>\n )\n}\n","import type { ReplayMismatch } from \"../hooks/use-replay\"\nimport { cn } from \"../lib/utils\"\n\nexport interface ReplayMismatchPromptProps {\n /** 来自 useReplay().mismatch;为 null 时不渲染。 */\n mismatch: ReplayMismatch | null\n className?: string\n}\n\n/** 回放中输入和录制内容对不上时的选择卡:按录制继续,还是从这里真跑。 */\nexport function ReplayMismatchPrompt({ mismatch, className }: ReplayMismatchPromptProps) {\n if (!mismatch) return null\n\n return (\n <div\n className={cn(\n \"mx-auto my-3 max-w-3xl rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-sm\",\n className,\n )}\n >\n <div className=\"font-medium text-[hsl(var(--foreground))]\">\n 这句话和之前录制的不一样\n </div>\n <dl className=\"mt-2 space-y-1 text-xs text-[hsl(var(--muted-foreground))]\">\n <div className=\"flex gap-2\">\n <dt className=\"shrink-0\">录制的是</dt>\n <dd className=\"min-w-0 break-words text-[hsl(var(--foreground))]\">\n {mismatch.expectedMessage || \"(空)\"}\n </dd>\n </div>\n <div className=\"flex gap-2\">\n <dt className=\"shrink-0\">你输入的</dt>\n <dd className=\"min-w-0 break-words text-[hsl(var(--foreground))]\">\n {mismatch.actualMessage || \"(空)\"}\n </dd>\n </div>\n </dl>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => mismatch.resolve(\"keep_replay\")}\n className=\"h-8 rounded-md bg-[hsl(var(--primary))] px-3 text-xs font-medium text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90\"\n >\n 按录制内容继续\n </button>\n <button\n type=\"button\"\n onClick={() => mismatch.resolve(\"continue_replay\")}\n className=\"h-8 rounded-md border border-[hsl(var(--border))] px-3 text-xs text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]\"\n >\n 从这里开始真的运行\n </button>\n </div>\n </div>\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) => boolean | Promise<boolean>\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 = async () => {\n if (!canSend) return\n const accepted = await onSend(trimmed)\n if (!accepted) return\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 void handleSend()\n }\n }\n\n return (\n <div className={cn(\"blade-chat-input border-t border-[hsl(var(--border))] 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 { useCallback, useEffect, useMemo, useRef, useState, 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 /** 传给助手消息的 Markdown 渲染,用于把 MEDIA 行折叠成文件下载卡片。 */\n sessionId?: 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 sessionId,\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=\"blade-chat-messages-scroll\">\n <div className=\"blade-chat-messages-content mx-auto max-w-[748px]\">\n <div className=\"flex min-w-0 flex-col\">\n {renderBlocks.length === 0\n ? (emptyState ?? (\n <div className=\"blade-chat-empty\">\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 sessionId={sessionId}\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 const [visible, setVisible] = useState(false)\n const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n // 防抖:isAtBottom 变为 true 时不立即隐藏,等 300ms 确认稳定后再隐藏\n // 避免流式内容高度变化导致 isAtBottom 在 true/false 之间快速切换闪烁\n useEffect(() => {\n if (isAtBottom) {\n // 启动延迟隐藏定时器\n if (!hideTimerRef.current) {\n hideTimerRef.current = setTimeout(() => {\n setVisible(false)\n hideTimerRef.current = null\n }, 300)\n }\n } else {\n // 不在底部,取消所有待执行的隐藏,立即显示\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n setVisible(true)\n }\n\n return () => {\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n }\n }, [isAtBottom])\n\n // hook 必须在任何早退之前调用,否则 visible 变化时 hook 顺序不一致\n const handleClick = useCallback(() => {\n // 点击滚动到底部时立即隐藏按钮,避免闪烁\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n setVisible(false)\n scrollToBottom()\n }, [scrollToBottom])\n\n if (!visible) return null\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n aria-label=\"滚动到底部\"\n className=\"blade-chat-scroll-bottom 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 {/* 窄容器里这行字会横跨近半个宽度,收起来只留图标;可访问名由 aria-label 保住。 */}\n <span className=\"blade-chat-scroll-bottom-label\">滚动到底部</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","/*!---------------------------------------------------------------------------------------------\n * Copyright (c) StackBlitz. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nimport { useCallback, useMemo, useRef, useState, } from \"react\";\nconst DEFAULT_SPRING_ANIMATION = {\n /**\n * A value from 0 to 1, on how much to damp the animation.\n * 0 means no damping, 1 means full damping.\n *\n * @default 0.7\n */\n damping: 0.7,\n /**\n * The stiffness of how fast/slow the animation gets up to speed.\n *\n * @default 0.05\n */\n stiffness: 0.05,\n /**\n * The inertial mass associated with the animation.\n * Higher numbers make the animation slower.\n *\n * @default 1.25\n */\n mass: 1.25,\n};\nconst STICK_TO_BOTTOM_OFFSET_PX = 70;\nconst SIXTY_FPS_INTERVAL_MS = 1000 / 60;\nconst RETAIN_ANIMATION_DURATION_MS = 350;\nlet mouseDown = false;\nglobalThis.document?.addEventListener(\"mousedown\", () => {\n mouseDown = true;\n});\nglobalThis.document?.addEventListener(\"mouseup\", () => {\n mouseDown = false;\n});\nglobalThis.document?.addEventListener(\"click\", () => {\n mouseDown = false;\n});\nexport const useStickToBottom = (options = {}) => {\n const [escapedFromLock, updateEscapedFromLock] = useState(false);\n const [isAtBottom, updateIsAtBottom] = useState(options.initial !== false);\n const [isNearBottom, setIsNearBottom] = useState(false);\n const optionsRef = useRef(null);\n optionsRef.current = options;\n const isSelecting = useCallback(() => {\n if (!mouseDown) {\n return false;\n }\n const selection = window.getSelection();\n if (!selection || !selection.rangeCount) {\n return false;\n }\n const range = selection.getRangeAt(0);\n return (range.commonAncestorContainer.contains(scrollRef.current) ||\n scrollRef.current?.contains(range.commonAncestorContainer));\n }, []);\n const setIsAtBottom = useCallback((isAtBottom) => {\n state.isAtBottom = isAtBottom;\n updateIsAtBottom(isAtBottom);\n }, []);\n const setEscapedFromLock = useCallback((escapedFromLock) => {\n state.escapedFromLock = escapedFromLock;\n updateEscapedFromLock(escapedFromLock);\n }, []);\n // biome-ignore lint/correctness/useExhaustiveDependencies: not needed\n const state = useMemo(() => {\n let lastCalculation;\n return {\n escapedFromLock,\n isAtBottom,\n resizeDifference: 0,\n accumulated: 0,\n velocity: 0,\n listeners: new Set(),\n get scrollTop() {\n return scrollRef.current?.scrollTop ?? 0;\n },\n set scrollTop(scrollTop) {\n if (scrollRef.current) {\n scrollRef.current.scrollTop = scrollTop;\n state.ignoreScrollToTop = scrollRef.current.scrollTop;\n }\n },\n get targetScrollTop() {\n if (!scrollRef.current || !contentRef.current) {\n return 0;\n }\n return (scrollRef.current.scrollHeight - 1 - scrollRef.current.clientHeight);\n },\n get calculatedTargetScrollTop() {\n if (!scrollRef.current || !contentRef.current) {\n return 0;\n }\n const { targetScrollTop } = this;\n if (!options.targetScrollTop) {\n return targetScrollTop;\n }\n if (lastCalculation?.targetScrollTop === targetScrollTop) {\n return lastCalculation.calculatedScrollTop;\n }\n const calculatedScrollTop = Math.max(Math.min(options.targetScrollTop(targetScrollTop, {\n scrollElement: scrollRef.current,\n contentElement: contentRef.current,\n }), targetScrollTop), 0);\n lastCalculation = { targetScrollTop, calculatedScrollTop };\n requestAnimationFrame(() => {\n lastCalculation = undefined;\n });\n return calculatedScrollTop;\n },\n get scrollDifference() {\n return this.calculatedTargetScrollTop - this.scrollTop;\n },\n get isNearBottom() {\n return this.scrollDifference <= STICK_TO_BOTTOM_OFFSET_PX;\n },\n };\n }, []);\n const scrollToBottom = useCallback((scrollOptions = {}) => {\n if (typeof scrollOptions === \"string\") {\n scrollOptions = { animation: scrollOptions };\n }\n if (!scrollOptions.preserveScrollPosition) {\n setIsAtBottom(true);\n }\n const waitElapsed = Date.now() + (Number(scrollOptions.wait) || 0);\n const behavior = mergeAnimations(optionsRef.current, scrollOptions.animation);\n const { ignoreEscapes = false } = scrollOptions;\n let durationElapsed;\n let startTarget = state.calculatedTargetScrollTop;\n if (scrollOptions.duration instanceof Promise) {\n scrollOptions.duration.finally(() => {\n durationElapsed = Date.now();\n });\n }\n else {\n durationElapsed = waitElapsed + (scrollOptions.duration ?? 0);\n }\n const next = async () => {\n const promise = new Promise(requestAnimationFrame).then(() => {\n if (!state.isAtBottom) {\n state.animation = undefined;\n return false;\n }\n const { scrollTop } = state;\n const tick = performance.now();\n const tickDelta = (tick - (state.lastTick ?? tick)) / SIXTY_FPS_INTERVAL_MS;\n state.animation || (state.animation = { behavior, promise, ignoreEscapes });\n if (state.animation.behavior === behavior) {\n state.lastTick = tick;\n }\n if (isSelecting()) {\n return next();\n }\n if (waitElapsed > Date.now()) {\n return next();\n }\n if (scrollTop < Math.min(startTarget, state.calculatedTargetScrollTop)) {\n if (state.animation?.behavior === behavior) {\n if (behavior === \"instant\") {\n state.scrollTop = state.calculatedTargetScrollTop;\n return next();\n }\n state.velocity =\n (behavior.damping * state.velocity +\n behavior.stiffness * state.scrollDifference) /\n behavior.mass;\n state.accumulated += state.velocity * tickDelta;\n state.scrollTop += state.accumulated;\n if (state.scrollTop !== scrollTop) {\n state.accumulated = 0;\n }\n }\n return next();\n }\n if (durationElapsed > Date.now()) {\n startTarget = state.calculatedTargetScrollTop;\n return next();\n }\n state.animation = undefined;\n /**\n * If we're still below the target, then queue\n * up another scroll to the bottom with the last\n * requested animatino.\n */\n if (state.scrollTop < state.calculatedTargetScrollTop) {\n return scrollToBottom({\n animation: mergeAnimations(optionsRef.current, optionsRef.current.resize),\n ignoreEscapes,\n duration: Math.max(0, durationElapsed - Date.now()) || undefined,\n });\n }\n return state.isAtBottom;\n });\n return promise.then((isAtBottom) => {\n requestAnimationFrame(() => {\n if (!state.animation) {\n state.lastTick = undefined;\n state.velocity = 0;\n }\n });\n return isAtBottom;\n });\n };\n if (scrollOptions.wait !== true) {\n state.animation = undefined;\n }\n if (state.animation?.behavior === behavior) {\n return state.animation.promise;\n }\n return next();\n }, [setIsAtBottom, isSelecting, state]);\n const stopScroll = useCallback(() => {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }, [setEscapedFromLock, setIsAtBottom]);\n const handleScroll = useCallback(({ target }) => {\n if (target !== scrollRef.current) {\n return;\n }\n const { scrollTop, ignoreScrollToTop } = state;\n let { lastScrollTop = scrollTop } = state;\n state.lastScrollTop = scrollTop;\n state.ignoreScrollToTop = undefined;\n if (ignoreScrollToTop && ignoreScrollToTop > scrollTop) {\n /**\n * When the user scrolls up while the animation plays, the `scrollTop` may\n * not come in separate events; if this happens, to make sure `isScrollingUp`\n * is correct, set the lastScrollTop to the ignored event.\n */\n lastScrollTop = ignoreScrollToTop;\n }\n setIsNearBottom(state.isNearBottom);\n /**\n * Scroll events may come before a ResizeObserver event,\n * so in order to ignore resize events correctly we use a\n * timeout.\n *\n * @see https://github.com/WICG/resize-observer/issues/25#issuecomment-248757228\n */\n setTimeout(() => {\n /**\n * When theres a resize difference ignore the resize event.\n */\n if (state.resizeDifference || scrollTop === ignoreScrollToTop) {\n return;\n }\n if (isSelecting()) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n return;\n }\n const isScrollingDown = scrollTop > lastScrollTop;\n const isScrollingUp = scrollTop < lastScrollTop;\n if (state.animation?.ignoreEscapes) {\n state.scrollTop = lastScrollTop;\n return;\n }\n if (isScrollingUp) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }\n if (isScrollingDown) {\n setEscapedFromLock(false);\n }\n if (!state.escapedFromLock && state.isNearBottom) {\n setIsAtBottom(true);\n }\n }, 1);\n }, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);\n const handleWheel = useCallback(({ target, deltaY }) => {\n let element = target;\n while (![\"scroll\", \"auto\"].includes(getComputedStyle(element).overflow)) {\n if (!element.parentElement) {\n return;\n }\n element = element.parentElement;\n }\n /**\n * The browser may cancel the scrolling from the mouse wheel\n * if we update it from the animation in meantime.\n * To prevent this, always escape when the wheel is scrolled up.\n */\n if (element === scrollRef.current &&\n deltaY < 0 &&\n scrollRef.current.scrollHeight > scrollRef.current.clientHeight &&\n !state.animation?.ignoreEscapes) {\n setEscapedFromLock(true);\n setIsAtBottom(false);\n }\n }, [setEscapedFromLock, setIsAtBottom, state]);\n const scrollRef = useRefCallback((scroll) => {\n scrollRef.current?.removeEventListener(\"scroll\", handleScroll);\n scrollRef.current?.removeEventListener(\"wheel\", handleWheel);\n scroll?.addEventListener(\"scroll\", handleScroll, { passive: true });\n scroll?.addEventListener(\"wheel\", handleWheel, { passive: true });\n }, []);\n const contentRef = useRefCallback((content) => {\n state.resizeObserver?.disconnect();\n if (!content) {\n return;\n }\n let previousHeight;\n state.resizeObserver = new ResizeObserver(([entry]) => {\n const { height } = entry.contentRect;\n const difference = height - (previousHeight ?? height);\n state.resizeDifference = difference;\n /**\n * Sometimes the browser can overscroll past the target,\n * so check for this and adjust appropriately.\n */\n if (state.scrollTop > state.targetScrollTop) {\n state.scrollTop = state.targetScrollTop;\n }\n setIsNearBottom(state.isNearBottom);\n if (difference >= 0) {\n /**\n * If it's a positive resize, scroll to the bottom when\n * we're already at the bottom.\n */\n const animation = mergeAnimations(optionsRef.current, previousHeight\n ? optionsRef.current.resize\n : optionsRef.current.initial);\n scrollToBottom({\n animation,\n wait: true,\n preserveScrollPosition: true,\n duration: animation === \"instant\" ? undefined : RETAIN_ANIMATION_DURATION_MS,\n });\n }\n else {\n /**\n * Else if it's a negative resize, check if we're near the bottom\n * if we are want to un-escape from the lock, because the resize\n * could have caused the container to be at the bottom.\n */\n if (state.isNearBottom) {\n setEscapedFromLock(false);\n setIsAtBottom(true);\n }\n }\n previousHeight = height;\n /**\n * Reset the resize difference after the scroll event\n * has fired. Requires a rAF to wait for the scroll event,\n * and a setTimeout to wait for the other timeout we have in\n * resizeObserver in case the scroll event happens after the\n * resize event.\n */\n requestAnimationFrame(() => {\n setTimeout(() => {\n if (state.resizeDifference === difference) {\n state.resizeDifference = 0;\n }\n }, 1);\n });\n });\n state.resizeObserver?.observe(content);\n }, []);\n return {\n contentRef,\n scrollRef,\n scrollToBottom,\n stopScroll,\n isAtBottom: isAtBottom || isNearBottom,\n isNearBottom,\n escapedFromLock,\n state,\n };\n};\nfunction useRefCallback(callback, deps) {\n // biome-ignore lint/correctness/useExhaustiveDependencies: not needed\n const result = useCallback((ref) => {\n result.current = ref;\n return callback(ref);\n }, deps);\n return result;\n}\nconst animationCache = new Map();\nfunction mergeAnimations(...animations) {\n const result = { ...DEFAULT_SPRING_ANIMATION };\n let instant = false;\n for (const animation of animations) {\n if (animation === \"instant\") {\n instant = true;\n continue;\n }\n if (typeof animation !== \"object\") {\n continue;\n }\n instant = false;\n result.damping = animation.damping ?? result.damping;\n result.stiffness = animation.stiffness ?? result.stiffness;\n result.mass = animation.mass ?? result.mass;\n }\n const key = JSON.stringify(result);\n if (!animationCache.has(key)) {\n animationCache.set(key, Object.freeze(result));\n }\n return instant ? \"instant\" : animationCache.get(key);\n}\n","/*!---------------------------------------------------------------------------------------------\n * Copyright (c) StackBlitz. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\nimport * as React from \"react\";\nimport { createContext, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, } from \"react\";\nimport { useStickToBottom, } from \"./useStickToBottom.js\";\nconst StickToBottomContext = createContext(null);\nconst useIsomorphicLayoutEffect = typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\nexport function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {\n const customTargetScrollTop = useRef(null);\n const targetScrollTop = React.useCallback((target, elements) => {\n const get = context?.targetScrollTop ?? currentTargetScrollTop;\n return get?.(target, elements) ?? target;\n }, [currentTargetScrollTop]);\n const defaultInstance = useStickToBottom({\n mass,\n damping,\n stiffness,\n resize,\n initial,\n targetScrollTop,\n });\n const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state, } = instance ?? defaultInstance;\n const context = useMemo(() => ({\n scrollToBottom,\n stopScroll,\n scrollRef,\n isAtBottom,\n escapedFromLock,\n contentRef,\n state,\n get targetScrollTop() {\n return customTargetScrollTop.current;\n },\n set targetScrollTop(targetScrollTop) {\n customTargetScrollTop.current = targetScrollTop;\n },\n }), [\n scrollToBottom,\n isAtBottom,\n contentRef,\n scrollRef,\n stopScroll,\n escapedFromLock,\n state,\n ]);\n useImperativeHandle(contextRef, () => context, [context]);\n useIsomorphicLayoutEffect(() => {\n if (!scrollRef.current) {\n return;\n }\n if (getComputedStyle(scrollRef.current).overflow === \"visible\") {\n scrollRef.current.style.overflow = \"auto\";\n }\n }, []);\n return (React.createElement(StickToBottomContext.Provider, { value: context },\n React.createElement(\"div\", { ...props }, typeof children === \"function\" ? children(context) : children)));\n}\n(function (StickToBottom) {\n function Content({ children, scrollClassName, ...props }) {\n const context = useStickToBottomContext();\n return (React.createElement(\"div\", { ref: context.scrollRef, style: {\n height: \"100%\",\n width: \"100%\",\n scrollbarGutter: \"stable both-edges\",\n }, className: scrollClassName },\n React.createElement(\"div\", { ...props, ref: context.contentRef }, typeof children === \"function\" ? children(context) : children)));\n }\n StickToBottom.Content = Content;\n})(StickToBottom || (StickToBottom = {}));\n/**\n * Use this hook inside a <StickToBottom> component to gain access to whether the component is at the bottom of the scrollable area.\n */\nexport function useStickToBottomContext() {\n const context = useContext(StickToBottomContext);\n if (!context) {\n throw new Error(\"use-stick-to-bottom component context must be used within a StickToBottom component\");\n }\n return context;\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 /** 传给 Markdown 渲染,用于把 MEDIA 行折叠成文件下载卡片。 */\n sessionId?: string\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 <span className=\"text-[hsl(var(--muted-foreground))]/70\">\n · {new Intl.NumberFormat(\"zh-CN\").format(reasoning.length)} 字\n </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\nfunction findLatestReasoningMessageIndex(messages: readonly ChatMessage[]): number {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n if (messages[index].reasoning) return index\n }\n return -1\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 sessionId,\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 const latestReasoningIndex = isStreaming\n ? findLatestReasoningMessageIndex(messages)\n : -1\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 const showReasoning =\n !!message.reasoning && (!isStreaming || index === latestReasoningIndex)\n\n return (\n <div\n key={message.entry_id ?? `${message.timestamp ?? \"assistant\"}-${index}`}\n className=\"flex flex-col gap-3\"\n >\n {showReasoning && 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 sessionId={sessionId}\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 { FILE_CARD_TAG, collapseMediaTags } from \"../lib/media-tags\"\nimport { cn, copyToClipboard } from \"../lib/utils\"\nimport { FileCard, type FileCardProps } from \"./FileCard\"\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\n/**\n * 文件卡片标签要显式过 sanitize 白名单,否则标签和属性都会被消毒掉。属性名写的是 HAST\n * 形式(`data-path` 解析进 HAST 后叫 `dataPath`);kebab-case 那两个是防御性冗余,\n * 万一 streamdown 换了属性名的处理方式也不至于静默丢属性。\n *\n * 注意不要给 Streamdown 传自定义 rehypePlugins——那样 streamdown 就不会再自动把这里声明的\n * 标签合并进它的 sanitize schema,白名单会静默失效。\n */\nconst CUSTOM_ALLOWED_TAGS = {\n [FILE_CARD_TAG]: [\"dataPath\", \"dataName\", \"data-path\", \"data-name\"],\n} as NonNullable<StreamdownProps[\"allowedTags\"]>\n\nexport interface MarkdownContentProps {\n children: string\n className?: string\n /** 流式渲染传 \"streaming\",历史静态内容默认 \"static\"。 */\n mode?: \"streaming\" | \"static\"\n /**\n * 会话 id。传了才会把 `MEDIA:` 行折叠成文件下载卡片——下载要用它。\n * 不要给用户消息传:用户自己在输入框打一行 `MEDIA:` 不该被当成平台产物。\n */\n sessionId?: string\n}\n\n/**\n * SDK 版 Markdown 渲染:附带代码块复制按钮、智能体产出文件的下载卡片;\n * 相比第一方版本去掉了 mermaid、外链确认弹窗与文件预览。\n */\nexport function MarkdownContent({ children, className, mode, sessionId }: MarkdownContentProps) {\n const streaming = mode === \"streaming\"\n const resolvedChildren = useMemo(() => {\n const withoutReminders = children.replace(SYSTEM_REMINDER_RE, \"\")\n if (!sessionId) return withoutReminders\n return collapseMediaTags(withoutReminders, { streaming })\n }, [children, sessionId, streaming])\n\n const components = useMemo(\n () =>\n ({\n ...MARKDOWN_COMPONENTS,\n [FILE_CARD_TAG]: (fileCardProps: FileCardProps) => (\n <FileCard {...fileCardProps} sessionId={sessionId} />\n ),\n }) as NonNullable<StreamdownProps[\"components\"]>,\n [sessionId],\n )\n\n return (\n <Streamdown\n // Streamdown 外层 memo 不比较 components,光换 sessionId 可能留着旧闭包,\n // 卡片就会拿旧会话去下载。挂 key 强制重建,切会话本来也要换内容。\n key={sessionId ?? \"no-session\"}\n className={cn(\"blade-chat-markdown break-words\", className)}\n mode={mode ?? \"static\"}\n components={components}\n allowedTags={CUSTOM_ALLOWED_TAGS}\n >\n {resolvedChildren}\n </Streamdown>\n )\n}\n","/**\n * MEDIA 行处理。后端提示词要求智能体产出文件时单独写一行 `MEDIA:<绝对路径>`\n * (见 host/src/blade_agent/host/orchestrator/prompt.py),界面必须把它折叠成文件卡片——\n * 这是内部标记,直接摆给用户看就是 bug。\n */\n\nexport const FILE_CARD_TAG = \"blade-file-card\"\n\n/**\n * 模型经常给这一行加点装饰——写成列表项、加粗、包引号、末尾补句号,这些都还是同一个\n * 意思,识别不了就会把内部标记裸露给用户,所以一并放宽。\n *\n * 尾部的包裹符用反向引用要求和开头那组一模一样:没写开头装饰就一个字符都不剥,\n * 否则 `draft_`、`报表*` 这种本来就以这些字符结尾的文件名会被削掉最后一位,卡片指向\n * 一个不存在的路径。行尾标点同理只认中文标点和逗号分号——英文句点是扩展名分隔符,\n * 不能碰。\n *\n * 路径部分用 `.+?` 而不是 `\\S+?`:文件名里带空格很常见(`季度 报告.xlsx`),用 `\\S`\n * 会在空格处截断、导致整行匹配不上,最后把 `MEDIA:` 原样暴露给用户。行尾的 `\\r?` 是给\n * CRLF 文本用的,不写会把 `\\r` 算进路径。\n */\nconst MEDIA_LINE_RE =\n /^[ \\t]*(?:[-*+][ \\t]+|\\d+\\.[ \\t]+)?(?<wrap>[`\"'*_]*)MEDIA:[ \\t]*(?<path>.+?)\\k<wrap>[ \\t]*[。,、;;,]?[ \\t]*\\r?$/gm\n\n/** 只判断「这行是不是正在写一个 MEDIA 行」,不要求路径已经写完。 */\nconst MEDIA_LINE_START_RE = /^[ \\t]*(?:[-*+][ \\t]+|\\d+\\.[ \\t]+)?[`\"'*_]*MEDIA:/\n\n/** 同时按 `/` 和 `\\` 取末段:daemon 可能跑在 Windows 上,路径是反斜杠分隔的。 */\nfunction basename(path: string): string {\n return path.split(/[/\\\\]/).filter(Boolean).pop() || path\n}\n\n/** 路径写成 `<...>` 的形式(Markdown 里包空格路径的写法)时剥掉尖括号。 */\nfunction unwrapMediaPath(path: string): string | null {\n if (!path.startsWith(\"<\")) return path\n const closingBracket = path.indexOf(\">\")\n // 流式输出里尖括号还没闭合,等后续 token 到齐再判断。\n if (closingBracket < 0) return null\n return `${path.slice(1, closingBracket)}${path.slice(closingBracket + 1)}`\n}\n\n/**\n * 能不能拿这个路径去下载。绝对路径原样交给服务端解析(它会把工作区路径映射进沙盒、\n * 并拒绝越界),所以这里只挡明显不该放过的:空路径和 `..`。\n */\nexport function isSafeMediaPath(path: string): boolean {\n if (!path) return false\n return !path.split(/[/\\\\]/).includes(\"..\")\n}\n\nfunction escapeAttribute(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n}\n\nfunction foldMediaLines(text: string): string {\n return text.replace(MEDIA_LINE_RE, (match: string, _wrap: string, rawPath: string) => {\n const path = unwrapMediaPath(rawPath)\n if (path === null) return match\n const name = basename(path)\n if (!isSafeMediaPath(path)) {\n // 路径没法用,但 MEDIA: 是内部标记,不能就这么摆给用户看——退化成文件名。\n return `\\`${name}\\``\n }\n return `<${FILE_CARD_TAG} data-path=\"${escapeAttribute(path)}\" data-name=\"${escapeAttribute(name)}\">${escapeAttribute(name)}</${FILE_CARD_TAG}>`\n })\n}\n\n/**\n * 把 MEDIA 行折叠成文件卡片标签。\n *\n * `streaming` 时最后一行要特殊对待:MEDIA_LINE_RE 用 `$` 匹配行尾,而流式过程中最后一行\n * 随时都是「行尾」,逐 token 折叠会依次闪出 `MEDIA:`、半截路径的卡片、再变成正确卡片。\n * 所以正在写的那一行整行先不渲染,等它以换行结束、或消息转为 static 之后再折叠。\n *\n * 判据只能是 `streaming`,不能是「有没有尾随换行」:AssistantTurnBlock 传进来的文本是\n * trim 过的,静态消息永远没有尾随换行,那样判断会导致 MEDIA 行永远不折叠。\n */\nexport function collapseMediaTags(text: string, options?: { streaming?: boolean }): string {\n if (!text.includes(\"MEDIA:\")) return text\n if (!options?.streaming) return foldMediaLines(text)\n\n const newlineIndex = text.lastIndexOf(\"\\n\")\n const head = newlineIndex < 0 ? \"\" : text.slice(0, newlineIndex + 1)\n const lastLine = newlineIndex < 0 ? text : text.slice(newlineIndex + 1)\n\n if (MEDIA_LINE_START_RE.test(lastLine)) {\n return foldMediaLines(head)\n }\n return foldMediaLines(head) + lastLine\n}\n","import {\n Archive,\n Download,\n File,\n FileCode2,\n FileSpreadsheet,\n FileText,\n Film,\n Image,\n Loader2,\n Music,\n} from \"lucide-react\"\nimport { type ComponentPropsWithRef, useState } from \"react\"\nimport type { ExtraProps } from \"streamdown\"\nimport { useBladeClient } from \"../context\"\nimport { cn } from \"../lib/utils\"\nimport { isSafeMediaPath } from \"../lib/media-tags\"\n\nexport type FileCardProps = ComponentPropsWithRef<\"span\"> &\n ExtraProps & {\n \"data-path\"?: string\n \"data-name\"?: string\n dataPath?: string\n dataName?: string\n sessionId?: string\n }\n\nconst IMAGE_EXTS = new Set([\"png\", \"jpg\", \"jpeg\", \"gif\", \"svg\", \"webp\", \"ico\", \"bmp\"])\nconst SHEET_EXTS = new Set([\"xlsx\", \"xls\", \"xlsm\", \"xlsb\", \"csv\"])\nconst TEXT_EXTS = new Set([\"txt\", \"md\", \"markdown\", \"pdf\", \"doc\", \"docx\", \"rtf\"])\nconst CODE_EXTS = new Set([\"js\", \"jsx\", \"ts\", \"tsx\", \"py\", \"go\", \"json\", \"yaml\", \"yml\", \"html\", \"css\"])\nconst VIDEO_EXTS = new Set([\"mp4\", \"webm\", \"mov\", \"mkv\", \"avi\"])\nconst AUDIO_EXTS = new Set([\"mp3\", \"wav\", \"ogg\", \"m4a\", \"flac\"])\nconst ARCHIVE_EXTS = new Set([\"zip\", \"tar\", \"gz\", \"tgz\", \"rar\", \"7z\"])\n\nfunction getFileIcon(name: string) {\n const ext = name.split(\".\").pop()?.toLowerCase() ?? \"\"\n if (IMAGE_EXTS.has(ext)) return Image\n if (SHEET_EXTS.has(ext)) return FileSpreadsheet\n if (TEXT_EXTS.has(ext)) return FileText\n if (CODE_EXTS.has(ext)) return FileCode2\n if (VIDEO_EXTS.has(ext)) return Film\n if (AUDIO_EXTS.has(ext)) return Music\n if (ARCHIVE_EXTS.has(ext)) return Archive\n return File\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined\n}\n\nfunction extractChildrenText(children: unknown): string {\n if (typeof children === \"string\") return children\n if (Array.isArray(children)) return children.map(extractChildrenText).join(\"\")\n if (children && typeof children === \"object\" && \"props\" in children) {\n return extractChildrenText((children as { props?: { children?: unknown } }).props?.children)\n }\n return \"\"\n}\n\n/**\n * 智能体产出文件的下载卡片(由 MEDIA 行折叠而来)。\n *\n * 整张卡片只有一个动作:下载。第一方界面点卡片是「在右侧预览」,那依赖 app 的 artifact\n * 面板,SDK 里没有这块,所以不摆预览入口也不写预览文案——UI 不能承诺不存在的能力。\n */\nexport function FileCard({\n node,\n \"data-path\": pathAttribute,\n \"data-name\": nameAttribute,\n dataPath,\n dataName,\n sessionId,\n children,\n className,\n ...props\n}: FileCardProps) {\n const client = useBladeClient()\n const [downloading, setDownloading] = useState(false)\n const [failed, setFailed] = useState(false)\n\n // sanitize 之后属性可能以 kebab-case prop、camelCase prop 或 HAST node 属性抵达,\n // 三种都读一遍;全都没有时退回标签内容(折叠时写的就是文件名)。\n const nodeProperties =\n node && typeof node === \"object\" && \"properties\" in node\n ? (node.properties as Record<string, unknown>)\n : undefined\n const path =\n pathAttribute ??\n dataPath ??\n stringValue(nodeProperties?.[\"data-path\"]) ??\n stringValue(nodeProperties?.dataPath) ??\n \"\"\n const name =\n nameAttribute ??\n dataName ??\n stringValue(nodeProperties?.[\"data-name\"]) ??\n stringValue(nodeProperties?.dataName) ??\n extractChildrenText(children) ??\n \"\"\n\n const disabled = !sessionId || !isSafeMediaPath(path)\n const Icon = getFileIcon(name)\n\n const handleDownload = async () => {\n if (disabled || downloading || !sessionId) return\n setDownloading(true)\n setFailed(false)\n try {\n await client.sessions.downloadFile(sessionId, path, name)\n } catch {\n setFailed(true)\n } finally {\n setDownloading(false)\n }\n }\n\n return (\n <span {...props} className={cn(\"blade-chat-file-card\", className)}>\n <button\n type=\"button\"\n onClick={handleDownload}\n disabled={disabled || downloading}\n aria-label={`下载文件:${name}`}\n title={failed ? \"下载失败,点击重试\" : `下载 ${name}`}\n >\n <span className=\"blade-chat-file-card-icon\">\n {downloading ? <Loader2 size={15} className=\"animate-spin\" /> : <Icon size={15} />}\n </span>\n <span className=\"blade-chat-file-card-name\">{name}</span>\n <Download size={13} className=\"blade-chat-file-card-action\" />\n </button>\n {failed && <span className=\"blade-chat-file-card-error\">下载失败</span>}\n </span>\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 * 判断提问卡片该等待作答还是展示成已作答,只认工具状态。\n * 与 apps/web 的 ToolCallBlock 里同名函数保持一致:两处算出不同结果时,\n * 同一份会话在内置页面能点、在 SDK 里点不动。\n *\n * 不要再拿 sessionStatus 兜底。历史投影算错时用 waiting_for_input 补救,\n * 会同时把已经回答过的旧提问重新变成可作答(历史加载不会带回 answerData),\n * 而且会盖住投影层的错误——这个 bug 就是这么藏住的。\n */\nexport function resolveAskQuestionState({\n toolStatus,\n hasAnswerData,\n fallbackAnswered,\n}: {\n toolStatus: ToolCallInfo[\"status\"]\n hasAnswerData: boolean\n fallbackAnswered?: boolean\n}) {\n const awaitingAnswer = !hasAnswerData && toolStatus === \"awaiting_answer\"\n return {\n awaitingAnswer,\n answered:\n hasAnswerData ||\n (!awaitingAnswer &&\n (Boolean(fallbackAnswered) ||\n toolStatus === \"done\" ||\n toolStatus === \"cancelled\" ||\n toolStatus === \"error\")),\n }\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 questionState = resolveAskQuestionState({\n toolStatus: toolCall.status,\n hasAnswerData: Boolean(answerData),\n fallbackAnswered: answered,\n })\n const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer)\n if (askData) {\n return (\n <AskUserQuestionBlock\n data={askData}\n answered={questionState.answered}\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 {/* 与内置 Web 的 ErrorMessageBlock 保持一致:消息流里的错误不用红色。 */}\n <div className=\"blade-chat-error-block max-w-[85%] border-l-[3px] border-[hsl(var(--border))] px-4 py-1 text-sm leading-7 text-[hsl(var(--muted-foreground))]\">\n {text}\n </div>\n </div>\n )\n}\n","import type { AskUserAnswerData, ChatMessage, ConnectionStatus } from \"@blade-hq/agent-client\"\nimport { CircleAlert } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\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\n/** 两种模式共用的外观属性。 */\nexport interface ChatPresentationProps {\n classNames?: ChatViewClassNames\n renderers?: ChatViewRenderers\n slots?: ChatViewSlots\n placeholder?: string\n /** 配色主题,默认浅色。宿主已经在祖先元素上写了 data-theme 时不用传。 */\n theme?: \"light\" | \"dark\"\n}\n\n/** 主题落到聊天根节点上:深色变量挂在 [data-theme=\"dark\"] 上,由它继承给整棵子树。 */\nexport function themeAttr(theme: ChatPresentationProps[\"theme\"]): \"dark\" | undefined {\n return theme === \"dark\" ? \"dark\" : undefined\n}\n\ninterface ChatSurfaceProps extends ChatPresentationProps {\n connection: ConnectionStatus\n errorMessage: string | null\n messages: ChatMessage[]\n isStreaming: boolean\n isStopping: boolean\n inputText: string\n onInputChange: (value: string) => void\n onSend: (text: string) => Promise<boolean>\n onStop: () => void\n sessionStatus?: string\n askAnswers?: Record<string, AskUserAnswerData>\n onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void\n sessionId?: string\n /** 输入框上方的内部区域(纯 LLM 模式的高级设置用)。不传时不渲染任何节点。 */\n beforeInput?: ReactNode\n /** 连接状态条下方的横幅(回放模式条用)。不传时不渲染任何节点。 */\n banner?: ReactNode\n}\n\n/**\n * 消息流 + 输入框:智能体模式与纯 LLM 模式共用同一套外观,差别只在谁喂数据。\n *\n * 这里的 DOM 结构受 tests/chat-view-compat.test.tsx 的快照保护——接入方拿\n * .blade-chat-* 类名写了自己的样式,结构一动他们的页面就歪。\n */\nexport function ChatSurface({\n theme,\n classNames,\n renderers,\n slots,\n placeholder,\n connection,\n errorMessage,\n messages,\n isStreaming,\n isStopping,\n inputText,\n onInputChange,\n onSend,\n onStop,\n sessionStatus,\n askAnswers,\n onAnswer,\n sessionId,\n beforeInput,\n banner,\n}: ChatSurfaceProps) {\n return (\n <div\n data-theme={themeAttr(theme)}\n className={cn(\n \"blade-chat flex min-h-0 flex-1 flex-col overflow-hidden\",\n classNames?.root,\n )}\n >\n <ConnectionBanner connection={connection} className={classNames?.banner} />\n {banner}\n {/* 出错条的配色由 .blade-chat-error-bar 的 --destructive 变量给,别写死 red-*:\n Tailwind 的 dark: 变体跟的是系统偏好,会和 data-theme 主题打架。 */}\n {errorMessage && (\n <div className=\"blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm\">\n <CircleAlert size={16} className=\"mt-0.5 shrink-0\" />\n <span>{errorMessage}</span>\n </div>\n )}\n {slots?.header}\n <MessageList\n messages={messages}\n isStreaming={isStreaming}\n sessionStatus={sessionStatus}\n askAnswers={askAnswers}\n onAnswer={onAnswer}\n toolCallRenderer={renderers?.toolCall}\n emptyState={slots?.emptyState}\n className={classNames?.messageList}\n sessionId={sessionId}\n />\n {beforeInput}\n <ChatInput\n value={inputText}\n onValueChange={onInputChange}\n onSend={onSend}\n onStop={onStop}\n isStreaming={isStreaming}\n isStopping={isStopping}\n placeholder={placeholder}\n className={classNames?.chatInput}\n />\n {slots?.footer}\n </div>\n )\n}\n","import { useEffect, useMemo, useState } from \"react\"\nimport { useLlmChat, type LlmChatOptions } from \"../hooks/use-llm-chat\"\nimport { ChatSurface, type ChatPresentationProps } from \"./ChatSurface\"\nimport {\n LlmAdvancedSettingsBar,\n readOverride,\n writeOverride,\n type LlmAdvancedSettings,\n type LlmOverride,\n} from \"./LlmAdvancedSettings\"\n\n/** 纯 LLM 模式的操作句柄,对应智能体模式的 session.insertText() / send()。 */\nexport interface LlmChatHandle {\n /** 把文本放进输入框交给用户确认,不直接发出去。 */\n insertText: (text: string) => void\n send: (text: string) => Promise<boolean>\n /** 清空这段对话。 */\n reset: () => void\n}\n\nexport interface LlmChatProps extends LlmChatOptions, ChatPresentationProps {\n /** 拿到操作句柄:业务界面把选中的数据塞进输入框,或直接发一句。 */\n onReady?: (handle: LlmChatHandle) => void\n /**\n * 打开输入框上方的「高级设置」,让用户自己改模型服务地址(默认还能改模型名)。\n * 不传就一个节点都不渲染。传 `true` 用默认配置,或用对象细调。\n */\n advanced?: boolean | LlmAdvancedSettings\n /** 用户改了覆盖值时回调,方便宿主自己也记一份。 */\n onOverrideChange?: (override: LlmOverride) => void\n}\n\n/**\n * 纯 LLM 模式的聊天界面:不建会话、不连实时通道、也不要求登录 Blade,\n * 只按 **OpenAI Chat Completions** 标准协议跟模型服务一问一答。\n *\n * 默认形态是把 `baseURL` 指向自己后端的透传路径,由后端补密钥再转发,浏览器不持有密钥。\n */\nexport function LlmChat({\n classNames,\n renderers,\n slots,\n placeholder,\n theme,\n onReady,\n advanced,\n onOverrideChange,\n ...options\n}: LlmChatProps) {\n // 用户在高级设置里填的值盖住调用方传进来的默认值;本轮请求用哪套由这里决定\n const [override, setOverride] = useState<LlmOverride>(() => readOverride(advanced, options.baseURL))\n // 每次渲染新建对象也没关系:useLlmChat 每轮都把配置写进 ref,send 用的是调用那一刻的值\n const effective = { ...options, ...override }\n const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective)\n const [inputText, setInputText] = useState(\"\")\n const [stopRequested, setStopRequested] = useState(false)\n\n // 句柄引用要稳定:随渲染换新对象的话,宿主放进 useEffect 依赖里会连环重跑\n const handle = useMemo<LlmChatHandle>(\n () => ({\n insertText: (text) => setInputText((prev) => (prev ? `${prev}\\n${text}` : text)),\n send,\n reset,\n }),\n [send, reset],\n )\n useEffect(() => {\n onReady?.(handle)\n }, [handle, onReady])\n\n return (\n <ChatSurface\n theme={theme}\n classNames={classNames}\n renderers={renderers}\n slots={slots}\n placeholder={placeholder}\n // 没有\"连接\"这回事:不建会话也不连实时通道,状态条恒为已连接\n connection=\"connected\"\n errorMessage={error}\n messages={messages}\n isStreaming={isStreaming}\n isStopping={stopRequested && isStreaming}\n inputText={inputText}\n onInputChange={setInputText}\n onSend={async (text) => {\n setStopRequested(false)\n if (!text.trim() || isStreaming) return false\n // 只等\"这条已经收下\",不等整段流跑完:等下去的话输入框在整个生成期间都清不掉,\n // 用户中途敲的下一句还会被清掉。失败通过出错条反馈,不靠输入框。\n void send(text)\n return true\n }}\n onStop={() => {\n setStopRequested(true)\n stop()\n }}\n beforeInput={\n advanced ? (\n <LlmAdvancedSettingsBar\n settings={advanced}\n defaults={{ baseURL: options.baseURL, model: options.model }}\n override={override}\n onChange={(next) => {\n setOverride(next)\n writeOverride(advanced, options.baseURL, next)\n onOverrideChange?.(next)\n }}\n />\n ) : undefined\n }\n />\n )\n}\n","import { Settings2 } from \"lucide-react\"\nimport { useState } from \"react\"\n\n/** 纯 LLM 模式的高级设置:让用户自己改模型服务地址。 */\nexport interface LlmAdvancedSettings {\n /** 允许改模型服务地址,默认允许。 */\n baseURL?: boolean\n /** 允许改模型名,默认允许。 */\n model?: boolean\n /**\n * 允许在界面上填密钥,默认**不允许**。\n * 打开意味着密钥存在浏览器里,只适合受控内网、演示,或密钥本身就属于当前用户的场景。\n */\n apiKey?: boolean\n /** 覆盖值存哪:`local` 记住用户的选择(默认),`memory` 只在本次会话有效。 */\n storage?: \"local\" | \"memory\"\n /** localStorage 的键,默认按组件拿到的 baseURL 生成。 */\n storageKey?: string\n}\n\nexport interface LlmOverride {\n baseURL?: string\n model?: string\n apiKey?: string\n}\n\nconst FIELDS: Array<{ id: keyof LlmOverride; label: string; placeholder: string; secret?: boolean }> = [\n { id: \"baseURL\", label: \"模型服务地址\", placeholder: \"http://内网地址/v1\" },\n { id: \"model\", label: \"模型\", placeholder: \"模型名称\" },\n { id: \"apiKey\", label: \"密钥\", placeholder: \"留空表示不改\", secret: true },\n]\n\nexport function normalizeAdvanced(\n value: boolean | LlmAdvancedSettings | undefined,\n): Required<Pick<LlmAdvancedSettings, \"baseURL\" | \"model\" | \"apiKey\" | \"storage\">> | null {\n if (!value) return null\n const config = value === true ? {} : value\n return {\n baseURL: config.baseURL ?? true,\n model: config.model ?? true,\n apiKey: config.apiKey ?? false,\n storage: config.storage ?? \"local\",\n }\n}\n\nfunction storageKeyFor(settings: boolean | LlmAdvancedSettings | undefined, baseURL: string): string {\n const explicit = typeof settings === \"object\" ? settings.storageKey : undefined\n return `blade-llm-override:${explicit ?? baseURL}`\n}\n\nexport function readOverride(\n settings: boolean | LlmAdvancedSettings | undefined,\n baseURL: string,\n): LlmOverride {\n const normalized = normalizeAdvanced(settings)\n if (!normalized || normalized.storage !== \"local\" || typeof localStorage === \"undefined\") return {}\n try {\n const raw = localStorage.getItem(storageKeyFor(settings, baseURL))\n if (!raw) return {}\n const stored = JSON.parse(raw) as LlmOverride\n // 只认当前还允许改的字段:宿主把密钥字段关掉之后,之前存下的密钥不能继续\n // 被悄悄发出去(同一个 storageKey 被别处写过也一样)。\n return Object.fromEntries(\n Object.entries(stored).filter(([key]) => normalized[key as keyof LlmOverride]),\n ) as LlmOverride\n } catch {\n // 隐私模式等场景读不到就当没设过\n return {}\n }\n}\n\nfunction writeOverride(\n settings: boolean | LlmAdvancedSettings | undefined,\n baseURL: string,\n override: LlmOverride,\n): void {\n const normalized = normalizeAdvanced(settings)\n if (!normalized || normalized.storage !== \"local\" || typeof localStorage === \"undefined\") return\n try {\n const key = storageKeyFor(settings, baseURL)\n if (Object.keys(override).length === 0) localStorage.removeItem(key)\n else localStorage.setItem(key, JSON.stringify(override))\n } catch {\n // 写不进去就退化成只在本次会话有效\n }\n}\n\ninterface Props {\n settings: boolean | LlmAdvancedSettings\n /** 组件拿到的默认值,用来做占位提示与\"恢复默认\" */\n defaults: { baseURL: string; model: string }\n override: LlmOverride\n onChange: (next: LlmOverride) => void\n}\n\n/**\n * 输入框上方的一行「高级设置」:默认收起,展开后可以改模型服务地址与模型。\n * 只有调用方显式打开才渲染——不传 advanced 时这里一个节点都不会出现。\n */\nexport function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }: Props) {\n const normalized = normalizeAdvanced(settings)\n const [open, setOpen] = useState(false)\n const [draft, setDraft] = useState<LlmOverride>(override)\n if (!normalized) return null\n\n const fields = FIELDS.filter((field) => normalized[field.id])\n const dirty = Object.keys(override).length > 0\n\n return (\n <div className=\"blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs\">\n <button\n type=\"button\"\n onClick={() => {\n setDraft(override)\n setOpen((value) => !value)\n }}\n className=\"flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]\"\n >\n <Settings2 size={13} />\n 高级设置\n {dirty && <span className=\"text-[hsl(var(--primary))]\">(已自定义)</span>}\n </button>\n {open && (\n <div className=\"mt-2 flex flex-col gap-2\">\n {fields.map((field) => (\n <label key={field.id} className=\"flex flex-col gap-1\">\n <span className=\"text-[hsl(var(--muted-foreground))]\">{field.label}</span>\n <input\n type={field.secret ? \"password\" : \"text\"}\n value={draft[field.id] ?? \"\"}\n placeholder={\n field.id === \"apiKey\"\n ? field.placeholder\n : (defaults[field.id as \"baseURL\" | \"model\"] || field.placeholder)\n }\n onChange={(event) => setDraft({ ...draft, [field.id]: event.target.value })}\n className=\"rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-2 py-1 text-xs text-[hsl(var(--foreground))] outline-none\"\n />\n </label>\n ))}\n {normalized.apiKey && (\n <p className=\"text-[hsl(var(--muted-foreground))]\">\n 密钥会存在这台浏览器里。只在你信得过这台机器时填。\n </p>\n )}\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n // 空字符串等于没改,不要把空值当成覆盖发出去\n const next = Object.fromEntries(\n Object.entries(draft).filter(([, value]) => String(value ?? \"\").trim() !== \"\"),\n ) as LlmOverride\n onChange(next)\n setOpen(false)\n }}\n className=\"rounded-md bg-[hsl(var(--primary))] px-2.5 py-1 font-medium text-[hsl(var(--primary-foreground))]\"\n >\n 保存\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setDraft({})\n onChange({})\n setOpen(false)\n }}\n className=\"rounded-md border border-[hsl(var(--border))] px-2.5 py-1 text-[hsl(var(--foreground))]\"\n >\n 恢复默认\n </button>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport { writeOverride }\n","import { AgentChat, type AgentChatProps } from \"./AgentChat\"\nimport { LlmChat, type LlmChatHandle } from \"./LlmChat\"\nimport type { LlmAdvancedSettings, LlmOverride } from \"./LlmAdvancedSettings\"\nimport type { LlmChatOptions } from \"../hooks/use-llm-chat\"\n\nexport type {\n ChatViewClassNames,\n ChatViewRenderers,\n ChatViewSlots,\n} from \"./ChatSurface\"\n\nexport interface ChatViewProps extends AgentChatProps {\n /** 挂哪个下层组件。默认 `agent`——不传就是接 Blade 智能体会话的原有行为。 */\n mode?: \"agent\" | \"llm\"\n /** `mode=\"llm\"` 时的模型服务配置;`advanced` 打开输入框上方的「高级设置」。 */\n llm?: LlmChatOptions & {\n advanced?: boolean | LlmAdvancedSettings\n onOverrideChange?: (override: LlmOverride) => void\n }\n /** `mode=\"llm\"` 时拿到操作句柄;智能体模式请用 `onSessionReady`。 */\n onLlmReady?: (handle: LlmChatHandle) => void\n}\n\n/**\n * 聊天界面。默认接 Blade 智能体会话,`mode=\"llm\"` 时改接标准 OpenAI 对话接口。\n *\n * 这一层只做分流:两种模式各自是独立组件,已经确定要哪一种的可以直接用\n * `<AgentChat>` 或 `<LlmChat>`。\n */\nexport function ChatView(props: ChatViewProps) {\n // 参数保持单个 props、在函数体里解构:公共面报告会把参数列表原样印出来,\n // 在参数位置解构会让签名行发生变化,而这一行是接入方看兼容性的依据。\n const { mode, llm, onLlmReady, ...rest } = props\n if (mode === \"llm\") {\n if (!llm) {\n throw new Error('ChatView: mode=\"llm\" 需要同时传 llm={{ baseURL, model }}')\n }\n return (\n <LlmChat\n {...llm}\n classNames={rest.classNames}\n renderers={rest.renderers}\n slots={rest.slots}\n placeholder={rest.placeholder}\n theme={rest.theme}\n onReady={onLlmReady}\n />\n )\n }\n return <AgentChat {...rest} />\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\"\nexport { useReplay } from \"./hooks/use-replay\"\nexport type { ReplayMismatch, UseReplayResult } from \"./hooks/use-replay\"\nexport { useLlmChat } from \"./hooks/use-llm-chat\"\nexport type {\n ChatCompletionTool,\n LlmChatOptions,\n LlmToolCall,\n UseLlmChatResult,\n} from \"./hooks/use-llm-chat\"\n\n// ============================== 聊天界面 ==============================\nexport { ChatView } from \"./components/ChatView\"\nexport type {\n ChatViewClassNames,\n ChatViewProps,\n ChatViewRenderers,\n ChatViewSlots,\n} from \"./components/ChatView\"\n// 下层组件:已经确定要哪种模式的可以直接用,不必经 ChatView 分流\nexport { AgentChat } from \"./components/AgentChat\"\nexport type { AgentChatProps } from \"./components/AgentChat\"\nexport { LlmChat } from \"./components/LlmChat\"\nexport type { LlmChatHandle, LlmChatProps } from \"./components/LlmChat\"\nexport type { LlmAdvancedSettings, LlmOverride } from \"./components/LlmAdvancedSettings\"\nexport { MarkdownContent } from \"./components/MarkdownContent\"\nexport type { MarkdownContentProps } from \"./components/MarkdownContent\"\nexport { ReplayBar } from \"./components/ReplayBar\"\nexport type { ReplayBarProps } from \"./components/ReplayBar\"\nexport { ReplayMismatchPrompt } from \"./components/ReplayMismatchPrompt\"\nexport type { ReplayMismatchPromptProps } from \"./components/ReplayMismatchPrompt\"\nexport type { ToolCallRenderer } from \"./components/ToolCallBlock\"\n\n// ============================== 客户端全量转出 ==============================\n// 方便接入方只装一个包:agent-client 的全部公共面从这里原样可用。\nexport * from \"@blade-hq/agent-client\"\n"],"mappings":";;;;;;AACA,SAAS,eAAe,kBAAkC;AAcjD;AAZT,IAAM,qBAAqB,cAAkC,IAAI;AAW1D,SAAS,cAAc,EAAE,QAAQ,SAAS,GAAuB;AACtE,SAAO,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,QAAS,UAAS;AAC/D;AAGO,SAAS,iBAA8B;AAC5C,QAAM,SAAS,WAAW,kBAAkB;AAC5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,sEAAwC;AAAA,EAC1D;AACA,SAAO;AACT;;;ACpBA,SAAS,WAAW,SAAS,QAAQ,UAAU,4BAA4B;AAmB3E,IAAM,kBAAkB,MAAM,MAAM;AAAC;AAKrC,IAAM,mBAAmB;AAelB,SAAS,gBACd,WACA,UAAkC,CAAC,GACZ;AACvB,QAAM,SAAS,eAAe;AAC9B,QAAM,CAAC,SAAS,UAAU,IAAI,SAA8B,IAAI;AAChE,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AAGrD,QAAM,UAAU,OAAyE;AAAA,IACvF,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,KAAK;AAAA,EACP,CAAC;AAED,QAAM,sBAAsB,OAA+B,IAAI;AAC/D,QAAM,eAAe,OAAO,QAAQ,gBAAgB;AACpD,eAAa,UAAU,QAAQ;AAC/B,QAAM,mBAAmB,OAAO,QAAQ,aAAa;AACrD,mBAAiB,UAAU,QAAQ;AAEnC,QAAM,eAAe,OAAO,SAAS;AACrC,eAAa,UAAU;AAGvB,QAAM,UAAU,QAAQ,MAAM;AAC5B,WAAO,CAAC,aAAiC;AACvC,YAAM,MAAM,EAAE,QAAQ,QAAQ;AAC9B,YAAM,YAAY,WACd,QAAQ,QAAQ,QAAQ;AAAA;AAAA,QAEvB,oBAAoB,YAAY,OAAO,SACrC,OAAO,iBAAiB,WAAW,CAAC,CAAC,EACrC,KAAK,CAAC,YAAY;AAEjB,gBAAM,KAAK,QAAQ;AACnB,kBAAQ,QAAQ;AAChB,uBAAa,UAAU,EAAE;AACzB,iBAAO;AAAA,QACT,CAAC;AAAA;AAEP,gBACG,KAAK,CAAC,OAAO,OAAO,IAAI,QAAQ,EAAE,CAAC,EACnC,KAAK,CAAC,SAAS;AACd,YAAI,QAAQ,QAAQ,QAAQ,KAAK;AAC/B,eAAK,QAAQ;AACb;AAAA,QACF;AACA,gBAAQ,QAAQ,KAAK,KAAK;AAC1B,gBAAQ,QAAQ,UAAU;AAC1B,mBAAW,IAAI;AACf,iBAAS,IAAI;AAAA,MACf,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,YAAI,QAAQ,QAAQ,QAAQ,IAAK;AACjC,4BAAoB,UAAU;AAC9B,iBAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,MAC9D,CAAC;AAAA,IACL;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,YAAU,MAAM;AACd,YAAQ,aAAa,OAAO;AAC5B,WAAO,MAAM;AACX,cAAQ,QAAQ;AAChB,YAAM,YAAY,QAAQ,QAAQ;AAClC,cAAQ,QAAQ,KAAK;AACrB,cAAQ,QAAQ,UAAU;AAC1B,iBAAW,IAAI;AACf,UAAI,UAAW,YAAW,MAAM,UAAU,QAAQ,GAAG,gBAAgB;AAAA,IACvE;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAIZ,YAAU,MAAM;AACd,QAAI,CAAC,UAAW;AAChB,QAAI,QAAQ,QAAQ,OAAO,KAAM;AACjC,QAAI,cAAc,QAAQ,QAAQ,GAAI;AACtC,UAAM,WAAW,QAAQ,QAAQ;AACjC,YAAQ,QAAQ,KAAK;AACrB,YAAQ,QAAQ,UAAU;AAC1B,QAAI,SAAU,YAAW,MAAM,SAAS,QAAQ,GAAG,gBAAgB;AACnE,YAAQ,SAAS;AAAA,EACnB,GAAG,CAAC,WAAW,OAAO,CAAC;AAEvB,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL,WAAW,UAAU,CAAC,aAAyB,QAAQ,UAAU,QAAQ,IAAI;AAAA,MAC7E,aAAa,MAAO,UAAU,QAAQ,SAAS,IAAI;AAAA,IACrD;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AACA,QAAM,QAAQ,qBAAqB,MAAM,WAAW,MAAM,aAAa,MAAM,WAAW;AAExF,SAAO,EAAE,SAAS,OAAO,MAAM;AACjC;;;AC7IA,SAAS,4BAA4B;AAErC,SAAS,aAAa,aAAAA,YAAW,YAAAC,iBAAgB;AAsCjD,IAAM,iBAAiB,oBAAI,IAAY;AAmBhC,SAAS,UAAU,SAA+C;AACvE,QAAM,SAAS,eAAe;AAC9B,QAAM,YAAY,SAAS,aAAa;AAExC,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAiC,IAAI;AACjE,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAqC,IAAI;AAE7E,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,KAAK;AACxD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAgC,IAAI;AAEpE,QAAM,CAAC,SAAS,UAAU,IAAIA,UAE5B,EAAE,QAAQ,UAAU,CAAC;AACvB,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,KAAK;AAClD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAErD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAsB,oBAAoB;AAG9E,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ,gBAAU,IAAI;AACd,oBAAc,IAAI;AAClB,uBAAiB,KAAK;AACtB;AAAA,IACF;AACA,UAAM,OAAO,MAAM;AACjB,YAAM,QAAQ,QAAQ,SAAS;AAC/B,gBAAU,MAAM,MAAM;AACtB,oBAAc,MAAM,UAAU;AAC9B,uBAAiB,MAAM,eAAe,MAAM,WAAW,SAAS;AAAA,IAClE;AACA,SAAK;AACL,WAAO,QAAQ,UAAU,IAAI;AAAA,EAC/B,GAAG,CAAC,OAAO,CAAC;AAMZ,QAAM,CAAC,eAAe,gBAAgB,IAAID,UAAS,SAAS;AAC5D,MAAI,cAAc,eAAe;AAC/B,qBAAiB,SAAS;AAC1B,gBAAY,IAAI;AAChB,aAAS,IAAI;AACb,kBAAc,oBAAoB;AAAA,EACpC;AAGA,EAAAC,WAAU,MAAM;AACd,eAAW,EAAE,QAAQ,UAAU,CAAC;AAChC,QAAI,CAAC,UAAW;AAChB,QAAI,QAAQ;AACZ,WAAO,SACJ,iBAAiB,SAAS,EAC1B,KAAK,CAAC,WAAW;AAChB,UAAI,CAAC,MAAO;AACZ,iBAAW;AAAA,QACT,QAAQ;AAAA,QACR,WAAW,OAAO;AAAA,QAClB,QAAQ,OAAO,UAAU;AAAA,MAC3B,CAAC;AAAA,IACH,CAAC,EACA,MAAM,MAAM;AAGX,UAAI,MAAO,YAAW,EAAE,QAAQ,SAAS,WAAW,MAAM,QAAQ,KAAK,CAAC;AAAA,IAC1E,CAAC;AACH,WAAO,MAAM;AACX,cAAQ;AAAA,IACV;AAAA,EACF,GAAG,CAAC,QAAQ,WAAW,aAAa,CAAC;AAErC,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,QAAS;AACd,WAAO,QAAQ,GAAG,kBAAkB,CAAC,EAAE,eAAe,iBAAiB,QAAQ,MAAM;AACnF,kBAAY;AAAA,QACV,eAAe,iBAAiB;AAAA,QAChC,iBAAiB,mBAAmB;AAAA,QACpC,SAAS,CAAC,aAAa;AACrB,sBAAY,IAAI;AAChB,kBAAQ,QAAQ;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,WAAW,QAAQ,YAAY;AAErC,QAAM,WAAW;AAAA,IACf,OAAO,SAAsB;AAC3B,oBAAc,IAAI;AAClB,UAAI,CAAC,QAAS;AACd,eAAS,IAAI;AACb,UAAI;AACF,cAAM,QAAQ,eAAe,IAAI;AAAA,MACnC,SAAS,KAAK;AACZ,iBAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,MAC9D;AAAA,IACF;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,mBAAmB,YAAY,YAAY;AAC/C,QAAI,CAAC,QAAS;AACd,aAAS,IAAI;AAIb,UAAM,UAAU;AAChB,QAAI,SAAS;AACX,cAAQ,QAAQ,iBAAiB;AACjC;AAAA,IACF;AACA,QAAI;AACF,YAAM,QAAQ,WAAW;AAAA,IAC3B,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,IAC9D;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,CAAC;AAEtB,QAAM,cAAc;AAAA,IAClB,OAAO,UAAwB;AAC7B,UAAI,CAAC,UAAW,OAAM,IAAI,MAAM,8DAAY;AAG5C,UAAI,eAAe,IAAI,SAAS,EAAG,OAAM,IAAI,MAAM,0EAAc;AACjE,qBAAe,IAAI,SAAS;AAC5B,oBAAc,IAAI;AAClB,eAAS,IAAI;AACb,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,SAAS,mBAAmB,WAAW,SAAS,UAAU;AACtF,eAAO,OAAO;AAAA,MAChB,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAClE,iBAAS,OAAO;AAChB,cAAM;AAAA,MACR,UAAE;AACA,uBAAe,OAAO,SAAS;AAC/B,sBAAc,KAAK;AAAA,MACrB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,WAAW,UAAU;AAAA,EAChC;AAEA,SAAO;AAAA,IACL;AAAA,IACA,OAAO,WAAY,QAAQ,SAAS,IAAK;AAAA,IACzC;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,eAAe;AAAA,IAC3B,WACE,QAAQ,SAAS,KACjB,QAAQ,WAAW,WACnB,QAAQ,aACR,eAAe;AAAA,IACjB,mBACE,QAAQ,WAAW,WAAW,CAAC,QAAQ,YAAY,QAAQ,SAAS;AAAA,IACtE;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC7NA,SAAS,eAAAC,cAAa,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAgEvD,IAAM,wBAAwB;AAC9B,IAAM,0BAA0B;AAGhC,IAAI,iBAAiB;AASd,SAAS,WAAW,SAA2C;AACpE,QAAM,CAAC,SAAS,UAAU,IAAIA,UAA0B,CAAC,CAAC;AAC1D,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAwB,IAAI;AACtE,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAA2B,CAAC,CAAC;AACzE,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAmB,CAAC,CAAC;AAC/D,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AACtD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,WAAWD,QAA+B,IAAI;AAEpD,QAAM,gBAAgBA,QAAO,CAAC;AAE9B,QAAM,aAAaA,QAAwB,CAAC,CAAC;AAC7C,QAAM,aAAaA,QAAO,OAAO;AACjC,aAAW,UAAU;AACrB,aAAW,UAAU;AAErB,QAAM,OAAOF,aAAY,MAAM;AAC7B,aAAS,SAAS,MAAM;AACxB,aAAS,UAAU;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQA,aAAY,MAAM;AAC9B,kBAAc,WAAW;AACzB,SAAK;AACL,eAAW,CAAC,CAAC;AACb,qBAAiB,IAAI;AACrB,sBAAkB,CAAC,CAAC;AACpB,qBAAiB,CAAC,CAAC;AACnB,aAAS,IAAI;AACb,mBAAe,KAAK;AAAA,EACtB,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,OAAOA,aAAY,OAAO,SAAmC;AACjE,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,WAAW,SAAS,QAAS,QAAO;AACzC,UAAM,OAAO,WAAW;AACxB,UAAM,YAAY,KAAK,iBAAiB;AAExC,UAAM,SAAS,CAAC,YAA2B;AACzC,iBAAW,UAAU,CAAC,GAAG,WAAW,SAAS,OAAO;AACpD,iBAAW,WAAW,OAAO;AAAA,IAC/B;AAEA,aAAS,IAAI;AACb,mBAAe,IAAI;AACnB,qBAAiB,EAAE;AACnB,sBAAkB,CAAC,CAAC;AACpB,WAAO,EAAE,MAAM,QAAQ,QAAQ,CAAC;AAEhC,QAAI,UAAU;AAEd,UAAM,aAAa,IAAI,gBAAgB;AACvC,aAAS,UAAU;AACnB,UAAM,aAAa,cAAc;AACjC,QAAI;AACF,eAAS,QAAQ,KAAK,SAAS,GAAG;AAChC,cAAM,SAAS,MAAM,WAAW,MAAM,WAAW,SAAS,WAAW,QAAQ;AAAA,UAC3E,QAAQ,CAAC,UAAU;AACjB,uBAAW;AACX,6BAAiB,CAAC,SAAS,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAE;AAAA,UACpD;AAAA,UACA,aAAa,CAAC,UAAU,kBAAkB,KAAK;AAAA,QACjD,CAAC;AAED,cAAM,YAA2B;AAAA,UAC/B,MAAM;AAAA,UACN,SAAS,OAAO;AAAA,UAChB,GAAI,OAAO,UAAU,SAAS,EAAE,YAAY,OAAO,UAAU,IAAI,CAAC;AAAA,QACpE;AACA,yBAAiB,IAAI;AACrB,0BAAkB,CAAC,CAAC;AACpB,eAAO,SAAS;AAEhB,YAAI,CAAC,OAAO,UAAU,OAAQ,QAAO;AAKrC,cAAM,OAAO,CAAC,WAAmB;AAC/B,qBAAW,QAAQ,OAAO,WAAW;AACnC,mBAAO,EAAE,MAAM,QAAQ,cAAc,KAAK,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,OAAO,CAAC,EAAE,CAAC;AAAA,UAC5F;AACA,2BAAiB,CAAC,SAAS,CAAC,GAAG,MAAM,GAAG,OAAO,UAAU,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC,CAAC;AAAA,QAClF;AACA,YAAI,CAAC,KAAK,YAAY;AACpB,eAAK,sFAAgB;AACrB,iBAAO;AAAA,QACT;AACA,YAAI,SAAS,WAAW;AACtB,eAAK,oDAAY,SAAS,uCAAS;AACnC,mBAAS,wCAAU,SAAS,2EAAe;AAC3C,iBAAO;AAAA,QACT;AAEA,mBAAW,QAAQ,OAAO,WAAW;AACnC,cAAI;AACJ,cAAI;AACF,kBAAM,QAAQ,MAAM,KAAK,WAAW;AAAA,cAClC,IAAI,KAAK;AAAA,cACT,MAAM,KAAK,SAAS;AAAA,cACpB,WAAW,KAAK,SAAS;AAAA,YAC3B,CAAC;AACD,qBAAS,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,IAAI;AAAA,UAC3E,SAAS,KAAK;AAEZ,qBAAS,KAAK,UAAU,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE,CAAC;AACnF,6BAAiB,CAAC,SAAS,CAAC,GAAG,MAAM,KAAK,EAAE,CAAC;AAAA,UAC/C;AACA,iBAAO,EAAE,MAAM,QAAQ,cAAc,KAAK,IAAI,SAAS,OAAO,CAAC;AAAA,QACjE;AACA,yBAAiB,EAAE;AAAA,MACrB;AAAA,IACF,SAAS,KAAK;AACZ,uBAAiB,IAAI;AACrB,wBAAkB,CAAC,CAAC;AACpB,UAAI,WAAW,OAAO,SAAS;AAE7B,YAAI,eAAe,cAAc,QAAS,QAAO;AAGjD,eAAO,EAAE,MAAM,aAAa,SAAS,UAAU,GAAG,OAAO,mCAAU,iCAAQ,CAAC;AAC5E,eAAO;AAAA,MACT;AAGA,UAAI,WAAW,eAAe,cAAc,SAAS;AACnD,eAAO,EAAE,MAAM,aAAa,SAAS,QAAQ,CAAC;AAAA,MAChD;AACA,eAAS,eAAe,SAAS,IAAI,UAAU,IAAI,UAAU,kDAAU;AACvE,aAAO;AAAA,IACT,UAAE;AACA,UAAI,SAAS,YAAY,WAAY,UAAS,UAAU;AACxD,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,WAAWC;AAAA,IACf,MAAM,eAAe,SAAS,eAAe,gBAAgB,aAAa;AAAA,IAC1E,CAAC,SAAS,eAAe,gBAAgB,aAAa;AAAA,EACxD;AAEA,SAAO,EAAE,UAAU,aAAa,OAAO,MAAM,MAAM,MAAM;AAC3D;AAQA,eAAe,WACb,SACA,SACA,QACA,UACuB;AACvB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,UAAU,YAAY,SAAS,YAAY;AACjD,QAAM,UAAU;AAAA,IACd;AAAA,IACA,QAAQ;AAAA,IACR,UAAU,SAAS,CAAC,EAAE,MAAM,UAAU,SAAS,OAAO,GAAG,GAAG,OAAO,IAAI;AAAA,IACvE,GAAI,OAAO,SAAS,EAAE,MAAM,IAAI,CAAC;AAAA,IACjC,GAAI,gBAAgB,SAAY,CAAC,IAAI,EAAE,YAAY;AAAA,IACnD,GAAG;AAAA,EACL;AAEA,QAAM,UAAU,aAAa,WAAW;AACxC,QAAM,MAAM,GAAG,QAAQ,QAAQ,OAAO,EAAE,CAAC;AACzC,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,QAAQ,KAAK;AAAA,MAC5B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,GAAI,SAAS,EAAE,eAAe,UAAU,MAAM,GAAG,IAAI,CAAC;AAAA,QACtD,GAAG;AAAA,MACL;AAAA,MACA,MAAM,KAAK,UAAU,OAAO;AAAA,MAC5B;AAAA,IACF,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,OAAO,QAAS,OAAM;AAG1B,UAAM,IAAI,MAAM,8CAAW,GAAG,SAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,EACtF;AACA,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,MAAM,kBAAkB,QAAQ,CAAC;AAAA,EACnD;AACA,MAAI,CAAC,SAAS,MAAM;AAClB,UAAM,IAAI,MAAM,0EAAc;AAAA,EAChC;AAEA,MAAI,UAAU;AACd,QAAM,QAA0B,CAAC;AACjC,mBAAiB,SAAS,QAAQ,SAAS,MAAM,MAAM,GAAG;AACxD,QAAI,MAAM,OAAO;AACf,YAAM,IAAI,MAAM,MAAM,KAAK;AAAA,IAC7B;AACA,QAAI,MAAM,MAAM;AACd,iBAAW,MAAM;AACjB,eAAS,OAAO,MAAM,IAAI;AAAA,IAC5B;AACA,QAAI,MAAM,gBAAgB;AACxB,iBAAW,SAAS,MAAM,gBAAgB;AAExC,cAAM,QAAQ,MAAM,SAAS;AAC7B,cAAM,WAAW,MAAM,KAAK,KAAK,EAAE,IAAI,IAAI,MAAM,YAAqB,UAAU,EAAE,MAAM,IAAI,WAAW,GAAG,EAAE;AAC5G,cAAM,KAAK,IAAI;AAAA,UACb,IAAI,MAAM,MAAM,SAAS;AAAA,UACzB,MAAM;AAAA,UACN,UAAU;AAAA,YACR,MAAM,MAAM,UAAU,QAAQ,SAAS,SAAS;AAAA,YAChD,WAAW,GAAG,SAAS,SAAS,SAAS,GAAG,MAAM,UAAU,aAAa,EAAE;AAAA,UAC7E;AAAA,QACF;AAAA,MACF;AACA,eAAS,YAAY,MAAM,OAAO,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,GAAG,KAAK,EAAE,CAAC;AAAA,IACzE;AAAA,EACF;AACA,QAAM,aAAa,MAChB,OAAO,OAAO,EAId,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,EAGnC,IAAI,CAAC,SAAS;AACb,QAAI,KAAK,GAAI,QAAO;AACpB,sBAAkB;AAClB,WAAO,EAAE,GAAG,MAAM,IAAI,aAAa,cAAc,GAAG;AAAA,EACtD,CAAC;AACH,SAAO,EAAE,SAAS,WAAW,WAAW;AAC1C;AAGA,SAAS,YAAY,SAA0B,cAAuC;AACpF,QAAM,cAAc,QAAQ,OAAiB,CAAC,KAAK,KAAK,MAAM;AAC5D,QAAI,IAAI,SAAS,OAAQ,KAAI,KAAK,CAAC;AACnC,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AACL,MAAI,YAAY,UAAU,aAAc,QAAO;AAC/C,SAAO,QAAQ,MAAM,YAAY,YAAY,SAAS,YAAY,CAAC;AACrE;AAEA,eAAe,kBAAkB,UAAqC;AACpE,QAAM,MAAM,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AAChD,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAM,UACJ,OAAO,OAAO,UAAU,WACpB,OAAO,QACN,OAAO,OAAO,YACd,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AAC3D,QAAI,QAAS,QAAO,wCAAU,SAAS,MAAM,SAAI,OAAO;AAAA,EAC1D,QAAQ;AAAA,EAER;AACA,QAAM,UAAU,IAAI,KAAK,EAAE,MAAM,GAAG,GAAG;AACvC,SAAO,UAAU,wCAAU,SAAS,MAAM,SAAI,OAAO,KAAK,wCAAU,SAAS,MAAM;AACrF;AAgBA,gBAAgB,QACd,QACA,QAC6B;AAC7B,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AACb,MAAI;AACF,WAAO,CAAC,OAAO,SAAS;AACtB,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,MAAM;AAIR,cAAM,OAAO,SAAS,MAAM;AAC5B,YAAI,QAAQ,SAAS,OAAQ,OAAM;AACnC;AAAA,MACF;AAIA,eAAS,GAAG,MAAM,GAAG,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC,GAAG,QAAQ,SAAS,IAAI;AACpF,UAAI,WAAW,OAAO,QAAQ,MAAM;AACpC,aAAO,aAAa,IAAI;AACtB,cAAM,QAAQ,SAAS,OAAO,MAAM,GAAG,QAAQ,CAAC;AAChD,iBAAS,OAAO,MAAM,WAAW,CAAC;AAClC,YAAI,UAAU,OAAQ;AACtB,YAAI,MAAO,OAAM;AACjB,mBAAW,OAAO,QAAQ,MAAM;AAAA,MAClC;AAAA,IACF;AAAA,EACF,UAAE;AACA,WAAO,OAAO,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAChC;AACF;AAEA,SAAS,SAAS,KAA0C;AAC1D,QAAM,YAAsB,CAAC;AAC7B,aAAW,QAAQ,IAAI,MAAM,IAAI,GAAG;AAClC,QAAI,KAAK,WAAW,OAAO,EAAG,WAAU,KAAK,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC;AAAA,EACnE;AACA,MAAI,CAAC,UAAU,OAAQ,QAAO;AAC9B,QAAM,OAAO,UAAU,KAAK,IAAI;AAChC,MAAI,SAAS,SAAU,QAAO;AAC9B,MAAI;AAIJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,OAAO;AAChB,UAAM,UAAU,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ,OAAO,MAAM;AAC/E,WAAO,EAAE,OAAO,WAAW,yDAAY;AAAA,EACzC;AACA,QAAM,QAAQ,OAAO,UAAU,CAAC,GAAG;AACnC,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,EAAE,MAAM,MAAM,SAAS,gBAAgB,MAAM,WAAW;AACjE;AAGA,SAAS,eACP,SACA,eACA,gBACA,eACe;AACf,QAAM,UAAU,oBAAI,IAAoB;AACxC,aAAW,OAAO,SAAS;AACzB,QAAI,IAAI,SAAS,OAAQ,SAAQ,IAAI,IAAI,cAAc,IAAI,OAAO;AAAA,EACpE;AAEA,QAAM,WAA0B,CAAC;AACjC,aAAW,OAAO,SAAS;AACzB,QAAI,IAAI,SAAS,OAAQ;AACzB,QAAI,IAAI,SAAS,QAAQ;AACvB,eAAS,KAAK,EAAE,MAAM,QAAQ,SAAS,IAAI,SAAS,QAAQ,YAAY,CAAC;AACzE;AAAA,IACF;AACA,aAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,SAAS,IAAI;AAAA,MACb,QAAQ;AAAA,MACR,GAAI,IAAI,YAAY,SAChB,EAAE,YAAY,IAAI,WAAW,IAAI,CAAC,SAAS,eAAe,MAAM,SAAS,aAAa,CAAC,EAAE,IACzF,CAAC;AAAA,IACP,CAAC;AAAA,EACH;AAEA,MAAI,kBAAkB,MAAM;AAC1B,aAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,GAAI,eAAe,SACf,EAAE,YAAY,eAAe,IAAI,CAAC,SAAS,eAAe,MAAM,SAAS,aAAa,CAAC,EAAE,IACzF,CAAC;AAAA,IACP,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,eACP,MACA,SACA,eACc;AACd,QAAM,SAAS,QAAQ,IAAI,KAAK,EAAE;AAClC,QAAM,SAAS,cAAc,SAAS,KAAK,EAAE;AAC7C,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,MAAM,KAAK,SAAS;AAAA,IACpB,WAAW,KAAK,SAAS;AAAA,IACzB,GAAI,WAAW,SAAY,CAAC,IAAI,EAAE,OAAO;AAAA,IACzC,QAAQ,WAAW,SAAY,YAAY,SAAS,UAAU;AAAA,EAChE;AACF;;;ACreA,SAAS,qBAAqB;A;;;;;ACOjB,IAAA,cAAc,CAAC,WAC1B,OAAO,QAAQ,sBAAsB,OAAO,EAAE,YAAY;AA+B/C,IAAA,eAAe,IAA2C,YACrE,QACG,OAAO,CAAC,WAAW,OAAO,UAAU;AAEjC,SAAA,QAAQ,SAAS,KAChB,UAAqB,KAAA,MAAW,MACjC,MAAM,QAAQ,SAAS,MAAM;AAEjC,CAAC,EACA,KAAK,GAAG,EACR,KAAK;A;;;;;AClDV,IAAe,oBAAA;EACb,OAAO;EACP,OAAO;EACP,QAAQ;EACR,SAAS;EACT,MAAM;EACN,QAAQ;EACR,aAAa;EACb,eAAe;EACf,gBAAgB;AAClB;;;ACcA,IAAM,OAAO;EACX,CACE;IACE,QAAQ;IACR,OAAO;IACP,cAAc;IACd;IACA,YAAY;IACZ;IACA;IACA,GAAG;EAAA,GAEL,QACG;AACI,WAAA;MACL;MACA;QACE;QACA,GAAG;QACH,OAAO;QACP,QAAQ;QACR,QAAQ;QACR,aAAa,sBAAuB,OAAO,WAAW,IAAI,KAAM,OAAO,IAAI,IAAI;QAC/E,WAAW,aAAa,UAAU,SAAS;QAC3C,GAAG;MACL;MACA;QACE,GAAG,SAAS,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,cAAc,KAAK,KAAK,CAAC;QAC3D,GAAI,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;MACpD;IAAA;EAEJ;AACF;;;AC7CM,IAAA,mBAAmB,CAAC,UAAkB,aAAuB;AACjE,QAAMG,aAAYC;IAAuC,CAAC,EAAE,WAAW,GAAG,MAAS,GAAA,QACjFC,eAAc,MAAM;MAClB;MACA;MACA,WAAW,aAAa,UAAU,YAAY,QAAQ,CAAC,IAAI,SAAS;MACpE,GAAG;IAAA,CACJ;EAAA;AAGO,EAAAF,WAAA,cAAc,GAAG,QAAQ;AAE5B,SAAAA;AACT;;;ACXM,IAAA,UAAU,iBAAiB,WAAW;EAC1C,CAAC,QAAQ,EAAE,OAAO,MAAM,QAAQ,KAAK,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,SAAA,CAAU;EAC7E,CAAC,QAAQ,EAAE,GAAG,4CAA4C,KAAK,SAAA,CAAU;EACzE,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;AAC3C,CAAC;;;ACJK,IAAA,UAAU,iBAAiB,WAAW;EAC1C,CAAC,QAAQ,EAAE,GAAG,iBAAiB,KAAK,SAAA,CAAU;EAC9C,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;AAC3C,CAAC;;;ACHK,IAAA,MAAM,iBAAiB,OAAO;EAClC,CAAC,QAAQ,EAAE,GAAG,aAAa,KAAK,SAAA,CAAU;EAC1C,CAAC,QAAQ,EAAE,OAAO,MAAM,QAAQ,MAAM,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,SAAA,CAAU;EAC9E,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAA,CAAU;EACxC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;EACzC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;EACzC,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAA,CAAU;AAC1C,CAAC;;;ACPK,IAAA,QAAQ,iBAAiB,SAAS;EACtC;IACE;IACA;MACE,GAAG;MACH,KAAK;IACP;EACF;EACA;IACE;IACA;MACE,GAAG;MACH,KAAK;IACP;EACF;EACA,CAAC,QAAQ,EAAE,GAAG,8CAA8C,KAAK,SAAA,CAAU;EAC3E,CAAC,QAAQ,EAAE,GAAG,oCAAoC,KAAK,SAAA,CAAU;EACjE,CAAC,QAAQ,EAAE,GAAG,oCAAoC,KAAK,SAAA,CAAU;EACjE,CAAC,QAAQ,EAAE,GAAG,qCAAqC,KAAK,SAAA,CAAU;EAClE,CAAC,QAAQ,EAAE,GAAG,mCAAmC,KAAK,SAAA,CAAU;EAChE,CAAC,QAAQ,EAAE,GAAG,8BAA8B,KAAK,SAAA,CAAU;EAC3D,CAAC,QAAQ,EAAE,GAAG,kCAAkC,KAAK,SAAA,CAAU;AACjE,CAAC;;;ACtBD,IAAM,QAAQ,iBAAiB,SAAS,CAAC,CAAC,QAAQ,EAAE,GAAG,mBAAmB,KAAK,SAAS,CAAC,CAAC,CAAC;;;ACArF,IAAA,cAAc,iBAAiB,eAAe;EAClD,CAAC,QAAQ,EAAE,GAAG,gBAAgB,KAAK,SAAA,CAAU;AAC/C,CAAC;;;ACFK,IAAA,eAAe,iBAAiB,gBAAgB;EACpD,CAAC,QAAQ,EAAE,GAAG,iBAAiB,KAAK,SAAA,CAAU;AAChD,CAAC;;;ACFK,IAAA,cAAc,iBAAiB,eAAe;EAClD,CAAC,UAAU,EAAE,IAAI,MAAM,IAAI,MAAM,GAAG,MAAM,KAAK,SAAA,CAAU;EACzD,CAAC,QAAQ,EAAE,IAAI,MAAM,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,KAAK,SAAA,CAAU;EACjE,CAAC,QAAQ,EAAE,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,IAAI,MAAM,KAAK,SAAA,CAAU;AACvE,CAAC;;;ACJK,IAAA,OAAO,iBAAiB,QAAQ;EACpC,CAAC,QAAQ,EAAE,OAAO,MAAM,QAAQ,MAAM,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,SAAA,CAAU;EACvF,CAAC,QAAQ,EAAE,GAAG,2DAA2D,KAAK,SAAA,CAAU;AAC1F,CAAC;;;ACHK,IAAA,WAAW,iBAAiB,YAAY;EAC5C,CAAC,QAAQ,EAAE,GAAG,6CAA6C,KAAK,SAAA,CAAU;EAC1E,CAAC,YAAY,EAAE,QAAQ,oBAAoB,KAAK,SAAA,CAAU;EAC1D,CAAC,QAAQ,EAAE,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI,KAAK,KAAK,SAAA,CAAU;AACnE,CAAC;;;ACJK,IAAA,YAAY,iBAAiB,aAAa;EAC9C,CAAC,QAAQ,EAAE,GAAG,mDAAmD,KAAK,SAAA,CAAU;EAChF,CAAC,QAAQ,EAAE,GAAG,2BAA2B,KAAK,SAAA,CAAU;EACxD,CAAC,QAAQ,EAAE,GAAG,iBAAiB,KAAK,SAAA,CAAU;EAC9C,CAAC,QAAQ,EAAE,GAAG,iBAAiB,KAAK,SAAA,CAAU;AAChD,CAAC;;;ACLK,IAAA,kBAAkB,iBAAiB,mBAAmB;EAC1D,CAAC,QAAQ,EAAE,GAAG,8DAA8D,KAAK,SAAA,CAAU;EAC3F,CAAC,QAAQ,EAAE,GAAG,2BAA2B,KAAK,SAAA,CAAU;EACxD,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAA,CAAU;EACxC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;EACzC,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAA,CAAU;EACxC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;AAC3C,CAAC;;;ACPK,IAAA,WAAW,iBAAiB,YAAY;EAC5C,CAAC,QAAQ,EAAE,GAAG,8DAA8D,KAAK,SAAA,CAAU;EAC3F,CAAC,QAAQ,EAAE,GAAG,2BAA2B,KAAK,SAAA,CAAU;EACxD,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAA,CAAU;EACxC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;EACzC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;AAC3C,CAAC;;;ACNK,IAAA,OAAO,iBAAiB,QAAQ;EACpC,CAAC,QAAQ,EAAE,GAAG,8DAA8D,KAAK,SAAA,CAAU;EAC3F,CAAC,QAAQ,EAAE,GAAG,2BAA2B,KAAK,SAAA,CAAU;AAC1D,CAAC;;;ACHK,IAAA,OAAO,iBAAiB,QAAQ;EACpC,CAAC,QAAQ,EAAE,OAAO,MAAM,QAAQ,MAAM,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,SAAA,CAAU;EAC9E,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAA,CAAU;EACxC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;EACzC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;EACzC,CAAC,QAAQ,EAAE,GAAG,aAAa,KAAK,SAAA,CAAU;EAC1C,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;EACzC,CAAC,QAAQ,EAAE,GAAG,aAAa,KAAK,SAAA,CAAU;EAC1C,CAAC,QAAQ,EAAE,GAAG,cAAc,KAAK,SAAA,CAAU;AAC7C,CAAC;;;ACTK,IAAA,QAAQ,iBAAiB,SAAS;EACtC,CAAC,QAAQ,EAAE,OAAO,MAAM,QAAQ,MAAM,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,SAAA,CAAU;EACvF,CAAC,UAAU,EAAE,IAAI,KAAK,IAAI,KAAK,GAAG,KAAK,KAAK,SAAA,CAAU;EACtD,CAAC,QAAQ,EAAE,GAAG,6CAA6C,KAAK,SAAA,CAAU;AAC5E,CAAC;;;ACJK,IAAA,SAAS,iBAAiB,UAAU;EACxC;IACE;IACA;MACE,GAAG;MACH,KAAK;IACP;EACF;EACA;IACE;IACA;MACE,GAAG;MACH,KAAK;IACP;EACF;EACA;IACE;IACA;MACE,GAAG;MACH,KAAK;IACP;EACF;AACF,CAAC;;;ACtBK,IAAA,YAAY,iBAAiB,aAAa;EAC9C;IACE;IACA;MACE,GAAG;MACH,KAAK;IACP;EACF;EACA,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAA,CAAU;EACxC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;AAC3C,CAAC;;;ACVK,IAAA,eAAe,iBAAiB,gBAAgB;EACpD,CAAC,QAAQ,EAAE,GAAG,+BAA+B,KAAK,SAAA,CAAU;AAC9D,CAAC;;;ACFK,IAAA,cAAc,iBAAiB,eAAe;EAClD,CAAC,UAAU,EAAE,IAAI,MAAM,IAAI,MAAM,GAAG,KAAK,KAAK,SAAA,CAAU;EACxD,CAAC,QAAQ,EAAE,GAAG,KAAK,GAAG,MAAM,OAAO,MAAM,QAAQ,MAAM,IAAI,KAAK,KAAK,SAAA,CAAU;EAC/E,CAAC,QAAQ,EAAE,GAAG,4BAA4B,KAAK,SAAA,CAAU;AAC3D,CAAC;;;ACJK,IAAA,oBAAoB,iBAAiB,qBAAqB;EAC9D,CAAC,QAAQ,EAAE,GAAG,iEAAiE,KAAK,SAAA,CAAU;EAC9F,CAAC,QAAQ,EAAE,GAAG,aAAa,KAAK,SAAA,CAAU;EAC1C,CAAC,QAAQ,EAAE,GAAG,cAAc,KAAK,SAAA,CAAU;EAC3C,CAAC,QAAQ,EAAE,GAAG,cAAc,KAAK,SAAA,CAAU;AAC7C,CAAC;;;ACLK,IAAA,gBAAgB,iBAAiB,iBAAiB;EACtD,CAAC,QAAQ,EAAE,GAAG,iEAAiE,KAAK,SAAA,CAAU;AAChG,CAAC;;;ACFK,IAAA,QAAQ,iBAAiB,SAAS;EACtC,CAAC,QAAQ,EAAE,GAAG,mBAAmB,KAAK,SAAA,CAAU;EAChD,CAAC,UAAU,EAAE,IAAI,KAAK,IAAI,MAAM,GAAG,KAAK,KAAK,SAAA,CAAU;EACvD,CAAC,UAAU,EAAE,IAAI,MAAM,IAAI,MAAM,GAAG,KAAK,KAAK,SAAA,CAAU;AAC1D,CAAC;;;ACJK,IAAA,OAAO,iBAAiB,QAAQ;EACpC,CAAC,WAAW,EAAE,QAAQ,sBAAsB,KAAK,SAAA,CAAU;AAC7D,CAAC;;;ACFK,IAAA,YAAY,iBAAiB,aAAa;EAC9C,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;EACzC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAA,CAAU;EACzC,CAAC,UAAU,EAAE,IAAI,MAAM,IAAI,MAAM,GAAG,KAAK,KAAK,SAAA,CAAU;EACxD,CAAC,UAAU,EAAE,IAAI,KAAK,IAAI,KAAK,GAAG,KAAK,KAAK,SAAA,CAAU;AACxD,CAAC;;;ACLK,IAAA,SAAS,iBAAiB,UAAU;EACxC,CAAC,QAAQ,EAAE,OAAO,MAAM,QAAQ,MAAM,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,KAAK,SAAA,CAAU;AAChF,CAAC;;;ACFK,IAAA,gBAAgB,iBAAiB,iBAAiB;EACtD;IACE;IACA;MACE,GAAG;MACH,KAAK;IACP;EACF;EACA,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAA,CAAU;EACxC,CAAC,QAAQ,EAAE,GAAG,cAAc,KAAK,SAAA,CAAU;AAC7C,CAAC;;;ACVK,IAAA,IAAI,iBAAiB,KAAK;EAC9B,CAAC,QAAQ,EAAE,GAAG,cAAc,KAAK,SAAA,CAAU;EAC3C,CAAC,QAAQ,EAAE,GAAG,cAAc,KAAK,SAAA,CAAU;AAC7C,CAAC;;;AhCbD,SAAS,aAAAG,YAAW,YAAAC,kBAAgB;;;AiCA7B,SAAS,MAAM,QAA8B;AAClD,SAAO,KAAK,MAAM;AACpB;AAGA,eAAsB,gBAAgB,MAAgC;AACpE,MAAI;AACF,UAAM,UAAU,UAAU,UAAU,IAAI;AACxC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACoBM,SACE,OAAAC,MADF;AA/BN,IAAM,gBAA+B,CAAC,GAAG,GAAG,CAAC;AActC,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AACF,GAAmB;AACjB,MAAI,CAAC,SAAU,QAAO;AAEtB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,UAAK,WAAU,8EACd;AAAA,0BAAAA,KAAC,QAAK,MAAM,IAAI;AAAA,UAAE;AAAA,WAEpB;AAAA,QACA,gBAAAA,KAAC,UAAK,WAAU,uCAAsC,oEAEtD;AAAA,QACA,qBAAC,SAAI,WAAU,mCACb;AAAA,0BAAAA,KAAC,SAAI,WAAU,uGACZ,wBAAc,IAAI,CAAC,WAClB;AAAA,YAAC;AAAA;AAAA,cAEC,MAAK;AAAA,cACL,SAAS,MAAM,cAAc,MAAM;AAAA,cACnC,gBAAc,UAAU;AAAA,cACxB,UAAU,CAAC;AAAA,cACX,WAAW;AAAA,gBACT;AAAA,gBACA,UAAU,SACN,mEACA;AAAA,gBACJ,CAAC,cAAc;AAAA,cACjB;AAAA,cAEC;AAAA;AAAA,gBAAO;AAAA;AAAA;AAAA,YAbH;AAAA,UAcP,CACD,GACH;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,UAAU,CAAC;AAAA,cACX,WAAW;AAAA,gBACT;AAAA,gBACA,CAAC,cAAc;AAAA,cACjB;AAAA,cACD;AAAA;AAAA,UAED;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACzDM,gBAAAC,MAIE,QAAAC,aAJF;AAVC,SAAS,qBAAqB,EAAE,UAAU,UAAU,GAA8B;AACvF,MAAI,CAAC,SAAU,QAAO;AAEtB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA;AAAA,wBAAAD,KAAC,SAAI,WAAU,6CAA4C,sFAE3D;AAAA,QACA,gBAAAC,MAAC,QAAG,WAAU,8DACZ;AAAA,0BAAAA,MAAC,SAAI,WAAU,cACb;AAAA,4BAAAD,KAAC,QAAG,WAAU,YAAW,sCAAI;AAAA,YAC7B,gBAAAA,KAAC,QAAG,WAAU,qDACX,mBAAS,mBAAmB,sBAC/B;AAAA,aACF;AAAA,UACA,gBAAAC,MAAC,SAAI,WAAU,cACb;AAAA,4BAAAD,KAAC,QAAG,WAAU,YAAW,sCAAI;AAAA,YAC7B,gBAAAA,KAAC,QAAG,WAAU,qDACX,mBAAS,iBAAiB,sBAC7B;AAAA,aACF;AAAA,WACF;AAAA,QACA,gBAAAC,MAAC,SAAI,WAAU,6BACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,SAAS,QAAQ,aAAa;AAAA,cAC7C,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,SAAS,QAAQ,iBAAiB;AAAA,cACjD,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACJM,SACE,OAAAE,MADF,QAAAC,aAAA;AA9BC,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,cAAc;AAAA,EACd;AACF,GAAU;AACR,QAAM,UAAU,MAAM,KAAK;AAC3B,QAAM,UAAU,QAAQ,SAAS,KAAK,CAAC;AAEvC,QAAM,aAAa,YAAY;AAC7B,QAAI,CAAC,QAAS;AACd,UAAM,WAAW,MAAM,OAAO,OAAO;AACrC,QAAI,CAAC,SAAU;AACf,kBAAc,EAAE;AAAA,EAClB;AAEA,QAAM,gBAAgB,CAAC,UAA8C;AAEnE,QAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,YAAY,CAAC,MAAM,YAAY,aAAa;AAC9E,YAAM,eAAe;AACrB,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAEA,SACE,gBAAAD,KAAC,SAAI,WAAW,GAAG,8DAA8D,SAAS,GACxF,0BAAAC,MAAC,SAAI,WAAU,oJACb;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,UAAU,CAAC,UAAU,cAAc,MAAM,OAAO,KAAK;AAAA,QACrD,WAAW;AAAA,QACX,SAAS,CAAC,UAAU;AAElB,gBAAM,KAAK,MAAM;AACjB,aAAG,MAAM,SAAS;AAClB,aAAG,MAAM,SAAS,GAAG,KAAK,IAAI,GAAG,cAAc,GAAG,CAAC;AAAA,QACrD;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA,cAAW;AAAA,QACX,WAAU;AAAA;AAAA,IACZ;AAAA,IACC,cACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,cAAY,aAAa,6BAAS;AAAA,QAClC,OAAO,aAAa,6BAAS;AAAA,QAC7B,WAAU;AAAA,QAET,uBACC,gBAAAA,KAAC,gBAAQ,MAAM,IAAI,WAAU,gBAAe,IAE5C,gBAAAA,KAAC,UAAO,MAAM,IAAI,MAAK,gBAAe;AAAA;AAAA,IAE1C,IAEA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,UAAU,CAAC;AAAA,QACX,cAAW;AAAA,QACX,OAAM;AAAA,QACN,WAAU;AAAA,QAEV,0BAAAA,KAAC,WAAQ,MAAM,IAAI;AAAA;AAAA,IACrB;AAAA,KAEJ,GACF;AAEJ;;;ACpEY,gBAAAE,MAKJ,QAAAC,aALI;AAnBL,SAAS,iBAAiB,EAAE,YAAY,UAAU,GAAU;AACjE,MAAI,eAAe,eAAe,eAAe,cAAc;AAC7D,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,eAAe;AAEpC,SACE,gBAAAD,KAAC,SAAI,WAAW,GAAG,2DAA2D,SAAS,GACrF,0BAAAC;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,eACI,uDACA;AAAA,MACN;AAAA,MAEA;AAAA,wBAAAD,KAAC,UAAK,WAAU,mBACb,yBACC,gBAAAA,KAAC,gBAAa,MAAM,IAAI,WAAU,gBAAe,IAEjD,gBAAAA,KAAC,iBAAc,MAAM,IAAI,GAE7B;AAAA,QACA,gBAAAC,MAAC,SAAI,WAAU,WACb;AAAA,0BAAAD,KAAC,SAAI,WAAU,uBACZ,yBAAe,uEAAgB,kCAClC;AAAA,UACA,gBAAAA,KAAC,SAAI,WAAU,sBAAqB,gIAAmB;AAAA,WACzD;AAAA;AAAA;AAAA,EACF,GACF;AAEJ;;;AC1CA,SAAS,+BAA+B;AAExC,SAAS,eAAAE,cAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,kBAAgC;;;ACClF,SAAS,eAAAC,cAAa,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAiB;AACxD,IAAM,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7B,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMT,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOX,MAAM;AACV;AACA,IAAM,4BAA4B;AAClC,IAAM,wBAAwB,MAAO;AACrC,IAAM,+BAA+B;AACrC,IAAI,YAAY;AAChB,WAAW,UAAU,iBAAiB,aAAa,MAAM;AACrD,cAAY;AAChB,CAAC;AACD,WAAW,UAAU,iBAAiB,WAAW,MAAM;AACnD,cAAY;AAChB,CAAC;AACD,WAAW,UAAU,iBAAiB,SAAS,MAAM;AACjD,cAAY;AAChB,CAAC;AACM,IAAM,mBAAmB,CAAC,UAAU,CAAC,MAAM;AAC9C,QAAM,CAAC,iBAAiB,qBAAqB,IAAIA,UAAS,KAAK;AAC/D,QAAM,CAAC,YAAY,gBAAgB,IAAIA,UAAS,QAAQ,YAAY,KAAK;AACzE,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAS,KAAK;AACtD,QAAM,aAAaD,QAAO,IAAI;AAC9B,aAAW,UAAU;AACrB,QAAM,cAAcF,aAAY,MAAM;AAClC,QAAI,CAAC,WAAW;AACZ,aAAO;AAAA,IACX;AACA,UAAM,YAAY,OAAO,aAAa;AACtC,QAAI,CAAC,aAAa,CAAC,UAAU,YAAY;AACrC,aAAO;AAAA,IACX;AACA,UAAM,QAAQ,UAAU,WAAW,CAAC;AACpC,WAAQ,MAAM,wBAAwB,SAAS,UAAU,OAAO,KAC5D,UAAU,SAAS,SAAS,MAAM,uBAAuB;AAAA,EACjE,GAAG,CAAC,CAAC;AACL,QAAM,gBAAgBA,aAAY,CAACI,gBAAe;AAC9C,UAAM,aAAaA;AACnB,qBAAiBA,WAAU;AAAA,EAC/B,GAAG,CAAC,CAAC;AACL,QAAM,qBAAqBJ,aAAY,CAACK,qBAAoB;AACxD,UAAM,kBAAkBA;AACxB,0BAAsBA,gBAAe;AAAA,EACzC,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQJ,SAAQ,MAAM;AACxB,QAAI;AACJ,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA,kBAAkB;AAAA,MAClB,aAAa;AAAA,MACb,UAAU;AAAA,MACV,WAAW,oBAAI,IAAI;AAAA,MACnB,IAAI,YAAY;AACZ,eAAO,UAAU,SAAS,aAAa;AAAA,MAC3C;AAAA,MACA,IAAI,UAAU,WAAW;AACrB,YAAI,UAAU,SAAS;AACnB,oBAAU,QAAQ,YAAY;AAC9B,gBAAM,oBAAoB,UAAU,QAAQ;AAAA,QAChD;AAAA,MACJ;AAAA,MACA,IAAI,kBAAkB;AAClB,YAAI,CAAC,UAAU,WAAW,CAAC,WAAW,SAAS;AAC3C,iBAAO;AAAA,QACX;AACA,eAAQ,UAAU,QAAQ,eAAe,IAAI,UAAU,QAAQ;AAAA,MACnE;AAAA,MACA,IAAI,4BAA4B;AAC5B,YAAI,CAAC,UAAU,WAAW,CAAC,WAAW,SAAS;AAC3C,iBAAO;AAAA,QACX;AACA,cAAM,EAAE,gBAAgB,IAAI;AAC5B,YAAI,CAAC,QAAQ,iBAAiB;AAC1B,iBAAO;AAAA,QACX;AACA,YAAI,iBAAiB,oBAAoB,iBAAiB;AACtD,iBAAO,gBAAgB;AAAA,QAC3B;AACA,cAAM,sBAAsB,KAAK,IAAI,KAAK,IAAI,QAAQ,gBAAgB,iBAAiB;AAAA,UACnF,eAAe,UAAU;AAAA,UACzB,gBAAgB,WAAW;AAAA,QAC/B,CAAC,GAAG,eAAe,GAAG,CAAC;AACvB,0BAAkB,EAAE,iBAAiB,oBAAoB;AACzD,8BAAsB,MAAM;AACxB,4BAAkB;AAAA,QACtB,CAAC;AACD,eAAO;AAAA,MACX;AAAA,MACA,IAAI,mBAAmB;AACnB,eAAO,KAAK,4BAA4B,KAAK;AAAA,MACjD;AAAA,MACA,IAAI,eAAe;AACf,eAAO,KAAK,oBAAoB;AAAA,MACpC;AAAA,IACJ;AAAA,EACJ,GAAG,CAAC,CAAC;AACL,QAAM,iBAAiBD,aAAY,CAAC,gBAAgB,CAAC,MAAM;AACvD,QAAI,OAAO,kBAAkB,UAAU;AACnC,sBAAgB,EAAE,WAAW,cAAc;AAAA,IAC/C;AACA,QAAI,CAAC,cAAc,wBAAwB;AACvC,oBAAc,IAAI;AAAA,IACtB;AACA,UAAM,cAAc,KAAK,IAAI,KAAK,OAAO,cAAc,IAAI,KAAK;AAChE,UAAM,WAAW,gBAAgB,WAAW,SAAS,cAAc,SAAS;AAC5E,UAAM,EAAE,gBAAgB,MAAM,IAAI;AAClC,QAAI;AACJ,QAAI,cAAc,MAAM;AACxB,QAAI,cAAc,oBAAoB,SAAS;AAC3C,oBAAc,SAAS,QAAQ,MAAM;AACjC,0BAAkB,KAAK,IAAI;AAAA,MAC/B,CAAC;AAAA,IACL,OACK;AACD,wBAAkB,eAAe,cAAc,YAAY;AAAA,IAC/D;AACA,UAAM,OAAO,YAAY;AACrB,YAAM,UAAU,IAAI,QAAQ,qBAAqB,EAAE,KAAK,MAAM;AAC1D,YAAI,CAAC,MAAM,YAAY;AACnB,gBAAM,YAAY;AAClB,iBAAO;AAAA,QACX;AACA,cAAM,EAAE,UAAU,IAAI;AACtB,cAAM,OAAO,YAAY,IAAI;AAC7B,cAAM,aAAa,QAAQ,MAAM,YAAY,SAAS;AACtD,cAAM,cAAc,MAAM,YAAY,EAAE,UAAU,SAAS,cAAc;AACzE,YAAI,MAAM,UAAU,aAAa,UAAU;AACvC,gBAAM,WAAW;AAAA,QACrB;AACA,YAAI,YAAY,GAAG;AACf,iBAAO,KAAK;AAAA,QAChB;AACA,YAAI,cAAc,KAAK,IAAI,GAAG;AAC1B,iBAAO,KAAK;AAAA,QAChB;AACA,YAAI,YAAY,KAAK,IAAI,aAAa,MAAM,yBAAyB,GAAG;AACpE,cAAI,MAAM,WAAW,aAAa,UAAU;AACxC,gBAAI,aAAa,WAAW;AACxB,oBAAM,YAAY,MAAM;AACxB,qBAAO,KAAK;AAAA,YAChB;AACA,kBAAM,YACD,SAAS,UAAU,MAAM,WACtB,SAAS,YAAY,MAAM,oBAC3B,SAAS;AACjB,kBAAM,eAAe,MAAM,WAAW;AACtC,kBAAM,aAAa,MAAM;AACzB,gBAAI,MAAM,cAAc,WAAW;AAC/B,oBAAM,cAAc;AAAA,YACxB;AAAA,UACJ;AACA,iBAAO,KAAK;AAAA,QAChB;AACA,YAAI,kBAAkB,KAAK,IAAI,GAAG;AAC9B,wBAAc,MAAM;AACpB,iBAAO,KAAK;AAAA,QAChB;AACA,cAAM,YAAY;AAMlB,YAAI,MAAM,YAAY,MAAM,2BAA2B;AACnD,iBAAO,eAAe;AAAA,YAClB,WAAW,gBAAgB,WAAW,SAAS,WAAW,QAAQ,MAAM;AAAA,YACxE;AAAA,YACA,UAAU,KAAK,IAAI,GAAG,kBAAkB,KAAK,IAAI,CAAC,KAAK;AAAA,UAC3D,CAAC;AAAA,QACL;AACA,eAAO,MAAM;AAAA,MACjB,CAAC;AACD,aAAO,QAAQ,KAAK,CAACI,gBAAe;AAChC,8BAAsB,MAAM;AACxB,cAAI,CAAC,MAAM,WAAW;AAClB,kBAAM,WAAW;AACjB,kBAAM,WAAW;AAAA,UACrB;AAAA,QACJ,CAAC;AACD,eAAOA;AAAA,MACX,CAAC;AAAA,IACL;AACA,QAAI,cAAc,SAAS,MAAM;AAC7B,YAAM,YAAY;AAAA,IACtB;AACA,QAAI,MAAM,WAAW,aAAa,UAAU;AACxC,aAAO,MAAM,UAAU;AAAA,IAC3B;AACA,WAAO,KAAK;AAAA,EAChB,GAAG,CAAC,eAAe,aAAa,KAAK,CAAC;AACtC,QAAM,aAAaJ,aAAY,MAAM;AACjC,uBAAmB,IAAI;AACvB,kBAAc,KAAK;AAAA,EACvB,GAAG,CAAC,oBAAoB,aAAa,CAAC;AACtC,QAAM,eAAeA,aAAY,CAAC,EAAE,OAAO,MAAM;AAC7C,QAAI,WAAW,UAAU,SAAS;AAC9B;AAAA,IACJ;AACA,UAAM,EAAE,WAAW,kBAAkB,IAAI;AACzC,QAAI,EAAE,gBAAgB,UAAU,IAAI;AACpC,UAAM,gBAAgB;AACtB,UAAM,oBAAoB;AAC1B,QAAI,qBAAqB,oBAAoB,WAAW;AAMpD,sBAAgB;AAAA,IACpB;AACA,oBAAgB,MAAM,YAAY;AAQlC,eAAW,MAAM;AAIb,UAAI,MAAM,oBAAoB,cAAc,mBAAmB;AAC3D;AAAA,MACJ;AACA,UAAI,YAAY,GAAG;AACf,2BAAmB,IAAI;AACvB,sBAAc,KAAK;AACnB;AAAA,MACJ;AACA,YAAM,kBAAkB,YAAY;AACpC,YAAM,gBAAgB,YAAY;AAClC,UAAI,MAAM,WAAW,eAAe;AAChC,cAAM,YAAY;AAClB;AAAA,MACJ;AACA,UAAI,eAAe;AACf,2BAAmB,IAAI;AACvB,sBAAc,KAAK;AAAA,MACvB;AACA,UAAI,iBAAiB;AACjB,2BAAmB,KAAK;AAAA,MAC5B;AACA,UAAI,CAAC,MAAM,mBAAmB,MAAM,cAAc;AAC9C,sBAAc,IAAI;AAAA,MACtB;AAAA,IACJ,GAAG,CAAC;AAAA,EACR,GAAG,CAAC,oBAAoB,eAAe,aAAa,KAAK,CAAC;AAC1D,QAAM,cAAcA,aAAY,CAAC,EAAE,QAAQ,OAAO,MAAM;AACpD,QAAI,UAAU;AACd,WAAO,CAAC,CAAC,UAAU,MAAM,EAAE,SAAS,iBAAiB,OAAO,EAAE,QAAQ,GAAG;AACrE,UAAI,CAAC,QAAQ,eAAe;AACxB;AAAA,MACJ;AACA,gBAAU,QAAQ;AAAA,IACtB;AAMA,QAAI,YAAY,UAAU,WACtB,SAAS,KACT,UAAU,QAAQ,eAAe,UAAU,QAAQ,gBACnD,CAAC,MAAM,WAAW,eAAe;AACjC,yBAAmB,IAAI;AACvB,oBAAc,KAAK;AAAA,IACvB;AAAA,EACJ,GAAG,CAAC,oBAAoB,eAAe,KAAK,CAAC;AAC7C,QAAM,YAAY,eAAe,CAAC,WAAW;AACzC,cAAU,SAAS,oBAAoB,UAAU,YAAY;AAC7D,cAAU,SAAS,oBAAoB,SAAS,WAAW;AAC3D,YAAQ,iBAAiB,UAAU,cAAc,EAAE,SAAS,KAAK,CAAC;AAClE,YAAQ,iBAAiB,SAAS,aAAa,EAAE,SAAS,KAAK,CAAC;AAAA,EACpE,GAAG,CAAC,CAAC;AACL,QAAM,aAAa,eAAe,CAAC,YAAY;AAC3C,UAAM,gBAAgB,WAAW;AACjC,QAAI,CAAC,SAAS;AACV;AAAA,IACJ;AACA,QAAI;AACJ,UAAM,iBAAiB,IAAI,eAAe,CAAC,CAAC,KAAK,MAAM;AACnD,YAAM,EAAE,OAAO,IAAI,MAAM;AACzB,YAAM,aAAa,UAAU,kBAAkB;AAC/C,YAAM,mBAAmB;AAKzB,UAAI,MAAM,YAAY,MAAM,iBAAiB;AACzC,cAAM,YAAY,MAAM;AAAA,MAC5B;AACA,sBAAgB,MAAM,YAAY;AAClC,UAAI,cAAc,GAAG;AAKjB,cAAM,YAAY,gBAAgB,WAAW,SAAS,iBAChD,WAAW,QAAQ,SACnB,WAAW,QAAQ,OAAO;AAChC,uBAAe;AAAA,UACX;AAAA,UACA,MAAM;AAAA,UACN,wBAAwB;AAAA,UACxB,UAAU,cAAc,YAAY,SAAY;AAAA,QACpD,CAAC;AAAA,MACL,OACK;AAMD,YAAI,MAAM,cAAc;AACpB,6BAAmB,KAAK;AACxB,wBAAc,IAAI;AAAA,QACtB;AAAA,MACJ;AACA,uBAAiB;AAQjB,4BAAsB,MAAM;AACxB,mBAAW,MAAM;AACb,cAAI,MAAM,qBAAqB,YAAY;AACvC,kBAAM,mBAAmB;AAAA,UAC7B;AAAA,QACJ,GAAG,CAAC;AAAA,MACR,CAAC;AAAA,IACL,CAAC;AACD,UAAM,gBAAgB,QAAQ,OAAO;AAAA,EACzC,GAAG,CAAC,CAAC;AACL,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,cAAc;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACJ;AACA,SAAS,eAAe,UAAU,MAAM;AAEpC,QAAM,SAASA,aAAY,CAAC,QAAQ;AAChC,WAAO,UAAU;AACjB,WAAO,SAAS,GAAG;AAAA,EACvB,GAAG,IAAI;AACP,SAAO;AACX;AACA,IAAM,iBAAiB,oBAAI,IAAI;AAC/B,SAAS,mBAAmB,YAAY;AACpC,QAAM,SAAS,EAAE,GAAG,yBAAyB;AAC7C,MAAI,UAAU;AACd,aAAW,aAAa,YAAY;AAChC,QAAI,cAAc,WAAW;AACzB,gBAAU;AACV;AAAA,IACJ;AACA,QAAI,OAAO,cAAc,UAAU;AAC/B;AAAA,IACJ;AACA,cAAU;AACV,WAAO,UAAU,UAAU,WAAW,OAAO;AAC7C,WAAO,YAAY,UAAU,aAAa,OAAO;AACjD,WAAO,OAAO,UAAU,QAAQ,OAAO;AAAA,EAC3C;AACA,QAAM,MAAM,KAAK,UAAU,MAAM;AACjC,MAAI,CAAC,eAAe,IAAI,GAAG,GAAG;AAC1B,mBAAe,IAAI,KAAK,OAAO,OAAO,MAAM,CAAC;AAAA,EACjD;AACA,SAAO,UAAU,YAAY,eAAe,IAAI,GAAG;AACvD;;;AC9YA,YAAY,WAAW;AACvB,SAAS,iBAAAM,gBAAe,cAAAC,aAAY,aAAAC,YAAW,qBAAqB,iBAAiB,WAAAC,UAAS,UAAAC,eAAe;AAE7G,IAAM,uBAAuBC,eAAc,IAAI;AAC/C,IAAM,4BAA4B,OAAO,WAAW,cAAc,kBAAkBC;AAC7E,SAAS,cAAc,EAAE,UAAU,UAAU,QAAQ,SAAS,MAAM,SAAS,WAAW,iBAAiB,wBAAwB,YAAY,GAAG,MAAM,GAAG;AAC5J,QAAM,wBAAwBC,QAAO,IAAI;AACzC,QAAM,kBAAwB,kBAAY,CAAC,QAAQ,aAAa;AAC5D,UAAM,MAAM,SAAS,mBAAmB;AACxC,WAAO,MAAM,QAAQ,QAAQ,KAAK;AAAA,EACtC,GAAG,CAAC,sBAAsB,CAAC;AAC3B,QAAM,kBAAkB,iBAAiB;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ,CAAC;AACD,QAAM,EAAE,WAAW,YAAY,gBAAgB,YAAY,YAAY,iBAAiB,MAAO,IAAI,YAAY;AAC/G,QAAM,UAAUC,SAAQ,OAAO;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI,kBAAkB;AAClB,aAAO,sBAAsB;AAAA,IACjC;AAAA,IACA,IAAI,gBAAgBC,kBAAiB;AACjC,4BAAsB,UAAUA;AAAA,IACpC;AAAA,EACJ,IAAI;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ,CAAC;AACD,sBAAoB,YAAY,MAAM,SAAS,CAAC,OAAO,CAAC;AACxD,4BAA0B,MAAM;AAC5B,QAAI,CAAC,UAAU,SAAS;AACpB;AAAA,IACJ;AACA,QAAI,iBAAiB,UAAU,OAAO,EAAE,aAAa,WAAW;AAC5D,gBAAU,QAAQ,MAAM,WAAW;AAAA,IACvC;AAAA,EACJ,GAAG,CAAC,CAAC;AACL,SAAc;AAAA,IAAc,qBAAqB;AAAA,IAAU,EAAE,OAAO,QAAQ;AAAA,IAClE,oBAAc,OAAO,EAAE,GAAG,MAAM,GAAG,OAAO,aAAa,aAAa,SAAS,OAAO,IAAI,QAAQ;AAAA,EAAC;AAC/G;AAAA,CACC,SAAUC,gBAAe;AACtB,WAAS,QAAQ,EAAE,UAAU,iBAAiB,GAAG,MAAM,GAAG;AACtD,UAAM,UAAU,wBAAwB;AACxC,WAAc;AAAA,MAAc;AAAA,MAAO,EAAE,KAAK,QAAQ,WAAW,OAAO;AAAA,QAC5D,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,iBAAiB;AAAA,MACrB,GAAG,WAAW,gBAAgB;AAAA,MACxB,oBAAc,OAAO,EAAE,GAAG,OAAO,KAAK,QAAQ,WAAW,GAAG,OAAO,aAAa,aAAa,SAAS,OAAO,IAAI,QAAQ;AAAA,IAAC;AAAA,EACxI;AACA,EAAAA,eAAc,UAAU;AAC5B,GAAG,kBAAkB,gBAAgB,CAAC,EAAE;AAIjC,SAAS,0BAA0B;AACtC,QAAM,UAAUC,YAAW,oBAAoB;AAC/C,MAAI,CAAC,SAAS;AACV,UAAM,IAAI,MAAM,qFAAqF;AAAA,EACzG;AACA,SAAO;AACX;;;AC/EA,SAAS,gBAAgB,+BAA+B;AAExD,SAAS,YAAAC,kBAAgB;;;ACDzB,SAAS,YAAAC,iBAAgB;;;ACCzB,IAAM,oBAA4C;AAAA,EAChD,OAAO;AAAA,EACP,mBAAmB;AAAA,EACnB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,MAAM;AAAA,EACN,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,OAAO;AACT;AAGA,IAAM,sBAA8C;AAAA,EAClD,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AAAA,EACX,UAAU;AAAA,EACV,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,cAAc;AAAA,EACd,mBAAmB;AACrB;AAIA,SAAS,cAAc,OAA2C;AAChE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,WAAO,KAAK,MAAM,KAAK;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,MAAsC,KAAqB;AACpF,QAAM,QAAQ,OAAO,GAAG;AACxB,SAAO,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI;AACpD;AAGO,SAAS,eAAe,MAAsB;AACnD,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,WAAW,QAAQ,MAAM,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,EAAE,IAAI,GAAG,KAAK,KAAK;AACzF,QAAM,aAAa,SAChB,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,kBAAkB,GAAG,EAC7B,QAAQ,YAAY,EAAE,EACtB,YAAY;AAEf,SAAO,kBAAkB,UAAU,KAAK;AAC1C;AAMO,SAAS,oBAAoB,UAAgC;AAClE,QAAM,aAAa,eAAe,SAAS,IAAI;AAC/C,QAAM,OAAO,cAAc,SAAS,SAAS;AAC7C,QAAM,cAAc,SAAS,cAAc,KAAK,KAAK;AACrD,QAAM,YAAY,eAAe,oBAAoB,UAAU,KAAK;AACpE,QAAM,kBAAkB,kBAAkB,MAAM,oBAAoB;AACpE,MAAI,iBAAiB;AACnB,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,kBAAkB,MAAM,aAAa;AAEzD,MAAI,eAAe,UAAU;AAC3B,WAAO,cAAc,iCAAQ,WAAW,KAAK;AAAA,EAC/C;AACA,MAAI,eAAe,aAAa;AAC9B,UAAM,YAAY,kBAAkB,MAAM,OAAO,KAAK,kBAAkB,MAAM,YAAY;AAC1F,WAAO,YAAY,GAAG,SAAS,SAAI,SAAS,WAAM;AAAA,EACpD;AACA,MAAI,eAAe,cAAc;AAC/B,UAAM,QAAQ,kBAAkB,MAAM,OAAO;AAC7C,WAAO,QAAQ,GAAG,SAAS,SAAI,KAAK,KAAK;AAAA,EAC3C;AAEA,SAAO,eAAe;AACxB;AAEO,SAAS,YAAY,QAA2C;AACrE,MAAI,WAAW,WAAW,WAAW,YAAa,QAAO;AACzD,MAAI,WAAW,kBAAmB,QAAO;AACzC,MAAI,WAAW,UAAW,QAAO;AACjC,SAAO;AACT;AAEO,SAAS,mBAAmB,QAAyC;AAC1E,MAAI,WAAW,UAAW,QAAO;AACjC,MAAI,WAAW,kBAAmB,QAAO;AACzC,MAAI,WAAW,QAAS,QAAO;AAC/B,MAAI,WAAW,YAAa,QAAO;AACnC,SAAO;AACT;AAEO,SAAS,mBAAmB,IAAoB;AACrD,MAAI,KAAK,IAAM,QAAO,GAAG,KAAK,MAAM,EAAE,CAAC;AACvC,QAAM,UAAU,KAAK;AACrB,MAAI,UAAU,GAAI,QAAO,GAAG,QAAQ,QAAQ,CAAC,CAAC;AAC9C,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,QAAM,mBAAmB,KAAK,MAAM,UAAU,EAAE;AAChD,SAAO,mBAAmB,IAAI,GAAG,OAAO,IAAI,gBAAgB,MAAM,GAAG,OAAO;AAC9E;AAGO,SAAS,eAAe,MAAsB;AACnD,MAAI;AACF,WAAO,KAAK,UAAU,KAAK,MAAM,IAAI,GAAG,MAAM,CAAC;AAAA,EACjD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,uBAAuB;AAGtB,SAAS,iBAAiB,QAAyB;AACxD,QAAM,OAAO,OAAO,WAAW,WAAW,SAAS,KAAK,UAAU,QAAQ,MAAM,CAAC;AACjF,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI,KAAK,UAAU,qBAAsB,QAAO;AAChD,SAAO,GAAG,KAAK,MAAM,GAAG,oBAAoB,CAAC;AAAA;AAC/C;;;ADnGU,gBAAAC,MAQA,QAAAC,aARA;AAvCV,SAAS,sBAAsB,eAA+B;AAC5D,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa;AACvC,WAAO,OAAO,QAAQ,gBAAgB,YAAY,OAAO,cACrD,OAAO,cACP;AAAA,EACN,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,eAAe,EAAE,SAAS,GAAU;AAClD,QAAM,CAAC,UAAU,WAAW,IAAIC,UAAS,KAAK;AAC9C,QAAM,cAAc,sBAAsB,SAAS,SAAS;AAC5D,QAAM,UAAU,SAAS,WAAW,aAAa,SAAS,WAAW;AACrE,QAAM,SAAS,SAAS,WAAW,WAAW,SAAS,WAAW;AAElE,SACE,gBAAAD,MAAC,SAAI,WAAU,sCACb;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,SACI,gDACA,UACE,sBACA;AAAA,QACR;AAAA,QAEA;AAAA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,YAAY,CAAC,QAAQ;AAAA,cACpC,WAAU;AAAA,cACV,iBAAe;AAAA,cAEf;AAAA,gCAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAM;AAAA,oBACN,WAAW;AAAA,sBACT;AAAA,sBACA,YAAY;AAAA,oBACd;AAAA;AAAA,gBACF;AAAA,gBACA,gBAAAA,KAAC,OAAI,MAAM,IAAI,WAAU,gDAA+C;AAAA,gBACxE,gBAAAC;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAW;AAAA,sBACT;AAAA,sBACA,SACI,wCACA,UACE,kBACA;AAAA,oBACR;AAAA,oBAEC;AAAA,gCACC,gBAAAD,KAAC,gBAAQ,MAAM,IAAI,WAAU,gBAAe,IAC1C,SACF,gBAAAA,KAAC,KAAE,MAAM,IAAI,IAEb,gBAAAA,KAAC,SAAM,MAAM,IAAI;AAAA,sBAEnB,gBAAAA,KAAC,UAAM,oBAAU,uBAAQ,SAAS,uBAAQ,gBAAK;AAAA;AAAA;AAAA,gBACjD;AAAA,gBACA,gBAAAC,MAAC,UAAK,WAAU,qEAAoE;AAAA;AAAA,kBAC5E;AAAA,mBACR;AAAA;AAAA;AAAA,UACF;AAAA,UACC,OAAO,SAAS,gBAAgB,YAAY,SAAS,cAAc,KAClE,gBAAAD,KAAC,UAAK,WAAU,sEACb,6BAAmB,SAAS,WAAW,GAC1C;AAAA;AAAA;AAAA,IAEJ;AAAA,IACC,YAAY,SAAS,UAAU,QAC9B,gBAAAC,MAAC,SAAI,WAAU,wDACb;AAAA,sBAAAD,KAAC,SAAI,WAAU,iFAAgF,0BAE/F;AAAA,MACA,gBAAAA,KAAC,SAAI,WAAU,6IACZ,2BAAiB,SAAS,MAAM,GACnC;AAAA,OACF;AAAA,KAEJ;AAEJ;;;AEjGA;AAAA,EAGE,aAAAG;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OACK;;;ACFA,IAAM,gBAAgB;AAe7B,IAAM,gBACJ;AAGF,IAAM,sBAAsB;AAG5B,SAAS,SAAS,MAAsB;AACtC,SAAO,KAAK,MAAM,OAAO,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK;AACtD;AAGA,SAAS,gBAAgB,MAA6B;AACpD,MAAI,CAAC,KAAK,WAAW,GAAG,EAAG,QAAO;AAClC,QAAM,iBAAiB,KAAK,QAAQ,GAAG;AAEvC,MAAI,iBAAiB,EAAG,QAAO;AAC/B,SAAO,GAAG,KAAK,MAAM,GAAG,cAAc,CAAC,GAAG,KAAK,MAAM,iBAAiB,CAAC,CAAC;AAC1E;AAMO,SAAS,gBAAgB,MAAuB;AACrD,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,CAAC,KAAK,MAAM,OAAO,EAAE,SAAS,IAAI;AAC3C;AAEA,SAAS,gBAAgB,OAAuB;AAC9C,SAAO,MACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM;AACzB;AAEA,SAAS,eAAe,MAAsB;AAC5C,SAAO,KAAK,QAAQ,eAAe,CAAC,OAAe,OAAe,YAAoB;AACpF,UAAM,OAAO,gBAAgB,OAAO;AACpC,QAAI,SAAS,KAAM,QAAO;AAC1B,UAAM,OAAO,SAAS,IAAI;AAC1B,QAAI,CAAC,gBAAgB,IAAI,GAAG;AAE1B,aAAO,KAAK,IAAI;AAAA,IAClB;AACA,WAAO,IAAI,aAAa,eAAe,gBAAgB,IAAI,CAAC,gBAAgB,gBAAgB,IAAI,CAAC,KAAK,gBAAgB,IAAI,CAAC,KAAK,aAAa;AAAA,EAC/I,CAAC;AACH;AAYO,SAAS,kBAAkB,MAAc,SAA2C;AACzF,MAAI,CAAC,KAAK,SAAS,QAAQ,EAAG,QAAO;AACrC,MAAI,CAAC,SAAS,UAAW,QAAO,eAAe,IAAI;AAEnD,QAAM,eAAe,KAAK,YAAY,IAAI;AAC1C,QAAM,OAAO,eAAe,IAAI,KAAK,KAAK,MAAM,GAAG,eAAe,CAAC;AACnE,QAAM,WAAW,eAAe,IAAI,OAAO,KAAK,MAAM,eAAe,CAAC;AAEtE,MAAI,oBAAoB,KAAK,QAAQ,GAAG;AACtC,WAAO,eAAe,IAAI;AAAA,EAC5B;AACA,SAAO,eAAe,IAAI,IAAI;AAChC;;;ACjFA,SAAqC,YAAAC,iBAAgB;AA2G/C,SAQmB,OAAAC,MARnB,QAAAC,aAAA;AA5FN,IAAM,aAAa,oBAAI,IAAI,CAAC,OAAO,OAAO,QAAQ,OAAO,OAAO,QAAQ,OAAO,KAAK,CAAC;AACrF,IAAM,aAAa,oBAAI,IAAI,CAAC,QAAQ,OAAO,QAAQ,QAAQ,KAAK,CAAC;AACjE,IAAM,YAAY,oBAAI,IAAI,CAAC,OAAO,MAAM,YAAY,OAAO,OAAO,QAAQ,KAAK,CAAC;AAChF,IAAM,YAAY,oBAAI,IAAI,CAAC,MAAM,OAAO,MAAM,OAAO,MAAM,MAAM,QAAQ,QAAQ,OAAO,QAAQ,KAAK,CAAC;AACtG,IAAM,aAAa,oBAAI,IAAI,CAAC,OAAO,QAAQ,OAAO,OAAO,KAAK,CAAC;AAC/D,IAAM,aAAa,oBAAI,IAAI,CAAC,OAAO,OAAO,OAAO,OAAO,MAAM,CAAC;AAC/D,IAAM,eAAe,oBAAI,IAAI,CAAC,OAAO,OAAO,MAAM,OAAO,OAAO,IAAI,CAAC;AAErE,SAAS,YAAY,MAAc;AACjC,QAAM,MAAM,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY,KAAK;AACpD,MAAI,WAAW,IAAI,GAAG,EAAG,QAAO;AAChC,MAAI,WAAW,IAAI,GAAG,EAAG,QAAO;AAChC,MAAI,UAAU,IAAI,GAAG,EAAG,QAAO;AAC/B,MAAI,UAAU,IAAI,GAAG,EAAG,QAAO;AAC/B,MAAI,WAAW,IAAI,GAAG,EAAG,QAAO;AAChC,MAAI,WAAW,IAAI,GAAG,EAAG,QAAO;AAChC,MAAI,aAAa,IAAI,GAAG,EAAG,QAAO;AAClC,SAAO;AACT;AAEA,SAAS,YAAY,OAAoC;AACvD,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAEA,SAAS,oBAAoB,UAA2B;AACtD,MAAI,OAAO,aAAa,SAAU,QAAO;AACzC,MAAI,MAAM,QAAQ,QAAQ,EAAG,QAAO,SAAS,IAAI,mBAAmB,EAAE,KAAK,EAAE;AAC7E,MAAI,YAAY,OAAO,aAAa,YAAY,WAAW,UAAU;AACnE,WAAO,oBAAqB,SAAgD,OAAO,QAAQ;AAAA,EAC7F;AACA,SAAO;AACT;AAQO,SAAS,SAAS;AAAA,EACvB;AAAA,EACA,aAAa;AAAA,EACb,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkB;AAChB,QAAM,SAAS,eAAe;AAC9B,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAS,KAAK;AACpD,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAS,KAAK;AAI1C,QAAM,iBACJ,QAAQ,OAAO,SAAS,YAAY,gBAAgB,OAC/C,KAAK,aACN;AACN,QAAM,OACJ,iBACA,YACA,YAAY,iBAAiB,WAAW,CAAC,KACzC,YAAY,gBAAgB,QAAQ,KACpC;AACF,QAAM,OACJ,iBACA,YACA,YAAY,iBAAiB,WAAW,CAAC,KACzC,YAAY,gBAAgB,QAAQ,KACpC,oBAAoB,QAAQ,KAC5B;AAEF,QAAM,WAAW,CAAC,aAAa,CAAC,gBAAgB,IAAI;AACpD,QAAMC,QAAO,YAAY,IAAI;AAE7B,QAAM,iBAAiB,YAAY;AACjC,QAAI,YAAY,eAAe,CAAC,UAAW;AAC3C,mBAAe,IAAI;AACnB,cAAU,KAAK;AACf,QAAI;AACF,YAAM,OAAO,SAAS,aAAa,WAAW,MAAM,IAAI;AAAA,IAC1D,QAAQ;AACN,gBAAU,IAAI;AAAA,IAChB,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,SACE,gBAAAF,MAAC,UAAM,GAAG,OAAO,WAAW,GAAG,wBAAwB,SAAS,GAC9D;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,UAAU,YAAY;AAAA,QACtB,cAAY,iCAAQ,IAAI;AAAA,QACxB,OAAO,SAAS,2DAAc,gBAAM,IAAI;AAAA,QAExC;AAAA,0BAAAD,KAAC,UAAK,WAAU,6BACb,wBAAc,gBAAAA,KAAC,gBAAQ,MAAM,IAAI,WAAU,gBAAe,IAAK,gBAAAA,KAACG,OAAA,EAAK,MAAM,IAAI,GAClF;AAAA,UACA,gBAAAH,KAAC,UAAK,WAAU,6BAA6B,gBAAK;AAAA,UAClD,gBAAAA,KAAC,YAAS,MAAM,IAAI,WAAU,+BAA8B;AAAA;AAAA;AAAA,IAC9D;AAAA,IACC,UAAU,gBAAAA,KAAC,UAAK,WAAU,8BAA6B,sCAAI;AAAA,KAC9D;AAEJ;;;AF3FQ,gBAAAI,MAGA,QAAAC,aAHA;AA3BR,IAAM,qBAAqB;AAK3B,SAAS,aAAa,EAAE,UAAU,MAAM,OAAO,GAAG,MAAM,GAAa;AACnE,QAAM,SAASC,QAAuB,IAAI;AAC1C,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAS,KAAK;AAC1C,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,EAAE;AAG3C,EAAAC,WAAU,MAAM;AACd,UAAM,SAAS,OAAO,SAAS,cAAc,MAAM;AACnD,gBAAY,QAAQ,UAAU,MAAM,gBAAgB,IAAI,CAAC,KAAK,EAAE;AAAA,EAClE,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,YAAY;AAC7B,UAAM,KAAK,MAAM,gBAAgB,OAAO,SAAS,cAAc,MAAM,GAAG,eAAe,EAAE;AACzF,QAAI,IAAI;AACN,gBAAU,IAAI;AACd,iBAAW,MAAM,UAAU,KAAK,GAAG,GAAI;AAAA,IACzC;AAAA,EACF;AAEA,SACE,gBAAAH,MAAC,SAAI,WAAU,qGACb;AAAA,oBAAAA,MAAC,SAAI,WAAU,wJACb;AAAA,sBAAAD,KAAC,UAAK,WAAU,6DACb,sBAAY,QACf;AAAA,MACA,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAW;AAAA,YACT;AAAA,YACA,SACI,+BACA;AAAA,UACN;AAAA,UAEC;AAAA,qBAAS,gBAAAD,KAAC,SAAM,MAAM,IAAI,IAAK,gBAAAA,KAAC,QAAK,MAAM,IAAI;AAAA,YAChD,gBAAAA,KAAC,UAAM,mBAAS,uBAAQ,gBAAK;AAAA;AAAA;AAAA,MAC/B;AAAA,OACF;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACJ,GAAG;AAAA,QACJ,WAAW,GAAG,MAAM,WAAW,+CAA+C;AAAA,QAE7E;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAKA,SAAS,eAAe,EAAE,MAAM,OAAO,UAAU,GAAG,MAAM,GAAgB;AACxE,SACE,gBAAAA,KAAC,OAAG,GAAG,OAAO,QAAO,UAAS,KAAI,uBAC/B,UACH;AAEJ;AAEA,IAAM,sBAAsB;AAAA,EAC1B,KAAK;AAAA,EACL,GAAG;AACL;AAUA,IAAM,sBAAsB;AAAA,EAC1B,CAAC,aAAa,GAAG,CAAC,YAAY,YAAY,aAAa,WAAW;AACpE;AAkBO,SAAS,gBAAgB,EAAE,UAAU,WAAW,MAAM,UAAU,GAAyB;AAC9F,QAAM,YAAY,SAAS;AAC3B,QAAM,mBAAmBK,SAAQ,MAAM;AACrC,UAAM,mBAAmB,SAAS,QAAQ,oBAAoB,EAAE;AAChE,QAAI,CAAC,UAAW,QAAO;AACvB,WAAO,kBAAkB,kBAAkB,EAAE,UAAU,CAAC;AAAA,EAC1D,GAAG,CAAC,UAAU,WAAW,SAAS,CAAC;AAEnC,QAAM,aAAaA;AAAA,IACjB,OACG;AAAA,MACC,GAAG;AAAA,MACH,CAAC,aAAa,GAAG,CAAC,kBAChB,gBAAAL,KAAC,YAAU,GAAG,eAAe,WAAsB;AAAA,IAEvD;AAAA,IACF,CAAC,SAAS;AAAA,EACZ;AAEA,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MAIC,WAAW,GAAG,mCAAmC,SAAS;AAAA,MAC1D,MAAM,QAAQ;AAAA,MACd;AAAA,MACA,aAAa;AAAA,MAEZ;AAAA;AAAA,IANI,aAAa;AAAA,EAOpB;AAEJ;;;AG1II,gBAAAM,YAAA;AAFG,SAAS,QAAQ,EAAE,WAAW,+BAAW,UAAU,GAAiB;AACzE,SACE,gBAAAA,KAAC,UAAK,WAAW,GAAG,0CAA0C,SAAS,GAAI,UAAS;AAExF;;;ACVA,SAAS,YAAAC,iBAAgC;;;ACAzC,SAAS,aAAAC,YAAW,WAAAC,UAAS,YAAAC,iBAAgB;AAwKrC,SAKA,OAAAC,OALA,QAAAC,aAAA;AArID,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAU;AAER,QAAM,CAAC,YAAY,aAAa,IAAIC,UAAmC,oBAAI,IAAI,CAAC;AAChF,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA8B,oBAAI,IAAI,CAAC;AAC7E,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAsB,oBAAI,IAAI,CAAC;AACrE,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAEhD,EAAAC,WAAU,MAAM;AACd,QAAI,kBAAkB,YAAY,kBAAkB,eAAe;AACjE,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,aAAa,CAAC;AAGlB,QAAM,qBAAqBC,SAAQ,MAAM;AACvC,QAAI,EAAE,YAAY,aAAa;AAC7B,aAAO,EAAE,YAAY,aAAa,YAAY;AAAA,IAChD;AAEA,UAAM,iBAAiB,oBAAI,IAAyB;AACpD,UAAM,kBAAkB,oBAAI,IAAoB;AAChD,UAAM,kBAAkB,oBAAI,IAAY;AAExC,eAAW,CAAC,aAAa,aAAa,KAAK,OAAO,QAAQ,WAAW,UAAU,GAAG;AAChF,qBAAe,IAAI,OAAO,WAAW,GAAG,IAAI,IAAI,aAAa,CAAC;AAAA,IAChE;AAEA,eAAW,CAAC,aAAa,IAAI,KAAK,OAAO,QAAQ,WAAW,MAAM,GAAG;AACnE,YAAM,OAAO,OAAO,WAAW;AAC/B,sBAAgB,IAAI,MAAM,IAAI;AAC9B,sBAAgB,IAAI,IAAI;AAAA,IAC1B;AAEA,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,EACF,GAAG,CAAC,YAAY,UAAU,aAAa,YAAY,WAAW,CAAC;AAE/D,QAAM,eAAe,CAAC,MAAc,QAAgB,UAAmB;AACrE,QAAI,YAAY,UAAW;AAC3B,kBAAc,CAAC,SAAS;AACtB,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,YAAM,UAAU,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,CAAC;AAC5C,UAAI,OAAO;AACT,YAAI,QAAQ,IAAI,MAAM,EAAG,SAAQ,OAAO,MAAM;AAAA,YACzC,SAAQ,IAAI,MAAM;AAAA,MACzB,OAAO;AACL,gBAAQ,MAAM;AACd,gBAAQ,IAAI,MAAM;AAAA,MACpB;AACA,WAAK,IAAI,MAAM,OAAO;AACtB,aAAO;AAAA,IACT,CAAC;AACD,mBAAe,CAAC,SAAS;AACvB,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,WAAK,OAAO,IAAI;AAChB,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,QAAM,oBAAoB,CAAC,SAAiB;AAC1C,QAAI,YAAY,UAAW;AAC3B,kBAAc,CAAC,SAAS;AACtB,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,WAAK,OAAO,IAAI;AAChB,aAAO;AAAA,IACT,CAAC;AACD,mBAAe,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC;AAAA,EAClD;AAEA,QAAM,gBAAgB,CAAC,MAAc,SAAiB;AACpD,QAAI,YAAY,UAAW;AAC3B,mBAAe,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,IAAI,MAAM,IAAI,CAAC;AAAA,EACxD;AAEA,QAAM,YAAY,CAAC,SAAgC;AACjD,UAAM,IAAI,KAAK,UAAU,IAAI;AAC7B,QAAI,YAAY,IAAI,IAAI,GAAG;AACzB,YAAM,QAAQ,YAAY,IAAI,IAAI,KAAK,IAAI,KAAK;AAChD,aAAO,QAAQ;AAAA,IACjB;AACA,UAAM,MAAM,WAAW,IAAI,IAAI;AAC/B,QAAI,CAAC,OAAO,IAAI,SAAS,EAAG,QAAO;AACnC,WAAO,CAAC,GAAG,GAAG,EACX,KAAK,EACL,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,KAAK,EAC7B,KAAK,IAAI;AAAA,EACd;AAEA,QAAM,cAAc,KAAK,UAAU,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,MAAM,IAAI;AAExE,QAAM,eAAe,MAAM;AACzB,QAAI,YAAY,aAAa,CAAC,eAAe,CAAC,SAAU;AAExD,UAAM,iBAAoC;AAAA,MACxC,YAAY,OAAO;AAAA,QACjB,MAAM,KAAK,WAAW,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,aAAa,MAAM;AAAA,UAC9D;AAAA,UACA,MAAM,KAAK,aAAa,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,QAChD,CAAC;AAAA,MACH;AAAA,MACA,QAAQ,OAAO;AAAA,QACb,MAAM,KAAK,WAAW,EACnB,IAAI,CAAC,SAAS,CAAC,OAAO,YAAY,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC,CAAU,EACnE,OAAO,CAAC,CAAC,EAAEC,KAAI,MAAMA,MAAK,SAAS,CAAC;AAAA,MACzC;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,UAAU,IAAI,CAAC,GAAG,MAAM,KAAK,EAAE,QAAQ,OAAO,UAAU,CAAC,CAAC,EAAE;AAC/E,UAAM,OAAO;AAAA,EAAuB,MAAM,KAAK,IAAI,CAAC;AACpD,iBAAa,IAAI;AACjB,aAAS,MAAM,YAAY,cAAc;AAAA,EAC3C;AAEA,SACE,gBAAAJ;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,WACI,mFACA;AAAA,MACN;AAAA,MAEC;AAAA,aAAK,aAAa,eACjB,gBAAAA,MAAC,SAAI,WAAU,gGAA+F;AAAA;AAAA,UACtG,KAAK,YAAY;AAAA,UAAY;AAAA,WACrC;AAAA,QAED,KAAK,UAAU,IAAI,CAAC,GAAG,SACtB,gBAAAD;AAAA,UAAC;AAAA;AAAA,YAEC,UAAU;AAAA,YACV;AAAA,YACA;AAAA,YACA,UAAU,mBAAmB,WAAW,IAAI,IAAI,KAAK,oBAAI,IAAI;AAAA,YAC7D,UAAU,mBAAmB,YAAY,IAAI,IAAI;AAAA,YACjD,YAAY,mBAAmB,YAAY,IAAI,IAAI,KAAK;AAAA,YACxD,UAAU;AAAA,YACV,eAAe;AAAA,YACf,gBAAgB;AAAA;AAAA,UATX,EAAE;AAAA,QAUT,CACD;AAAA,QAEA,CAAC,YAAY,CAAC,aAAa,YAC1B,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YACT,UAAU,CAAC;AAAA,YACX,WAAU;AAAA,YAET,wBAAc,iBAAO;AAAA;AAAA,QACxB;AAAA,QAGD,aAAa,CAAC,YACb,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,UAAQ;AAAA,YACR,WAAU;AAAA,YAEV;AAAA,8BAAAD,MAAC,gBAAQ,MAAM,IAAI,WAAU,gBAAe;AAAA,cAAE;AAAA;AAAA;AAAA,QAEhD;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAUG;AACD,QAAM,QAAQ,SAAS,eAAe;AAEtC,SACE,gBAAAC,MAAC,SACC;AAAA,oBAAAA,MAAC,SAAI,WAAW,GAAG,0BAA0B,WAAW,SAAS,MAAM,GACrE;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,WAAW,KAAK;AAAA,UACtB,WAAU;AAAA;AAAA,MACZ;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,WAAW,YAAY;AAAA,UACzB;AAAA,UAEA,0BAAAA,MAAC,mBAAgB,WAAU,oBAAoB,mBAAS,UAAS;AAAA;AAAA,MACnE;AAAA,OACF;AAAA,IAEA,gBAAAC,MAAC,SAAI,WAAW,GAAG,sBAAsB,WAAW,UAAU,SAAS,GACpE;AAAA,eAAS,QAAQ,IAAI,CAAC,KAAK,WAAW;AACrC,cAAM,QAAQ,SAAS,IAAI,MAAM;AACjC,eACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,UAAU;AAAA,YACV,SAAS,MAAM,SAAS,MAAM,QAAQ,KAAK;AAAA,YAC3C,WAAW;AAAA,cACT;AAAA,cACA,WAAW,kBAAkB;AAAA,cAC7B,SAAS,CAAC,WACN,oGACA,QACE,kGACA;AAAA,cACN,CAAC,YACC,CAAC,SACD;AAAA,cACF,YAAY;AAAA,YACd;AAAA,YAEC;AAAA,uBACC,gBAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAW;AAAA,oBACT;AAAA,oBACA,SAAS,CAAC,WACN,wFACA,QACE,oEACA;AAAA,kBACR;AAAA,kBAEC,mBACC,gBAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAM;AAAA,sBACN,WACE,WACI,+BACA;AAAA;AAAA,kBAER;AAAA;AAAA,cAEJ;AAAA,cAEF,gBAAAC,MAAC,SAAI,WAAU,WACb;AAAA,gCAAAD,MAAC,SAAI,WAAW,GAAG,eAAe,WAAW,YAAY,aAAa,GACnE,cAAI,OACP;AAAA,gBACC,IAAI,eACH,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAW;AAAA,sBACT;AAAA,sBACA,WAAW,gBAAgB;AAAA,sBAC3B,SAAS,CAAC,WAAW,eAAe;AAAA,oBACtC;AAAA,oBAEC,cAAI;AAAA;AAAA,gBACP;AAAA,iBAEJ;AAAA;AAAA;AAAA,UAxDK,IAAI;AAAA,QAyDX;AAAA,MAEJ,CAAC;AAAA,MAGA,YAAY,CAAC,WAAW,OACvB,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,WAAW,kBAAkB;AAAA,YAC7B,WACI,0DACA;AAAA,YACJ,YAAY;AAAA,UACd;AAAA,UAEA;AAAA,4BAAAD,MAAC,UAAK,WAAU,wDAAuD,gCAAG;AAAA,YAC1E,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,OAAO;AAAA,gBACP,UAAU;AAAA,gBACV,UAAU,CAAC,MAAM,eAAe,MAAM,EAAE,OAAO,KAAK;AAAA,gBACpD,SAAS,MAAM,cAAc,IAAI;AAAA,gBACjC,cAAW;AAAA,gBACX,aAAY;AAAA,gBACZ,WAAW;AAAA,kBACT;AAAA,kBACA,WAAW,YAAY;AAAA,gBACzB;AAAA;AAAA,YACF;AAAA;AAAA;AAAA,MACF;AAAA,OAEJ;AAAA,KACF;AAEJ;AAGO,SAAS,qBACd,YAC4B;AAC5B,MAAI,CAAC,WAAY,QAAO;AACxB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,UAAU;AACpC,QAAI,YAAY,QAAQ;AACxB,QAAI,OAAO,cAAc,UAAU;AACjC,UAAI;AACF,oBAAY,KAAK,MAAM,SAAS;AAAA,MAClC,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,SAAS,KAAK,UAAU,SAAS,GAAG;AACpD,YAAM,aAAa,UAAU,IAAI,qBAAqB;AACtD,UAAI,WAAW,MAAM,CAAC,aAAuC,aAAa,IAAI,GAAG;AAC/E,eAAO,EAAE,GAAG,QAAQ,WAAW,WAAW;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,OAAqC;AAClE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,OAAO;AAMb,MAAI,OAAO,KAAK,aAAa,YAAY,CAAC,MAAM,QAAQ,KAAK,OAAO,EAAG,QAAO;AAC9E,QAAM,UAAU,KAAK,QAAQ,IAAI,mBAAmB;AACpD,MAAI,CAAC,QAAQ,MAAM,CAAC,WAAiC,WAAW,IAAI,EAAG,QAAO;AAC9E,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf;AAAA,IACA,aAAa,KAAK,gBAAgB,QAAQ,KAAK,iBAAiB;AAAA,EAClE;AACF;AAEA,SAAS,oBAAoB,OAAmC;AAC9D,MAAI,OAAO,UAAU,SAAU,QAAO,EAAE,OAAO,OAAO,aAAa,GAAG;AACtE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,SAAS;AACf,MAAI,OAAO,OAAO,UAAU,SAAU,QAAO;AAC7C,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,aAAa,OAAO,OAAO,gBAAgB,WAAW,OAAO,cAAc;AAAA,EAC7E;AACF;;;AD3Ua,0BAAAM,OA6BL,QAAAC,aA7BK;AAxCN,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,iBAAiB,CAAC,iBAAiB,eAAe;AACxD,SAAO;AAAA,IACL;AAAA,IACA,UACE,iBACC,CAAC,mBACC,QAAQ,gBAAgB,KACvB,eAAe,UACf,eAAe,eACf,eAAe;AAAA,EACvB;AACF;AAMO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAU;AACR,QAAM,CAAC,UAAU,WAAW,IAAIC,UAAS,KAAK;AAC9C,QAAM,iBAAiB,eAAe,SAAS,IAAI;AAEnD,MAAI,UAAU;AACZ,UAAM,SAAS,SAAS,QAAQ;AAChC,QAAI,WAAW,QAAQ,WAAW,QAAW;AAC3C,aAAO,gBAAAF,MAAA,YAAG,kBAAO;AAAA,IACnB;AAAA,EACF;AAGA,MAAI,mBAAmB,mBAAmB;AACxC,UAAM,UACJ,qBAAqB,OAAO,SAAS,WAAW,WAAW,SAAS,SAAS,IAAI,KACjF,qBAAqB,oBAAoB,SAAS,SAAS,CAAC;AAC9D,UAAM,gBAAgB,wBAAwB;AAAA,MAC5C,YAAY,SAAS;AAAA,MACrB,eAAe,QAAQ,UAAU;AAAA,MACjC,kBAAkB;AAAA,IACpB,CAAC;AACD,UAAM,YAAY,cAAc,kBAAkB,QAAQ,QAAQ;AAClE,QAAI,SAAS;AACX,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,UAAU,cAAc;AAAA,UACxB,YAAY,SAAS;AAAA,UACrB,eAAe,iBAAiB;AAAA,UAChC;AAAA,UACA,UAAU,YAAY,WAAW;AAAA;AAAA,MACnC;AAAA,IAEJ;AACA,QAAI,SAAS,WAAW,WAAW;AACjC,aACE,gBAAAC,MAAC,SAAI,WAAU,6JACb;AAAA,wBAAAD,MAAC,gBAAQ,MAAM,IAAI,WAAU,gBAAe;AAAA,QAC5C,gBAAAA,MAAC,UAAK,wDAAO;AAAA,SACf;AAAA,IAEJ;AACA,WACE,gBAAAC,MAAC,SAAI,WAAU,iHACb;AAAA,sBAAAD,MAAC,SAAI,WAAU,iBAAgB,gFAAW;AAAA,MAC1C,gBAAAA,MAAC,SAAI,WAAU,8DAA6D,4IAE5E;AAAA,OACF;AAAA,EAEJ;AAEA,QAAM,OAAO,YAAY,SAAS,MAAM;AACxC,QAAM,cAAc,oBAAoB,QAAQ;AAChD,QAAM,YACJ,SAAS,QACL,gDACA,SAAS,UACP,uBACA,SAAS,SACP,sBACA;AAEV,QAAM,aACJ,SAAS,WAAW,YAClB,gBAAAA,MAAC,gBAAQ,MAAM,IAAI,WAAU,gBAAe,IAC1C,SAAS,WAAW,oBACtB,gBAAAA,MAAC,qBAAkB,MAAM,IAAI,IAC3B,SAAS,WAAW,eAAe,SAAS,WAAW,UACzD,gBAAAA,MAAC,KAAE,MAAM,IAAI,IAEb,gBAAAA,MAAC,SAAM,MAAM,IAAI;AAGrB,QAAM,kBACJ,SAAS,QACL,wCACA,SAAS,UACP,mBACA,SAAS,SACP,kBACA;AAEV,SACE,gBAAAC,MAAC,SAAI,WAAU,gCACb;AAAA,oBAAAA,MAAC,SAAI,WAAW,GAAG,oDAAoD,SAAS,GAC9E;AAAA,sBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,YAAY,CAAC,QAAQ;AAAA,UACpC,WAAU;AAAA,UACV,iBAAe;AAAA,UAEf;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM;AAAA,gBACN,WAAW;AAAA,kBACT;AAAA,kBACA,YAAY;AAAA,gBACd;AAAA;AAAA,YACF;AAAA,YACA,gBAAAC,MAAC,UAAK,WAAW,GAAG,gDAAgD,eAAe,GAChF;AAAA;AAAA,cACD,gBAAAD,MAAC,UAAM,6BAAmB,SAAS,MAAM,GAAE;AAAA,eAC7C;AAAA,YACA,gBAAAA,MAAC,UAAK,WAAU,qEACb,uBACH;AAAA;AAAA;AAAA,MACF;AAAA,MACC,OAAO,SAAS,gBAAgB,YAAY,SAAS,cAAc,KAClE,gBAAAA,MAAC,UAAK,WAAU,sEACb,6BAAmB,SAAS,WAAW,GAC1C;AAAA,OAEJ;AAAA,IACC,YACC,gBAAAC,MAAC,SAAI,WAAU,+EACb;AAAA,sBAAAD,MAAC,SAAI,WAAU,iFAAgF,0BAE/F;AAAA,MACA,gBAAAA,MAAC,SAAI,WAAU,4DACZ,0BACH;AAAA,MACA,gBAAAA,MAAC,SAAI,WAAU,iFAAgF,0BAE/F;AAAA,MACA,gBAAAA,MAAC,SAAI,WAAU,iIACZ,yBAAe,SAAS,SAAS,GACpC;AAAA,MACC,SAAS,UAAU,QAClB,gBAAAC,MAAA,YACE;AAAA,wBAAAD,MAAC,SAAI,WAAU,sFAAqF,0BAEpG;AAAA,QACA,gBAAAA,MAAC,SAAI,WAAU,6IACZ,2BAAiB,SAAS,MAAM,GACnC;AAAA,SACF;AAAA,OAEJ;AAAA,KAEJ;AAEJ;AAEA,SAAS,oBAAoB,eAAsC;AACjE,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa;AACvC,QACE,UACA,OAAO,WAAW,YAClB,MAAM,QAAS,OAAmC,SAAS,GAC3D;AACA,aAAO,KAAK,UAAU,EAAE,WAAY,OAAoC,UAAU,CAAC;AAAA,IACrF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;APhMQ,gBAAAG,OAEA,QAAAC,cAFA;AAXR,SAAS,cAAc,EAAE,WAAW,YAAY,GAAgD;AAC9F,QAAM,CAAC,MAAM,OAAO,IAAIC,WAAS,KAAK;AAEtC,SACE,gBAAAD,OAAC,SAAI,WAAU,oCACb;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,QAAQ,CAAC,IAAI;AAAA,QAC5B,iBAAe;AAAA,QACf,WAAU;AAAA,QAEV;AAAA,0BAAAD,MAAC,SAAM,MAAM,IAAI,WAAU,YAAW;AAAA,UACrC,cAAc,gBAAAA,MAAC,WAAQ,WAAU,WAAU,sCAAI,IAAa,gBAAAA,MAAC,UAAK,sCAAI;AAAA,UACvE,gBAAAC,OAAC,UAAK,WAAU,0CAAyC;AAAA;AAAA,YACpD,IAAI,KAAK,aAAa,OAAO,EAAE,OAAO,UAAU,MAAM;AAAA,YAAE;AAAA,aAC7D;AAAA,UACA,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAM;AAAA,cACN,WAAW,GAAG,iCAAiC,QAAQ,YAAY;AAAA;AAAA,UACrE;AAAA;AAAA;AAAA,IACF;AAAA,IACC,QACC,gBAAAA,MAAC,SAAI,WAAU,oIACZ,qBACH;AAAA,KAEJ;AAEJ;AAEA,SAAS,eAAe,SAA8B;AACpD,SAAO,eAAe,wBAAwB,QAAQ,OAAO,CAAC,EAAE,KAAK;AACvE;AAEA,SAAS,gCAAgC,UAA0C;AACjF,WAAS,QAAQ,SAAS,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;AAC5D,QAAI,SAAS,KAAK,EAAE,UAAW,QAAO;AAAA,EACxC;AACA,SAAO;AACT;AAMO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAU;AACR,QAAM,iBAAiB,SAAS,KAAK,CAAC,YAAY,QAAQ,WAAW,aAAa;AAClF,QAAM,gBAAgB,SAAS;AAAA,IAC7B,CAAC,YACC,eAAe,OAAO,KAAK,QAAQ,cAAc,QAAQ,YAAY,UAAU,KAAK;AAAA,EACxF;AACA,QAAM,uBAAuB,cACzB,gCAAgC,QAAQ,IACxC;AAEJ,SACE,gBAAAC,OAAC,SAAI,WAAU,iDACZ;AAAA,sBACC,gBAAAD,MAAC,SAAI,WAAU,qJAAoJ,gCAEnK;AAAA,IAED,SAAS,IAAI,CAAC,SAAS,UAAU;AAChC,YAAM,SAAS,UAAU,SAAS,SAAS;AAC3C,YAAM,gBAAgB,eAAe;AACrC,YAAM,OAAO,eAAe,OAAO;AACnC,YAAM,YAAY,QAAQ,cAAc,CAAC;AACzC,YAAM,gBACJ,CAAC,CAAC,QAAQ,cAAc,CAAC,eAAe,UAAU;AAEpD,aACE,gBAAAC;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UAET;AAAA,6BAAiB,QAAQ,aACxB,gBAAAD,MAAC,iBAAc,WAAW,QAAQ,WAAW,aAAa,iBAAiB,CAAC,MAAM;AAAA,YAEnF,QACC,gBAAAA,MAAC,SAAI,WAAU,iFACb,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,gBAAgB,cAAc;AAAA,gBACpC,WAAU;AAAA,gBACV;AAAA,gBAEC;AAAA;AAAA,YACH,GACF;AAAA,YAED,UAAU,SAAS,KAClB,gBAAAA,MAAC,SAAI,WAAU,uBACZ,oBAAU;AAAA,cAAI,CAAC,aACd,eAAe,SAAS,IAAI,MAAM,UAChC,gBAAAA,MAAC,kBAAiC,YAAb,SAAS,EAAwB,IAEtD,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBAEC;AAAA,kBACA,YAAY,aAAa,SAAS,EAAE;AAAA,kBACpC;AAAA,kBACA,UAAU,kBAAkB;AAAA,kBAC5B;AAAA,kBACA,UAAU;AAAA;AAAA,gBANL,SAAS;AAAA,cAOhB;AAAA,YAEJ,GACF;AAAA;AAAA;AAAA,QAlCG,QAAQ,YAAY,GAAG,QAAQ,aAAa,WAAW,IAAI,KAAK;AAAA,MAoCvE;AAAA,IAEJ,CAAC;AAAA,IACA,eAAe,CAAC,iBAAiB,gBAAAA,MAAC,WAAQ,WAAU,QAAO,yCAAO;AAAA,KACrE;AAEJ;;;AShJA,SAAS,iBAAiD;AA0DhD,gBAAAG,OAEE,QAAAC,cAFF;AA5CV,SAAS,sBAAsB,gBAAuC;AACpE,QAAM,QAAQ,eAAe,MAAM,qBAAqB;AACxD,SAAO,QAAQ,CAAC,KAAK;AACvB;AAGO,IAAM,sBAAN,cAAkC,UAAwB;AAAA,EAC/D,QAAe;AAAA,IACb,gBAAgB;AAAA,IAChB,OAAO;AAAA,EACT;AAAA,EAEA,OAAO,yBAAyB,OAAqB;AACnD,WAAO;AAAA,MACL,gBAAgB;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,kBAAkB,OAAc,MAAiB;AAC/C,UAAM,iBAAiB,KAAK,kBAAkB;AAC9C,YAAQ,MAAM,GAAG,KAAK,MAAM,KAAK,kBAAkB,OAAO,IAAI;AAC9D,SAAK,SAAS,EAAE,eAAe,CAAC;AAAA,EAClC;AAAA,EAEA,mBAAmB,WAAkB;AACnC,QAAI,KAAK,MAAM,SAAS,UAAU,aAAa,KAAK,MAAM,UAAU;AAClE,WAAK,SAAS,EAAE,gBAAgB,IAAI,OAAO,KAAK,CAAC;AAAA,IACnD;AAAA,EACF;AAAA,EAEA,SAAS;AACP,UAAM,EAAE,UAAU,SAAS,MAAM,IAAI,KAAK;AAC1C,UAAM,EAAE,gBAAgB,MAAM,IAAI,KAAK;AAEvC,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,UAAM,gBAAgB,sBAAsB,cAAc;AAE1D,WACE,gBAAAD,MAAC,SAAI,WAAU,iHACb,0BAAAC,OAAC,SAAI,WAAU,0BACb;AAAA,sBAAAD,MAAC,iBAAc,WAAU,0CAAyC;AAAA,MAClE,gBAAAC,OAAC,SAAI,WAAU,kBACb;AAAA,wBAAAA,OAAC,SAAI,WAAU,eAAe;AAAA;AAAA,UAAM;AAAA,WAAI;AAAA,QACxC,gBAAAA,OAAC,SAAI,WAAU,wDACZ;AAAA,0BAAgB,qBAAM,aAAa,WAAM;AAAA,UACzC,MAAM,WAAW;AAAA,WACpB;AAAA,QACC,UACC,gBAAAD,MAAC,SAAI,WAAU,2CAA2C,mBAAQ,IAChE;AAAA,SACN;AAAA,OACF,GACF;AAAA,EAEJ;AACF;;;ACzEA,SAAS,cAAc,eAAe,kBAAAE,uBAAsB;AAmC9C,gBAAAC,OAYA,QAAAC,cAZA;AA3BP,SAAS,cAAc,SAAkD;AAC9E,SAAO,QAAQ,SAAS;AAC1B;AAEO,SAAS,eAAe,SAAmD;AAChF,SAAO,QAAQ,SAAS;AAC1B;AAOA,IAAM,YAAY,CAAC,YAAyB,QAAQ,WAAW;AAGxD,SAAS,kBAAkB,EAAE,SAAS,UAAU,GAAqB;AAC1E,QAAM,OAAOC,gBAAe,QAAQ,OAAO,EAAE,KAAK;AAClD,QAAM,YAAY,aAAa,QAAQ,OAAO;AAC9C,QAAM,aAAa,cAAc,QAAQ,OAAO;AAEhD,SACE,gBAAAF,MAAC,SAAI,WAAW,GAAG,wCAAwC,SAAS,GAClE,0BAAAC,OAAC,SAAI,WAAU,iEACZ;AAAA,eAAW,SAAS,KACnB,gBAAAD,MAAC,SAAI,WAAU,cACZ,qBAAW,IAAI,CAAC,SACf,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC,KAAK,KAAK,UAAU;AAAA,QACpB,KAAI;AAAA,QACJ,WAAU;AAAA;AAAA,MAHL,KAAK,UAAU;AAAA,IAItB,CACD,GACH;AAAA,IAED,UAAU,SAAS,KAClB,gBAAAA,MAAC,SAAI,WAAU,mCACZ,oBAAU,IAAI,CAAC,SACd,gBAAAC;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QAEV;AAAA,0BAAAD,MAAC,YAAS,MAAM,IAAI,WAAU,YAAW;AAAA,UACzC,gBAAAA,MAAC,UAAK,WAAU,qBAAoB,OAAO,KAAK,MAC7C,eAAK,MACR;AAAA;AAAA;AAAA,MANK,GAAG,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM;AAAA,IAOvC,CACD,GACH;AAAA,IAED,QACC,gBAAAA,MAAC,SAAI,WAAU,wNACb,0BAAAA,MAAC,mBAAgB,WAAU,oBAAoB,gBAAK,GACtD;AAAA,IAED,QAAQ,UAAU,OAAO,KACxB,gBAAAC,OAAC,SAAI,WAAU,iGACb;AAAA,sBAAAD,MAAC,gBAAQ,MAAM,IAAI,WAAU,gBAAe,eAAY,QAAO;AAAA,MAC/D,gBAAAA,MAAC,UAAK,gCAAG;AAAA,OACX;AAAA,KAEJ,GACF;AAEJ;AAGO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AACF,GAGG;AACD,QAAM,OAAOE,gBAAe,QAAQ,OAAO;AAC3C,SACE,gBAAAF,MAAC,SAAI,WAAW,GAAG,4CAA4C,SAAS,GAEtE,0BAAAA,MAAC,SAAI,WAAU,iJACZ,gBACH,GACF;AAEJ;;;AbwFoB,SACE,OAAAG,OADF,QAAAC,cAAA;AA/JpB,SAAS,gBAAgB,SAA2D;AAClF,MAAI,QAAQ,SAAS,iBAAiB,OAAO,QAAQ,YAAY,UAAU;AACzE,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,QAAQ,OAAO;AACzC,QAAI,OAAO,OAAO,SAAS,YAAY,OAAO,OAAO,OAAO,UAAU;AACpE,aAAO,EAAE,MAAM,OAAO,MAAM,IAAI,OAAO,GAAG;AAAA,IAC5C;AAAA,EACF,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AAEA,SAAS,uBAAuB,SAA+C;AAC7E,QAAM,aAAa,gBAAgB,OAAO;AAC1C,MAAI,YAAY,OAAO,WAAY,QAAO;AAC1C,MAAI,YAAY,SAAS,WAAY,QAAO;AAC5C,SAAO;AACT;AAEA,SAAS,yBAAyB,UAAiC;AACjE,SAAO,SACJ,IAAI,CAAC,YAAY;AAChB,UAAM,cACJ,OAAO,QAAQ,YAAY,WAAW,QAAQ,QAAQ,SAAS,QAAQ,QAAQ;AACjF,UAAM,gBACJ,QAAQ,YACJ,IAAI,CAAC,aAAa,GAAG,SAAS,EAAE,IAAI,SAAS,UAAU,EAAE,EAAE,EAC5D,KAAK,GAAG,KAAK;AAClB,WAAO;AAAA,MACL,QAAQ,YAAY,QAAQ,aAAa,QAAQ;AAAA,MACjD,QAAQ,UAAU;AAAA,MAClB;AAAA,MACA;AAAA,IACF,EAAE,KAAK,GAAG;AAAA,EACZ,CAAC,EACA,KAAK,GAAG;AACb;AAmBO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAU;AACR,QAAM,eAAeC,SAAQ,MAAM;AACjC,UAAM,UAAU,SAAS,OAAO,CAAC,YAAY;AAC3C,WAAK,QAAQ,aAAa,YAAY,OAAQ,QAAO;AACrD,UAAI,wBAAwB,OAAO,EAAG,QAAO;AAC7C,UAAI,QAAQ,SAAS,aAAc,QAAO;AAC1C,aAAO,QAAQ,SAAS,UAAU,uBAAuB,OAAO,MAAM;AAAA,IACxE,CAAC;AAED,UAAM,SAAwB,CAAC;AAC/B,QAAI,kBAAiC,CAAC;AACtC,QAAI,qBAAqB;AAEzB,UAAM,iBAAiB,MAAM;AAC3B,UAAI,gBAAgB,WAAW,EAAG;AAClC,4BAAsB;AACtB,YAAM,QAAQ,gBAAgB,CAAC;AAC/B,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,UAAU;AAAA,QACV,KAAK,MAAM,YAAY,aAAa,kBAAkB;AAAA,QACtD,aAAa,gBAAgB,KAAK,CAAC,YAAY,QAAQ,WAAW,WAAW;AAAA,MAC/E,CAAC;AACD,wBAAkB,CAAC;AAAA,IACrB;AAEA,eAAW,WAAW,SAAS;AAC7B,YAAM,cAAc,uBAAuB,OAAO;AAClD,UAAI,aAAa;AACf,uBAAe;AACf,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,MAAM;AAAA,UACN,KAAK,QAAQ,YAAY,GAAG,WAAW,IAAI,OAAO,MAAM;AAAA,QAC1D,CAAC;AACD;AAAA,MACF;AAEA,UAAI,QAAQ,SAAS,cAAc;AACjC,uBAAe;AACf,eAAO,KAAK,EAAE,MAAM,cAAc,KAAK,QAAQ,YAAY,cAAc,OAAO,MAAM,GAAG,CAAC;AAC1F;AAAA,MACF;AAEA,UAAI,QAAQ,SAAS,aAAa;AAChC,wBAAgB,KAAK,OAAO;AAC5B;AAAA,MACF;AAEA,qBAAe;AACf,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN;AAAA,QACA,KAAK,QAAQ,YAAY,GAAG,QAAQ,IAAI,IAAI,OAAO,MAAM;AAAA,MAC3D,CAAC;AAAA,IACH;AAEA,mBAAe;AAGf,UAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,QAAI,aAAa;AACf,UAAI,MAAM,SAAS,kBAAkB;AACnC,aAAK,cAAc;AAAA,MACrB,WAAW,MAAM,SAAS,aAAa,cAAc,KAAK,OAAO,GAAG;AAClE,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,UAAU;AAAA,YACR;AAAA,cACE,MAAM;AAAA,cACN,SAAS;AAAA,cACT,UAAU,qBAAqB,KAAK,GAAG;AAAA,cACvC,QAAQ;AAAA,cACR,WAAW;AAAA,YACb;AAAA,UACF;AAAA,UACA,KAAK,qBAAqB,KAAK,GAAG;AAAA,UAClC,aAAa;AAAA,QACf,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,WAAW,CAAC;AAE1B,SACE,gBAAAF,MAAC,SAAI,WAAW,GAAG,+CAA+C,SAAS,GACzE,0BAAAC,OAAC,iBAAc,WAAU,4BAA2B,SAAQ,WAAU,QAAO,WAC3E;AAAA,oBAAAD,MAAC,cAAc,SAAd,EAAsB,WAAU,8BAC/B,0BAAAA,MAAC,SAAI,WAAU,qDACb,0BAAAC,OAAC,SAAI,WAAU,yBACZ;AAAA,mBAAa,WAAW,IACpB,cACC,gBAAAA,OAAC,SAAI,WAAU,oBACb;AAAA,wBAAAD,MAAC,iBAAc,MAAM,IAAI,aAAa,KAAK;AAAA,QAC3C,gBAAAA,MAAC,UAAK,WAAU,yBAAwB,sCAAI;AAAA,QAC5C,gBAAAA,MAAC,UAAK,WAAU,sBAAqB,gFAAW;AAAA,SAClD,IAEF,aAAa,IAAI,CAAC,UAAU;AAC1B,YAAI,MAAM,SAAS,WAAW;AAC5B,iBACE,gBAAAA,MAAC,SAAoB,iBAAe,MAAM,QAAQ,UAC/C,wBAAc,MAAM,OAAO,IAC1B,gBAAAA,MAAC,qBAAkB,SAAS,MAAM,SAAS,IACzC,eAAe,MAAM,OAAO,IAC9B,gBAAAA,MAAC,qBAAkB,SAAS,MAAM,SAAS,IACzC,QALI,MAAM,GAMhB;AAAA,QAEJ;AAEA,YAAI,MAAM,SAAS,kBAAkB;AACnC,iBACE,gBAAAA,MAAC,SAAoB,iBAAe,MAAM,SAAS,CAAC,GAAG,UACrD,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,SAAS,MAAM;AAAA,cACf,UAAU,yBAAyB,MAAM,QAAQ;AAAA,cAEjD,0BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,UAAU,MAAM;AAAA,kBAChB,aAAa,MAAM;AAAA,kBACnB;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA;AAAA,cACF;AAAA;AAAA,UACF,KAfQ,MAAM,GAgBhB;AAAA,QAEJ;AAEA,YAAI,MAAM,SAAS,cAAc;AAC/B,iBACE,gBAAAC;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cAEV;AAAA,gCAAAD,MAAC,UAAO,MAAM,IAAI;AAAA,gBAClB,gBAAAA,MAAC,UAAK,kDAAM;AAAA;AAAA;AAAA,YAJP,MAAM;AAAA,UAKb;AAAA,QAEJ;AAEA,eAAO,gBAAAA,MAAC,mBAAgC,MAAM,MAAM,QAAvB,MAAM,GAAuB;AAAA,MAC5D,CAAC;AAAA,MAEJ,kBAAkB,iBAAiB,CAAC,cACnC,gBAAAA,MAAC,SAAI,WAAU,QACb,0BAAAA,MAAC,SAAI,WAAU,0IAAyI,gCAExJ,GACF,IACE;AAAA,OACN,GACF,GACF;AAAA,IACA,gBAAAA,MAAC,wBAAqB,kBAAkB,SAAS,OAAO,CAAC,MAAM,cAAc,CAAC,CAAC,EAAE,QAAQ;AAAA,IACzF,gBAAAA,MAAC,wBAAqB;AAAA,KACxB,GACF;AAEJ;AAGA,SAAS,qBAAqB,EAAE,iBAAiB,GAAiC;AAChF,QAAM,EAAE,eAAe,IAAI,wBAAwB;AACnD,QAAM,mBAAmBG,QAAO,gBAAgB;AAChD,EAAAC,WAAU,MAAM;AACd,QAAI,mBAAmB,iBAAiB,SAAS;AAC/C,qBAAe,SAAS;AAAA,IAC1B;AACA,qBAAiB,UAAU;AAAA,EAC7B,GAAG,CAAC,kBAAkB,cAAc,CAAC;AACrC,SAAO;AACT;AAEA,SAAS,uBAAuB;AAC9B,QAAM,EAAE,YAAY,eAAe,IAAI,wBAAwB;AAC/D,QAAM,CAAC,SAAS,UAAU,IAAIC,WAAS,KAAK;AAC5C,QAAM,eAAeF,QAA6C,IAAI;AAItE,EAAAC,WAAU,MAAM;AACd,QAAI,YAAY;AAEd,UAAI,CAAC,aAAa,SAAS;AACzB,qBAAa,UAAU,WAAW,MAAM;AACtC,qBAAW,KAAK;AAChB,uBAAa,UAAU;AAAA,QACzB,GAAG,GAAG;AAAA,MACR;AAAA,IACF,OAAO;AAEL,UAAI,aAAa,SAAS;AACxB,qBAAa,aAAa,OAAO;AACjC,qBAAa,UAAU;AAAA,MACzB;AACA,iBAAW,IAAI;AAAA,IACjB;AAEA,WAAO,MAAM;AACX,UAAI,aAAa,SAAS;AACxB,qBAAa,aAAa,OAAO;AACjC,qBAAa,UAAU;AAAA,MACzB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,UAAU,CAAC;AAGf,QAAM,cAAcE,aAAY,MAAM;AAEpC,QAAI,aAAa,SAAS;AACxB,mBAAa,aAAa,OAAO;AACjC,mBAAa,UAAU;AAAA,IACzB;AACA,eAAW,KAAK;AAChB,mBAAe;AAAA,EACjB,GAAG,CAAC,cAAc,CAAC;AAEnB,MAAI,CAAC,QAAS,QAAO;AAErB,SACE,gBAAAL;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT,cAAW;AAAA,MACX,WAAU;AAAA,MAEV;AAAA,wBAAAD,MAAC,eAAY,MAAM,IAAI;AAAA,QAEvB,gBAAAA,MAAC,UAAK,WAAU,kCAAiC,4CAAK;AAAA;AAAA;AAAA,EACxD;AAEJ;AAEA,SAAS,gBAAgB,EAAE,KAAK,GAA+B;AAC7D,SACE,gBAAAC,OAAC,SAAI,WAAU,gCACb;AAAA,oBAAAD,MAAC,SAAI,WAAU,iFAAgF;AAAA,IAC/F,gBAAAC,OAAC,SAAI,WAAU,iIACb;AAAA,sBAAAD,MAAC,aAAU,MAAM,IAAI;AAAA,MACrB,gBAAAA,MAAC,UAAM,mBAAS,UAAU,yCAAW,4BAAO;AAAA,OAC9C;AAAA,IACA,gBAAAA,MAAC,SAAI,WAAU,iFAAgF;AAAA,KACjG;AAEJ;;;Ac7OM,gBAAAO,OAKE,QAAAC,cALF;AA5DC,SAAS,UAAU,OAA2D;AACnF,SAAO,UAAU,SAAS,SAAS;AACrC;AA4BO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,cAAY,UAAU,KAAK;AAAA,MAC3B,WAAW;AAAA,QACT;AAAA,QACA,YAAY;AAAA,MACd;AAAA,MAEA;AAAA,wBAAAD,MAAC,oBAAiB,YAAwB,WAAW,YAAY,QAAQ;AAAA,QACxE;AAAA,QAGA,gBACC,gBAAAC,OAAC,SAAI,WAAU,0EACb;AAAA,0BAAAD,MAAC,eAAY,MAAM,IAAI,WAAU,mBAAkB;AAAA,UACnD,gBAAAA,MAAC,UAAM,wBAAa;AAAA,WACtB;AAAA,QAED,OAAO;AAAA,QACR,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,kBAAkB,WAAW;AAAA,YAC7B,YAAY,OAAO;AAAA,YACnB,WAAW,YAAY;AAAA,YACvB;AAAA;AAAA,QACF;AAAA,QACC;AAAA,QACD,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,eAAe;AAAA,YACf;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,WAAW,YAAY;AAAA;AAAA,QACzB;AAAA,QACC,OAAO;AAAA;AAAA;AAAA,EACV;AAEJ;;;ApDzFM,SA8IE,YAAAE,WA7IA,OAAAC,OADF,QAAAC,cAAA;AAxBN,SAAS,oBAAoB,OAA8B;AACzD,SAAO,iBAAiB,iBAAiB,MAAM,WAAW;AAC5D;AAGA,SAAS,UAAU,EAAE,QAAQ,WAAW,GAAoD;AAC1F,QAAM,CAAC,WAAW,YAAY,IAAIC,WAAS,KAAK;AAChD,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAwB,IAAI;AAEhE,QAAM,cAAc,YAAY;AAC9B,iBAAa,IAAI;AACjB,kBAAc,IAAI;AAClB,QAAI;AACF,YAAM,OAAO,MAAM;AACnB,iBAAW;AAAA,IACb,SAAS,OAAO;AACd,oBAAc,iBAAiB,SAAS,MAAM,UAAU,MAAM,UAAU,kDAAU;AAAA,IACpF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SACE,gBAAAF,MAAC,SAAI,WAAU,gEACb,0BAAAC,OAAC,SAAI,WAAU,+IACb;AAAA,oBAAAD,MAAC,eAAY,MAAM,IAAI,WAAU,uCAAsC;AAAA,IACvE,gBAAAA,MAAC,SAAI,WAAU,uDAAsD,wDAAO;AAAA,IAC5E,gBAAAA,MAAC,SAAI,WAAU,+CAA8C,8JAE7D;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAU;AAAA,QAET,sBAAY,6BAAS;AAAA;AAAA,IACxB;AAAA,IACC,cAAc,gBAAAA,MAAC,SAAI,WAAU,0CAA0C,sBAAW;AAAA,KACrF,GACF;AAEJ;AAMO,SAAS,UAAU,OAAuB;AAC/C,QAAM,SAAS,eAAe;AAE9B,QAAM,CAAC,SAAS,UAAU,IAAIE,WAAS,CAAC;AACxC,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,MAAM,CAAC,OAAO,SAAS,CAAC;AAEnE,MAAI,WAAW;AACb,WACE,gBAAAF;AAAA,MAAC;AAAA;AAAA,QACC,cAAY,UAAU,MAAM,KAAK;AAAA,QACjC,WAAW,GAAG,2CAA2C,MAAM,YAAY,IAAI;AAAA,QAE/E,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,YAAY,MAAM;AAChB,2BAAa,KAAK;AAClB,yBAAW,CAAC,UAAU,QAAQ,CAAC;AAAA,YACjC;AAAA;AAAA,QACF;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SAAO,gBAAAA,MAAC,mBAA+B,GAAG,OAAO,gBAAgB,MAAM,aAAa,IAAI,KAA3D,OAA8D;AAC7F;AAEA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAoD;AAClD,QAAM,EAAE,SAAS,OAAO,MAAM,IAAI,gBAAgB,WAAW;AAAA,IAC3D;AAAA,IACA;AAAA,EACF,CAAC;AAGD,QAAM,SAAS,UAAU,OAAO;AAChC,QAAM,CAAC,eAAe,gBAAgB,IAAIE,WAAS,KAAK;AACxD,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,EAAE;AAG7C,EAAAC,WAAU,MAAM;AACd,QAAI,SAAS;AACX,uBAAiB,OAAO;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,SAAS,cAAc,CAAC;AAI5B,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,QAAS;AACd,UAAM,YAAY,QAAQ,GAAG,mBAAmB,CAAC,EAAE,OAAO,QAAQ,MAAM;AACtE,mBAAa,CAAC,SAAS,GAAG,OAAO,GAAG,IAAI;AAAA;AAAA,IAAS,EAAE,IAAI,KAAK;AAAA,EAAM,OAAO,EAAE;AAAA,IAC7E,CAAC;AACD,UAAM,YAAY,QAAQ,GAAG,uBAAuB,CAAC,EAAE,KAAK,MAAM;AAChE,mBAAa,CAAC,SAAS,OAAO,IAAI;AAAA,IACpC,CAAC;AACD,WAAO,MAAM;AACX,gBAAU;AACV,gBAAU;AAAA,IACZ;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAGZ,EAAAA,WAAU,MAAM;AACd,QAAI,oBAAoB,KAAK,GAAG;AAC9B,qBAAe;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,OAAO,cAAc,CAAC;AAG1B,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,SAAU;AAC3B,UAAM,eAAe,OAAO,QAAQ,QAAQ,EAAE;AAAA,MAAI,CAAC,CAAC,QAAQ,OAAO,MACjE,QAAQ,UAAU,QAAQ,CAAC,YAAY,QAAQ,OAAO,CAAC;AAAA,IACzD;AACA,WAAO,MAAM;AACX,iBAAW,eAAe,aAAc,aAAY;AAAA,IACtD;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,CAAC;AAEtB,QAAM,cAAc,OAAO,eAAe;AAC1C,QAAM,aAAa,iBAAiB;AACpC,QAAM,eACJ,SAAS,CAAC,oBAAoB,KAAK,IAC/B,MAAM,WAAW,iEACjB;AAEN,QAAM,eAAe,gBAAgB,OAAO,gBAAgB,OAAO,OAAO,WAAW;AAErF,QAAM,aAAa,CAAC,SAAmC;AACrD,qBAAiB,KAAK;AACtB,WAAO,SAAS,KAAK,MAAM,EAAE,MAAM,OAAO,QAAQ,OAAU,CAAC,KAAK,QAAQ,QAAQ,KAAK;AAAA,EACzF;AAEA,QAAM,aAAa,MAAM;AACvB,qBAAiB,IAAI;AACrB,SAAK,SAAS,KAAK;AAAA,EACrB;AAEA,SACE,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,OAAO,cAAc;AAAA,MACjC,QACE,gBAAAC,OAAAF,WAAA,EACE;AAAA,wBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,UAAU,OAAO;AAAA,YACjB,OAAO,OAAO;AAAA,YACd,YAAY,OAAO;AAAA,YACnB,eAAe,CAAC,SAAS,KAAK,OAAO,SAAS,IAAI;AAAA,YAClD,QAAQ,MAAM,KAAK,OAAO,iBAAiB;AAAA;AAAA,QAC7C;AAAA,QACA,gBAAAA,MAAC,wBAAqB,UAAU,OAAO,UAAU;AAAA,SACnD;AAAA,MAEF;AAAA,MACA,UAAU,OAAO,YAAY,CAAC;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe;AAAA,MACf,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,eAAe,OAAO,UAAU;AAAA,MAChC,YAAY,OAAO;AAAA,MACnB,UAAU,CAAC,QAAQ,YAAY,eAAe;AAC5C,aAAK,SAAS,KAAK,QAAQ;AAAA,UACzB,MAAM,OAAO,QAAQ;AAAA,UACrB,eAAe,EAAE,cAAc,YAAY,GAAG,WAAW;AAAA,QAC3D,CAAC;AAAA,MACH;AAAA,MAGA,WAAW,SAAS;AAAA;AAAA,EACtB;AAEJ;;;AqD9NA,SAAS,aAAAI,YAAW,WAAAC,UAAS,YAAAC,kBAAgB;;;ACC7C,SAAS,YAAAC,kBAAgB;AA6GnB,SAQE,OAAAC,OARF,QAAAC,cAAA;AApFN,IAAM,SAAiG;AAAA,EACrG,EAAE,IAAI,WAAW,OAAO,wCAAU,aAAa,qCAAiB;AAAA,EAChE,EAAE,IAAI,SAAS,OAAO,gBAAM,aAAa,2BAAO;AAAA,EAChD,EAAE,IAAI,UAAU,OAAO,gBAAM,aAAa,wCAAU,QAAQ,KAAK;AACnE;AAEO,SAAS,kBACd,OACwF;AACxF,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,UAAU,OAAO,CAAC,IAAI;AACrC,SAAO;AAAA,IACL,SAAS,OAAO,WAAW;AAAA,IAC3B,OAAO,OAAO,SAAS;AAAA,IACvB,QAAQ,OAAO,UAAU;AAAA,IACzB,SAAS,OAAO,WAAW;AAAA,EAC7B;AACF;AAEA,SAAS,cAAc,UAAqD,SAAyB;AACnG,QAAM,WAAW,OAAO,aAAa,WAAW,SAAS,aAAa;AACtE,SAAO,sBAAsB,YAAY,OAAO;AAClD;AAEO,SAAS,aACd,UACA,SACa;AACb,QAAM,aAAa,kBAAkB,QAAQ;AAC7C,MAAI,CAAC,cAAc,WAAW,YAAY,WAAW,OAAO,iBAAiB,YAAa,QAAO,CAAC;AAClG,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,cAAc,UAAU,OAAO,CAAC;AACjE,QAAI,CAAC,IAAK,QAAO,CAAC;AAClB,UAAM,SAAS,KAAK,MAAM,GAAG;AAG7B,WAAO,OAAO;AAAA,MACZ,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,WAAW,GAAwB,CAAC;AAAA,IAC/E;AAAA,EACF,QAAQ;AAEN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,cACP,UACA,SACA,UACM;AACN,QAAM,aAAa,kBAAkB,QAAQ;AAC7C,MAAI,CAAC,cAAc,WAAW,YAAY,WAAW,OAAO,iBAAiB,YAAa;AAC1F,MAAI;AACF,UAAM,MAAM,cAAc,UAAU,OAAO;AAC3C,QAAI,OAAO,KAAK,QAAQ,EAAE,WAAW,EAAG,cAAa,WAAW,GAAG;AAAA,QAC9D,cAAa,QAAQ,KAAK,KAAK,UAAU,QAAQ,CAAC;AAAA,EACzD,QAAQ;AAAA,EAER;AACF;AAcO,SAAS,uBAAuB,EAAE,UAAU,UAAU,UAAU,SAAS,GAAU;AACxF,QAAM,aAAa,kBAAkB,QAAQ;AAC7C,QAAM,CAAC,MAAM,OAAO,IAAIF,WAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAsB,QAAQ;AACxD,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,SAAS,OAAO,OAAO,CAAC,UAAU,WAAW,MAAM,EAAE,CAAC;AAC5D,QAAM,QAAQ,OAAO,KAAK,QAAQ,EAAE,SAAS;AAE7C,SACE,gBAAAE,OAAC,SAAI,WAAU,8EACb;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM;AACb,mBAAS,QAAQ;AACjB,kBAAQ,CAAC,UAAU,CAAC,KAAK;AAAA,QAC3B;AAAA,QACA,WAAU;AAAA,QAEV;AAAA,0BAAAD,MAAC,aAAU,MAAM,IAAI;AAAA,UAAE;AAAA,UAEtB,SAAS,gBAAAA,MAAC,UAAK,WAAU,8BAA6B,kDAAM;AAAA;AAAA;AAAA,IAC/D;AAAA,IACC,QACC,gBAAAC,OAAC,SAAI,WAAU,4BACZ;AAAA,aAAO,IAAI,CAAC,UACX,gBAAAA,OAAC,WAAqB,WAAU,uBAC9B;AAAA,wBAAAD,MAAC,UAAK,WAAU,uCAAuC,gBAAM,OAAM;AAAA,QACnE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM,MAAM,SAAS,aAAa;AAAA,YAClC,OAAO,MAAM,MAAM,EAAE,KAAK;AAAA,YAC1B,aACE,MAAM,OAAO,WACT,MAAM,cACL,SAAS,MAAM,EAAyB,KAAK,MAAM;AAAA,YAE1D,UAAU,CAAC,UAAU,SAAS,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,MAAM,OAAO,MAAM,CAAC;AAAA,YAC1E,WAAU;AAAA;AAAA,QACZ;AAAA,WAZU,MAAM,EAalB,CACD;AAAA,MACA,WAAW,UACV,gBAAAA,MAAC,OAAE,WAAU,uCAAsC,oKAEnD;AAAA,MAEF,gBAAAC,OAAC,SAAI,WAAU,cACb;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM;AAEb,oBAAM,OAAO,OAAO;AAAA,gBAClB,OAAO,QAAQ,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,OAAO,SAAS,EAAE,EAAE,KAAK,MAAM,EAAE;AAAA,cAC/E;AACA,uBAAS,IAAI;AACb,sBAAQ,KAAK;AAAA,YACf;AAAA,YACA,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM;AACb,uBAAS,CAAC,CAAC;AACX,uBAAS,CAAC,CAAC;AACX,sBAAQ,KAAK;AAAA,YACf;AAAA,YACA,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;;;AD7EU,gBAAAE,aAAA;AA7DH,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAiB;AAEf,QAAM,CAAC,UAAU,WAAW,IAAIC,WAAsB,MAAM,aAAa,UAAU,QAAQ,OAAO,CAAC;AAEnG,QAAM,YAAY,EAAE,GAAG,SAAS,GAAG,SAAS;AAC5C,QAAM,EAAE,UAAU,aAAa,OAAO,MAAM,MAAM,MAAM,IAAI,WAAW,SAAS;AAChF,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,EAAE;AAC7C,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAAS,KAAK;AAGxD,QAAM,SAASC;AAAA,IACb,OAAO;AAAA,MACL,YAAY,CAAC,SAAS,aAAa,CAAC,SAAU,OAAO,GAAG,IAAI;AAAA,EAAK,IAAI,KAAK,IAAK;AAAA,MAC/E;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,MAAM,KAAK;AAAA,EACd;AACA,EAAAC,WAAU,MAAM;AACd,cAAU,MAAM;AAAA,EAClB,GAAG,CAAC,QAAQ,OAAO,CAAC;AAEpB,SACE,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAEA,YAAW;AAAA,MACX,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA,YAAY,iBAAiB;AAAA,MAC7B;AAAA,MACA,eAAe;AAAA,MACf,QAAQ,OAAO,SAAS;AACtB,yBAAiB,KAAK;AACtB,YAAI,CAAC,KAAK,KAAK,KAAK,YAAa,QAAO;AAGxC,aAAK,KAAK,IAAI;AACd,eAAO;AAAA,MACT;AAAA,MACA,QAAQ,MAAM;AACZ,yBAAiB,IAAI;AACrB,aAAK;AAAA,MACP;AAAA,MACA,aACE,WACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,UAAU;AAAA,UACV,UAAU,EAAE,SAAS,QAAQ,SAAS,OAAO,QAAQ,MAAM;AAAA,UAC3D;AAAA,UACA,UAAU,CAAC,SAAS;AAClB,wBAAY,IAAI;AAChB,0BAAc,UAAU,QAAQ,SAAS,IAAI;AAC7C,+BAAmB,IAAI;AAAA,UACzB;AAAA;AAAA,MACF,IACE;AAAA;AAAA,EAER;AAEJ;;;AE3EM,gBAAAI,aAAA;AATC,SAAS,SAAS,OAAsB;AAG7C,QAAM,EAAE,MAAM,KAAK,YAAY,GAAG,KAAK,IAAI;AAC3C,MAAI,SAAS,OAAO;AAClB,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,8EAAqD;AAAA,IACvE;AACA,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,YAAY,KAAK;AAAA,QACjB,WAAW,KAAK;AAAA,QAChB,OAAO,KAAK;AAAA,QACZ,aAAa,KAAK;AAAA,QAClB,OAAO,KAAK;AAAA,QACZ,SAAS;AAAA;AAAA,IACX;AAAA,EAEJ;AACA,SAAO,gBAAAA,MAAC,aAAW,GAAG,MAAM;AAC9B;;;ACXA,cAAc;","names":["useEffect","useState","useState","useEffect","useCallback","useMemo","useRef","useState","Component","forwardRef","createElement","useEffect","useState","jsx","jsx","jsxs","jsx","jsxs","jsx","jsxs","useCallback","useEffect","useMemo","useRef","useState","useCallback","useMemo","useRef","useState","isAtBottom","escapedFromLock","createContext","useContext","useEffect","useMemo","useRef","createContext","useEffect","useRef","useMemo","targetScrollTop","StickToBottom","useContext","useState","useState","jsx","jsxs","useState","useEffect","useMemo","useRef","useState","useState","jsx","jsxs","useState","Icon","jsx","jsxs","useRef","useState","useEffect","useMemo","jsx","useState","useEffect","useMemo","useState","jsx","jsxs","useState","useEffect","useMemo","text","jsx","jsxs","useState","jsx","jsxs","useState","jsx","jsxs","getTextContent","jsx","jsxs","getTextContent","jsx","jsxs","useMemo","useRef","useEffect","useState","useCallback","jsx","jsxs","Fragment","jsx","jsxs","useState","useEffect","useEffect","useMemo","useState","useState","jsx","jsxs","jsx","useState","useMemo","useEffect","jsx"]}
|