@blade-hq/agent-react 2612.0.0-beta.18 → 2612.0.0-beta.19
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/dist/chat/ChatLoadingIndicator.d.ts +7 -0
- package/dist/{chunk-VVIXFBWF.js → chunk-ZAEHTW2O.js} +884 -868
- package/dist/chunk-ZAEHTW2O.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +5 -1
- package/dist/internal.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-VVIXFBWF.js.map +0 -1
package/dist/index.js
CHANGED
package/dist/internal.d.ts
CHANGED
|
@@ -59,3 +59,4 @@ export { BackgroundWakeNotice } from "./components/BackgroundWakeNotice";
|
|
|
59
59
|
export { TopicAssetSubmitDialog, useTopicAssetSubmissionAccess } from "./components/TopicAssetSubmitDialog";
|
|
60
60
|
export type { TopicAssetSubmitDialogProps } from "./components/TopicAssetSubmitDialog";
|
|
61
61
|
export { CodeBlockPre } from "./chat/markdown/MarkdownContent";
|
|
62
|
+
export { ChatLoadingIndicator, isPreparingReply } from "./chat/ChatLoadingIndicator";
|
package/dist/internal.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentChat,
|
|
3
3
|
BackgroundWakeNotice,
|
|
4
|
+
ChatLoadingIndicator,
|
|
4
5
|
ChatView,
|
|
5
6
|
CodeBlockPre,
|
|
6
7
|
ContextCard,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
ToolUiCardView,
|
|
20
21
|
TopicAssetSubmitDialog,
|
|
21
22
|
WhatIfUserBubble,
|
|
23
|
+
isPreparingReply,
|
|
22
24
|
isQueuePanelVisible,
|
|
23
25
|
latestUserSpeechEntryId,
|
|
24
26
|
parseWhatIfPrompt,
|
|
@@ -30,7 +32,7 @@ import {
|
|
|
30
32
|
useTopicAssetSubmissionAccess,
|
|
31
33
|
useTurnNavigation,
|
|
32
34
|
useTypewriterReveal
|
|
33
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-ZAEHTW2O.js";
|
|
34
36
|
import {
|
|
35
37
|
ConnectorPanel,
|
|
36
38
|
PluginTriggerStack,
|
|
@@ -556,6 +558,7 @@ export {
|
|
|
556
558
|
AgentChat,
|
|
557
559
|
BackgroundWakeNotice,
|
|
558
560
|
BladeProvider,
|
|
561
|
+
ChatLoadingIndicator,
|
|
559
562
|
ChatView,
|
|
560
563
|
CodeBlockPre,
|
|
561
564
|
ConnectorPanel,
|
|
@@ -591,6 +594,7 @@ export {
|
|
|
591
594
|
filterConnectorItems,
|
|
592
595
|
getPlanUpdateDisplayState,
|
|
593
596
|
isPlanUpdateTool,
|
|
597
|
+
isPreparingReply,
|
|
594
598
|
isQueuePanelVisible,
|
|
595
599
|
latestUserSpeechEntryId,
|
|
596
600
|
mergeConnectorItems,
|
package/dist/internal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal.ts","../src/lib/mount-renderers.tsx","../src/components/SessionMemoryToggle.tsx","../src/components/SessionBranchPicker.tsx"],"sourcesContent":["// 第一方内部入口(`@blade-hq/agent-react/internal`):内置 Web、浏览器插件用的聊天内部块、\n// 判定逻辑与连接器组件。包含公开入口的全部内容;不进 public-api.md、README 不写,\n// 版本之间不做任何兼容保证。\nexport * from \"./index\"\n// 迁入的聊天组件、会话来源、聊天环境与宿主桥接(内置 Web、插件共用同一份)\nexport * from \"./chat\"\nexport { useBladeHost, useOptionalBladeClient } from \"./context\"\nexport { MountHostContext, MountSlot, mountRenderersToReact } from \"./lib/mount-renderers\"\n\nexport { useLlmChat } from \"./hooks/use-llm-chat\"\nexport { useMessagePin } from \"./hooks/use-message-pin\"\nexport type { UseMessagePinOptions } from \"./hooks/use-message-pin\"\nexport { useTypewriterReveal } from \"./hooks/use-typewriter-reveal\"\nexport type { UseTypewriterRevealResult } from \"./hooks/use-typewriter-reveal\"\nexport {\n latestUserSpeechEntryId,\n TURN_NAV_HIGHLIGHT_CLASS,\n turnNavItemsFromMessages,\n useTurnNavigation,\n} from \"./hooks/use-turn-navigation\"\nexport type {\n TurnNavigationOptions,\n TurnNavigationResult,\n TurnNavigationSource,\n} from \"./hooks/use-turn-navigation\"\nexport type { UseLlmChatResult } from \"./hooks/use-llm-chat\"\n\n// ============================== 连接器与插件 ==============================\nexport { ConnectorPanel } from \"./components/ConnectorPanel\"\nexport type { ConnectorPanelProps } from \"./components/ConnectorPanel\"\nexport { PluginConnectorList } from \"./components/PluginConnectorList\"\nexport type { ConfiguringPlugin, PluginConnectorListProps } from \"./components/PluginConnectorList\"\nexport { useSessionPluginActivation } from \"./components/use-session-plugin-activation\"\nexport type { UseSessionPluginActivationResult } from \"./components/use-session-plugin-activation\"\nexport {\n catalogToConnectorItems,\n filterConnectorItems,\n mergeConnectorItems,\n} from \"./components/plugin-connector\"\nexport type { PluginConnectorItem } from \"./components/plugin-connector\"\nexport { selectionFromItem, triggerSummary, TRIGGER_ICON_LIMIT } from \"./components/connector-panel-types\"\nexport type {\n PluginConnectorSelection,\n TriggerSummary,\n} from \"./components/connector-panel-types\"\nexport { SessionPluginSelector } from \"./components/SessionPluginSelector\"\nexport type { SessionPluginSelectorProps } from \"./components/SessionPluginSelector\"\nexport { SessionPluginIcon, sessionPluginLabel } from \"./components/SessionPluginIcon\"\nexport type { SessionPluginIconProps, SessionPluginLabelProps } from \"./components/SessionPluginIcon\"\nexport { PluginTriggerStack } from \"./components/PluginTriggerStack\"\nexport type { PluginTriggerStackProps } from \"./components/PluginTriggerStack\"\nexport { SessionPluginConfigDialog } from \"./components/SessionPluginConfigDialog\"\nexport type { SessionPluginConfigDialogProps } from \"./components/SessionPluginConfigDialog\"\nexport { SessionPluginConfigForm } from \"./components/SessionPluginConfigForm\"\nexport type {\n SessionPluginConfigDraft,\n SessionPluginConfigFormProps,\n} from \"./components/SessionPluginConfigForm\"\nexport { AgentChat } from \"./components/AgentChat\"\nexport type { AgentChatProps } from \"./components/AgentChat\"\nexport { LlmChat } from \"./components/LlmChat\"\nexport type { LlmChatProps } from \"./components/LlmChat\"\nexport type { LlmAdvancedSettings, LlmOverride } from \"./components/LlmAdvancedSettings\"\nexport { DEFAULT_ALLOWED_TAGS, MarkdownRenderer } from \"./components/markdown\"\nexport type {\n AllowedTags,\n CodeBlockOverrides,\n LinkSafetyConfig,\n MarkdownComponents,\n MarkdownRendererProps,\n RawHtmlPlugin,\n} from \"./components/markdown\"\nexport { WhatIfUserBubble } from \"./components/WhatIfUserBubble\"\nexport type { WhatIfUserBubbleProps } from \"./components/WhatIfUserBubble\"\nexport { parseWhatIfPrompt } from \"./lib/whatif-prompt\"\nexport type { ParsedWhatIfPrompt, WhatIfQuote } from \"./lib/whatif-prompt\"\nexport {\n getPlanUpdateDisplayState,\n isPlanUpdateTool,\n PLAN_AUTO_COLLAPSE_MS,\n parsePlanUpdate,\n pickCurrentPlanStep,\n PlanUpdateBlock,\n} from \"./components/PlanUpdateBlock\"\nexport type { PlanUpdateDisplayState } from \"./components/PlanUpdateBlock\"\nexport { ContextCard, ContextGroupCard } from \"./components/ContextCard\"\nexport type { ContextCardProps, ContextGroupCardProps } from \"./components/ContextCard\"\nexport { ToolUiCardView } from \"./components/ToolUiCardView\"\nexport { McpAppCard } from \"./components/McpAppCard\"\nexport type { McpAppMessage } from \"./components/McpAppCard\"\nexport { SessionMemoryToggle } from \"./components/SessionMemoryToggle\"\nexport type { SessionMemoryToggleProps } from \"./components/SessionMemoryToggle\"\nexport { SessionQueuePanel, isQueuePanelVisible } from \"./components/SessionQueuePanel\"\nexport type { SessionQueuePanelProps } from \"./components/SessionQueuePanel\"\n\nexport { SessionBranchPicker } from \"./components/SessionBranchPicker\"\nexport type { SessionBranchPickerProps } from \"./components/SessionBranchPicker\"\n\nexport { MessageImage, type MessageImageProps } from \"./components/MessageImage\"\n\nexport { BackgroundWakeNotice } from \"./components/BackgroundWakeNotice\"\n\nexport { TopicAssetSubmitDialog, useTopicAssetSubmissionAccess } from \"./components/TopicAssetSubmitDialog\"\nexport type { TopicAssetSubmitDialogProps } from \"./components/TopicAssetSubmitDialog\"\n\nexport { CodeBlockPre } from \"./chat/markdown/MarkdownContent\"\n","import type {\n BladeChatMountRenderers,\n MountHandle,\n MountRenderer,\n MountRendererObject,\n MountResult,\n} from \"@blade-hq/agent-client\"\nimport { createContext, useContext, useId, useLayoutEffect, useRef, type ReactNode } from \"react\"\nimport type { ChatViewRenderers } from \"../chat/customization\"\n\n/**\n * 挂载节点放在哪里。\n *\n * `<blade-chat>` 把它设成元素本身:挂载节点作为元素的 light DOM 子节点,经 Shadow DOM\n * 里的 `<slot>` 显示在区块位置,宿主页面的样式(包括 Vue 的 scoped 样式)直接生效。\n * 没有设置时(npm 包里),挂载节点就是组件内的一个 div。\n */\nexport const MountHostContext = createContext<HTMLElement | null>(null)\n\nfunction normalizeResult<T>(result: MountResult<T>): MountHandle<T> {\n if (typeof result === \"function\") return { destroy: result }\n if (result && typeof result === \"object\") return result\n return { destroy: () => {} }\n}\n\nfunction toObject<T>(renderer: MountRenderer<T>): MountRendererObject<T> {\n return typeof renderer === \"function\" ? { mount: renderer } : renderer\n}\n\n/** 两份数据的顶层字段逐个相同时不必通知宿主(渲染器每次都会新建数据对象)。 */\nfunction sameData(a: object, b: object): boolean {\n const aKeys = Object.keys(a)\n if (aKeys.length !== Object.keys(b).length) return false\n return aKeys.every((key) => Object.is((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]))\n}\n\ninterface MountSlotProps<T extends object> {\n renderer: MountRendererObject<T>\n data: T\n}\n\n/** 给宿主一个 DOM 节点挂自己的内容;数据变化时原地更新或清理后重挂,卸载时清理。 */\nexport function MountSlot<T extends object>({ renderer, data }: MountSlotProps<T>) {\n const host = useContext(MountHostContext)\n const slotName = `blade-mount-${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`\n const localRef = useRef<HTMLDivElement>(null)\n const dataRef = useRef(data)\n const mountedRef = useRef<{ target: HTMLElement; handle: MountHandle<T>; data: T } | null>(null)\n\n useLayoutEffect(() => {\n dataRef.current = data\n })\n\n // 与外部系统(宿主挂载的 DOM)同步:挂载一次,渲染器或挂载位置变了才重挂\n useLayoutEffect(() => {\n let target: HTMLElement\n if (host) {\n target = document.createElement(\"div\")\n target.slot = slotName\n host.appendChild(target)\n } else {\n if (!localRef.current) return\n target = localRef.current\n }\n const initial = dataRef.current\n mountedRef.current = { target, handle: normalizeResult(renderer.mount(target, initial)), data: initial }\n return () => {\n const mounted = mountedRef.current\n mountedRef.current = null\n mounted?.handle.destroy()\n if (host) target.remove()\n else target.replaceChildren()\n }\n }, [host, renderer, slotName])\n\n useLayoutEffect(() => {\n const mounted = mountedRef.current\n if (!mounted || sameData(mounted.data, data)) return\n mounted.data = data\n if (mounted.handle.update) {\n mounted.handle.update(data)\n return\n }\n mounted.handle.destroy()\n mounted.target.replaceChildren()\n mounted.handle = normalizeResult(renderer.mount(mounted.target, data))\n }, [data, renderer])\n\n return host ? <slot name={slotName} /> : <div ref={localRef} data-blade-mount=\"\" />\n}\n\nfunction slotFor<T extends object>(renderer: MountRendererObject<T>, data: T): ReactNode | null {\n if (renderer.match && !renderer.match(data)) return null\n return <MountSlot renderer={renderer} data={data} />\n}\n\n/**\n * 把挂载式渲染器转成普通 React 渲染器:两种写法走同一条渲染路径,`match` 返回\n * false 等于 React 渲染器返回 null(走默认渲染)。调用方要按 `renderers` 对象缓存结果,\n * 否则每次渲染都会得到新的渲染器、挂载内容被反复重建。\n */\nexport function mountRenderersToReact(\n renderers: BladeChatMountRenderers | null | undefined,\n context: { sessionId?: string } = {},\n): ChatViewRenderers | undefined {\n if (!renderers) return undefined\n const result: ChatViewRenderers = {}\n if (renderers.toolCall) {\n const renderer = toObject(renderers.toolCall)\n result.toolCall = (toolCall) => slotFor(renderer, { toolCall, sessionId: context.sessionId })\n }\n if (renderers.userMessage) {\n const renderer = toObject(renderers.userMessage)\n result.userMessage = ({ message }) => slotFor(renderer, { message })\n }\n if (renderers.assistantText) {\n const renderer = toObject(renderers.assistantText)\n result.assistantText = ({ message, streaming, compact, sessionId }) =>\n slotFor(renderer, { message, streaming, compact, sessionId })\n }\n return result\n}\n","import type { BladeClient } from \"@blade-hq/agent-client\"\nimport { useCallback, useEffect, useRef, useState, useSyncExternalStore } from \"react\"\nimport { useOptionalBladeClient } from \"../context\"\nimport { cn } from \"../lib/utils\"\n\ninterface SaveState {\n saving: boolean\n listeners: Set<() => void>\n}\n\nconst saveStates = new WeakMap<BladeClient, Map<string, SaveState>>()\n\nfunction getSaveState(client: BladeClient, sessionId: string): SaveState {\n let clientStates = saveStates.get(client)\n if (!clientStates) {\n clientStates = new Map()\n saveStates.set(client, clientStates)\n }\n let state = clientStates.get(sessionId)\n if (!state) {\n state = { saving: false, listeners: new Set() }\n clientStates.set(sessionId, state)\n }\n return state\n}\n\nfunction notify(state: SaveState) {\n for (const listener of state.listeners) listener()\n}\n\nfunction cleanupSaveState(client: BladeClient, sessionId: string, state: SaveState) {\n if (state.saving || state.listeners.size > 0) return\n const clientStates = saveStates.get(client)\n if (clientStates?.get(sessionId) === state) clientStates.delete(sessionId)\n}\n\nexport interface SessionMemoryToggleProps {\n sessionId: string\n enabled: boolean\n /** 不在 BladeProvider 内使用时显式传入,与宿主现有 BladeClient 复用连接和鉴权。 */\n client?: BladeClient\n disabled?: boolean\n label?: string\n className?: string\n labelClassName?: string\n inputClassName?: string\n onSaved?: (sessionId: string, enabled: boolean) => void\n onError?: (error: unknown) => void\n}\n\n/**\n * 当前会话记忆开关。同一 client + sessionId 的所有实例共享保存锁,避免多个聊天入口\n * 或会话切换时发出乱序更新;服务端成功后才通过 onSaved 交还权威值。\n */\nexport function SessionMemoryToggle({\n sessionId,\n enabled,\n client: clientProp,\n disabled = false,\n label = \"当前会话使用记忆\",\n className,\n labelClassName,\n inputClassName,\n onSaved,\n onError,\n}: SessionMemoryToggleProps) {\n const contextClient = useOptionalBladeClient()\n const client = clientProp ?? contextClient\n if (!client) {\n throw new Error(\"SessionMemoryToggle 必须在 <BladeProvider> 内使用或显式传入 client\")\n }\n\n const saveState = getSaveState(client, sessionId)\n const subscribe = useCallback(\n (listener: () => void) => {\n saveState.listeners.add(listener)\n return () => {\n saveState.listeners.delete(listener)\n cleanupSaveState(client, sessionId, saveState)\n }\n },\n [client, saveState, sessionId],\n )\n const getSaving = useCallback(() => saveState.saving, [saveState])\n const saving = useSyncExternalStore(\n subscribe,\n getSaving,\n getSaving,\n )\n const [draftEnabled, setDraftEnabled] = useState(enabled)\n const activeSessionIdRef = useRef(sessionId)\n activeSessionIdRef.current = sessionId\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: a new session must discard this component instance's optimistic value even when both sessions share the same enabled prop\n useEffect(() => {\n setDraftEnabled(enabled)\n }, [enabled, sessionId])\n\n const update = useCallback(\n (nextEnabled: boolean) => {\n const currentSaveState = getSaveState(client, sessionId)\n if (currentSaveState.saving) return\n currentSaveState.saving = true\n notify(currentSaveState)\n setDraftEnabled(nextEnabled)\n\n void client.sessions.updateSessionMemory(sessionId, nextEnabled).then(\n (updated) => {\n if (activeSessionIdRef.current === sessionId) {\n setDraftEnabled(updated.memory_enabled)\n }\n onSaved?.(sessionId, updated.memory_enabled)\n },\n (error) => {\n if (activeSessionIdRef.current === sessionId) setDraftEnabled(enabled)\n onError?.(error)\n },\n ).finally(() => {\n currentSaveState.saving = false\n notify(currentSaveState)\n cleanupSaveState(client, sessionId, currentSaveState)\n })\n },\n [client, enabled, onError, onSaved, sessionId],\n )\n\n return (\n <label className={cn(\"flex items-center justify-between\", className)}>\n <span className={labelClassName}>{label}</span>\n <input\n type=\"checkbox\"\n checked={draftEnabled}\n onChange={(event) => update(event.target.checked)}\n disabled={disabled || saving}\n className={inputClassName}\n />\n </label>\n )\n}\n","import type { BladeClient } from \"@blade-hq/agent-client\"\nimport * as Tooltip from \"@radix-ui/react-tooltip\"\nimport { useMemo, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\n\ntype CheckpointTree = Awaited<ReturnType<BladeClient[\"sessions\"][\"getSessionCheckpoints\"]>>\ntype CheckpointNode = CheckpointTree[\"nodes\"][number]\n\nexport interface SessionBranchPickerProps {\n tree: CheckpointTree\n /** Disable changes while this session is running or read-only. */\n disabled?: boolean\n /** Restore only the conversation; the host owns history refresh and composer drafts. */\n onCheckout: (checkpointId: string, position: \"before\" | \"turn_end\") => Promise<void>\n}\n\nconst ROW_HEIGHT = 32\nconst SVG_WIDTH = 44\n\ninterface LayoutNode {\n id: string\n column: number\n row: number\n parentRow: number | null\n parentColumn: number | null\n isActive: boolean\n isCurrent: boolean\n isVirtual: boolean\n preview: string\n content: string\n}\n\ninterface LayoutGraph {\n nodes: LayoutNode[]\n hasCycle: boolean\n maxColumn: number\n maxRow: number\n}\n\nexport function SessionBranchPicker({\n tree,\n disabled = false,\n onCheckout,\n}: SessionBranchPickerProps) {\n const [checkoutError, setCheckoutError] = useState<string | null>(null)\n const [pendingNodeId, setPendingNodeId] = useState<string | null>(null)\n const hasHistory = tree.nodes.length > 0\n const layout = useMemo(() => buildLayoutNodes(tree), [tree])\n const nodeMap = useMemo(() => new Map(tree.nodes.map((node) => [node.id, node])), [tree])\n const isBusy = disabled || pendingNodeId !== null\n const graphHeight = Math.max((layout.maxRow + 1) * ROW_HEIGHT, ROW_HEIGHT)\n const graphWidth = Math.max(SVG_WIDTH, getColumnX(layout.maxColumn) + 12)\n\n async function checkoutNode(node: CheckpointNode, position: \"before\" | \"turn_end\") {\n if (isBusy) return\n setCheckoutError(null)\n setPendingNodeId(node.id)\n try {\n await onCheckout(node.id, position)\n } catch (error) {\n setCheckoutError(error instanceof Error ? error.message : \"切换失败,请稍后重试\")\n } finally {\n setPendingNodeId(null)\n }\n }\n\n async function handleVirtualClick(node: LayoutNode) {\n if (isBusy || !node.isVirtual || node.isActive) return\n const sourceNode = nodeMap.get(node.content)\n if (sourceNode) await checkoutNode(sourceNode, \"turn_end\")\n }\n\n async function handleNodeSelect(node: LayoutNode) {\n if (isBusy) return\n const sourceNode = nodeMap.get(node.id)\n if (sourceNode) await checkoutNode(sourceNode, \"before\")\n }\n\n if (!hasHistory) return null\n\n return (\n <Tooltip.Provider delayDuration={200}>\n {checkoutError ? (\n <p role=\"alert\" className=\"mb-2 text-xs text-destructive\">\n {checkoutError}\n </p>\n ) : null}\n <div className=\"flex max-h-52 justify-center overflow-x-auto overflow-y-auto\">\n <div className=\"relative\" style={{ height: graphHeight, width: graphWidth }}>\n <svg\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute left-0 top-0 z-10 overflow-visible\"\n width={graphWidth}\n height={graphHeight}\n viewBox={`0 0 ${graphWidth} ${graphHeight}`}\n fill=\"none\"\n >\n {layout.nodes.map((node) => {\n if (node.parentRow == null || node.parentColumn == null) {\n return null\n }\n\n const childStroke = node.isActive\n ? \"hsl(var(--primary))\"\n : \"hsl(var(--muted-foreground))\"\n\n return (\n <path\n key={`path-${node.id}`}\n d={buildEdgePath(\n getColumnX(node.parentColumn),\n getRowY(node.parentRow),\n getColumnX(node.column),\n getRowY(node.row),\n )}\n stroke={childStroke}\n strokeOpacity={node.isActive ? 0.95 : 0.45}\n strokeWidth={node.isActive ? 1.8 : 1.4}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeDasharray={node.isVirtual ? \"3 3\" : undefined}\n />\n )\n })}\n </svg>\n\n <div className=\"relative z-0 h-full\">\n {layout.nodes.map((node) => {\n const isPendingNode = pendingNodeId === node.id\n const color = node.isActive ? \"hsl(var(--primary))\" : \"hsl(var(--muted-foreground))\"\n const previewText = node.isVirtual\n ? isPendingNode\n ? \"切换中...\"\n : \"\"\n : isPendingNode\n ? \"切换中...\"\n : node.preview\n const shouldShowTooltip = !node.isVirtual || Boolean(previewText)\n const button = (\n <button\n type=\"button\"\n onClick={() =>\n void (node.isVirtual ? handleVirtualClick(node) : handleNodeSelect(node))\n }\n disabled={isBusy}\n aria-current={!node.isVirtual && node.isCurrent ? \"step\" : undefined}\n aria-label={node.isVirtual ? \"切换到该分支\" : node.preview}\n className={cn(\n \"absolute z-20 m-0 h-8 w-6 overflow-visible border-0 bg-transparent p-0\",\n isBusy ? \"cursor-not-allowed opacity-60\" : \"cursor-pointer\",\n )}\n style={{\n top: node.row * ROW_HEIGHT,\n left: getColumnX(node.column) - 12,\n }}\n >\n <span aria-hidden=\"true\" className=\"absolute left-0 top-0 h-8 w-6\">\n <svg\n aria-hidden=\"true\"\n role=\"img\"\n className=\"pointer-events-none absolute left-0 top-0 h-full w-full overflow-visible\"\n width={24}\n height={ROW_HEIGHT}\n viewBox={`0 0 24 ${ROW_HEIGHT}`}\n fill=\"none\"\n >\n {node.isVirtual ? (\n <circle\n cx={12}\n cy={ROW_HEIGHT / 2}\n r={3.5}\n fill=\"none\"\n stroke={color}\n strokeWidth={1.2}\n strokeDasharray=\"2 2\"\n opacity={0.7}\n />\n ) : (\n <g>\n {node.isCurrent ? (\n <circle cx={12} cy={ROW_HEIGHT / 2} r={9} fill={color} opacity={0.18} />\n ) : null}\n <circle\n cx={12}\n cy={ROW_HEIGHT / 2}\n r={node.isCurrent ? 5.5 : 4}\n fill={color}\n />\n </g>\n )}\n </svg>\n </span>\n </button>\n )\n\n if (!shouldShowTooltip) {\n return <div key={node.id}>{button}</div>\n }\n\n return (\n <Tooltip.Root key={node.id}>\n <Tooltip.Trigger asChild>{button}</Tooltip.Trigger>\n <Tooltip.Portal>\n <Tooltip.Content\n side=\"right\"\n sideOffset={8}\n className={cn(\n // z-[60]:这张图会被放进回溯弹层(z-50 且背景不透明),\n // 消息预览不抬到弹层之上就会被它的背景吃掉。\n \"z-[60] rounded-md border border-border bg-popover px-2 py-1 text-xs text-foreground shadow-md\",\n !node.isVirtual && node.isCurrent && \"font-semibold\",\n )}\n >\n {previewText}\n </Tooltip.Content>\n </Tooltip.Portal>\n </Tooltip.Root>\n )\n })}\n </div>\n </div>\n </div>\n </Tooltip.Provider>\n )\n}\nfunction buildLayoutNodes(tree: CheckpointTree | undefined): LayoutGraph {\n if (!tree || tree.nodes.length === 0) {\n return { nodes: [], hasCycle: false, maxColumn: 0, maxRow: 0 }\n }\n\n const nodeMap = new Map(tree.nodes.map((node) => [node.id, node]))\n const order = new Map(tree.nodes.map((node, index) => [node.id, index]))\n const childrenMap = new Map<string | null, CheckpointNode[]>()\n\n for (const node of tree.nodes) {\n const parentId = node.parent_id && nodeMap.has(node.parent_id) ? node.parent_id : null\n const siblings = childrenMap.get(parentId) ?? []\n siblings.push(node)\n childrenMap.set(parentId, siblings)\n }\n\n const activeIds = new Set<string>()\n const activeVisited = new Set<string>()\n const leafId = tree.leaf_id\n let current = tree.leaf_id\n\n while (current && nodeMap.has(current) && !activeVisited.has(current)) {\n activeIds.add(current)\n activeVisited.add(current)\n current = nodeMap.get(current)?.parent_id ?? null\n }\n\n const layoutNodes: LayoutNode[] = []\n const placed = new Set<string>()\n const visiting = new Set<string>()\n let nextColumn = 1\n let maxColumn = 0\n let maxRow = 0\n let hasCycle = false\n\n const rootSort = (a: CheckpointNode, b: CheckpointNode) =>\n (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0)\n\n const childSort = (a: CheckpointNode, b: CheckpointNode) =>\n (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0)\n\n const allocateColumn = () => {\n const fresh = nextColumn\n nextColumn += 1\n return fresh\n }\n\n function walk(node: CheckpointNode, parent: LayoutNode | null, column: number, row: number) {\n if (visiting.has(node.id)) {\n hasCycle = true\n return\n }\n\n if (placed.has(node.id)) {\n return\n }\n\n visiting.add(node.id)\n maxColumn = Math.max(maxColumn, column)\n maxRow = Math.max(maxRow, row)\n\n const layoutNode: LayoutNode = {\n id: node.id,\n column,\n row,\n parentRow: parent?.row ?? null,\n parentColumn: parent?.column ?? null,\n isActive: activeIds.has(node.id),\n isCurrent: node.id === leafId,\n isVirtual: false,\n preview: formatPreview(node.content_preview),\n content: node.content,\n }\n\n layoutNodes.push(layoutNode)\n placed.add(node.id)\n\n const children = [...(childrenMap.get(node.id) ?? [])].sort(childSort)\n\n if (children.length === 0) {\n const virtualRow = row + 1\n maxRow = Math.max(maxRow, virtualRow)\n layoutNodes.push({\n id: `__virtual__${node.id}`,\n column,\n row: virtualRow,\n parentRow: layoutNode.row,\n parentColumn: layoutNode.column,\n isActive: activeIds.has(node.id),\n isCurrent: false,\n isVirtual: true,\n preview: \"\",\n content: node.id,\n })\n } else {\n for (const [index, child] of children.entries()) {\n if (visiting.has(child.id)) {\n hasCycle = true\n continue\n }\n\n const childColumn = index === 0 ? column : allocateColumn()\n walk(child, layoutNode, childColumn, row + 1)\n }\n }\n\n visiting.delete(node.id)\n }\n\n const roots = [...(childrenMap.get(null) ?? [])].sort(rootSort)\n for (const [index, root] of roots.entries()) {\n const rootColumn = index === 0 ? 0 : allocateColumn()\n walk(root, null, rootColumn, 0)\n }\n\n for (const node of [...tree.nodes].sort(rootSort)) {\n if (!placed.has(node.id)) {\n hasCycle = true\n const rootColumn = layoutNodes.length === 0 ? 0 : allocateColumn()\n walk(node, null, rootColumn, 0)\n }\n }\n\n return {\n nodes: layoutNodes,\n hasCycle,\n maxColumn,\n maxRow,\n }\n}\n\nfunction formatPreview(preview: string): string {\n const normalized = preview.replace(/\\s+/g, \" \").trim()\n return normalized.length > 0 ? normalized : \"(空消息)\"\n}\n\nfunction getColumnX(column: number): number {\n return 12 + column * 24\n}\n\nfunction getRowY(row: number): number {\n return row * ROW_HEIGHT + ROW_HEIGHT / 2\n}\n\nfunction buildEdgePath(fromX: number, fromY: number, toX: number, toY: number): string {\n if (fromX === toX || fromY === toY) {\n return `M ${fromX} ${fromY} L ${toX} ${toY}`\n }\n\n // Branch line: horizontal from parent to child column, then vertical down\n const r = Math.min(5, Math.abs(toX - fromX), Math.abs(toY - fromY))\n const dx = toX > fromX ? 1 : -1\n\n return [\n `M ${fromX} ${fromY}`,\n `H ${toX - dx * r}`,\n `A ${r} ${r} 0 0 ${dx > 0 ? 1 : 0} ${toX} ${fromY + r}`,\n `V ${toY}`,\n ].join(\" \")\n}\n"],"mappings":"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","names":["useRef","jsx","useRef","useState","jsx","jsxs","useState"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal.ts","../src/lib/mount-renderers.tsx","../src/components/SessionMemoryToggle.tsx","../src/components/SessionBranchPicker.tsx"],"sourcesContent":["// 第一方内部入口(`@blade-hq/agent-react/internal`):内置 Web、浏览器插件用的聊天内部块、\n// 判定逻辑与连接器组件。包含公开入口的全部内容;不进 public-api.md、README 不写,\n// 版本之间不做任何兼容保证。\nexport * from \"./index\"\n// 迁入的聊天组件、会话来源、聊天环境与宿主桥接(内置 Web、插件共用同一份)\nexport * from \"./chat\"\nexport { useBladeHost, useOptionalBladeClient } from \"./context\"\nexport { MountHostContext, MountSlot, mountRenderersToReact } from \"./lib/mount-renderers\"\n\nexport { useLlmChat } from \"./hooks/use-llm-chat\"\nexport { useMessagePin } from \"./hooks/use-message-pin\"\nexport type { UseMessagePinOptions } from \"./hooks/use-message-pin\"\nexport { useTypewriterReveal } from \"./hooks/use-typewriter-reveal\"\nexport type { UseTypewriterRevealResult } from \"./hooks/use-typewriter-reveal\"\nexport {\n latestUserSpeechEntryId,\n TURN_NAV_HIGHLIGHT_CLASS,\n turnNavItemsFromMessages,\n useTurnNavigation,\n} from \"./hooks/use-turn-navigation\"\nexport type {\n TurnNavigationOptions,\n TurnNavigationResult,\n TurnNavigationSource,\n} from \"./hooks/use-turn-navigation\"\nexport type { UseLlmChatResult } from \"./hooks/use-llm-chat\"\n\n// ============================== 连接器与插件 ==============================\nexport { ConnectorPanel } from \"./components/ConnectorPanel\"\nexport type { ConnectorPanelProps } from \"./components/ConnectorPanel\"\nexport { PluginConnectorList } from \"./components/PluginConnectorList\"\nexport type { ConfiguringPlugin, PluginConnectorListProps } from \"./components/PluginConnectorList\"\nexport { useSessionPluginActivation } from \"./components/use-session-plugin-activation\"\nexport type { UseSessionPluginActivationResult } from \"./components/use-session-plugin-activation\"\nexport {\n catalogToConnectorItems,\n filterConnectorItems,\n mergeConnectorItems,\n} from \"./components/plugin-connector\"\nexport type { PluginConnectorItem } from \"./components/plugin-connector\"\nexport { selectionFromItem, triggerSummary, TRIGGER_ICON_LIMIT } from \"./components/connector-panel-types\"\nexport type {\n PluginConnectorSelection,\n TriggerSummary,\n} from \"./components/connector-panel-types\"\nexport { SessionPluginSelector } from \"./components/SessionPluginSelector\"\nexport type { SessionPluginSelectorProps } from \"./components/SessionPluginSelector\"\nexport { SessionPluginIcon, sessionPluginLabel } from \"./components/SessionPluginIcon\"\nexport type { SessionPluginIconProps, SessionPluginLabelProps } from \"./components/SessionPluginIcon\"\nexport { PluginTriggerStack } from \"./components/PluginTriggerStack\"\nexport type { PluginTriggerStackProps } from \"./components/PluginTriggerStack\"\nexport { SessionPluginConfigDialog } from \"./components/SessionPluginConfigDialog\"\nexport type { SessionPluginConfigDialogProps } from \"./components/SessionPluginConfigDialog\"\nexport { SessionPluginConfigForm } from \"./components/SessionPluginConfigForm\"\nexport type {\n SessionPluginConfigDraft,\n SessionPluginConfigFormProps,\n} from \"./components/SessionPluginConfigForm\"\nexport { AgentChat } from \"./components/AgentChat\"\nexport type { AgentChatProps } from \"./components/AgentChat\"\nexport { LlmChat } from \"./components/LlmChat\"\nexport type { LlmChatProps } from \"./components/LlmChat\"\nexport type { LlmAdvancedSettings, LlmOverride } from \"./components/LlmAdvancedSettings\"\nexport { DEFAULT_ALLOWED_TAGS, MarkdownRenderer } from \"./components/markdown\"\nexport type {\n AllowedTags,\n CodeBlockOverrides,\n LinkSafetyConfig,\n MarkdownComponents,\n MarkdownRendererProps,\n RawHtmlPlugin,\n} from \"./components/markdown\"\nexport { WhatIfUserBubble } from \"./components/WhatIfUserBubble\"\nexport type { WhatIfUserBubbleProps } from \"./components/WhatIfUserBubble\"\nexport { parseWhatIfPrompt } from \"./lib/whatif-prompt\"\nexport type { ParsedWhatIfPrompt, WhatIfQuote } from \"./lib/whatif-prompt\"\nexport {\n getPlanUpdateDisplayState,\n isPlanUpdateTool,\n PLAN_AUTO_COLLAPSE_MS,\n parsePlanUpdate,\n pickCurrentPlanStep,\n PlanUpdateBlock,\n} from \"./components/PlanUpdateBlock\"\nexport type { PlanUpdateDisplayState } from \"./components/PlanUpdateBlock\"\nexport { ContextCard, ContextGroupCard } from \"./components/ContextCard\"\nexport type { ContextCardProps, ContextGroupCardProps } from \"./components/ContextCard\"\nexport { ToolUiCardView } from \"./components/ToolUiCardView\"\nexport { McpAppCard } from \"./components/McpAppCard\"\nexport type { McpAppMessage } from \"./components/McpAppCard\"\nexport { SessionMemoryToggle } from \"./components/SessionMemoryToggle\"\nexport type { SessionMemoryToggleProps } from \"./components/SessionMemoryToggle\"\nexport { SessionQueuePanel, isQueuePanelVisible } from \"./components/SessionQueuePanel\"\nexport type { SessionQueuePanelProps } from \"./components/SessionQueuePanel\"\n\nexport { SessionBranchPicker } from \"./components/SessionBranchPicker\"\nexport type { SessionBranchPickerProps } from \"./components/SessionBranchPicker\"\n\nexport { MessageImage, type MessageImageProps } from \"./components/MessageImage\"\n\nexport { BackgroundWakeNotice } from \"./components/BackgroundWakeNotice\"\n\nexport { TopicAssetSubmitDialog, useTopicAssetSubmissionAccess } from \"./components/TopicAssetSubmitDialog\"\nexport type { TopicAssetSubmitDialogProps } from \"./components/TopicAssetSubmitDialog\"\n\nexport { CodeBlockPre } from \"./chat/markdown/MarkdownContent\"\n\nexport { ChatLoadingIndicator, isPreparingReply } from \"./chat/ChatLoadingIndicator\"\n","import type {\n BladeChatMountRenderers,\n MountHandle,\n MountRenderer,\n MountRendererObject,\n MountResult,\n} from \"@blade-hq/agent-client\"\nimport { createContext, useContext, useId, useLayoutEffect, useRef, type ReactNode } from \"react\"\nimport type { ChatViewRenderers } from \"../chat/customization\"\n\n/**\n * 挂载节点放在哪里。\n *\n * `<blade-chat>` 把它设成元素本身:挂载节点作为元素的 light DOM 子节点,经 Shadow DOM\n * 里的 `<slot>` 显示在区块位置,宿主页面的样式(包括 Vue 的 scoped 样式)直接生效。\n * 没有设置时(npm 包里),挂载节点就是组件内的一个 div。\n */\nexport const MountHostContext = createContext<HTMLElement | null>(null)\n\nfunction normalizeResult<T>(result: MountResult<T>): MountHandle<T> {\n if (typeof result === \"function\") return { destroy: result }\n if (result && typeof result === \"object\") return result\n return { destroy: () => {} }\n}\n\nfunction toObject<T>(renderer: MountRenderer<T>): MountRendererObject<T> {\n return typeof renderer === \"function\" ? { mount: renderer } : renderer\n}\n\n/** 两份数据的顶层字段逐个相同时不必通知宿主(渲染器每次都会新建数据对象)。 */\nfunction sameData(a: object, b: object): boolean {\n const aKeys = Object.keys(a)\n if (aKeys.length !== Object.keys(b).length) return false\n return aKeys.every((key) => Object.is((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]))\n}\n\ninterface MountSlotProps<T extends object> {\n renderer: MountRendererObject<T>\n data: T\n}\n\n/** 给宿主一个 DOM 节点挂自己的内容;数据变化时原地更新或清理后重挂,卸载时清理。 */\nexport function MountSlot<T extends object>({ renderer, data }: MountSlotProps<T>) {\n const host = useContext(MountHostContext)\n const slotName = `blade-mount-${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`\n const localRef = useRef<HTMLDivElement>(null)\n const dataRef = useRef(data)\n const mountedRef = useRef<{ target: HTMLElement; handle: MountHandle<T>; data: T } | null>(null)\n\n useLayoutEffect(() => {\n dataRef.current = data\n })\n\n // 与外部系统(宿主挂载的 DOM)同步:挂载一次,渲染器或挂载位置变了才重挂\n useLayoutEffect(() => {\n let target: HTMLElement\n if (host) {\n target = document.createElement(\"div\")\n target.slot = slotName\n host.appendChild(target)\n } else {\n if (!localRef.current) return\n target = localRef.current\n }\n const initial = dataRef.current\n mountedRef.current = { target, handle: normalizeResult(renderer.mount(target, initial)), data: initial }\n return () => {\n const mounted = mountedRef.current\n mountedRef.current = null\n mounted?.handle.destroy()\n if (host) target.remove()\n else target.replaceChildren()\n }\n }, [host, renderer, slotName])\n\n useLayoutEffect(() => {\n const mounted = mountedRef.current\n if (!mounted || sameData(mounted.data, data)) return\n mounted.data = data\n if (mounted.handle.update) {\n mounted.handle.update(data)\n return\n }\n mounted.handle.destroy()\n mounted.target.replaceChildren()\n mounted.handle = normalizeResult(renderer.mount(mounted.target, data))\n }, [data, renderer])\n\n return host ? <slot name={slotName} /> : <div ref={localRef} data-blade-mount=\"\" />\n}\n\nfunction slotFor<T extends object>(renderer: MountRendererObject<T>, data: T): ReactNode | null {\n if (renderer.match && !renderer.match(data)) return null\n return <MountSlot renderer={renderer} data={data} />\n}\n\n/**\n * 把挂载式渲染器转成普通 React 渲染器:两种写法走同一条渲染路径,`match` 返回\n * false 等于 React 渲染器返回 null(走默认渲染)。调用方要按 `renderers` 对象缓存结果,\n * 否则每次渲染都会得到新的渲染器、挂载内容被反复重建。\n */\nexport function mountRenderersToReact(\n renderers: BladeChatMountRenderers | null | undefined,\n context: { sessionId?: string } = {},\n): ChatViewRenderers | undefined {\n if (!renderers) return undefined\n const result: ChatViewRenderers = {}\n if (renderers.toolCall) {\n const renderer = toObject(renderers.toolCall)\n result.toolCall = (toolCall) => slotFor(renderer, { toolCall, sessionId: context.sessionId })\n }\n if (renderers.userMessage) {\n const renderer = toObject(renderers.userMessage)\n result.userMessage = ({ message }) => slotFor(renderer, { message })\n }\n if (renderers.assistantText) {\n const renderer = toObject(renderers.assistantText)\n result.assistantText = ({ message, streaming, compact, sessionId }) =>\n slotFor(renderer, { message, streaming, compact, sessionId })\n }\n return result\n}\n","import type { BladeClient } from \"@blade-hq/agent-client\"\nimport { useCallback, useEffect, useRef, useState, useSyncExternalStore } from \"react\"\nimport { useOptionalBladeClient } from \"../context\"\nimport { cn } from \"../lib/utils\"\n\ninterface SaveState {\n saving: boolean\n listeners: Set<() => void>\n}\n\nconst saveStates = new WeakMap<BladeClient, Map<string, SaveState>>()\n\nfunction getSaveState(client: BladeClient, sessionId: string): SaveState {\n let clientStates = saveStates.get(client)\n if (!clientStates) {\n clientStates = new Map()\n saveStates.set(client, clientStates)\n }\n let state = clientStates.get(sessionId)\n if (!state) {\n state = { saving: false, listeners: new Set() }\n clientStates.set(sessionId, state)\n }\n return state\n}\n\nfunction notify(state: SaveState) {\n for (const listener of state.listeners) listener()\n}\n\nfunction cleanupSaveState(client: BladeClient, sessionId: string, state: SaveState) {\n if (state.saving || state.listeners.size > 0) return\n const clientStates = saveStates.get(client)\n if (clientStates?.get(sessionId) === state) clientStates.delete(sessionId)\n}\n\nexport interface SessionMemoryToggleProps {\n sessionId: string\n enabled: boolean\n /** 不在 BladeProvider 内使用时显式传入,与宿主现有 BladeClient 复用连接和鉴权。 */\n client?: BladeClient\n disabled?: boolean\n label?: string\n className?: string\n labelClassName?: string\n inputClassName?: string\n onSaved?: (sessionId: string, enabled: boolean) => void\n onError?: (error: unknown) => void\n}\n\n/**\n * 当前会话记忆开关。同一 client + sessionId 的所有实例共享保存锁,避免多个聊天入口\n * 或会话切换时发出乱序更新;服务端成功后才通过 onSaved 交还权威值。\n */\nexport function SessionMemoryToggle({\n sessionId,\n enabled,\n client: clientProp,\n disabled = false,\n label = \"当前会话使用记忆\",\n className,\n labelClassName,\n inputClassName,\n onSaved,\n onError,\n}: SessionMemoryToggleProps) {\n const contextClient = useOptionalBladeClient()\n const client = clientProp ?? contextClient\n if (!client) {\n throw new Error(\"SessionMemoryToggle 必须在 <BladeProvider> 内使用或显式传入 client\")\n }\n\n const saveState = getSaveState(client, sessionId)\n const subscribe = useCallback(\n (listener: () => void) => {\n saveState.listeners.add(listener)\n return () => {\n saveState.listeners.delete(listener)\n cleanupSaveState(client, sessionId, saveState)\n }\n },\n [client, saveState, sessionId],\n )\n const getSaving = useCallback(() => saveState.saving, [saveState])\n const saving = useSyncExternalStore(\n subscribe,\n getSaving,\n getSaving,\n )\n const [draftEnabled, setDraftEnabled] = useState(enabled)\n const activeSessionIdRef = useRef(sessionId)\n activeSessionIdRef.current = sessionId\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: a new session must discard this component instance's optimistic value even when both sessions share the same enabled prop\n useEffect(() => {\n setDraftEnabled(enabled)\n }, [enabled, sessionId])\n\n const update = useCallback(\n (nextEnabled: boolean) => {\n const currentSaveState = getSaveState(client, sessionId)\n if (currentSaveState.saving) return\n currentSaveState.saving = true\n notify(currentSaveState)\n setDraftEnabled(nextEnabled)\n\n void client.sessions.updateSessionMemory(sessionId, nextEnabled).then(\n (updated) => {\n if (activeSessionIdRef.current === sessionId) {\n setDraftEnabled(updated.memory_enabled)\n }\n onSaved?.(sessionId, updated.memory_enabled)\n },\n (error) => {\n if (activeSessionIdRef.current === sessionId) setDraftEnabled(enabled)\n onError?.(error)\n },\n ).finally(() => {\n currentSaveState.saving = false\n notify(currentSaveState)\n cleanupSaveState(client, sessionId, currentSaveState)\n })\n },\n [client, enabled, onError, onSaved, sessionId],\n )\n\n return (\n <label className={cn(\"flex items-center justify-between\", className)}>\n <span className={labelClassName}>{label}</span>\n <input\n type=\"checkbox\"\n checked={draftEnabled}\n onChange={(event) => update(event.target.checked)}\n disabled={disabled || saving}\n className={inputClassName}\n />\n </label>\n )\n}\n","import type { BladeClient } from \"@blade-hq/agent-client\"\nimport * as Tooltip from \"@radix-ui/react-tooltip\"\nimport { useMemo, useState } from \"react\"\nimport { cn } from \"../lib/utils\"\n\ntype CheckpointTree = Awaited<ReturnType<BladeClient[\"sessions\"][\"getSessionCheckpoints\"]>>\ntype CheckpointNode = CheckpointTree[\"nodes\"][number]\n\nexport interface SessionBranchPickerProps {\n tree: CheckpointTree\n /** Disable changes while this session is running or read-only. */\n disabled?: boolean\n /** Restore only the conversation; the host owns history refresh and composer drafts. */\n onCheckout: (checkpointId: string, position: \"before\" | \"turn_end\") => Promise<void>\n}\n\nconst ROW_HEIGHT = 32\nconst SVG_WIDTH = 44\n\ninterface LayoutNode {\n id: string\n column: number\n row: number\n parentRow: number | null\n parentColumn: number | null\n isActive: boolean\n isCurrent: boolean\n isVirtual: boolean\n preview: string\n content: string\n}\n\ninterface LayoutGraph {\n nodes: LayoutNode[]\n hasCycle: boolean\n maxColumn: number\n maxRow: number\n}\n\nexport function SessionBranchPicker({\n tree,\n disabled = false,\n onCheckout,\n}: SessionBranchPickerProps) {\n const [checkoutError, setCheckoutError] = useState<string | null>(null)\n const [pendingNodeId, setPendingNodeId] = useState<string | null>(null)\n const hasHistory = tree.nodes.length > 0\n const layout = useMemo(() => buildLayoutNodes(tree), [tree])\n const nodeMap = useMemo(() => new Map(tree.nodes.map((node) => [node.id, node])), [tree])\n const isBusy = disabled || pendingNodeId !== null\n const graphHeight = Math.max((layout.maxRow + 1) * ROW_HEIGHT, ROW_HEIGHT)\n const graphWidth = Math.max(SVG_WIDTH, getColumnX(layout.maxColumn) + 12)\n\n async function checkoutNode(node: CheckpointNode, position: \"before\" | \"turn_end\") {\n if (isBusy) return\n setCheckoutError(null)\n setPendingNodeId(node.id)\n try {\n await onCheckout(node.id, position)\n } catch (error) {\n setCheckoutError(error instanceof Error ? error.message : \"切换失败,请稍后重试\")\n } finally {\n setPendingNodeId(null)\n }\n }\n\n async function handleVirtualClick(node: LayoutNode) {\n if (isBusy || !node.isVirtual || node.isActive) return\n const sourceNode = nodeMap.get(node.content)\n if (sourceNode) await checkoutNode(sourceNode, \"turn_end\")\n }\n\n async function handleNodeSelect(node: LayoutNode) {\n if (isBusy) return\n const sourceNode = nodeMap.get(node.id)\n if (sourceNode) await checkoutNode(sourceNode, \"before\")\n }\n\n if (!hasHistory) return null\n\n return (\n <Tooltip.Provider delayDuration={200}>\n {checkoutError ? (\n <p role=\"alert\" className=\"mb-2 text-xs text-destructive\">\n {checkoutError}\n </p>\n ) : null}\n <div className=\"flex max-h-52 justify-center overflow-x-auto overflow-y-auto\">\n <div className=\"relative\" style={{ height: graphHeight, width: graphWidth }}>\n <svg\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute left-0 top-0 z-10 overflow-visible\"\n width={graphWidth}\n height={graphHeight}\n viewBox={`0 0 ${graphWidth} ${graphHeight}`}\n fill=\"none\"\n >\n {layout.nodes.map((node) => {\n if (node.parentRow == null || node.parentColumn == null) {\n return null\n }\n\n const childStroke = node.isActive\n ? \"hsl(var(--primary))\"\n : \"hsl(var(--muted-foreground))\"\n\n return (\n <path\n key={`path-${node.id}`}\n d={buildEdgePath(\n getColumnX(node.parentColumn),\n getRowY(node.parentRow),\n getColumnX(node.column),\n getRowY(node.row),\n )}\n stroke={childStroke}\n strokeOpacity={node.isActive ? 0.95 : 0.45}\n strokeWidth={node.isActive ? 1.8 : 1.4}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeDasharray={node.isVirtual ? \"3 3\" : undefined}\n />\n )\n })}\n </svg>\n\n <div className=\"relative z-0 h-full\">\n {layout.nodes.map((node) => {\n const isPendingNode = pendingNodeId === node.id\n const color = node.isActive ? \"hsl(var(--primary))\" : \"hsl(var(--muted-foreground))\"\n const previewText = node.isVirtual\n ? isPendingNode\n ? \"切换中...\"\n : \"\"\n : isPendingNode\n ? \"切换中...\"\n : node.preview\n const shouldShowTooltip = !node.isVirtual || Boolean(previewText)\n const button = (\n <button\n type=\"button\"\n onClick={() =>\n void (node.isVirtual ? handleVirtualClick(node) : handleNodeSelect(node))\n }\n disabled={isBusy}\n aria-current={!node.isVirtual && node.isCurrent ? \"step\" : undefined}\n aria-label={node.isVirtual ? \"切换到该分支\" : node.preview}\n className={cn(\n \"absolute z-20 m-0 h-8 w-6 overflow-visible border-0 bg-transparent p-0\",\n isBusy ? \"cursor-not-allowed opacity-60\" : \"cursor-pointer\",\n )}\n style={{\n top: node.row * ROW_HEIGHT,\n left: getColumnX(node.column) - 12,\n }}\n >\n <span aria-hidden=\"true\" className=\"absolute left-0 top-0 h-8 w-6\">\n <svg\n aria-hidden=\"true\"\n role=\"img\"\n className=\"pointer-events-none absolute left-0 top-0 h-full w-full overflow-visible\"\n width={24}\n height={ROW_HEIGHT}\n viewBox={`0 0 24 ${ROW_HEIGHT}`}\n fill=\"none\"\n >\n {node.isVirtual ? (\n <circle\n cx={12}\n cy={ROW_HEIGHT / 2}\n r={3.5}\n fill=\"none\"\n stroke={color}\n strokeWidth={1.2}\n strokeDasharray=\"2 2\"\n opacity={0.7}\n />\n ) : (\n <g>\n {node.isCurrent ? (\n <circle cx={12} cy={ROW_HEIGHT / 2} r={9} fill={color} opacity={0.18} />\n ) : null}\n <circle\n cx={12}\n cy={ROW_HEIGHT / 2}\n r={node.isCurrent ? 5.5 : 4}\n fill={color}\n />\n </g>\n )}\n </svg>\n </span>\n </button>\n )\n\n if (!shouldShowTooltip) {\n return <div key={node.id}>{button}</div>\n }\n\n return (\n <Tooltip.Root key={node.id}>\n <Tooltip.Trigger asChild>{button}</Tooltip.Trigger>\n <Tooltip.Portal>\n <Tooltip.Content\n side=\"right\"\n sideOffset={8}\n className={cn(\n // z-[60]:这张图会被放进回溯弹层(z-50 且背景不透明),\n // 消息预览不抬到弹层之上就会被它的背景吃掉。\n \"z-[60] rounded-md border border-border bg-popover px-2 py-1 text-xs text-foreground shadow-md\",\n !node.isVirtual && node.isCurrent && \"font-semibold\",\n )}\n >\n {previewText}\n </Tooltip.Content>\n </Tooltip.Portal>\n </Tooltip.Root>\n )\n })}\n </div>\n </div>\n </div>\n </Tooltip.Provider>\n )\n}\nfunction buildLayoutNodes(tree: CheckpointTree | undefined): LayoutGraph {\n if (!tree || tree.nodes.length === 0) {\n return { nodes: [], hasCycle: false, maxColumn: 0, maxRow: 0 }\n }\n\n const nodeMap = new Map(tree.nodes.map((node) => [node.id, node]))\n const order = new Map(tree.nodes.map((node, index) => [node.id, index]))\n const childrenMap = new Map<string | null, CheckpointNode[]>()\n\n for (const node of tree.nodes) {\n const parentId = node.parent_id && nodeMap.has(node.parent_id) ? node.parent_id : null\n const siblings = childrenMap.get(parentId) ?? []\n siblings.push(node)\n childrenMap.set(parentId, siblings)\n }\n\n const activeIds = new Set<string>()\n const activeVisited = new Set<string>()\n const leafId = tree.leaf_id\n let current = tree.leaf_id\n\n while (current && nodeMap.has(current) && !activeVisited.has(current)) {\n activeIds.add(current)\n activeVisited.add(current)\n current = nodeMap.get(current)?.parent_id ?? null\n }\n\n const layoutNodes: LayoutNode[] = []\n const placed = new Set<string>()\n const visiting = new Set<string>()\n let nextColumn = 1\n let maxColumn = 0\n let maxRow = 0\n let hasCycle = false\n\n const rootSort = (a: CheckpointNode, b: CheckpointNode) =>\n (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0)\n\n const childSort = (a: CheckpointNode, b: CheckpointNode) =>\n (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0)\n\n const allocateColumn = () => {\n const fresh = nextColumn\n nextColumn += 1\n return fresh\n }\n\n function walk(node: CheckpointNode, parent: LayoutNode | null, column: number, row: number) {\n if (visiting.has(node.id)) {\n hasCycle = true\n return\n }\n\n if (placed.has(node.id)) {\n return\n }\n\n visiting.add(node.id)\n maxColumn = Math.max(maxColumn, column)\n maxRow = Math.max(maxRow, row)\n\n const layoutNode: LayoutNode = {\n id: node.id,\n column,\n row,\n parentRow: parent?.row ?? null,\n parentColumn: parent?.column ?? null,\n isActive: activeIds.has(node.id),\n isCurrent: node.id === leafId,\n isVirtual: false,\n preview: formatPreview(node.content_preview),\n content: node.content,\n }\n\n layoutNodes.push(layoutNode)\n placed.add(node.id)\n\n const children = [...(childrenMap.get(node.id) ?? [])].sort(childSort)\n\n if (children.length === 0) {\n const virtualRow = row + 1\n maxRow = Math.max(maxRow, virtualRow)\n layoutNodes.push({\n id: `__virtual__${node.id}`,\n column,\n row: virtualRow,\n parentRow: layoutNode.row,\n parentColumn: layoutNode.column,\n isActive: activeIds.has(node.id),\n isCurrent: false,\n isVirtual: true,\n preview: \"\",\n content: node.id,\n })\n } else {\n for (const [index, child] of children.entries()) {\n if (visiting.has(child.id)) {\n hasCycle = true\n continue\n }\n\n const childColumn = index === 0 ? column : allocateColumn()\n walk(child, layoutNode, childColumn, row + 1)\n }\n }\n\n visiting.delete(node.id)\n }\n\n const roots = [...(childrenMap.get(null) ?? [])].sort(rootSort)\n for (const [index, root] of roots.entries()) {\n const rootColumn = index === 0 ? 0 : allocateColumn()\n walk(root, null, rootColumn, 0)\n }\n\n for (const node of [...tree.nodes].sort(rootSort)) {\n if (!placed.has(node.id)) {\n hasCycle = true\n const rootColumn = layoutNodes.length === 0 ? 0 : allocateColumn()\n walk(node, null, rootColumn, 0)\n }\n }\n\n return {\n nodes: layoutNodes,\n hasCycle,\n maxColumn,\n maxRow,\n }\n}\n\nfunction formatPreview(preview: string): string {\n const normalized = preview.replace(/\\s+/g, \" \").trim()\n return normalized.length > 0 ? normalized : \"(空消息)\"\n}\n\nfunction getColumnX(column: number): number {\n return 12 + column * 24\n}\n\nfunction getRowY(row: number): number {\n return row * ROW_HEIGHT + ROW_HEIGHT / 2\n}\n\nfunction buildEdgePath(fromX: number, fromY: number, toX: number, toY: number): string {\n if (fromX === toX || fromY === toY) {\n return `M ${fromX} ${fromY} L ${toX} ${toY}`\n }\n\n // Branch line: horizontal from parent to child column, then vertical down\n const r = Math.min(5, Math.abs(toX - fromX), Math.abs(toY - fromY))\n const dx = toX > fromX ? 1 : -1\n\n return [\n `M ${fromX} ${fromY}`,\n `H ${toX - dx * r}`,\n `A ${r} ${r} 0 0 ${dx > 0 ? 1 : 0} ${toX} ${fromY + r}`,\n `V ${toY}`,\n ].join(\" \")\n}\n"],"mappings":"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","names":["useRef","jsx","useRef","useState","jsx","jsxs","useState"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blade-hq/agent-react",
|
|
3
|
-
"version": "2612.0.0-beta.
|
|
3
|
+
"version": "2612.0.0-beta.19",
|
|
4
4
|
"description": "Blade Agent React 绑定:BladeProvider、useAgentSession、开箱即用的 ChatView 聊天界面。",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@blade-hq/agent-client": "2612.0.0-beta.
|
|
43
|
+
"@blade-hq/agent-client": "2612.0.0-beta.19",
|
|
44
44
|
"@radix-ui/react-collapsible": "1.1.12",
|
|
45
45
|
"@tiptap/core": "3.20.4",
|
|
46
46
|
"@tiptap/extension-mention": "3.20.4",
|