@blade-hq/agent-kit 0.4.4 → 0.4.6

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.
Files changed (51) hide show
  1. package/README.md +8 -1
  2. package/dist/chunk-2UP7MG3J.js +66 -0
  3. package/dist/chunk-2UP7MG3J.js.map +1 -0
  4. package/dist/chunk-4VWLTG5L.js +2984 -0
  5. package/dist/chunk-4VWLTG5L.js.map +1 -0
  6. package/dist/chunk-7LEKQI47.js +32 -0
  7. package/dist/chunk-7LEKQI47.js.map +1 -0
  8. package/dist/chunk-DQCXSPHP.js +33 -0
  9. package/dist/chunk-DQCXSPHP.js.map +1 -0
  10. package/dist/chunk-I3FFV63W.js +30 -0
  11. package/dist/chunk-I3FFV63W.js.map +1 -0
  12. package/dist/chunk-J3XVFPOV.js +58 -0
  13. package/dist/chunk-J3XVFPOV.js.map +1 -0
  14. package/dist/chunk-JCJFFJ42.js +39 -0
  15. package/dist/chunk-JCJFFJ42.js.map +1 -0
  16. package/dist/chunk-LIL4FIZP.js +7992 -0
  17. package/dist/chunk-LIL4FIZP.js.map +1 -0
  18. package/dist/chunk-OKQWPNE3.js +1077 -0
  19. package/dist/chunk-OKQWPNE3.js.map +1 -0
  20. package/dist/chunk-PZ5AY32C.js +10 -0
  21. package/dist/chunk-PZ5AY32C.js.map +1 -0
  22. package/dist/chunk-TC5BBLWO.js +29 -0
  23. package/dist/chunk-TC5BBLWO.js.map +1 -0
  24. package/dist/chunk-VD4CKRMT.js +127 -0
  25. package/dist/chunk-VD4CKRMT.js.map +1 -0
  26. package/dist/chunk-X6MEYCU7.js +1401 -0
  27. package/dist/chunk-X6MEYCU7.js.map +1 -0
  28. package/dist/client/index.d.ts +529 -40
  29. package/dist/client/index.js +24 -1033
  30. package/dist/client/index.js.map +1 -1
  31. package/dist/react/api/licenses.js +11 -1470
  32. package/dist/react/api/licenses.js.map +1 -1
  33. package/dist/react/api/vibe-coding.js +25 -1481
  34. package/dist/react/api/vibe-coding.js.map +1 -1
  35. package/dist/react/cards/register.js +45 -138
  36. package/dist/react/cards/register.js.map +1 -1
  37. package/dist/react/components/chat/index.js +28 -11366
  38. package/dist/react/components/chat/index.js.map +1 -1
  39. package/dist/react/components/plan/index.js +135 -3054
  40. package/dist/react/components/plan/index.js.map +1 -1
  41. package/dist/react/components/session/index.js +21 -1499
  42. package/dist/react/components/session/index.js.map +1 -1
  43. package/dist/react/components/workspace/index.js +116 -1715
  44. package/dist/react/components/workspace/index.js.map +1 -1
  45. package/dist/react/devtools/bridge-devtools/index.js +8 -51
  46. package/dist/react/devtools/bridge-devtools/index.js.map +1 -1
  47. package/dist/react/index.d.ts +74 -2
  48. package/dist/react/index.js +656 -13958
  49. package/dist/react/index.js.map +1 -1
  50. package/dist/style.css +2 -0
  51. package/package.json +5 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/react/components/workspace/WorkspaceFilesPanel.tsx","../../../../src/client/resources/models.ts","../../../../src/client/socket.ts","../../../../src/react/schemas/partner-skill.ts","../../../../src/react/stores/ui-bridge-store.ts","../../../../src/react/stores/client-aware.ts","../../../../src/react/lib/chat.ts","../../../../src/react/lib/ui-meta.ts","../../../../src/react/stores/auth-store.ts","../../../../src/react/api/auth.ts","../../../../src/react/sockets/socket-state.ts","../../../../src/react/stores/session-store.ts","../../../../src/react/stores/chat-store.ts","../../../../src/react/components/chat/display-utils.ts","../../../../src/react/stores/ui-store.ts","../../../../src/react/stores/task-store.ts","../../../../src/react/stores/background-store.ts","../../../../src/react/stores/card-state-store.ts","../../../../src/react/stores/connection-store.ts","../../../../src/react/stores/runtime-store.ts","../../../../src/react/stores/gis-store.ts","../../../../src/react/stores/answer-callback-store.ts","../../../../src/react/stores/runtime-features-store.ts","../../../../src/react/bootstrap.ts","../../../../src/react/api/client.ts","../../../../src/react/api/sessions.ts","../../../../src/react/lib/utils.ts","../../../../src/react/components/workspace/FileTree.tsx","../../../../src/react/lib/session-file-preview.ts","../../../../src/react/lib/open-session-file.ts","../../../../src/react/components/ui/collapsible.tsx","../../../../src/react/components/ai-elements/file-tree.tsx"],"sourcesContent":["import { useQueryClient } from \"@tanstack/react-query\"\nimport { FileText, FolderTree, FolderUp, Loader2, PanelRightOpen, RefreshCw, Upload } from \"lucide-react\"\nimport { type ChangeEvent, type ReactNode, useEffect, useMemo, useRef, useState } from \"react\"\nimport { uploadFiles } from \"../../api/sessions\"\nimport { isUiMeta } from \"../../lib/ui-meta\"\nimport { cn } from \"../../lib/utils\"\nimport type { PreviewTarget } from \"../../stores/ui-store\"\nimport { useChatStore } from \"../../stores/chat-store\"\nimport { useUiStore } from \"../../stores/ui-store\"\nimport { FileTree } from \"./FileTree\"\n\ninterface WorkspaceFilesPanelProps {\n sessionId: string | null\n enableEditor?: boolean\n className?: string\n treeContainerClassName?: string\n title?: string\n rootPath?: string\n readOnly?: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n onSync?: () => Promise<void>\n allowDelete?: boolean\n /** Optional slot above the file tree (e.g. phase progress in host app). */\n topSlot?: ReactNode\n /** Custom file open handler; used when enableEditor is false if provided. */\n onOpenFile?: (path: string, name: string) => Promise<void>\n}\n\ntype WorkspaceTab = \"files\" | \"resources\"\n\ninterface InlineResourceItem {\n key: string\n title: string\n target: PreviewTarget\n}\n\nconst EMPTY_MESSAGES: ReturnType<typeof useChatStore.getState>[\"messages\"][string] = []\n\nexport function WorkspaceFilesPanel({\n sessionId,\n enableEditor = false,\n className,\n treeContainerClassName,\n title = \"工作区\",\n rootPath = \".\",\n readOnly = false,\n onShareFile,\n onSync,\n allowDelete = false,\n topSlot,\n onOpenFile,\n}: WorkspaceFilesPanelProps) {\n const [expandedDirs, setExpandedDirs] = useState<Set<string>>(new Set())\n const [isUploading, setIsUploading] = useState(false)\n const [activeTab, setActiveTab] = useState<WorkspaceTab>(\"files\")\n const [isSyncing, setIsSyncing] = useState(false)\n const queryClient = useQueryClient()\n const fileInputRef = useRef<HTMLInputElement>(null)\n const folderInputRef = useRef<HTMLInputElement>(null)\n const messages = useChatStore((state) =>\n sessionId ? (state.messages[sessionId] ?? EMPTY_MESSAGES) : EMPTY_MESSAGES,\n )\n const pushArtifact = useUiStore((state) => state.pushArtifact)\n\n const inlineResources = useMemo<InlineResourceItem[]>(() => {\n if (!sessionId) return []\n return messages.flatMap((message, messageIndex) =>\n (message.blocks ?? []).flatMap((block, blockIndex): InlineResourceItem[] => {\n if (block.type !== \"tool_ui\" || !isUiMeta(block.content) || block.content.target !== \"inline\") {\n return []\n }\n\n const ui = block.content\n const type = ui.resourceHTML ? \"resource-html\" : \"resource-uri\"\n const content = ui.resourceHTML ?? ui.resourceUri ?? ui.resourceURI ?? \"\"\n if (!content) return []\n\n const title =\n ui.title ??\n (typeof block.display_name === \"string\" && block.display_name.trim().length > 0\n ? block.display_name\n : \"可视化产物\")\n const key = `${message.entry_id ?? message.timestamp ?? messageIndex}-${block.tool_call_id ?? \"ui\"}-${blockIndex}`\n\n return [\n {\n key,\n title,\n target: {\n type,\n content,\n title,\n key,\n bridgeSessionId: sessionId,\n },\n },\n ]\n }),\n )\n }, [messages, sessionId])\n\n useEffect(() => {\n if (activeTab === \"resources\" && inlineResources.length === 0) {\n setActiveTab(\"files\")\n }\n }, [activeTab, inlineResources.length])\n\n const uploadTargetDir = () => {\n const candidates = Array.from(expandedDirs)\n .filter((path) => path !== \".\")\n .filter((path) => {\n const parts = path.split(\"/\").filter(Boolean)\n for (let index = 1; index < parts.length; index += 1) {\n const ancestor = parts.slice(0, index).join(\"/\")\n if (!expandedDirs.has(ancestor)) return false\n }\n return true\n })\n\n if (candidates.length === 0) {\n return \".\"\n }\n\n return candidates.reduce((deepestPath, currentPath) => {\n const deepestDepth = deepestPath.split(\"/\").filter(Boolean).length\n const currentDepth = currentPath.split(\"/\").filter(Boolean).length\n return currentDepth > deepestDepth ? currentPath : deepestPath\n }, candidates[0] ?? \".\")\n }\n\n const handleUploadChange = async (event: ChangeEvent<HTMLInputElement>) => {\n const files = event.target.files\n if (!sessionId || !files || files.length === 0) {\n event.target.value = \"\"\n return\n }\n\n setIsUploading(true)\n try {\n const targetDir = uploadTargetDir()\n const result = await uploadFiles(sessionId, targetDir, files)\n await queryClient.invalidateQueries({\n queryKey: [\"file-tree\", sessionId],\n refetchType: \"all\",\n })\n await queryClient.refetchQueries({\n queryKey: [\"file-tree\", sessionId],\n type: \"active\",\n })\n\n if (targetDir !== \".\") {\n setExpandedDirs((prev) => {\n const next = new Set(prev)\n next.add(targetDir)\n return next\n })\n }\n\n if (result.failed.length > 0) {\n alert(`部分文件上传失败: ${result.failed.join(\", \")}`)\n }\n } catch (error) {\n console.error(\"Upload files failed\", error)\n alert(\"上传文件失败\")\n } finally {\n setIsUploading(false)\n event.target.value = \"\"\n }\n }\n\n const handleUploadClick = () => {\n fileInputRef.current?.click()\n }\n\n const handleFolderUploadClick = () => {\n folderInputRef.current?.click()\n }\n\n return (\n <div className={cn(\"flex flex-col\", className)}>\n {topSlot ? <div className=\"mb-3\">{topSlot}</div> : null}\n <div className=\"mb-2 flex shrink-0 items-center justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-1 rounded-lg bg-[hsl(var(--muted))]/60 p-0.5 text-[11px] font-medium\">\n <button\n type=\"button\"\n onClick={() => setActiveTab(\"files\")}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-2 py-1 transition-colors\",\n activeTab === \"files\"\n ? \"bg-[hsl(var(--background))] text-[hsl(var(--foreground))] shadow-sm\"\n : \"text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]\",\n )}\n >\n <FolderTree size={12} />\n {title}\n </button>\n {inlineResources.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => setActiveTab(\"resources\")}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-2 py-1 transition-colors\",\n activeTab === \"resources\"\n ? \"bg-[hsl(var(--background))] text-[hsl(var(--foreground))] shadow-sm\"\n : \"text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]\",\n )}\n >\n <PanelRightOpen size={12} />\n 可视化产物\n <span className=\"font-mono text-[10px] text-[hsl(var(--muted-foreground))]\">\n {inlineResources.length}\n </span>\n </button>\n ) : null}\n </div>\n {onSync ? <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n disabled={!sessionId || isSyncing}\n onClick={async () => {\n setIsSyncing(true)\n try {\n await onSync()\n await queryClient.invalidateQueries({\n queryKey: [\"file-tree\", sessionId],\n refetchType: \"all\",\n })\n } finally {\n setIsSyncing(false)\n }\n }}\n className=\"rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"同步共享区\"\n >\n {isSyncing ? <Loader2 size={12} className=\"animate-spin\" /> : <RefreshCw size={12} />}\n </button>\n </div> : null}\n {!readOnly && activeTab === \"files\" ? <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n disabled={!sessionId || isUploading}\n onClick={handleUploadClick}\n className=\"rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"上传文件\"\n >\n {isUploading ? <Loader2 size={12} className=\"animate-spin\" /> : <Upload size={12} />}\n </button>\n <button\n type=\"button\"\n disabled={!sessionId || isUploading}\n onClick={handleFolderUploadClick}\n className=\"rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"上传文件夹\"\n >\n {isUploading ? <Loader2 size={12} className=\"animate-spin\" /> : <FolderUp size={12} />}\n </button>\n </div> : null}\n </div>\n {activeTab === \"resources\" && inlineResources.length > 0 ? (\n <div className={cn(\"min-h-0 overflow-auto\", treeContainerClassName)}>\n <div className=\"flex flex-col gap-1.5\">\n {inlineResources.map((item, index) => (\n <button\n key={item.key}\n type=\"button\"\n onClick={() => pushArtifact(item.target)}\n className=\"group flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors hover:bg-[hsl(var(--muted))]\"\n title={item.title}\n >\n <FileText size={14} className=\"shrink-0 text-[hsl(var(--primary))]\" />\n <span className=\"min-w-0 flex-1 truncate text-[hsl(var(--foreground))]\">\n {item.title}\n </span>\n <span className=\"shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]\">\n {index + 1}\n </span>\n </button>\n ))}\n </div>\n </div>\n ) : (\n <div className={treeContainerClassName}>\n <FileTree\n key={sessionId ?? \"workspace-empty\"}\n sessionId={sessionId}\n enableEditor={enableEditor}\n onExpandedDirsChange={setExpandedDirs}\n rootPath={rootPath}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n onOpenFile={onOpenFile}\n />\n </div>\n )}\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n className=\"hidden\"\n onChange={handleUploadChange}\n />\n <input\n ref={folderInputRef}\n type=\"file\"\n multiple\n className=\"hidden\"\n onChange={handleUploadChange}\n // biome-ignore lint/suspicious/noExplicitAny: webkitdirectory is a non-standard HTML attribute\n {...({ webkitdirectory: \"\" } as any)}\n />\n </div>\n )\n}\n","import { type } from \"arktype\"\nimport type { BladeClient } from \"../blade-client\"\n\nexport const ModelOption = type({\n id: \"string\",\n label: \"string\",\n})\nexport type ModelOption = typeof ModelOption.infer\n\nexport const ModelsConfig = type({\n default: \"string\",\n models: ModelOption.array(),\n})\n\nexport type ModelsConfig = typeof ModelsConfig.infer\n\nexport class ModelsResource {\n constructor(private client: BladeClient) {}\n\n async getModelsConfig(init?: RequestInit): Promise<ModelsConfig> {\n const data = await this.client.jsonFromInit<unknown>(\"/api/config/models\", init)\n return ModelsConfig.assert(data)\n }\n}\n","import { io, type Socket } from \"socket.io-client\"\nimport { buildSocketAuth, type AuthOptions } from \"./auth\"\nimport type { ClientToServerEvents, ServerToClientEvents } from \"./types/socket-events\"\n\nexport type TypedSocket = Socket<ServerToClientEvents, ClientToServerEvents>\n\nexport interface CreateSocketOptions extends AuthOptions {\n baseUrl: string\n path?: string\n}\n\nexport function createSocket(options: CreateSocketOptions): TypedSocket {\n const auth = buildSocketAuth(options)\n return io(options.baseUrl, {\n path: options.path ?? \"/socket.io\",\n withCredentials: true,\n auth: typeof options.token === \"function\" ? (cb) => cb(buildSocketAuth(options) ?? {}) : auth,\n autoConnect: false,\n })\n}\n","import { type } from \"arktype\"\n\nexport const PartnerSkillName = type(\"/^[a-z0-9-]+\\\\/[a-z0-9-]+$/\")\nexport type PartnerSkillName = typeof PartnerSkillName.infer\n\nexport const PartnerSkillFile = type({\n path: \"string > 0\",\n content: \"string\",\n})\nexport type PartnerSkillFile = typeof PartnerSkillFile.infer\n\nexport const PartnerSkillInstallPayload = type({\n name: PartnerSkillName,\n files: PartnerSkillFile.array().atLeastLength(1),\n})\nexport type PartnerSkillInstallPayload = typeof PartnerSkillInstallPayload.infer\n\nexport const PartnerSkillInstallResult = type({\n name: PartnerSkillName,\n skill_dir: \"string\",\n file_count: \"number.integer >= 0\",\n overwritten: \"boolean\",\n})\nexport type PartnerSkillInstallResult = typeof PartnerSkillInstallResult.infer\n\nexport function parsePartnerSkillInstallPayload(payload: unknown): PartnerSkillInstallPayload {\n const parsed = PartnerSkillInstallPayload.assert(payload)\n\n if (!parsed.files.some((file) => file.path === \"SKILL.md\")) {\n throw new Error(\"installSkill payload must include SKILL.md\")\n }\n\n return parsed\n}\n","import { create } from \"zustand\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\nfunction buildSignalId() {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID()\n }\n return `bridge-${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\nfunction clearSignalRecord<T>(\n record: Record<string, T[]>,\n sessionId: string,\n) {\n if (!(sessionId in record)) {\n return record\n }\n\n const next = { ...record }\n delete next[sessionId]\n return next\n}\n\nexport interface PendingContextSignal {\n id: string\n label: string\n content: string\n}\n\nexport interface DraftAppendSignal {\n id: string\n text: string\n}\n\nexport interface SendRequestSignal {\n id: string\n}\n\ninterface UiBridgeState extends ClientAwareState {\n pendingContexts: Record<string, PendingContextSignal[]>\n draftAppends: Record<string, DraftAppendSignal[]>\n sendRequests: Record<string, SendRequestSignal[]>\n\n addPendingContext: (sessionId: string, context: Omit<PendingContextSignal, \"id\">) => void\n removePendingContext: (sessionId: string, contextId: string) => void\n consumePendingContexts: (sessionId: string) => PendingContextSignal[]\n clearPendingContexts: (sessionId: string) => void\n\n addDraftAppend: (sessionId: string, text: string) => void\n consumeDraftAppends: (sessionId: string) => DraftAppendSignal[]\n clearDraftAppends: (sessionId: string) => void\n\n addSendRequest: (sessionId: string) => void\n consumeSendRequests: (sessionId: string) => SendRequestSignal[]\n clearSendRequests: (sessionId: string) => void\n\n clearSession: (sessionId: string) => void\n}\n\nexport const useUiBridgeStore = create<UiBridgeState>()((set, get) => ({\n ...createClientActions(set),\n pendingContexts: {},\n draftAppends: {},\n sendRequests: {},\n\n addPendingContext: (sessionId, context) =>\n set((state) => ({\n pendingContexts: {\n ...state.pendingContexts,\n [sessionId]: [\n ...(state.pendingContexts[sessionId] ?? []),\n {\n id: buildSignalId(),\n label: context.label,\n content: context.content,\n },\n ],\n },\n })),\n removePendingContext: (sessionId, contextId) =>\n set((state) => {\n const contexts = state.pendingContexts[sessionId]\n if (!contexts?.length) {\n return state\n }\n\n const nextContexts = contexts.filter((context) => context.id !== contextId)\n if (nextContexts.length === contexts.length) {\n return state\n }\n\n return {\n pendingContexts:\n nextContexts.length > 0\n ? {\n ...state.pendingContexts,\n [sessionId]: nextContexts,\n }\n : clearSignalRecord(state.pendingContexts, sessionId),\n }\n }),\n consumePendingContexts: (sessionId) => {\n const signals = get().pendingContexts[sessionId] ?? []\n if (signals.length === 0) return []\n set((state) => ({\n pendingContexts: clearSignalRecord(state.pendingContexts, sessionId),\n }))\n return signals\n },\n clearPendingContexts: (sessionId) =>\n set((state) => ({\n pendingContexts: clearSignalRecord(state.pendingContexts, sessionId),\n })),\n\n addDraftAppend: (sessionId, text) =>\n set((state) => ({\n draftAppends: {\n ...state.draftAppends,\n [sessionId]: [\n ...(state.draftAppends[sessionId] ?? []),\n {\n id: buildSignalId(),\n text,\n },\n ],\n },\n })),\n consumeDraftAppends: (sessionId) => {\n const signals = get().draftAppends[sessionId] ?? []\n if (signals.length === 0) return []\n set((state) => ({\n draftAppends: clearSignalRecord(state.draftAppends, sessionId),\n }))\n return signals\n },\n clearDraftAppends: (sessionId) =>\n set((state) => ({\n draftAppends: clearSignalRecord(state.draftAppends, sessionId),\n })),\n\n addSendRequest: (sessionId) =>\n set((state) => ({\n sendRequests: {\n ...state.sendRequests,\n [sessionId]: [\n ...(state.sendRequests[sessionId] ?? []),\n {\n id: buildSignalId(),\n },\n ],\n },\n })),\n consumeSendRequests: (sessionId) => {\n const signals = get().sendRequests[sessionId] ?? []\n if (signals.length === 0) return []\n set((state) => ({\n sendRequests: clearSignalRecord(state.sendRequests, sessionId),\n }))\n return signals\n },\n clearSendRequests: (sessionId) =>\n set((state) => ({\n sendRequests: clearSignalRecord(state.sendRequests, sessionId),\n })),\n\n clearSession: (sessionId) =>\n set((state) => ({\n pendingContexts: clearSignalRecord(state.pendingContexts, sessionId),\n draftAppends: clearSignalRecord(state.draftAppends, sessionId),\n sendRequests: clearSignalRecord(state.sendRequests, sessionId),\n })),\n}))\n","import type { StoreApi } from \"zustand\"\nimport type { BladeClient } from \"../../client\"\n\nexport interface ClientAwareState {\n _client: BladeClient | null\n setClient: (client: BladeClient) => void\n}\n\nexport function createClientActions<T extends ClientAwareState>(\n set: StoreApi<T>[\"setState\"],\n): ClientAwareState {\n return {\n _client: null,\n setClient: (client) => set({ _client: client } as Partial<T>),\n }\n}\n","import type {\n ChatMessage,\n FileContentPart,\n ImageUrlContentPart,\n MessageContent,\n MessageContentPart,\n} from \"../schemas/message\"\n\nconst SYSTEM_REMINDER_PATTERN = /^<system-reminder>\\s*[\\s\\S]*?\\s*<\\/system-reminder>$/i\nconst LEGACY_FORK_CONTEXT_MARKERS = [\n \"父智能体在派生你之前已经完成了一些操作。\",\n \"可通过 `read_file` 读取。\",\n \"# 你的任务\",\n]\n\nexport function normalizeMessageContent(content: unknown): MessageContent {\n if (typeof content === \"string\") return content\n if (Array.isArray(content)) return content as MessageContentPart[]\n return String(content ?? \"\")\n}\n\nexport function getTextContent(content: MessageContent): string {\n if (typeof content === \"string\") return content\n return content\n .filter((part) => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\")\n}\n\nexport function isSystemReminderText(text: string): boolean {\n return SYSTEM_REMINDER_PATTERN.test(text.trim())\n}\n\nexport function isLegacyForkContextText(text: string): boolean {\n const normalized = text.trim()\n return LEGACY_FORK_CONTEXT_MARKERS.every((marker) => normalized.includes(marker))\n}\n\nexport function isHiddenInternalMessage(message: Pick<ChatMessage, \"role\" | \"content\">): boolean {\n if (message.role !== \"user\") return false\n const text = getTextContent(normalizeMessageContent(message.content))\n return isSystemReminderText(text) || isLegacyForkContextText(text)\n}\n\nexport function buildMessageContent(\n text: string,\n attachments: Array<{\n kind: \"file\"\n name: string\n uploadedPath?: string\n textContent?: string | null\n mimeType?: string\n }>,\n): MessageContent {\n if (attachments.length === 0) return text\n const textSegments: string[] = []\n\n if (text.trim()) {\n textSegments.push(text)\n }\n\n for (const attachment of attachments) {\n let fileText: string\n if (attachment.uploadedPath) {\n const displayName = attachment.uploadedPath.split(\"/\").pop() || attachment.name\n fileText = `[附件: ${displayName}] 已上传到工作区路径: ${attachment.uploadedPath}`\n } else if (attachment.textContent) {\n const MAX_TEXT_CHARS = 100_000\n const content =\n attachment.textContent.length > MAX_TEXT_CHARS\n ? `${attachment.textContent.slice(0, MAX_TEXT_CHARS)}\\n\\n[内容已截断,原始共 ${attachment.textContent.length} 字符]`\n : attachment.textContent\n fileText = `[附件: ${attachment.name}]\\n${content}`\n } else {\n fileText = `[附件: ${attachment.name}]`\n }\n textSegments.push(fileText)\n }\n\n return textSegments.join(\"\\n\\n\")\n}\n\nexport function contentPreview(content: MessageContent, maxLen = 80): string {\n const text = getTextContent(content).trim()\n return text.length > maxLen ? `${text.slice(0, maxLen)}…` : text\n}\n\n/**\n * Transform a skill mention into a natural language skill invocation.\n * Strips the `<skill>name</skill>` tag from the raw input and prepends the instruction.\n */\nexport function transformSlashCommand(skillName: string, rawInput: string): string {\n const prompt = rawInput.replace(`<skill>${skillName}</skill>`, \"\").trim()\n return prompt\n ? `请使用 ${skillName} skill 完成任务\\n${prompt}`\n : `请使用 ${skillName} skill 完成任务`\n}\n\nconst ATTACHMENT_TAG_RE = /\\[附件:\\s*([^\\]]+)\\](?:[ \\t]*已上传到工作区路径:[ \\t]*([^\\r\\n]+))?/g\n// 来自 addContext 的 context 在发送时序列化为独立 text part:`[上下文: LABEL]\\nCONTENT`\n// 整块 part 匹配而非行内替换,避免破坏正文中可能出现的\"[上下文:\"字样\nconst CONTEXT_PART_RE = /^\\[上下文:\\s*([^\\]]+)\\]\\n([\\s\\S]*)$/\n\nexport interface ParsedTextAttachment {\n name: string\n uploadedPath?: string\n}\n\nexport interface ParsedTextContext {\n label: string\n content: string\n}\n\n/**\n * Extract `[附件: name]` and `[上下文: label]` tags from text parts and return\n * the cleaned text plus the collected attachment / context metadata. 每个 text\n * part 独立匹配,以避免跨 part 拼接打乱正则。\n */\nexport function extractTextAttachments(content: MessageContent): {\n cleanText: string\n attachments: ParsedTextAttachment[]\n contexts: ParsedTextContext[]\n} {\n if (typeof content === \"string\") {\n const attachments: ParsedTextAttachment[] = []\n const contexts: ParsedTextContext[] = []\n const ctxMatch = CONTEXT_PART_RE.exec(content)\n let working = content\n if (ctxMatch) {\n contexts.push({ label: ctxMatch[1].trim(), content: ctxMatch[2].trim() })\n working = \"\"\n }\n for (const match of working.matchAll(ATTACHMENT_TAG_RE)) {\n attachments.push({\n name: match[1].trim(),\n uploadedPath: match[2]?.trim() || undefined,\n })\n }\n return {\n cleanText: working.replace(ATTACHMENT_TAG_RE, \"\").trim(),\n attachments,\n contexts,\n }\n }\n\n const attachments: ParsedTextAttachment[] = []\n const contexts: ParsedTextContext[] = []\n const cleanParts: string[] = []\n\n for (const part of content) {\n if (part.type !== \"text\") continue\n\n const ctxMatch = CONTEXT_PART_RE.exec(part.text)\n if (ctxMatch) {\n contexts.push({ label: ctxMatch[1].trim(), content: ctxMatch[2].trim() })\n continue\n }\n\n let hasAttachment = false\n for (const match of part.text.matchAll(ATTACHMENT_TAG_RE)) {\n attachments.push({\n name: match[1].trim(),\n uploadedPath: match[2]?.trim() || undefined,\n })\n hasAttachment = true\n }\n if (hasAttachment) {\n const remaining = part.text.replace(ATTACHMENT_TAG_RE, \"\").trim()\n if (remaining) cleanParts.push(remaining)\n } else {\n cleanParts.push(part.text)\n }\n }\n\n return { cleanText: cleanParts.join(\"\\n\").trim(), attachments, contexts }\n}\n\nexport function getImageParts(content: MessageContent): ImageUrlContentPart[] {\n if (typeof content === \"string\") return []\n return content.filter((part): part is ImageUrlContentPart => part.type === \"image_url\")\n}\n\nexport function getFileParts(content: MessageContent): FileContentPart[] {\n if (typeof content === \"string\") return []\n return content.filter((part): part is FileContentPart => part.type === \"file\")\n}\n\ninterface MessageGroup {\n root: ChatMessage | null\n childLoops: { loopName: string; messages: ChatMessage[] }[]\n}\n\nexport function groupMessagesByLoop(messages: ChatMessage[]): MessageGroup[] {\n const groups: MessageGroup[] = []\n let currentGroup: MessageGroup | null = null\n\n for (const msg of messages) {\n const loop = msg.loop_name ?? \"root\"\n\n if (loop === \"root\") {\n currentGroup = { root: msg, childLoops: [] }\n groups.push(currentGroup)\n } else {\n if (!currentGroup) {\n currentGroup = { root: null, childLoops: [] }\n groups.push(currentGroup)\n }\n const existingLoop = currentGroup.childLoops.find((l) => l.loopName === loop)\n if (existingLoop) {\n existingLoop.messages.push(msg)\n } else {\n currentGroup.childLoops.push({ loopName: loop, messages: [msg] })\n }\n }\n }\n\n return groups\n}\n","export interface UiMeta {\n resourceUri?: string\n resourceURI?: string\n resourceHTML?: string\n target: \"inline\" | \"preview\"\n height: number\n title?: string\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0\n}\n\nfunction isPositiveNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0\n}\n\nexport function isUiMeta(value: unknown): value is UiMeta {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return false\n }\n const raw = value as Record<string, unknown>\n if (raw.target !== \"inline\" && raw.target !== \"preview\") {\n return false\n }\n if (!isPositiveNumber(raw.height)) {\n return false\n }\n if (\n !isNonEmptyString(raw.resourceHTML) &&\n !isNonEmptyString(raw.resourceUri) &&\n !isNonEmptyString(raw.resourceURI)\n ) {\n return false\n }\n if (raw.title != null && !isNonEmptyString(raw.title)) {\n return false\n }\n return true\n}\n","import { create } from \"zustand\"\nimport { createJSONStorage, persist } from \"zustand/middleware\"\nimport * as authApi from \"../api/auth\"\nimport { agentSocket } from \"../sockets/socket-state\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\nimport { useSessionStore } from \"./session-store\"\n\ninterface User {\n id: string\n username: string\n display_name?: string | null\n avatar_url: string | null\n is_admin?: boolean\n}\n\ninterface AuthState extends ClientAwareState {\n token: string | null\n socketAuthToken: string | null\n user: User | null\n loading: boolean\n error: string | null\n\n logout: () => void\n checkAuth: () => Promise<void>\n hydrateFromCookie: () => Promise<void>\n}\n\ntype PersistedAuthState = Pick<AuthState, \"token\" | \"user\">\n\nconst noopStorage = {\n getItem: () => null,\n setItem: () => {},\n removeItem: () => {},\n}\n\nfunction shouldClearPersistedAuthState(value: string): boolean {\n try {\n const parsed = JSON.parse(value) as {\n state?: PersistedAuthState | null\n }\n return parsed.state?.token == null && parsed.state?.user == null\n } catch {\n return false\n }\n}\n\nconst authStorage = createJSONStorage<PersistedAuthState>(() => {\n if (typeof localStorage === \"undefined\") {\n return noopStorage\n }\n\n return {\n getItem: (name) => localStorage.getItem(name),\n setItem: (name, value) => {\n if (shouldClearPersistedAuthState(value)) {\n localStorage.removeItem(name)\n return\n }\n\n localStorage.setItem(name, value)\n },\n removeItem: (name) => localStorage.removeItem(name),\n }\n})\n\nfunction finishAuth(set: (partial: Partial<AuthState>) => void, payload: { token: string; user: User }) {\n set({\n token: payload.token,\n socketAuthToken: null,\n user: payload.user,\n loading: false,\n error: null,\n })\n agentSocket?.reconnect()\n useSessionStore\n .getState()\n .fetchSessions()\n .catch(() => {})\n}\n\nfunction finishCookieHydration(\n set: (partial: Partial<AuthState>) => void,\n payload: { token: string; user: User },\n) {\n set({\n token: null,\n socketAuthToken: payload.token,\n user: payload.user,\n loading: false,\n error: null,\n })\n agentSocket?.reconnect()\n useSessionStore\n .getState()\n .fetchSessions()\n .catch(() => {})\n}\n\nfunction toUser(info: authApi.UserInfo): User {\n return {\n id: info.id,\n username: info.username,\n display_name: info.display_name,\n avatar_url: info.avatar_url,\n is_admin: info.is_admin,\n }\n}\n\nexport const useAuthStore = create<AuthState>()(\n persist(\n (set, get) => ({\n ...createClientActions(set),\n token: null,\n socketAuthToken: null,\n user: null,\n loading: false,\n error: null,\n\n logout: () => {\n void authApi.logout().catch(() => {})\n set({ token: null, socketAuthToken: null, user: null, error: null })\n agentSocket?.disconnect()\n useSessionStore.getState().reset()\n },\n\n checkAuth: async () => {\n const token = get().token\n if (!token) return\n try {\n const auth = await authApi.getMe()\n finishAuth(set, { token: auth.token, user: toUser(auth) })\n } catch {\n set({ token: null, socketAuthToken: null, user: null, error: null })\n useSessionStore.getState().reset()\n }\n },\n\n hydrateFromCookie: async () => {\n set({ loading: true, error: null })\n const previousToken = get().token\n\n if (previousToken) {\n try {\n const auth = await authApi.getMe()\n finishAuth(set, { token: auth.token, user: toUser(auth) })\n return\n } catch {\n set({ token: null, socketAuthToken: null, user: null, error: null })\n useSessionStore.getState().reset()\n }\n }\n\n try {\n const auth = await authApi.getMe()\n finishCookieHydration(set, { token: auth.token, user: toUser(auth) })\n } catch {\n set({\n token: null,\n socketAuthToken: null,\n user: null,\n loading: false,\n error: null,\n })\n useSessionStore.getState().reset()\n }\n },\n }),\n {\n name: \"agent-auth\",\n storage: authStorage,\n partialize: (state) => {\n if (state.socketAuthToken) {\n return { token: null, user: null }\n }\n return { token: state.token, user: state.user }\n },\n },\n ),\n)\n","import type { AuthResource } from \"../../client/resources/auth\"\nimport { getClient } from \"./client\"\nexport type * from \"../../client/resources/auth\"\n\nconst r = (): AuthResource => getClient().auth\nexport const getProviders = (...args: Parameters<AuthResource[\"getProviders\"]>) => r().getProviders(...args)\nexport const getMe = (...args: Parameters<AuthResource[\"getMe\"]>) => r().getMe(...args)\nexport const logout = (...args: Parameters<AuthResource[\"logout\"]>) => r().logout(...args)\n","import type { AgentSocket } from \"./event-bridge\"\n\nexport let agentSocket: AgentSocket | null = null\n\nexport function setAgentSocket(nextSocket: AgentSocket | null): void {\n agentSocket = nextSocket\n}\n\nexport function getSocket(): AgentSocket {\n if (!agentSocket) throw new Error(\"Socket not initialized. Call bootstrapBladeClient() first.\")\n return agentSocket\n}\n","import { create } from \"zustand\"\nimport * as sessionsApi from \"../api/sessions\"\nimport type { AskUserAnswerData } from \"../components/chat/AskUserQuestionBlock\"\nimport type { ContentBlock, TurnProjection } from \"../schemas/projection\"\nimport type { ModeId, SessionInfo, SessionStatus } from \"../schemas/session\"\nimport { setChatStoreSessionAccessor, useChatStore } from \"./chat-store\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\nimport { useTaskStore } from \"./task-store\"\n\nexport type SessionMode = ModeId\nconst DEFAULT_SESSION_MODE: SessionMode = \"executing\"\n\ntype QueryClientLike = {\n invalidateQueries: (options: { queryKey: readonly unknown[] }) => Promise<unknown>\n}\n\ninterface SessionState extends ClientAwareState {\n sessions: SessionInfo[]\n activeSessionId: string | null\n loading: boolean\n modes: Record<string, SessionMode>\n rewindDrafts: Record<string, string>\n /** Session IDs that were just created and haven't had their first history load yet */\n _freshSessions: Set<string>\n\n fetchSessions: () => Promise<void>\n createSession: (intent?: string) => Promise<string>\n registerCreatedSession: (session: SessionInfo, mode?: SessionMode) => void\n upsertSession: (session: SessionInfo) => void\n isFreshSession: (sessionId: string) => boolean\n setActiveSession: (id: string) => void\n clearActiveSession: () => void\n deleteSession: (id: string) => Promise<void>\n updateSessionStatus: (sessionId: string, status: SessionStatus) => void\n updateSessionIntent: (sessionId: string, intent: string) => void\n /**\n * ship-attack v2:通用增量更新 session 字段。用于 session:updated 等事件\n * 广播\"后端刚改了 session 元数据(intent / bound_skill_id / ...)\"时,\n * 前端原地 patch sessions 缓存,而不需要重新拉整个列表。\n */\n patchSession: (sessionId: string, patch: Partial<SessionInfo>) => void\n pinSession: (sessionId: string, pinned: boolean) => Promise<void>\n setRewindDraft: (sessionId: string, text: string | null) => void\n setMode: (sessionId: string, mode: SessionMode) => void\n togglePlanningMode: (sessionId: string) => void\n toggleSharing: (sessionId: string) => Promise<void>\n reset: () => void\n}\n\n// agentSocket will be set after socket init -- see socket.ts\nlet onSessionChange: ((sessionId: string | null) => void) | null = null\nexport function setOnSessionChange(fn: (sessionId: string | null) => void) {\n onSessionChange = fn\n}\n\n/** 侧栏等使用 React Query 拉取会话列表时,与 zustand patch 同步失效。 */\nexport function invalidateHomeSidebarSessions() {\n const queryClient = (globalThis as { __agentQueryClient?: QueryClientLike }).__agentQueryClient\n if (!queryClient) return\n void queryClient.invalidateQueries({ queryKey: [\"sessions\", \"home-sidebar\"] })\n}\n\nfunction isSessionAccessRevoked(error: unknown) {\n if (error instanceof Error) {\n const msg = error.message\n return msg.includes(\"API 403\") || msg.includes(\"API 404\")\n }\n return false\n}\n\nfunction removeSessionArtifacts(sessionId: string) {\n useChatStore.getState().clearMessages(sessionId)\n useTaskStore.getState().setTasks(sessionId, [])\n}\n\nfunction pruneSessionState(state: SessionState, sessionId: string) {\n const nextFresh = new Set(state._freshSessions)\n nextFresh.delete(sessionId)\n const { [sessionId]: _mode, ...modes } = state.modes\n const { [sessionId]: _rewindDraft, ...rewindDrafts } = state.rewindDrafts\n\n return {\n sessions: state.sessions.filter((session) => session.id !== sessionId),\n activeSessionId: state.activeSessionId === sessionId ? null : state.activeSessionId,\n modes,\n rewindDrafts,\n _freshSessions: nextFresh,\n }\n}\n\nfunction navigateAwayFromSession(sessionId: string) {\n if (typeof window === \"undefined\") return\n if (window.location.pathname !== `/chat/${sessionId}`) return\n window.history.replaceState(window.history.state, \"\", \"/chat\")\n window.dispatchEvent(new PopStateEvent(\"popstate\"))\n}\n\nfunction handleUnreadableSession(\n set: (\n partial:\n | Partial<SessionState>\n | ((state: SessionState) => Partial<SessionState> | SessionState),\n ) => void,\n get: () => SessionState,\n sessionId: string,\n) {\n const wasActive = get().activeSessionId === sessionId\n removeSessionArtifacts(sessionId)\n set((state) => pruneSessionState(state, sessionId))\n if (wasActive) {\n onSessionChange?.(null)\n navigateAwayFromSession(sessionId)\n }\n invalidateHomeSidebarSessions()\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n\nfunction extractModeFromBlocks(blocks: ContentBlock[]): SessionMode | null {\n const modeBlock = blocks.find((block) => block.type === \"mode_change\")\n if (\n modeBlock &&\n isPlainRecord(modeBlock.content) &&\n (modeBlock.content.to === \"planning\" || modeBlock.content.to === \"executing\")\n ) {\n return modeBlock.content.to\n }\n if (blocks.some((block) => block.type === \"planning_enter\")) return \"planning\"\n if (blocks.some((block) => block.type === \"planning_exit\")) return \"executing\"\n return null\n}\n\nfunction toSelectionMap(value: unknown): Record<number, number[]> {\n if (!isPlainRecord(value)) return {}\n const entries = Object.entries(value)\n .map(([questionKey, optionIndexes]) => {\n if (!Array.isArray(optionIndexes)) return null\n const parsedIndexes = optionIndexes\n .map((item) => (typeof item === \"number\" ? item : Number(item)))\n .filter((item) => Number.isInteger(item))\n return [Number(questionKey), parsedIndexes] as const\n })\n .filter((entry): entry is readonly [number, number[]] => entry !== null)\n return Object.fromEntries(entries) as Record<number, number[]>\n}\n\nfunction toCustomMap(value: unknown): Record<number, string> {\n if (!isPlainRecord(value)) return {}\n const entries = Object.entries(value)\n .filter(([, text]) => typeof text === \"string\")\n .map(([questionKey, text]) => [Number(questionKey), text] as const)\n return Object.fromEntries(entries) as Record<number, string>\n}\n\nfunction extractAskAnswers(turns: TurnProjection[]): Record<string, AskUserAnswerData> {\n const answers: Record<string, AskUserAnswerData> = {}\n\n for (const turn of turns) {\n for (const block of turn.blocks) {\n if (block.type !== \"ask_user_answer\" || typeof block.tool_call_id !== \"string\") continue\n answers[block.tool_call_id] = {\n selections: toSelectionMap(isPlainRecord(block.content) ? block.content.selections : undefined),\n custom: toCustomMap(isPlainRecord(block.content) ? block.content.custom : undefined),\n }\n }\n }\n\n return answers\n}\n\nfunction registerCreatedSessionState(\n set: (\n partial:\n | Partial<SessionState>\n | ((state: SessionState) => Partial<SessionState> | SessionState),\n ) => void,\n session: SessionInfo,\n mode: SessionMode = DEFAULT_SESSION_MODE,\n) {\n useChatStore.getState().setTurns(session.id, [])\n useTaskStore.getState().setTasks(session.id, [])\n set((state) => ({\n sessions: [session, ...state.sessions.filter((item) => item.id !== session.id)],\n modes: { ...state.modes, [session.id]: state.modes[session.id] ?? mode },\n _freshSessions: new Set(state._freshSessions).add(session.id),\n }))\n}\n\nasync function revalidateViewerSessions(existingSessions: SessionInfo[]): Promise<SessionInfo[]> {\n const viewerSessions = existingSessions.filter((session) => session.viewer_role === \"viewer\")\n if (viewerSessions.length === 0) {\n return []\n }\n const refreshed = await Promise.all(\n viewerSessions.map(async (session) => {\n try {\n return await sessionsApi.getSession(session.id)\n } catch (error) {\n if (isSessionAccessRevoked(error)) {\n return null\n }\n return session\n }\n }),\n )\n return refreshed.filter((session): session is SessionInfo => session !== null)\n}\n\nexport const useSessionStore = create<SessionState>()((set, get) => ({\n ...createClientActions(set),\n sessions: [],\n activeSessionId: null,\n loading: false,\n modes: {},\n rewindDrafts: {},\n _freshSessions: new Set(),\n\n fetchSessions: async () => {\n set({ loading: true })\n try {\n const currentState = get()\n const [sessions, viewerSessions] = await Promise.all([\n sessionsApi.listSessions(),\n revalidateViewerSessions(currentState.sessions),\n ])\n const knownSessionIds = new Set(sessions.map((session) => session.id))\n const mergedSessions = [\n ...sessions,\n ...viewerSessions.filter((session) => !knownSessionIds.has(session.id)),\n ]\n const removedSessionIds = currentState.sessions\n .filter(\n (session) =>\n session.viewer_role === \"viewer\" &&\n !mergedSessions.some((candidate) => candidate.id === session.id),\n )\n .map((session) => session.id)\n const activeSessionId = currentState.activeSessionId\n\n for (const sessionId of removedSessionIds) {\n removeSessionArtifacts(sessionId)\n }\n\n let nextState: Partial<SessionState> = {\n sessions: mergedSessions,\n activeSessionId: currentState.activeSessionId,\n loading: false,\n modes: currentState.modes,\n rewindDrafts: currentState.rewindDrafts,\n _freshSessions: currentState._freshSessions,\n }\n for (const sessionId of removedSessionIds) {\n nextState = {\n ...nextState,\n ...pruneSessionState(\n {\n ...currentState,\n sessions: nextState.sessions ?? currentState.sessions,\n activeSessionId: nextState.activeSessionId ?? currentState.activeSessionId,\n loading: nextState.loading ?? currentState.loading,\n modes: nextState.modes ?? currentState.modes,\n rewindDrafts: nextState.rewindDrafts ?? currentState.rewindDrafts,\n _freshSessions: nextState._freshSessions ?? currentState._freshSessions,\n },\n sessionId,\n ),\n }\n }\n set(nextState)\n\n if (activeSessionId && removedSessionIds.includes(activeSessionId)) {\n onSessionChange?.(null)\n navigateAwayFromSession(activeSessionId)\n }\n\n invalidateHomeSidebarSessions()\n } catch (error) {\n set({ loading: false })\n throw error\n }\n },\n\n createSession: async (intent) => {\n const { session_id } = await sessionsApi.createSession(intent)\n const now = new Date().toISOString()\n get().registerCreatedSession({\n id: session_id,\n intent: intent ?? \"\",\n status: \"created\" as const,\n created_at: now,\n updated_at: now,\n })\n get().setActiveSession(session_id)\n invalidateHomeSidebarSessions()\n get()\n .fetchSessions()\n .catch(() => {})\n return session_id\n },\n\n registerCreatedSession: (session, mode = DEFAULT_SESSION_MODE) => {\n registerCreatedSessionState(set, session, mode)\n },\n\n upsertSession: (session) => {\n set((state) => ({\n sessions: state.sessions.some((item) => item.id === session.id)\n ? state.sessions.map((item) => (item.id === session.id ? { ...item, ...session } : item))\n : [session, ...state.sessions],\n }))\n },\n\n isFreshSession: (sessionId) => get()._freshSessions.has(sessionId),\n\n setActiveSession: (id) => {\n set({ activeSessionId: id })\n onSessionChange?.(id)\n // 切换 session 时后端可能已经走完了一轮对话但前端没订阅,sessions 列表里的\n // status 还停留在旧值(running/waiting_for_input 混乱)。异步拉一次 detail\n // 合回 sessions 数组,保证 AskUser 卡片的 answered 判定正确。\n sessionsApi\n .getSession(id)\n .then((detail) => {\n set((state) => ({\n sessions: state.sessions.some((s) => s.id === id)\n ? state.sessions.map((s) =>\n s.id === id\n ? { ...s, status: detail.status, updated_at: detail.updated_at }\n : s,\n )\n : [detail, ...state.sessions],\n }))\n })\n .catch(() => {})\n const tasksPromise = sessionsApi.getSessionTasks(id).catch(() => null)\n Promise.all([sessionsApi.getSessionTurns(id), tasksPromise])\n .then(([turns, tasks]) => {\n if (tasks) useTaskStore.getState().setTasks(id, tasks)\n useChatStore.getState().setAskAnswers(id, extractAskAnswers(turns))\n let inferredMode: SessionMode = DEFAULT_SESSION_MODE\n for (const turn of turns) {\n const nextMode = extractModeFromBlocks(turn.blocks)\n if (nextMode) {\n inferredMode = nextMode\n }\n }\n set((state) => {\n if (state.modes[id] == null) {\n return { modes: { ...state.modes, [id]: inferredMode } }\n }\n return state\n })\n\n // Skip overwriting messages for fresh sessions — the optimistic seed is\n // correct and the history response is empty; overwriting would erase any\n // message that was sent between create and this response arriving.\n const isFresh = get()._freshSessions.has(id)\n if (isFresh) {\n set((state) => {\n const next = new Set(state._freshSessions)\n next.delete(id)\n return { _freshSessions: next }\n })\n // Only apply if history is non-empty (e.g. resumed session)\n if (turns.length > 0) {\n useChatStore.getState().setTurns(id, turns)\n }\n } else {\n useChatStore.getState().setTurns(id, turns)\n }\n })\n .catch((error) => {\n if (isSessionAccessRevoked(error)) {\n handleUnreadableSession(set, get, id)\n return\n }\n console.error(\"Failed to load session data\", error)\n })\n },\n\n clearActiveSession: () => {\n set({ activeSessionId: null })\n onSessionChange?.(null)\n },\n\n deleteSession: async (id) => {\n await sessionsApi.deleteSession(id)\n invalidateHomeSidebarSessions()\n const wasActive = get().activeSessionId === id\n await get().fetchSessions()\n if (!wasActive) {\n return\n }\n\n const nextId = get().sessions[0]?.id ?? null\n if (nextId) {\n get().setActiveSession(nextId)\n return\n }\n\n get().clearActiveSession()\n },\n\n updateSessionStatus: (sessionId, status) => {\n set((state) => ({\n sessions: state.sessions.map((s) => (s.id === sessionId ? { ...s, status } : s)),\n }))\n // Only reset streaming on terminal statuses to avoid race conditions\n // where a stale status update from a previous run could cancel streaming\n // for a new in-flight run. The primary streaming reset is via chat:end;\n // this serves as a fallback for missed chat:end events.\n if (status === \"failed\" || status === \"interrupted\") {\n useChatStore.getState().setStreaming(sessionId, false)\n if (status === \"interrupted\") {\n useChatStore.getState().markInterrupted(sessionId)\n } else {\n useChatStore.getState().markFailed(sessionId)\n }\n }\n },\n\n updateSessionIntent: (sessionId, intent) => {\n set((state) => ({\n sessions: state.sessions.map((s) => (s.id === sessionId ? { ...s, intent } : s)),\n }))\n },\n\n patchSession: (sessionId, patch) => {\n set((state) => ({\n sessions: state.sessions.map((s) =>\n s.id === sessionId ? { ...s, ...patch } : s,\n ),\n }))\n },\n\n pinSession: async (sessionId, pinned) => {\n const updated = await sessionsApi.pinSession(sessionId, pinned)\n set((state) => ({\n sessions: state.sessions.map((s) =>\n s.id === sessionId\n ? { ...s, is_pinned: updated.is_pinned, pinned_at: updated.pinned_at }\n : s,\n ),\n }))\n invalidateHomeSidebarSessions()\n },\n\n setRewindDraft: (sessionId, text) => {\n set((state) => {\n if (text == null) {\n const { [sessionId]: _, ...rest } = state.rewindDrafts\n return { rewindDrafts: rest }\n }\n\n return {\n rewindDrafts: {\n ...state.rewindDrafts,\n [sessionId]: text,\n },\n }\n })\n },\n\n setMode: (sessionId, mode) => {\n set((state) => ({ modes: { ...state.modes, [sessionId]: mode } }))\n },\n\n togglePlanningMode: (sessionId) => {\n const current = get().modes[sessionId] ?? DEFAULT_SESSION_MODE\n const next = current === \"executing\" ? \"planning\" : \"executing\"\n set((state) => ({ modes: { ...state.modes, [sessionId]: next } }))\n },\n\n toggleSharing: async (sessionId) => {\n const session = get().sessions.find((s) => s.id === sessionId)\n if (!session) return\n const newShared = !session.shared\n const result = await sessionsApi.updateSharing(sessionId, newShared)\n set((state) => ({\n sessions: state.sessions.map((s) =>\n s.id === sessionId ? { ...s, shared: result.shared } : s,\n ),\n }))\n },\n\n reset: () => {\n set({\n sessions: [],\n activeSessionId: null,\n loading: false,\n modes: {},\n rewindDrafts: {},\n _freshSessions: new Set(),\n })\n invalidateHomeSidebarSessions()\n },\n}))\n\n// Wire up lazy accessor to break circular dependency with chat-store\nsetChatStoreSessionAccessor(() => useSessionStore.getState().activeSessionId)\n","import { create } from \"zustand\"\nimport type { AskUserAnswerData } from \"../components/chat/AskUserQuestionBlock\"\nimport { formatToolName } from \"../components/chat/display-utils\"\nimport { normalizeMessageContent } from \"../lib/chat\"\nimport type { ChatMessage, CompactionInfo, MessageContent, ToolCallInfo } from \"../schemas/message\"\nimport type { ContentBlock, PatchEnvelope, TurnProjection } from \"../schemas/projection\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\nimport { useUiStore } from \"./ui-store\"\n\nlet _getActiveSessionId: (() => string | null) | null = null\n\nexport function setChatStoreSessionAccessor(fn: () => string | null) {\n _getActiveSessionId = fn\n}\n\nexport interface AgentLoopInfo {\n toolCallId: string\n description: string\n status: \"running\" | \"done\" | \"error\" | \"cancelled\" | \"awaiting_answer\"\n}\n\nexport interface ActiveCompactionState extends CompactionInfo {\n turn_id: string\n status: \"streaming\" | \"completed\" | \"paused\" | \"failed\" | \"interrupted\"\n}\n\ninterface ChatState extends ClientAwareState {\n turns: Record<string, TurnProjection[]>\n messages: Record<string, ChatMessage[]>\n askAnswers: Record<string, Record<string, AskUserAnswerData>>\n isStreaming: Record<string, boolean>\n agentLoops: Record<string, Record<string, AgentLoopInfo>>\n activeCompactions: Record<string, ActiveCompactionState | null>\n\n addUserMessage: (sessionId: string, content: MessageContent) => void\n setTurns: (sessionId: string, turns: TurnProjection[]) => void\n upsertTurn: (sessionId: string, turn: TurnProjection) => void\n applyTurnPatch: (sessionId: string, patch: PatchEnvelope) => void\n addErrorMessage: (sessionId: string, content: string) => void\n markInterrupted: (sessionId: string) => void\n markFailed: (sessionId: string) => void\n setStreaming: (sessionId: string, streaming: boolean) => void\n setAskAnswers: (sessionId: string, answers: Record<string, AskUserAnswerData>) => void\n clearMessages: (sessionId: string) => void\n}\n\nfunction parseAgentDescription(argumentsJson: string): string {\n try {\n return JSON.parse(argumentsJson)?.description ?? \"子智能体\"\n } catch {\n return \"子智能体\"\n }\n}\n\nfunction inferLoopStatusFromMessages(messages: ChatMessage[]): AgentLoopInfo[\"status\"] {\n const toolCalls = messages.flatMap((message) => message.tool_calls ?? [])\n if (messages.some((message) => message.status === \"streaming\")) return \"running\"\n if (toolCalls.some((toolCall) => toolCall.status === \"awaiting_answer\")) return \"awaiting_answer\"\n if (toolCalls.some((toolCall) => toolCall.status === \"error\")) return \"error\"\n if (toolCalls.some((toolCall) => toolCall.status === \"cancelled\")) return \"cancelled\"\n if (toolCalls.some((toolCall) => toolCall.status === \"pending\")) return \"running\"\n return \"done\"\n}\n\nfunction inferLoopStatusFromTurns(turns: TurnProjection[], messages: ChatMessage[]): AgentLoopInfo[\"status\"] {\n const latestAgentNotification = [...turns]\n .reverse()\n .flatMap((turn) => turn.blocks)\n .find((block) => {\n if (block.type !== \"system_notification\" || !isRecord(block.content)) return false\n return block.content.notification_type === \"agent:start\" || block.content.notification_type === \"agent:end\"\n })\n if (\n latestAgentNotification?.type === \"system_notification\" &&\n isRecord(latestAgentNotification.content)\n ) {\n const notificationType = latestAgentNotification.content.notification_type\n const status = latestAgentNotification.content.status\n if (notificationType === \"agent:start\" || status === \"running\") return \"running\"\n if (status === \"error\") return \"error\"\n if (status === \"cancelled\") return \"cancelled\"\n }\n return inferLoopStatusFromMessages(messages)\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n\nfunction parentForkToolCallIdFromTurn(turn: TurnProjection): string | null {\n if (typeof turn.parent_fork_tool_call_id === \"string\" && turn.parent_fork_tool_call_id.length > 0) {\n return turn.parent_fork_tool_call_id\n }\n for (const block of turn.blocks) {\n if (block.type !== \"system_notification\" || !isRecord(block.content)) continue\n const metadata = block.content.metadata\n if (!isRecord(metadata)) continue\n const parentId = metadata.parent_fork_tool_call_id\n if (typeof parentId === \"string\" && parentId.length > 0) return parentId\n }\n return null\n}\n\nfunction buildMessageContent(turn: TurnProjection): MessageContent {\n const textBlocks = turn.blocks.filter((block) => block.type === \"text\")\n if (textBlocks.length === 0) return \"\"\n if (textBlocks.length === 1) return normalizeMessageContent(textBlocks[0].content)\n return textBlocks\n .map((block) => {\n if (typeof block.content === \"string\") return block.content\n return JSON.stringify(block.content)\n })\n .join(\"\")\n}\n\nfunction buildReasoning(turn: TurnProjection): string | undefined {\n const thinking = turn.blocks\n .filter((block) => block.type === \"thinking\")\n .map((block) => (typeof block.content === \"string\" ? block.content : JSON.stringify(block.content)))\n .filter(Boolean)\n if (thinking.length === 0) return undefined\n return thinking.join(\"\\n\\n\")\n}\n\nfunction isModeChangeContent(value: unknown): value is { from: string; to: string } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { from?: unknown }).from === \"string\" &&\n typeof (value as { to?: unknown }).to === \"string\"\n )\n}\n\nfunction extractModeFromBlocks(blocks: ContentBlock[]): \"planning\" | \"executing\" | null {\n const modeBlock = blocks.find((block) => block.type === \"mode_change\")\n if (modeBlock && isModeChangeContent(modeBlock.content)) {\n return modeBlock.content.to === \"planning\" || modeBlock.content.to === \"executing\"\n ? modeBlock.content.to\n : null\n }\n if (blocks.some((block) => block.type === \"planning_enter\")) return \"planning\"\n if (blocks.some((block) => block.type === \"planning_exit\")) return \"executing\"\n return null\n}\n\nfunction projectionToMessage(turn: TurnProjection): ChatMessage | null {\n if (turn.kind === \"compaction\" && turn.compaction_id) {\n return {\n role: \"assistant\",\n content: turn.summary_preview ?? \"\",\n kind: \"compaction\",\n loop_name: turn.loop_id,\n entry_id: turn.turn_id,\n status: turn.status,\n compaction: {\n compaction_id: turn.compaction_id,\n summary_preview: turn.summary_preview,\n summary_full: turn.summary_full,\n archived_count: turn.archived_count,\n archived_files: turn.archived_files,\n archived_tool_calls: turn.archived_tool_calls,\n tokens_before: turn.tokens_before,\n tokens_after: turn.tokens_after,\n saved_ratio: turn.saved_ratio,\n trigger: turn.trigger,\n failure_reason: turn.failure_reason,\n fallback_applied: turn.fallback_applied,\n },\n }\n }\n\n const planningBlock = turn.blocks.find(\n (block) =>\n block.type === \"mode_change\" ||\n block.type === \"planning_enter\" ||\n block.type === \"planning_exit\" ||\n block.type === \"plan_status\",\n )\n if (planningBlock) {\n if (planningBlock.type === \"plan_status\") {\n return {\n role: \"tool\",\n content:\n typeof planningBlock.content === \"string\"\n ? planningBlock.content\n : JSON.stringify(planningBlock.content ?? {}, null, 2),\n kind: \"plan_status\",\n loop_name: turn.loop_id,\n entry_id: turn.turn_id,\n status: turn.status,\n }\n }\n return {\n role: \"assistant\",\n content:\n planningBlock.type === \"mode_change\"\n ? typeof planningBlock.content === \"string\"\n ? planningBlock.content\n : JSON.stringify(planningBlock.content ?? {})\n : \"\",\n kind: planningBlock.type,\n loop_name: turn.loop_id,\n entry_id: turn.turn_id,\n status: turn.status,\n }\n }\n\n if (turn.blocks.some((block) => block.type === \"ask_user_answer\")) {\n return null\n }\n\n const content = buildMessageContent(turn)\n const reasoning = buildReasoning(turn)\n const toolCalls: ToolCallInfo[] | undefined =\n turn.tool_calls.length > 0\n ? turn.tool_calls.map((toolCall) => ({\n id: toolCall.id,\n name: toolCall.tool_name,\n display_name: toolCall.display_name,\n arguments: toolCall.arguments,\n result: toolCall.result ?? undefined,\n pending_question_ref: toolCall.pending_question_ref ?? undefined,\n status:\n toolCall.status === \"pending\" ||\n toolCall.status === \"awaiting_answer\" ||\n toolCall.status === \"done\" ||\n toolCall.status === \"error\" ||\n toolCall.status === \"cancelled\"\n ? toolCall.status\n : \"pending\",\n ...(typeof toolCall.duration_ms === \"number\" ? { duration_ms: toolCall.duration_ms } : {}),\n }))\n : undefined\n\n if (turn.role === \"system\" && !content && !toolCalls?.length) {\n return null\n }\n\n return {\n role: turn.role === \"system\" ? \"assistant\" : turn.role,\n content,\n blocks: turn.blocks,\n ...(reasoning ? { reasoning } : {}),\n ...(toolCalls ? { tool_calls: toolCalls } : {}),\n loop_name: turn.loop_id,\n entry_id: turn.turn_id,\n status: turn.status,\n ...(typeof turn.duration_ms === \"number\" ? { duration_ms: turn.duration_ms } : {}),\n ...(turn.started_at ? { timestamp: turn.started_at } : {}),\n ...(turn.memory_refs?.length ? { memory_refs: turn.memory_refs } : {}),\n }\n}\n\nfunction rebuildAgentLoops(turns: TurnProjection[]): Record<string, AgentLoopInfo> {\n const messages = turns.map(projectionToMessage).filter(Boolean) as ChatMessage[]\n const childLoopNames = [...new Set(turns.map((turn) => turn.loop_id).filter((name) => name !== \"root\"))]\n if (childLoopNames.length === 0) return {}\n\n const agentToolCalls = messages\n .filter((message) => message.role === \"assistant\" && (message.loop_name ?? \"root\") === \"root\")\n .flatMap((message) => message.tool_calls ?? [])\n .filter((toolCall) => formatToolName(toolCall.name) === \"Agent\")\n\n const loops: Record<string, AgentLoopInfo> = {}\n const agentToolCallsById = new Map(agentToolCalls.map((toolCall) => [toolCall.id, toolCall]))\n const explicitParentToolCallIds = new Set(\n turns.map(parentForkToolCallIdFromTurn).filter((id): id is string => id !== null),\n )\n const usedToolCallIds = new Set<string>()\n for (const loopName of childLoopNames) {\n const loopMessages = messages.filter((message) => (message.loop_name ?? \"root\") === loopName)\n const loopTurns = turns.filter((turn) => turn.loop_id === loopName)\n const parentToolCallId = loopTurns.map(parentForkToolCallIdFromTurn).find(Boolean)\n const explicitToolCall =\n parentToolCallId && !usedToolCallIds.has(parentToolCallId)\n ? (agentToolCallsById.get(parentToolCallId) ?? null)\n : null\n const fallbackToolCall =\n explicitToolCall === null\n ? agentToolCalls.find(\n (toolCall) => !usedToolCallIds.has(toolCall.id) && !explicitParentToolCallIds.has(toolCall.id),\n )\n : null\n const toolCall = explicitToolCall ?? fallbackToolCall\n if (!toolCall) continue\n usedToolCallIds.add(toolCall.id)\n loops[loopName] = {\n toolCallId: toolCall.id,\n description: parseAgentDescription(toolCall.arguments),\n status: inferLoopStatusFromTurns(loopTurns, loopMessages),\n }\n }\n return loops\n}\n\nfunction applyPlanningSideEffects(sessionId: string, turns: TurnProjection[]) {\n const latestMode = [...turns]\n .reverse()\n .map((turn) => extractModeFromBlocks(turn.blocks))\n .find((mode): mode is \"planning\" | \"executing\" => mode !== null)\n if (latestMode !== \"planning\") return\n if (_getActiveSessionId?.() !== sessionId) return\n const ui = useUiStore.getState()\n ui.setActiveRightTab(\"situation\")\n if (ui.rightPanelCollapsed) {\n ui.toggleRightPanel()\n }\n}\n\nfunction materialize(turns: TurnProjection[]) {\n const messages = turns\n .map(projectionToMessage)\n .filter((message): message is ChatMessage => message !== null)\n const activeCompaction = [...turns]\n .reverse()\n .find(\n (turn): turn is TurnProjection =>\n turn.kind === \"compaction\" && turn.status === \"streaming\" && typeof turn.compaction_id === \"string\",\n )\n return {\n messages,\n agentLoops: rebuildAgentLoops(turns),\n activeCompaction: activeCompaction\n ? {\n turn_id: activeCompaction.turn_id,\n status: activeCompaction.status,\n compaction_id: activeCompaction.compaction_id!,\n summary_preview: activeCompaction.summary_preview,\n summary_full: activeCompaction.summary_full,\n archived_count: activeCompaction.archived_count,\n archived_files: activeCompaction.archived_files,\n archived_tool_calls: activeCompaction.archived_tool_calls,\n tokens_before: activeCompaction.tokens_before,\n tokens_after: activeCompaction.tokens_after,\n saved_ratio: activeCompaction.saved_ratio,\n trigger: activeCompaction.trigger,\n failure_reason: activeCompaction.failure_reason,\n fallback_applied: activeCompaction.fallback_applied,\n }\n : null,\n }\n}\n\nconst ERROR_ANCHOR_PREFIX = \"error-anchor:\"\n\nfunction updateSessionState(state: ChatState, sessionId: string, turns: TurnProjection[]) {\n const orderedTurns = [...turns].sort((left, right) => left.sequence - right.sequence)\n const { messages, agentLoops, activeCompaction } = materialize(orderedTurns)\n applyPlanningSideEffects(sessionId, orderedTurns)\n // addErrorMessage 注入的 role===\"error\" 不在 turns 里,默认会被 materialize 抹掉。\n // 给每条 error 打上 turn_id 锚点(见 addErrorMessage),只保留锚点命中当前最后一个 turn 的 error:\n // - upsertTurn/applyTurnPatch 推进同一 turn:锚点仍命中 → 错误气泡保留\n // - 用户发新消息(addUserMessage 追加 user turn):最后一个 turn 变化 → 旧错误自动丢弃\n // - session rewind 到更早 checkpoint:锚点的 turn 已不在列表 → 旧错误自动丢弃\n // - 重连/reload 同一 session:最后一个 turn 仍是那个 failed turn → 错误气泡保留\n const lastTurnId = orderedTurns[orderedTurns.length - 1]?.turn_id ?? null\n const preservedErrors = lastTurnId\n ? (state.messages[sessionId] ?? []).filter(\n (m) =>\n m.role === \"error\" &&\n typeof m.entry_id === \"string\" &&\n m.entry_id.startsWith(`${ERROR_ANCHOR_PREFIX}${lastTurnId}:`),\n )\n : []\n const mergedMessages = preservedErrors.length > 0 ? [...messages, ...preservedErrors] : messages\n return {\n turns: { ...state.turns, [sessionId]: orderedTurns },\n messages: { ...state.messages, [sessionId]: mergedMessages },\n agentLoops: { ...state.agentLoops, [sessionId]: agentLoops },\n activeCompactions: { ...state.activeCompactions, [sessionId]: activeCompaction },\n }\n}\n\nexport const useChatStore = create<ChatState>()((set) => ({\n ...createClientActions(set),\n turns: {},\n messages: {},\n askAnswers: {},\n isStreaming: {},\n agentLoops: {},\n activeCompactions: {},\n\n addUserMessage: (sessionId, content) => {\n set((state) => {\n const existing = state.turns[sessionId] ?? []\n const turnId = `local-user-${Date.now()}`\n const optimisticTurn: TurnProjection = {\n id: turnId,\n sequence: Math.max(0, ...existing.map((turn) => turn.sequence)) + 1,\n turn_id: turnId,\n loop_id: \"root\",\n role: \"user\",\n status: \"completed\",\n blocks: [{ type: \"text\", content }],\n tool_calls: [],\n model: null,\n usage: null,\n duration_ms: 0,\n }\n return updateSessionState(state, sessionId, [...existing, optimisticTurn])\n })\n },\n\n setTurns: (sessionId, turns) => {\n set((state) => updateSessionState(state, sessionId, [...turns]))\n },\n\n upsertTurn: (sessionId, turn) => {\n set((state) => {\n const existing = [...(state.turns[sessionId] ?? [])]\n const index = existing.findIndex((item) => item.turn_id === turn.turn_id)\n if (index >= 0) {\n existing[index] = turn\n } else {\n existing.push(turn)\n }\n return {\n ...updateSessionState(state, sessionId, existing),\n }\n })\n },\n\n applyTurnPatch: (sessionId, patch) => {\n set((state) => {\n const turn = patch.data.turn\n if (!turn) return state\n\n const existing = [...(state.turns[sessionId] ?? [])]\n const index = existing.findIndex((item) => item.turn_id === turn.turn_id)\n const lastSequence = index >= 0 ? existing[index].sequence : null\n if (lastSequence !== null && patch.sequence <= lastSequence) {\n return state\n }\n\n const nextTurn = {\n ...turn,\n sequence: patch.sequence,\n }\n if (index >= 0) {\n existing[index] = nextTurn\n } else {\n existing.push(nextTurn)\n }\n return {\n ...updateSessionState(state, sessionId, existing),\n }\n })\n },\n\n addErrorMessage: (sessionId, content) => {\n set((state) => {\n const turns = state.turns[sessionId] ?? []\n const anchorTurnId = turns[turns.length - 1]?.turn_id ?? null\n // 用 entry_id 携带 turn_id 锚点,updateSessionState 据此决定错误气泡去留\n const entry_id = anchorTurnId\n ? `${ERROR_ANCHOR_PREFIX}${anchorTurnId}:${Date.now()}`\n : undefined\n return {\n messages: {\n ...state.messages,\n [sessionId]: [\n ...(state.messages[sessionId] ?? []),\n { role: \"error\", content, loop_name: \"root\", entry_id },\n ],\n },\n }\n })\n },\n\n markInterrupted: (sessionId) => {\n set((state) => {\n const turns = (state.turns[sessionId] ?? []).map((turn) => {\n if (turn.status !== \"streaming\") return turn\n return {\n ...turn,\n status: \"interrupted\" as const,\n tool_calls: turn.tool_calls.map((toolCall) =>\n toolCall.status === \"pending\" || toolCall.status === \"awaiting_answer\"\n ? { ...toolCall, status: \"cancelled\" as const }\n : toolCall,\n ),\n }\n })\n return {\n ...updateSessionState(state, sessionId, turns),\n }\n })\n },\n\n markFailed: (sessionId) => {\n set((state) => {\n const turns = (state.turns[sessionId] ?? []).map((turn) => {\n if (turn.status !== \"streaming\") return turn\n return {\n ...turn,\n status: \"failed\" as const,\n tool_calls: turn.tool_calls.map((toolCall) =>\n toolCall.status === \"pending\" || toolCall.status === \"awaiting_answer\"\n ? { ...toolCall, status: \"error\" as const }\n : toolCall,\n ),\n }\n })\n return {\n ...updateSessionState(state, sessionId, turns),\n }\n })\n },\n\n setStreaming: (sessionId, streaming) => {\n set((state) => ({\n isStreaming: { ...state.isStreaming, [sessionId]: streaming },\n }))\n },\n\n setAskAnswers: (sessionId, answers) => {\n set((state) => ({\n askAnswers: {\n ...state.askAnswers,\n [sessionId]: answers,\n },\n }))\n },\n\n clearMessages: (sessionId) => {\n set((state) => {\n const { [sessionId]: _turns, ...restTurns } = state.turns\n const { [sessionId]: _messages, ...restMessages } = state.messages\n const { [sessionId]: _answers, ...restAnswers } = state.askAnswers\n const { [sessionId]: _loops, ...restLoops } = state.agentLoops\n const { [sessionId]: _compaction, ...restCompactions } = state.activeCompactions\n return {\n turns: restTurns,\n messages: restMessages,\n askAnswers: restAnswers,\n agentLoops: restLoops,\n activeCompactions: restCompactions,\n }\n })\n },\n}))\n","import type { ChatMessage, ToolCallInfo } from \"../../schemas/message\"\n\nconst TOOL_NAME_ALIASES: Record<string, string> = {\n agent: \"Agent\",\n ask_user_question: \"AskUserQuestion\",\n bash: \"Bash\",\n bg_bash: \"BgBash\",\n edit: \"Edit\",\n exit_plan_mode: \"ExitPlanMode\",\n file_edit: \"Edit\",\n file_read: \"Read\",\n file_write: \"Write\",\n finish_task: \"FinishTask\",\n get_skill_content: \"GetSkillContent\",\n glob: \"Glob\",\n grep: \"Grep\",\n list_skill_tools: \"ListSkillTools\",\n load_skill_tools: \"LoadSkillTools\",\n ls: \"Ls\",\n read: \"Read\",\n run_skill_tool: \"RunSkillTool\",\n search_skills: \"SearchSkills\",\n web_fetch: \"WebFetch\",\n web_search: \"WebSearch\",\n write: \"Write\",\n}\n\n/** System tool Chinese display labels */\nconst TOOL_DISPLAY_LABELS: Record<string, string> = {\n Bash: \"执行命令\",\n BgBash: \"后台执行命令\",\n Read: \"读取文件\",\n Write: \"写入文件\",\n Edit: \"编辑文件\",\n Ls: \"列出目录\",\n Glob: \"匹配文件\",\n Grep: \"搜索文本\",\n WebSearch: \"搜索网页\",\n WebFetch: \"整理网页内容\",\n Agent: \"派生子智能体\",\n AskUserQuestion: \"向用户提问\",\n SearchSkills: \"搜索技能\",\n GetSkillContent: \"读取技能\",\n ListSkillTools: \"查看工具列表\",\n LoadSkillTools: \"加载技能\",\n RunSkillTool: \"执行技能工具\",\n FinishTask: \"任务完成\",\n ExitPlanMode: \"提交计划\",\n ListSessions: \"列出历史会话\",\n GetSessionHistory: \"读取会话历史\",\n}\n\n/** Summary format: \"动词 + N + 量词\" style, e.g. \"读取 4 个技能\" */\nconst TOOL_SUMMARY_TEMPLATES: Record<string, (n: number) => string> = {\n Bash: (n) => `执行 ${n} 条命令`,\n BgBash: (n) => `启动 ${n} 个后台任务`,\n Read: (n) => `读取 ${n} 个文件`,\n Write: (n) => `写入 ${n} 个文件`,\n Edit: (n) => `编辑 ${n} 个文件`,\n Ls: (n) => `查看 ${n} 个目录`,\n Glob: (n) => `匹配 ${n} 组文件`,\n Grep: (n) => `搜索 ${n} 次文本`,\n WebSearch: (n) => `搜索 ${n} 次网页`,\n WebFetch: (n) => `整理 ${n} 次网页`,\n SearchSkills: (n) => `搜索 ${n} 次技能`,\n GetSkillContent: (n) => `读取 ${n} 个技能`,\n ListSkillTools: (n) => `查看 ${n} 个工具列表`,\n RunSkillTool: (n) => `执行 ${n} 个技能工具`,\n Agent: (n) => `调用 ${n} 个子智能体`,\n}\n\nconst OUTPUT_TOOL_ICONS: Record<string, string> = {\n Write: \"📄\",\n Edit: \"📝\",\n}\n\nconst PRIORITY_FILE_KEYS = [\n \"file_path\",\n \"output_file\",\n \"output_path\",\n \"path\",\n \"filepath\",\n \"target_file\",\n \"destination\",\n \"filename\",\n]\n\nconst FILE_PATH_PATTERN =\n /(?:^|[\\s\"'`::])([^\\s\"'`,;:)\\]},。;、]+?\\.[a-z0-9]{1,8})(?=$|[\\s\"'`,.;:)\\]},。;、])/i\n\nexport type ToolTone = \"emerald\" | \"blue\" | \"amber\" | \"red\"\n\nexport interface ToolSummaryTag {\n key: string\n label: string\n tone: \"emerald\" | \"blue\"\n}\n\nconst INDIVIDUAL_SUMMARY_TOOL_NAMES = new Set([\"RunSkillTool\", \"Bash\", \"BgBash\"])\n\nfunction safeParseJson(value: string | null | undefined): unknown {\n if (!value) return null\n try {\n return JSON.parse(value)\n } catch {\n return null\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value)\n}\n\nfunction getStringArgValue(args: Record<string, unknown> | null, key: string): string {\n const value = args?.[key]\n return typeof value === \"string\" ? value.trim() : \"\"\n}\n\n/** Generic spec labels that should NOT be treated as per-call display names. */\nconst GENERIC_DISPLAY_NAMES = new Set(Object.values(TOOL_DISPLAY_LABELS))\n\nfunction getRunSkillDisplayName(\n toolCall: ToolCallInfo,\n args: Record<string, unknown> | null,\n): string {\n // Prefer per-call display name from arguments over the spec-level generic label.\n const argDisplayName = getStringArgValue(args, \"display_name\")\n if (argDisplayName) return argDisplayName\n\n // Only use toolCall.display_name if it's not a generic spec label\n const displayName = toolCall.display_name?.trim() ?? \"\"\n if (displayName && (displayName === \"写入文件\" || !GENERIC_DISPLAY_NAMES.has(displayName))) {\n return displayName\n }\n\n const argToolName = getStringArgValue(args, \"tool_name\")\n if (argToolName) return argToolName\n\n return \"\"\n}\n\nfunction getCommandDescription(args: Record<string, unknown> | null): string {\n return getStringArgValue(args, \"description\")\n}\n\nfunction findFileLikeValue(value: unknown, depth = 0, allowPlainString = false): string | null {\n if (depth > 3) return null\n if (typeof value === \"string\") {\n const trimmed = value.trim()\n if (!trimmed) return null\n if (allowPlainString || trimmed.includes(\"/\") || /\\.[a-z0-9]{1,8}$/i.test(trimmed)) {\n return trimmed\n }\n return null\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n const found = findFileLikeValue(item, depth + 1, allowPlainString)\n if (found) return found\n }\n return null\n }\n if (isPlainObject(value)) {\n for (const key of PRIORITY_FILE_KEYS) {\n const direct = findFileLikeValue(value[key], depth + 1, true)\n if (direct) return direct\n }\n for (const nested of Object.values(value)) {\n const found = findFileLikeValue(nested, depth + 1, allowPlainString)\n if (found) return found\n }\n }\n return null\n}\n\nfunction extractFilePathFromText(text: string): string | null {\n return text.match(FILE_PATH_PATTERN)?.[1] ?? null\n}\n\nfunction extractFilePathFromResult(result: unknown): string | null {\n if (typeof result === \"string\") {\n const parsed = safeParseJson(result)\n if (parsed != null && parsed !== result) {\n const found = extractFilePathFromResult(parsed)\n if (found) return found\n }\n return extractFilePathFromText(result)\n }\n if (Array.isArray(result)) {\n for (const item of result) {\n const found = extractFilePathFromResult(item)\n if (found) return found\n }\n return null\n }\n if (isPlainObject(result)) {\n for (const key of PRIORITY_FILE_KEYS) {\n const direct = findFileLikeValue(result[key], 0, true)\n if (direct) return direct\n }\n for (const nested of Object.values(result)) {\n const found = extractFilePathFromResult(nested)\n if (found) return found\n }\n }\n return null\n}\n\nfunction isWriteLikeRunSkillTool(label: string, args: Record<string, unknown> | null): boolean {\n const normalizedLabel = label.trim()\n if (normalizedLabel === \"写入文件\" || /write/i.test(normalizedLabel)) return true\n\n const toolName = getStringArgValue(args, \"tool_name\")\n if (/write/i.test(toolName)) return true\n\n return false\n}\n\nfunction formatFileName(filePath: string): string {\n return filePath.split(\"/\").pop() ?? filePath\n}\n\nfunction isInternalStatusFile(filePath: string): boolean {\n const fileName = formatFileName(filePath).toLowerCase()\n return fileName === \"phase.json\"\n}\n\nexport function formatToolName(name: string): string {\n const trimmed = name.trim()\n if (!trimmed) return name\n\n const stripped =\n trimmed.split(\":\").pop()?.split(\"/\").pop()?.split(\".\").pop()?.trim() || trimmed\n const normalized = stripped\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\n .replace(/[^a-zA-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .toLowerCase()\n\n return TOOL_NAME_ALIASES[normalized] ?? stripped\n}\n\n/**\n * Returns a context-aware Chinese display label for a tool call.\n * Uses tool arguments to provide specific context (e.g., skill name, file path).\n */\nexport function getToolDisplayLabel(toolCall: ToolCallInfo): string {\n const normalized = formatToolName(toolCall.name)\n const args = safeParseJson(toolCall.arguments) as Record<string, unknown> | null\n const displayName = toolCall.display_name?.trim() ?? \"\"\n const baseLabel = displayName || TOOL_DISPLAY_LABELS[normalized] || normalized\n const metaDisplayName = getStringArgValue(args, \"_meta_display_name\")\n if (metaDisplayName) {\n return metaDisplayName\n }\n\n switch (normalized) {\n case \"RunSkillTool\": {\n const runSkillDisplayName = getRunSkillDisplayName(toolCall, args)\n if (runSkillDisplayName) {\n const filePath = extractToolFilePath(toolCall)\n if (filePath && isWriteLikeRunSkillTool(runSkillDisplayName, args)) {\n return `${runSkillDisplayName}「${formatFileName(filePath)}」`\n }\n return runSkillDisplayName\n }\n return \"执行技能工具\"\n }\n case \"Bash\": {\n const description = getCommandDescription(args)\n return description ? `执行命令行操作:${description}` : \"执行命令行操作\"\n }\n case \"BgBash\": {\n const description = getCommandDescription(args)\n return description ? `后台执行:${description}` : \"后台执行命令\"\n }\n case \"Ls\": {\n const path = args && typeof args.path === \"string\" ? args.path : \"\"\n return path ? `${baseLabel}「${path}」` : baseLabel\n }\n case \"Glob\": {\n const pattern = args && typeof args.pattern === \"string\" ? args.pattern : \"\"\n return pattern ? `${baseLabel}「${pattern}」` : baseLabel\n }\n case \"Grep\": {\n const pattern = args && typeof args.pattern === \"string\" ? args.pattern : \"\"\n return pattern ? `${baseLabel}「${pattern}」` : baseLabel\n }\n case \"WebSearch\":\n case \"WebFetch\": {\n const query = args && typeof args.query === \"string\" ? args.query : \"\"\n return query ? `${baseLabel}「${query}」` : baseLabel\n }\n case \"GetSkillContent\": {\n const skillName =\n args && typeof args.skill_id === \"string\"\n ? args.skill_id\n : args && typeof args.skill_name === \"string\"\n ? args.skill_name\n : \"\"\n return skillName ? `${baseLabel}「${skillName}」` : baseLabel\n }\n case \"SearchSkills\": {\n const query = args && typeof args.query === \"string\" ? args.query : \"\"\n return query ? `${baseLabel}「${query}」` : baseLabel\n }\n case \"ListSkillTools\": {\n const skillId = args && typeof args.skill_id === \"string\" ? args.skill_id : \"\"\n return skillId ? `${baseLabel}「${skillId}」` : baseLabel\n }\n case \"LoadSkillTools\":\n return \"加载技能\"\n case \"FinishTask\": {\n const title = args && typeof args.title === \"string\" ? args.title : \"\"\n return title ? `${baseLabel}:${title}` : baseLabel\n }\n default:\n return baseLabel\n }\n}\n\nexport function getToolTone(status?: ToolCallInfo[\"status\"]): ToolTone {\n if (status === \"error\" || status === \"cancelled\") return \"red\"\n if (status === \"awaiting_answer\") return \"amber\"\n if (status === \"pending\") return \"blue\"\n return \"emerald\"\n}\n\nexport function getToolStatusLabel(status?: ToolCallInfo[\"status\"]): string {\n if (status === \"pending\") return \"运行中\"\n if (status === \"awaiting_answer\") return \"等待回答\"\n if (status === \"error\") return \"错误\"\n if (status === \"cancelled\") return \"已取消\"\n return \"完成\"\n}\n\nexport function getAggregateToolTone(toolCalls: ToolCallInfo[]): ToolTone {\n if (\n toolCalls.some((toolCall) => toolCall.status === \"error\" || toolCall.status === \"cancelled\")\n ) {\n return \"red\"\n }\n if (toolCalls.some((toolCall) => toolCall.status === \"awaiting_answer\")) {\n return \"amber\"\n }\n if (toolCalls.some((toolCall) => toolCall.status === \"pending\")) {\n return \"blue\"\n }\n return \"emerald\"\n}\n\nexport function extractToolFilePath(toolCall: ToolCallInfo): string | null {\n const formattedName = formatToolName(toolCall.name)\n const argsValue = safeParseJson(toolCall.arguments)\n let filePath: string | null = null\n\n if (formattedName === \"Read\" || formattedName === \"Write\" || formattedName === \"Edit\") {\n filePath = findFileLikeValue(argsValue)\n } else if (formattedName === \"RunSkillTool\") {\n filePath = findFileLikeValue(argsValue) ?? extractFilePathFromResult(toolCall.result)\n }\n\n if (!filePath || isInternalStatusFile(filePath)) return null\n return filePath\n}\n\nexport function getToolOutputTag(toolCall: ToolCallInfo): ToolSummaryTag | null {\n const toolName = formatToolName(toolCall.name)\n const filePath = extractToolFilePath(toolCall)\n const icon = OUTPUT_TOOL_ICONS[toolName]\n\n if (!icon || !filePath) return null\n\n return {\n key: `${toolCall.id}-${filePath}`,\n label: `${icon} ${filePath.split(\"/\").pop() ?? filePath}`,\n tone: \"blue\",\n }\n}\n\nexport function isSuccessfulToolCall(toolCall: ToolCallInfo): boolean {\n if (toolCall.status === \"error\" || toolCall.status === \"cancelled\") return false\n if (toolCall.status === \"awaiting_answer\") return false\n if (toolCall.status === \"pending\") return false\n return toolCall.status === \"done\" || toolCall.result != null || toolCall.status == null\n}\n\nexport function countSuccessfulToolCalls(toolCalls: ToolCallInfo[]): number {\n return toolCalls.filter(isSuccessfulToolCall).length\n}\n\nexport function countPendingToolCalls(toolCalls: ToolCallInfo[]): number {\n return toolCalls.filter(\n (toolCall) => toolCall.status === \"pending\" || toolCall.status === \"awaiting_answer\",\n ).length\n}\n\nexport function formatToolCallCount(count: number): string {\n return `已执行 ${count} 次工具调用`\n}\n\nexport function summarizeToolCalls(toolCalls: ToolCallInfo[]): ToolSummaryTag[] {\n const outputTagMap = new Map<string, ToolSummaryTag>()\n const counts = new Map<string, number>()\n const individualTags: ToolSummaryTag[] = []\n\n for (const toolCall of toolCalls.filter(isSuccessfulToolCall)) {\n const normalized = formatToolName(toolCall.name)\n const args = safeParseJson(toolCall.arguments) as Record<string, unknown> | null\n\n if (normalized === \"Agent\" || normalized === \"FinishTask\") continue\n\n if (INDIVIDUAL_SUMMARY_TOOL_NAMES.has(normalized)) {\n if (normalized === \"RunSkillTool\") {\n const runSkillDisplayName = getRunSkillDisplayName(toolCall, args)\n const filePath = extractToolFilePath(toolCall)\n if (filePath && isWriteLikeRunSkillTool(runSkillDisplayName, args)) {\n individualTags.push({\n key: `${individualTags.length}-${toolCall.id}`,\n label: `📄 ${formatFileName(filePath)}`,\n tone: \"blue\",\n })\n continue\n }\n individualTags.push({\n key: `${individualTags.length}-${toolCall.id}`,\n label: runSkillDisplayName || \"执行技能工具\",\n tone: \"emerald\",\n })\n continue\n }\n\n if (normalized === \"Bash\") {\n individualTags.push({\n key: `${individualTags.length}-${toolCall.id}`,\n label: getCommandDescription(args) || \"执行命令行操作\",\n tone: \"emerald\",\n })\n continue\n }\n\n if (normalized === \"BgBash\") {\n individualTags.push({\n key: `${individualTags.length}-${toolCall.id}`,\n label: getCommandDescription(args) || \"后台执行命令\",\n tone: \"emerald\",\n })\n continue\n }\n }\n\n const outputTag = getToolOutputTag(toolCall)\n if (outputTag) {\n const fileLabel = outputTag.label.replace(/^[^\\s]+\\s+/, \"\")\n const existing = outputTagMap.get(fileLabel)\n if (existing?.label.startsWith(\"📄\")) {\n continue\n }\n outputTagMap.set(fileLabel, {\n ...outputTag,\n key: fileLabel,\n label:\n normalized === \"Write\" ||\n existing?.label.startsWith(\"📝\")\n ? `📄 ${fileLabel}`\n : outputTag.label,\n })\n continue\n }\n\n counts.set(normalized, (counts.get(normalized) ?? 0) + 1)\n }\n\n const aggregatedCountTags: ToolSummaryTag[] = []\n for (const [normalized, count] of counts.entries()) {\n const template = TOOL_SUMMARY_TEMPLATES[normalized]\n const label = template ? template(count) : `${TOOL_DISPLAY_LABELS[normalized] ?? normalized} ×${count}`\n aggregatedCountTags.push({\n key: `${normalized}-${count}`,\n label,\n tone: \"emerald\",\n })\n }\n\n const outputTags = [...outputTagMap.values()].sort((left, right) =>\n left.label.localeCompare(right.label),\n )\n const sortedCountTags = aggregatedCountTags.sort((left, right) =>\n left.label.localeCompare(right.label),\n )\n const merged = [...individualTags, ...outputTags, ...sortedCountTags]\n\n if (merged.length <= 5) return merged\n\n return [\n ...merged.slice(0, 5),\n {\n key: `overflow-${merged.length - 5}`,\n label: `+${merged.length - 5} 更多`,\n tone: \"emerald\",\n },\n ]\n}\n\nfunction getMessageText(message: ChatMessage): string {\n if (typeof message.content === \"string\") return message.content\n return message.content\n .filter((part) => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\")\n}\n\nexport function hasNaturalLanguageText(message: ChatMessage): boolean {\n return getMessageText(message).trim().length > 0\n}\n\nexport function findAnchorMessageIndex(messages: ChatMessage[]): number {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n if (hasNaturalLanguageText(messages[index])) {\n return index\n }\n }\n return Math.max(messages.length - 1, 0)\n}\n\nexport function getCollapsedTurnSegments(messages: ChatMessage[]): {\n anchorIndex: number\n summaryMessages: ChatMessage[]\n visibleMessages: ChatMessage[]\n} {\n if (messages.length === 0) {\n return { anchorIndex: 0, summaryMessages: [], visibleMessages: [] }\n }\n\n const anchorIndex = findAnchorMessageIndex(messages)\n return {\n anchorIndex,\n summaryMessages: messages.slice(0, anchorIndex),\n visibleMessages: messages.slice(anchorIndex),\n }\n}\n\nexport function getLatestPendingToolName(toolCalls: ToolCallInfo[]): string | null {\n for (let index = toolCalls.length - 1; index >= 0; index -= 1) {\n if (\n toolCalls[index].status === \"pending\" ||\n toolCalls[index].status === \"awaiting_answer\"\n ) {\n return formatToolName(toolCalls[index].name)\n }\n }\n return null\n}\n\nexport function getFinishTaskTitle(toolCalls: ToolCallInfo[]): string | null {\n for (let index = toolCalls.length - 1; index >= 0; index -= 1) {\n if (formatToolName(toolCalls[index].name) !== \"FinishTask\") continue\n const args = safeParseJson(toolCalls[index].arguments)\n if (!isPlainObject(args)) continue\n const title = typeof args.title === \"string\" ? args.title.trim() : \"\"\n if (title) return title\n }\n return null\n}\n\nexport function getTextContentSegments(messages: ChatMessage[]): string[] {\n return messages\n .map((message) => {\n if (typeof message.content === \"string\") return message.content\n return message.content\n .filter((part) => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\")\n })\n .map((text) => text.trim())\n .filter(Boolean)\n}\n\nexport function getLastTextSegment(messages: ChatMessage[]): string {\n const texts = getTextContentSegments(messages)\n return texts[texts.length - 1] ?? \"\"\n}\n\nexport function countMessageLines(message: ChatMessage): number {\n const text = typeof message.content === \"string\" ? message.content : \"\"\n const reasoningLines = message.reasoning ? message.reasoning.split(\"\\n\").length : 0\n const textLines = text.trim() ? text.trim().split(\"\\n\").length : 0\n const toolLines = message.tool_calls?.length ?? 0\n return reasoningLines + textLines + toolLines\n}\n","import { create } from \"zustand\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\ntype ThemeValue = \"light\" | \"dark\" | \"system\"\n\nconst isBrowser = typeof window !== \"undefined\" && typeof document !== \"undefined\"\n\nexport function resolveEffectiveTheme(theme: ThemeValue): \"light\" | \"dark\" {\n if (theme !== \"system\") return theme\n return isBrowser && window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\"\n}\n\nfunction applyTheme(theme: ThemeValue) {\n if (!isBrowser) return\n const effective = resolveEffectiveTheme(theme)\n document.documentElement.setAttribute(\"data-theme\", effective)\n}\n\n// Initialize theme from localStorage before store creation\nconst storedTheme = isBrowser\n ? (localStorage.getItem(\"blade-theme\") as ThemeValue | null) ?? \"light\"\n : \"light\"\napplyTheme(storedTheme)\n\n// Listen for system preference changes\nif (isBrowser) {\n window.matchMedia(\"(prefers-color-scheme: dark)\").addEventListener(\"change\", () => {\n const current = useUiStore?.getState?.()?.theme\n if (current === \"system\") applyTheme(\"system\")\n })\n}\n\nexport interface PreviewTarget {\n type:\n | \"file\"\n | \"markdown\"\n | \"image\"\n | \"url\"\n | \"html\"\n | \"resource-html\"\n | \"resource-uri\"\n | \"diff\"\n | \"pdf\"\n | \"docx\"\n | \"video\"\n | \"audio\"\n | \"download\"\n | \"card\"\n | \"excel\"\n | \"csv\"\n | \"ppt\"\n content: string\n title?: string\n /** Stable identity key for tab deduplication (e.g. file_path). Falls back to title. */\n key?: string\n /** For diff type: the old content before edit */\n oldContent?: string\n /** Optional bridge session binding for resource iframes. */\n bridgeSessionId?: string\n /** Original source file URL when preview content uses a derived representation (e.g. PPT -> PDF). */\n sourceUrl?: string\n /** Changes when an artifact with the same key is updated in place. */\n revision?: number\n}\n\nexport type ActiveRightTab = \"situation\" | \"preview\" | \"skill-test\" | \"vibe-service\" | \"vibe-preview\"\n\ninterface UpsertArtifactOptions {\n reveal?: boolean\n activate?: boolean\n}\n\ninterface UiState extends ClientAwareState {\n leftPanelSize: number\n rightPanelSize: number\n leftPanelCollapsed: boolean\n rightPanelCollapsed: boolean\n activeRightTab: ActiveRightTab\n /** All open artifact tabs */\n artifacts: PreviewTarget[]\n /** Index of the active tab (-1 if none) */\n activeArtifactIndex: number\n theme: ThemeValue\n\n setLeftPanelSize: (size: number) => void\n setRightPanelSize: (size: number) => void\n setLeftPanelCollapsed: (collapsed: boolean) => void\n setRightPanelCollapsed: (collapsed: boolean) => void\n toggleLeftPanel: () => void\n toggleRightPanel: () => void\n setActiveRightTab: (tab: ActiveRightTab) => void\n /** Add or update an artifact tab (matched by key/title). Opens the panel automatically. */\n pushArtifact: (target: PreviewTarget) => void\n /** Add or update an artifact tab with optional reveal/activate control. */\n upsertArtifact: (target: PreviewTarget, options?: UpsertArtifactOptions) => void\n /** Switch to a tab by index */\n setActiveArtifact: (index: number) => void\n /** Close a tab by index */\n closeArtifact: (index: number) => void\n /** Close a tab by stable key if it exists. */\n removeArtifactByKey: (key: string) => void\n /** Close all tabs and collapse panel */\n clearArtifacts: () => void\n /** Legacy single-target setter for backwards compat */\n setPreviewTarget: (target: PreviewTarget | null) => void\n setTheme: (theme: ThemeValue) => void\n}\n\nfunction removeArtifactAtIndex(state: UiState, index: number) {\n const next = state.artifacts.filter((_: PreviewTarget, i: number) => i !== index)\n let nextActive = state.activeArtifactIndex\n\n if (next.length === 0) {\n return { artifacts: [], activeArtifactIndex: -1, rightPanelCollapsed: true }\n }\n\n if (index < nextActive) {\n nextActive -= 1\n } else if (index === nextActive) {\n nextActive = Math.min(index, next.length - 1)\n }\n\n return { artifacts: next, activeArtifactIndex: nextActive }\n}\n\nfunction upsertArtifactState(state: UiState, target: PreviewTarget, options?: UpsertArtifactOptions) {\n const reveal = options?.reveal ?? true\n const activate = options?.activate ?? true\n const targetKey = target.key ?? target.title\n const applyUiState = (partial: {\n artifacts: PreviewTarget[]\n activeArtifactIndex?: number\n }) => ({\n ...partial,\n ...(reveal ? { rightPanelCollapsed: false, activeRightTab: \"preview\" as const } : {}),\n })\n\n const existing = state.artifacts.findIndex((artifact) => targetKey && (artifact.key ?? artifact.title) === targetKey)\n if (existing >= 0) {\n const updated = [...state.artifacts]\n updated[existing] = target\n return applyUiState({\n artifacts: updated,\n activeArtifactIndex: activate ? existing : state.activeArtifactIndex,\n })\n }\n\n const next = [...state.artifacts, target]\n return applyUiState({\n artifacts: next,\n activeArtifactIndex: activate ? next.length - 1 : state.activeArtifactIndex >= 0 ? state.activeArtifactIndex : 0,\n })\n}\n\nexport const useUiStore = create<UiState>()((set) => ({\n ...createClientActions(set),\n leftPanelSize: 20,\n rightPanelSize: 25,\n leftPanelCollapsed: false,\n rightPanelCollapsed: false,\n activeRightTab: \"situation\",\n artifacts: [],\n activeArtifactIndex: -1,\n theme: storedTheme,\n\n setLeftPanelSize: (size) => set({ leftPanelSize: size }),\n setRightPanelSize: (size) => set({ rightPanelSize: size }),\n setLeftPanelCollapsed: (collapsed) => set({ leftPanelCollapsed: collapsed }),\n setRightPanelCollapsed: (collapsed) => set({ rightPanelCollapsed: collapsed }),\n toggleLeftPanel: () => set((s) => ({ leftPanelCollapsed: !s.leftPanelCollapsed })),\n toggleRightPanel: () => set((s) => ({ rightPanelCollapsed: !s.rightPanelCollapsed })),\n setActiveRightTab: (tab) => set({ activeRightTab: tab }),\n\n pushArtifact: (target) => set((state) => upsertArtifactState(state, target)),\n\n upsertArtifact: (target, options) => set((state) => upsertArtifactState(state, target, options)),\n\n setActiveArtifact: (index) => set({ activeArtifactIndex: index }),\n\n closeArtifact: (index) => set((state) => removeArtifactAtIndex(state, index)),\n\n removeArtifactByKey: (key) =>\n set((state) => {\n const index = state.artifacts.findIndex((artifact) => (artifact.key ?? artifact.title) === key)\n if (index < 0) return state\n return removeArtifactAtIndex(state, index)\n }),\n\n clearArtifacts: () => set({ artifacts: [], activeArtifactIndex: -1 }),\n\n setPreviewTarget: (target: PreviewTarget | null) =>\n set(() => {\n if (target === null) {\n return { artifacts: [], activeArtifactIndex: -1 }\n }\n // Push as single artifact\n return {\n artifacts: [target],\n activeArtifactIndex: 0,\n rightPanelCollapsed: false,\n activeRightTab: \"preview\",\n }\n }),\n\n setTheme: (theme: ThemeValue) => {\n localStorage.setItem(\"blade-theme\", theme)\n applyTheme(theme)\n set({ theme })\n },\n}))\n","import { create } from \"zustand\"\nimport type { Task } from \"../schemas/task\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\nconst EMPTY_TASKS: Task[] = []\n\ninterface TaskState extends ClientAwareState {\n tasks: Record<string, Task[]>\n setTasks: (sessionId: string, tasks: Task[]) => void\n getTasks: (sessionId: string) => Task[]\n}\n\nexport const useTaskStore = create<TaskState>()((set, get) => ({\n ...createClientActions(set),\n tasks: {},\n\n setTasks: (sessionId, tasks) => {\n set((state) => ({\n tasks: { ...state.tasks, [sessionId]: tasks },\n }))\n },\n\n getTasks: (sessionId) => {\n return get().tasks[sessionId] ?? EMPTY_TASKS\n },\n}))\n","import { create } from \"zustand\"\nimport type { BackgroundTask } from \"../schemas/background\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\ninterface BackgroundState extends ClientAwareState {\n tasks: Record<string, BackgroundTask[]>\n selectedTaskId: Record<string, string | null>\n setTasks: (sessionId: string, tasks: BackgroundTask[]) => void\n upsertTask: (sessionId: string, task: BackgroundTask) => void\n selectTask: (sessionId: string, taskId: string | null) => void\n}\n\nexport const useBackgroundStore = create<BackgroundState>()((set) => ({\n ...createClientActions(set),\n tasks: {},\n selectedTaskId: {},\n\n setTasks: (sessionId, tasks) =>\n set((state) => ({\n tasks: { ...state.tasks, [sessionId]: tasks },\n selectedTaskId: {\n ...state.selectedTaskId,\n [sessionId]: state.selectedTaskId[sessionId] ?? tasks[0]?.id ?? null,\n },\n })),\n\n upsertTask: (sessionId, task) =>\n set((state) => {\n const existing = state.tasks[sessionId] ?? []\n const index = existing.findIndex((item) => item.id === task.id)\n const next = [...existing]\n if (index >= 0) {\n next[index] = { ...next[index], ...task }\n } else {\n next.unshift(task)\n }\n return {\n tasks: { ...state.tasks, [sessionId]: next },\n selectedTaskId: {\n ...state.selectedTaskId,\n [sessionId]: state.selectedTaskId[sessionId] ?? task.id,\n },\n }\n }),\n\n selectTask: (sessionId, taskId) =>\n set((state) => ({\n selectedTaskId: { ...state.selectedTaskId, [sessionId]: taskId },\n })),\n}))\n","import { create } from \"zustand\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\ninterface CardStateStore extends ClientAwareState {\n states: Record<string, unknown>\n getCardState: <T>(cardId: string) => T | undefined\n setCardState: <T>(cardId: string, state: T) => void\n removeCardState: (cardId: string) => void\n clearAllStates: () => void\n}\n\nexport const useCardStateStore = create<CardStateStore>((set, get) => ({\n ...createClientActions(set),\n states: {},\n\n getCardState: <T,>(cardId: string): T | undefined => {\n return get().states[cardId] as T | undefined\n },\n\n setCardState: <T,>(cardId: string, state: T) => {\n set((prev) => ({\n states: { ...prev.states, [cardId]: state },\n }))\n },\n\n removeCardState: (cardId: string) => {\n set((prev) => {\n const { [cardId]: _, ...rest } = prev.states\n return { states: rest }\n })\n },\n\n clearAllStates: () => {\n set({ states: {} })\n },\n}))\n","import { create } from \"zustand\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\nexport type ConnectionStatus = \"connecting\" | \"connected\" | \"disconnected\"\n\nexport interface ConnectionState extends ClientAwareState {\n status: ConnectionStatus\n reconnectAttempt: number\n lastConnectedAt: number | null\n lastDisconnectedAt: number | null\n hasEverConnected: boolean\n markConnecting: (attempt?: number) => void\n markConnected: () => void\n markDisconnected: () => void\n reset: () => void\n}\n\nconst initialConnectionState = {\n status: \"disconnected\" as ConnectionStatus,\n reconnectAttempt: 0,\n lastConnectedAt: null,\n lastDisconnectedAt: null,\n hasEverConnected: false,\n}\n\nexport const useConnectionStore = create<ConnectionState>()((set) => ({\n ...createClientActions(set),\n ...initialConnectionState,\n\n markConnecting: (attempt = 0) =>\n set({\n status: \"connecting\",\n reconnectAttempt: attempt,\n }),\n\n markConnected: () =>\n set({\n status: \"connected\",\n reconnectAttempt: 0,\n lastConnectedAt: Date.now(),\n hasEverConnected: true,\n }),\n\n markDisconnected: () =>\n set({\n status: \"disconnected\",\n reconnectAttempt: 0,\n lastDisconnectedAt: Date.now(),\n }),\n\n reset: () => set(initialConnectionState),\n}))\n","import { create } from \"zustand\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\nexport interface RuntimeEvent {\n id: string\n sessionId: string\n type: string\n title: string\n status: \"info\" | \"running\" | \"done\" | \"error\"\n timestamp: string\n loopName?: string\n detail?: string\n}\n\ninterface RuntimeState extends ClientAwareState {\n events: Record<string, RuntimeEvent[]>\n addEvent: (sessionId: string, event: Omit<RuntimeEvent, \"id\" | \"sessionId\" | \"timestamp\">) => void\n clearSession: (sessionId: string) => void\n}\n\nexport const useRuntimeStore = create<RuntimeState>()((set) => ({\n ...createClientActions(set),\n events: {},\n\n addEvent: (sessionId, event) =>\n set((state) => {\n const current = state.events[sessionId] ?? []\n const nextEvent: RuntimeEvent = {\n ...event,\n id: `${Date.now()}-${current.length}`,\n sessionId,\n timestamp: new Date().toISOString(),\n }\n return {\n events: {\n ...state.events,\n [sessionId]: [...current.slice(-59), nextEvent],\n },\n }\n }),\n\n clearSession: (sessionId) =>\n set((state) => ({\n events: { ...state.events, [sessionId]: [] },\n })),\n}))\n","import { create } from \"zustand\"\nimport type { GisGoal, GisMapCommand, GisResource, GisTarget } from \"../schemas/gis\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\nconst EMPTY_GOALS: GisGoal[] = []\nconst EMPTY_RESOURCES: GisResource[] = []\nconst EMPTY_TARGETS: GisTarget[] = []\nconst EMPTY_COMMANDS: GisMapCommand[] = []\n\ninterface GisStateStore extends ClientAwareState {\n goalsBySession: Record<string, GisGoal[]>\n resourcesBySession: Record<string, GisResource[]>\n targetsBySession: Record<string, GisTarget[]>\n pendingMapCommandsBySession: Record<string, GisMapCommand[]>\n setGoals: (sessionId: string, goals: GisGoal[]) => void\n setResources: (sessionId: string, resources: GisResource[]) => void\n setTargets: (sessionId: string, targets: GisTarget[]) => void\n pushMapCommand: (\n sessionId: string,\n command: Omit<GisMapCommand, \"id\" | \"createdAt\">,\n ) => void\n consumeMapCommand: (sessionId: string, commandId: string) => void\n getGoals: (sessionId: string) => GisGoal[]\n getResources: (sessionId: string) => GisResource[]\n getTargets: (sessionId: string) => GisTarget[]\n getPendingMapCommands: (sessionId: string) => GisMapCommand[]\n}\n\nfunction newCommandId() {\n if (typeof globalThis !== \"undefined\" && \"crypto\" in globalThis) {\n return globalThis.crypto?.randomUUID?.() ?? `gis-map-${Date.now()}-${Math.random()}`\n }\n return `gis-map-${Date.now()}-${Math.random()}`\n}\n\nexport const useGisStore = create<GisStateStore>()((set, get) => ({\n ...createClientActions(set),\n goalsBySession: {},\n resourcesBySession: {},\n targetsBySession: {},\n pendingMapCommandsBySession: {},\n\n setGoals: (sessionId, goals) => {\n set((state) => ({\n goalsBySession: { ...state.goalsBySession, [sessionId]: goals },\n }))\n },\n\n setResources: (sessionId, resources) => {\n set((state) => ({\n resourcesBySession: { ...state.resourcesBySession, [sessionId]: resources },\n }))\n },\n\n setTargets: (sessionId, targets) => {\n set((state) => ({\n targetsBySession: { ...state.targetsBySession, [sessionId]: targets },\n }))\n },\n\n pushMapCommand: (sessionId, command) => {\n set((state) => ({\n pendingMapCommandsBySession: {\n ...state.pendingMapCommandsBySession,\n [sessionId]: [\n ...(state.pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS),\n {\n ...command,\n id: newCommandId(),\n createdAt: Date.now(),\n },\n ],\n },\n }))\n },\n\n consumeMapCommand: (sessionId, commandId) => {\n set((state) => ({\n pendingMapCommandsBySession: {\n ...state.pendingMapCommandsBySession,\n [sessionId]: (state.pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS).filter(\n (command) => command.id !== commandId,\n ),\n },\n }))\n },\n\n getGoals: (sessionId) => get().goalsBySession[sessionId] ?? EMPTY_GOALS,\n getResources: (sessionId) => get().resourcesBySession[sessionId] ?? EMPTY_RESOURCES,\n getTargets: (sessionId) => get().targetsBySession[sessionId] ?? EMPTY_TARGETS,\n getPendingMapCommands: (sessionId) =>\n get().pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS,\n}))\n","import { create } from \"zustand\"\nimport type { AskUserAnswerData } from \"../components/chat/AskUserQuestionBlock\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\nexport type AnswerCallback = (\n answer: string,\n toolCallId: string,\n answerData: AskUserAnswerData,\n) => void\n\ninterface AnswerCallbackState extends ClientAwareState {\n callbacks: Record<string, AnswerCallback | undefined>\n setAnswerCallback: (sessionId: string, callback?: AnswerCallback) => void\n}\n\nexport const useAnswerCallbackStore = create<AnswerCallbackState>()((set) => ({\n ...createClientActions(set),\n callbacks: {},\n setAnswerCallback: (sessionId, callback) => {\n set((state) => ({\n callbacks: {\n ...state.callbacks,\n [sessionId]: callback,\n },\n }))\n },\n}))\n","import { create } from \"zustand\"\nimport { createClientActions, type ClientAwareState } from \"./client-aware\"\n\nexport type AsrProvider = \"volcengine\" | \"qwen\"\n\ninterface RuntimeFeaturesState extends ClientAwareState {\n asrEnabled: boolean\n asrProvider: AsrProvider\n publicSharingEnabled: boolean\n memoryEnabled: boolean\n setFeatures: (features: {\n asrEnabled?: boolean\n asrProvider?: AsrProvider\n publicSharingEnabled?: boolean\n memoryEnabled?: boolean\n }) => void\n}\n\n/**\n * 运行期特性开关(由后端 /api/config 下发)。默认全部关闭,main.tsx 启动时 fetch 一次填充。\n */\nexport const useRuntimeFeaturesStore = create<RuntimeFeaturesState>((set) => ({\n ...createClientActions(set),\n asrEnabled: false,\n asrProvider: \"volcengine\",\n publicSharingEnabled: false,\n memoryEnabled: false,\n setFeatures: (features) =>\n set((prev) => ({\n asrEnabled: features.asrEnabled ?? prev.asrEnabled,\n asrProvider: features.asrProvider ?? prev.asrProvider,\n publicSharingEnabled:\n features.publicSharingEnabled ?? prev.publicSharingEnabled,\n memoryEnabled: features.memoryEnabled ?? prev.memoryEnabled,\n })),\n}))\n","import { BladeClient, type BladeClientOptions } from \"../client\"\nimport { attachHostBridgeListener } from \"./lib/parent-bridge\"\nimport { bridgeSocketEvents } from \"./sockets/event-bridge\"\nimport { useAnswerCallbackStore } from \"./stores/answer-callback-store\"\nimport { useAuthStore } from \"./stores/auth-store\"\nimport { useBackgroundStore } from \"./stores/background-store\"\nimport { useCardStateStore } from \"./stores/card-state-store\"\nimport { useChatStore } from \"./stores/chat-store\"\nimport { useConnectionStore } from \"./stores/connection-store\"\nimport { useGisStore } from \"./stores/gis-store\"\nimport { useRuntimeFeaturesStore } from \"./stores/runtime-features-store\"\nimport { useRuntimeStore } from \"./stores/runtime-store\"\nimport { useSessionStore } from \"./stores/session-store\"\nimport { useTaskStore } from \"./stores/task-store\"\nimport { useUiBridgeStore } from \"./stores/ui-bridge-store\"\nimport { useUiStore } from \"./stores/ui-store\"\n\nlet bootstrappedClient: BladeClient | null = null\nlet detachHostBridgeListener: (() => void) | null = null\n\nconst stores = {\n answerCallback: useAnswerCallbackStore,\n auth: useAuthStore,\n background: useBackgroundStore,\n cardState: useCardStateStore,\n chat: useChatStore,\n connection: useConnectionStore,\n gis: useGisStore,\n runtime: useRuntimeStore,\n runtimeFeatures: useRuntimeFeaturesStore,\n session: useSessionStore,\n task: useTaskStore,\n ui: useUiStore,\n uiBridge: useUiBridgeStore,\n}\n\nexport function attachClientToStores(client: BladeClient): void {\n for (const store of Object.values(stores)) {\n const state = (store as { getState: () => { setClient: (client: BladeClient) => void } }).getState()\n state.setClient(client)\n }\n bridgeSocketEvents(client, stores)\n detachHostBridgeListener?.()\n detachHostBridgeListener = attachHostBridgeListener(() => {\n const activeSessionId = useSessionStore.getState().activeSessionId\n return {\n sessionId: activeSessionId,\n activeSessionId,\n isStreaming: activeSessionId\n ? (useChatStore.getState().isStreaming[activeSessionId] ?? false)\n : false,\n }\n })\n}\n\nexport function bootstrapBladeClient(options: BladeClientOptions): BladeClient {\n const client = new BladeClient(withStoreAuth(options))\n if (options.token === undefined) {\n client._attachStoreRestTokenResolver(() => useAuthStore.getState().token)\n client._attachStoreSocketTokenResolver(() => {\n const auth = useAuthStore.getState()\n return auth.token ?? auth.socketAuthToken\n })\n }\n attachClientToStores(client)\n bootstrappedClient = client\n return client\n}\n\nexport function getBootstrappedClient(): BladeClient {\n if (!bootstrappedClient) {\n throw new Error(\"bootstrapBladeClient() must be called before any SDK usage\")\n }\n return bootstrappedClient\n}\n\nfunction withStoreAuth(options: BladeClientOptions): BladeClientOptions {\n if (options.token !== undefined) {\n return options\n }\n return {\n ...options,\n onRefreshSuccess: async () => {\n await options.onRefreshSuccess?.()\n useAuthStore.setState({ token: null })\n },\n }\n}\n","import type { BladeClient, BladeClientOptions } from \"../../client\"\nimport { getBootstrappedClient } from \"../bootstrap\"\n\nexport function getBaseUrl(): string {\n return getClient().options.baseUrl\n}\n\nexport function getAuthedUrl(path: string): string {\n return getClient().buildAuthedUrl(path)\n}\n\nexport async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {\n return getClient().jsonFromInit<T>(path, init)\n}\n\nexport async function apiFetchText(path: string, init?: RequestInit): Promise<string> {\n return getClient().textFromInit(path, init)\n}\n\nexport async function apiFetchResponse(path: string, init?: RequestInit): Promise<Response> {\n return getClient().responseFromInit(path, init)\n}\n\nexport function getClient(): BladeClient {\n return getBootstrappedClient()\n}\n\nexport type { BladeClientOptions }\n","import type { SessionsResource } from \"../../client/resources/sessions\"\nimport { getClient } from \"./client\"\nexport type * from \"../../client/resources/sessions\"\n\nconst r = (): SessionsResource => getClient().sessions\n\nexport const listSessions = (...args: Parameters<SessionsResource[\"listSessions\"]>) => r().listSessions(...args)\nexport const listSessionsPaginated = (...args: Parameters<SessionsResource[\"listSessionsPaginated\"]>) => r().listSessionsPaginated(...args)\nexport const listSessionsWithSkillData = (...args: Parameters<SessionsResource[\"listSessionsWithSkillData\"]>) => r().listSessionsWithSkillData(...args)\nexport const createSession = (...args: Parameters<SessionsResource[\"createSession\"]>) => r().createSession(...args)\nexport const createSessionWithRequest = (...args: Parameters<SessionsResource[\"createSessionWithRequest\"]>) => r().createSessionWithRequest(...args)\nexport const getSession = (...args: Parameters<SessionsResource[\"getSession\"]>) => r().getSession(...args)\nexport const updateSession = (...args: Parameters<SessionsResource[\"updateSession\"]>) => r().updateSession(...args)\nexport const pinSession = (...args: Parameters<SessionsResource[\"pinSession\"]>) => r().pinSession(...args)\nexport const startReplaySession = (...args: Parameters<SessionsResource[\"startReplaySession\"]>) => r().startReplaySession(...args)\nexport const updateReplaySession = (...args: Parameters<SessionsResource[\"updateReplaySession\"]>) => r().updateReplaySession(...args)\nexport const updateSharing = (...args: Parameters<SessionsResource[\"updateSharing\"]>) => r().updateSharing(...args)\nexport const updateSessionMemory = (...args: Parameters<SessionsResource[\"updateSessionMemory\"]>) => r().updateSessionMemory(...args)\nexport const createShare = (...args: Parameters<SessionsResource[\"createShare\"]>) => r().createShare(...args)\nexport const revokeShare = (...args: Parameters<SessionsResource[\"revokeShare\"]>) => r().revokeShare(...args)\nexport const getSharedSession = (...args: Parameters<SessionsResource[\"getSharedSession\"]>) => r().getSharedSession(...args)\nexport const getSessionTasks = (...args: Parameters<SessionsResource[\"getSessionTasks\"]>) => r().getSessionTasks(...args)\nexport const getSessionTurns = (...args: Parameters<SessionsResource[\"getSessionTurns\"]>) => r().getSessionTurns(...args)\nexport const getSessionContextStats = (...args: Parameters<SessionsResource[\"getSessionContextStats\"]>) => r().getSessionContextStats(...args)\nexport const getSessionHistory = (...args: Parameters<SessionsResource[\"getSessionHistory\"]>) => r().getSessionHistory(...args)\nexport const tokenizePrompt = (...args: Parameters<SessionsResource[\"tokenizePrompt\"]>) => r().tokenizePrompt(...args)\nexport const tokenizeMessages = (...args: Parameters<SessionsResource[\"tokenizeMessages\"]>) => r().tokenizeMessages(...args)\nexport const getSessionCheckpoints = (...args: Parameters<SessionsResource[\"getSessionCheckpoints\"]>) => r().getSessionCheckpoints(...args)\nexport const checkoutSession = (...args: Parameters<SessionsResource[\"checkoutSession\"]>) => r().checkoutSession(...args)\nexport const rewindSession = (...args: Parameters<SessionsResource[\"rewindSession\"]>) => r().rewindSession(...args)\nexport const switchBranch = (...args: Parameters<SessionsResource[\"switchBranch\"]>) => r().switchBranch(...args)\nexport const deleteSession = (...args: Parameters<SessionsResource[\"deleteSession\"]>) => r().deleteSession(...args)\nexport const listBackgroundTasks = (...args: Parameters<SessionsResource[\"listBackgroundTasks\"]>) => r().listBackgroundTasks(...args)\nexport const getBackgroundTask = (...args: Parameters<SessionsResource[\"getBackgroundTask\"]>) => r().getBackgroundTask(...args)\nexport const stopBackgroundTask = (...args: Parameters<SessionsResource[\"stopBackgroundTask\"]>) => r().stopBackgroundTask(...args)\nexport const listDir = (...args: Parameters<SessionsResource[\"listDir\"]>) => r().listDir(...args)\nexport const uploadFiles = (...args: Parameters<SessionsResource[\"uploadFiles\"]>) => r().uploadFiles(...args)\nexport const deleteFile = (...args: Parameters<SessionsResource[\"deleteFile\"]>) => r().deleteFile(...args)\nexport const writeFile = (...args: Parameters<SessionsResource[\"writeFile\"]>) => r().writeFile(...args)\nexport const renameFile = (...args: Parameters<SessionsResource[\"renameFile\"]>) => r().renameFile(...args)\nexport const copyFile = (...args: Parameters<SessionsResource[\"copyFile\"]>) => r().copyFile(...args)\nexport const shareFile = (...args: Parameters<SessionsResource[\"shareFile\"]>) => r().shareFile(...args)\nexport const getDownloadDirUrl = (...args: Parameters<SessionsResource[\"getDownloadDirUrl\"]>) => r().getDownloadDirUrl(...args)\nexport const exportSession = (...args: Parameters<SessionsResource[\"exportSession\"]>) => r().exportSession(...args)\nexport const previewImport = (...args: Parameters<SessionsResource[\"previewImport\"]>) => r().previewImport(...args)\nexport const importSession = (...args: Parameters<SessionsResource[\"importSession\"]>) => r().importSession(...args)\n","import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\nexport async function copyToClipboard(text: string): Promise<boolean> {\n if (navigator.clipboard) {\n try {\n await navigator.clipboard.writeText(text)\n return true\n } catch {\n // fall through to textarea fallback\n }\n }\n // Fallback for insecure contexts (HTTP non-localhost)\n const textarea = document.createElement(\"textarea\")\n textarea.value = text\n textarea.style.position = \"fixed\"\n textarea.style.opacity = \"0\"\n document.body.appendChild(textarea)\n textarea.select()\n try {\n return document.execCommand(\"copy\")\n } finally {\n document.body.removeChild(textarea)\n }\n}\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\"\nimport {\n ChevronRight,\n Copy,\n Download,\n Link2,\n Loader2,\n Pencil,\n Trash2,\n} from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { useEffect, useEffectEvent, useRef, useState } from \"react\"\nimport * as sessionsApi from \"../../api/sessions\"\nimport { apiFetchText } from \"../../api/client\"\nimport { getAuthedUrl } from \"../../api/client\"\nimport { openSessionFileInPreview } from \"../../lib/open-session-file\"\nimport { cn } from \"../../lib/utils\"\nimport { useChatStore } from \"../../stores/chat-store\"\nimport { useSessionStore } from \"../../stores/session-store\"\nimport { useUiStore } from \"../../stores/ui-store\"\nimport { FileTree as AIFileTree, FileTreeActions, FileTreeIcon, FileTreeName } from \"../ai-elements/file-tree\"\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"../ui/collapsible\"\n\ntype FileEntry = Awaited<ReturnType<typeof sessionsApi.listDir>>[number]\n\nconst ROOT_PATH = \".\"\nconst TREE_ROW_CLASS =\n \"group flex w-full items-center gap-1 rounded px-2 py-1 text-left transition-colors hover:bg-muted/50\"\nconst FILE_ICON_GROUPS: Record<string, string> = {\n csv: \"office\",\n doc: \"office\",\n docx: \"office\",\n ppt: \"office\",\n pptx: \"office\",\n rtf: \"office\",\n tsv: \"office\",\n xls: \"office\",\n xlsx: \"office\",\n odg: \"open-document\",\n odp: \"open-document\",\n ods: \"open-document\",\n odt: \"open-document\",\n epub: \"document\",\n key: \"document\",\n log: \"document\",\n markdown: \"document\",\n md: \"document\",\n numbers: \"document\",\n pages: \"document\",\n pdf: \"document\",\n tex: \"document\",\n txt: \"document\",\n ai: \"image\",\n avif: \"image\",\n bmp: \"image\",\n gif: \"image\",\n heic: \"image\",\n heif: \"image\",\n ico: \"image\",\n jpeg: \"image\",\n jpg: \"image\",\n png: \"image\",\n psd: \"image\",\n svg: \"image\",\n tif: \"image\",\n tiff: \"image\",\n webp: \"image\",\n avi: \"video\",\n flv: \"video\",\n m4v: \"video\",\n mkv: \"video\",\n mov: \"video\",\n mp4: \"video\",\n webm: \"video\",\n wmv: \"video\",\n aac: \"audio\",\n aiff: \"audio\",\n flac: \"audio\",\n m4a: \"audio\",\n mid: \"audio\",\n mp3: \"audio\",\n ogg: \"audio\",\n wav: \"audio\",\n \"7z\": \"archive\",\n bz2: \"archive\",\n dmg: \"archive\",\n gz: \"archive\",\n iso: \"archive\",\n pkg: \"archive\",\n rar: \"archive\",\n tar: \"archive\",\n tgz: \"archive\",\n xz: \"archive\",\n zip: \"archive\",\n c: \"code\",\n cpp: \"code\",\n cs: \"code\",\n css: \"code\",\n go: \"code\",\n h: \"code\",\n hpp: \"code\",\n html: \"code\",\n java: \"code\",\n js: \"code\",\n json: \"code\",\n jsx: \"code\",\n kt: \"code\",\n lock: \"code\",\n php: \"code\",\n py: \"code\",\n rb: \"code\",\n rs: \"code\",\n scss: \"code\",\n sh: \"code\",\n sql: \"code\",\n svelte: \"code\",\n swift: \"code\",\n toml: \"code\",\n ts: \"code\",\n tsx: \"code\",\n vue: \"code\",\n xml: \"code\",\n yaml: \"code\",\n yml: \"code\",\n zsh: \"code\",\n db: \"data\",\n jsonl: \"data\",\n ndjson: \"data\",\n parquet: \"data\",\n plist: \"data\",\n sqlite: \"data\",\n otf: \"font\",\n ttf: \"font\",\n woff: \"font\",\n woff2: \"font\",\n blend: \"design\",\n fig: \"design\",\n sketch: \"design\",\n xd: \"design\",\n conf: \"config\",\n env: \"config\",\n ini: \"config\",\n}\n\ninterface FileTreeProps {\n sessionId: string | null\n checkActiveSession?: boolean\n enableEditor?: boolean\n onExpandedDirsChange?: (dirs: Set<string>) => void\n rootPath?: string\n readOnly?: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n allowDelete?: boolean\n onOpenFile?: (path: string, name: string) => Promise<void>\n}\n\nexport function FileTree({\n sessionId,\n checkActiveSession = true,\n enableEditor = false,\n onExpandedDirsChange,\n rootPath = ROOT_PATH,\n readOnly = false,\n onShareFile,\n allowDelete = false,\n onOpenFile,\n}: FileTreeProps) {\n const pushArtifact = useUiStore((state) => state.pushArtifact)\n const [expandedDirs, setExpandedDirs] = useState<Set<string>>(new Set([rootPath]))\n const [selectedPath, setSelectedPath] = useState<string | undefined>()\n const [openingFilePath, setOpeningFilePath] = useState<string | null>(null)\n const isStreaming = useChatStore((s) => (sessionId ? (s.isStreaming[sessionId] ?? false) : false))\n const queryClient = useQueryClient()\n const prevStreamingRef = useRef(isStreaming)\n\n const applyExpandedDirs = useEffectEvent((dirs: Set<string>) => {\n setExpandedDirs(dirs)\n onExpandedDirsChange?.(dirs)\n })\n\n useEffect(() => {\n if (prevStreamingRef.current && !isStreaming && sessionId) {\n queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n }\n prevStreamingRef.current = isStreaming\n }, [isStreaming, sessionId, queryClient])\n\n const onExpandedDirsChangeStable = useEffectEvent((dirs: Set<string>) => {\n onExpandedDirsChange?.(dirs)\n })\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: mount-only effect, intentionally empty deps\n useEffect(() => {\n onExpandedDirsChangeStable(expandedDirs)\n }, [])\n\n const handleExpandedChange = (dirs: Set<string>) => {\n applyExpandedDirs(dirs)\n }\n\n const toggleExpandedDir = (path: string) => {\n const next = new Set(expandedDirs)\n if (next.has(path)) {\n next.delete(path)\n } else {\n next.add(path)\n }\n handleExpandedChange(next)\n }\n\n const handleTreePathChange = useEffectEvent((previousPath: string, nextPath?: string) => {\n setSelectedPath((current) => {\n if (!current) {\n return current\n }\n if (current === previousPath || current.startsWith(`${previousPath}/`)) {\n if (!nextPath) {\n return undefined\n }\n return current === previousPath\n ? nextPath\n : `${nextPath}${current.slice(previousPath.length)}`\n }\n return current\n })\n\n const nextExpanded = new Set<string>()\n for (const path of expandedDirs) {\n if (path === previousPath || path.startsWith(`${previousPath}/`)) {\n if (nextPath) {\n nextExpanded.add(\n path === previousPath ? nextPath : `${nextPath}${path.slice(previousPath.length)}`,\n )\n }\n continue\n }\n nextExpanded.add(path)\n }\n handleExpandedChange(nextExpanded)\n })\n\n const {\n data: rootEntries,\n isLoading: rootLoading,\n error: rootError,\n } = useQuery({\n queryKey: [\"file-tree\", sessionId, rootPath],\n queryFn: () => sessionsApi.listDir(sessionId!, rootPath),\n enabled: !!sessionId,\n refetchInterval: isStreaming ? 60_000 : false,\n })\n\n const handleOpenFile = useEffectEvent(async (path: string, name: string) => {\n if (!sessionId) return\n const currentSessionId = sessionId\n setOpeningFilePath(path)\n setSelectedPath(path)\n try {\n if (onOpenFile) {\n await onOpenFile(path, name)\n } else if (enableEditor) {\n const content = await apiFetchText(\n `/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(path)}`,\n )\n void content\n alert(\"enableEditor 需在宿主应用中提供 onOpenFile\")\n } else {\n await openSessionFileInPreview(sessionId, path, name, pushArtifact, () => {\n return checkActiveSession ? useSessionStore.getState().activeSessionId === currentSessionId : true\n })\n }\n } finally {\n setOpeningFilePath((current) => (current === path ? null : current))\n }\n })\n\n const handleSelect = (path: string) => {\n setSelectedPath(path)\n }\n\n if (!sessionId) {\n return <EmptyState title=\"暂无活跃会话\" description=\"选择或创建会话后查看工作区文件\" />\n }\n\n if (rootLoading) {\n return (\n <div className=\"flex items-center gap-2 px-3 py-4 text-sm text-[hsl(var(--muted-foreground))]\">\n <Loader2 size={14} className=\"animate-spin\" />\n <span>加载中...</span>\n </div>\n )\n }\n\n if (rootError) {\n return (\n <EmptyState\n title=\"文件树加载失败\"\n description={rootError instanceof Error ? rootError.message : \"加载失败\"}\n />\n )\n }\n\n if (!rootEntries || rootEntries.length === 0) {\n return <EmptyState title=\"当前工作区为空\" description=\"会话产生文件后会显示在这里\" />\n }\n\n return (\n <AIFileTree\n expanded={expandedDirs}\n onExpandedChange={handleExpandedChange}\n selectedPath={selectedPath}\n onSelect={handleSelect}\n className=\"border-none bg-transparent\"\n >\n {rootEntries.map((entry) =>\n entry.is_dir ? (\n <FolderNode\n key={entry.path}\n entry={entry}\n expandedDirs={expandedDirs}\n isStreaming={isStreaming}\n onOpenFile={handleOpenFile}\n onSelectPath={handleSelect}\n onToggleDir={toggleExpandedDir}\n onTreePathChange={handleTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n ) : (\n <FileNode\n key={entry.path}\n entry={entry}\n onOpenFile={handleOpenFile}\n onSelectPath={handleSelect}\n onTreePathChange={handleTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n ),\n )}\n </AIFileTree>\n )\n}\n\nfunction FolderNode({\n entry,\n expandedDirs,\n isStreaming,\n openingFilePath,\n onOpenFile,\n onSelectPath,\n onToggleDir,\n onTreePathChange,\n selectedPath,\n sessionId,\n readOnly,\n onShareFile,\n allowDelete,\n}: {\n entry: FileEntry\n expandedDirs: Set<string>\n isStreaming: boolean\n openingFilePath: string | null\n onOpenFile: (path: string, name: string) => Promise<void>\n onSelectPath: (path: string) => void\n onToggleDir: (path: string) => void\n onTreePathChange: (previousPath: string, nextPath?: string) => void\n selectedPath?: string\n sessionId: string\n readOnly: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n allowDelete: boolean\n}) {\n const queryClient = useQueryClient()\n const isExpanded = expandedDirs.has(entry.path)\n const isSelected = selectedPath === entry.path\n const renameInputRef = useRef<HTMLInputElement>(null)\n const [isRenaming, setIsRenaming] = useState(false)\n const [renamingValue, setRenamingValue] = useState(entry.name)\n const [activeAction, setActiveAction] = useState<\"copy\" | \"delete\" | \"rename\" | \"share\" | null>(null)\n\n const downloadUrl = sessionsApi.getDownloadDirUrl(sessionId, entry.path)\n\n useEffect(() => {\n if (!isRenaming) setRenamingValue(entry.name)\n }, [entry.name, isRenaming])\n\n useEffect(() => {\n if (!isRenaming) return\n renameInputRef.current?.focus()\n renameInputRef.current?.select()\n }, [isRenaming])\n\n const finishRename = useEffectEvent(async () => {\n const nextName = renamingValue.trim()\n if (!nextName || nextName === entry.name) {\n setIsRenaming(false)\n setRenamingValue(entry.name)\n return\n }\n setActiveAction(\"rename\")\n try {\n const result = await sessionsApi.renameFile(sessionId, entry.path, nextName)\n onTreePathChange(entry.path, result.path)\n setIsRenaming(false)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"重命名失败\"))\n renameInputRef.current?.focus()\n renameInputRef.current?.select()\n } finally {\n setActiveAction(null)\n }\n })\n\n const cancelRename = () => {\n setIsRenaming(false)\n setRenamingValue(entry.name)\n }\n\n const handleCopy = async () => {\n setActiveAction(\"copy\")\n try {\n await sessionsApi.copyFile(sessionId, entry.path)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"复制目录失败\"))\n } finally {\n setActiveAction(null)\n }\n }\n\n const handleDelete = async () => {\n if (!window.confirm(`确认删除目录\"${entry.name}\"吗?`)) {\n return\n }\n setActiveAction(\"delete\")\n try {\n await sessionsApi.deleteFile(sessionId, entry.path)\n onTreePathChange(entry.path)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"删除目录失败\"))\n } finally {\n setActiveAction(null)\n }\n }\n\n return (\n <Collapsible open={isExpanded} onOpenChange={() => onToggleDir(entry.path)}>\n <div>\n <div className={cn(TREE_ROW_CLASS, isSelected && \"bg-muted\")}>\n <CollapsibleTrigger asChild>\n <button\n type=\"button\"\n className=\"flex shrink-0 cursor-pointer items-center rounded border-none bg-transparent p-0\"\n title={isExpanded ? \"收起目录\" : \"展开目录\"}\n >\n <ChevronRight\n className={cn(\n \"size-4 shrink-0 text-[hsl(var(--muted-foreground))] transition-transform\",\n isExpanded && \"rotate-90\",\n )}\n />\n </button>\n </CollapsibleTrigger>\n {isRenaming ? (\n <>\n <FileTreeIcon>{getFolderIcon()}</FileTreeIcon>\n <input\n ref={renameInputRef}\n value={renamingValue}\n className=\"h-6 min-w-0 flex-1 rounded border border-[hsl(var(--border))] bg-background px-2 text-xs outline-none ring-offset-background focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))]\"\n disabled={activeAction === \"rename\"}\n onBlur={() => void finishRename()}\n onChange={(event) => setRenamingValue(event.target.value)}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === \"Enter\") {\n event.preventDefault()\n void finishRename()\n } else if (event.key === \"Escape\") {\n event.preventDefault()\n cancelRename()\n }\n }}\n />\n </>\n ) : (\n <button\n type=\"button\"\n className=\"flex min-w-0 flex-1 cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-left\"\n onClick={() => {\n onToggleDir(entry.path)\n onSelectPath(entry.path)\n }}\n >\n <FileTreeIcon>{getFolderIcon()}</FileTreeIcon>\n <FileTreeName className=\"min-w-0 flex-1\">{entry.name}</FileTreeName>\n </button>\n )}\n {!readOnly ? <FileTreeActions className=\"ml-auto hidden shrink-0 group-hover:flex\">\n <TreeActionButton\n onClick={() => {\n const anchor = document.createElement(\"a\")\n anchor.href = downloadUrl\n anchor.download = `${entry.name}.zip`\n anchor.click()\n }}\n title=\"下载目录\"\n >\n <Download size={12} />\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => {\n onSelectPath(entry.path)\n setRenamingValue(entry.name)\n setIsRenaming(true)\n }}\n title=\"重命名\"\n >\n {activeAction === \"rename\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Pencil size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleCopy()}\n title=\"复制目录\"\n >\n {activeAction === \"copy\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Copy size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null || !onShareFile}\n onClick={async () => {\n if (!onShareFile) return\n setActiveAction(\"share\")\n try {\n await onShareFile(entry.path, entry.name)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"共享失败\"))\n } finally {\n setActiveAction(null)\n }\n }}\n title=\"共享到 .share\"\n >\n {activeAction === \"share\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Link2 size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleDelete()}\n title=\"删除目录\"\n >\n {activeAction === \"delete\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Trash2 size={12} />}\n </TreeActionButton>\n </FileTreeActions> : allowDelete ? (\n <FileTreeActions className=\"ml-auto hidden shrink-0 group-hover:flex\">\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleDelete()}\n title=\"删除目录\"\n >\n {activeAction === \"delete\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Trash2 size={12} />}\n </TreeActionButton>\n </FileTreeActions>\n ) : null}\n </div>\n <CollapsibleContent>\n <div className=\"ml-4 border-l pl-2\">\n <LazyDirChildren\n dirPath={entry.path}\n expandedDirs={expandedDirs}\n isStreaming={isStreaming}\n onOpenFile={onOpenFile}\n onSelectPath={onSelectPath}\n onToggleDir={onToggleDir}\n onTreePathChange={onTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n </div>\n </CollapsibleContent>\n </div>\n </Collapsible>\n )\n}\n\nfunction FileNode({\n entry,\n onOpenFile,\n onSelectPath,\n onTreePathChange,\n openingFilePath,\n selectedPath,\n sessionId,\n readOnly,\n onShareFile,\n allowDelete,\n}: {\n entry: FileEntry\n onOpenFile: (path: string, name: string) => Promise<void>\n onSelectPath: (path: string) => void\n onTreePathChange: (previousPath: string, nextPath?: string) => void\n openingFilePath: string | null\n selectedPath?: string\n sessionId: string\n readOnly: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n allowDelete: boolean\n}) {\n const downloadUrl = getAuthedUrl(`/api/sessions/${sessionId}/files/${encodeURIComponent(entry.path)}`)\n const isOpening = openingFilePath === entry.path\n const isSelected = selectedPath === entry.path\n const queryClient = useQueryClient()\n const renameInputRef = useRef<HTMLInputElement>(null)\n const [isRenaming, setIsRenaming] = useState(false)\n const [renamingValue, setRenamingValue] = useState(entry.name)\n const [activeAction, setActiveAction] = useState<\"copy\" | \"delete\" | \"rename\" | \"share\" | null>(null)\n\n useEffect(() => {\n if (!isRenaming) {\n setRenamingValue(entry.name)\n }\n }, [entry.name, isRenaming])\n\n useEffect(() => {\n if (!isRenaming) {\n return\n }\n renameInputRef.current?.focus()\n renameInputRef.current?.select()\n }, [isRenaming])\n\n const finishRename = useEffectEvent(async () => {\n const nextName = renamingValue.trim()\n if (!nextName || nextName === entry.name) {\n setIsRenaming(false)\n setRenamingValue(entry.name)\n return\n }\n\n setActiveAction(\"rename\")\n try {\n const result = await sessionsApi.renameFile(sessionId, entry.path, nextName)\n onTreePathChange(entry.path, result.path)\n setIsRenaming(false)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"重命名失败\"))\n renameInputRef.current?.focus()\n renameInputRef.current?.select()\n } finally {\n setActiveAction(null)\n }\n })\n\n const cancelRename = () => {\n setIsRenaming(false)\n setRenamingValue(entry.name)\n }\n\n const handleCopy = async () => {\n setActiveAction(\"copy\")\n try {\n await sessionsApi.copyFile(sessionId, entry.path)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"复制文件失败\"))\n } finally {\n setActiveAction(null)\n }\n }\n\n const handleDelete = async () => {\n if (!window.confirm(`确认删除\"${entry.name}\"吗?`)) {\n return\n }\n setActiveAction(\"delete\")\n try {\n await sessionsApi.deleteFile(sessionId, entry.path)\n onTreePathChange(entry.path)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"删除文件失败\"))\n } finally {\n setActiveAction(null)\n }\n }\n\n return (\n <div className={cn(TREE_ROW_CLASS, isSelected && \"bg-muted\")}>\n <span className=\"size-4 shrink-0\" />\n <FileTreeIcon>{getFileIcon(entry.name)}</FileTreeIcon>\n {isRenaming ? (\n <input\n ref={renameInputRef}\n value={renamingValue}\n className=\"h-6 min-w-0 flex-1 rounded border border-[hsl(var(--border))] bg-background px-2 text-xs outline-none ring-offset-background focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))]\"\n disabled={activeAction === \"rename\"}\n onBlur={() => void finishRename()}\n onChange={(event) => setRenamingValue(event.target.value)}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === \"Enter\") {\n event.preventDefault()\n void finishRename()\n } else if (event.key === \"Escape\") {\n event.preventDefault()\n cancelRename()\n }\n }}\n />\n ) : (\n <button\n type=\"button\"\n className=\"flex min-w-0 flex-1 cursor-pointer items-center border-none bg-transparent p-0 text-left\"\n onClick={() => {\n onSelectPath(entry.path)\n void onOpenFile(entry.path, entry.name)\n }}\n >\n <FileTreeName className=\"min-w-0 flex-1\">{entry.name}</FileTreeName>\n <FileTagList tags={entry.tags} />\n </button>\n )}\n {isOpening ? (\n <Loader2 size={12} className=\"ml-auto shrink-0 animate-spin text-[hsl(var(--muted-foreground))]\" />\n ) : !readOnly ? (\n <FileTreeActions className=\"ml-auto hidden shrink-0 group-hover:flex\">\n <TreeActionButton\n onClick={() => {\n const anchor = document.createElement(\"a\")\n anchor.href = downloadUrl\n anchor.download = entry.name\n anchor.click()\n }}\n title=\"下载文件\"\n >\n <Download size={12} />\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => {\n onSelectPath(entry.path)\n setRenamingValue(entry.name)\n setIsRenaming(true)\n }}\n title=\"重命名\"\n >\n {activeAction === \"rename\" ? (\n <Loader2 size={12} className=\"animate-spin\" />\n ) : (\n <Pencil size={12} />\n )}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleCopy()}\n title=\"复制文件\"\n >\n {activeAction === \"copy\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Copy size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null || !onShareFile}\n onClick={async () => {\n if (!onShareFile) return\n setActiveAction(\"share\")\n try {\n await onShareFile(entry.path, entry.name)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"共享失败\"))\n } finally {\n setActiveAction(null)\n }\n }}\n title=\"共享到 .share\"\n >\n {activeAction === \"share\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Link2 size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleDelete()}\n title=\"删除文件\"\n >\n {activeAction === \"delete\" ? (\n <Loader2 size={12} className=\"animate-spin\" />\n ) : (\n <Trash2 size={12} />\n )}\n </TreeActionButton>\n </FileTreeActions>\n ) : allowDelete ? (\n <FileTreeActions className=\"ml-auto hidden shrink-0 group-hover:flex\">\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleDelete()}\n title=\"删除文件\"\n >\n {activeAction === \"delete\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Trash2 size={12} />}\n </TreeActionButton>\n </FileTreeActions>\n ) : null}\n </div>\n )\n}\n\nfunction LazyDirChildren({\n dirPath,\n expandedDirs,\n isStreaming,\n onOpenFile,\n onSelectPath,\n onToggleDir,\n onTreePathChange,\n openingFilePath,\n selectedPath,\n sessionId,\n readOnly,\n onShareFile,\n allowDelete,\n}: {\n dirPath: string\n expandedDirs: Set<string>\n isStreaming: boolean\n onOpenFile: (path: string, name: string) => Promise<void>\n onSelectPath: (path: string) => void\n onToggleDir: (path: string) => void\n onTreePathChange: (previousPath: string, nextPath?: string) => void\n openingFilePath: string | null\n selectedPath?: string\n sessionId: string\n readOnly: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n allowDelete: boolean\n}) {\n const {\n data: entries,\n isLoading,\n error,\n } = useQuery({\n queryKey: [\"file-tree\", sessionId, dirPath],\n queryFn: () => sessionsApi.listDir(sessionId, dirPath),\n refetchInterval: isStreaming ? 60_000 : false,\n })\n\n if (isLoading) {\n return (\n <div className=\"flex items-center gap-2 px-2 py-1 text-xs text-[hsl(var(--muted-foreground))]\">\n <Loader2 size={12} className=\"animate-spin\" />\n <span>加载中...</span>\n </div>\n )\n }\n if (error) {\n return (\n <div className=\"px-2 py-1 text-xs text-[hsl(var(--muted-foreground))]\">\n {error instanceof Error ? error.message : \"加载失败\"}\n </div>\n )\n }\n if (!entries || entries.length === 0) {\n return <div className=\"px-2 py-1 text-xs text-[hsl(var(--muted-foreground))]\">空目录</div>\n }\n\n return (\n <>\n {entries.map((child) =>\n child.is_dir ? (\n <FolderNode\n key={child.path}\n entry={child}\n expandedDirs={expandedDirs}\n isStreaming={isStreaming}\n onOpenFile={onOpenFile}\n onSelectPath={onSelectPath}\n onToggleDir={onToggleDir}\n onTreePathChange={onTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n ) : (\n <FileNode\n key={child.path}\n entry={child}\n onOpenFile={onOpenFile}\n onSelectPath={onSelectPath}\n onTreePathChange={onTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n ),\n )}\n </>\n )\n}\n\nfunction FileTagList({ tags }: { tags?: string[] }) {\n if (!tags || tags.length === 0) return null\n return (\n <span className=\"ml-2 flex min-w-0 shrink-0 items-center gap-1\">\n {tags.map((tag) => (\n <span\n key={tag}\n className=\"max-w-20 truncate rounded border border-[hsl(var(--border))] bg-[hsl(var(--muted))] px-1.5 py-0.5 text-[10px] leading-none text-[hsl(var(--muted-foreground))]\"\n title={tag}\n >\n {tag}\n </span>\n ))}\n </span>\n )\n}\n\nfunction TreeActionButton({\n children,\n disabled = false,\n onClick,\n title,\n}: {\n children: ReactNode\n disabled?: boolean\n onClick: () => void\n title: string\n}) {\n return (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={onClick}\n title={title}\n className=\"flex items-center rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {children}\n </button>\n )\n}\n\nfunction EmptyState({ title, description }: { title: string; description: string }) {\n return (\n <div className=\"flex h-full min-h-[12rem] flex-col items-center justify-center gap-3 px-6 text-center\">\n <div className=\"flex h-10 w-10 items-center justify-center text-[hsl(var(--muted-foreground))]\">\n {getFolderIcon()}\n </div>\n <div className=\"space-y-1\">\n <div className=\"text-sm font-medium text-[hsl(var(--foreground))]\">{title}</div>\n <div className=\"text-sm text-[hsl(var(--muted-foreground))]\">{description}</div>\n </div>\n </div>\n )\n}\n\nfunction getFileIcon(fileName: string): ReactNode {\n const ext = fileName.split(\".\").pop()?.toLowerCase() ?? \"\"\n const group = FILE_ICON_GROUPS[ext]\n const src = group ? `/file-icons/${group}/${ext}.png` : \"/file-icons/generic-file.png\"\n return <FileTypeIcon src={src} alt={`${ext || \"file\"} 文件`} />\n}\n\nfunction getFolderIcon(): ReactNode {\n return <FileTypeIcon src=\"/file-icons/folder.png\" alt=\"文件夹\" />\n}\n\nfunction FileTypeIcon({ src, alt }: { src: string; alt: string }) {\n return (\n <img\n src={src}\n alt={alt}\n className=\"size-4 shrink-0 object-contain\"\n draggable={false}\n loading=\"lazy\"\n />\n )\n}\n\nfunction getErrorMessage(error: unknown, fallback: string): string {\n if (error instanceof Error && error.message) {\n return error.message\n }\n return fallback\n}\n","import { apiFetchResponse, getAuthedUrl } from \"../api/client\"\nimport type { PreviewTarget } from \"../stores/ui-store\"\n\nconst IMAGE_EXTS = new Set([\"png\", \"jpg\", \"jpeg\", \"gif\", \"svg\", \"webp\", \"ico\", \"bmp\"])\nconst DOCX_EXTS = new Set([\"docx\", \"doc\"])\nconst EXCEL_EXTS = new Set([\"xlsx\", \"xls\", \"xlsm\", \"xlsb\"])\nconst PPT_EXTS = new Set([\"pptx\", \"ppt\"])\nconst CSV_EXTS = new Set([\"csv\"])\nconst MARKDOWN_EXTS = new Set([\"md\", \"markdown\"])\nconst HTML_EXTS = new Set([\"html\", \"htm\"])\n\nfunction getExt(fileName: string): string {\n return fileName.split(\".\").pop()?.toLowerCase() ?? \"\"\n}\n\nfunction getDisplayFileName(filePath: string, fileName?: string): string {\n return fileName?.trim() || filePath.split(\"/\").pop() || filePath\n}\n\nfunction getSessionFilePath(sessionId: string, filePath: string): string {\n return `/api/sessions/${sessionId}/files/${encodeURIComponent(filePath)}`\n}\n\nfunction getSessionFileDownloadUrl(sessionId: string, filePath: string): string {\n return getAuthedUrl(getSessionFilePath(sessionId, filePath))\n}\n\n/**\n * 后端把可解码为 UTF-8 的文件返回为 text/plain,二进制文件返回各自的 MIME。\n * 只有文本类内容才能安全地整体读进内存渲染——其余一律不能读 body。\n */\nfunction isTextualContentType(contentType: string): boolean {\n return (\n contentType.startsWith(\"text/\") ||\n contentType.includes(\"json\") ||\n contentType.includes(\"xml\") ||\n contentType.includes(\"yaml\") ||\n contentType.includes(\"javascript\")\n )\n}\n\nexport function buildSessionBinaryPreviewTarget(\n sessionId: string,\n filePath: string,\n fileName?: string,\n): PreviewTarget | null {\n const resolvedFileName = getDisplayFileName(filePath, fileName)\n const ext = getExt(resolvedFileName)\n const filePathUrl = getSessionFilePath(sessionId, filePath)\n const downloadUrl = getSessionFileDownloadUrl(sessionId, filePath)\n\n if (IMAGE_EXTS.has(ext)) {\n return { type: \"image\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (ext === \"pdf\") {\n return { type: \"pdf\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (DOCX_EXTS.has(ext)) {\n return { type: \"docx\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (EXCEL_EXTS.has(ext)) {\n return { type: \"excel\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (PPT_EXTS.has(ext)) {\n return {\n type: \"ppt\",\n content: filePathUrl,\n sourceUrl: downloadUrl,\n title: resolvedFileName,\n key: filePath,\n }\n }\n\n return null\n}\n\nexport async function resolveSessionFilePreviewTarget(\n sessionId: string,\n filePath: string,\n fileName?: string,\n): Promise<PreviewTarget> {\n const resolvedFileName = getDisplayFileName(filePath, fileName)\n const binaryTarget = buildSessionBinaryPreviewTarget(sessionId, filePath, resolvedFileName)\n if (binaryTarget) {\n return binaryTarget\n }\n\n const downloadUrl = getSessionFileDownloadUrl(sessionId, filePath)\n\n // 其余文件按后端返回的 Content-Type 分派,而不是猜扩展名:\n // 视频/音频/压缩包等二进制文件绝不能读 body(整体读进内存会卡死页面),\n // 改为把下载 URL 交给原生 <video>/<audio> 流式播放,或回退为下载。\n const res = await apiFetchResponse(getSessionFilePath(sessionId, filePath))\n const contentType = (res.headers.get(\"content-type\") ?? \"\").toLowerCase()\n\n if (contentType.startsWith(\"video/\")) {\n void res.body?.cancel()\n return { type: \"video\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (contentType.startsWith(\"audio/\")) {\n void res.body?.cancel()\n return { type: \"audio\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (!isTextualContentType(contentType)) {\n void res.body?.cancel()\n return { type: \"download\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n const content = await res.text()\n const ext = getExt(resolvedFileName)\n const type: PreviewTarget[\"type\"] = MARKDOWN_EXTS.has(ext)\n ? \"markdown\"\n : CSV_EXTS.has(ext)\n ? \"csv\"\n : HTML_EXTS.has(ext)\n ? \"html\"\n : \"file\"\n\n return {\n type,\n content,\n title: resolvedFileName,\n key: filePath,\n }\n}\n","import type { PreviewTarget } from \"../stores/ui-store\"\nimport { resolveSessionFilePreviewTarget } from \"./session-file-preview\"\n\nexport async function openSessionFileInPreview(\n sessionId: string,\n filePath: string,\n fileName: string,\n pushArtifact: (target: PreviewTarget) => void,\n shouldOpen: () => boolean = () => true,\n): Promise<void> {\n try {\n const target = await resolveSessionFilePreviewTarget(sessionId, filePath, fileName)\n if (!shouldOpen()) {\n return\n }\n pushArtifact(target)\n } catch {\n /* file may not exist */\n }\n}\n","import { Collapsible as CollapsiblePrimitive } from \"radix-ui\"\n\nfunction Collapsible({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />\n}\n\nfunction CollapsibleTrigger({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {\n return (\n <CollapsiblePrimitive.CollapsibleTrigger\n data-slot=\"collapsible-trigger\"\n {...props}\n />\n )\n}\n\nfunction CollapsibleContent({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {\n return (\n <CollapsiblePrimitive.CollapsibleContent\n data-slot=\"collapsible-content\"\n {...props}\n />\n )\n}\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent }\n","\"use client\";\n\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"../ui/collapsible\"\nimport { cn } from \"../../lib/utils\"\nimport {\n ChevronRightIcon,\n FileIcon,\n FolderIcon,\n FolderOpenIcon,\n} from \"lucide-react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from \"react\";\n\ninterface FileTreeContextType {\n expandedPaths: Set<string>;\n togglePath: (path: string) => void;\n selectedPath?: string;\n onSelect?: (path: string) => void;\n}\n\n// Default noop for context default value\n// oxlint-disable-next-line eslint(no-empty-function)\nconst noop = () => {};\n\nconst FileTreeContext = createContext<FileTreeContextType>({\n // oxlint-disable-next-line eslint-plugin-unicorn(no-new-builtin)\n expandedPaths: new Set(),\n togglePath: noop,\n});\n\nexport type FileTreeProps = Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> & {\n expanded?: Set<string>;\n defaultExpanded?: Set<string>;\n selectedPath?: string;\n onSelect?: (path: string) => void;\n onExpandedChange?: (expanded: Set<string>) => void;\n};\n\nexport const FileTree = ({\n expanded: controlledExpanded,\n defaultExpanded = new Set(),\n selectedPath,\n onSelect,\n onExpandedChange,\n className,\n children,\n ...props\n}: FileTreeProps) => {\n const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);\n const expandedPaths = controlledExpanded ?? internalExpanded;\n\n const togglePath = useCallback(\n (path: string) => {\n const newExpanded = new Set(expandedPaths);\n if (newExpanded.has(path)) {\n newExpanded.delete(path);\n } else {\n newExpanded.add(path);\n }\n setInternalExpanded(newExpanded);\n onExpandedChange?.(newExpanded);\n },\n [expandedPaths, onExpandedChange]\n );\n\n const contextValue = useMemo(\n () => ({ expandedPaths, onSelect, selectedPath, togglePath }),\n [expandedPaths, onSelect, selectedPath, togglePath]\n );\n\n return (\n <FileTreeContext.Provider value={contextValue}>\n <div\n className={cn(\n \"rounded-lg border bg-background font-mono text-sm\",\n className\n )}\n role=\"tree\"\n {...props}\n >\n <div className=\"p-2\">{children}</div>\n </div>\n </FileTreeContext.Provider>\n );\n};\n\nexport type FileTreeIconProps = HTMLAttributes<HTMLSpanElement>;\n\nexport const FileTreeIcon = ({\n className,\n children,\n ...props\n}: FileTreeIconProps) => (\n <span className={cn(\"shrink-0\", className)} {...props}>\n {children}\n </span>\n);\n\nexport type FileTreeNameProps = HTMLAttributes<HTMLSpanElement>;\n\nexport const FileTreeName = ({\n className,\n children,\n ...props\n}: FileTreeNameProps) => (\n <span className={cn(\"truncate\", className)} {...props}>\n {children}\n </span>\n);\n\ninterface FileTreeFolderContextType {\n path: string;\n name: string;\n isExpanded: boolean;\n}\n\nconst FileTreeFolderContext = createContext<FileTreeFolderContextType>({\n isExpanded: false,\n name: \"\",\n path: \"\",\n});\n\nexport type FileTreeFolderProps = HTMLAttributes<HTMLDivElement> & {\n path: string;\n name: string;\n};\n\nexport const FileTreeFolder = ({\n path,\n name,\n className,\n children,\n ...props\n}: FileTreeFolderProps) => {\n const { expandedPaths, togglePath, selectedPath, onSelect } =\n useContext(FileTreeContext);\n const isExpanded = expandedPaths.has(path);\n const isSelected = selectedPath === path;\n\n const handleOpenChange = useCallback(() => {\n togglePath(path);\n }, [togglePath, path]);\n\n const handleSelect = useCallback(() => {\n togglePath(path);\n onSelect?.(path);\n }, [togglePath, onSelect, path]);\n\n const folderContextValue = useMemo(\n () => ({ isExpanded, name, path }),\n [isExpanded, name, path]\n );\n\n return (\n <FileTreeFolderContext.Provider value={folderContextValue}>\n <Collapsible onOpenChange={handleOpenChange} open={isExpanded}>\n <div\n className={cn(\"\", className)}\n {...props}\n >\n <div\n className={cn(\n \"flex w-full items-center gap-1 rounded px-2 py-1 text-left transition-colors hover:bg-muted/50\",\n isSelected && \"bg-muted\"\n )}\n >\n <CollapsibleTrigger asChild>\n <button\n className=\"flex shrink-0 cursor-pointer items-center border-none bg-transparent p-0\"\n type=\"button\"\n >\n <ChevronRightIcon\n className={cn(\n \"size-4 shrink-0 text-muted-foreground transition-transform\",\n isExpanded && \"rotate-90\"\n )}\n />\n </button>\n </CollapsibleTrigger>\n <button\n className=\"flex min-w-0 flex-1 cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-left\"\n onClick={handleSelect}\n type=\"button\"\n >\n <FileTreeIcon>\n {isExpanded ? (\n <FolderOpenIcon className=\"size-4 text-primary\" />\n ) : (\n <FolderIcon className=\"size-4 text-primary\" />\n )}\n </FileTreeIcon>\n <FileTreeName>{name}</FileTreeName>\n </button>\n </div>\n <CollapsibleContent>\n <div className=\"ml-4 border-l pl-2\">{children}</div>\n </CollapsibleContent>\n </div>\n </Collapsible>\n </FileTreeFolderContext.Provider>\n );\n};\n\ninterface FileTreeFileContextType {\n path: string;\n name: string;\n}\n\nconst FileTreeFileContext = createContext<FileTreeFileContextType>({\n name: \"\",\n path: \"\",\n});\n\nexport type FileTreeFileProps = HTMLAttributes<HTMLButtonElement> & {\n path: string;\n name: string;\n icon?: ReactNode;\n};\n\nexport const FileTreeFile = ({\n path,\n name,\n icon,\n className,\n children,\n ...props\n}: FileTreeFileProps) => {\n const { selectedPath, onSelect } = useContext(FileTreeContext);\n const isSelected = selectedPath === path;\n\n const handleClick = useCallback(() => {\n onSelect?.(path);\n }, [onSelect, path]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n onSelect?.(path);\n }\n },\n [onSelect, path]\n );\n\n const fileContextValue = useMemo(() => ({ name, path }), [name, path]);\n\n return (\n <FileTreeFileContext.Provider value={fileContextValue}>\n <button\n type=\"button\"\n className={cn(\n \"flex w-full cursor-pointer items-center gap-1 rounded border-none bg-transparent px-2 py-1 text-left text-inherit transition-colors hover:bg-muted/50\",\n isSelected && \"bg-muted\",\n className\n )}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {children ?? (\n <>\n {/* Spacer for alignment */}\n <span className=\"size-4 shrink-0\" />\n <FileTreeIcon>\n {icon ?? <FileIcon className=\"size-4 text-muted-foreground\" />}\n </FileTreeIcon>\n <FileTreeName>{name}</FileTreeName>\n </>\n )}\n </button>\n </FileTreeFileContext.Provider>\n );\n};\n\nexport type FileTreeActionsProps = HTMLAttributes<HTMLDivElement>;\n\nconst stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();\n\nexport const FileTreeActions = ({\n className,\n children,\n ...props\n}: FileTreeActionsProps) => (\n <div\n className={cn(\"ml-auto flex items-center gap-1\", className)}\n onClick={stopPropagation}\n onKeyDown={stopPropagation}\n {...props}\n >\n {children}\n </div>\n);\n"],"mappings":"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","names":["useQueryClient","Loader2","useEffect","useMemo","useRef","useState","type","create","create","create","create","create","toolCall","create","create","create","extractModeFromBlocks","create","create","create","create","create","create","create","create","create","create","create","create","create","create","create","create","r","r","r","r","r","r","r","r","r","r","useState","type","jsx","jsx","Fragment","jsx","jsxs","FileTree","useState","jsx","jsxs","useState","useQueryClient","useRef","useMemo","type","title","useEffect","Loader2","FileTree"]}
