@blade-hq/agent-react 2612.0.0-beta.17 → 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-7TQVTNPL.js → chunk-GXJCVUXP.js} +1 -1
- package/dist/chunk-GXJCVUXP.js.map +1 -0
- package/dist/{chunk-EYRP6PV3.js → chunk-LC7Q64YI.js} +2 -2
- package/dist/{chunk-6YMUPKGL.js → chunk-ZAEHTW2O.js} +885 -869
- package/dist/chunk-ZAEHTW2O.js.map +1 -0
- package/dist/embed/entry.d.ts +1 -0
- package/dist/index.js +2 -2
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +7 -3
- package/dist/internal.js.map +1 -1
- package/dist/rich-input/index.d.ts +1 -1
- package/dist/rich-input/lib/composer-content.d.ts +5 -1
- package/dist/rich-input/lib/plugin-suggestions.d.ts +4 -4
- package/dist/rich-input.js +34 -16
- package/dist/rich-input.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-6YMUPKGL.js.map +0 -1
- package/dist/chunk-7TQVTNPL.js.map +0 -1
- /package/dist/{chunk-EYRP6PV3.js.map → chunk-LC7Q64YI.js.map} +0 -0
package/dist/embed/entry.d.ts
CHANGED
|
@@ -70,6 +70,7 @@ declare const BladeAgent: {
|
|
|
70
70
|
userFacingErrorText: typeof agentClient.userFacingErrorText;
|
|
71
71
|
normalizeTopicAssetRefs: typeof agentClient.normalizeTopicAssetRefs;
|
|
72
72
|
normalizePluginRefs: typeof agentClient.normalizePluginRefs;
|
|
73
|
+
normalizeMessageRefs: typeof agentClient.normalizeMessageRefs;
|
|
73
74
|
ToolProvider: typeof agentClient.ToolProvider;
|
|
74
75
|
getChat: typeof getChat;
|
|
75
76
|
BladeChatElement: typeof BladeChatElement;
|
package/dist/index.js
CHANGED
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
useAgentSession,
|
|
8
8
|
useReplay,
|
|
9
9
|
useTopicAssetSubmissionAccess
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ZAEHTW2O.js";
|
|
11
11
|
import {
|
|
12
12
|
BladeProvider,
|
|
13
13
|
useBladeClient
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-GXJCVUXP.js";
|
|
15
15
|
import "./chunk-PR4QN5HX.js";
|
|
16
16
|
export {
|
|
17
17
|
BladeProvider,
|
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,14 +32,14 @@ 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,
|
|
37
39
|
TRIGGER_ICON_LIMIT,
|
|
38
40
|
selectionFromItem,
|
|
39
41
|
triggerSummary
|
|
40
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-LC7Q64YI.js";
|
|
41
43
|
import {
|
|
42
44
|
BladeProvider,
|
|
43
45
|
Content2,
|
|
@@ -64,7 +66,7 @@ import {
|
|
|
64
66
|
useBladeHost,
|
|
65
67
|
useOptionalBladeClient,
|
|
66
68
|
useSessionPluginActivation
|
|
67
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-GXJCVUXP.js";
|
|
68
70
|
import "./chunk-PR4QN5HX.js";
|
|
69
71
|
|
|
70
72
|
// src/internal.ts
|
|
@@ -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"]}
|
|
@@ -48,4 +48,4 @@ export * from "./lib/topic-asset-suggestions";
|
|
|
48
48
|
export * from "./use-reference-candidates";
|
|
49
49
|
export { PluginMention, isPluginSuggestionItem } from "./extensions/plugin-mention";
|
|
50
50
|
export { getPluginSuggestionItems } from "./lib/plugin-suggestions";
|
|
51
|
-
export { collectPluginMentions } from "./lib/composer-content";
|
|
51
|
+
export { collectPluginMentions, collectMessageMentions } from "./lib/composer-content";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type PluginRef, type TopicAssetRef, type SkillMentionAvailability } from "@blade-hq/agent-client";
|
|
1
|
+
import { type PluginRef, type MessageRef, type TopicAssetRef, type SkillMentionAvailability } from "@blade-hq/agent-client";
|
|
2
2
|
/**
|
|
3
3
|
* 输入框(tiptap)文档的最小结构,以及把它翻译成待发送文本的规则。
|
|
4
4
|
*
|
|
@@ -30,3 +30,7 @@ export declare function buildEditorDocumentWithMentions(text: string, topicAsset
|
|
|
30
30
|
export declare function retainTopicAssetMentions(node: EditorJSON, topicId: string | null): EditorJSON;
|
|
31
31
|
export declare function collectPluginMentions(node: EditorJSON | null | undefined): PluginRef[];
|
|
32
32
|
export declare function buildEditorDocument(text: string): EditorJSON;
|
|
33
|
+
/** Mention identity is separate from user intent; position never implies execution. */
|
|
34
|
+
export declare function collectMessageMentions(node: EditorJSON | null | undefined): MessageRef[];
|
|
35
|
+
/** Only plugin selections persist into the next message. */
|
|
36
|
+
export declare function buildPluginSelectionDocument(refs: readonly MessageRef[]): EditorJSON;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { BladeClient,
|
|
1
|
+
import type { BladeClient, MessageRef } from "@blade-hq/agent-client";
|
|
2
2
|
import type { PluginSuggestionItem, PluginSuggestionError } from "../extensions/plugin-mention";
|
|
3
|
-
export declare function pluginRefsForSend(refs:
|
|
3
|
+
export declare function pluginRefsForSend(refs: MessageRef[], turns?: readonly {
|
|
4
4
|
role: string;
|
|
5
|
-
refs?:
|
|
6
|
-
}[]):
|
|
5
|
+
refs?: MessageRef[] | null;
|
|
6
|
+
}[]): MessageRef[] | undefined;
|
|
7
7
|
export declare function getPluginSuggestionItems(client: BladeClient, session: string | null | undefined, query: string, pendingPlugins?: readonly string[]): Promise<Array<PluginSuggestionItem | PluginSuggestionError>>;
|
package/dist/rich-input.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
PluginTriggerStack,
|
|
4
4
|
selectionFromItem,
|
|
5
5
|
triggerSummary
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LC7Q64YI.js";
|
|
7
7
|
import {
|
|
8
8
|
Archive,
|
|
9
9
|
BookOpen,
|
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
usePortalContainer,
|
|
63
63
|
useSessionHandle,
|
|
64
64
|
useSessionState
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-GXJCVUXP.js";
|
|
66
66
|
import {
|
|
67
67
|
__commonJS,
|
|
68
68
|
__require,
|
|
@@ -18948,7 +18948,7 @@ var PluginMention = index_default.extend({
|
|
|
18948
18948
|
});
|
|
18949
18949
|
|
|
18950
18950
|
// src/rich-input/lib/composer-content.ts
|
|
18951
|
-
import { normalizePluginRefs, normalizeTopicAssetRefs } from "@blade-hq/agent-client";
|
|
18951
|
+
import { normalizePluginRefs, normalizeMessageRefs, normalizeTopicAssetRefs } from "@blade-hq/agent-client";
|
|
18952
18952
|
function serializeEditorContent(node) {
|
|
18953
18953
|
if (!node) return "";
|
|
18954
18954
|
if (node.type === "text") {
|
|
@@ -18963,7 +18963,7 @@ function serializeEditorContent(node) {
|
|
|
18963
18963
|
}
|
|
18964
18964
|
if (node.type === "skillMention") {
|
|
18965
18965
|
const skillId = typeof node.attrs?.skillId === "string" ? node.attrs.skillId : "";
|
|
18966
|
-
return skillId ?
|
|
18966
|
+
return skillId ? `@${node.attrs?.displayName || skillId}` : "";
|
|
18967
18967
|
}
|
|
18968
18968
|
if (node.type === "pluginMention") return `@${node.attrs?.label ?? ""}`;
|
|
18969
18969
|
if (node.type === "topicAssetMention") {
|
|
@@ -19065,6 +19065,27 @@ function collectPluginMentions(node) {
|
|
|
19065
19065
|
function buildEditorDocument(text) {
|
|
19066
19066
|
return { type: "doc", content: text.split("\n").map((line) => ({ type: "paragraph", content: line ? [{ type: "text", text: line }] : [] })) };
|
|
19067
19067
|
}
|
|
19068
|
+
function collectMessageMentions(node) {
|
|
19069
|
+
const refs = [];
|
|
19070
|
+
const visit = (current) => {
|
|
19071
|
+
if (!current) return;
|
|
19072
|
+
if (current.type === "pluginMention") refs.push(current.attrs);
|
|
19073
|
+
if (current.type === "skillMention") refs.push({
|
|
19074
|
+
provider: "skill",
|
|
19075
|
+
id: current.attrs?.skillId,
|
|
19076
|
+
label: current.attrs?.displayName || current.attrs?.skillId
|
|
19077
|
+
});
|
|
19078
|
+
current.content?.forEach(visit);
|
|
19079
|
+
};
|
|
19080
|
+
visit(node);
|
|
19081
|
+
return normalizeMessageRefs(refs);
|
|
19082
|
+
}
|
|
19083
|
+
function buildPluginSelectionDocument(refs) {
|
|
19084
|
+
return { type: "doc", content: [{ type: "paragraph", content: normalizePluginRefs(refs).flatMap((ref) => [
|
|
19085
|
+
{ type: "pluginMention", attrs: { ...ref } },
|
|
19086
|
+
{ type: "text", text: " " }
|
|
19087
|
+
]) }] };
|
|
19088
|
+
}
|
|
19068
19089
|
|
|
19069
19090
|
// src/rich-input/extensions/topic-asset-mention.ts
|
|
19070
19091
|
function isTopicAssetSuggestionItem(value) {
|
|
@@ -27062,8 +27083,7 @@ function splitFilesBySize(files, limitBytes = ATTACHMENT_SIZE_LIMIT_BYTES) {
|
|
|
27062
27083
|
|
|
27063
27084
|
// src/rich-input/ChatInput.tsx
|
|
27064
27085
|
import {
|
|
27065
|
-
buildMessageContent
|
|
27066
|
-
transformSlashCommand
|
|
27086
|
+
buildMessageContent
|
|
27067
27087
|
} from "@blade-hq/agent-client/internal";
|
|
27068
27088
|
|
|
27069
27089
|
// src/rich-input/lib/upload-results.ts
|
|
@@ -28901,7 +28921,7 @@ function ChatInput({
|
|
|
28901
28921
|
if (!latest || latest.turn_id.startsWith("local-user-")) return;
|
|
28902
28922
|
restoredPluginSelection.current = activeSessionId;
|
|
28903
28923
|
if (!getEditorText(editor).trim() && latest.refs?.length) {
|
|
28904
|
-
editor.commands.setContent(
|
|
28924
|
+
editor.commands.setContent(buildPluginSelectionDocument(latest.refs));
|
|
28905
28925
|
}
|
|
28906
28926
|
};
|
|
28907
28927
|
restore();
|
|
@@ -29001,7 +29021,7 @@ ${append2.text}` : append2.text;
|
|
|
29001
29021
|
}
|
|
29002
29022
|
if (isStreaming) {
|
|
29003
29023
|
const json = editor?.getJSON();
|
|
29004
|
-
if (composerAttachments.length > 0 ||
|
|
29024
|
+
if (composerAttachments.length > 0 || collectMessageMentions(json).length > 0 || collectTopicAssetMentions(json).length > 0) {
|
|
29005
29025
|
notifyChat(environment, "info", "\u5F53\u524D\u56DE\u590D\u5C1A\u672A\u7ED3\u675F\uFF0C\u5E26\u9644\u4EF6\u6216\u5F15\u7528\u7684\u6D88\u606F\u8BF7\u7B49\u5F85\u672C\u8F6E\u7ED3\u675F\u540E\u518D\u53D1\u9001");
|
|
29006
29026
|
return;
|
|
29007
29027
|
}
|
|
@@ -29033,10 +29053,9 @@ ${append2.text}` : append2.text;
|
|
|
29033
29053
|
const rawText = serializeEditorContent(json);
|
|
29034
29054
|
let submittedSessionId = activeSessionId;
|
|
29035
29055
|
const submittedAttachmentIds = composerAttachments.map((attachment) => attachment.id);
|
|
29036
|
-
const
|
|
29037
|
-
const refs = collectPluginMentions(json);
|
|
29056
|
+
const refs = collectMessageMentions(json);
|
|
29038
29057
|
if (refs.length > 20) {
|
|
29039
|
-
notifyChat(environment, "error", "\u6BCF\u6761\u6D88\u606F\u6700\u591A\u9009\u62E9 20 \u4E2A\
|
|
29058
|
+
notifyChat(environment, "error", "\u6BCF\u6761\u6D88\u606F\u6700\u591A\u9009\u62E9 20 \u4E2A\u5F15\u7528");
|
|
29040
29059
|
return;
|
|
29041
29060
|
}
|
|
29042
29061
|
const topicAssetRefs = collectTopicAssetMentions(json);
|
|
@@ -29044,10 +29063,7 @@ ${append2.text}` : append2.text;
|
|
|
29044
29063
|
notifyChat(environment, "error", "\u6BCF\u6761\u6D88\u606F\u6700\u591A\u5F15\u7528 20 \u4E2A\u4E3B\u9898\u8D44\u4EA7");
|
|
29045
29064
|
return;
|
|
29046
29065
|
}
|
|
29047
|
-
const text =
|
|
29048
|
-
local: skill.local,
|
|
29049
|
-
installed: skill.installed
|
|
29050
|
-
}) : rawText;
|
|
29066
|
+
const text = rawText;
|
|
29051
29067
|
const pendingFiles = composerAttachments.filter(
|
|
29052
29068
|
(a) => a.kind === "file" && a.status === "pending" && !!a.file
|
|
29053
29069
|
);
|
|
@@ -29200,7 +29216,7 @@ ${append2.text}` : append2.text;
|
|
|
29200
29216
|
return;
|
|
29201
29217
|
}
|
|
29202
29218
|
if (refs.length && editor && !getEditorText(editor).trim()) {
|
|
29203
|
-
editor.commands.setContent(
|
|
29219
|
+
editor.commands.setContent(buildPluginSelectionDocument(refs));
|
|
29204
29220
|
}
|
|
29205
29221
|
if (activeSessionId && activePendingContexts.length > 0) {
|
|
29206
29222
|
composerChannels.consumePendingContexts(activeSessionId);
|
|
@@ -29805,8 +29821,10 @@ export {
|
|
|
29805
29821
|
VoiceWaveform,
|
|
29806
29822
|
buildEditorDocument,
|
|
29807
29823
|
buildEditorDocumentWithMentions,
|
|
29824
|
+
buildPluginSelectionDocument,
|
|
29808
29825
|
buildSubmitPayload,
|
|
29809
29826
|
buildVoiceInputUnavailableError,
|
|
29827
|
+
collectMessageMentions,
|
|
29810
29828
|
collectPluginMentions,
|
|
29811
29829
|
collectTopicAssetMentions,
|
|
29812
29830
|
entryLabel,
|