1
+ {"version":3,"sources":["../../../../src/react/components/workspace/WorkspaceFilesPanel.tsx","../../../../src/react/components/workspace/FileTree.tsx","../../../../src/react/lib/open-session-file.ts","../../../../src/react/components/ai-elements/file-tree.tsx"],"sourcesContent":["import { useQueryClient } from \"@tanstack/react-query\"\nimport { FileText, FolderTree, FolderUp, Loader2, PanelRightOpen, RefreshCw, Upload } from \"lucide-react\"\nimport { type ChangeEvent, type ReactNode, useEffect, useMemo, useRef, useState } from \"react\"\nimport { uploadFiles } from \"../../api/sessions\"\nimport { isUiMeta } from \"../../lib/ui-meta\"\nimport { cn } from \"../../lib/utils\"\nimport type { PreviewTarget } from \"../../stores/ui-store\"\nimport { useChatStore } from \"../../stores/chat-store\"\nimport { useUiStore } from \"../../stores/ui-store\"\nimport { FileTree } from \"./FileTree\"\n\ninterface WorkspaceFilesPanelProps {\n sessionId: string | null\n enableEditor?: boolean\n className?: string\n treeContainerClassName?: string\n title?: string\n rootPath?: string\n readOnly?: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n onSync?: () => Promise<void>\n allowDelete?: boolean\n /** Optional slot above the file tree (e.g. phase progress in host app). */\n topSlot?: ReactNode\n /** Custom file open handler; used when enableEditor is false if provided. */\n onOpenFile?: (path: string, name: string) => Promise<void>\n}\n\ntype WorkspaceTab = \"files\" | \"resources\"\n\ninterface InlineResourceItem {\n key: string\n title: string\n target: PreviewTarget\n}\n\nconst EMPTY_MESSAGES: ReturnType<typeof useChatStore.getState>[\"messages\"][string] = []\n\nexport function WorkspaceFilesPanel({\n sessionId,\n enableEditor = false,\n className,\n treeContainerClassName,\n title = \"工作区\",\n rootPath = \".\",\n readOnly = false,\n onShareFile,\n onSync,\n allowDelete = false,\n topSlot,\n onOpenFile,\n}: WorkspaceFilesPanelProps) {\n const [expandedDirs, setExpandedDirs] = useState<Set<string>>(new Set())\n const [isUploading, setIsUploading] = useState(false)\n const [activeTab, setActiveTab] = useState<WorkspaceTab>(\"files\")\n const [isSyncing, setIsSyncing] = useState(false)\n const queryClient = useQueryClient()\n const fileInputRef = useRef<HTMLInputElement>(null)\n const folderInputRef = useRef<HTMLInputElement>(null)\n const messages = useChatStore((state) =>\n sessionId ? (state.messages[sessionId] ?? EMPTY_MESSAGES) : EMPTY_MESSAGES,\n )\n const pushArtifact = useUiStore((state) => state.pushArtifact)\n\n const inlineResources = useMemo<InlineResourceItem[]>(() => {\n if (!sessionId) return []\n return messages.flatMap((message, messageIndex) =>\n (message.blocks ?? []).flatMap((block, blockIndex): InlineResourceItem[] => {\n if (block.type !== \"tool_ui\" || !isUiMeta(block.content) || block.content.target !== \"inline\") {\n return []\n }\n\n const ui = block.content\n const type = ui.resourceHTML ? \"resource-html\" : \"resource-uri\"\n const content = ui.resourceHTML ?? ui.resourceUri ?? ui.resourceURI ?? \"\"\n if (!content) return []\n\n const title =\n ui.title ??\n (typeof block.display_name === \"string\" && block.display_name.trim().length > 0\n ? block.display_name\n : \"可视化产物\")\n const key = `${message.entry_id ?? message.timestamp ?? messageIndex}-${block.tool_call_id ?? \"ui\"}-${blockIndex}`\n\n return [\n {\n key,\n title,\n target: {\n type,\n content,\n title,\n key,\n bridgeSessionId: sessionId,\n },\n },\n ]\n }),\n )\n }, [messages, sessionId])\n\n useEffect(() => {\n if (activeTab === \"resources\" && inlineResources.length === 0) {\n setActiveTab(\"files\")\n }\n }, [activeTab, inlineResources.length])\n\n const uploadTargetDir = () => {\n const candidates = Array.from(expandedDirs)\n .filter((path) => path !== \".\")\n .filter((path) => {\n const parts = path.split(\"/\").filter(Boolean)\n for (let index = 1; index < parts.length; index += 1) {\n const ancestor = parts.slice(0, index).join(\"/\")\n if (!expandedDirs.has(ancestor)) return false\n }\n return true\n })\n\n if (candidates.length === 0) {\n return \".\"\n }\n\n return candidates.reduce((deepestPath, currentPath) => {\n const deepestDepth = deepestPath.split(\"/\").filter(Boolean).length\n const currentDepth = currentPath.split(\"/\").filter(Boolean).length\n return currentDepth > deepestDepth ? currentPath : deepestPath\n }, candidates[0] ?? \".\")\n }\n\n const handleUploadChange = async (event: ChangeEvent<HTMLInputElement>) => {\n const files = event.target.files\n if (!sessionId || !files || files.length === 0) {\n event.target.value = \"\"\n return\n }\n\n setIsUploading(true)\n try {\n const targetDir = uploadTargetDir()\n const result = await uploadFiles(sessionId, targetDir, files)\n await queryClient.invalidateQueries({\n queryKey: [\"file-tree\", sessionId],\n refetchType: \"all\",\n })\n await queryClient.refetchQueries({\n queryKey: [\"file-tree\", sessionId],\n type: \"active\",\n })\n\n if (targetDir !== \".\") {\n setExpandedDirs((prev) => {\n const next = new Set(prev)\n next.add(targetDir)\n return next\n })\n }\n\n if (result.failed.length > 0) {\n alert(`部分文件上传失败: ${result.failed.join(\", \")}`)\n }\n } catch (error) {\n console.error(\"Upload files failed\", error)\n alert(\"上传文件失败\")\n } finally {\n setIsUploading(false)\n event.target.value = \"\"\n }\n }\n\n const handleUploadClick = () => {\n fileInputRef.current?.click()\n }\n\n const handleFolderUploadClick = () => {\n folderInputRef.current?.click()\n }\n\n return (\n <div className={cn(\"flex flex-col\", className)}>\n {topSlot ? <div className=\"mb-3\">{topSlot}</div> : null}\n <div className=\"mb-2 flex shrink-0 items-center justify-between gap-2\">\n <div className=\"flex min-w-0 items-center gap-1 rounded-lg bg-[hsl(var(--muted))]/60 p-0.5 text-[11px] font-medium\">\n <button\n type=\"button\"\n onClick={() => setActiveTab(\"files\")}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-2 py-1 transition-colors\",\n activeTab === \"files\"\n ? \"bg-[hsl(var(--background))] text-[hsl(var(--foreground))] shadow-sm\"\n : \"text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]\",\n )}\n >\n <FolderTree size={12} />\n {title}\n </button>\n {inlineResources.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => setActiveTab(\"resources\")}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-2 py-1 transition-colors\",\n activeTab === \"resources\"\n ? \"bg-[hsl(var(--background))] text-[hsl(var(--foreground))] shadow-sm\"\n : \"text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]\",\n )}\n >\n <PanelRightOpen size={12} />\n 可视化产物\n <span className=\"font-mono text-[10px] text-[hsl(var(--muted-foreground))]\">\n {inlineResources.length}\n </span>\n </button>\n ) : null}\n </div>\n {onSync ? <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n disabled={!sessionId || isSyncing}\n onClick={async () => {\n setIsSyncing(true)\n try {\n await onSync()\n await queryClient.invalidateQueries({\n queryKey: [\"file-tree\", sessionId],\n refetchType: \"all\",\n })\n } finally {\n setIsSyncing(false)\n }\n }}\n className=\"rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"同步共享区\"\n >\n {isSyncing ? <Loader2 size={12} className=\"animate-spin\" /> : <RefreshCw size={12} />}\n </button>\n </div> : null}\n {!readOnly && activeTab === \"files\" ? <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n disabled={!sessionId || isUploading}\n onClick={handleUploadClick}\n className=\"rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"上传文件\"\n >\n {isUploading ? <Loader2 size={12} className=\"animate-spin\" /> : <Upload size={12} />}\n </button>\n <button\n type=\"button\"\n disabled={!sessionId || isUploading}\n onClick={handleFolderUploadClick}\n className=\"rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50\"\n title=\"上传文件夹\"\n >\n {isUploading ? <Loader2 size={12} className=\"animate-spin\" /> : <FolderUp size={12} />}\n </button>\n </div> : null}\n </div>\n {activeTab === \"resources\" && inlineResources.length > 0 ? (\n <div className={cn(\"min-h-0 overflow-auto\", treeContainerClassName)}>\n <div className=\"flex flex-col gap-1.5\">\n {inlineResources.map((item, index) => (\n <button\n key={item.key}\n type=\"button\"\n onClick={() => pushArtifact(item.target)}\n className=\"group flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors hover:bg-[hsl(var(--muted))]\"\n title={item.title}\n >\n <FileText size={14} className=\"shrink-0 text-[hsl(var(--primary))]\" />\n <span className=\"min-w-0 flex-1 truncate text-[hsl(var(--foreground))]\">\n {item.title}\n </span>\n <span className=\"shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]\">\n {index + 1}\n </span>\n </button>\n ))}\n </div>\n </div>\n ) : (\n <div className={treeContainerClassName}>\n <FileTree\n key={sessionId ?? \"workspace-empty\"}\n sessionId={sessionId}\n enableEditor={enableEditor}\n onExpandedDirsChange={setExpandedDirs}\n rootPath={rootPath}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n onOpenFile={onOpenFile}\n />\n </div>\n )}\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n className=\"hidden\"\n onChange={handleUploadChange}\n />\n <input\n ref={folderInputRef}\n type=\"file\"\n multiple\n className=\"hidden\"\n onChange={handleUploadChange}\n // biome-ignore lint/suspicious/noExplicitAny: webkitdirectory is a non-standard HTML attribute\n {...({ webkitdirectory: \"\" } as any)}\n />\n </div>\n )\n}\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\"\nimport {\n ChevronRight,\n Copy,\n Download,\n Link2,\n Loader2,\n Pencil,\n Trash2,\n} from \"lucide-react\"\nimport type { ReactNode } from \"react\"\nimport { useEffect, useEffectEvent, useRef, useState } from \"react\"\nimport * as sessionsApi from \"../../api/sessions\"\nimport { apiFetchText } from \"../../api/client\"\nimport { getAuthedUrl } from \"../../api/client\"\nimport { openSessionFileInPreview } from \"../../lib/open-session-file\"\nimport { cn } from \"../../lib/utils\"\nimport { useChatStore } from \"../../stores/chat-store\"\nimport { useSessionStore } from \"../../stores/session-store\"\nimport { useUiStore } from \"../../stores/ui-store\"\nimport { FileTree as AIFileTree, FileTreeActions, FileTreeIcon, FileTreeName } from \"../ai-elements/file-tree\"\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"../ui/collapsible\"\n\ntype FileEntry = Awaited<ReturnType<typeof sessionsApi.listDir>>[number]\n\nconst ROOT_PATH = \".\"\nconst TREE_ROW_CLASS =\n \"group flex w-full items-center gap-1 rounded px-2 py-1 text-left transition-colors hover:bg-muted/50\"\nconst FILE_ICON_GROUPS: Record<string, string> = {\n csv: \"office\",\n doc: \"office\",\n docx: \"office\",\n ppt: \"office\",\n pptx: \"office\",\n rtf: \"office\",\n tsv: \"office\",\n xls: \"office\",\n xlsx: \"office\",\n odg: \"open-document\",\n odp: \"open-document\",\n ods: \"open-document\",\n odt: \"open-document\",\n epub: \"document\",\n key: \"document\",\n log: \"document\",\n markdown: \"document\",\n md: \"document\",\n numbers: \"document\",\n pages: \"document\",\n pdf: \"document\",\n tex: \"document\",\n txt: \"document\",\n ai: \"image\",\n avif: \"image\",\n bmp: \"image\",\n gif: \"image\",\n heic: \"image\",\n heif: \"image\",\n ico: \"image\",\n jpeg: \"image\",\n jpg: \"image\",\n png: \"image\",\n psd: \"image\",\n svg: \"image\",\n tif: \"image\",\n tiff: \"image\",\n webp: \"image\",\n avi: \"video\",\n flv: \"video\",\n m4v: \"video\",\n mkv: \"video\",\n mov: \"video\",\n mp4: \"video\",\n webm: \"video\",\n wmv: \"video\",\n aac: \"audio\",\n aiff: \"audio\",\n flac: \"audio\",\n m4a: \"audio\",\n mid: \"audio\",\n mp3: \"audio\",\n ogg: \"audio\",\n wav: \"audio\",\n \"7z\": \"archive\",\n bz2: \"archive\",\n dmg: \"archive\",\n gz: \"archive\",\n iso: \"archive\",\n pkg: \"archive\",\n rar: \"archive\",\n tar: \"archive\",\n tgz: \"archive\",\n xz: \"archive\",\n zip: \"archive\",\n c: \"code\",\n cpp: \"code\",\n cs: \"code\",\n css: \"code\",\n go: \"code\",\n h: \"code\",\n hpp: \"code\",\n html: \"code\",\n java: \"code\",\n js: \"code\",\n json: \"code\",\n jsx: \"code\",\n kt: \"code\",\n lock: \"code\",\n php: \"code\",\n py: \"code\",\n rb: \"code\",\n rs: \"code\",\n scss: \"code\",\n sh: \"code\",\n sql: \"code\",\n svelte: \"code\",\n swift: \"code\",\n toml: \"code\",\n ts: \"code\",\n tsx: \"code\",\n vue: \"code\",\n xml: \"code\",\n yaml: \"code\",\n yml: \"code\",\n zsh: \"code\",\n db: \"data\",\n jsonl: \"data\",\n ndjson: \"data\",\n parquet: \"data\",\n plist: \"data\",\n sqlite: \"data\",\n otf: \"font\",\n ttf: \"font\",\n woff: \"font\",\n woff2: \"font\",\n blend: \"design\",\n fig: \"design\",\n sketch: \"design\",\n xd: \"design\",\n conf: \"config\",\n env: \"config\",\n ini: \"config\",\n}\n\ninterface FileTreeProps {\n sessionId: string | null\n checkActiveSession?: boolean\n enableEditor?: boolean\n onExpandedDirsChange?: (dirs: Set<string>) => void\n rootPath?: string\n readOnly?: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n allowDelete?: boolean\n onOpenFile?: (path: string, name: string) => Promise<void>\n}\n\nexport function FileTree({\n sessionId,\n checkActiveSession = true,\n enableEditor = false,\n onExpandedDirsChange,\n rootPath = ROOT_PATH,\n readOnly = false,\n onShareFile,\n allowDelete = false,\n onOpenFile,\n}: FileTreeProps) {\n const pushArtifact = useUiStore((state) => state.pushArtifact)\n const [expandedDirs, setExpandedDirs] = useState<Set<string>>(new Set([rootPath]))\n const [selectedPath, setSelectedPath] = useState<string | undefined>()\n const [openingFilePath, setOpeningFilePath] = useState<string | null>(null)\n const isStreaming = useChatStore((s) => (sessionId ? (s.isStreaming[sessionId] ?? false) : false))\n const queryClient = useQueryClient()\n const prevStreamingRef = useRef(isStreaming)\n\n const applyExpandedDirs = useEffectEvent((dirs: Set<string>) => {\n setExpandedDirs(dirs)\n onExpandedDirsChange?.(dirs)\n })\n\n useEffect(() => {\n if (prevStreamingRef.current && !isStreaming && sessionId) {\n queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n }\n prevStreamingRef.current = isStreaming\n }, [isStreaming, sessionId, queryClient])\n\n const onExpandedDirsChangeStable = useEffectEvent((dirs: Set<string>) => {\n onExpandedDirsChange?.(dirs)\n })\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: mount-only effect, intentionally empty deps\n useEffect(() => {\n onExpandedDirsChangeStable(expandedDirs)\n }, [])\n\n const handleExpandedChange = (dirs: Set<string>) => {\n applyExpandedDirs(dirs)\n }\n\n const toggleExpandedDir = (path: string) => {\n const next = new Set(expandedDirs)\n if (next.has(path)) {\n next.delete(path)\n } else {\n next.add(path)\n }\n handleExpandedChange(next)\n }\n\n const handleTreePathChange = useEffectEvent((previousPath: string, nextPath?: string) => {\n setSelectedPath((current) => {\n if (!current) {\n return current\n }\n if (current === previousPath || current.startsWith(`${previousPath}/`)) {\n if (!nextPath) {\n return undefined\n }\n return current === previousPath\n ? nextPath\n : `${nextPath}${current.slice(previousPath.length)}`\n }\n return current\n })\n\n const nextExpanded = new Set<string>()\n for (const path of expandedDirs) {\n if (path === previousPath || path.startsWith(`${previousPath}/`)) {\n if (nextPath) {\n nextExpanded.add(\n path === previousPath ? nextPath : `${nextPath}${path.slice(previousPath.length)}`,\n )\n }\n continue\n }\n nextExpanded.add(path)\n }\n handleExpandedChange(nextExpanded)\n })\n\n const {\n data: rootEntries,\n isLoading: rootLoading,\n error: rootError,\n } = useQuery({\n queryKey: [\"file-tree\", sessionId, rootPath],\n queryFn: () => sessionsApi.listDir(sessionId!, rootPath),\n enabled: !!sessionId,\n refetchInterval: isStreaming ? 60_000 : false,\n })\n\n const handleOpenFile = useEffectEvent(async (path: string, name: string) => {\n if (!sessionId) return\n const currentSessionId = sessionId\n setOpeningFilePath(path)\n setSelectedPath(path)\n try {\n if (onOpenFile) {\n await onOpenFile(path, name)\n } else if (enableEditor) {\n const content = await apiFetchText(\n `/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(path)}`,\n )\n void content\n alert(\"enableEditor 需在宿主应用中提供 onOpenFile\")\n } else {\n await openSessionFileInPreview(sessionId, path, name, pushArtifact, () => {\n return checkActiveSession ? useSessionStore.getState().activeSessionId === currentSessionId : true\n })\n }\n } finally {\n setOpeningFilePath((current) => (current === path ? null : current))\n }\n })\n\n const handleSelect = (path: string) => {\n setSelectedPath(path)\n }\n\n if (!sessionId) {\n return <EmptyState title=\"暂无活跃会话\" description=\"选择或创建会话后查看工作区文件\" />\n }\n\n if (rootLoading) {\n return (\n <div className=\"flex items-center gap-2 px-3 py-4 text-sm text-[hsl(var(--muted-foreground))]\">\n <Loader2 size={14} className=\"animate-spin\" />\n <span>加载中...</span>\n </div>\n )\n }\n\n if (rootError) {\n return (\n <EmptyState\n title=\"文件树加载失败\"\n description={rootError instanceof Error ? rootError.message : \"加载失败\"}\n />\n )\n }\n\n if (!rootEntries || rootEntries.length === 0) {\n return <EmptyState title=\"当前工作区为空\" description=\"会话产生文件后会显示在这里\" />\n }\n\n return (\n <AIFileTree\n expanded={expandedDirs}\n onExpandedChange={handleExpandedChange}\n selectedPath={selectedPath}\n onSelect={handleSelect}\n className=\"border-none bg-transparent\"\n >\n {rootEntries.map((entry) =>\n entry.is_dir ? (\n <FolderNode\n key={entry.path}\n entry={entry}\n expandedDirs={expandedDirs}\n isStreaming={isStreaming}\n onOpenFile={handleOpenFile}\n onSelectPath={handleSelect}\n onToggleDir={toggleExpandedDir}\n onTreePathChange={handleTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n ) : (\n <FileNode\n key={entry.path}\n entry={entry}\n onOpenFile={handleOpenFile}\n onSelectPath={handleSelect}\n onTreePathChange={handleTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n ),\n )}\n </AIFileTree>\n )\n}\n\nfunction FolderNode({\n entry,\n expandedDirs,\n isStreaming,\n openingFilePath,\n onOpenFile,\n onSelectPath,\n onToggleDir,\n onTreePathChange,\n selectedPath,\n sessionId,\n readOnly,\n onShareFile,\n allowDelete,\n}: {\n entry: FileEntry\n expandedDirs: Set<string>\n isStreaming: boolean\n openingFilePath: string | null\n onOpenFile: (path: string, name: string) => Promise<void>\n onSelectPath: (path: string) => void\n onToggleDir: (path: string) => void\n onTreePathChange: (previousPath: string, nextPath?: string) => void\n selectedPath?: string\n sessionId: string\n readOnly: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n allowDelete: boolean\n}) {\n const queryClient = useQueryClient()\n const isExpanded = expandedDirs.has(entry.path)\n const isSelected = selectedPath === entry.path\n const renameInputRef = useRef<HTMLInputElement>(null)\n const [isRenaming, setIsRenaming] = useState(false)\n const [renamingValue, setRenamingValue] = useState(entry.name)\n const [activeAction, setActiveAction] = useState<\"copy\" | \"delete\" | \"rename\" | \"share\" | null>(null)\n\n const downloadUrl = sessionsApi.getDownloadDirUrl(sessionId, entry.path)\n\n useEffect(() => {\n if (!isRenaming) setRenamingValue(entry.name)\n }, [entry.name, isRenaming])\n\n useEffect(() => {\n if (!isRenaming) return\n renameInputRef.current?.focus()\n renameInputRef.current?.select()\n }, [isRenaming])\n\n const finishRename = useEffectEvent(async () => {\n const nextName = renamingValue.trim()\n if (!nextName || nextName === entry.name) {\n setIsRenaming(false)\n setRenamingValue(entry.name)\n return\n }\n setActiveAction(\"rename\")\n try {\n const result = await sessionsApi.renameFile(sessionId, entry.path, nextName)\n onTreePathChange(entry.path, result.path)\n setIsRenaming(false)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"重命名失败\"))\n renameInputRef.current?.focus()\n renameInputRef.current?.select()\n } finally {\n setActiveAction(null)\n }\n })\n\n const cancelRename = () => {\n setIsRenaming(false)\n setRenamingValue(entry.name)\n }\n\n const handleCopy = async () => {\n setActiveAction(\"copy\")\n try {\n await sessionsApi.copyFile(sessionId, entry.path)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"复制目录失败\"))\n } finally {\n setActiveAction(null)\n }\n }\n\n const handleDelete = async () => {\n if (!window.confirm(`确认删除目录\"${entry.name}\"吗?`)) {\n return\n }\n setActiveAction(\"delete\")\n try {\n await sessionsApi.deleteFile(sessionId, entry.path)\n onTreePathChange(entry.path)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"删除目录失败\"))\n } finally {\n setActiveAction(null)\n }\n }\n\n return (\n <Collapsible open={isExpanded} onOpenChange={() => onToggleDir(entry.path)}>\n <div>\n <div className={cn(TREE_ROW_CLASS, isSelected && \"bg-muted\")}>\n <CollapsibleTrigger asChild>\n <button\n type=\"button\"\n className=\"flex shrink-0 cursor-pointer items-center rounded border-none bg-transparent p-0\"\n title={isExpanded ? \"收起目录\" : \"展开目录\"}\n >\n <ChevronRight\n className={cn(\n \"size-4 shrink-0 text-[hsl(var(--muted-foreground))] transition-transform\",\n isExpanded && \"rotate-90\",\n )}\n />\n </button>\n </CollapsibleTrigger>\n {isRenaming ? (\n <>\n <FileTreeIcon>{getFolderIcon()}</FileTreeIcon>\n <input\n ref={renameInputRef}\n value={renamingValue}\n className=\"h-6 min-w-0 flex-1 rounded border border-[hsl(var(--border))] bg-background px-2 text-xs outline-none ring-offset-background focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))]\"\n disabled={activeAction === \"rename\"}\n onBlur={() => void finishRename()}\n onChange={(event) => setRenamingValue(event.target.value)}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === \"Enter\") {\n event.preventDefault()\n void finishRename()\n } else if (event.key === \"Escape\") {\n event.preventDefault()\n cancelRename()\n }\n }}\n />\n </>\n ) : (\n <button\n type=\"button\"\n className=\"flex min-w-0 flex-1 cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-left\"\n onClick={() => {\n onToggleDir(entry.path)\n onSelectPath(entry.path)\n }}\n >\n <FileTreeIcon>{getFolderIcon()}</FileTreeIcon>\n <FileTreeName className=\"min-w-0 flex-1\">{entry.name}</FileTreeName>\n </button>\n )}\n {!readOnly ? <FileTreeActions className=\"ml-auto hidden shrink-0 group-hover:flex\">\n <TreeActionButton\n onClick={() => {\n const anchor = document.createElement(\"a\")\n anchor.href = downloadUrl\n anchor.download = `${entry.name}.zip`\n anchor.click()\n }}\n title=\"下载目录\"\n >\n <Download size={12} />\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => {\n onSelectPath(entry.path)\n setRenamingValue(entry.name)\n setIsRenaming(true)\n }}\n title=\"重命名\"\n >\n {activeAction === \"rename\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Pencil size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleCopy()}\n title=\"复制目录\"\n >\n {activeAction === \"copy\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Copy size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null || !onShareFile}\n onClick={async () => {\n if (!onShareFile) return\n setActiveAction(\"share\")\n try {\n await onShareFile(entry.path, entry.name)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"共享失败\"))\n } finally {\n setActiveAction(null)\n }\n }}\n title=\"共享到 .share\"\n >\n {activeAction === \"share\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Link2 size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleDelete()}\n title=\"删除目录\"\n >\n {activeAction === \"delete\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Trash2 size={12} />}\n </TreeActionButton>\n </FileTreeActions> : allowDelete ? (\n <FileTreeActions className=\"ml-auto hidden shrink-0 group-hover:flex\">\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleDelete()}\n title=\"删除目录\"\n >\n {activeAction === \"delete\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Trash2 size={12} />}\n </TreeActionButton>\n </FileTreeActions>\n ) : null}\n </div>\n <CollapsibleContent>\n <div className=\"ml-4 border-l pl-2\">\n <LazyDirChildren\n dirPath={entry.path}\n expandedDirs={expandedDirs}\n isStreaming={isStreaming}\n onOpenFile={onOpenFile}\n onSelectPath={onSelectPath}\n onToggleDir={onToggleDir}\n onTreePathChange={onTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n </div>\n </CollapsibleContent>\n </div>\n </Collapsible>\n )\n}\n\nfunction FileNode({\n entry,\n onOpenFile,\n onSelectPath,\n onTreePathChange,\n openingFilePath,\n selectedPath,\n sessionId,\n readOnly,\n onShareFile,\n allowDelete,\n}: {\n entry: FileEntry\n onOpenFile: (path: string, name: string) => Promise<void>\n onSelectPath: (path: string) => void\n onTreePathChange: (previousPath: string, nextPath?: string) => void\n openingFilePath: string | null\n selectedPath?: string\n sessionId: string\n readOnly: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n allowDelete: boolean\n}) {\n const downloadUrl = getAuthedUrl(`/api/sessions/${sessionId}/files/${encodeURIComponent(entry.path)}`)\n const isOpening = openingFilePath === entry.path\n const isSelected = selectedPath === entry.path\n const queryClient = useQueryClient()\n const renameInputRef = useRef<HTMLInputElement>(null)\n const [isRenaming, setIsRenaming] = useState(false)\n const [renamingValue, setRenamingValue] = useState(entry.name)\n const [activeAction, setActiveAction] = useState<\"copy\" | \"delete\" | \"rename\" | \"share\" | null>(null)\n\n useEffect(() => {\n if (!isRenaming) {\n setRenamingValue(entry.name)\n }\n }, [entry.name, isRenaming])\n\n useEffect(() => {\n if (!isRenaming) {\n return\n }\n renameInputRef.current?.focus()\n renameInputRef.current?.select()\n }, [isRenaming])\n\n const finishRename = useEffectEvent(async () => {\n const nextName = renamingValue.trim()\n if (!nextName || nextName === entry.name) {\n setIsRenaming(false)\n setRenamingValue(entry.name)\n return\n }\n\n setActiveAction(\"rename\")\n try {\n const result = await sessionsApi.renameFile(sessionId, entry.path, nextName)\n onTreePathChange(entry.path, result.path)\n setIsRenaming(false)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"重命名失败\"))\n renameInputRef.current?.focus()\n renameInputRef.current?.select()\n } finally {\n setActiveAction(null)\n }\n })\n\n const cancelRename = () => {\n setIsRenaming(false)\n setRenamingValue(entry.name)\n }\n\n const handleCopy = async () => {\n setActiveAction(\"copy\")\n try {\n await sessionsApi.copyFile(sessionId, entry.path)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"复制文件失败\"))\n } finally {\n setActiveAction(null)\n }\n }\n\n const handleDelete = async () => {\n if (!window.confirm(`确认删除\"${entry.name}\"吗?`)) {\n return\n }\n setActiveAction(\"delete\")\n try {\n await sessionsApi.deleteFile(sessionId, entry.path)\n onTreePathChange(entry.path)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"删除文件失败\"))\n } finally {\n setActiveAction(null)\n }\n }\n\n return (\n <div className={cn(TREE_ROW_CLASS, isSelected && \"bg-muted\")}>\n <span className=\"size-4 shrink-0\" />\n <FileTreeIcon>{getFileIcon(entry.name)}</FileTreeIcon>\n {isRenaming ? (\n <input\n ref={renameInputRef}\n value={renamingValue}\n className=\"h-6 min-w-0 flex-1 rounded border border-[hsl(var(--border))] bg-background px-2 text-xs outline-none ring-offset-background focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))]\"\n disabled={activeAction === \"rename\"}\n onBlur={() => void finishRename()}\n onChange={(event) => setRenamingValue(event.target.value)}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === \"Enter\") {\n event.preventDefault()\n void finishRename()\n } else if (event.key === \"Escape\") {\n event.preventDefault()\n cancelRename()\n }\n }}\n />\n ) : (\n <button\n type=\"button\"\n className=\"flex min-w-0 flex-1 cursor-pointer items-center border-none bg-transparent p-0 text-left\"\n onClick={() => {\n onSelectPath(entry.path)\n void onOpenFile(entry.path, entry.name)\n }}\n >\n <FileTreeName className=\"min-w-0 flex-1\">{entry.name}</FileTreeName>\n <FileTagList tags={entry.tags} />\n </button>\n )}\n {isOpening ? (\n <Loader2 size={12} className=\"ml-auto shrink-0 animate-spin text-[hsl(var(--muted-foreground))]\" />\n ) : !readOnly ? (\n <FileTreeActions className=\"ml-auto hidden shrink-0 group-hover:flex\">\n <TreeActionButton\n onClick={() => {\n const anchor = document.createElement(\"a\")\n anchor.href = downloadUrl\n anchor.download = entry.name\n anchor.click()\n }}\n title=\"下载文件\"\n >\n <Download size={12} />\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => {\n onSelectPath(entry.path)\n setRenamingValue(entry.name)\n setIsRenaming(true)\n }}\n title=\"重命名\"\n >\n {activeAction === \"rename\" ? (\n <Loader2 size={12} className=\"animate-spin\" />\n ) : (\n <Pencil size={12} />\n )}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleCopy()}\n title=\"复制文件\"\n >\n {activeAction === \"copy\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Copy size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null || !onShareFile}\n onClick={async () => {\n if (!onShareFile) return\n setActiveAction(\"share\")\n try {\n await onShareFile(entry.path, entry.name)\n await queryClient.invalidateQueries({ queryKey: [\"file-tree\", sessionId] })\n } catch (error) {\n alert(getErrorMessage(error, \"共享失败\"))\n } finally {\n setActiveAction(null)\n }\n }}\n title=\"共享到 .share\"\n >\n {activeAction === \"share\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Link2 size={12} />}\n </TreeActionButton>\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleDelete()}\n title=\"删除文件\"\n >\n {activeAction === \"delete\" ? (\n <Loader2 size={12} className=\"animate-spin\" />\n ) : (\n <Trash2 size={12} />\n )}\n </TreeActionButton>\n </FileTreeActions>\n ) : allowDelete ? (\n <FileTreeActions className=\"ml-auto hidden shrink-0 group-hover:flex\">\n <TreeActionButton\n disabled={activeAction !== null}\n onClick={() => void handleDelete()}\n title=\"删除文件\"\n >\n {activeAction === \"delete\" ? <Loader2 size={12} className=\"animate-spin\" /> : <Trash2 size={12} />}\n </TreeActionButton>\n </FileTreeActions>\n ) : null}\n </div>\n )\n}\n\nfunction LazyDirChildren({\n dirPath,\n expandedDirs,\n isStreaming,\n onOpenFile,\n onSelectPath,\n onToggleDir,\n onTreePathChange,\n openingFilePath,\n selectedPath,\n sessionId,\n readOnly,\n onShareFile,\n allowDelete,\n}: {\n dirPath: string\n expandedDirs: Set<string>\n isStreaming: boolean\n onOpenFile: (path: string, name: string) => Promise<void>\n onSelectPath: (path: string) => void\n onToggleDir: (path: string) => void\n onTreePathChange: (previousPath: string, nextPath?: string) => void\n openingFilePath: string | null\n selectedPath?: string\n sessionId: string\n readOnly: boolean\n onShareFile?: (path: string, name: string) => Promise<void>\n allowDelete: boolean\n}) {\n const {\n data: entries,\n isLoading,\n error,\n } = useQuery({\n queryKey: [\"file-tree\", sessionId, dirPath],\n queryFn: () => sessionsApi.listDir(sessionId, dirPath),\n refetchInterval: isStreaming ? 60_000 : false,\n })\n\n if (isLoading) {\n return (\n <div className=\"flex items-center gap-2 px-2 py-1 text-xs text-[hsl(var(--muted-foreground))]\">\n <Loader2 size={12} className=\"animate-spin\" />\n <span>加载中...</span>\n </div>\n )\n }\n if (error) {\n return (\n <div className=\"px-2 py-1 text-xs text-[hsl(var(--muted-foreground))]\">\n {error instanceof Error ? error.message : \"加载失败\"}\n </div>\n )\n }\n if (!entries || entries.length === 0) {\n return <div className=\"px-2 py-1 text-xs text-[hsl(var(--muted-foreground))]\">空目录</div>\n }\n\n return (\n <>\n {entries.map((child) =>\n child.is_dir ? (\n <FolderNode\n key={child.path}\n entry={child}\n expandedDirs={expandedDirs}\n isStreaming={isStreaming}\n onOpenFile={onOpenFile}\n onSelectPath={onSelectPath}\n onToggleDir={onToggleDir}\n onTreePathChange={onTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n ) : (\n <FileNode\n key={child.path}\n entry={child}\n onOpenFile={onOpenFile}\n onSelectPath={onSelectPath}\n onTreePathChange={onTreePathChange}\n openingFilePath={openingFilePath}\n selectedPath={selectedPath}\n sessionId={sessionId}\n readOnly={readOnly}\n onShareFile={onShareFile}\n allowDelete={allowDelete}\n />\n ),\n )}\n </>\n )\n}\n\nfunction FileTagList({ tags }: { tags?: string[] }) {\n if (!tags || tags.length === 0) return null\n return (\n <span className=\"ml-2 flex min-w-0 shrink-0 items-center gap-1\">\n {tags.map((tag) => (\n <span\n key={tag}\n className=\"max-w-20 truncate rounded border border-[hsl(var(--border))] bg-[hsl(var(--muted))] px-1.5 py-0.5 text-[10px] leading-none text-[hsl(var(--muted-foreground))]\"\n title={tag}\n >\n {tag}\n </span>\n ))}\n </span>\n )\n}\n\nfunction TreeActionButton({\n children,\n disabled = false,\n onClick,\n title,\n}: {\n children: ReactNode\n disabled?: boolean\n onClick: () => void\n title: string\n}) {\n return (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={onClick}\n title={title}\n className=\"flex items-center rounded p-1 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--muted-foreground))/10] hover:text-[hsl(var(--foreground))] disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {children}\n </button>\n )\n}\n\nfunction EmptyState({ title, description }: { title: string; description: string }) {\n return (\n <div className=\"flex h-full min-h-[12rem] flex-col items-center justify-center gap-3 px-6 text-center\">\n <div className=\"flex h-10 w-10 items-center justify-center text-[hsl(var(--muted-foreground))]\">\n {getFolderIcon()}\n </div>\n <div className=\"space-y-1\">\n <div className=\"text-sm font-medium text-[hsl(var(--foreground))]\">{title}</div>\n <div className=\"text-sm text-[hsl(var(--muted-foreground))]\">{description}</div>\n </div>\n </div>\n )\n}\n\nfunction getFileIcon(fileName: string): ReactNode {\n const ext = fileName.split(\".\").pop()?.toLowerCase() ?? \"\"\n const group = FILE_ICON_GROUPS[ext]\n const src = group ? `/file-icons/${group}/${ext}.png` : \"/file-icons/generic-file.png\"\n return <FileTypeIcon src={src} alt={`${ext || \"file\"} 文件`} />\n}\n\nfunction getFolderIcon(): ReactNode {\n return <FileTypeIcon src=\"/file-icons/folder.png\" alt=\"文件夹\" />\n}\n\nfunction FileTypeIcon({ src, alt }: { src: string; alt: string }) {\n return (\n <img\n src={src}\n alt={alt}\n className=\"size-4 shrink-0 object-contain\"\n draggable={false}\n loading=\"lazy\"\n />\n )\n}\n\nfunction getErrorMessage(error: unknown, fallback: string): string {\n if (error instanceof Error && error.message) {\n return error.message\n }\n return fallback\n}\n","import type { PreviewTarget } from \"../stores/ui-store\"\nimport { resolveSessionFilePreviewTarget } from \"./session-file-preview\"\n\nexport async function openSessionFileInPreview(\n sessionId: string,\n filePath: string,\n fileName: string,\n pushArtifact: (target: PreviewTarget) => void,\n shouldOpen: () => boolean = () => true,\n): Promise<void> {\n try {\n const target = await resolveSessionFilePreviewTarget(sessionId, filePath, fileName)\n if (!shouldOpen()) {\n return\n }\n pushArtifact(target)\n } catch {\n /* file may not exist */\n }\n}\n","\"use client\";\n\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"../ui/collapsible\"\nimport { cn } from \"../../lib/utils\"\nimport {\n ChevronRightIcon,\n FileIcon,\n FolderIcon,\n FolderOpenIcon,\n} from \"lucide-react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from \"react\";\n\ninterface FileTreeContextType {\n expandedPaths: Set<string>;\n togglePath: (path: string) => void;\n selectedPath?: string;\n onSelect?: (path: string) => void;\n}\n\n// Default noop for context default value\n// oxlint-disable-next-line eslint(no-empty-function)\nconst noop = () => {};\n\nconst FileTreeContext = createContext<FileTreeContextType>({\n // oxlint-disable-next-line eslint-plugin-unicorn(no-new-builtin)\n expandedPaths: new Set(),\n togglePath: noop,\n});\n\nexport type FileTreeProps = Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> & {\n expanded?: Set<string>;\n defaultExpanded?: Set<string>;\n selectedPath?: string;\n onSelect?: (path: string) => void;\n onExpandedChange?: (expanded: Set<string>) => void;\n};\n\nexport const FileTree = ({\n expanded: controlledExpanded,\n defaultExpanded = new Set(),\n selectedPath,\n onSelect,\n onExpandedChange,\n className,\n children,\n ...props\n}: FileTreeProps) => {\n const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);\n const expandedPaths = controlledExpanded ?? internalExpanded;\n\n const togglePath = useCallback(\n (path: string) => {\n const newExpanded = new Set(expandedPaths);\n if (newExpanded.has(path)) {\n newExpanded.delete(path);\n } else {\n newExpanded.add(path);\n }\n setInternalExpanded(newExpanded);\n onExpandedChange?.(newExpanded);\n },\n [expandedPaths, onExpandedChange]\n );\n\n const contextValue = useMemo(\n () => ({ expandedPaths, onSelect, selectedPath, togglePath }),\n [expandedPaths, onSelect, selectedPath, togglePath]\n );\n\n return (\n <FileTreeContext.Provider value={contextValue}>\n <div\n className={cn(\n \"rounded-lg border bg-background font-mono text-sm\",\n className\n )}\n role=\"tree\"\n {...props}\n >\n <div className=\"p-2\">{children}</div>\n </div>\n </FileTreeContext.Provider>\n );\n};\n\nexport type FileTreeIconProps = HTMLAttributes<HTMLSpanElement>;\n\nexport const FileTreeIcon = ({\n className,\n children,\n ...props\n}: FileTreeIconProps) => (\n <span className={cn(\"shrink-0\", className)} {...props}>\n {children}\n </span>\n);\n\nexport type FileTreeNameProps = HTMLAttributes<HTMLSpanElement>;\n\nexport const FileTreeName = ({\n className,\n children,\n ...props\n}: FileTreeNameProps) => (\n <span className={cn(\"truncate\", className)} {...props}>\n {children}\n </span>\n);\n\ninterface FileTreeFolderContextType {\n path: string;\n name: string;\n isExpanded: boolean;\n}\n\nconst FileTreeFolderContext = createContext<FileTreeFolderContextType>({\n isExpanded: false,\n name: \"\",\n path: \"\",\n});\n\nexport type FileTreeFolderProps = HTMLAttributes<HTMLDivElement> & {\n path: string;\n name: string;\n};\n\nexport const FileTreeFolder = ({\n path,\n name,\n className,\n children,\n ...props\n}: FileTreeFolderProps) => {\n const { expandedPaths, togglePath, selectedPath, onSelect } =\n useContext(FileTreeContext);\n const isExpanded = expandedPaths.has(path);\n const isSelected = selectedPath === path;\n\n const handleOpenChange = useCallback(() => {\n togglePath(path);\n }, [togglePath, path]);\n\n const handleSelect = useCallback(() => {\n togglePath(path);\n onSelect?.(path);\n }, [togglePath, onSelect, path]);\n\n const folderContextValue = useMemo(\n () => ({ isExpanded, name, path }),\n [isExpanded, name, path]\n );\n\n return (\n <FileTreeFolderContext.Provider value={folderContextValue}>\n <Collapsible onOpenChange={handleOpenChange} open={isExpanded}>\n <div\n className={cn(\"\", className)}\n {...props}\n >\n <div\n className={cn(\n \"flex w-full items-center gap-1 rounded px-2 py-1 text-left transition-colors hover:bg-muted/50\",\n isSelected && \"bg-muted\"\n )}\n >\n <CollapsibleTrigger asChild>\n <button\n className=\"flex shrink-0 cursor-pointer items-center border-none bg-transparent p-0\"\n type=\"button\"\n >\n <ChevronRightIcon\n className={cn(\n \"size-4 shrink-0 text-muted-foreground transition-transform\",\n isExpanded && \"rotate-90\"\n )}\n />\n </button>\n </CollapsibleTrigger>\n <button\n className=\"flex min-w-0 flex-1 cursor-pointer items-center gap-1 border-none bg-transparent p-0 text-left\"\n onClick={handleSelect}\n type=\"button\"\n >\n <FileTreeIcon>\n {isExpanded ? (\n <FolderOpenIcon className=\"size-4 text-primary\" />\n ) : (\n <FolderIcon className=\"size-4 text-primary\" />\n )}\n </FileTreeIcon>\n <FileTreeName>{name}</FileTreeName>\n </button>\n </div>\n <CollapsibleContent>\n <div className=\"ml-4 border-l pl-2\">{children}</div>\n </CollapsibleContent>\n </div>\n </Collapsible>\n </FileTreeFolderContext.Provider>\n );\n};\n\ninterface FileTreeFileContextType {\n path: string;\n name: string;\n}\n\nconst FileTreeFileContext = createContext<FileTreeFileContextType>({\n name: \"\",\n path: \"\",\n});\n\nexport type FileTreeFileProps = HTMLAttributes<HTMLButtonElement> & {\n path: string;\n name: string;\n icon?: ReactNode;\n};\n\nexport const FileTreeFile = ({\n path,\n name,\n icon,\n className,\n children,\n ...props\n}: FileTreeFileProps) => {\n const { selectedPath, onSelect } = useContext(FileTreeContext);\n const isSelected = selectedPath === path;\n\n const handleClick = useCallback(() => {\n onSelect?.(path);\n }, [onSelect, path]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n onSelect?.(path);\n }\n },\n [onSelect, path]\n );\n\n const fileContextValue = useMemo(() => ({ name, path }), [name, path]);\n\n return (\n <FileTreeFileContext.Provider value={fileContextValue}>\n <button\n type=\"button\"\n className={cn(\n \"flex w-full cursor-pointer items-center gap-1 rounded border-none bg-transparent px-2 py-1 text-left text-inherit transition-colors hover:bg-muted/50\",\n isSelected && \"bg-muted\",\n className\n )}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {children ?? (\n <>\n {/* Spacer for alignment */}\n <span className=\"size-4 shrink-0\" />\n <FileTreeIcon>\n {icon ?? <FileIcon className=\"size-4 text-muted-foreground\" />}\n </FileTreeIcon>\n <FileTreeName>{name}</FileTreeName>\n </>\n )}\n </button>\n </FileTreeFileContext.Provider>\n );\n};\n\nexport type FileTreeActionsProps = HTMLAttributes<HTMLDivElement>;\n\nconst stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();\n\nexport const FileTreeActions = ({\n className,\n children,\n ...props\n}: FileTreeActionsProps) => (\n <div\n className={cn(\"ml-auto flex items-center gap-1\", className)}\n onClick={stopPropagation}\n onKeyDown={stopPropagation}\n {...props}\n >\n {children}\n </div>\n);\n"],"mappings":"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","names":["useQueryClient","Loader2","useEffect","useMemo","useRef","useState","useState","Fragment","jsx","jsxs","FileTree","useState","jsx","jsxs","useState","useQueryClient","useRef","useMemo","title","useEffect","Loader2","FileTree"]}