@zzusp/ccsm 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +236 -236
- package/bin/cli.mjs +52 -52
- package/dist/assets/{DiskUsage-CKhggLs5.js → DiskUsage-BY6XwffG.js} +2 -2
- package/dist/assets/DiskUsage-BY6XwffG.js.map +1 -0
- package/dist/assets/{ImportPage-wge4VhZ-.js → ImportPage-Cwq5bx7G.js} +2 -2
- package/dist/assets/ImportPage-Cwq5bx7G.js.map +1 -0
- package/dist/assets/MarkdownContent-BFu7Nkk_.js +2 -0
- package/dist/assets/MarkdownContent-BFu7Nkk_.js.map +1 -0
- package/dist/assets/{ProjectMemory-Q4XX40j_.js → ProjectMemory-CcE3KbUK.js} +2 -2
- package/dist/assets/ProjectMemory-CcE3KbUK.js.map +1 -0
- package/dist/assets/index-CrWxV6sb.css +1 -0
- package/dist/assets/index-DTbWl1jb.js +11 -0
- package/dist/assets/index-DTbWl1jb.js.map +1 -0
- package/dist/assets/markdown-Bag5rX3T.js +30 -0
- package/dist/assets/markdown-Bag5rX3T.js.map +1 -0
- package/dist/index.html +26 -26
- package/package.json +81 -83
- package/server/index.ts +130 -130
- package/server/lib/active-sessions.test.ts +119 -119
- package/server/lib/active-sessions.ts +95 -95
- package/server/lib/bundle.test.ts +182 -182
- package/server/lib/bundle.ts +86 -86
- package/server/lib/claude-paths.test.ts +126 -126
- package/server/lib/claude-paths.ts +43 -43
- package/server/lib/cleanup-suggestions.ts +131 -131
- package/server/lib/constants.ts +8 -8
- package/server/lib/delete-project.ts +100 -100
- package/server/lib/delete.test.ts +244 -244
- package/server/lib/delete.ts +192 -192
- package/server/lib/disk-usage.ts +81 -81
- package/server/lib/encode-cwd.ts +24 -24
- package/server/lib/export-bundle.ts +236 -236
- package/server/lib/export-import-bundle.test.ts +337 -337
- package/server/lib/fs-size.ts +38 -38
- package/server/lib/import-bundle.ts +488 -488
- package/server/lib/load-memory.ts +120 -120
- package/server/lib/load-session.ts +209 -209
- package/server/lib/modified-files.test.ts +280 -280
- package/server/lib/modified-files.ts +228 -228
- package/server/lib/open-folder.ts +47 -47
- package/server/lib/parse-jsonl.ts +160 -139
- package/server/lib/port.ts +23 -23
- package/server/lib/safe-id.test.ts +41 -41
- package/server/lib/safe-id.ts +6 -6
- package/server/lib/safe-remove.test.ts +73 -73
- package/server/lib/safe-remove.ts +25 -25
- package/server/lib/scan.ts +289 -286
- package/server/lib/search-all.ts +130 -130
- package/server/lib/search-session.ts +203 -203
- package/server/lib/system-tags.ts +20 -20
- package/server/lib/update.ts +67 -67
- package/server/lib/version.test.ts +39 -39
- package/server/lib/version.ts +117 -117
- package/server/routes/disk-cleanup.ts +54 -54
- package/server/routes/disk.ts +9 -9
- package/server/routes/import.ts +87 -87
- package/server/routes/projects.ts +104 -104
- package/server/routes/search.ts +79 -79
- package/server/routes/sessions.ts +130 -130
- package/server/routes/version.ts +34 -34
- package/server/types.ts +1 -1
- package/shared/constants.ts +7 -7
- package/shared/types.ts +513 -511
- package/dist/assets/DiskUsage-CKhggLs5.js.map +0 -1
- package/dist/assets/ImportPage-wge4VhZ-.js.map +0 -1
- package/dist/assets/ProjectMemory-Q4XX40j_.js.map +0 -1
- package/dist/assets/index-7aMrnHJG.js +0 -7
- package/dist/assets/index-7aMrnHJG.js.map +0 -1
- package/dist/assets/index-BOeI_J4B.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"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","names":["Loading","label","className","jsxs","jsx","LoadingDots","KB","MB","GB","formatBytes","bytes","formatRelativeTime","iso","t","diffMs","sec","min","hr","day","mo","formatDateTime","d","isMac","useGlobalHotkey","combo","handler","useEffect","onKey","e","HOTKEY_HINT","STORAGE_KEY","DICT","readInitial","saved","_locale","listeners","setLocale","next","fn","useLocale","locale","setLocaleState","useState","l","setNext","useCallback","toggle","useT","key","params","translate","str","k","v","streamSearch","opts","res","text","reader","buffer","value","done","event","parseLine","nl","raw","line","MIN_QUERY","DEBOUNCE_MS","SearchModal","open","onClose","navigate","useNavigate","query","setQuery","hits","setHits","setDone","loading","setLoading","error","setError","activeIndex","setActiveIndex","inputRef","useRef","listRef","controllerRef","flatItems","useMemo","out","flat","hit","hitIndex","snippet","snippetIndex","trimmedQuery","id","runSearch","_a","raf","onWindowKey","q","controller","prev","err","navigateToSnippet","onKeyDown","i","item","el","showEmpty","showRefine","showNoResults","SearchIcon","Hint","SessionGroup","onPick","onHover","title","projectTail","f","active","kindLabel","kind","children","tone","cls","LocaleToggle","Pill","onClick","useTheme","theme","setThemeState","setTheme","mq","ThemeToggle","isDark","MoonIcon","SunIcon","api","path","init","detail","parsed","queryKeys","projectId","sessionId","UPDATE_COMMAND","useVersionInfo","useQuery","VersionNotice","setOpen","data","Fragment","createPortal","AnimatePresence","VersionModal","info","mutation","useMutation","isPendingRef","result","hasUpdate","notes","published","updateDone","showUpdateAction","motion","VersionChip","UpdateOutcome","ReleaseNotes","ExternalLink","Spinner","OutputBlock","CommandHint","CopyButton","output","copied","setCopied","href","accent","source","renderBlocks","isBlockStart","src","lines","at","n","buf","h","level","renderInline","ordered","items","para","INLINE_RE","nodes","last","m","tok","lm","NAV","FolderIcon","p","DiskIcon","ImportIcon","Sidebar","onSearchOpen","pathname","useLocation","Brand","MenuIcon","isActive","Link","Glyph","ITEM_BASE","Breadcrumbs","c","family","inner","ChevronSep","BreadcrumbFolderIcon","ExportDialog","sessions","destDir","setDestDir","s","showResult","canSubmit","PageHeader","eyebrow","actions","meta","editableValue","onTitleEdit","TitleSlot","TITLE_CLASS","editing","setEditing","draft","setDraft","submitting","setSubmitting","startEdit","commit","PencilIcon","MetaItem","Sep","RECENT_ACTIVITY_WINDOW_MIN","MAX_SESSION_MESSAGES","INTERRUPTED_MARKER_RE","variantOf","StatusDot","session","withLabel","labelClass","Dot","variant","staggerParent","fadeUpItem","ProjectDetail","queryClient","useQueryClient","useParams","selected","setSelected","selectMode","setSelectMode","showExport","setShowExport","bulkProgress","setBulkProgress","bulkOutcome","setBulkOutcome","bulkDetailOpen","setBulkDetailOpen","sessionsQuery","projectsQuery","memoryCount","revealMutation","project","selectedSessions","sessionsToExport","projectBytes","a","totalBytes","workingCount","liveCount","recentCount","sid","toggleAll","enterSelectMode","exitSelectMode","runBulkDelete","targets","outcome","cwd","parts","tail","head","FolderOpenIcon","BrainIcon","ExportIcon","CheckSquareIcon","BulkBar","isSel","displayTitle","breakdown","TrashIcon","selectedCount","totalCount","progress","detailOpen","onToggleAll","onDelete","onCancel","onToggleDetail","onDismiss","allSelected","isBusy","BulkOutcomeRibbon","hasIssue","detailToggleable","DeleteProjectDialog","blockers","hasBlockers","totalDeletedBytes","ProjectsList","pendingDelete","setPendingDelete","health","projects","list","acc","totalSessions","lastActive","x","Masthead","Admonition","Ledger","tagline","stats","onRequestDelete","LedgerRow","index","ChevronRight","colors","DeleteDialog","onDeleted","willSkip","willDelete","totalFree","removed","skipReason","highlight","lowerText","lowerQuery","segments","cursor","idx","HighlightedText","seg","rowsFromHunks","hunks","rows","prevNewEnd","gap","oldNo","newNo","dels","adds","delStart","addStart","paired","wordSegments","rowsFromStrings","oldStr","newStr","ops","diffOps","op","cur","WORD_RE","oldLine","newLine","b","left","right","pushSeg","arr","changed","LCS_CELL_CAP","dp","row","j","PREVIEW_CHARS","MAX_DIFF_ROWS","ToolUseBlock","block","input","asRecord","summary","toolSummary","searching","Caret","JsonDump","ToolUseBody","name","body","buildToolBody","FilePathLine","DiffRows","BashBody","TodoList","strOf","edits","er","todos","todosOf","firstLine","pathTail","r","todo","isDone","command","description","json","shown","omitted","DiffRowLine","bg","marker","markerColor","hl","ToolResultBlock","toolName","long","visible","ThinkingBlock","hasText","CheckIcon","CopyIcon","common","MarkdownContent","lazy","__vitePreload","MessageBubble","message","toolNames","SystemMessage","AssistantMessage","UserMessage","isTool","borderClass","Avatar","Header","Blocks","align","model","ts","blocks","markdown","plain","Suspense","WorkingIndicator","role","CONTENT_MIN_PX","CONV_INITIAL_VISIBLE","CONV_LOAD_STEP","CONV_BOTTOM_STICK_PX","ModifiedFilesDrawer","files","editLookup","messages","isWorking","onOpenFile","tree","buildTree","selectedFile","allFolders","collectFolderPaths","collapsed","setCollapsed","toggleFolder","splitRef","convScrollRef","convWidth","setConvWidth","railWidth","setRailWidth","visibleCount","setVisibleCount","startIndex","visibleMessages","hiddenCount","restoreFromBottom","stickToBottom","prevMsgCount","prevIsWorking","useLayoutEffect","onConvScroll","grew","startedWorking","showEarlier","prevOverflow","count","countLabel","TreeGlyph","CloseIcon","Splitter","clientX","rect","clampWidth","FileDetail","node","TreeRow","nodeKey","getRect","onResize","dragging","available","max","fileChangeType","file","first","changeToneClass","type","root","display","segs","materialize","folders","child","collapseChain","z","fileNodes","folder","only","walk","ns","depth","onToggleFolder","onSelectFile","indent","isCollapsed","isSelected","openLabel","changeType","nameTone","FileIcon","ExternalIcon","newFile","buildFileRows","ToolChip","SplitDiff","sorted","rec","toSplitRows","EmptyBody","split","SplitLine","side","cell","errored","color","INITIAL_WINDOW","LOAD_STEP","LIVE_POLL_INTERVAL_MS","LIVE_WINDOW_MS","BOTTOM_STICK_THRESHOLD_PX","isWithinLiveWindow","lastAt","ms","lastTurnIncomplete","SessionDetailRoute","searchParams","useSearchParams","pid","urlFocus","urlQuery","showMeta","setShowMeta","onlyUser","setOnlyUser","onlyError","setOnlyError","deferredQuery","useDeferredValue","windowSize","setWindowSize","showDeleteDialog","setShowDeleteDialog","showModifiedDrawer","setShowModifiedDrawer","railRef","followerRef","pinTop","setPinTop","stuck","setStuck","urlAppliedRef","flashedKeyRef","stickToBottomRef","prevMsgCountRef","prevIsWorkingRef","isLoading","isLive","projectSessionsQuery","currentSummary","deleteTooltip","modifiedFilesQuery","modifiedFiles","openFileMutation","filePath","indexed","indexMessage","map","conversationMessages","isUserTyped","hasError","skipWindowing","renderList","hasMoreEarlier","target","needed","rafId","flashTarget","onScroll","distance","measure","ro","frame","check","schedule","sessionTitle","renameMutation","customTitle","taglineBranchPart","crumbs","compactTitle","SessionMasthead","deletedIds","_b","CompactMasthead","FilterLedger","w","isMeta","ScrollToEdges","firstAt","messageCount","version","branch","renameDisabled","renameTooltip","deleteDisabled","deleteLabel","onOpenModified","modifiedCount","modifiedLoading","dateline","formatDateline","FilesIcon","Fact","StatusBeacon","MASTHEAD_TITLE_CLASS","isFallback","disabled","disabledTooltip","onQuery","onShowMeta","onOnlyUser","onOnlyError","total","hasData","ToggleSwitch","checked","onChange","blockText","EDGE_THRESHOLD","showTop","setShowTop","showBottom","setShowBottom","update","scrollY","viewport","buttonClass","ChevronIcon","direction","DiskUsage","ProjectMemory","ImportPage","App","searchOpen","setSearchOpen","toggleSearch","openSearch","closeSearch","Routes","Route","RouteFallback","SessionDetail","QueryClient","rootEl","ReactDOM","React","QueryClientProvider","BrowserRouter"],"ignoreList":[],"sources":["../../web/src/components/Loading.tsx","../../web/src/lib/format.ts","../../web/src/lib/hotkeys.ts","../../web/src/lib/i18n.ts","../../web/src/lib/search-stream.ts","../../web/src/components/SearchModal.tsx","../../web/src/components/LocaleToggle.tsx","../../web/src/lib/theme.ts","../../web/src/components/ThemeToggle.tsx","../../web/src/lib/api.ts","../../web/src/lib/query-keys.ts","../../web/src/components/VersionNotice.tsx","../../web/src/components/Sidebar.tsx","../../web/src/components/Breadcrumbs.tsx","../../web/src/components/ExportDialog.tsx","../../web/src/components/PageHeader.tsx","../../shared/constants.ts","../../web/src/components/StatusDot.tsx","../../web/src/lib/motion.ts","../../web/src/routes/ProjectDetail.tsx","../../web/src/components/DeleteProjectDialog.tsx","../../web/src/routes/ProjectsList.tsx","../../web/src/components/DeleteDialog.tsx","../../web/src/lib/highlight.ts","../../web/src/components/HighlightedText.tsx","../../web/src/lib/diff.ts","../../web/src/components/ToolBlock.tsx","../../web/src/components/MessageBubble.tsx","../../web/src/components/ModifiedFilesDrawer.tsx","../../web/src/routes/SessionDetail.tsx","../../web/src/App.tsx","../../web/src/main.tsx"],"sourcesContent":["type LoadingProps = {\n label: string;\n className?: string;\n};\n\nexport function Loading({ label, className }: LoadingProps) {\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={['flex flex-col gap-2.5', className].filter(Boolean).join(' ')}\n >\n <span className=\"eyebrow tabular-nums\">{label}</span>\n <span aria-hidden className=\"loading-rule\" />\n </div>\n );\n}\n\nexport function LoadingDots({ className }: { className?: string }) {\n return (\n <span\n aria-hidden\n className={['loading-dots', className].filter(Boolean).join(' ')}\n >\n <span />\n <span />\n <span />\n </span>\n );\n}\n\nexport default Loading;\n","const KB = 1024;\nconst MB = KB * 1024;\nconst GB = MB * 1024;\n\nexport function formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';\n if (bytes >= GB) return `${(bytes / GB).toFixed(2)} GB`;\n if (bytes >= MB) return `${(bytes / MB).toFixed(1)} MB`;\n if (bytes >= KB) return `${(bytes / KB).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n\nexport function formatRelativeTime(iso: string | null): string {\n if (!iso) return '—';\n const t = new Date(iso).getTime();\n if (Number.isNaN(t)) return iso;\n const diffMs = Date.now() - t;\n const sec = Math.round(diffMs / 1000);\n if (sec < 60) return `${sec}s ago`;\n const min = Math.round(sec / 60);\n if (min < 60) return `${min}m ago`;\n const hr = Math.round(min / 60);\n if (hr < 48) return `${hr}h ago`;\n const day = Math.round(hr / 24);\n if (day < 30) return `${day}d ago`;\n const mo = Math.round(day / 30);\n if (mo < 12) return `${mo}mo ago`;\n const yr = Math.round(mo / 12);\n return `${yr}y ago`;\n}\n\nexport function formatDateTime(iso: string | null): string {\n if (!iso) return '—';\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return iso;\n return d.toLocaleString();\n}\n","import { useEffect } from 'react';\n\nconst isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform);\n\nexport function useGlobalHotkey(combo: 'mod+k', handler: () => void): void {\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (combo !== 'mod+k') return;\n const mod = isMac ? e.metaKey : e.ctrlKey;\n if (!mod) return;\n if (e.key !== 'k' && e.key !== 'K') return;\n e.preventDefault();\n handler();\n }\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [combo, handler]);\n}\n\nexport const HOTKEY_HINT = isMac ? '⌘K' : 'Ctrl+K';\n","import { useCallback, useEffect, useState } from 'react';\n\nexport type Locale = 'en' | 'zh';\n\nconst STORAGE_KEY = 'locale';\n\nconst DICT = {\n en: {\n 'app.brand.title': 'Claude Sessions',\n 'app.brand.subtitle': 'local archive',\n 'app.brand.footnote': 'local · read-only by default',\n 'nav.workspace': 'Workspace',\n 'nav.projects': 'Projects',\n 'nav.disk': 'Disk usage',\n 'nav.import': 'Import',\n 'nav.theme': 'Theme',\n 'nav.language': 'Language',\n 'nav.toggleNav': 'Toggle navigation',\n 'nav.closeNav': 'Close navigation',\n\n 'version.label': 'Version',\n 'version.badge.new': 'Update {{v}}',\n 'version.upToDate': 'Up to date',\n 'version.checkFailed': 'Update check failed',\n 'version.modal.eyebrowUpdate': 'Update available',\n 'version.modal.eyebrowLatest': 'Version',\n 'version.modal.titleUpdate': 'A new version is available',\n 'version.modal.titleLatest': \"You're on the latest version\",\n 'version.current': 'Current',\n 'version.latest': 'Latest',\n 'version.published': 'Released {{date}}',\n 'version.notes': \"What's new\",\n 'version.notesEmpty': 'No release notes were provided for this release.',\n 'version.btn.update': 'Update now',\n 'version.btn.updating': 'Updating…',\n 'version.btn.releasePage': 'Release page',\n 'version.btn.repo': 'Repository',\n 'version.btn.close': 'Close',\n 'version.update.eyebrow': 'Result',\n 'version.update.successTitle': 'Update complete',\n 'version.update.success': 'Updated to {{v}}. Restart ccsm to load the new version.',\n 'version.update.failTitle': 'Update failed',\n 'version.update.fail':\n 'npm could not complete the update — see the output below, or run the command manually.',\n 'version.update.command': 'Or run manually',\n 'version.update.output': 'Command output',\n\n 'common.loading': 'loading',\n 'common.scanning': 'scanning ~/.claude/projects/…',\n 'common.computing': 'computing…',\n 'common.readingSessions': 'reading sessions…',\n 'common.loadingSession': 'loading session…',\n 'common.noData': 'no data',\n 'common.entries': 'entries',\n 'common.entry': 'entry',\n 'common.selectAll': 'select all',\n 'common.deselectAll': 'deselect all',\n 'common.cancel': 'Cancel',\n 'common.done': 'Done',\n 'common.expand': 'expand',\n 'common.collapse': 'collapse',\n 'common.system': 'system',\n 'common.onlyUser': 'only me',\n 'common.onlyError': 'errors',\n 'common.missing': 'missing',\n 'common.noSessions': 'No sessions in this project.',\n 'common.noProjects': 'No projects found.',\n 'common.noMessagesMatch': 'No messages match.',\n 'common.failedProjects': 'Failed to load projects',\n 'common.failedSessions': 'Failed to load sessions',\n 'common.failedSession': 'Failed to load',\n 'common.failedMemory': 'Failed to load memory',\n 'common.failed': 'Failed',\n 'common.searchPlaceholder': 'Search this session…',\n 'common.loadEarlier': 'Load earlier (+{{n}})',\n 'common.scrollToTop': 'Jump to top',\n 'common.scrollToBottom': 'Jump to bottom',\n\n 'projects.title': 'Projects',\n 'projects.tagline':\n \"Every directory you've touched with Claude Code, sorted by recency. Open one to inspect or prune its sessions.\",\n 'projects.indexHeading': 'Index',\n 'projects.stat.projects': 'Projects',\n 'projects.stat.sessions': 'Sessions',\n 'projects.stat.onDisk': 'On disk',\n 'projects.card.eyebrow': 'project',\n 'projects.card.sessions': 'Sessions',\n 'projects.card.onDisk': 'On disk',\n 'projects.card.lastSeen': 'Last seen',\n 'projects.warn.rootMissing':\n \"Claude root {{root}} doesn't exist on this machine — nothing to display.\",\n\n 'project.eyebrow': 'Project',\n 'project.warn.missingDir': 'Directory missing on disk',\n 'project.action.openFolder': 'Open folder',\n 'project.action.openFolderTooltipMissing': 'Directory no longer exists on disk',\n 'project.action.openFolderFailed': 'Failed to open folder: {{msg}}',\n 'project.action.delete': 'Delete · {{n}}',\n 'project.action.select': 'Select',\n 'project.action.exitSelect': 'Exit',\n 'project.bulk.selectedCount': '{{n}} selected',\n 'project.bulk.deleteCta': 'Delete selected',\n 'project.bulk.deletePending': 'Deleting {{i}}/{{total}}…',\n 'project.bulk.resultOk': 'Deleted {{ok}}',\n 'project.bulk.resultSkipped': 'skipped {{n}}',\n 'project.bulk.resultFailed': 'failed {{n}}',\n 'project.bulk.viewDetail': 'Details',\n 'project.bulk.hideDetail': 'Hide',\n 'project.bulk.detailSkipped': 'Skipped',\n 'project.bulk.detailFailed': 'Failed',\n 'project.bulk.dismiss': 'Dismiss',\n 'project.meta.sessions': 'Sessions',\n 'project.meta.onDisk': 'On disk',\n 'project.meta.working': 'Working',\n 'project.meta.live': 'Live',\n 'project.meta.recent': 'Recent',\n 'project.heading': 'Sessions',\n 'project.col.title': 'Title',\n 'project.col.msgs': 'Msgs',\n 'project.col.last': 'Last',\n 'project.col.size': 'Size',\n 'project.col.status': 'Status',\n 'project.col.errors': 'Errors',\n\n 'session.crumbProjects': 'Projects',\n 'session.action.delete': 'Delete',\n 'session.action.deleteTooltipBlocked': 'Session metadata not available yet',\n 'session.action.rename': 'Rename',\n 'session.action.renameTooltipLive': 'Live PID {{pid}} owns this session — close the running claude first',\n 'session.tagline':\n 'Started {{started}} · last touched {{lastTouched}}{{branchPart}}',\n 'session.tagline.branch': ' · branch {{branch}}',\n 'session.meta.messages': 'Messages',\n 'session.meta.size': 'Size',\n 'session.meta.version': 'Version',\n 'session.meta.started': 'Started',\n 'session.truncated': 'Session truncated to first {{n}} messages.',\n 'session.shown': '{{shown}} / {{total}}',\n 'session.live': 'Live',\n 'session.live.tooltip': \"Auto-updating — new messages stream in as they're written\",\n 'session.working': 'Working',\n 'session.working.tooltip': 'Claude is processing this turn — generating a reply or running a tool',\n 'session.working.indicator': 'Claude is working…',\n\n 'session.modified.title': 'Modified files',\n 'session.modified.empty': 'No files were modified in this session.',\n 'session.modified.loading': 'scanning for file edits…',\n 'session.modified.failed': 'Failed to load modified files',\n 'session.modified.count': '{{n}} file',\n 'session.modified.countPlural': '{{n}} files',\n 'session.modified.openAria': 'Open modified files',\n 'session.modified.close': 'Close',\n 'session.modified.openFile': 'open file',\n 'session.modified.openFailed': 'Failed to open file: {{msg}}',\n 'session.modified.col.file': 'Files',\n 'session.modified.col.conversation': 'Conversation',\n 'session.modified.collapseAll': 'collapse all',\n 'session.modified.expandAll': 'expand all',\n 'session.modified.selectFile': 'Select a file to view its changes in this session.',\n 'session.modified.errorBadge': '{{n}} errored',\n 'session.modified.absolutePath': 'absolute path',\n 'session.modified.jump': 'Jump to',\n 'session.modified.opErrored': 'errored',\n 'session.modified.opPending': 'pending',\n 'session.modified.newContent': 'new content',\n 'session.modified.linesOmitted': '{{n}} unchanged lines',\n 'session.modified.editN': 'edit {{n}}',\n 'session.modified.contentUnavailable':\n 'Edit content unavailable (session truncated past this point).',\n 'session.modified.noContent': 'No content recorded for this operation.',\n 'session.modified.showEarlier': 'show {{n}} earlier messages',\n 'session.modified.added': 'added',\n 'session.modified.modified': 'modified',\n\n 'message.role.you': 'You',\n 'message.role.claude': 'Claude',\n 'message.role.tool': 'Tool',\n 'message.role.system': 'system',\n\n 'tool.use': 'tool',\n 'tool.result': 'tool result',\n 'tool.error': 'tool error',\n 'tool.thinking': 'thinking',\n 'tool.thinkingEncrypted': 'Content is encrypted, no readable text.',\n 'tool.image': 'image',\n 'tool.copy': 'copy',\n 'tool.copied': 'copied',\n 'tool.moreLines': '… {{n}} more lines',\n 'tool.replaceAll': 'replace all',\n\n 'status.working': 'working',\n 'status.live': 'live · pid {{pid}}',\n 'status.recent': 'recent',\n 'status.idle': 'idle',\n 'status.tooltip.working': 'Claude is actively processing this session right now',\n 'status.tooltip.live': 'PID {{pid}} is alive and registered for this session',\n 'status.tooltip.recent': 'jsonl modified within the last {{n}} minutes',\n 'status.tooltip.idle': 'idle — safe to delete',\n\n 'disk.tagline':\n \"How `~/.claude/` is using your disk: which projects carry the most weight, and which sessions are heaviest.\",\n 'disk.title': 'Disk usage',\n 'disk.meta.total': 'Total',\n 'disk.meta.projects': 'Projects',\n 'disk.meta.sessions': 'Sessions',\n 'disk.stat.total': 'Total on disk',\n 'disk.stat.acrossProjects': 'across {{n}} projects',\n 'disk.stat.sessions': 'Sessions',\n 'disk.stat.largest': 'largest {{size}}',\n 'disk.stat.months': 'Months tracked',\n 'disk.composition.title': 'Composition',\n 'disk.composition.subtitle': 'bytes per project',\n 'disk.composition.total': 'Total',\n 'disk.cadence.title': 'Cadence',\n 'disk.cadence.subtitle': 'MB per month',\n 'disk.heaviest.title': 'Heaviest sessions',\n 'disk.heaviest.top': 'top {{n}}',\n 'disk.col.num': '#',\n 'disk.col.title': 'Title',\n 'disk.col.project': 'Project',\n 'disk.col.last': 'Last',\n 'disk.col.size': 'Size',\n\n 'cleanup.title': 'Cleanup suggestions',\n 'cleanup.tagline':\n 'Things worth pruning. Review each row, then act — nothing here runs automatically.',\n 'cleanup.section.largeSessions': 'Largest sessions',\n 'cleanup.section.largeSessions.hint': 'Top {{n}} by total bytes on disk',\n 'cleanup.section.orphanFileHistory': 'Orphan file-history',\n 'cleanup.section.orphanFileHistory.hint':\n 'file-history/<sid>/ folders with no surviving session',\n 'cleanup.section.orphanSessionEnv': 'Orphan session-env',\n 'cleanup.section.orphanSessionEnv.hint':\n 'session-env/<sid>/ folders with no surviving session',\n 'cleanup.empty.largeSessions': 'No sessions on disk yet.',\n 'cleanup.empty.orphan': 'No orphans — nothing to prune.',\n 'cleanup.col.session': 'Session',\n 'cleanup.col.project': 'Project',\n 'cleanup.col.last': 'Last',\n 'cleanup.col.size': 'Size',\n 'cleanup.col.sid': 'Session id',\n 'cleanup.col.actions': 'Actions',\n 'cleanup.action.view': 'View',\n 'cleanup.action.delete': 'Delete',\n 'cleanup.action.deleting': 'Deleting…',\n 'cleanup.confirm.title': 'Delete orphan?',\n 'cleanup.confirm.body':\n 'This removes {{kind}}/{{sid}}/ ({{size}}) permanently. The session jsonl is already gone, so nothing else is affected.',\n 'cleanup.confirm.confirm': 'Delete',\n 'cleanup.confirm.cancel': 'Cancel',\n 'cleanup.failed': 'Failed: {{msg}}',\n\n 'memory.title': 'Memory',\n 'memory.empty': 'No memory recorded yet for this project.',\n 'memory.action.open': 'Memory',\n 'memory.action.openCount': 'Memory · {{n}}',\n 'memory.meta.entries': 'Entries',\n 'memory.meta.types': 'Types',\n 'memory.meta.lastUpdate': 'Last update',\n 'memory.type.user': 'User',\n 'memory.type.feedback': 'Feedback',\n 'memory.type.project': 'Project',\n 'memory.type.reference': 'Reference',\n 'memory.type.other': 'Untyped',\n 'memory.loading': 'reading memory…',\n 'memory.search.placeholder': 'Search title, hook, or body…',\n 'memory.search.hint': 'Press {{hotkey}} to focus',\n 'memory.filter.all': 'All',\n 'memory.sort.label': 'Sort',\n 'memory.sort.index': 'MEMORY.md order',\n 'memory.sort.recent': 'Recent first',\n 'memory.sort.name': 'Name A→Z',\n 'memory.sort.size': 'Largest first',\n 'memory.list.noResults': 'No entries match.',\n 'memory.list.count': '{{n}} of {{total}}',\n 'memory.cover.title': 'Memory index',\n 'memory.cover.subtitle': 'MEMORY.md',\n 'memory.cover.viewRaw': 'view raw',\n 'memory.cover.showAll': 'Show all ({{n}})',\n 'memory.cover.collapse': 'Collapse',\n 'memory.cover.missingTooltip': 'No matching entry file',\n 'memory.cover.orphan': 'Not in index',\n 'memory.empty.title': 'No memory yet',\n 'memory.empty.body':\n 'Claude saves long-term notes here when you ask it to remember something or when it observes a stable preference. Memory files live under ~/.claude/projects/<id>/memory/.',\n 'memory.empty.orphanIndex': 'The index references {{n}} entries, but none of them have memory files yet.',\n 'memory.section.untyped': 'Untyped',\n 'memory.drawer.close': 'Close',\n 'memory.drawer.indexHook': 'Index hook',\n 'memory.drawer.notFound.title': 'Entry not found',\n 'memory.drawer.notFound.body':\n 'The selected memory file may have been removed. It might still appear in the curated index above.',\n 'memory.raw.title': 'MEMORY.md',\n 'memory.raw.subtitle': 'Curated index — raw source',\n\n 'delete.eyebrow.confirm': 'Confirm deletion',\n 'delete.eyebrow.result': 'Result',\n 'delete.title.confirm': 'Delete sessions',\n 'delete.title.result': 'Sessions removed',\n 'delete.summary':\n '{{n}} will be removed · {{skipped}} skipped · ~{{free}} to free',\n 'delete.skipped.heading': 'These {{n}} will be skipped:',\n 'delete.skipped.reasonLive': 'live PID {{pid}}',\n 'delete.skipped.reasonRecent': 'modified within last {{n}} minutes',\n 'delete.skipped.reasonUnknown': 'unknown',\n 'delete.success':\n 'Deleted {{n}} session(s). {{free}} freed · {{lines}} history lines removed',\n 'delete.btn.confirm': 'Delete {{n}} {{label}}',\n 'delete.btn.confirmPending': 'Deleting…',\n 'delete.label.session': 'session',\n 'delete.label.sessions': 'sessions',\n 'delete.close': 'Close',\n\n 'deleteProject.row.action': 'Delete project',\n 'deleteProject.eyebrow.confirm': 'Confirm deletion',\n 'deleteProject.eyebrow.result': 'Result',\n 'deleteProject.title.confirm': 'Delete project',\n 'deleteProject.title.result': 'Project removed',\n 'deleteProject.summary':\n '{{n}} session(s) · ~{{free}} to free · directory will be removed',\n 'deleteProject.warning':\n 'This removes every session in {{cwd}} along with its history.jsonl entries. Cannot be undone.',\n 'deleteProject.blocked.heading':\n '{{n}} session(s) are live or were modified in the last 5 minutes — nothing will be deleted. Try again later.',\n 'deleteProject.success':\n 'Removed {{n}} session(s) and the project directory. {{free}} freed · {{lines}} history lines removed',\n 'deleteProject.successKept':\n 'Removed {{n}} session(s). {{free}} freed · {{lines}} history lines removed · directory kept',\n 'deleteProject.btn.confirm': 'Delete project',\n 'deleteProject.btn.confirmPending': 'Deleting…',\n\n 'search.action.open': 'Search sessions',\n 'search.placeholder': 'Search across all sessions…',\n 'search.empty': 'Type at least 2 characters to search across every session on disk.',\n 'search.refineQuery': 'Type a longer query to refine the search.',\n 'search.scanning': 'scanning…',\n 'search.noResults': 'No matches.',\n 'search.error': 'Search failed: {{msg}}',\n 'search.moreInSession': '+more in this session',\n 'search.moreSessions': 'Showing first {{n}} sessions — refine your query for more.',\n 'search.summary': '{{matched}} session(s) · scanned {{scanned}} · {{ms}}ms',\n 'search.shortcut': '{{hint}} to open',\n 'search.escapeHint': 'esc to close',\n 'search.kindText': 'text',\n 'search.kindToolUse': 'tool',\n 'search.kindToolResult': 'tool result',\n 'search.kindThinking': 'thinking',\n 'search.role.user': 'You',\n 'search.role.assistant': 'Claude',\n\n 'export.action': 'Export',\n 'export.eyebrow.confirm': 'Share across devices',\n 'export.eyebrow.result': 'Result',\n 'export.title.confirm': 'Export project',\n 'export.title.result': 'Export complete',\n 'export.summary': 'Bundle {{n}} session(s) + memory into a portable folder.',\n 'export.destLabel': 'Destination folder',\n 'export.destPlaceholder': 'e.g. D:\\\\sync\\\\claude-shared',\n 'export.destHint':\n 'An absolute path outside ~/.claude. Drop it into a git repo or cloud drive to sync.',\n 'export.privacy':\n 'This writes your conversations and memory outside the app. If you push the folder to a remote, those contents leave your machine.',\n 'export.btn.confirm': 'Export',\n 'export.btn.pending': 'Exporting…',\n 'export.success':\n 'Exported {{sessions}} session(s) · {{memory}} memory file(s) · {{lines}} history line(s)',\n 'export.successDest': 'Written to {{dest}}',\n 'export.close': 'Close',\n\n 'import.title': 'Import bundle',\n 'import.tagline':\n \"Bring a project's sessions and memory from another device. Paths are remapped to this machine.\",\n 'import.bundleLabel': 'Bundle folder',\n 'import.bundlePlaceholder': 'Path to an exported bundle',\n 'import.btn.load': 'Load bundle',\n 'import.btn.recheck': 'Re-check',\n 'import.btn.loading': 'Loading…',\n 'import.source': 'From {{platform}} · {{cwd}}',\n 'import.targetLabel': 'Import into (this device)',\n 'import.targetHint':\n 'Absolute path on this machine. Sessions are associated with this folder.',\n 'import.targetId': 'Project id: {{id}}',\n 'import.suggestions': 'Suggestions',\n 'import.suggestion.existing-project': 'existing project',\n 'import.suggestion.original-path': 'original path',\n 'import.suggestion.same-basename': 'same folder name',\n 'import.policyLabel': 'If a session already exists',\n 'import.policy.skip': 'Skip',\n 'import.policy.overwrite-if-newer': 'Overwrite if newer',\n 'import.policy.keep-both': 'Keep both',\n 'import.sessions.heading': 'Sessions',\n 'import.sessions.empty': 'No sessions in this bundle.',\n 'import.action.create': 'create',\n 'import.action.overwrite': 'overwrite',\n 'import.action.keep-both': 'keep both',\n 'import.action.skip': 'skip',\n 'import.memory.heading': 'Memory',\n 'import.memory.create': 'create',\n 'import.memory.skip': 'identical',\n 'import.memory.conflict': 'conflict → {{name}}',\n 'import.history': '{{n}} history line(s) to add',\n 'import.btn.commit': 'Import',\n 'import.btn.committing': 'Importing…',\n 'import.result.title': 'Import complete',\n 'import.result.summary':\n 'Imported {{n}} session(s) · {{skipped}} skipped · {{memory}} memory file(s) · {{lines}} history line(s)',\n 'import.result.viewProject': 'Open imported project',\n 'import.empty': 'Enter a bundle folder path to begin.',\n },\n\n zh: {\n 'app.brand.title': 'Claude 会话',\n 'app.brand.subtitle': '本地归档',\n 'app.brand.footnote': '本地 · 默认只读',\n 'nav.workspace': '工作区',\n 'nav.projects': '项目',\n 'nav.disk': '磁盘占用',\n 'nav.import': '导入',\n 'nav.theme': '主题',\n 'nav.language': '语言',\n 'nav.toggleNav': '切换导航',\n 'nav.closeNav': '关闭导航',\n\n 'version.label': '版本',\n 'version.badge.new': '新版本 {{v}}',\n 'version.upToDate': '已是最新',\n 'version.checkFailed': '检查更新失败',\n 'version.modal.eyebrowUpdate': '有可用更新',\n 'version.modal.eyebrowLatest': '版本',\n 'version.modal.titleUpdate': '发现新版本',\n 'version.modal.titleLatest': '已是最新版本',\n 'version.current': '当前版本',\n 'version.latest': '最新版本',\n 'version.published': '发布于 {{date}}',\n 'version.notes': '更新内容',\n 'version.notesEmpty': '本次发布没有提供更新说明。',\n 'version.btn.update': '立即更新',\n 'version.btn.updating': '更新中…',\n 'version.btn.releasePage': '发布页',\n 'version.btn.repo': '代码仓库',\n 'version.btn.close': '关闭',\n 'version.update.eyebrow': '结果',\n 'version.update.successTitle': '更新完成',\n 'version.update.success': '已更新到 {{v}}。请重启 ccsm 以加载新版本。',\n 'version.update.failTitle': '更新失败',\n 'version.update.fail': 'npm 未能完成更新 — 请查看下方输出,或手动执行更新命令。',\n 'version.update.command': '或手动执行',\n 'version.update.output': '命令输出',\n\n 'common.loading': '加载中',\n 'common.scanning': '正在扫描 ~/.claude/projects/…',\n 'common.computing': '统计中…',\n 'common.readingSessions': '读取会话中…',\n 'common.loadingSession': '加载会话中…',\n 'common.noData': '暂无数据',\n 'common.entries': '项',\n 'common.entry': '项',\n 'common.selectAll': '全选',\n 'common.deselectAll': '取消全选',\n 'common.cancel': '取消',\n 'common.done': '完成',\n 'common.expand': '展开',\n 'common.collapse': '收起',\n 'common.system': '系统',\n 'common.onlyUser': '仅我',\n 'common.onlyError': '错误',\n 'common.missing': '已不存在',\n 'common.noSessions': '该项目下暂无会话。',\n 'common.noProjects': '未发现项目。',\n 'common.noMessagesMatch': '没有匹配的消息。',\n 'common.failedProjects': '加载项目失败',\n 'common.failedSessions': '加载会话列表失败',\n 'common.failedSession': '加载失败',\n 'common.failedMemory': '加载记忆失败',\n 'common.failed': '失败',\n 'common.searchPlaceholder': '在此会话中搜索…',\n 'common.loadEarlier': '加载更早 (+{{n}})',\n 'common.scrollToTop': '回到顶部',\n 'common.scrollToBottom': '回到底部',\n\n 'projects.title': '项目',\n 'projects.tagline':\n '所有曾用 Claude Code 的工作目录,按最近活跃度排序。点击进入查看或清理其会话。',\n 'projects.indexHeading': '索引',\n 'projects.stat.projects': '项目数',\n 'projects.stat.sessions': '会话数',\n 'projects.stat.onDisk': '占用',\n 'projects.card.eyebrow': '项目',\n 'projects.card.sessions': '会话',\n 'projects.card.onDisk': '占用',\n 'projects.card.lastSeen': '最近',\n 'projects.warn.rootMissing':\n 'Claude 根目录 {{root}} 在本机不存在 — 无可显示内容。',\n\n 'project.eyebrow': '项目',\n 'project.warn.missingDir': '目录已从磁盘删除',\n 'project.action.openFolder': '打开目录',\n 'project.action.openFolderTooltipMissing': '目录已不在本机磁盘上',\n 'project.action.openFolderFailed': '打开目录失败:{{msg}}',\n 'project.action.delete': '删除 · {{n}}',\n 'project.action.select': '选择',\n 'project.action.exitSelect': '退出',\n 'project.bulk.selectedCount': '已选 {{n}} 条',\n 'project.bulk.deleteCta': '删除选中',\n 'project.bulk.deletePending': '正在删除 {{i}}/{{total}}…',\n 'project.bulk.resultOk': '成功 {{ok}} 条',\n 'project.bulk.resultSkipped': '跳过 {{n}} 条',\n 'project.bulk.resultFailed': '失败 {{n}} 条',\n 'project.bulk.viewDetail': '查看明细',\n 'project.bulk.hideDetail': '收起',\n 'project.bulk.detailSkipped': '跳过',\n 'project.bulk.detailFailed': '失败',\n 'project.bulk.dismiss': '关闭',\n 'project.meta.sessions': '会话',\n 'project.meta.onDisk': '占用',\n 'project.meta.working': '工作中',\n 'project.meta.live': '运行中',\n 'project.meta.recent': '近期',\n 'project.heading': '会话',\n 'project.col.title': '标题',\n 'project.col.msgs': '消息',\n 'project.col.last': '最近',\n 'project.col.size': '大小',\n 'project.col.status': '状态',\n 'project.col.errors': '错误',\n\n 'session.crumbProjects': '项目',\n 'session.action.delete': '删除',\n 'session.action.deleteTooltipBlocked': '暂无法获取该会话状态',\n 'session.action.rename': '重命名',\n 'session.action.renameTooltipLive': '运行中 PID {{pid}} 占用此会话 — 请先关闭对应的 claude',\n 'session.tagline': '开始于 {{started}} · 最后触达 {{lastTouched}}{{branchPart}}',\n 'session.tagline.branch': ' · 分支 {{branch}}',\n 'session.meta.messages': '消息数',\n 'session.meta.size': '大小',\n 'session.meta.version': '版本',\n 'session.meta.started': '开始时间',\n 'session.truncated': '会话已截断至前 {{n}} 条消息。',\n 'session.shown': '{{shown}} / {{total}}',\n 'session.live': '实时',\n 'session.live.tooltip': '自动更新 — 新消息写入时实时追加',\n 'session.working': '工作中',\n 'session.working.tooltip': 'Claude 正在处理这一轮 — 生成回复或执行工具',\n 'session.working.indicator': 'Claude 正在处理…',\n\n 'session.modified.title': '修改的文件',\n 'session.modified.empty': '本次会话没有修改任何文件。',\n 'session.modified.loading': '扫描文件改动…',\n 'session.modified.failed': '加载修改文件清单失败',\n 'session.modified.count': '{{n}} 个文件',\n 'session.modified.countPlural': '{{n}} 个文件',\n 'session.modified.openAria': '打开修改的文件',\n 'session.modified.close': '关闭',\n 'session.modified.openFile': '打开文件',\n 'session.modified.openFailed': '打开文件失败:{{msg}}',\n 'session.modified.col.file': '文件',\n 'session.modified.col.conversation': '对话',\n 'session.modified.collapseAll': '全部收起',\n 'session.modified.expandAll': '全部展开',\n 'session.modified.selectFile': '从左侧选择一个文件,查看本次会话对它的改动。',\n 'session.modified.errorBadge': '{{n}} 次失败',\n 'session.modified.absolutePath': '绝对路径',\n 'session.modified.jump': '跳转到',\n 'session.modified.opErrored': '失败',\n 'session.modified.opPending': '未完成',\n 'session.modified.newContent': '新内容',\n 'session.modified.linesOmitted': '省略 {{n}} 行未改动',\n 'session.modified.editN': '改动 {{n}}',\n 'session.modified.contentUnavailable': '改动内容不可用(会话在此处之后被截断)。',\n 'session.modified.noContent': '该操作没有记录到内容。',\n 'session.modified.showEarlier': '显示更早的 {{n}} 条消息',\n 'session.modified.added': '新增',\n 'session.modified.modified': '修改',\n\n 'message.role.you': '我',\n 'message.role.claude': 'Claude',\n 'message.role.tool': '工具',\n 'message.role.system': '系统',\n\n 'tool.use': '工具',\n 'tool.result': '工具返回',\n 'tool.error': '工具错误',\n 'tool.thinking': '思考',\n 'tool.thinkingEncrypted': '内容被加密,无可读文本。',\n 'tool.image': '图片',\n 'tool.copy': '复制',\n 'tool.copied': '已复制',\n 'tool.moreLines': '… 还有 {{n}} 行',\n 'tool.replaceAll': '全部替换',\n\n 'status.working': '工作中',\n 'status.live': '运行中 · pid {{pid}}',\n 'status.recent': '近期',\n 'status.idle': '空闲',\n 'status.tooltip.working': 'Claude 正在这个会话里处理任务',\n 'status.tooltip.live': 'PID {{pid}} 仍在运行并绑定此会话',\n 'status.tooltip.recent': 'jsonl 在过去 {{n}} 分钟内被修改',\n 'status.tooltip.idle': '空闲 — 可安全删除',\n\n 'disk.tagline':\n '`~/.claude/` 占用磁盘的全貌:哪些项目最沉,哪些会话最大。',\n 'disk.title': '磁盘占用',\n 'disk.meta.total': '总计',\n 'disk.meta.projects': '项目',\n 'disk.meta.sessions': '会话',\n 'disk.stat.total': '总占用',\n 'disk.stat.acrossProjects': '覆盖 {{n}} 个项目',\n 'disk.stat.sessions': '会话',\n 'disk.stat.largest': '最大 {{size}}',\n 'disk.stat.months': '月份覆盖',\n 'disk.composition.title': '构成',\n 'disk.composition.subtitle': '按项目占用',\n 'disk.composition.total': '合计',\n 'disk.cadence.title': '节奏',\n 'disk.cadence.subtitle': '每月 MB',\n 'disk.heaviest.title': '最沉的会话',\n 'disk.heaviest.top': '前 {{n}}',\n 'disk.col.num': '#',\n 'disk.col.title': '标题',\n 'disk.col.project': '项目',\n 'disk.col.last': '最近',\n 'disk.col.size': '大小',\n\n 'cleanup.title': '清理建议',\n 'cleanup.tagline':\n '值得清理的目标。逐条审视并手动操作 — 这里没有任何自动清理。',\n 'cleanup.section.largeSessions': '最大的会话',\n 'cleanup.section.largeSessions.hint': '按总占用前 {{n}} 名',\n 'cleanup.section.orphanFileHistory': '孤儿 file-history',\n 'cleanup.section.orphanFileHistory.hint':\n 'file-history/<sid>/ 目录但无对应会话',\n 'cleanup.section.orphanSessionEnv': '孤儿 session-env',\n 'cleanup.section.orphanSessionEnv.hint':\n 'session-env/<sid>/ 目录但无对应会话',\n 'cleanup.empty.largeSessions': '磁盘上还没有会话。',\n 'cleanup.empty.orphan': '无孤儿 — 无可清理。',\n 'cleanup.col.session': '会话',\n 'cleanup.col.project': '项目',\n 'cleanup.col.last': '最近',\n 'cleanup.col.size': '大小',\n 'cleanup.col.sid': '会话 id',\n 'cleanup.col.actions': '操作',\n 'cleanup.action.view': '查看',\n 'cleanup.action.delete': '删除',\n 'cleanup.action.deleting': '删除中…',\n 'cleanup.confirm.title': '确认删除孤儿?',\n 'cleanup.confirm.body':\n '将永久删除 {{kind}}/{{sid}}/({{size}})。其会话主体已不存在,不会影响其他数据。',\n 'cleanup.confirm.confirm': '删除',\n 'cleanup.confirm.cancel': '取消',\n 'cleanup.failed': '失败:{{msg}}',\n\n 'memory.title': '记忆',\n 'memory.empty': '该项目尚未沉淀任何记忆。',\n 'memory.action.open': '记忆',\n 'memory.action.openCount': '记忆 · {{n}}',\n 'memory.meta.entries': '条目数',\n 'memory.meta.types': '类型',\n 'memory.meta.lastUpdate': '最近更新',\n 'memory.type.user': '用户',\n 'memory.type.feedback': '反馈',\n 'memory.type.project': '项目',\n 'memory.type.reference': '外链',\n 'memory.type.other': '未分类',\n 'memory.loading': '读取记忆中…',\n 'memory.search.placeholder': '搜索标题、摘要或正文…',\n 'memory.search.hint': '按 {{hotkey}} 聚焦',\n 'memory.filter.all': '全部',\n 'memory.sort.label': '排序',\n 'memory.sort.index': 'MEMORY.md 顺序',\n 'memory.sort.recent': '最近修改',\n 'memory.sort.name': '名称 A→Z',\n 'memory.sort.size': '体积大→小',\n 'memory.list.noResults': '没有匹配的条目。',\n 'memory.list.count': '{{n}} / {{total}}',\n 'memory.cover.title': '记忆索引',\n 'memory.cover.subtitle': 'MEMORY.md',\n 'memory.cover.viewRaw': '查看原文',\n 'memory.cover.showAll': '展开全部({{n}})',\n 'memory.cover.collapse': '收起',\n 'memory.cover.missingTooltip': '未找到对应条目文件',\n 'memory.cover.orphan': '未列入索引',\n 'memory.empty.title': '尚未沉淀记忆',\n 'memory.empty.body':\n 'Claude 会在你明确要求记住或观察到稳定偏好时,把记忆存到 ~/.claude/projects/<id>/memory/ 下。',\n 'memory.empty.orphanIndex': '索引中引用了 {{n}} 条记忆,但目前一条都不存在。',\n 'memory.section.untyped': '未分类',\n 'memory.drawer.close': '关闭',\n 'memory.drawer.indexHook': '在索引中的呈现',\n 'memory.drawer.notFound.title': '未找到条目',\n 'memory.drawer.notFound.body':\n '该记忆文件可能已被移除;如果索引里还有,会以灰化的形态出现在上方。',\n 'memory.raw.title': 'MEMORY.md',\n 'memory.raw.subtitle': '精选索引 —— 原文',\n\n 'delete.eyebrow.confirm': '确认删除',\n 'delete.eyebrow.result': '结果',\n 'delete.title.confirm': '删除会话',\n 'delete.title.result': '已删除会话',\n 'delete.summary': '将删除 {{n}} 项 · 跳过 {{skipped}} 项 · 释放约 {{free}}',\n 'delete.skipped.heading': '将跳过这 {{n}} 项:',\n 'delete.skipped.reasonLive': '运行中 PID {{pid}}',\n 'delete.skipped.reasonRecent': '过去 {{n}} 分钟内有修改',\n 'delete.skipped.reasonUnknown': '未知',\n 'delete.success': '已删除 {{n}} 个会话。释放 {{free}} · 清理 {{lines}} 条历史记录',\n 'delete.btn.confirm': '删除 {{n}} {{label}}',\n 'delete.btn.confirmPending': '删除中…',\n 'delete.label.session': '个会话',\n 'delete.label.sessions': '个会话',\n 'delete.close': '关闭',\n\n 'deleteProject.row.action': '删除项目',\n 'deleteProject.eyebrow.confirm': '确认删除',\n 'deleteProject.eyebrow.result': '结果',\n 'deleteProject.title.confirm': '删除项目',\n 'deleteProject.title.result': '项目已删除',\n 'deleteProject.summary': '{{n}} 个会话 · 约释放 {{free}} · 项目目录将被删除',\n 'deleteProject.warning':\n '将删除 {{cwd}} 下的全部会话及其在 history.jsonl 的记录,操作不可撤销。',\n 'deleteProject.blocked.heading':\n '{{n}} 个会话正在运行或最近 5 分钟内被修改 — 本次不会删除任何内容,请稍后再试。',\n 'deleteProject.success':\n '已删除 {{n}} 个会话及项目目录。释放 {{free}} · 清理 {{lines}} 条历史记录',\n 'deleteProject.successKept':\n '已删除 {{n}} 个会话。释放 {{free}} · 清理 {{lines}} 条历史记录 · 项目目录已保留',\n 'deleteProject.btn.confirm': '删除项目',\n 'deleteProject.btn.confirmPending': '删除中…',\n\n 'search.action.open': '搜索会话',\n 'search.placeholder': '在所有会话中搜索…',\n 'search.empty': '至少输入 2 个字符,将在磁盘上的所有会话中搜索。',\n 'search.refineQuery': '请输入更长的关键词以缩小范围。',\n 'search.scanning': '扫描中…',\n 'search.noResults': '没有匹配项。',\n 'search.error': '搜索失败:{{msg}}',\n 'search.moreInSession': '该会话中还有更多',\n 'search.moreSessions': '仅显示前 {{n}} 个会话 — 请细化关键词。',\n 'search.summary': '{{matched}} 个会话 · 扫描 {{scanned}} 项 · {{ms}}ms',\n 'search.shortcut': '{{hint}} 唤起',\n 'search.escapeHint': 'esc 关闭',\n 'search.kindText': '文本',\n 'search.kindToolUse': '工具',\n 'search.kindToolResult': '工具返回',\n 'search.kindThinking': '思考',\n 'search.role.user': '我',\n 'search.role.assistant': 'Claude',\n\n 'export.action': '导出',\n 'export.eyebrow.confirm': '跨设备共享',\n 'export.eyebrow.result': '结果',\n 'export.title.confirm': '导出项目',\n 'export.title.result': '导出完成',\n 'export.summary': '将 {{n}} 个会话 + 记忆打包到可移植文件夹。',\n 'export.destLabel': '目标文件夹',\n 'export.destPlaceholder': '例如 D:\\\\sync\\\\claude-shared',\n 'export.destHint': '~/.claude 之外的绝对路径。放进 git 仓库或云盘即可同步。',\n 'export.privacy':\n '这会把你的对话与记忆写到应用之外。如果把该文件夹推送到远端,这些内容将离开本机。',\n 'export.btn.confirm': '导出',\n 'export.btn.pending': '导出中…',\n 'export.success': '已导出 {{sessions}} 个会话 · {{memory}} 个记忆文件 · {{lines}} 条历史',\n 'export.successDest': '写入到 {{dest}}',\n 'export.close': '关闭',\n\n 'import.title': '导入归档',\n 'import.tagline': '从另一台设备导入项目的会话与记忆。路径会被重映射到本机。',\n 'import.bundleLabel': '归档文件夹',\n 'import.bundlePlaceholder': '已导出归档的路径',\n 'import.btn.load': '加载归档',\n 'import.btn.recheck': '重新检查',\n 'import.btn.loading': '加载中…',\n 'import.source': '来自 {{platform}} · {{cwd}}',\n 'import.targetLabel': '导入到(本机)',\n 'import.targetHint': '本机绝对路径。会话将与该目录关联。',\n 'import.targetId': '项目 id:{{id}}',\n 'import.suggestions': '建议',\n 'import.suggestion.existing-project': '已有项目',\n 'import.suggestion.original-path': '原始路径',\n 'import.suggestion.same-basename': '同名目录',\n 'import.policyLabel': '当会话已存在时',\n 'import.policy.skip': '跳过',\n 'import.policy.overwrite-if-newer': '较新则覆盖',\n 'import.policy.keep-both': '两者都留',\n 'import.sessions.heading': '会话',\n 'import.sessions.empty': '此归档没有会话。',\n 'import.action.create': '新建',\n 'import.action.overwrite': '覆盖',\n 'import.action.keep-both': '另存',\n 'import.action.skip': '跳过',\n 'import.memory.heading': '记忆',\n 'import.memory.create': '新建',\n 'import.memory.skip': '相同',\n 'import.memory.conflict': '冲突 → {{name}}',\n 'import.history': '将新增 {{n}} 条历史',\n 'import.btn.commit': '导入',\n 'import.btn.committing': '导入中…',\n 'import.result.title': '导入完成',\n 'import.result.summary':\n '已导入 {{n}} 个会话 · 跳过 {{skipped}} · {{memory}} 个记忆文件 · {{lines}} 条历史',\n 'import.result.viewProject': '打开已导入项目',\n 'import.empty': '输入归档文件夹路径以开始。',\n },\n} as const;\n\ntype Key = keyof (typeof DICT)['en'];\n\nfunction readInitial(): Locale {\n if (typeof window === 'undefined') return 'en';\n try {\n const saved = localStorage.getItem(STORAGE_KEY);\n if (saved === 'en' || saved === 'zh') return saved;\n } catch {\n /* fall through */\n }\n return navigator.language.toLowerCase().startsWith('zh') ? 'zh' : 'en';\n}\n\nlet _locale: Locale = typeof window !== 'undefined' ? readInitial() : 'en';\nconst listeners = new Set<(l: Locale) => void>();\n\nexport function getLocale(): Locale {\n return _locale;\n}\n\nexport function setLocale(next: Locale) {\n if (next === _locale) return;\n _locale = next;\n try {\n localStorage.setItem(STORAGE_KEY, next);\n } catch {\n /* storage might be blocked */\n }\n document.documentElement.setAttribute('lang', next);\n listeners.forEach((fn) => fn(next));\n}\n\nexport function useLocale(): { locale: Locale; setLocale: (l: Locale) => void; toggle: () => void } {\n const [locale, setLocaleState] = useState(_locale);\n\n useEffect(() => {\n const handler = (l: Locale) => setLocaleState(l);\n listeners.add(handler);\n return () => {\n listeners.delete(handler);\n };\n }, []);\n\n const setNext = useCallback((l: Locale) => {\n setLocale(l);\n }, []);\n\n const toggle = useCallback(() => {\n setLocale(_locale === 'en' ? 'zh' : 'en');\n }, []);\n\n return { locale, setLocale: setNext, toggle };\n}\n\nexport function useT(): (key: Key, params?: Record<string, string | number>) => string {\n const { locale } = useLocale();\n return useCallback(\n (key: Key, params?: Record<string, string | number>) => translate(locale, key, params),\n [locale],\n );\n}\n\nexport function translate(\n locale: Locale,\n key: Key,\n params?: Record<string, string | number>,\n): string {\n const dict = DICT[locale];\n let str: string = dict[key] ?? DICT.en[key] ?? key;\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n str = str.replaceAll(`{{${k}}}`, String(v));\n }\n }\n return str;\n}\n","import type { SearchEvent } from './api.ts';\n\nexport interface StreamSearchOpts {\n query: string;\n perSession?: number;\n maxSessions?: number;\n signal?: AbortSignal;\n}\n\nexport async function* streamSearch(opts: StreamSearchOpts): AsyncGenerator<SearchEvent> {\n const params = new URLSearchParams({ q: opts.query });\n if (opts.perSession !== undefined) params.set('perSession', String(opts.perSession));\n if (opts.maxSessions !== undefined) params.set('maxSessions', String(opts.maxSessions));\n\n const res = await fetch(`/api/search?${params.toString()}`, {\n signal: opts.signal,\n headers: { accept: 'application/x-ndjson' },\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`${res.status} ${res.statusText}${text ? ` — ${text}` : ''}`);\n }\n if (!res.body) {\n throw new Error('search stream returned no body');\n }\n\n const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();\n let buffer = '';\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n if (buffer.trim()) {\n const event = parseLine(buffer);\n if (event) yield event;\n }\n return;\n }\n buffer += value;\n let nl: number;\n while ((nl = buffer.indexOf('\\n')) !== -1) {\n const raw = buffer.slice(0, nl);\n buffer = buffer.slice(nl + 1);\n const event = parseLine(raw);\n if (event) yield event;\n }\n }\n } finally {\n try {\n await reader.cancel();\n } catch {\n /* ignore */\n }\n }\n}\n\nfunction parseLine(raw: string): SearchEvent | null {\n const line = raw.trim();\n if (!line) return null;\n try {\n return JSON.parse(line) as SearchEvent;\n } catch {\n return null;\n }\n}\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { LoadingDots } from './Loading.tsx';\nimport type { SearchBlockKind, SearchDone, SearchSessionHit, SearchSnippet } from '../lib/api.ts';\nimport { formatRelativeTime } from '../lib/format.ts';\nimport { HOTKEY_HINT } from '../lib/hotkeys.ts';\nimport { useT } from '../lib/i18n.ts';\nimport { streamSearch } from '../lib/search-stream.ts';\n\nconst MIN_QUERY = 2;\nconst DEBOUNCE_MS = 220;\n\ninterface FlatItem {\n hit: SearchSessionHit;\n snippet: SearchSnippet;\n hitIndex: number;\n snippetIndex: number;\n flatIndex: number;\n}\n\nexport default function SearchModal({\n open,\n onClose,\n}: {\n open: boolean;\n onClose: () => void;\n}) {\n const t = useT();\n const navigate = useNavigate();\n const [query, setQuery] = useState('');\n const [hits, setHits] = useState<SearchSessionHit[]>([]);\n const [done, setDone] = useState<SearchDone | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const inputRef = useRef<HTMLInputElement | null>(null);\n const listRef = useRef<HTMLDivElement | null>(null);\n const controllerRef = useRef<AbortController | null>(null);\n\n const flatItems: FlatItem[] = useMemo(() => {\n const out: FlatItem[] = [];\n let flat = 0;\n hits.forEach((hit, hitIndex) => {\n hit.snippets.forEach((snippet, snippetIndex) => {\n out.push({ hit, snippet, hitIndex, snippetIndex, flatIndex: flat++ });\n });\n });\n return out;\n }, [hits]);\n\n const trimmedQuery = query.trim();\n\n useEffect(() => {\n if (!open) return;\n const id = window.setTimeout(() => {\n runSearch(trimmedQuery);\n }, DEBOUNCE_MS);\n return () => window.clearTimeout(id);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [trimmedQuery, open]);\n\n useEffect(() => {\n setActiveIndex(0);\n }, [hits.length]);\n\n useEffect(() => {\n if (!open) {\n controllerRef.current?.abort();\n controllerRef.current = null;\n setQuery('');\n setHits([]);\n setDone(null);\n setError(null);\n setLoading(false);\n setActiveIndex(0);\n return;\n }\n // Focus input on open. requestAnimationFrame ensures the dialog is in the DOM.\n let raf = 0;\n raf = requestAnimationFrame(() => {\n inputRef.current?.focus();\n });\n function onWindowKey(e: KeyboardEvent) {\n if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n }\n }\n window.addEventListener('keydown', onWindowKey);\n return () => {\n cancelAnimationFrame(raf);\n window.removeEventListener('keydown', onWindowKey);\n };\n }, [open, onClose]);\n\n const runSearch = useCallback((q: string) => {\n controllerRef.current?.abort();\n if (q.length < MIN_QUERY) {\n setHits([]);\n setDone(null);\n setError(null);\n setLoading(false);\n return;\n }\n const controller = new AbortController();\n controllerRef.current = controller;\n setHits([]);\n setDone(null);\n setError(null);\n setLoading(true);\n (async () => {\n try {\n for await (const event of streamSearch({ query: q, signal: controller.signal })) {\n if (controller.signal.aborted) return;\n if (event.type === 'session') {\n setHits((prev) => [...prev, event]);\n } else if (event.type === 'done') {\n setDone(event);\n }\n }\n } catch (err) {\n if (controller.signal.aborted) return;\n setError((err as Error).message);\n } finally {\n if (!controller.signal.aborted) setLoading(false);\n }\n })();\n }, []);\n\n const navigateToSnippet = useCallback(\n (hit: SearchSessionHit, snippet: SearchSnippet) => {\n const params = new URLSearchParams({ focus: snippet.uuid, q: trimmedQuery });\n navigate(\n `/projects/${encodeURIComponent(hit.projectId)}/sessions/${encodeURIComponent(hit.sessionId)}?${params.toString()}`,\n );\n onClose();\n },\n [navigate, onClose, trimmedQuery],\n );\n\n function onKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {\n // Escape is handled at window level (works regardless of focus).\n if (flatItems.length === 0) return;\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setActiveIndex((i) => Math.min(i + 1, flatItems.length - 1));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setActiveIndex((i) => Math.max(i - 1, 0));\n } else if (e.key === 'Home') {\n e.preventDefault();\n setActiveIndex(0);\n } else if (e.key === 'End') {\n e.preventDefault();\n setActiveIndex(flatItems.length - 1);\n } else if (e.key === 'Enter') {\n e.preventDefault();\n const item = flatItems[activeIndex];\n if (item) navigateToSnippet(item.hit, item.snippet);\n }\n }\n\n useEffect(() => {\n if (!listRef.current) return;\n const el = listRef.current.querySelector<HTMLElement>(`[data-flat-index=\"${activeIndex}\"]`);\n el?.scrollIntoView({ block: 'nearest' });\n }, [activeIndex]);\n\n if (!open) return null;\n\n const showEmpty = trimmedQuery.length === 0;\n const showRefine = !showEmpty && trimmedQuery.length < MIN_QUERY;\n const showNoResults = !loading && !error && trimmedQuery.length >= MIN_QUERY && hits.length === 0 && done !== null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-start justify-center px-4 pt-[12vh] sm:px-6\">\n <button\n type=\"button\"\n aria-label={t('delete.close')}\n onClick={onClose}\n className=\"fixed inset-0 bg-[var(--color-canvas)]/65 backdrop-blur-sm\"\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={t('search.action.open')}\n className=\"relative z-10 w-full max-w-2xl rounded-[var(--radius-panel)] border border-[var(--color-hairline)] bg-[var(--color-surface)] shadow-[var(--shadow-pop)]\"\n onKeyDown={onKeyDown}\n >\n <header className=\"flex items-center gap-3 border-b border-[var(--color-hairline)] px-5 py-4\">\n <SearchIcon className=\"text-[var(--color-fg-muted)]\" />\n <input\n ref={inputRef}\n type=\"search\"\n autoFocus\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={t('search.placeholder')}\n className=\"flex-1 bg-transparent text-[15px] text-[var(--color-fg-primary)] placeholder:text-[var(--color-fg-faint)] focus:outline-none\"\n />\n {loading && (\n <span className=\"inline-flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--color-fg-muted)]\">\n {t('search.scanning')}\n <LoadingDots />\n </span>\n )}\n </header>\n\n <div ref={listRef} className=\"max-h-[60vh] overflow-y-auto px-1 py-2\">\n {showEmpty && (\n <Hint>{t('search.empty')}</Hint>\n )}\n {showRefine && (\n <Hint>{t('search.refineQuery')}</Hint>\n )}\n {error && (\n <Hint tone=\"danger\">{t('search.error', { msg: error })}</Hint>\n )}\n {showNoResults && (\n <Hint>{t('search.noResults')}</Hint>\n )}\n\n {hits.map((hit, hitIndex) => (\n <SessionGroup\n key={`${hit.projectId}/${hit.sessionId}`}\n hit={hit}\n hitIndex={hitIndex}\n flatItems={flatItems}\n activeIndex={activeIndex}\n query={trimmedQuery}\n onPick={navigateToSnippet}\n onHover={setActiveIndex}\n />\n ))}\n\n {done?.truncated && (\n <Hint>{t('search.moreSessions', { n: done.matched })}</Hint>\n )}\n </div>\n\n <footer className=\"flex items-center justify-between border-t border-[var(--color-hairline)] px-5 py-2.5 font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--color-fg-muted)]\">\n <span>{t('search.shortcut', { hint: HOTKEY_HINT })} · {t('search.escapeHint')}</span>\n {done && hits.length > 0 && (\n <span>\n {t('search.summary', {\n matched: done.matched,\n scanned: done.scanned,\n ms: done.durationMs,\n })}\n </span>\n )}\n </footer>\n </div>\n </div>\n );\n}\n\nfunction SessionGroup({\n hit,\n hitIndex,\n flatItems,\n activeIndex,\n query,\n onPick,\n onHover,\n}: {\n hit: SearchSessionHit;\n hitIndex: number;\n flatItems: FlatItem[];\n activeIndex: number;\n query: string;\n onPick: (hit: SearchSessionHit, snippet: SearchSnippet) => void;\n onHover: (flatIndex: number) => void;\n}) {\n const t = useT();\n const title = hit.customTitle ?? hit.title;\n const projectTail = useMemo(() => {\n const parts = hit.projectDecodedCwd.split(/[\\\\/]+/).filter(Boolean);\n return parts.at(-1) ?? hit.projectDecodedCwd;\n }, [hit.projectDecodedCwd]);\n\n return (\n <div className=\"px-2 pb-1 pt-3\">\n <div className=\"flex items-baseline justify-between gap-3 px-2\">\n <div className=\"min-w-0 flex items-baseline gap-2\">\n <span className=\"font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--color-accent)]\">\n {projectTail}\n </span>\n <span className=\"truncate font-display text-[13px] text-[var(--color-fg-primary)]\">\n {title}\n </span>\n </div>\n <span className=\"shrink-0 font-mono text-[10px] tabular-nums text-[var(--color-fg-muted)]\">\n {formatRelativeTime(hit.lastAt)}\n </span>\n </div>\n <ul className=\"mt-1.5 space-y-0.5\">\n {hit.snippets.map((snippet, i) => {\n const item = flatItems.find(\n (f) => f.hitIndex === hitIndex && f.snippetIndex === i,\n );\n const flat = item?.flatIndex ?? -1;\n const active = flat === activeIndex;\n return (\n <li key={`${snippet.uuid}-${i}`}>\n <button\n type=\"button\"\n data-flat-index={flat}\n onMouseEnter={() => onHover(flat)}\n onClick={() => onPick(hit, snippet)}\n className={\n 'block w-full rounded-[var(--radius-input)] px-3 py-2 text-left transition ' +\n (active\n ? 'bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\n : 'text-[var(--color-fg-secondary)] hover:bg-[var(--color-sunken)]')\n }\n >\n <div className=\"flex items-baseline gap-2 font-mono text-[10px] uppercase tracking-[0.16em] text-[var(--color-fg-muted)]\">\n <span>{t(`search.role.${snippet.role}` as const)}</span>\n <span>·</span>\n <span>{kindLabel(snippet.blockKind, t)}</span>\n </div>\n <p className=\"mt-1 truncate text-[13px] leading-relaxed\">\n <span className=\"text-[var(--color-fg-faint)]\">{snippet.before}</span>\n <mark className=\"rounded-sm bg-[var(--color-accent-soft)] px-0.5 font-medium text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\n {snippet.match}\n </mark>\n <span className=\"text-[var(--color-fg-faint)]\">{snippet.after}</span>\n </p>\n </button>\n </li>\n );\n })}\n </ul>\n {hit.hasMore && (\n <p className=\"px-3 pt-1 font-mono text-[10px] uppercase tracking-[0.16em] text-[var(--color-fg-faint)]\">\n {t('search.moreInSession')}\n </p>\n )}\n </div>\n );\n}\n\nfunction kindLabel(\n kind: SearchBlockKind,\n t: ReturnType<typeof useT>,\n): string {\n switch (kind) {\n case 'text':\n return t('search.kindText');\n case 'tool_use':\n return t('search.kindToolUse');\n case 'tool_result':\n return t('search.kindToolResult');\n case 'thinking':\n return t('search.kindThinking');\n }\n}\n\nfunction Hint({\n children,\n tone = 'normal',\n}: {\n children: React.ReactNode;\n tone?: 'normal' | 'danger';\n}) {\n const cls =\n tone === 'danger'\n ? 'text-[var(--color-danger)]'\n : 'text-[var(--color-fg-muted)]';\n return (\n <p className={`px-5 py-6 text-center text-sm ${cls}`}>{children}</p>\n );\n}\n\nfunction SearchIcon({ className = '' }: { className?: string }) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n className={className}\n aria-hidden\n >\n <circle cx=\"11\" cy=\"11\" r=\"6.2\" />\n <path d=\"M20 20l-4.3-4.3\" />\n </svg>\n );\n}\n","import { useLocale } from '../lib/i18n.ts';\n\nexport default function LocaleToggle({ className = '' }: { className?: string }) {\n const { locale, setLocale } = useLocale();\n return (\n <div\n role=\"group\"\n aria-label=\"Language\"\n className={\n 'inline-flex h-9 items-center rounded-full border border-[var(--color-hairline)] bg-[var(--color-sunken)] p-0.5 text-[11px] font-medium ' +\n className\n }\n >\n <Pill active={locale === 'en'} onClick={() => setLocale('en')} label=\"EN\" />\n <Pill active={locale === 'zh'} onClick={() => setLocale('zh')} label=\"中\" />\n </div>\n );\n}\n\nfunction Pill({\n active,\n onClick,\n label,\n}: {\n active: boolean;\n onClick: () => void;\n label: string;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-pressed={active}\n className={\n 'h-7 min-w-[2rem] rounded-full px-2 transition ' +\n (active\n ? 'bg-[var(--color-surface)] text-[var(--color-fg-primary)] shadow-[var(--shadow-rise)]'\n : 'text-[var(--color-fg-muted)] hover:text-[var(--color-fg-primary)]')\n }\n >\n {label}\n </button>\n );\n}\n","import { useCallback, useEffect, useState } from 'react';\n\nexport type Theme = 'light' | 'dark';\n\nconst STORAGE_KEY = 'theme';\n\nfunction readInitial(): Theme {\n if (typeof document === 'undefined') return 'light';\n return document.documentElement.classList.contains('dark') ? 'dark' : 'light';\n}\n\nexport function useTheme(): {\n theme: Theme;\n setTheme: (next: Theme) => void;\n toggle: () => void;\n} {\n const [theme, setThemeState] = useState<Theme>(readInitial);\n\n const setTheme = useCallback((next: Theme) => {\n setThemeState(next);\n document.documentElement.classList.toggle('dark', next === 'dark');\n try {\n localStorage.setItem(STORAGE_KEY, next);\n } catch {\n /* storage might be blocked — silently fall back to in-memory */\n }\n }, []);\n\n const toggle = useCallback(() => {\n setTheme(theme === 'dark' ? 'light' : 'dark');\n }, [theme, setTheme]);\n\n // Sync if the system preference changes and the user hasn't explicitly chosen\n useEffect(() => {\n const mq = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = (e: MediaQueryListEvent) => {\n if (localStorage.getItem(STORAGE_KEY)) return;\n setTheme(e.matches ? 'dark' : 'light');\n };\n mq.addEventListener('change', handler);\n return () => mq.removeEventListener('change', handler);\n }, [setTheme]);\n\n return { theme, setTheme, toggle };\n}\n","import { useTheme } from '../lib/theme.ts';\n\nexport default function ThemeToggle({ className = '' }: { className?: string }) {\n const { theme, toggle } = useTheme();\n const isDark = theme === 'dark';\n\n return (\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={isDark ? 'Switch to light theme' : 'Switch to dark theme'}\n title={isDark ? 'Light' : 'Dark'}\n className={\n 'group inline-flex h-9 w-16 items-center rounded-full border border-[var(--color-hairline)] bg-[var(--color-sunken)] px-1 transition hover:border-[var(--color-hairline-strong)] ' +\n className\n }\n >\n <span\n className=\"flex h-7 w-7 items-center justify-center rounded-full bg-[var(--color-surface)] text-[var(--color-fg-secondary)] shadow-[var(--shadow-rise)] transition-transform duration-300\"\n style={{ transform: isDark ? 'translateX(28px)' : 'translateX(0)' }}\n >\n {isDark ? <MoonIcon /> : <SunIcon />}\n </span>\n <span className=\"sr-only\">Toggle theme</span>\n </button>\n );\n}\n\nfunction SunIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"3.6\" />\n <path d=\"M12 3v2.4M12 18.6V21M3 12h2.4M18.6 12H21M5.6 5.6l1.7 1.7M16.7 16.7l1.7 1.7M5.6 18.4l1.7-1.7M16.7 7.3l1.7-1.7\" />\n </svg>\n );\n}\n\nfunction MoonIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20.5 14.4A8.5 8.5 0 1 1 9.6 3.5a7 7 0 0 0 10.9 10.9z\" />\n </svg>\n );\n}\n","export * from '../../../shared/types.ts';\n\nexport async function api<T>(path: string, init?: RequestInit): Promise<T> {\n const res = await fetch(path, {\n ...init,\n headers: { 'content-type': 'application/json', ...(init?.headers ?? {}) },\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n let detail = text;\n if (text) {\n try {\n const parsed = JSON.parse(text) as { error?: unknown };\n if (typeof parsed.error === 'string') detail = parsed.error;\n } catch {\n /* keep raw text */\n }\n }\n throw new Error(`${res.status} ${res.statusText}${detail ? ` — ${detail}` : ''}`);\n }\n return res.json() as Promise<T>;\n}\n","export const queryKeys = {\n health: () => ['health'] as const,\n projects: () => ['projects'] as const,\n projectSessions: (projectId: string) => ['project-sessions', projectId] as const,\n projectMemory: (projectId: string) => ['project-memory', projectId] as const,\n session: (projectId: string, sessionId: string) =>\n ['session', projectId, sessionId] as const,\n sessionModifiedFiles: (projectId: string, sessionId: string) =>\n ['session-modified-files', projectId, sessionId] as const,\n diskUsage: () => ['disk-usage'] as const,\n diskCleanupSuggestions: () => ['disk-cleanup-suggestions'] as const,\n search: (query: string) => ['search', query] as const,\n version: () => ['version'] as const,\n};\n","import { useMutation, useQuery } from '@tanstack/react-query';\nimport { AnimatePresence, motion } from 'motion/react';\nimport { useEffect, useRef, useState, type ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport { api, type VersionInfo, type VersionUpdateResult } from '../lib/api.ts';\nimport { useT } from '../lib/i18n.ts';\nimport { queryKeys } from '../lib/query-keys.ts';\n\nconst UPDATE_COMMAND = 'npm install -g @zzusp/ccsm@latest';\n\n/** Shared version query — deduped by query key, so the sidebar badge and the\n * mobile hamburger dot read the same cached result. */\nexport function useVersionInfo() {\n return useQuery({\n queryKey: queryKeys.version(),\n queryFn: () => api<VersionInfo>('/api/version'),\n staleTime: 30 * 60 * 1000,\n refetchOnWindowFocus: false,\n });\n}\n\n/** Sidebar footer row: current version, or an amber \"new version\" pill with a\n * red notification dot. Opens the modal. */\nexport default function VersionNotice() {\n const t = useT();\n const [open, setOpen] = useState(false);\n const { data } = useVersionInfo();\n\n if (!data) return null;\n\n return (\n <>\n <button\n type=\"button\"\n onClick={() => setOpen(true)}\n className=\"group flex w-full items-center justify-between gap-2 text-left\"\n >\n <span className=\"eyebrow\">{t('version.label')}</span>\n {data.hasUpdate ? (\n <span className=\"inline-flex items-center gap-1.5 rounded-[var(--radius-control)] border border-[var(--color-accent)]/45 bg-[var(--color-accent-soft)] px-2 py-0.5 font-mono text-[10px] font-medium uppercase tracking-[0.1em] text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\n <span aria-hidden className=\"relative inline-flex h-1.5 w-1.5\">\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-danger)] pulse-danger\" />\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-danger)]\" />\n </span>\n {t('version.badge.new', { v: `v${data.latest}` })}\n </span>\n ) : (\n <span className=\"font-mono text-[11px] text-[var(--color-fg-faint)] transition-colors group-hover:text-[var(--color-fg-secondary)]\">\n v{data.current}\n </span>\n )}\n </button>\n {createPortal(\n <AnimatePresence>\n {open && <VersionModal info={data} onClose={() => setOpen(false)} />}\n </AnimatePresence>,\n document.body,\n )}\n </>\n );\n}\n\nfunction VersionModal({ info, onClose }: { info: VersionInfo; onClose: () => void }) {\n const t = useT();\n const mutation = useMutation({\n mutationFn: () => api<VersionUpdateResult>('/api/version/update', { method: 'POST' }),\n });\n\n const isPendingRef = useRef(mutation.isPending);\n isPendingRef.current = mutation.isPending;\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape' && !isPendingRef.current) onClose();\n }\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [onClose]);\n\n const result = mutation.data;\n const hasUpdate = info.hasUpdate;\n const notes = info.releaseNotes?.trim();\n const published = info.publishedAt ? new Date(info.publishedAt).toLocaleDateString() : null;\n const updateDone = !!result?.ok;\n // After a successful update the page should stop offering it; a failure keeps \"retry\" available.\n const showUpdateAction = hasUpdate && !updateDone;\n\n return (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.18 }}\n className=\"fixed inset-0 z-[60] flex items-start justify-center bg-[oklch(0.16_0.006_85_/_0.55)] px-4 py-12 backdrop-blur-[2px]\"\n onClick={() => !mutation.isPending && onClose()}\n >\n <motion.div\n initial={{ y: 8, opacity: 0 }}\n animate={{ y: 0, opacity: 1 }}\n transition={{ duration: 0.24, ease: [0.16, 1, 0.3, 1] }}\n className=\"flex max-h-[80vh] w-full max-w-xl flex-col overflow-hidden rounded-[var(--radius-panel)] border border-[var(--color-hairline)] bg-[var(--color-surface)] shadow-[var(--shadow-pop)]\"\n onClick={(e) => e.stopPropagation()}\n >\n <header className=\"flex items-start justify-between gap-4 border-b border-[var(--color-hairline)] px-6 py-5\">\n <div className=\"min-w-0\">\n <p className=\"eyebrow text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\n {hasUpdate ? t('version.modal.eyebrowUpdate') : t('version.modal.eyebrowLatest')}\n </p>\n <h2 className=\"mt-1 font-display text-2xl font-light tracking-tight text-[var(--color-fg-primary)]\">\n {hasUpdate ? t('version.modal.titleUpdate') : t('version.modal.titleLatest')}\n </h2>\n <div className=\"mt-3 flex flex-wrap items-center gap-2\">\n <VersionChip label={t('version.current')} value={`v${info.current}`} />\n {hasUpdate && info.latest && (\n <>\n <span aria-hidden className=\"font-mono text-sm text-[var(--color-fg-faint)]\">→</span>\n <VersionChip label={t('version.latest')} value={`v${info.latest}`} accent />\n </>\n )}\n </div>\n {(published || info.checkError) && (\n <p className=\"mt-2 font-mono text-[11px] text-[var(--color-fg-faint)]\">\n {published && t('version.published', { date: published })}\n {info.checkError && (published ? ` · ${t('version.checkFailed')}` : t('version.checkFailed'))}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={mutation.isPending}\n aria-label={t('version.btn.close')}\n className=\"shrink-0 rounded-xl p-1.5 text-[var(--color-fg-muted)] hover:bg-[var(--color-sunken)] hover:text-[var(--color-fg-primary)] disabled:opacity-50\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\">\n <path d=\"M6 6l12 12M18 6L6 18\" />\n </svg>\n </button>\n </header>\n\n <div className=\"min-h-0 flex-1 space-y-4 overflow-y-auto px-6 py-5\">\n {result ? (\n <UpdateOutcome result={result} />\n ) : (\n <section>\n <p className=\"eyebrow mb-2\">{t('version.notes')}</p>\n {notes ? (\n <div className=\"max-h-72 overflow-y-auto rounded-[var(--radius-input)] border border-[var(--color-hairline)] bg-[var(--color-sunken)] px-4 py-3.5\">\n <ReleaseNotes source={notes} />\n </div>\n ) : (\n <p className=\"text-sm text-[var(--color-fg-muted)]\">{t('version.notesEmpty')}</p>\n )}\n </section>\n )}\n\n {mutation.error && (\n <p className=\"rounded-md border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-3 py-2 text-sm text-[var(--color-danger)]\">\n {(mutation.error as Error).message}\n </p>\n )}\n </div>\n\n <footer className=\"flex items-center justify-between gap-2 border-t border-[var(--color-hairline)] px-6 py-4\">\n <div className=\"flex items-center gap-1\">\n {info.releaseUrl && (\n <ExternalLink href={info.releaseUrl} label={t('version.btn.releasePage')} />\n )}\n <ExternalLink href={info.repositoryUrl} label={t('version.btn.repo')} />\n </div>\n <div className=\"flex items-center gap-2\">\n {showUpdateAction ? (\n <>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={mutation.isPending}\n className=\"rounded-[var(--radius-control)] border border-[var(--color-hairline-strong)] px-4 py-1.5 text-sm text-[var(--color-fg-secondary)] hover:bg-[var(--color-sunken)] disabled:opacity-50\"\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() => mutation.mutate()}\n disabled={mutation.isPending}\n className=\"inline-flex items-center gap-2 rounded-[var(--radius-control)] bg-[var(--color-fg-primary)] px-4 py-1.5 text-sm font-medium text-[var(--color-canvas)] shadow-[var(--shadow-rise)] hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {mutation.isPending && <Spinner />}\n {mutation.isPending ? t('version.btn.updating') : t('version.btn.update')}\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-[var(--radius-control)] bg-[var(--color-fg-primary)] px-4 py-1.5 text-sm font-medium text-[var(--color-canvas)] hover:opacity-90\"\n >\n {t('common.done')}\n </button>\n )}\n </div>\n </footer>\n </motion.div>\n </motion.div>\n );\n}\n\nfunction UpdateOutcome({ result }: { result: VersionUpdateResult }) {\n const t = useT();\n if (result.ok) {\n return (\n <section className=\"space-y-3\">\n <div className=\"rounded-[var(--radius-card)] border border-[var(--color-moss)]/40 bg-[var(--color-moss-soft)] px-3 py-2.5 text-sm text-[var(--color-fg-primary)]\">\n <p className=\"font-medium\">{t('version.update.successTitle')}</p>\n <p className=\"mt-0.5 text-[var(--color-fg-secondary)]\">\n {t('version.update.success', { v: `v${result.toVersion ?? ''}` })}\n </p>\n </div>\n {result.output && <OutputBlock output={result.output} />}\n </section>\n );\n }\n return (\n <section className=\"space-y-3\">\n <div className=\"rounded-[var(--radius-card)] border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-3 py-2.5 text-sm\">\n <p className=\"font-medium text-[var(--color-danger)]\">{t('version.update.failTitle')}</p>\n <p className=\"mt-0.5 text-[var(--color-fg-secondary)]\">{t('version.update.fail')}</p>\n </div>\n <CommandHint />\n {result.output && <OutputBlock output={result.output} />}\n </section>\n );\n}\n\nfunction CommandHint() {\n const t = useT();\n return (\n <div>\n <p className=\"eyebrow mb-1.5\">{t('version.update.command')}</p>\n <div className=\"flex items-center gap-2 rounded-[var(--radius-input)] border border-[var(--color-hairline)] bg-[var(--color-sunken)] px-3 py-2\">\n <code className=\"min-w-0 flex-1 truncate font-mono text-[12.5px] text-[var(--color-fg-primary)]\">\n {UPDATE_COMMAND}\n </code>\n <CopyButton text={UPDATE_COMMAND} />\n </div>\n </div>\n );\n}\n\nfunction OutputBlock({ output }: { output: string }) {\n const t = useT();\n return (\n <div>\n <p className=\"eyebrow mb-1.5\">{t('version.update.output')}</p>\n <pre className=\"max-h-48 overflow-y-auto whitespace-pre-wrap break-words rounded-[var(--radius-input)] border border-[var(--color-hairline)] bg-[var(--color-sunken)] px-3 py-2 font-mono text-[11.5px] leading-[1.6] text-[var(--color-fg-muted)]\">\n {output}\n </pre>\n </div>\n );\n}\n\nfunction CopyButton({ text }: { text: string }) {\n const [copied, setCopied] = useState(false);\n return (\n <button\n type=\"button\"\n onClick={async () => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n } catch {\n /* clipboard may be blocked */\n }\n }}\n className=\"shrink-0 rounded-[var(--radius-control)] border border-[var(--color-hairline-strong)] px-2 py-1 text-[var(--color-fg-muted)] hover:bg-[var(--color-surface)] hover:text-[var(--color-fg-primary)]\"\n aria-label=\"copy\"\n >\n {copied ? (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M5 12l5 5L20 6\" />\n </svg>\n ) : (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" />\n <path d=\"M5 15V5a2 2 0 0 1 2-2h10\" />\n </svg>\n )}\n </button>\n );\n}\n\nfunction ExternalLink({ href, label }: { href: string; label: string }) {\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 rounded-[var(--radius-control)] px-2.5 py-1.5 text-xs font-medium text-[var(--color-fg-secondary)] hover:bg-[var(--color-sunken)] hover:text-[var(--color-fg-primary)]\"\n >\n {label}\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M7 17L17 7\" />\n <path d=\"M8 7h9v9\" />\n </svg>\n </a>\n );\n}\n\nfunction Spinner() {\n return (\n <svg className=\"animate-spin\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" strokeWidth=\"2.5\" opacity=\"0.25\" />\n <path d=\"M21 12a9 9 0 0 0-9-9\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" />\n </svg>\n );\n}\n\n/** A labelled version pill: small uppercase label + mono version number. */\nfunction VersionChip({ label, value, accent = false }: { label: string; value: string; accent?: boolean }) {\n return (\n <span\n className={\n 'inline-flex items-baseline gap-1.5 rounded-[var(--radius-control)] border px-2.5 py-1 font-mono ' +\n (accent\n ? 'border-[var(--color-accent)]/45 bg-[var(--color-accent-soft)]'\n : 'border-[var(--color-hairline)] bg-[var(--color-surface)]')\n }\n >\n <span className=\"text-[9px] uppercase tracking-[0.12em] text-[var(--color-fg-faint)]\">{label}</span>\n <span\n className={\n 'text-[13px] ' +\n (accent\n ? 'text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]'\n : 'text-[var(--color-fg-primary)]')\n }\n >\n {value}\n </span>\n </span>\n );\n}\n\n/** Minimal, dependency-free Markdown for GitHub release notes. Renders a safe\n * subset (headings, lists, code, links, emphasis) as React nodes — never raw\n * HTML, so notes can't inject markup. */\nfunction ReleaseNotes({ source }: { source: string }) {\n return (\n <div className=\"space-y-2.5 text-[13px] leading-[1.65] text-[var(--color-fg-primary)]\">\n {renderBlocks(source)}\n </div>\n );\n}\n\nfunction isBlockStart(l: string): boolean {\n return (\n /^\\s*```/.test(l) ||\n /^(#{1,6})\\s+/.test(l) ||\n /^\\s*([-*+]|\\d+\\.)\\s+/.test(l) ||\n /^\\s*([-*_])\\1{2,}\\s*$/.test(l)\n );\n}\n\nfunction renderBlocks(src: string): ReactNode[] {\n const lines = src.replace(/\\r\\n/g, '\\n').split('\\n');\n const at = (n: number) => lines[n] ?? '';\n const out: ReactNode[] = [];\n let i = 0;\n let key = 0;\n\n while (i < lines.length) {\n const line = at(i);\n\n if (!line.trim()) {\n i++;\n continue;\n }\n\n // Fenced code block\n if (/^\\s*```/.test(line)) {\n const buf: string[] = [];\n i++;\n while (i < lines.length && !/^\\s*```/.test(at(i))) {\n buf.push(at(i));\n i++;\n }\n i++; // skip closing fence\n out.push(\n <pre\n key={key++}\n className=\"overflow-x-auto rounded-[var(--radius-control)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-3 py-2 font-mono text-[12px] leading-[1.6] text-[var(--color-fg-secondary)]\"\n >\n {buf.join('\\n')}\n </pre>,\n );\n continue;\n }\n\n // Heading\n const h = line.match(/^(#{1,6})\\s+(.*)$/);\n if (h) {\n const level = (h[1] ?? '').length;\n out.push(\n <p\n key={key++}\n className={\n 'font-display font-medium tracking-tight text-[var(--color-fg-primary)] ' +\n (level <= 2 ? 'text-[15px]' : 'text-[13.5px]')\n }\n >\n {renderInline(h[2] ?? '')}\n </p>,\n );\n i++;\n continue;\n }\n\n // Horizontal rule\n if (/^\\s*([-*_])\\1{2,}\\s*$/.test(line)) {\n out.push(<hr key={key++} className=\"border-[var(--color-hairline)]\" />);\n i++;\n continue;\n }\n\n // List (consecutive items)\n if (/^\\s*([-*+]|\\d+\\.)\\s+/.test(line)) {\n const ordered = /^\\s*\\d+\\.\\s+/.test(line);\n const items: ReactNode[] = [];\n while (i < lines.length && /^\\s*([-*+]|\\d+\\.)\\s+/.test(at(i))) {\n const text = at(i).replace(/^\\s*([-*+]|\\d+\\.)\\s+/, '');\n items.push(<li key={items.length}>{renderInline(text)}</li>);\n i++;\n }\n const cls =\n 'ml-4 space-y-1 marker:text-[var(--color-fg-faint)] ' + (ordered ? 'list-decimal' : 'list-disc');\n out.push(\n ordered ? (\n <ol key={key++} className={cls}>\n {items}\n </ol>\n ) : (\n <ul key={key++} className={cls}>\n {items}\n </ul>\n ),\n );\n continue;\n }\n\n // Paragraph (consecutive plain lines)\n const para: string[] = [line];\n i++;\n while (i < lines.length && at(i).trim() && !isBlockStart(at(i))) {\n para.push(at(i));\n i++;\n }\n out.push(<p key={key++}>{renderInline(para.join(' '))}</p>);\n }\n\n return out;\n}\n\nconst INLINE_RE =\n /(`[^`]+`)|(\\[[^\\]]+\\]\\([^)\\s]+\\))|(\\*\\*[^*]+\\*\\*)|(\\*[^*\\s][^*]*\\*)|(_[^_\\s][^_]*_)/g;\n\nfunction renderInline(text: string): ReactNode[] {\n const nodes: ReactNode[] = [];\n let last = 0;\n let key = 0;\n let m: RegExpExecArray | null;\n INLINE_RE.lastIndex = 0;\n while ((m = INLINE_RE.exec(text)) !== null) {\n if (m.index > last) nodes.push(text.slice(last, m.index));\n const tok = m[0] ?? '';\n if (m[1]) {\n nodes.push(\n <code\n key={key++}\n className=\"rounded bg-[var(--color-surface)] px-1 py-0.5 font-mono text-[12px] text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\"\n >\n {tok.slice(1, -1)}\n </code>,\n );\n } else if (m[2]) {\n const lm = /^\\[([^\\]]+)\\]\\(([^)\\s]+)\\)$/.exec(tok)!;\n nodes.push(\n <a\n key={key++}\n href={lm[2] ?? '#'}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-[var(--color-accent-ink)] underline decoration-[var(--color-hairline-strong)] underline-offset-2 hover:decoration-[var(--color-accent)] dark:text-[var(--color-accent)]\"\n >\n {lm[1] ?? ''}\n </a>,\n );\n } else if (m[3]) {\n nodes.push(\n <strong key={key++} className=\"font-semibold text-[var(--color-fg-primary)]\">\n {tok.slice(2, -2)}\n </strong>,\n );\n } else {\n // m[4] or m[5]: emphasis\n nodes.push(<em key={key++}>{tok.slice(1, -1)}</em>);\n }\n last = m.index + tok.length;\n }\n if (last < text.length) nodes.push(text.slice(last));\n return nodes;\n}\n","import { useEffect, useState, type ReactNode } from 'react';\nimport { Link, useLocation } from 'react-router-dom';\nimport { HOTKEY_HINT } from '../lib/hotkeys.ts';\nimport { useT } from '../lib/i18n.ts';\nimport LocaleToggle from './LocaleToggle.tsx';\nimport ThemeToggle from './ThemeToggle.tsx';\nimport VersionNotice, { useVersionInfo } from './VersionNotice.tsx';\n\ninterface NavItem {\n to: string;\n labelKey: 'nav.projects' | 'nav.disk' | 'nav.import';\n icon: ReactNode;\n match: (pathname: string) => boolean;\n}\n\nconst NAV: NavItem[] = [\n {\n to: '/',\n labelKey: 'nav.projects',\n icon: <FolderIcon />,\n match: (p) => p === '/' || p.startsWith('/projects/'),\n },\n {\n to: '/disk',\n labelKey: 'nav.disk',\n icon: <DiskIcon />,\n match: (p) => p === '/disk' || p.startsWith('/disk/'),\n },\n {\n to: '/import',\n labelKey: 'nav.import',\n icon: <ImportIcon />,\n match: (p) => p === '/import' || p.startsWith('/import/'),\n },\n];\n\nexport default function Sidebar({ onSearchOpen }: { onSearchOpen?: () => void }) {\n const t = useT();\n const { pathname } = useLocation();\n const [open, setOpen] = useState(false);\n const hasUpdate = useVersionInfo().data?.hasUpdate ?? false;\n\n useEffect(() => {\n const handler = () => setOpen(false);\n window.addEventListener('resize', handler);\n return () => window.removeEventListener('resize', handler);\n }, []);\n\n return (\n <>\n <div className=\"topbar-glass sticky top-0 z-40 flex items-center justify-between border-b border-[var(--color-hairline)] px-4 py-3 lg:hidden\">\n <Brand />\n <div className=\"flex items-center gap-2\">\n {onSearchOpen && (\n <button\n type=\"button\"\n onClick={onSearchOpen}\n aria-label={t('search.action.open')}\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-xl border border-[var(--color-hairline)] text-[var(--color-fg-secondary)] hover:border-[var(--color-hairline-strong)] hover:text-[var(--color-accent)]\"\n >\n <SearchIcon />\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n aria-label={t('nav.toggleNav')}\n className=\"relative inline-flex h-9 w-9 items-center justify-center rounded-xl border border-[var(--color-hairline)] text-[var(--color-fg-secondary)] hover:border-[var(--color-hairline-strong)]\"\n >\n <MenuIcon open={open} />\n {hasUpdate && !open && (\n <span\n aria-hidden\n className=\"absolute right-1 top-1 inline-flex h-2 w-2\"\n title={t('version.modal.eyebrowUpdate')}\n >\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-danger)] pulse-danger\" />\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-danger)]\" />\n </span>\n )}\n </button>\n </div>\n </div>\n\n {open && (\n <button\n type=\"button\"\n aria-label={t('nav.closeNav')}\n onClick={() => setOpen(false)}\n className=\"fixed inset-0 z-40 bg-[var(--color-canvas)]/70 backdrop-blur-sm lg:hidden\"\n />\n )}\n\n <aside\n className={\n 'fixed inset-y-0 left-0 z-50 flex w-72 flex-col border-r border-[var(--color-hairline)] bg-[var(--color-sunken)] transition-transform duration-300 lg:sticky lg:top-0 lg:h-dvh lg:translate-x-0 ' +\n (open ? 'translate-x-0' : '-translate-x-full')\n }\n >\n <div className=\"flex h-[68px] items-center px-5\">\n <Brand />\n </div>\n\n {onSearchOpen && (\n <div className=\"px-4 pb-2\">\n <button\n type=\"button\"\n onClick={() => {\n setOpen(false);\n onSearchOpen();\n }}\n aria-label={t('search.action.open')}\n className=\"surface-card is-interactive flex w-full items-center gap-2.5 px-3 py-3 text-left\"\n style={{ borderRadius: 'var(--radius-input)' }}\n >\n <SearchIcon className=\"text-[var(--color-fg-muted)]\" />\n <span className=\"flex-1 truncate text-[13px] text-[var(--color-fg-muted)]\">\n {t('search.action.open')}\n </span>\n <kbd className=\"rounded border border-[var(--color-hairline)] bg-[var(--color-sunken)] px-1.5 py-0.5 font-mono text-[10px] uppercase tracking-[0.14em] text-[var(--color-fg-faint)]\">\n {HOTKEY_HINT}\n </kbd>\n </button>\n </div>\n )}\n\n <nav className=\"flex-1 overflow-y-auto px-4 py-3\">\n <p className=\"eyebrow px-2 pb-2\">{t('nav.workspace')}</p>\n <ul className=\"space-y-1\">\n {NAV.map((item) => {\n const isActive = item.match(pathname);\n return (\n <li key={item.to}>\n <Link\n to={item.to}\n aria-current={isActive ? 'page' : undefined}\n onClick={() => setOpen(false)}\n className={\n 'group flex items-center gap-3 rounded-[var(--radius-input)] border px-4 py-3 text-sm transition ' +\n (isActive\n ? 'border-[var(--color-hairline)] bg-[var(--color-surface)] text-[var(--color-fg-primary)] shadow-[var(--shadow-rise)]'\n : 'border-transparent text-[var(--color-fg-secondary)] hover:bg-[color-mix(in_oklch,var(--color-surface)_60%,transparent)] hover:text-[var(--color-fg-primary)]')\n }\n >\n <span\n className={\n 'transition-colors ' +\n (isActive\n ? 'text-[var(--color-accent)]'\n : 'text-[var(--color-fg-muted)] group-hover:text-[var(--color-accent)]')\n }\n >\n {item.icon}\n </span>\n <span className=\"font-medium tracking-tight\">{t(item.labelKey)}</span>\n </Link>\n </li>\n );\n })}\n </ul>\n </nav>\n\n <div className=\"surface-card mx-4 mb-4 space-y-3 p-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"eyebrow\">{t('nav.language')}</span>\n <LocaleToggle />\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"eyebrow\">{t('nav.theme')}</span>\n <ThemeToggle />\n </div>\n <VersionNotice />\n <p className=\"font-mono text-[10px] leading-snug text-[var(--color-fg-faint)]\">\n {t('app.brand.footnote')}\n </p>\n </div>\n </aside>\n </>\n );\n}\n\nfunction Brand() {\n const t = useT();\n return (\n <div className=\"flex items-center gap-2.5\">\n <span\n aria-hidden\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-2xl border border-[var(--color-hairline)] bg-[var(--color-surface)] text-[var(--color-accent)] shadow-[var(--shadow-rise)]\"\n >\n <Glyph />\n </span>\n <span className=\"flex flex-col leading-tight\">\n <span className=\"font-display text-[15px] font-medium tracking-tight text-[var(--color-fg-primary)]\">\n {t('app.brand.title')}\n </span>\n <span className=\"font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--color-fg-muted)]\">\n {t('app.brand.subtitle')}\n </span>\n </span>\n </div>\n );\n}\n\nfunction Glyph() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M19 7.5A8 8 0 1 0 19 16.5\" />\n <path d=\"M15.5 9.5a4.5 4.5 0 1 0 0 5\" opacity=\"0.45\" />\n </svg>\n );\n}\n\nfunction FolderIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M3 6.5A1.5 1.5 0 0 1 4.5 5h4.2a1.5 1.5 0 0 1 1.05.43l1.34 1.32A1.5 1.5 0 0 0 12.14 7.2H19.5A1.5 1.5 0 0 1 21 8.7v9.3a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 18z\" />\n </svg>\n );\n}\n\nfunction DiskIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <ellipse cx=\"12\" cy=\"6.5\" rx=\"8\" ry=\"2.5\" />\n <path d=\"M4 6.5v5c0 1.4 3.6 2.5 8 2.5s8-1.1 8-2.5v-5\" />\n <path d=\"M4 11.5v5c0 1.4 3.6 2.5 8 2.5s8-1.1 8-2.5v-5\" />\n </svg>\n );\n}\n\nfunction ImportIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 14V3\" />\n <path d=\"M8 10l4 4 4-4\" />\n <path d=\"M4 15v3.5A1.5 1.5 0 0 0 5.5 20h13a1.5 1.5 0 0 0 1.5-1.5V15\" />\n </svg>\n );\n}\n\nfunction SearchIcon({ className = '' }: { className?: string }) {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n className={className}\n aria-hidden\n >\n <circle cx=\"11\" cy=\"11\" r=\"6.2\" />\n <path d=\"M20 20l-4.3-4.3\" />\n </svg>\n );\n}\n\nfunction MenuIcon({ open }: { open: boolean }) {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" aria-hidden>\n {open ? (\n <>\n <path d=\"M6 6l12 12\" />\n <path d=\"M18 6L6 18\" />\n </>\n ) : (\n <>\n <path d=\"M4 7h16\" />\n <path d=\"M4 12h16\" />\n <path d=\"M4 17h12\" />\n </>\n )}\n </svg>\n );\n}\n","import { Fragment, type ReactNode } from 'react';\nimport { Link } from 'react-router-dom';\n\nexport interface Crumb {\n label: string;\n to?: string;\n mono?: boolean;\n icon?: ReactNode;\n}\n\nconst ITEM_BASE = 'inline-flex min-w-0 items-center gap-1.5 rounded-lg px-3 py-1';\n\nexport default function Breadcrumbs({ items }: { items: Crumb[] }) {\n return (\n <nav\n aria-label=\"Breadcrumb\"\n className=\"inline-flex max-w-full items-center gap-1 overflow-hidden rounded-xl border border-[var(--color-accent)] bg-[var(--color-surface)] px-2 py-1.5 text-sm shadow-[var(--shadow-rise)]\"\n >\n {items.map((c, i) => {\n const last = i === items.length - 1;\n const family = c.mono ? 'font-mono' : 'font-sans';\n const tone = last\n ? 'text-[var(--color-fg-primary)]'\n : 'text-[var(--color-fg-secondary)]';\n const inner = (\n <>\n {c.icon && <span className=\"shrink-0 text-[var(--color-accent)]\">{c.icon}</span>}\n <span className=\"truncate\">{c.label}</span>\n </>\n );\n return (\n <Fragment key={`${c.label}-${i}`}>\n {c.to && !last ? (\n <Link\n to={c.to}\n className={`${ITEM_BASE} ${family} ${tone} transition-colors duration-150 hover:bg-[var(--color-accent-soft)] hover:text-[var(--color-accent-ink)]`}\n >\n {inner}\n </Link>\n ) : (\n <span\n className={`${ITEM_BASE} ${family} ${tone}`}\n aria-current={last ? 'page' : undefined}\n >\n {inner}\n </span>\n )}\n {!last && <ChevronSep />}\n </Fragment>\n );\n })}\n </nav>\n );\n}\n\nfunction ChevronSep() {\n return (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.4\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"shrink-0 text-[var(--color-accent)]\"\n aria-hidden\n >\n <polyline points=\"9 6 15 12 9 18\" />\n </svg>\n );\n}\n\nexport function BreadcrumbFolderIcon() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M3 6.5A1.5 1.5 0 0 1 4.5 5h4.2a1.5 1.5 0 0 1 1.05.43l1.34 1.32A1.5 1.5 0 0 0 12.14 7.2H19.5A1.5 1.5 0 0 1 21 8.7v9.3a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 18z\" />\n </svg>\n );\n}\n","import { useMutation } from '@tanstack/react-query';\nimport { motion } from 'motion/react';\nimport { useEffect, useRef, useState } from 'react';\nimport { api, type ExportResult, type SessionSummary } from '../lib/api.ts';\nimport { useT } from '../lib/i18n.ts';\n\ninterface Props {\n projectId: string;\n sessions: SessionSummary[];\n onClose: () => void;\n}\n\nexport default function ExportDialog({ projectId, sessions, onClose }: Props) {\n const t = useT();\n const [destDir, setDestDir] = useState('');\n\n const mutation = useMutation({\n mutationFn: () =>\n api<ExportResult>(`/api/projects/${encodeURIComponent(projectId)}/export`, {\n method: 'POST',\n body: JSON.stringify({ sessionIds: sessions.map((s) => s.id), destDir: destDir.trim() }),\n }),\n });\n\n const isPendingRef = useRef(mutation.isPending);\n isPendingRef.current = mutation.isPending;\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape' && !isPendingRef.current) onClose();\n }\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [onClose]);\n\n const result = mutation.data;\n const showResult = !!result;\n const canSubmit = destDir.trim() !== '' && !mutation.isPending;\n\n return (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.18 }}\n className=\"fixed inset-0 z-[60] flex items-start justify-center bg-[oklch(0.16_0.006_85_/_0.55)] px-4 py-12 backdrop-blur-[2px]\"\n onClick={() => !mutation.isPending && onClose()}\n >\n <motion.div\n initial={{ y: 8, opacity: 0 }}\n animate={{ y: 0, opacity: 1 }}\n transition={{ duration: 0.24, ease: [0.16, 1, 0.3, 1] }}\n className=\"w-full max-w-xl overflow-hidden rounded-[var(--radius-panel)] border border-[var(--color-hairline)] bg-[var(--color-surface)] shadow-[var(--shadow-pop)]\"\n onClick={(e) => e.stopPropagation()}\n >\n <header className=\"flex items-start justify-between gap-4 border-b border-[var(--color-hairline)] px-6 py-5\">\n <div>\n <p className=\"eyebrow text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\n {showResult ? t('export.eyebrow.result') : t('export.eyebrow.confirm')}\n </p>\n <h2 className=\"mt-1 font-display text-2xl font-light tracking-tight text-[var(--color-fg-primary)]\">\n {showResult ? t('export.title.result') : t('export.title.confirm')}\n </h2>\n {!showResult && (\n <p className=\"mt-1.5 text-sm text-[var(--color-fg-muted)]\">\n {t('export.summary', { n: sessions.length })}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={mutation.isPending}\n aria-label={t('export.close')}\n className=\"rounded-xl p-1.5 text-[var(--color-fg-muted)] hover:bg-[var(--color-sunken)] hover:text-[var(--color-fg-primary)] disabled:opacity-50\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\">\n <path d=\"M6 6l12 12M18 6L6 18\" />\n </svg>\n </button>\n </header>\n\n {!showResult && (\n <div className=\"space-y-4 px-6 py-5\">\n <label className=\"block\">\n <span className=\"eyebrow\">{t('export.destLabel')}</span>\n <input\n type=\"text\"\n value={destDir}\n onChange={(e) => setDestDir(e.target.value)}\n placeholder={t('export.destPlaceholder')}\n spellCheck={false}\n autoFocus\n className=\"mt-1.5 w-full rounded-[var(--radius-input)] border border-[var(--color-hairline-strong)] bg-[var(--color-canvas)] px-3 py-2 font-mono text-sm text-[var(--color-fg-primary)] outline-none focus:border-[var(--color-accent)]\"\n />\n <span className=\"mt-1.5 block text-xs text-[var(--color-fg-muted)]\">\n {t('export.destHint')}\n </span>\n </label>\n\n <p className=\"rounded-[var(--radius-card)] border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-xs leading-relaxed text-[var(--color-accent-ink)] dark:text-[var(--color-fg-secondary)]\">\n {t('export.privacy')}\n </p>\n </div>\n )}\n\n {showResult && result && (\n <div className=\"space-y-3 px-6 py-5 text-sm\">\n <div className=\"rounded-[var(--radius-card)] border border-[var(--color-moss)]/40 bg-[var(--color-moss-soft)] px-3 py-2.5 text-[var(--color-fg-primary)]\">\n {t('export.success', {\n sessions: result.sessionsExported,\n memory: result.memoryFilesExported,\n lines: result.historyLinesExported,\n })}\n </div>\n <p className=\"break-all font-mono text-xs text-[var(--color-fg-muted)]\">\n {t('export.successDest', { dest: result.destDir })}\n </p>\n </div>\n )}\n\n {mutation.error && (\n <p className=\"mx-6 mb-3 rounded-md border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-3 py-2 text-sm text-[var(--color-danger)]\">\n {(mutation.error as Error).message}\n </p>\n )}\n\n <footer className=\"flex justify-end gap-2 border-t border-[var(--color-hairline)] px-6 py-4\">\n {!showResult ? (\n <>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={mutation.isPending}\n className=\"rounded-[var(--radius-control)] border border-[var(--color-hairline-strong)] px-4 py-1.5 text-sm text-[var(--color-fg-secondary)] hover:bg-[var(--color-sunken)] disabled:opacity-50\"\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() => mutation.mutate()}\n disabled={!canSubmit}\n className=\"rounded-[var(--radius-control)] bg-[var(--color-fg-primary)] px-4 py-1.5 text-sm font-medium text-[var(--color-canvas)] shadow-[var(--shadow-rise)] hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {mutation.isPending ? t('export.btn.pending') : t('export.btn.confirm')}\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-[var(--radius-control)] bg-[var(--color-fg-primary)] px-4 py-1.5 text-sm font-medium text-[var(--color-canvas)] hover:opacity-90\"\n >\n {t('common.done')}\n </button>\n )}\n </footer>\n </motion.div>\n </motion.div>\n );\n}\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\n\ninterface Props {\n eyebrow?: ReactNode;\n title: ReactNode;\n actions?: ReactNode;\n meta?: ReactNode;\n /** Plain-text version of the title used as the input's initial value during edit. */\n editableValue?: string;\n /** When provided, an edit affordance appears next to the title. */\n onTitleEdit?: (next: string) => Promise<void>;\n}\n\nexport default function PageHeader({\n eyebrow,\n title,\n actions,\n meta,\n editableValue,\n onTitleEdit,\n}: Props) {\n return (\n <header className=\"relative\">\n {eyebrow && <div className=\"eyebrow mb-1\">{eyebrow}</div>}\n <div className=\"flex flex-wrap items-center justify-between gap-x-6 gap-y-2\">\n <div className=\"min-w-0 flex-1\">\n <TitleSlot\n title={title}\n editableValue={editableValue}\n onTitleEdit={onTitleEdit}\n />\n </div>\n {actions && <div className=\"flex shrink-0 items-center gap-2\">{actions}</div>}\n </div>\n {meta && (\n <div className=\"mt-2 flex flex-wrap items-baseline gap-x-3 gap-y-1 text-xs\">\n {meta}\n </div>\n )}\n </header>\n );\n}\n\nconst TITLE_CLASS =\n 'font-display text-[clamp(1.375rem,2.4vw,1.75rem)] font-light leading-[1.15] tracking-[-0.015em] text-[var(--color-fg-primary)]';\n\nfunction TitleSlot({\n title,\n editableValue,\n onTitleEdit,\n}: {\n title: ReactNode;\n editableValue?: string;\n onTitleEdit?: Props['onTitleEdit'];\n}) {\n const [editing, setEditing] = useState(false);\n const [draft, setDraft] = useState(editableValue ?? '');\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n useEffect(() => {\n if (!editing && editableValue !== undefined) setDraft(editableValue);\n }, [editing, editableValue]);\n\n useEffect(() => {\n if (editing) inputRef.current?.select();\n }, [editing]);\n\n if (!onTitleEdit) {\n return <h1 className={TITLE_CLASS}>{title}</h1>;\n }\n\n function startEdit() {\n setDraft(editableValue ?? '');\n setError(null);\n setEditing(true);\n }\n\n async function commit() {\n const next = draft.trim();\n if (!onTitleEdit || !next || next === (editableValue ?? '')) {\n setEditing(false);\n return;\n }\n setSubmitting(true);\n setError(null);\n try {\n await onTitleEdit(next);\n setEditing(false);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n if (editing) {\n return (\n <div>\n <input\n ref={inputRef}\n value={draft}\n disabled={submitting}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n void commit();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n setEditing(false);\n setError(null);\n }\n }}\n onBlur={() => {\n if (!submitting) {\n setEditing(false);\n setError(null);\n }\n }}\n maxLength={200}\n className={\n TITLE_CLASS +\n ' w-full bg-transparent border-b border-[var(--color-accent)] outline-none focus:outline-none disabled:opacity-60'\n }\n />\n {error && (\n <p className=\"mt-1 text-xs text-[var(--color-danger)]\">{error}</p>\n )}\n </div>\n );\n }\n\n return (\n <div className=\"group flex items-center gap-3\">\n <h1 className={TITLE_CLASS}>{title}</h1>\n <button\n type=\"button\"\n onClick={startEdit}\n aria-label=\"Rename\"\n title=\"Rename\"\n className=\"flex-shrink-0 rounded-xl p-1.5 text-[var(--color-fg-muted)] opacity-0 transition hover:bg-[var(--color-sunken)] hover:text-[var(--color-fg-primary)] focus:opacity-100 group-hover:opacity-100\"\n >\n <PencilIcon />\n </button>\n </div>\n );\n}\n\nfunction PencilIcon() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M12 20h9\" />\n <path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z\" />\n </svg>\n );\n}\n\nexport function MetaItem({ label, value }: { label: string; value: ReactNode }) {\n return (\n <span className=\"inline-flex items-baseline gap-1.5\">\n <span className=\"eyebrow\">{label}</span>\n <span className=\"font-mono text-[13px] tabular-nums text-[var(--color-fg-primary)]\">{value}</span>\n </span>\n );\n}\n\nexport function Sep() {\n return <span aria-hidden className=\"text-[var(--color-fg-faint)]\">·</span>;\n}\n","export const RECENT_ACTIVITY_WINDOW_MIN = 5;\nexport const MAX_SESSION_MESSAGES = 5000;\n\n// Claude Code writes this synthetic `user` record when the operator aborts a turn\n// (Esc / Ctrl-C). It means the turn was *stopped*, not that Claude is still\n// working — so the \"working\" heuristic treats a trailing interrupt as idle.\nexport const INTERRUPTED_MARKER_RE = /^\\s*\\[Request interrupted by user/;\n","import type { SessionSummary } from '../lib/api.ts';\nimport { RECENT_ACTIVITY_WINDOW_MIN } from '../lib/constants.ts';\nimport { useT } from '../lib/i18n.ts';\n\ntype Variant = 'working' | 'live' | 'recent' | 'idle';\n\nfunction variantOf(s: SessionSummary): Variant {\n if (s.isWorking) return 'working';\n if (s.isLivePid) return 'live';\n if (s.isRecentlyActive) return 'recent';\n return 'idle';\n}\n\nexport function StatusDot({ session, withLabel = true }: { session: SessionSummary; withLabel?: boolean }) {\n const t = useT();\n const v = variantOf(session);\n const title =\n v === 'working'\n ? t('status.tooltip.working')\n : v === 'live'\n ? t('status.tooltip.live', { pid: session.livePid ?? '?' })\n : v === 'recent'\n ? t('status.tooltip.recent', { n: RECENT_ACTIVITY_WINDOW_MIN })\n : t('status.tooltip.idle');\n\n const label =\n v === 'working'\n ? t('status.working')\n : v === 'live'\n ? t('status.live', { pid: session.livePid ?? '?' })\n : v === 'recent'\n ? t('status.recent')\n : t('status.idle');\n\n // working + live share the accent ink; recent stays secondary; idle is faintest.\n const labelClass =\n v === 'idle'\n ? 'text-[var(--color-fg-faint)]'\n : v === 'recent'\n ? 'text-[var(--color-fg-secondary)]'\n : 'text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]';\n\n return (\n <span title={title} className=\"inline-flex items-center gap-2 text-xs\">\n <Dot variant={v} />\n {withLabel && (\n <span className={'font-mono uppercase tracking-[0.14em] ' + labelClass}>{label}</span>\n )}\n </span>\n );\n}\n\nfunction Dot({ variant }: { variant: Variant }) {\n if (variant === 'working') {\n // Marching three-dot breather — reads as \"actively doing something\", distinct\n // from the live pulse (idle-but-alive).\n return (\n <span aria-hidden className=\"loading-dots text-[var(--color-accent)]\">\n <span />\n <span />\n <span />\n </span>\n );\n }\n if (variant === 'live') {\n return (\n <span aria-hidden className=\"relative inline-flex h-2.5 w-2.5\">\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)] pulse-amber\" />\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)]\" />\n </span>\n );\n }\n if (variant === 'recent') {\n return (\n <span\n aria-hidden\n className=\"inline-block h-2.5 w-2.5 rounded-full bg-[var(--color-accent)]\"\n />\n );\n }\n return (\n <span\n aria-hidden\n className=\"inline-block h-2.5 w-2.5 rounded-full border border-[var(--color-hairline-strong)]\"\n />\n );\n}\n\nexport default StatusDot;\n","import type { Variants } from 'motion/react';\n\nexport const staggerParent: Variants = {\n hidden: {},\n show: {\n transition: { staggerChildren: 0.035, delayChildren: 0.04 },\n },\n};\n\nexport const fadeUpItem: Variants = {\n hidden: { opacity: 0, y: 8 },\n show: {\n opacity: 1,\n y: 0,\n transition: { duration: 0.36, ease: [0.16, 1, 0.3, 1] },\n },\n};\n\nexport const subtleFade: Variants = {\n hidden: { opacity: 0 },\n show: { opacity: 1, transition: { duration: 0.4 } },\n};\n","import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { motion } from 'motion/react';\nimport { useMemo, useState } from 'react';\nimport { Link, useParams } from 'react-router-dom';\nimport Breadcrumbs, { BreadcrumbFolderIcon } from '../components/Breadcrumbs.tsx';\nimport ExportDialog from '../components/ExportDialog.tsx';\nimport { Loading } from '../components/Loading.tsx';\nimport PageHeader, { MetaItem, Sep } from '../components/PageHeader.tsx';\nimport StatusDot from '../components/StatusDot.tsx';\nimport {\n api,\n type DeleteResult,\n type MemoryResponse,\n type ProjectSummary,\n type RevealProjectResult,\n type SessionSummary,\n} from '../lib/api.ts';\nimport { formatBytes, formatRelativeTime } from '../lib/format.ts';\nimport { useT } from '../lib/i18n.ts';\nimport { fadeUpItem, staggerParent } from '../lib/motion.ts';\nimport { queryKeys } from '../lib/query-keys.ts';\n\ninterface BulkProgress {\n index: number;\n total: number;\n}\n\ninterface BulkOutcome {\n ok: { sessionId: string; title: string }[];\n skipped: { sessionId: string; title: string; reason: string }[];\n failed: { sessionId: string; title: string; error: string }[];\n}\n\nexport default function ProjectDetail() {\n const t = useT();\n const queryClient = useQueryClient();\n const { projectId } = useParams<{ projectId: string }>();\n const id = projectId ?? '';\n const [selected, setSelected] = useState<Set<string>>(new Set());\n const [selectMode, setSelectMode] = useState(false);\n const [showExport, setShowExport] = useState(false);\n // 批量删除运行态:null 表示空闲;progress 推进时显示「正在删除 i/total」\n const [bulkProgress, setBulkProgress] = useState<BulkProgress | null>(null);\n const [bulkOutcome, setBulkOutcome] = useState<BulkOutcome | null>(null);\n const [bulkDetailOpen, setBulkDetailOpen] = useState(false);\n\n const sessionsQuery = useQuery({\n queryKey: queryKeys.projectSessions(id),\n queryFn: () => api<SessionSummary[]>(`/api/projects/${encodeURIComponent(id)}/sessions`),\n enabled: !!id,\n });\n\n const projectsQuery = useQuery({\n queryKey: queryKeys.projects(),\n queryFn: () => api<ProjectSummary[]>('/api/projects'),\n });\n\n const memoryQuery = useQuery({\n queryKey: queryKeys.projectMemory(id),\n queryFn: () => api<MemoryResponse>(`/api/projects/${encodeURIComponent(id)}/memory`),\n enabled: !!id,\n });\n const memoryCount = memoryQuery.data?.entries.length ?? 0;\n\n const revealMutation = useMutation({\n mutationFn: () =>\n api<RevealProjectResult>(`/api/projects/${encodeURIComponent(id)}/reveal`, {\n method: 'POST',\n }),\n onError: (err: Error) => {\n window.alert(t('project.action.openFolderFailed', { msg: err.message }));\n },\n });\n\n const project = useMemo(\n () => projectsQuery.data?.find((p) => p.id === id),\n [projectsQuery.data, id],\n );\n\n const sessions = sessionsQuery.data ?? [];\n const selectedSessions = useMemo(\n () => sessions.filter((s) => selected.has(s.id)),\n [sessions, selected],\n );\n const sessionsToExport = selected.size > 0 ? selectedSessions : sessions;\n const projectBytes = useMemo(() => sessions.reduce((a, s) => a + totalBytes(s), 0), [sessions]);\n // Disjoint buckets: working ⊂ live, so subtract working from the live count to\n // keep the three meta tallies mutually exclusive (matches the per-row StatusDot).\n const workingCount = useMemo(() => sessions.filter((s) => s.isWorking).length, [sessions]);\n const liveCount = useMemo(\n () => sessions.filter((s) => s.isLivePid && !s.isWorking).length,\n [sessions],\n );\n const recentCount = useMemo(\n () => sessions.filter((s) => s.isRecentlyActive && !s.isLivePid).length,\n [sessions],\n );\n\n function toggle(sid: string) {\n const next = new Set(selected);\n if (next.has(sid)) next.delete(sid);\n else next.add(sid);\n setSelected(next);\n }\n\n function toggleAll() {\n if (selected.size === sessions.length) setSelected(new Set());\n else setSelected(new Set(sessions.map((s) => s.id)));\n }\n\n function enterSelectMode() {\n setSelectMode(true);\n setBulkOutcome(null);\n setBulkDetailOpen(false);\n }\n\n function exitSelectMode() {\n setSelectMode(false);\n setSelected(new Set());\n }\n\n // 串行调用 DELETE /api/sessions(每次 items 长度 1),失败继续推进,\n // 完成后写入 outcome 并退出选择模式;不在完成时跳转,留给用户查看反馈。\n async function runBulkDelete() {\n const targets = selectedSessions;\n if (targets.length === 0) return;\n const outcome: BulkOutcome = { ok: [], skipped: [], failed: [] };\n setBulkOutcome(null);\n setBulkDetailOpen(false);\n let i = 0;\n for (const s of targets) {\n i++;\n const title = s.customTitle ?? s.title;\n setBulkProgress({ index: i, total: targets.length });\n try {\n const res = await api<DeleteResult>('/api/sessions', {\n method: 'DELETE',\n body: JSON.stringify({ items: [{ projectId: id, sessionId: s.id }] }),\n });\n if (res.deleted.length > 0) {\n outcome.ok.push({ sessionId: s.id, title });\n } else if (res.skipped.length > 0) {\n outcome.skipped.push({\n sessionId: s.id,\n title,\n reason: res.skipped[0]?.reason ?? 'unknown',\n });\n } else {\n // 后端既未删也未跳过的边界情况,记为失败避免静默\n outcome.failed.push({ sessionId: s.id, title, error: 'no-op' });\n }\n } catch (err) {\n outcome.failed.push({\n sessionId: s.id,\n title,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n setBulkProgress(null);\n setBulkOutcome(outcome);\n setBulkDetailOpen(outcome.skipped.length + outcome.failed.length > 0);\n setSelectMode(false);\n setSelected(new Set());\n // 刷新列表 / 项目元数据 / 磁盘统计\n queryClient.invalidateQueries({ queryKey: queryKeys.projects() });\n queryClient.invalidateQueries({ queryKey: queryKeys.projectSessions(id) });\n queryClient.invalidateQueries({ queryKey: queryKeys.diskUsage() });\n }\n\n const cwd = project?.decodedCwd ?? id;\n const parts = cwd.split(/[\\\\/]+/).filter(Boolean);\n const tail = parts.at(-1) ?? cwd;\n const head = parts.slice(0, -1).join('/');\n\n return (\n <section>\n <Breadcrumbs\n items={[\n { label: t('session.crumbProjects'), to: '/' },\n { label: tail, mono: true, icon: <BreadcrumbFolderIcon /> },\n ]}\n />\n\n <div className=\"surface-card mt-6 p-6\">\n <PageHeader\n eyebrow={\n <span className=\"inline-flex items-center gap-2\">\n {head ? (\n <span className=\"font-mono normal-case tracking-normal\">{head}/</span>\n ) : (\n t('project.eyebrow')\n )}\n {project?.cwdResolved === false && (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-2 py-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-[var(--color-danger)]\">\n <span aria-hidden className=\"h-1.5 w-1.5 rounded-full bg-[var(--color-danger)]\" />\n {t('project.warn.missingDir')}\n </span>\n )}\n </span>\n }\n title={<span className=\"font-mono\">{tail}</span>}\n actions={\n <>\n <button\n type=\"button\"\n onClick={() => revealMutation.mutate()}\n disabled={\n revealMutation.isPending || project?.cwdResolved === false\n }\n title={\n project?.cwdResolved === false\n ? t('project.action.openFolderTooltipMissing')\n : cwd\n }\n className=\"inline-flex items-center gap-2 rounded-[var(--radius-control)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-4 py-1.5 text-xs font-medium uppercase tracking-[0.14em] text-[var(--color-fg-secondary)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent-ink)] disabled:cursor-not-allowed disabled:opacity-40 dark:hover:text-[var(--color-accent)]\"\n >\n <FolderOpenIcon />\n {t('project.action.openFolder')}\n </button>\n <Link\n to={`/projects/${encodeURIComponent(id)}/memory`}\n className=\"inline-flex items-center gap-2 rounded-[var(--radius-control)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-4 py-1.5 text-xs font-medium uppercase tracking-[0.14em] text-[var(--color-fg-secondary)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent-ink)] dark:hover:text-[var(--color-accent)]\"\n >\n <BrainIcon />\n {memoryCount > 0\n ? t('memory.action.openCount', { n: memoryCount })\n : t('memory.action.open')}\n </Link>\n <button\n type=\"button\"\n onClick={() => setShowExport(true)}\n disabled={sessions.length === 0}\n title={selected.size > 0 ? `${selected.size}` : undefined}\n className=\"inline-flex items-center gap-2 rounded-[var(--radius-control)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-4 py-1.5 text-xs font-medium uppercase tracking-[0.14em] text-[var(--color-fg-secondary)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent-ink)] disabled:cursor-not-allowed disabled:opacity-40 dark:hover:text-[var(--color-accent)]\"\n >\n <ExportIcon />\n {selected.size > 0\n ? `${t('export.action')} · ${selected.size}`\n : t('export.action')}\n </button>\n <button\n type=\"button\"\n onClick={() => (selectMode ? exitSelectMode() : enterSelectMode())}\n disabled={sessions.length === 0 || bulkProgress !== null}\n aria-pressed={selectMode}\n className={\n 'inline-flex items-center gap-2 rounded-[var(--radius-control)] border px-4 py-1.5 text-xs font-medium uppercase tracking-[0.14em] transition disabled:cursor-not-allowed disabled:opacity-40 ' +\n (selectMode\n ? 'border-[var(--color-accent)] bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\n : 'border-[var(--color-hairline)] bg-[var(--color-surface)] text-[var(--color-fg-secondary)] hover:border-[var(--color-accent)] hover:text-[var(--color-accent-ink)] dark:hover:text-[var(--color-accent)]')\n }\n >\n <CheckSquareIcon />\n {selectMode ? t('project.action.exitSelect') : t('project.action.select')}\n </button>\n </>\n }\n meta={\n sessions.length > 0 ? (\n <>\n <MetaItem label={t('project.meta.sessions')} value={sessions.length} />\n <Sep />\n <MetaItem label={t('project.meta.onDisk')} value={formatBytes(projectBytes)} />\n {workingCount > 0 && (\n <>\n <Sep />\n <MetaItem\n label={t('project.meta.working')}\n value={\n <span className=\"text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\n {workingCount}\n </span>\n }\n />\n </>\n )}\n <Sep />\n <MetaItem\n label={t('project.meta.live')}\n value={\n liveCount > 0 ? (\n <span className=\"text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\n {liveCount}\n </span>\n ) : (\n 0\n )\n }\n />\n <Sep />\n <MetaItem label={t('project.meta.recent')} value={recentCount} />\n </>\n ) : null\n }\n />\n </div>\n\n {sessionsQuery.isLoading && <Loading label={t('common.readingSessions')} className=\"mt-10\" />}\n {sessionsQuery.error && (\n <p className=\"mt-10 rounded-md border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-4 py-3 text-sm text-[var(--color-danger)]\">\n {t('common.failedSessions')}: {(sessionsQuery.error as Error).message}\n </p>\n )}\n {sessionsQuery.data && sessionsQuery.data.length === 0 && (\n <p className=\"mt-10 text-sm text-[var(--color-fg-muted)]\">{t('common.noSessions')}</p>\n )}\n\n {sessions.length > 0 && (\n <div className=\"surface-card mt-6 p-6\">\n {(selectMode || bulkProgress || bulkOutcome) && (\n <BulkBar\n selectMode={selectMode}\n selectedCount={selected.size}\n totalCount={sessions.length}\n progress={bulkProgress}\n outcome={bulkOutcome}\n detailOpen={bulkDetailOpen}\n onToggleAll={toggleAll}\n onDelete={runBulkDelete}\n onCancel={exitSelectMode}\n onToggleDetail={() => setBulkDetailOpen((v) => !v)}\n onDismiss={() => {\n setBulkOutcome(null);\n setBulkDetailOpen(false);\n }}\n t={t}\n />\n )}\n <div className=\"flex items-baseline justify-between\">\n <h2 className=\"font-display text-xl font-light tracking-tight text-[var(--color-fg-primary)]\">\n {t('project.heading')}\n </h2>\n </div>\n <div className=\"rule-dotted mt-3\" aria-hidden />\n\n <div className=\"mt-4 -mx-6 overflow-x-auto px-6\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"text-left\">\n {selectMode && <th className=\"w-9 px-2 py-3\" />}\n <th className=\"px-2 py-3 eyebrow\">{t('project.col.title')}</th>\n <th className=\"px-2 py-3 eyebrow text-right\">{t('project.col.msgs')}</th>\n <th className=\"px-2 py-3 eyebrow text-right\">{t('project.col.errors')}</th>\n <th className=\"px-2 py-3 eyebrow text-right\">{t('project.col.last')}</th>\n <th className=\"px-2 py-3 eyebrow text-right\">{t('project.col.size')}</th>\n <th className=\"px-2 py-3 eyebrow\">{t('project.col.status')}</th>\n </tr>\n </thead>\n <motion.tbody\n initial=\"hidden\"\n animate=\"show\"\n variants={staggerParent}\n className=\"border-t border-[var(--color-hairline)]\"\n >\n {sessions.map((s) => {\n const isSel = selected.has(s.id);\n const displayTitle = s.customTitle ?? s.title;\n return (\n <motion.tr\n key={s.id}\n variants={fadeUpItem}\n data-active={isSel && selectMode ? 'true' : undefined}\n className={\n 'ribbon-row border-b border-[var(--color-hairline)] transition-colors ' +\n (isSel && selectMode\n ? 'bg-[var(--color-accent-soft)]/40'\n : 'hover:bg-[var(--color-sunken)]')\n }\n >\n {selectMode && (\n <td className=\"px-2 py-3 align-top\">\n <input\n type=\"checkbox\"\n aria-label={displayTitle}\n checked={isSel}\n onChange={() => toggle(s.id)}\n disabled={bulkProgress !== null}\n className=\"mt-0.5 h-3.5 w-3.5 cursor-pointer accent-[var(--color-accent)] disabled:cursor-not-allowed\"\n />\n </td>\n )}\n <td className=\"px-2 py-3 align-top\">\n <Link\n to={`/projects/${encodeURIComponent(id)}/sessions/${s.id}`}\n className=\"block max-w-md truncate font-medium text-[var(--color-fg-primary)] hover:text-[var(--color-accent-ink)] dark:hover:text-[var(--color-accent)]\"\n title={displayTitle}\n >\n {displayTitle}\n </Link>\n <div className=\"mt-1 truncate font-mono text-[10.5px] tracking-[0.04em] text-[var(--color-fg-faint)]\">\n {s.id}\n </div>\n </td>\n <td className=\"px-2 py-3 text-right align-top font-mono tabular-nums text-[var(--color-fg-secondary)]\">\n {s.messageCount.toLocaleString()}\n </td>\n <td className=\"px-2 py-3 text-right align-top font-mono tabular-nums\">\n {s.errorCount > 0 ? (\n <span className=\"text-[var(--color-danger)]\">{s.errorCount.toLocaleString()}</span>\n ) : (\n <span className=\"text-[var(--color-fg-faint)]\">0</span>\n )}\n </td>\n <td className=\"px-2 py-3 text-right align-top font-mono text-[12.5px] text-[var(--color-fg-secondary)]\">\n {formatRelativeTime(s.lastAt)}\n </td>\n <td\n className=\"px-2 py-3 text-right align-top font-mono tabular-nums text-[var(--color-fg-secondary)]\"\n title={breakdown(s)}\n >\n {formatBytes(totalBytes(s))}\n </td>\n <td className=\"px-2 py-3 align-top\">\n <StatusDot session={s} />\n </td>\n </motion.tr>\n );\n })}\n </motion.tbody>\n </table>\n </div>\n </div>\n )}\n\n {showExport && (\n <ExportDialog\n projectId={id}\n sessions={sessionsToExport}\n onClose={() => setShowExport(false)}\n />\n )}\n </section>\n );\n}\n\nfunction totalBytes(s: SessionSummary): number {\n const r = s.relatedBytes;\n return r.jsonl + r.subdir + r.fileHistory + r.sessionEnv;\n}\n\nfunction breakdown(s: SessionSummary): string {\n const r = s.relatedBytes;\n return [\n `jsonl ${formatBytes(r.jsonl)}`,\n `subdir ${formatBytes(r.subdir)}`,\n `file-history ${formatBytes(r.fileHistory)}`,\n `session-env ${formatBytes(r.sessionEnv)}`,\n ].join(' · ');\n}\n\nfunction TrashIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M3 6h18\" />\n <path d=\"M8 6V4.5A1.5 1.5 0 0 1 9.5 3h5A1.5 1.5 0 0 1 16 4.5V6\" />\n <path d=\"M5.5 6l1.1 13.2A1.5 1.5 0 0 0 8.1 20.5h7.8a1.5 1.5 0 0 0 1.5-1.3L18.5 6\" />\n </svg>\n );\n}\n\nfunction CheckSquareIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect x=\"3.5\" y=\"3.5\" width=\"17\" height=\"17\" rx=\"2.5\" />\n <path d=\"M8 12.2l3 3 5.5-6\" />\n </svg>\n );\n}\n\ninterface BulkBarProps {\n selectMode: boolean;\n selectedCount: number;\n totalCount: number;\n progress: BulkProgress | null;\n outcome: BulkOutcome | null;\n detailOpen: boolean;\n onToggleAll: () => void;\n onDelete: () => void;\n onCancel: () => void;\n onToggleDetail: () => void;\n onDismiss: () => void;\n t: (key: Parameters<ReturnType<typeof useT>>[0], params?: Record<string, string | number>) => string;\n}\n\nfunction BulkBar({\n selectMode,\n selectedCount,\n totalCount,\n progress,\n outcome,\n detailOpen,\n onToggleAll,\n onDelete,\n onCancel,\n onToggleDetail,\n onDismiss,\n t,\n}: BulkBarProps) {\n const allSelected = selectedCount === totalCount && totalCount > 0;\n const isBusy = progress !== null;\n return (\n // sticky 顶部:被外层 card 的 padding 抵消;用 -mx-6 让条目贴齐 card 边缘\n <div className=\"sticky top-2 z-30 -mx-6 mb-4 px-6\">\n {selectMode && (\n <div className=\"flex flex-wrap items-center justify-between gap-3 rounded-[var(--radius-input)] border border-[var(--color-hairline-strong)] bg-[var(--color-surface)] px-4 py-2.5 shadow-[var(--shadow-rise)]\">\n <div className=\"flex items-center gap-3\">\n <span className=\"font-mono text-[12px] font-medium uppercase tracking-[0.14em] text-[var(--color-fg-primary)]\">\n {isBusy\n ? t('project.bulk.deletePending', {\n i: progress!.index,\n total: progress!.total,\n })\n : t('project.bulk.selectedCount', { n: selectedCount })}\n </span>\n <button\n type=\"button\"\n onClick={onToggleAll}\n disabled={isBusy}\n className=\"font-mono text-[11px] uppercase tracking-[0.16em] text-[var(--color-fg-muted)] hover:text-[var(--color-fg-primary)] disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {allSelected ? t('common.deselectAll') : t('common.selectAll')}\n </button>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={isBusy}\n className=\"rounded-[var(--radius-control)] border border-[var(--color-hairline-strong)] px-3 py-1.5 text-xs font-medium uppercase tracking-[0.14em] text-[var(--color-fg-secondary)] hover:bg-[var(--color-sunken)] disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"button\"\n onClick={onDelete}\n disabled={isBusy || selectedCount === 0}\n className=\"inline-flex items-center gap-2 rounded-[var(--radius-control)] bg-[var(--color-danger)] px-3 py-1.5 text-xs font-medium uppercase tracking-[0.14em] text-white shadow-[var(--shadow-rise)] hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <TrashIcon />\n {t('project.bulk.deleteCta')}\n </button>\n </div>\n </div>\n )}\n {!selectMode && outcome && (\n <BulkOutcomeRibbon\n outcome={outcome}\n detailOpen={detailOpen}\n onToggleDetail={onToggleDetail}\n onDismiss={onDismiss}\n t={t}\n />\n )}\n </div>\n );\n}\n\ninterface BulkOutcomeRibbonProps {\n outcome: BulkOutcome;\n detailOpen: boolean;\n onToggleDetail: () => void;\n onDismiss: () => void;\n t: BulkBarProps['t'];\n}\n\nfunction BulkOutcomeRibbon({\n outcome,\n detailOpen,\n onToggleDetail,\n onDismiss,\n t,\n}: BulkOutcomeRibbonProps) {\n // 整体观感:成功为主时走 moss;有失败/跳过时走 accent\n const hasIssue = outcome.skipped.length + outcome.failed.length > 0;\n const tone = hasIssue\n ? 'border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)]'\n : 'border-[var(--color-moss)]/40 bg-[var(--color-moss-soft)]';\n const detailToggleable = hasIssue;\n return (\n <div className={`rounded-[var(--radius-input)] border ${tone} px-4 py-2.5`}>\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-[var(--color-fg-primary)]\">\n <span className=\"font-medium\">\n {t('project.bulk.resultOk', { ok: outcome.ok.length })}\n </span>\n {outcome.skipped.length > 0 && (\n <span className=\"font-mono text-[12px] text-[var(--color-fg-secondary)]\">\n · {t('project.bulk.resultSkipped', { n: outcome.skipped.length })}\n </span>\n )}\n {outcome.failed.length > 0 && (\n <span className=\"font-mono text-[12px] text-[var(--color-danger)]\">\n · {t('project.bulk.resultFailed', { n: outcome.failed.length })}\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {detailToggleable && (\n <button\n type=\"button\"\n onClick={onToggleDetail}\n className=\"rounded-[var(--radius-control)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-3 py-1 text-[11px] font-medium uppercase tracking-[0.14em] text-[var(--color-fg-secondary)] hover:text-[var(--color-fg-primary)]\"\n >\n {detailOpen ? t('project.bulk.hideDetail') : t('project.bulk.viewDetail')}\n </button>\n )}\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"rounded-[var(--radius-control)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-3 py-1 text-[11px] font-medium uppercase tracking-[0.14em] text-[var(--color-fg-muted)] hover:text-[var(--color-fg-primary)]\"\n >\n {t('project.bulk.dismiss')}\n </button>\n </div>\n </div>\n {detailOpen && detailToggleable && (\n <div className=\"mt-3 space-y-2\">\n {outcome.skipped.length > 0 && (\n <div>\n <div className=\"eyebrow text-[var(--color-fg-muted)]\">\n {t('project.bulk.detailSkipped')}\n </div>\n <ul className=\"mt-1 space-y-0.5 font-mono text-[11px] text-[var(--color-fg-secondary)]\">\n {outcome.skipped.map((s) => (\n <li key={s.sessionId}>\n <span className=\"text-[var(--color-fg-primary)]\">{s.title}</span>\n <span className=\"ml-2 text-[var(--color-fg-faint)]\">{s.sessionId}</span>\n <span className=\"ml-2\">— {s.reason}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n {outcome.failed.length > 0 && (\n <div>\n <div className=\"eyebrow text-[var(--color-danger)]\">\n {t('project.bulk.detailFailed')}\n </div>\n <ul className=\"mt-1 space-y-0.5 font-mono text-[11px] text-[var(--color-fg-secondary)]\">\n {outcome.failed.map((s) => (\n <li key={s.sessionId}>\n <span className=\"text-[var(--color-fg-primary)]\">{s.title}</span>\n <span className=\"ml-2 text-[var(--color-fg-faint)]\">{s.sessionId}</span>\n <span className=\"ml-2 text-[var(--color-danger)]\">— {s.error}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction ExportIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 3v12\" />\n <path d=\"M8 7l4-4 4 4\" />\n <path d=\"M4 15v3.5A1.5 1.5 0 0 0 5.5 20h13a1.5 1.5 0 0 0 1.5-1.5V15\" />\n </svg>\n );\n}\n\nfunction FolderOpenIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M3 7.5A1.5 1.5 0 0 1 4.5 6h4.4a1.5 1.5 0 0 1 1.06.44l1.1 1.1a1.5 1.5 0 0 0 1.06.44H19.5A1.5 1.5 0 0 1 21 9.48\" />\n <path d=\"M3.2 9.5h17.6a1 1 0 0 1 .98 1.2l-1.5 7.5a1.5 1.5 0 0 1-1.47 1.2H5.2a1.5 1.5 0 0 1-1.47-1.2l-1.5-7.5A1 1 0 0 1 3.2 9.5z\" />\n </svg>\n );\n}\n\nfunction BrainIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M9 4.5a3 3 0 0 0-3 3v.4a3 3 0 0 0-1.5 5.2A3 3 0 0 0 6 18.5a3 3 0 0 0 6 0V4.5a3 3 0 0 0-3 0z\" />\n <path d=\"M15 4.5a3 3 0 0 1 3 3v.4a3 3 0 0 1 1.5 5.2A3 3 0 0 1 18 18.5a3 3 0 0 1-6 0\" />\n </svg>\n );\n}\n","import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { motion } from 'motion/react';\nimport { useEffect, useRef } from 'react';\nimport {\n api,\n type DeleteProjectResult,\n type ProjectSummary,\n type SessionSummary,\n} from '../lib/api.ts';\nimport { formatBytes } from '../lib/format.ts';\nimport { useT } from '../lib/i18n.ts';\nimport { queryKeys } from '../lib/query-keys.ts';\n\ninterface Props {\n project: ProjectSummary;\n onClose: () => void;\n}\n\nexport default function DeleteProjectDialog({ project, onClose }: Props) {\n const t = useT();\n const queryClient = useQueryClient();\n\n const sessionsQuery = useQuery({\n queryKey: queryKeys.projectSessions(project.id),\n queryFn: () =>\n api<SessionSummary[]>(`/api/projects/${encodeURIComponent(project.id)}/sessions`),\n });\n const sessions = sessionsQuery.data ?? [];\n const blockers = sessions.filter((s) => s.isLivePid || s.isRecentlyActive);\n const hasBlockers = blockers.length > 0;\n\n const mutation = useMutation({\n mutationFn: () =>\n api<DeleteProjectResult>(`/api/projects/${encodeURIComponent(project.id)}`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: queryKeys.projects() });\n queryClient.invalidateQueries({ queryKey: queryKeys.projectSessions(project.id) });\n queryClient.invalidateQueries({ queryKey: queryKeys.diskUsage() });\n },\n });\n\n const isPendingRef = useRef(mutation.isPending);\n isPendingRef.current = mutation.isPending;\n\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape' && !isPendingRef.current) onClose();\n }\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [onClose]);\n\n const result = mutation.data;\n const showResult = !!result;\n const totalDeletedBytes = result?.deleted.reduce((a, d) => a + d.freedBytes, 0) ?? 0;\n\n return (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.18 }}\n className=\"fixed inset-0 z-[60] flex items-start justify-center bg-[oklch(0.16_0.006_85_/_0.55)] backdrop-blur-[2px] px-4 py-12\"\n onClick={() => !mutation.isPending && onClose()}\n >\n <motion.div\n initial={{ y: 8, opacity: 0 }}\n animate={{ y: 0, opacity: 1 }}\n transition={{ duration: 0.24, ease: [0.16, 1, 0.3, 1] }}\n className=\"w-full max-w-2xl overflow-hidden rounded-[var(--radius-panel)] border border-[var(--color-hairline)] bg-[var(--color-surface)] shadow-[var(--shadow-pop)]\"\n onClick={(e) => e.stopPropagation()}\n >\n <header className=\"flex items-start justify-between gap-4 border-b border-[var(--color-hairline)] px-6 py-5\">\n <div className=\"min-w-0\">\n <p className=\"eyebrow text-[var(--color-danger)]\">\n {showResult ? t('deleteProject.eyebrow.result') : t('deleteProject.eyebrow.confirm')}\n </p>\n <h2 className=\"mt-1 font-display text-2xl font-light tracking-tight text-[var(--color-fg-primary)]\">\n {showResult ? t('deleteProject.title.result') : t('deleteProject.title.confirm')}\n </h2>\n <p className=\"mt-1.5 truncate font-mono text-[12px] text-[var(--color-fg-muted)]\" title={project.decodedCwd}>\n {project.decodedCwd}\n </p>\n {!showResult && (\n <p className=\"mt-2 text-sm text-[var(--color-fg-muted)]\">\n {t('deleteProject.summary', {\n n: project.sessionCount,\n free: formatBytes(project.totalBytes),\n })}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={mutation.isPending}\n aria-label={t('delete.close')}\n className=\"rounded-xl p-1.5 text-[var(--color-fg-muted)] hover:bg-[var(--color-sunken)] hover:text-[var(--color-fg-primary)] disabled:opacity-50\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\">\n <path d=\"M6 6l12 12M18 6L6 18\" />\n </svg>\n </button>\n </header>\n\n {!showResult && (\n <div className=\"space-y-3 px-6 py-4 text-sm\">\n <p className=\"rounded-md border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-3 py-2.5 text-[var(--color-danger)]\">\n {t('deleteProject.warning', { cwd: project.decodedCwd })}\n </p>\n {hasBlockers && (\n <div className=\"rounded-md border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-xs\">\n <div className=\"mb-1.5 font-medium text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\n {t('deleteProject.blocked.heading', { n: blockers.length })}\n </div>\n <ul className=\"space-y-0.5\">\n {blockers.map((s) => (\n <li key={s.id} className=\"font-mono text-[11px] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-secondary)]\">\n {s.id} — {s.isLivePid ? `live PID ${s.livePid ?? '?'}` : 'recent'}\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )}\n\n {showResult && result && (\n <div className=\"max-h-[50vh] space-y-3 overflow-auto px-6 py-4 text-sm\">\n {result.projectDirRemoved ? (\n <div className=\"rounded-md border border-[var(--color-moss)]/40 bg-[var(--color-moss-soft)] px-3 py-2.5 text-[var(--color-fg-primary)]\">\n {t('deleteProject.success', {\n n: result.deleted.length,\n free: formatBytes(totalDeletedBytes),\n lines: result.historyLinesRemoved,\n })}\n </div>\n ) : result.deleted.length > 0 ? (\n <div className=\"rounded-md border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\n {t('deleteProject.successKept', {\n n: result.deleted.length,\n free: formatBytes(totalDeletedBytes),\n lines: result.historyLinesRemoved,\n })}\n </div>\n ) : null}\n {result.skipped.length > 0 && (\n <div className=\"rounded-md border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-xs\">\n <div className=\"mb-1 font-medium text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\n {t('deleteProject.blocked.heading', { n: result.skipped.length })}\n </div>\n <ul className=\"space-y-0.5\">\n {result.skipped.map((s) => (\n <li key={`${s.sessionId}-${s.reason}`} className=\"font-mono text-[11px] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-secondary)]\">\n {s.sessionId || '(project)'} — {s.reason}\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )}\n\n {mutation.error && (\n <p className=\"mx-6 mb-3 rounded-md border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-3 py-2 text-sm text-[var(--color-danger)]\">\n {(mutation.error as Error).message}\n </p>\n )}\n\n <footer className=\"flex justify-end gap-2 border-t border-[var(--color-hairline)] px-6 py-4\">\n {!showResult ? (\n <>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={mutation.isPending}\n className=\"rounded-[var(--radius-control)] border border-[var(--color-hairline-strong)] px-4 py-1.5 text-sm text-[var(--color-fg-secondary)] hover:bg-[var(--color-sunken)] disabled:opacity-50\"\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() => mutation.mutate()}\n disabled={mutation.isPending || sessionsQuery.isLoading || hasBlockers}\n className=\"rounded-[var(--radius-control)] bg-[var(--color-danger)] px-4 py-1.5 text-sm font-medium text-white shadow-[var(--shadow-rise)] hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {mutation.isPending\n ? t('deleteProject.btn.confirmPending')\n : t('deleteProject.btn.confirm')}\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-[var(--radius-control)] bg-[var(--color-fg-primary)] px-4 py-1.5 text-sm font-medium text-[var(--color-canvas)] hover:opacity-90\"\n >\n {t('common.done')}\n </button>\n )}\n </footer>\n </motion.div>\n </motion.div>\n );\n}\n","import { useQuery } from '@tanstack/react-query';\nimport { motion } from 'motion/react';\nimport { useState } from 'react';\nimport { Link } from 'react-router-dom';\nimport DeleteProjectDialog from '../components/DeleteProjectDialog.tsx';\nimport { Loading } from '../components/Loading.tsx';\nimport { MetaItem, Sep } from '../components/PageHeader.tsx';\nimport { api, type HealthResponse, type ProjectSummary } from '../lib/api.ts';\nimport { formatBytes, formatRelativeTime } from '../lib/format.ts';\nimport { useT } from '../lib/i18n.ts';\nimport { fadeUpItem, staggerParent } from '../lib/motion.ts';\nimport { queryKeys } from '../lib/query-keys.ts';\n\nexport default function ProjectsList() {\n const t = useT();\n const [pendingDelete, setPendingDelete] = useState<ProjectSummary | null>(null);\n const health = useQuery({\n queryKey: queryKeys.health(),\n queryFn: () => api<HealthResponse>('/api/health'),\n });\n\n const projects = useQuery({\n queryKey: queryKeys.projects(),\n queryFn: () => api<ProjectSummary[]>('/api/projects'),\n });\n\n const list = projects.data ?? [];\n const totalBytes = list.reduce((acc, p) => acc + p.totalBytes, 0);\n const totalSessions = list.reduce((acc, p) => acc + p.sessionCount, 0);\n const lastActive = list\n .map((p) => p.lastActiveAt)\n .filter((x): x is string => !!x)\n .sort()\n .at(-1) ?? null;\n\n return (\n <section>\n <div className=\"surface-card p-6\">\n <Masthead\n title={t('projects.title')}\n tagline={t('projects.tagline')}\n stats={\n list.length > 0\n ? { totalBytes, totalSessions, projectCount: list.length, lastActive }\n : null\n }\n />\n </div>\n\n {health.data && !health.data.claudeRootExists && (\n <Admonition tone=\"warn\" className=\"mt-6\">\n {t('projects.warn.rootMissing', { root: health.data.claudeRoot })}\n </Admonition>\n )}\n\n {projects.isLoading && <Loading label={t('common.scanning')} className=\"mt-10\" />}\n {projects.error && (\n <Admonition tone=\"danger\" className=\"mt-6\">\n {t('common.failedProjects')}: {(projects.error as Error).message}\n </Admonition>\n )}\n {projects.data && projects.data.length === 0 && (\n <p className=\"mt-10 text-sm text-[var(--color-fg-muted)]\">{t('common.noProjects')}</p>\n )}\n\n {list.length > 0 && (\n <div className=\"surface-card mt-6 p-6\">\n <div className=\"flex items-baseline justify-between\">\n <h2 className=\"font-display text-xl font-light tracking-tight text-[var(--color-fg-primary)]\">\n {t('projects.indexHeading')}\n </h2>\n <span className=\"font-mono text-[11px] uppercase tracking-[0.16em] tabular-nums text-[var(--color-fg-muted)]\">\n {String(list.length).padStart(2, '0')}{' '}\n {list.length === 1 ? t('common.entry') : t('common.entries')}\n </span>\n </div>\n <div className=\"rule-dotted mt-3\" aria-hidden />\n <Ledger projects={list} onRequestDelete={(p) => setPendingDelete(p)} />\n </div>\n )}\n\n {pendingDelete && (\n <DeleteProjectDialog\n project={pendingDelete}\n onClose={() => setPendingDelete(null)}\n />\n )}\n </section>\n );\n}\n\n/* ─────────────────────────────────────────────────────────────────── */\n\nfunction Masthead({\n title,\n tagline,\n stats,\n}: {\n title: string;\n tagline: string;\n stats: {\n totalBytes: number;\n totalSessions: number;\n projectCount: number;\n lastActive: string | null;\n } | null;\n}) {\n const t = useT();\n return (\n <header className=\"relative\">\n <div className=\"flex flex-wrap items-baseline gap-x-4 gap-y-1\">\n <h1 className=\"font-display text-[clamp(1.75rem,3.5vw,2.25rem)] font-light leading-[1.1] tracking-[-0.02em] text-[var(--color-fg-primary)]\">\n {title}\n <span className=\"text-[var(--color-accent)]\">.</span>\n </h1>\n <p className=\"min-w-0 flex-1 font-display text-[13px] italic leading-snug text-[var(--color-fg-muted)]\">\n {tagline}\n </p>\n </div>\n {stats && (\n <div className=\"mt-3 flex flex-wrap items-baseline gap-x-3 gap-y-1 text-xs\">\n <MetaItem label={t('projects.stat.onDisk')} value={formatBytes(stats.totalBytes)} />\n <Sep />\n <MetaItem label={t('projects.stat.projects')} value={stats.projectCount} />\n <Sep />\n <MetaItem label={t('projects.stat.sessions')} value={stats.totalSessions.toLocaleString()} />\n <Sep />\n <MetaItem label={t('projects.card.lastSeen')} value={formatRelativeTime(stats.lastActive)} />\n </div>\n )}\n </header>\n );\n}\n\n/* ─────────────────────────────────────────────────────────────────── */\n\nfunction Ledger({\n projects,\n onRequestDelete,\n}: {\n projects: ProjectSummary[];\n onRequestDelete: (p: ProjectSummary) => void;\n}) {\n const t = useT();\n return (\n <motion.ol\n initial=\"hidden\"\n animate=\"show\"\n variants={staggerParent}\n className=\"mt-4\"\n >\n <li\n aria-hidden\n className=\"grid grid-cols-[2.5rem_minmax(0,1fr)_5rem_5.5rem_5rem_3.5rem] items-center gap-x-4 border-b border-[var(--color-hairline)] py-3 sm:grid-cols-[3rem_minmax(0,1fr)_6rem_6rem_6rem_4rem]\"\n >\n <span className=\"eyebrow text-right\">№</span>\n <span className=\"eyebrow\">{t('projects.card.eyebrow')}</span>\n <span className=\"eyebrow text-right\">{t('projects.card.sessions')}</span>\n <span className=\"eyebrow text-right\">{t('projects.card.onDisk')}</span>\n <span className=\"eyebrow text-right\">{t('projects.card.lastSeen')}</span>\n <span className=\"eyebrow\" />\n </li>\n\n {projects.map((p, i) => (\n <motion.li key={p.id} variants={fadeUpItem}>\n <LedgerRow project={p} index={i} onRequestDelete={onRequestDelete} />\n </motion.li>\n ))}\n </motion.ol>\n );\n}\n\nfunction LedgerRow({\n project,\n index,\n onRequestDelete,\n}: {\n project: ProjectSummary;\n index: number;\n onRequestDelete: (p: ProjectSummary) => void;\n}) {\n const t = useT();\n const cwd = project.decodedCwd;\n const parts = cwd.split(/[\\\\/]+/).filter(Boolean);\n const tail = parts.slice(-2).join('/');\n const head = parts.slice(0, -2).join('/');\n\n return (\n <div className=\"ribbon-row group relative grid grid-cols-[2.5rem_minmax(0,1fr)_5rem_5.5rem_5rem_3.5rem] items-center gap-x-4 border-b border-[var(--color-hairline)] py-3 pl-3 transition-colors hover:bg-[var(--color-sunken)] sm:grid-cols-[3rem_minmax(0,1fr)_6rem_6rem_6rem_4rem]\">\n <Link\n to={`/projects/${encodeURIComponent(project.id)}`}\n aria-label={cwd}\n className=\"absolute inset-0 z-[1] rounded-[inherit] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[var(--color-accent)]\"\n >\n <span className=\"sr-only\">{cwd}</span>\n </Link>\n\n <span className=\"pointer-events-none text-right font-mono text-[11px] uppercase tracking-[0.16em] tabular-nums text-[var(--color-fg-faint)] group-hover:text-[var(--color-accent)]\">\n {String(index + 1).padStart(2, '0')}\n </span>\n\n <div className=\"pointer-events-none min-w-0\">\n <div\n className=\"flex items-baseline gap-2 truncate font-mono text-[13px] text-[var(--color-fg-primary)]\"\n title={cwd}\n >\n {head && <span className=\"text-[var(--color-fg-faint)]\">{head}/</span>}\n <span className=\"font-medium\">{tail}</span>\n {!project.cwdResolved && (\n <span className=\"ml-1 shrink-0 rounded-full border border-[var(--color-hairline-strong)] px-1.5 py-px font-mono text-[9px] uppercase tracking-[0.16em] text-[var(--color-fg-muted)]\">\n {t('common.missing')}\n </span>\n )}\n </div>\n </div>\n\n <span className=\"pointer-events-none text-right font-mono text-sm tabular-nums text-[var(--color-fg-primary)]\">\n {project.sessionCount.toLocaleString()}\n </span>\n <span className=\"pointer-events-none text-right font-mono text-sm tabular-nums text-[var(--color-fg-secondary)]\">\n {formatBytes(project.totalBytes)}\n </span>\n <span className=\"pointer-events-none text-right font-mono text-[12.5px] tabular-nums text-[var(--color-fg-secondary)]\">\n {formatRelativeTime(project.lastActiveAt)}\n </span>\n\n <div className=\"relative z-[2] flex items-center justify-end gap-1 pr-1\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onRequestDelete(project);\n }}\n aria-label={t('deleteProject.row.action')}\n title={t('deleteProject.row.action')}\n className=\"rounded-md border border-transparent p-1.5 text-[var(--color-fg-faint)] transition-colors hover:border-[var(--color-danger)]/40 hover:bg-[var(--color-danger-soft)] hover:text-[var(--color-danger)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-danger)]\"\n >\n <TrashIcon />\n </button>\n <span\n aria-hidden\n className=\"pointer-events-none text-[var(--color-fg-faint)] transition-transform duration-200 group-hover:translate-x-0.5 group-hover:text-[var(--color-accent)]\"\n >\n <ChevronRight />\n </span>\n </div>\n </div>\n );\n}\n\nfunction TrashIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M3 6h18\" />\n <path d=\"M8 6V4.5A1.5 1.5 0 0 1 9.5 3h5A1.5 1.5 0 0 1 16 4.5V6\" />\n <path d=\"M5.5 6l1.1 13.2A1.5 1.5 0 0 0 8.1 20.5h7.8a1.5 1.5 0 0 0 1.5-1.3L18.5 6\" />\n </svg>\n );\n}\n\nfunction ChevronRight() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M9 6l6 6-6 6\" />\n </svg>\n );\n}\n\n/* ─────────────────────────────────────────────────────────────────── */\n\nfunction Admonition({\n tone,\n className = '',\n children,\n}: {\n tone: 'warn' | 'danger';\n className?: string;\n children: React.ReactNode;\n}) {\n const colors =\n tone === 'warn'\n ? 'border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\n : 'border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] text-[var(--color-danger)]';\n return (\n <div className={`rounded-[10px] border px-4 py-3 text-sm ${colors} ${className}`}>\n {children}\n </div>\n );\n}\n","import { useMutation, useQueryClient } from '@tanstack/react-query';\nimport { motion } from 'motion/react';\nimport { useEffect, useRef } from 'react';\nimport {\n api,\n type DeleteRequestItem,\n type DeleteResult,\n type SessionSummary,\n} from '../lib/api.ts';\nimport { RECENT_ACTIVITY_WINDOW_MIN } from '../lib/constants.ts';\nimport { formatBytes } from '../lib/format.ts';\nimport { translate, useT, type Locale } from '../lib/i18n.ts';\nimport { useLocale } from '../lib/i18n.ts';\nimport { queryKeys } from '../lib/query-keys.ts';\n\ninterface Props {\n projectId: string;\n selected: SessionSummary[];\n onClose: () => void;\n onDeleted?: (deletedSessionIds: string[]) => void;\n}\n\nexport default function DeleteDialog({ projectId, selected, onClose, onDeleted }: Props) {\n const t = useT();\n const { locale } = useLocale();\n const queryClient = useQueryClient();\n\n const willSkip = selected.filter((s) => s.isLivePid || s.isRecentlyActive);\n const willDelete = selected.filter((s) => !s.isLivePid && !s.isRecentlyActive);\n const totalFree = willDelete.reduce(\n (acc, s) =>\n acc +\n s.relatedBytes.jsonl +\n s.relatedBytes.subdir +\n s.relatedBytes.fileHistory +\n s.relatedBytes.sessionEnv,\n 0,\n );\n\n const mutation = useMutation({\n mutationFn: (items: DeleteRequestItem[]) =>\n api<DeleteResult>('/api/sessions', {\n method: 'DELETE',\n body: JSON.stringify({ items }),\n }),\n onSuccess: (data) => {\n // Drop the deleted ids from the cached session list synchronously so the\n // list under the dialog (or after navigate-back from SessionDetail) doesn't\n // flash the just-deleted rows while the background refetch is in flight.\n if (data.deleted.length > 0) {\n const removed = new Set(data.deleted.map((d) => d.sessionId));\n queryClient.setQueryData<SessionSummary[]>(\n queryKeys.projectSessions(projectId),\n (prev) => prev?.filter((s) => !removed.has(s.id)),\n );\n }\n queryClient.invalidateQueries({ queryKey: queryKeys.projects() });\n queryClient.invalidateQueries({ queryKey: queryKeys.projectSessions(projectId) });\n queryClient.invalidateQueries({ queryKey: queryKeys.diskUsage() });\n if (data.deleted.length > 0) {\n onDeleted?.(data.deleted.map((d) => d.sessionId));\n }\n },\n });\n\n const isPendingRef = useRef(mutation.isPending);\n isPendingRef.current = mutation.isPending;\n\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape' && !isPendingRef.current) onClose();\n }\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [onClose]);\n\n const result = mutation.data;\n const showResult = !!result;\n\n return (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.18 }}\n className=\"fixed inset-0 z-[60] flex items-start justify-center bg-[oklch(0.16_0.006_85_/_0.55)] backdrop-blur-[2px] px-4 py-12\"\n onClick={() => !mutation.isPending && onClose()}\n >\n <motion.div\n initial={{ y: 8, opacity: 0 }}\n animate={{ y: 0, opacity: 1 }}\n transition={{ duration: 0.24, ease: [0.16, 1, 0.3, 1] }}\n className=\"w-full max-w-2xl overflow-hidden rounded-[var(--radius-panel)] border border-[var(--color-hairline)] bg-[var(--color-surface)] shadow-[var(--shadow-pop)]\"\n onClick={(e) => e.stopPropagation()}\n >\n <header className=\"flex items-start justify-between gap-4 border-b border-[var(--color-hairline)] px-6 py-5\">\n <div>\n <p className=\"eyebrow text-[var(--color-danger)]\">\n {showResult ? t('delete.eyebrow.result') : t('delete.eyebrow.confirm')}\n </p>\n <h2 className=\"mt-1 font-display text-2xl font-light tracking-tight text-[var(--color-fg-primary)]\">\n {showResult ? t('delete.title.result') : t('delete.title.confirm')}\n </h2>\n {!showResult && (\n <p className=\"mt-1.5 text-sm text-[var(--color-fg-muted)]\">\n {t('delete.summary', {\n n: willDelete.length,\n skipped: willSkip.length,\n free: formatBytes(totalFree),\n })}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={mutation.isPending}\n aria-label={t('delete.close')}\n className=\"rounded-xl p-1.5 text-[var(--color-fg-muted)] hover:bg-[var(--color-sunken)] hover:text-[var(--color-fg-primary)] disabled:opacity-50\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\">\n <path d=\"M6 6l12 12M18 6L6 18\" />\n </svg>\n </button>\n </header>\n\n {!showResult && (\n <div className=\"max-h-[50vh] space-y-2 overflow-auto px-6 py-4\">\n {willDelete.map((s) => (\n <div\n key={s.id}\n className=\"rounded-md border border-[var(--color-hairline)] bg-[var(--color-canvas)] px-3 py-2 text-xs\"\n >\n <div className=\"font-medium text-[var(--color-fg-primary)]\">{s.title}</div>\n <div className=\"mt-0.5 truncate font-mono text-[10.5px] text-[var(--color-fg-faint)]\">{s.id}</div>\n <div className=\"mt-1.5 grid grid-cols-2 gap-x-3 gap-y-0.5 font-mono text-[11px] text-[var(--color-fg-muted)] sm:grid-cols-4\">\n <span>jsonl <span className=\"text-[var(--color-fg-secondary)]\">{formatBytes(s.relatedBytes.jsonl)}</span></span>\n <span>subdir <span className=\"text-[var(--color-fg-secondary)]\">{formatBytes(s.relatedBytes.subdir)}</span></span>\n <span>file-history <span className=\"text-[var(--color-fg-secondary)]\">{formatBytes(s.relatedBytes.fileHistory)}</span></span>\n <span>session-env <span className=\"text-[var(--color-fg-secondary)]\">{formatBytes(s.relatedBytes.sessionEnv)}</span></span>\n </div>\n </div>\n ))}\n {willSkip.length > 0 && (\n <div className=\"rounded-md border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-xs\">\n <div className=\"mb-1.5 font-medium text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\n {t('delete.skipped.heading', { n: willSkip.length })}\n </div>\n <ul className=\"space-y-0.5\">\n {willSkip.map((s) => (\n <li key={s.id} className=\"font-mono text-[11px] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-secondary)]\">\n {s.id} — {skipReason(s, locale)}\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )}\n\n {showResult && result && (\n <div className=\"max-h-[50vh] space-y-3 overflow-auto px-6 py-4 text-sm\">\n <div className=\"rounded-md border border-[var(--color-moss)]/40 bg-[var(--color-moss-soft)] px-3 py-2.5 text-[var(--color-fg-primary)]\">\n {t('delete.success', {\n n: result.deleted.length,\n free: formatBytes(result.deleted.reduce((a, d) => a + d.freedBytes, 0)),\n lines: result.historyLinesRemoved,\n })}\n </div>\n {result.skipped.length > 0 && (\n <div className=\"rounded-md border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-xs\">\n <div className=\"mb-1 font-medium text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\n {t('delete.skipped.heading', { n: result.skipped.length })}\n </div>\n <ul className=\"space-y-0.5\">\n {result.skipped.map((s) => (\n <li key={s.sessionId} className=\"font-mono text-[11px] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-secondary)]\">\n {s.sessionId} — {s.reason}\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )}\n\n {mutation.error && (\n <p className=\"mx-6 mb-3 rounded-md border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-3 py-2 text-sm text-[var(--color-danger)]\">\n {(mutation.error as Error).message}\n </p>\n )}\n\n <footer className=\"flex justify-end gap-2 border-t border-[var(--color-hairline)] px-6 py-4\">\n {!showResult ? (\n <>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={mutation.isPending}\n className=\"rounded-[var(--radius-control)] border border-[var(--color-hairline-strong)] px-4 py-1.5 text-sm text-[var(--color-fg-secondary)] hover:bg-[var(--color-sunken)] disabled:opacity-50\"\n >\n {t('common.cancel')}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n mutation.mutate(\n selected.map((s) => ({ projectId, sessionId: s.id })),\n )\n }\n disabled={mutation.isPending || willDelete.length === 0}\n className=\"rounded-[var(--radius-control)] bg-[var(--color-danger)] px-4 py-1.5 text-sm font-medium text-white shadow-[var(--shadow-rise)] hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {mutation.isPending\n ? t('delete.btn.confirmPending')\n : t('delete.btn.confirm', {\n n: willDelete.length,\n label:\n willDelete.length === 1\n ? t('delete.label.session')\n : t('delete.label.sessions'),\n })}\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-[var(--radius-control)] bg-[var(--color-fg-primary)] px-4 py-1.5 text-sm font-medium text-[var(--color-canvas)] hover:opacity-90\"\n >\n {t('common.done')}\n </button>\n )}\n </footer>\n </motion.div>\n </motion.div>\n );\n}\n\nfunction skipReason(s: SessionSummary, locale: Locale): string {\n if (s.isLivePid) return translate(locale, 'delete.skipped.reasonLive', { pid: s.livePid ?? '?' });\n if (s.isRecentlyActive)\n return translate(locale, 'delete.skipped.reasonRecent', { n: RECENT_ACTIVITY_WINDOW_MIN });\n return translate(locale, 'delete.skipped.reasonUnknown');\n}\n","export interface Segment {\n text: string;\n match: boolean;\n}\n\nexport function highlight(text: string, query: string): Segment[] {\n if (!query) return [{ text, match: false }];\n const lowerText = text.toLowerCase();\n const lowerQuery = query.toLowerCase();\n const segments: Segment[] = [];\n let cursor = 0;\n\n while (cursor < text.length) {\n const idx = lowerText.indexOf(lowerQuery, cursor);\n if (idx === -1) {\n segments.push({ text: text.slice(cursor), match: false });\n break;\n }\n if (idx > cursor) {\n segments.push({ text: text.slice(cursor, idx), match: false });\n }\n segments.push({ text: text.slice(idx, idx + query.length), match: true });\n cursor = idx + query.length;\n }\n\n return segments;\n}\n\nexport function messageMatchesQuery(text: string, query: string): boolean {\n if (!query) return true;\n return text.toLowerCase().includes(query.toLowerCase());\n}\n","import { highlight } from '../lib/highlight.ts';\n\nexport default function HighlightedText({\n text,\n query,\n className,\n}: {\n text: string;\n query: string;\n className?: string;\n}) {\n const segments = highlight(text, query);\n return (\n <span className={className}>\n {segments.map((seg, i) =>\n seg.match ? (\n <mark\n key={i}\n className=\"rounded-sm bg-[var(--color-accent-soft)] px-0.5 text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\"\n >\n {seg.text}\n </mark>\n ) : (\n <span key={i}>{seg.text}</span>\n ),\n )}\n </span>\n );\n}\n","import type { DiffHunk } from './api.ts';\n\n// 行级 + 字内 diff 的纯函数集。「修改的文件」弹窗(split 视图)与消息流的\n// ToolBlock(Edit 展开体)共用同一套算法,保证两处的 −/+ 着色与字内高亮一致。\n\n/** 行内 word-level 片段:changed=该 token 仅存在于本侧(GitHub 行内高亮)。 */\nexport interface Seg {\n text: string;\n changed: boolean;\n}\n\n/** 统一视图的一行:context=两侧都在的未改动行;del/add=删除/新增行;\n * gap=被折叠省略的未改动区间(只显示省略了多少行,不渲染正文)。 */\nexport type RowKind = 'context' | 'del' | 'add' | 'gap';\nexport interface UnifiedRow {\n oldNo: number | null;\n newNo: number | null;\n kind: RowKind;\n text: string | null;\n /** 仅 gap 行有值:被折叠省略的行数。 */\n gap?: number;\n /** 仅在「删除 ↔ 新增」配对行上有值,用于行内高亮;其余为 null(整行着色)。 */\n segs: Seg[] | null;\n}\n\n/** structuredPatch(带真实行号的 hunk)→ 统一视图行。hunk 之间的未改动区折叠成一行 gap。 */\nexport function rowsFromHunks(hunks: DiffHunk[]): UnifiedRow[] {\n const rows: UnifiedRow[] = [];\n let prevNewEnd = 0; // 上一 hunk 结束时的新文件行号(0=文件开头)\n for (const h of hunks) {\n const gap = h.newStart - prevNewEnd - 1;\n if (gap > 0) rows.push({ oldNo: null, newNo: null, kind: 'gap', text: null, gap, segs: null });\n let oldNo = h.oldStart;\n let newNo = h.newStart;\n const lines = h.lines;\n let i = 0;\n while (i < lines.length) {\n const c = lines[i]![0];\n if (c === '-' || c === '+') {\n // 收集相邻的一段删除 + 新增,按序号配对做行内高亮(GitHub 习惯)。\n const dels: string[] = [];\n const adds: string[] = [];\n const delStart = oldNo;\n const addStart = newNo;\n while (i < lines.length && lines[i]![0] === '-') {\n dels.push(lines[i]!.slice(1));\n oldNo++;\n i++;\n }\n while (i < lines.length && lines[i]![0] === '+') {\n adds.push(lines[i]!.slice(1));\n newNo++;\n i++;\n }\n for (let x = 0; x < dels.length; x++) {\n const paired = x < adds.length ? wordSegments(dels[x]!, adds[x]!) : null;\n rows.push({ oldNo: delStart + x, newNo: null, kind: 'del', text: dels[x]!, segs: paired?.left ?? null });\n }\n for (let x = 0; x < adds.length; x++) {\n const paired = x < dels.length ? wordSegments(dels[x]!, adds[x]!) : null;\n rows.push({ oldNo: null, newNo: addStart + x, kind: 'add', text: adds[x]!, segs: paired?.right ?? null });\n }\n } else {\n // 上下文行(前导空格);异常前缀(如 \"\\")并入上下文不影响对齐。\n rows.push({ oldNo, newNo, kind: 'context', text: lines[i]!.slice(1), segs: null });\n oldNo++;\n newNo++;\n i++;\n }\n }\n prevNewEnd = newNo - 1;\n }\n return rows;\n}\n\n/** old/new 原文 → 统一视图行(行号从 1 起、无真实文件行号,用于 create 全新内容\n * 或结果尚未落地的兜底;一旦 structuredPatch 到位即被真实行号版本取代)。 */\nexport function rowsFromStrings(oldStr: string, newStr: string): UnifiedRow[] {\n // 空串视为 0 行(而非 ['']),避免纯新增 / 纯删除时出现一个幻影空行。\n const ops = diffOps(oldStr === '' ? [] : oldStr.split('\\n'), newStr === '' ? [] : newStr.split('\\n'));\n const rows: UnifiedRow[] = [];\n let oldNo = 0;\n let newNo = 0;\n let i = 0;\n while (i < ops.length) {\n const op = ops[i]!;\n if (op.type === 'equal') {\n oldNo++;\n newNo++;\n rows.push({ oldNo, newNo, kind: 'context', text: op.text, segs: null });\n i++;\n continue;\n }\n const dels: string[] = [];\n const adds: string[] = [];\n while (i < ops.length && ops[i]!.type !== 'equal') {\n const cur = ops[i]!;\n if (cur.type === 'del') dels.push(cur.text);\n else adds.push(cur.text);\n i++;\n }\n for (let x = 0; x < dels.length; x++) {\n const paired = x < adds.length ? wordSegments(dels[x]!, adds[x]!) : null;\n oldNo++;\n rows.push({ oldNo, newNo: null, kind: 'del', text: dels[x]!, segs: paired?.left ?? null });\n }\n for (let x = 0; x < adds.length; x++) {\n const paired = x < dels.length ? wordSegments(dels[x]!, adds[x]!) : null;\n newNo++;\n rows.push({ oldNo: null, newNo, kind: 'add', text: adds[x]!, segs: paired?.right ?? null });\n }\n }\n return rows;\n}\n\n// token 粒度:连续空白 / 连续单词字符 / 连续标点各成一段,贴近 GitHub 的行内分词。\nconst WORD_RE = /\\s+|\\w+|[^\\w\\s]+/g;\n\n/** 一行内的 word-level diff:复用 diffOps(行级 LCS)在 token 上再跑一次。\n * 返回左右两侧的 token 段(changed=仅本侧独有)。两行毫无公共 token、空行或过长行时返回 null(退回整行着色)。 */\nfunction wordSegments(oldLine: string, newLine: string): { left: Seg[]; right: Seg[] } | null {\n const a = oldLine.match(WORD_RE) ?? [];\n const b = newLine.match(WORD_RE) ?? [];\n if (a.length === 0 || b.length === 0) return null;\n if (a.length * b.length > 20000) return null; // O(n·m) 兜底,避免超长/压缩行卡顿\n const ops = diffOps(a, b);\n const left: Seg[] = [];\n const right: Seg[] = [];\n for (const op of ops) {\n if (op.type === 'equal') {\n pushSeg(left, op.text, false);\n pushSeg(right, op.text, false);\n } else if (op.type === 'del') {\n pushSeg(left, op.text, true);\n } else {\n pushSeg(right, op.text, true);\n }\n }\n // 两侧全是 changed → 没有公共 token,高亮等于整行,没意义,退回整行着色。\n if (left.every((s) => s.changed) && right.every((s) => s.changed)) return null;\n return { left, right };\n}\n\nfunction pushSeg(arr: Seg[], text: string, changed: boolean): void {\n const last = arr[arr.length - 1];\n if (last && last.changed === changed) last.text += text;\n else arr.push({ text, changed });\n}\n\ninterface DiffOp {\n type: 'equal' | 'del' | 'add';\n text: string;\n}\n\n// 规模上限:n·m 超过此值跳过 LCS 对齐,整体「全删 + 全增」。DP 是 O(n·m) 时间\n// + 内存,~2000×2000 行的巨型 Edit 会分配 ~32MB 并阻塞主线程数百 ms;超限直接\n// 退化能保证 UI 不卡死(代价是这种罕见的超大改动失去行级对齐)。\nconst LCS_CELL_CAP = 1_000_000;\n\n// 经典 LCS 行级 diff:dp[i][j] = a[i:] 与 b[j:] 的最长公共子序列长度,\n// 回溯得到 equal / del / add 操作序列。编辑片段通常很短,O(n·m) 足够。\nfunction diffOps(a: string[], b: string[]): DiffOp[] {\n const n = a.length;\n const m = b.length;\n if (n * m > LCS_CELL_CAP) {\n return [\n ...a.map((text): DiffOp => ({ type: 'del', text })),\n ...b.map((text): DiffOp => ({ type: 'add', text })),\n ];\n }\n const dp: number[][] = [];\n for (let i = 0; i <= n; i++) dp.push(new Array<number>(m + 1).fill(0));\n for (let i = n - 1; i >= 0; i--) {\n const row = dp[i]!;\n const next = dp[i + 1]!;\n for (let j = m - 1; j >= 0; j--) {\n row[j] = a[i] === b[j] ? next[j + 1]! + 1 : Math.max(next[j]!, row[j + 1]!);\n }\n }\n const ops: DiffOp[] = [];\n let i = 0;\n let j = 0;\n while (i < n && j < m) {\n if (a[i] === b[j]) {\n ops.push({ type: 'equal', text: a[i]! });\n i++;\n j++;\n } else if (dp[i + 1]![j]! >= dp[i]![j + 1]!) {\n ops.push({ type: 'del', text: a[i]! });\n i++;\n } else {\n ops.push({ type: 'add', text: b[j]! });\n j++;\n }\n }\n while (i < n) ops.push({ type: 'del', text: a[i++]! });\n while (j < m) ops.push({ type: 'add', text: b[j++]! });\n return ops;\n}\n","import { useMemo, useState, type ReactNode } from 'react';\nimport type { Block } from '../lib/api.ts';\nimport { rowsFromStrings, type UnifiedRow } from '../lib/diff.ts';\nimport { useT } from '../lib/i18n.ts';\nimport HighlightedText from './HighlightedText.tsx';\n\nconst PREVIEW_CHARS = 280;\n\n// Edit/Write 展开体最多渲染的 diff 行数——超出折叠成「还有 n 行」,避免一次\n// Write 几千行内容把消息流 DOM 撑爆。\nconst MAX_DIFF_ROWS = 160;\n\n/* ── tool_use ───────────────────────────────────────────────────────────── */\n\nexport function ToolUseBlock({\n block,\n query,\n}: {\n block: Extract<Block, { type: 'tool_use' }>;\n query: string;\n}) {\n const [open, setOpen] = useState(false);\n const input = asRecord(block.input);\n const summary = toolSummary(block.name, input);\n // 「读用富渲染,搜用原文高亮」:query 非空=搜索态,展开体退回 JSON 原文 +\n // HighlightedText(同 main),保证 haystack 命中的内容在 UI 上一定能看到高亮;\n // 否则走分工具的富展开体(diff / checklist / 命令块)。\n const searching = query.length > 0;\n return (\n <div className=\"overflow-hidden rounded-xl border border-[var(--color-hairline)] bg-[var(--color-sunken)] text-sm\">\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n className=\"flex w-full items-center gap-2 px-3 py-2 text-left transition hover:bg-[var(--color-canvas)]\"\n >\n <span className=\"flex shrink-0 items-center gap-2 font-mono text-[11.5px] font-medium uppercase tracking-[0.06em] text-[var(--color-fg-secondary)]\">\n <Glyph kind=\"tool\" /> {block.name}\n </span>\n {summary && (\n <span className=\"min-w-0 flex-1 truncate font-mono text-[11px] text-[var(--color-fg-muted)]\">\n <HighlightedText text={summary} query={query} />\n </span>\n )}\n <span className=\"ml-auto shrink-0\">\n <Caret open={open} />\n </span>\n </button>\n {open && (\n <div className=\"border-t border-[var(--color-hairline)] bg-[var(--color-surface)]\">\n {searching ? (\n <JsonDump input={input} query={query} />\n ) : (\n <ToolUseBody name={block.name} input={input} />\n )}\n </div>\n )}\n </div>\n );\n}\n\n/** 富展开体按工具特化:Edit/Write 走与「修改的文件」弹窗同语言的 −/+ diff 行,\n * TodoWrite 走 checklist,Bash 走命令块;其余保留原始 JSON。仅在非搜索态渲染,\n * 故不接 query(高亮由搜索态的 JsonDump 负责)。rows 用 useMemo 防 live 轮询重算。 */\nfunction ToolUseBody({ name, input }: { name: string; input: Record<string, unknown> }) {\n const t = useT();\n const body = useMemo(() => buildToolBody(name, input), [name, input]);\n switch (body.kind) {\n case 'diff':\n return (\n <>\n <FilePathLine path={body.filePath}>\n {body.replaceAll && (\n <span className=\"rounded-sm border border-[var(--color-hairline-strong)] px-1 py-px font-mono text-[9px] uppercase tracking-[0.1em] text-[var(--color-fg-muted)]\">\n {t('tool.replaceAll')}\n </span>\n )}\n </FilePathLine>\n <DiffRows rows={body.rows} />\n </>\n );\n case 'multidiff':\n return (\n <>\n <FilePathLine path={body.filePath} />\n {body.sections.map((rows, i) => (\n <div key={i}>\n {i > 0 && <div className=\"rule-dotted mx-3 my-1\" aria-hidden />}\n <DiffRows rows={rows} />\n </div>\n ))}\n </>\n );\n case 'bash':\n return <BashBody command={body.command} description={body.description} />;\n case 'todo':\n return <TodoList todos={body.todos} />;\n default:\n return <JsonDump input={input} />;\n }\n}\n\n/** 折叠 / 展开都要的 diff 结构在一处算好(useMemo 缓存),按工具分流。 */\ntype ToolBody =\n | { kind: 'diff'; filePath: string; rows: UnifiedRow[]; replaceAll: boolean }\n | { kind: 'multidiff'; filePath: string; sections: UnifiedRow[][] }\n | { kind: 'bash'; command: string; description: string }\n | { kind: 'todo'; todos: TodoItem[] }\n | { kind: 'json' };\n\nfunction buildToolBody(name: string, input: Record<string, unknown>): ToolBody {\n switch (name) {\n case 'Edit':\n return {\n kind: 'diff',\n filePath: strOf(input.file_path),\n rows: rowsFromStrings(strOf(input.old_string), strOf(input.new_string)),\n replaceAll: input.replace_all === true,\n };\n case 'Write':\n return {\n kind: 'diff',\n filePath: strOf(input.file_path),\n rows: rowsFromStrings('', strOf(input.content)),\n replaceAll: false,\n };\n case 'NotebookEdit':\n return {\n kind: 'diff',\n filePath: strOf(input.notebook_path),\n rows: rowsFromStrings('', strOf(input.new_source)),\n replaceAll: false,\n };\n case 'MultiEdit': {\n const edits = Array.isArray(input.edits) ? input.edits : [];\n return {\n kind: 'multidiff',\n filePath: strOf(input.file_path),\n sections: edits.map((e) => {\n const er = asRecord(e);\n return rowsFromStrings(strOf(er.old_string), strOf(er.new_string));\n }),\n };\n }\n case 'Bash':\n return { kind: 'bash', command: strOf(input.command), description: strOf(input.description) };\n case 'TodoWrite': {\n const todos = todosOf(input);\n return todos.length > 0 ? { kind: 'todo', todos } : { kind: 'json' };\n }\n default:\n return { kind: 'json' };\n }\n}\n\n/** 折叠头部的一行摘要:让消息流不展开也能看出这次调用动了什么。 */\nfunction toolSummary(name: string, input: Record<string, unknown>): string | null {\n switch (name) {\n case 'Bash':\n return firstLine(strOf(input.description) || strOf(input.command));\n case 'Edit':\n case 'Write':\n case 'MultiEdit':\n case 'Read':\n return pathTail(strOf(input.file_path));\n case 'NotebookEdit':\n return pathTail(strOf(input.notebook_path));\n case 'Glob':\n case 'Grep':\n return strOf(input.pattern) || null;\n case 'Task':\n case 'Agent':\n return strOf(input.description) || null;\n case 'WebFetch':\n return strOf(input.url) || null;\n case 'WebSearch':\n return strOf(input.query) || null;\n case 'Skill':\n return strOf(input.skill) || null;\n case 'TodoWrite': {\n const todos = todosOf(input);\n if (todos.length === 0) return null;\n const done = todos.filter((x) => x.status === 'completed').length;\n const active = todos.find((x) => x.status === 'in_progress');\n const label = active ? active.activeForm || active.content : '';\n return `${done}/${todos.length}${label ? ` · ${label}` : ''}`;\n }\n default: {\n // 未知工具:取第一个非空字符串字段当摘要,聊胜于无。\n for (const v of Object.values(input)) {\n if (typeof v === 'string' && v.trim()) return firstLine(v);\n }\n return null;\n }\n }\n}\n\ninterface TodoItem {\n content: string;\n status: string;\n activeForm: string;\n}\n\nfunction todosOf(input: Record<string, unknown>): TodoItem[] {\n if (!Array.isArray(input.todos)) return [];\n return input.todos.map((x) => {\n const r = asRecord(x);\n return { content: strOf(r.content), status: strOf(r.status), activeForm: strOf(r.activeForm) };\n });\n}\n\nfunction TodoList({ todos }: { todos: TodoItem[] }) {\n return (\n <ul className=\"space-y-1 px-3 py-2\">\n {todos.map((todo, i) => {\n const isDone = todo.status === 'completed';\n const isActive = todo.status === 'in_progress';\n const text = isActive && todo.activeForm ? todo.activeForm : todo.content;\n return (\n <li key={i} className=\"flex items-start gap-2 text-[13px] leading-snug\">\n <span\n aria-hidden\n className={\n 'mt-px w-3.5 shrink-0 text-center font-mono text-[12px] ' +\n (isDone\n ? 'text-[var(--color-moss)]'\n : isActive\n ? 'text-[var(--color-accent)]'\n : 'text-[var(--color-fg-faint)]')\n }\n >\n {isDone ? '✓' : isActive ? '●' : '○'}\n </span>\n <span\n className={\n isDone\n ? 'text-[var(--color-fg-muted)] line-through decoration-[var(--color-hairline-strong)]'\n : isActive\n ? 'font-medium text-[var(--color-fg-primary)]'\n : 'text-[var(--color-fg-secondary)]'\n }\n >\n {text}\n </span>\n </li>\n );\n })}\n </ul>\n );\n}\n\nfunction BashBody({ command, description }: { command: string; description: string }) {\n return (\n <div className=\"px-3 py-2\">\n {description && (\n <p className=\"mb-1.5 text-[12.5px] text-[var(--color-fg-muted)]\">{description}</p>\n )}\n <div className=\"group/cmd relative rounded-[var(--radius-control)] border border-[var(--color-hairline)] bg-[var(--color-sunken)]\">\n <span className=\"absolute right-1 top-1 opacity-0 transition group-hover/cmd:opacity-100\">\n <CopyButton text={command} />\n </span>\n <pre className=\"overflow-x-auto whitespace-pre-wrap break-words px-3 py-2 font-mono text-[11.5px] leading-[1.6] text-[var(--color-fg-primary)]\">\n {command}\n </pre>\n </div>\n </div>\n );\n}\n\n/** JSON 原文体。搜索态传 query 走 HighlightedText(haystack 命中可见);\n * 非搜索态(未知工具兜底)不传,纯文本即可。 */\nfunction JsonDump({ input, query }: { input: Record<string, unknown>; query?: string }) {\n const json = JSON.stringify(input, null, 2);\n return (\n <pre className=\"overflow-x-auto px-3 py-2 font-mono text-[11.5px] text-[var(--color-fg-primary)]\">\n {query ? <HighlightedText text={json} query={query} /> : json}\n </pre>\n );\n}\n\nfunction FilePathLine({ path, children }: { path: string; children?: ReactNode }) {\n if (!path) return null;\n return (\n <div className=\"flex items-center gap-1.5 px-3 pb-1 pt-2\">\n <span className=\"min-w-0 break-all font-mono text-[10.5px] text-[var(--color-fg-faint)]\">\n {path}\n </span>\n {children}\n </div>\n );\n}\n\n/** 消息流里的紧凑 unified diff:−/+ 着色 + 字内高亮,与弹窗 SplitDiff 同一套\n * 算法(lib/diff),不带行号(old/new 串 diff 没有真实文件行号,标了反而误导)。 */\nfunction DiffRows({ rows }: { rows: UnifiedRow[] }) {\n const t = useT();\n if (rows.length === 0) return null;\n const shown = rows.length > MAX_DIFF_ROWS ? rows.slice(0, MAX_DIFF_ROWS) : rows;\n const omitted = rows.length - shown.length;\n return (\n <div className=\"overflow-x-auto pb-1.5\">\n <div className=\"w-max min-w-full font-mono text-[11.5px] leading-[1.65]\">\n {shown.map((row, i) => (\n <DiffRowLine key={i} row={row} />\n ))}\n </div>\n {omitted > 0 && (\n <p className=\"px-3 pt-1 font-mono text-[10px] italic text-[var(--color-fg-faint)]\">\n {t('tool.moreLines', { n: omitted })}\n </p>\n )}\n </div>\n );\n}\n\nfunction DiffRowLine({ row }: { row: UnifiedRow }) {\n if (row.kind === 'gap') return null; // rowsFromStrings 不产 gap 行\n const bg =\n row.kind === 'del'\n ? 'bg-[var(--color-danger-soft)]'\n : row.kind === 'add'\n ? 'bg-[var(--color-moss-soft)]'\n : '';\n const marker = row.kind === 'del' ? '−' : row.kind === 'add' ? '+' : '';\n const markerColor =\n row.kind === 'del'\n ? 'text-[var(--color-danger)]'\n : row.kind === 'add'\n ? 'text-[var(--color-moss)]'\n : 'text-transparent';\n // 改动 token 的强调底色:在整行 -soft 底色上再叠一层更饱和的同色(同弹窗)。\n const hl = row.kind === 'del' ? 'bg-[var(--color-danger)]/25' : 'bg-[var(--color-moss)]/30';\n return (\n <div className={`flex ${bg}`}>\n <span className={`w-5 shrink-0 select-none text-center ${markerColor}`}>{marker}</span>\n <span className=\"whitespace-pre pr-3 text-[var(--color-fg-primary)]\">\n {row.segs && row.segs.length > 0\n ? row.segs.map((s, i) =>\n s.changed ? (\n <span key={i} className={hl}>\n {s.text}\n </span>\n ) : (\n <span key={i}>{s.text}</span>\n ),\n )\n : row.text === null || row.text === ''\n ? ' '\n : row.text}\n </span>\n </div>\n );\n}\n\n/* ── tool_result ────────────────────────────────────────────────────────── */\n\nexport function ToolResultBlock({\n block,\n query,\n toolName,\n}: {\n block: Extract<Block, { type: 'tool_result' }>;\n query: string;\n /** 发起方工具名(由调用方用 toolUseId 反查),有则标在头部:「工具返回 · Bash」。 */\n toolName?: string;\n}) {\n const t = useT();\n const [open, setOpen] = useState(false);\n const long = block.content.length > PREVIEW_CHARS;\n const visible = open || !long ? block.content : block.content.slice(0, PREVIEW_CHARS) + '…';\n\n const tone = block.isError\n ? 'border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] text-[var(--color-danger)]'\n : 'border-[var(--color-hairline)] bg-[var(--color-sunken)] text-[var(--color-fg-primary)]';\n\n return (\n <div className={`overflow-hidden rounded-xl border text-sm ${tone}`}>\n <div className=\"flex items-center justify-between gap-2 px-3 py-2\">\n <span className=\"flex items-center gap-2 font-mono text-[11.5px] font-medium uppercase tracking-[0.06em]\">\n <Glyph kind={block.isError ? 'error' : 'result'} />\n {block.isError ? t('tool.error') : t('tool.result')}\n {toolName && (\n <span className={block.isError ? 'opacity-70' : 'text-[var(--color-fg-muted)]'}>\n · {toolName}\n </span>\n )}\n </span>\n {long && (\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n className=\"font-mono text-[10.5px] uppercase tracking-[0.16em] underline-offset-2 hover:underline\"\n >\n {open ? t('common.collapse') : t('common.expand')}\n </button>\n )}\n </div>\n <pre className={`overflow-x-auto whitespace-pre-wrap break-words border-t px-3 py-2 font-mono text-[11.5px] ${block.isError ? 'border-[var(--color-danger)]/30' : 'border-[var(--color-hairline)] bg-[var(--color-surface)]'}`}>\n <HighlightedText text={visible} query={query} />\n </pre>\n </div>\n );\n}\n\n/* ── thinking ───────────────────────────────────────────────────────────── */\n\nexport function ThinkingBlock({\n block,\n query,\n}: {\n block: Extract<Block, { type: 'thinking' }>;\n query: string;\n}) {\n const t = useT();\n const [open, setOpen] = useState(false);\n const hasText = block.text.trim() !== '';\n return (\n <div className=\"overflow-hidden rounded-xl border border-[var(--color-hairline-strong)] bg-[var(--color-sunken)] text-sm text-[var(--color-fg-secondary)]\">\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n className=\"flex w-full items-center justify-between gap-2 px-3 py-2 text-left\"\n >\n <span className=\"flex items-center gap-2 font-mono text-[11.5px] font-medium uppercase tracking-[0.06em]\">\n <Glyph kind=\"thinking\" /> {t('tool.thinking')}\n </span>\n <Caret open={open} />\n </button>\n {open && (\n hasText ? (\n <div className=\"whitespace-pre-wrap break-words border-t border-[var(--color-hairline-strong)] bg-[var(--color-surface)] px-3 py-2 text-[13px] leading-relaxed text-[var(--color-fg-secondary)]\">\n <HighlightedText text={block.text} query={query} />\n </div>\n ) : (\n <p className=\"border-t border-[var(--color-hairline-strong)] bg-[var(--color-surface)] px-3 py-2 text-[12px] italic text-[var(--color-fg-muted)]\">\n {t('tool.thinkingEncrypted')}\n </p>\n )\n )}\n </div>\n );\n}\n\n/* ── shared bits ────────────────────────────────────────────────────────── */\n\n/** 悬浮复制按钮:markdown 代码块与 Bash 命令块共用。 */\nexport function CopyButton({ text }: { text: string }) {\n const t = useT();\n const [copied, setCopied] = useState(false);\n return (\n <button\n type=\"button\"\n aria-label={copied ? t('tool.copied') : t('tool.copy')}\n title={copied ? t('tool.copied') : t('tool.copy')}\n onClick={() => {\n void navigator.clipboard.writeText(text).then(() => {\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n });\n }}\n className={\n 'inline-flex items-center justify-center rounded-md border border-[var(--color-hairline)] bg-[var(--color-surface)] p-1 shadow-[var(--shadow-rise)] transition ' +\n (copied\n ? 'text-[var(--color-moss)]'\n : 'text-[var(--color-fg-muted)] hover:text-[var(--color-fg-primary)]')\n }\n >\n {copied ? <CheckIcon /> : <CopyIcon />}\n </button>\n );\n}\n\nfunction strOf(v: unknown): string {\n return typeof v === 'string' ? v : '';\n}\n\nfunction asRecord(x: unknown): Record<string, unknown> {\n return x && typeof x === 'object' ? (x as Record<string, unknown>) : {};\n}\n\nfunction firstLine(s: string): string | null {\n const line = s.split('\\n', 1)[0]!.trim();\n return line || null;\n}\n\n/** 路径尾部最多两段(`lib/diff.ts`),足够认出文件又不挤爆一行。 */\nfunction pathTail(p: string): string | null {\n if (!p) return null;\n const segs = p.split(/[\\\\/]+/).filter(Boolean);\n return segs.slice(-2).join('/') || null;\n}\n\nfunction Caret({ open }: { open: boolean }) {\n return (\n <svg\n width=\"11\"\n height=\"11\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"text-[var(--color-fg-muted)] transition-transform\"\n style={{ transform: open ? 'rotate(90deg)' : 'rotate(0)' }}\n aria-hidden\n >\n <path d=\"M9 6l6 6-6 6\" />\n </svg>\n );\n}\n\nfunction CopyIcon() {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" />\n <path d=\"M5 15V5a2 2 0 0 1 2-2h10\" />\n </svg>\n );\n}\n\nfunction CheckIcon() {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M4 12.5l5 5L20 6.5\" />\n </svg>\n );\n}\n\nfunction Glyph({ kind }: { kind: 'tool' | 'result' | 'error' | 'thinking' }) {\n const common = {\n width: 11,\n height: 11,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 1.8,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n 'aria-hidden': true,\n };\n if (kind === 'tool') {\n return (\n <svg {...common}>\n <path d=\"M14.7 5.3a3 3 0 1 0 4 4l-2.5 2.5 5 5-2.7 2.7-5-5-2.5 2.5a3 3 0 1 0-4-4z\" />\n </svg>\n );\n }\n if (kind === 'result') {\n return (\n <svg {...common}>\n <path d=\"M5 12h13\" />\n <path d=\"M13 7l5 5-5 5\" />\n </svg>\n );\n }\n if (kind === 'error') {\n return (\n <svg {...common}>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M12 8v4.5\" />\n <path d=\"M12 16h.01\" />\n </svg>\n );\n }\n return (\n <svg {...common}>\n <path d=\"M9 18h6\" />\n <path d=\"M10 21h4\" />\n <path d=\"M12 3a6 6 0 0 0-3.6 10.8c.8.6 1.1 1.6 1.1 2.7v.5h5v-.5c0-1.1.3-2.1 1.1-2.7A6 6 0 0 0 12 3z\" />\n </svg>\n );\n}\n","import { lazy, Suspense } from 'react';\nimport type { Block, Message } from '../lib/api.ts';\nimport { formatDateTime } from '../lib/format.ts';\nimport { useT } from '../lib/i18n.ts';\nimport HighlightedText from './HighlightedText.tsx';\nimport { ThinkingBlock, ToolResultBlock, ToolUseBlock } from './ToolBlock.tsx';\n\n// markdown 渲染按需加载(remark/micromark 在独立 chunk),加载完成前\n// Suspense fallback 退回纯文本——内容始终可见,不闪空白。\nconst MarkdownContent = lazy(() => import('./MarkdownContent.tsx'));\n\n/** toolUseId → 工具名。tool_use 与其 tool_result 分属两条消息,调用方\n * (时间线 / 弹窗对话栏)从已加载的全量消息构建后传入,用于 result 头部标注来源。 */\nexport type ToolNameLookup = ReadonlyMap<string, string>;\n\nexport default function MessageBubble({\n message,\n query,\n toolNames,\n}: {\n message: Message;\n query: string;\n toolNames?: ToolNameLookup;\n}) {\n if (message.isMeta) return <SystemMessage message={message} query={query} />;\n if (\n message.type === 'user' &&\n message.blocks.length > 0 &&\n message.blocks.every((b) => b.type === 'tool_result')\n ) {\n return (\n <AssistantMessage message={message} query={query} toolNames={toolNames} variant=\"tool\" />\n );\n }\n if (message.type === 'user') {\n return <UserMessage message={message} query={query} toolNames={toolNames} />;\n }\n return <AssistantMessage message={message} query={query} toolNames={toolNames} />;\n}\n\nfunction AssistantMessage({\n message,\n query,\n toolNames,\n variant = 'assistant',\n}: {\n message: Message;\n query: string;\n toolNames?: ToolNameLookup;\n variant?: 'assistant' | 'tool';\n}) {\n const t = useT();\n const isTool = variant === 'tool';\n const label = isTool ? t('message.role.tool') : t('message.role.claude');\n const borderClass = isTool\n ? 'border-l-[var(--color-hairline-strong)]'\n : 'border-l-[var(--color-accent)]';\n return (\n <div className=\"flex items-start gap-3\" data-uuid={message.uuid}>\n <Avatar role=\"assistant\" />\n <div className=\"min-w-0 flex-1 max-w-[min(54rem,calc(100%-3rem))]\">\n <Header\n align=\"left\"\n label={label}\n model={message.model}\n ts={message.ts}\n accent={!isTool}\n />\n <article\n className={\n 'mt-1.5 rounded-2xl rounded-tl-sm border border-l-[3px] border-[var(--color-hairline)] bg-[var(--color-surface)] px-4 py-3 shadow-[0_1px_0_0_var(--color-hairline)] ' +\n borderClass\n }\n >\n <Blocks\n blocks={message.blocks}\n query={query}\n toolNames={toolNames}\n markdown={!isTool && !query}\n />\n </article>\n </div>\n </div>\n );\n}\n\nfunction UserMessage({\n message,\n query,\n toolNames,\n}: {\n message: Message;\n query: string;\n toolNames?: ToolNameLookup;\n}) {\n const t = useT();\n return (\n <div className=\"flex items-start justify-end gap-3\" data-uuid={message.uuid}>\n <div className=\"min-w-0 max-w-[min(46rem,calc(100%-3rem))]\">\n <Header\n align=\"right\"\n label={t('message.role.you')}\n model={message.model}\n ts={message.ts}\n />\n <article className=\"mt-1.5 rounded-2xl rounded-tr-sm bg-[var(--color-accent-soft)] px-4 py-3 text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\n <Blocks blocks={message.blocks} query={query} toolNames={toolNames} />\n </article>\n </div>\n <Avatar role=\"user\" />\n </div>\n );\n}\n\nfunction SystemMessage({ message, query }: { message: Message; query: string }) {\n const t = useT();\n return (\n <div className=\"my-2 flex items-center gap-3 px-4\" data-uuid={message.uuid}>\n <span className=\"h-px flex-1 bg-[var(--color-hairline)]\" />\n <div className=\"max-w-2xl text-center\">\n <p className=\"font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--color-fg-muted)]\">\n {t('message.role.system')} · {formatDateTime(message.ts)}\n </p>\n <div className=\"mt-1 space-y-1 text-xs italic text-[var(--color-fg-muted)]\">\n {message.blocks.map((block, i) => {\n if (block.type === 'text') {\n const text = block.text.length > 200 ? block.text.slice(0, 200) + '…' : block.text;\n return (\n <p key={i} className=\"whitespace-pre-wrap break-words\">\n <HighlightedText text={text} query={query} />\n </p>\n );\n }\n if (block.type === 'tool_use') return <p key={i}>{t('tool.use')} · {block.name}</p>;\n if (block.type === 'tool_result') return <p key={i}>{t('tool.result')}</p>;\n return null;\n })}\n </div>\n </div>\n <span className=\"h-px flex-1 bg-[var(--color-hairline)]\" />\n </div>\n );\n}\n\nfunction Header({\n align,\n label,\n model,\n ts,\n accent,\n}: {\n align: 'left' | 'right';\n label: string;\n model: string | null;\n ts: string | null;\n accent?: boolean;\n}) {\n return (\n <div\n className={\n 'flex items-baseline gap-2 text-[11px] ' +\n (align === 'right' ? 'flex-row-reverse text-right' : '')\n }\n >\n <span\n className={\n 'font-display text-[14px] font-medium tracking-tight ' +\n (accent\n ? 'text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]'\n : 'text-[var(--color-fg-primary)]')\n }\n >\n {label}\n </span>\n {model && (\n <span className=\"truncate font-mono text-[10px] uppercase tracking-[0.14em] text-[var(--color-fg-faint)]\">\n {model}\n </span>\n )}\n <time className=\"font-mono tabular-nums text-[var(--color-fg-muted)]\">\n {formatDateTime(ts)}\n </time>\n </div>\n );\n}\n\nfunction Blocks({\n blocks,\n query,\n toolNames,\n markdown = false,\n}: {\n blocks: Block[];\n query: string;\n toolNames?: ToolNameLookup;\n /** true=assistant 回复且非搜索态,text block 走 markdown 排版;否则纯文本+高亮。 */\n markdown?: boolean;\n}) {\n const t = useT();\n return (\n <div className=\"space-y-2.5\">\n {blocks.map((block, i) => {\n switch (block.type) {\n case 'text': {\n const plain = (\n <p\n key={i}\n className=\"whitespace-pre-wrap break-words text-[14.5px] leading-relaxed\"\n >\n <HighlightedText text={block.text} query={query} />\n </p>\n );\n if (!markdown) return plain;\n return (\n <Suspense key={i} fallback={plain}>\n <MarkdownContent text={block.text} />\n </Suspense>\n );\n }\n case 'tool_use':\n return <ToolUseBlock key={i} block={block} query={query} />;\n case 'tool_result':\n return (\n <ToolResultBlock\n key={i}\n block={block}\n query={query}\n toolName={toolNames?.get(block.toolUseId)}\n />\n );\n case 'thinking':\n return <ThinkingBlock key={i} block={block} query={query} />;\n case 'image':\n return (\n <div\n key={i}\n className=\"rounded-xl border border-[var(--color-hairline)] bg-[var(--color-sunken)] px-3 py-2 font-mono text-[11px] text-[var(--color-fg-muted)]\"\n >\n {t('tool.image')}{block.mediaType ? ` · ${block.mediaType}` : ''}\n </div>\n );\n default:\n return (\n <pre\n key={i}\n className=\"overflow-x-auto rounded-xl border border-[var(--color-hairline)] bg-[var(--color-sunken)] px-3 py-2 font-mono text-xs text-[var(--color-fg-secondary)]\"\n >\n {JSON.stringify(block.raw, null, 2)}\n </pre>\n );\n }\n })}\n </div>\n );\n}\n\n// Trailing \"Claude is working…\" row — sits at the tail of a timeline while the\n// live poll keeps `isWorking` true, then unmounts when the reply lands. Shared by\n// the session detail timeline and the modified-files drawer's conversation column.\nexport function WorkingIndicator() {\n const t = useT();\n return (\n <li className=\"py-3\" aria-live=\"polite\">\n <div className=\"flex items-start gap-3\">\n <span\n aria-hidden\n className=\"mt-1 flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-[var(--color-hairline-strong)] bg-[var(--color-surface)]\"\n >\n <span className=\"relative inline-flex h-2.5 w-2.5\">\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)] pulse-amber\" />\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)]\" />\n </span>\n </span>\n <div className=\"min-w-0 flex-1\">\n <span className=\"block font-display text-[14px] font-medium tracking-tight text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\n {t('message.role.claude')}\n </span>\n <article className=\"mt-1.5 inline-flex items-center gap-2.5 rounded-2xl rounded-tl-sm border border-l-[3px] border-[var(--color-hairline)] border-l-[var(--color-accent)] bg-[var(--color-surface)] px-4 py-3\">\n <span aria-hidden className=\"loading-dots text-[var(--color-accent)]\">\n <span />\n <span />\n <span />\n </span>\n <span className=\"font-display text-[13.5px] italic text-[var(--color-fg-muted)]\">\n {t('session.working.indicator')}\n </span>\n </article>\n </div>\n </div>\n </li>\n );\n}\n\nfunction Avatar({ role }: { role: 'user' | 'assistant' }) {\n if (role === 'assistant') {\n return (\n <span\n aria-hidden\n className=\"mt-1 flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-[var(--color-hairline-strong)] bg-[var(--color-surface)] text-[var(--color-accent)]\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M19 7.5A8 8 0 1 0 19 16.5\" />\n <path d=\"M15.5 9.5a4.5 4.5 0 1 0 0 5\" opacity=\"0.45\" />\n </svg>\n </span>\n );\n }\n return (\n <span\n aria-hidden\n className=\"mt-1 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-[var(--color-fg-primary)] font-display text-sm font-medium text-[var(--color-canvas)]\"\n >\n Y\n </span>\n );\n}\n","import { motion } from 'motion/react';\nimport {\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n} from 'react';\nimport type {\n Message,\n ModifiedFileSummary,\n ModifiedFileToolName,\n} from '../lib/api.ts';\nimport {\n rowsFromHunks,\n rowsFromStrings,\n type Seg,\n type UnifiedRow,\n} from '../lib/diff.ts';\nimport { useT } from '../lib/i18n.ts';\nimport { Loading } from './Loading.tsx';\nimport MessageBubble, { WorkingIndicator } from './MessageBubble.tsx';\n\n// 三栏布局的最小内容区宽度:拖动任一分割线时,给中间文件内容栏保底的像素宽。\nconst CONTENT_MIN_PX = 320;\n\n// 对话栏初始只渲染最新的这么多条消息(默认落点在底部最新一条),更早的折叠在\n// 顶部「显示更早」后按这个步长逐批展开——会话动辄上百条,全量渲染既慢又埋没最新动态。\nconst CONV_INITIAL_VISIBLE = 20;\nconst CONV_LOAD_STEP = 40;\n\n// 对话栏「贴底跟随」阈值:滚动位置离底部小于这个像素时,视作用户在追看最新动态——\n// 实时轮询追加新消息(或 Claude 开始处理)后自动滚到底;超过则认为在往上翻历史,不打断。\nconst CONV_BOTTOM_STICK_PX = 120;\n\n/** Lookup from a tool_use id → the issuing tool's name + raw input, built from the\n * already-loaded session messages. Lets the detail pane render the actual edit\n * content (Write body / Edit diff) without a second backend round-trip. */\nexport type EditLookup = Map<string, { name: string; input: unknown }>;\n\ninterface Props {\n files: ModifiedFileSummary[];\n cwd: string | null;\n editLookup: EditLookup;\n /** Session conversation, rendered in the drawer's left column so edits can be\n * read alongside the dialogue that drove them. Already meta-filtered upstream. */\n messages: Message[];\n /** Active search query, forwarded to MessageBubble for in-message highlight. */\n query: string;\n /** Live poll says Claude is mid-turn — append the working indicator to the\n * conversation column's tail, mirroring the session timeline. */\n isWorking: boolean;\n loading: boolean;\n error: Error | null;\n onClose: () => void;\n /** Open the real file on disk in the OS default app. */\n onOpenFile: (filePath: string) => void;\n}\n\nexport default function ModifiedFilesDrawer({\n files,\n cwd,\n editLookup,\n messages,\n query,\n isWorking,\n loading,\n error,\n onClose,\n onOpenFile,\n}: Props) {\n const t = useT();\n const tree = useMemo(() => buildTree(files), [files]);\n\n // 对话栏 tool_result 头部标注来源工具:从 editLookup 萃取 toolUseId → 工具名。\n const toolNames = useMemo(() => {\n const m = new Map<string, string>();\n for (const [id, v] of editLookup) m.set(id, v.name);\n return m;\n }, [editLookup]);\n\n // 选中文件路径(明细对象用 filePath 作为稳定 key)。打开时自动选第一个,\n // 避免右侧空白;files 变化后若当前选中已不在则回落到第一个。\n const [selected, setSelected] = useState<string | null>(null);\n useEffect(() => {\n setSelected((prev) =>\n prev && files.some((f) => f.filePath === prev) ? prev : files[0]?.filePath ?? null,\n );\n }, [files]);\n\n const selectedFile = useMemo(\n () => files.find((f) => f.filePath === selected) ?? null,\n [files, selected],\n );\n\n // 折叠的文件夹集合(默认全展开——修改文件清单通常不长,铺开更利于一眼扫)。\n const allFolders = useMemo(() => collectFolderPaths(tree), [tree]);\n const [collapsed, setCollapsed] = useState<ReadonlySet<string>>(() => new Set());\n const toggleFolder = (path: string) =>\n setCollapsed((prev) => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n\n // 三栏(对话 | 内容 | 文件树)的两条可拖拽分割线:对话栏与文件树栏各持一个像素\n // 宽度,中间内容栏吃掉剩余空间。拖动时实时改、并各自给内容区留 CONTENT_MIN_PX 保底。\n const splitRef = useRef<HTMLDivElement>(null);\n const convScrollRef = useRef<HTMLDivElement>(null);\n const [convWidth, setConvWidth] = useState(420);\n const [railWidth, setRailWidth] = useState(280);\n\n // 对话栏可见消息:默认只展示尾部最新 CONV_INITIAL_VISIBLE 条,更早的折叠。\n const [visibleCount, setVisibleCount] = useState(CONV_INITIAL_VISIBLE);\n const startIndex = Math.max(0, messages.length - visibleCount);\n const visibleMessages = useMemo(() => messages.slice(startIndex), [messages, startIndex]);\n const hiddenCount = startIndex;\n\n // 展开更早消息时需要在重排后修正滚动位置:restoreFromBottom 保持「离底部的距离」\n // 不变,避免视口往上跳。\n const restoreFromBottom = useRef<number | null>(null);\n\n // 实时跟随状态:用户是否停在对话栏底部(决定要不要追新),以及上一次的消息数 /\n // 处理中标志(只在「真有新消息到达」或「刚开始处理」时才追,避免每次轮询都滚动)。\n const stickToBottom = useRef(true);\n const prevMsgCount = useRef(messages.length);\n const prevIsWorking = useRef(isWorking);\n\n // 打开抽屉时把对话栏滚到底——最新一条消息就是落点。messages 已就绪,commit 后\n // scrollHeight 即为准确值,用 layout effect 在绘制前定位,避免可见的跳动。\n useLayoutEffect(() => {\n const el = convScrollRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n // 只在首次挂载(抽屉打开)时落到底部。\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // visibleCount 变化(展开更早)后修正滚动位置。\n useLayoutEffect(() => {\n const el = convScrollRef.current;\n if (!el) return;\n if (restoreFromBottom.current != null) {\n el.scrollTop = el.scrollHeight - restoreFromBottom.current;\n restoreFromBottom.current = null;\n }\n }, [visibleCount]);\n\n // 滚动时记录是否停在底部,供下面的实时跟随判断「该不该追新」。\n function onConvScroll() {\n const el = convScrollRef.current;\n if (!el) return;\n stickToBottom.current = el.scrollHeight - (el.scrollTop + el.clientHeight) < CONV_BOTTOM_STICK_PX;\n }\n\n // 实时轮询追加了新消息、或 Claude 刚开始处理(WorkingIndicator 出现)时,若用户停在\n // 底部就跟随到最新——和会话时间线的自动跟随同义。展开更早触发的重排由上面的\n // visibleCount layout effect 负责,这里给 restoreFromBottom 让位,避免互相打架。\n useLayoutEffect(() => {\n const grew = messages.length > prevMsgCount.current;\n const startedWorking = isWorking && !prevIsWorking.current;\n prevMsgCount.current = messages.length;\n prevIsWorking.current = isWorking;\n if (!grew && !startedWorking) return;\n if (restoreFromBottom.current != null) return;\n if (!stickToBottom.current) return;\n const el = convScrollRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n }, [messages.length, isWorking]);\n\n function showEarlier() {\n const el = convScrollRef.current;\n restoreFromBottom.current = el ? el.scrollHeight - el.scrollTop : null;\n setVisibleCount((c) => Math.min(messages.length, c + CONV_LOAD_STEP));\n }\n\n // Esc 关闭 + 背景滚动锁。\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape') onClose();\n }\n window.addEventListener('keydown', onKey);\n const prevOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n window.removeEventListener('keydown', onKey);\n document.body.style.overflow = prevOverflow;\n };\n }, [onClose]);\n\n const count = files.length;\n const countLabel =\n count === 1\n ? t('session.modified.count', { n: count })\n : t('session.modified.countPlural', { n: count });\n\n return (\n <>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.18 }}\n onClick={onClose}\n className=\"fixed inset-0 z-[55] bg-[oklch(0.16_0.006_85_/_0.5)] backdrop-blur-[2px]\"\n aria-hidden\n />\n <motion.aside\n initial={{ x: '100%' }}\n animate={{ x: 0 }}\n exit={{ x: '100%' }}\n transition={{ duration: 0.26, ease: [0.16, 1, 0.3, 1] }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={t('session.modified.title')}\n className=\"fixed inset-0 z-[60] flex w-full flex-col bg-[var(--color-surface)] shadow-[var(--shadow-pop)]\"\n >\n <header className=\"flex items-center justify-between gap-3 border-b border-[var(--color-hairline)] px-5 py-1.5\">\n <div className=\"flex min-w-0 items-center gap-2.5\">\n <span className=\"text-[var(--color-accent)]\">\n <TreeGlyph />\n </span>\n <div className=\"min-w-0\">\n <h2 className=\"font-display text-[14px] font-light leading-tight tracking-tight text-[var(--color-fg-primary)]\">\n {t('session.modified.title')}\n </h2>\n {cwd && (\n <p\n className=\"truncate font-mono text-[10.5px] tracking-[0.02em] text-[var(--color-fg-faint)]\"\n title={cwd}\n >\n {cwd}\n </p>\n )}\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2.5\">\n {count > 0 && (\n <span className=\"font-mono text-[10.5px] uppercase tracking-[0.16em] tabular-nums text-[var(--color-fg-muted)]\">\n {countLabel}\n </span>\n )}\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={t('session.modified.close')}\n className=\"rounded-[var(--radius-control)] p-1.5 text-[var(--color-fg-muted)] transition hover:bg-[var(--color-sunken)] hover:text-[var(--color-fg-primary)]\"\n >\n <CloseIcon />\n </button>\n </div>\n </header>\n\n {loading && <Loading label={t('session.modified.loading')} className=\"m-6\" />}\n\n {error && !loading && (\n <p className=\"m-5 rounded-[var(--radius-control)] border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-4 py-3 text-sm text-[var(--color-danger)]\">\n {t('session.modified.failed')}: {error.message}\n </p>\n )}\n\n {/* 无修改文件时仍保留三栏框架与左侧对话栏——右两栏走各自的空态,\n 而不是把整页塌成一句提示。 */}\n {!loading && !error && (\n <div ref={splitRef} className=\"flex min-h-0 flex-1\">\n {/* ① 对话栏:左侧,入场时从左缘滑入——读作「对话流进了弹窗」。 */}\n <motion.div\n initial={{ opacity: 0, x: -32 }}\n animate={{ opacity: 1, x: 0 }}\n transition={{ duration: 0.34, ease: [0.16, 1, 0.3, 1], delay: 0.06 }}\n className=\"flex shrink-0 flex-col border-r border-[var(--color-hairline)]\"\n style={{ width: convWidth }}\n >\n <div className=\"flex items-center gap-2 border-b border-[var(--color-hairline)] px-4 py-1.5\">\n <span className=\"eyebrow\">{t('session.modified.col.conversation')}</span>\n <span className=\"ml-auto font-mono text-[10px] tabular-nums text-[var(--color-fg-muted)]\">\n {messages.length}\n </span>\n </div>\n <div\n ref={convScrollRef}\n onScroll={onConvScroll}\n className=\"min-h-0 flex-1 overflow-auto px-4 py-2\"\n >\n {messages.length === 0 && !isWorking ? (\n <p className=\"px-1 py-3 text-sm italic text-[var(--color-fg-muted)]\">\n {t('common.noMessagesMatch')}\n </p>\n ) : (\n <>\n {hiddenCount > 0 && (\n <button\n type=\"button\"\n onClick={showEarlier}\n className=\"mb-1 flex w-full items-center justify-center gap-1.5 rounded-[var(--radius-input)] border border-dashed border-[var(--color-hairline-strong)] py-2 font-mono text-[10.5px] uppercase tracking-[0.14em] text-[var(--color-fg-muted)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent-ink)] dark:hover:text-[var(--color-accent)]\"\n >\n {t('session.modified.showEarlier', { n: hiddenCount })}\n </button>\n )}\n <ol>\n {visibleMessages.map((m, i) => (\n <li key={m.uuid || m.ts || String(startIndex + i)} className=\"py-3\">\n <MessageBubble message={m} query={query} toolNames={toolNames} />\n </li>\n ))}\n {isWorking && <WorkingIndicator />}\n </ol>\n </>\n )}\n </div>\n </motion.div>\n\n <Splitter\n getRect={() => splitRef.current?.getBoundingClientRect() ?? null}\n onResize={(clientX, rect) =>\n setConvWidth(clampWidth(clientX - rect.left, rect.width - railWidth))\n }\n />\n\n {/* ② 文件内容栏:中间,吃掉剩余空间。 */}\n <div className=\"min-w-0 flex-1 overflow-auto\">\n {selectedFile ? (\n <FileDetail\n key={selectedFile.filePath}\n file={selectedFile}\n editLookup={editLookup}\n onOpenFile={onOpenFile}\n />\n ) : (\n <div className=\"flex h-full items-center justify-center px-6\">\n <p className=\"text-center text-sm italic text-[var(--color-fg-muted)]\">\n {count === 0\n ? t('session.modified.empty')\n : t('session.modified.selectFile')}\n </p>\n </div>\n )}\n </div>\n\n <Splitter\n getRect={() => splitRef.current?.getBoundingClientRect() ?? null}\n onResize={(clientX, rect) =>\n setRailWidth(clampWidth(rect.right - clientX, rect.width - convWidth))\n }\n />\n\n {/* ③ 文件树栏:右侧,入场时从右缘滑入。 */}\n <motion.div\n initial={{ opacity: 0, x: 32 }}\n animate={{ opacity: 1, x: 0 }}\n transition={{ duration: 0.34, ease: [0.16, 1, 0.3, 1], delay: 0.06 }}\n className=\"flex shrink-0 flex-col border-l border-[var(--color-hairline)]\"\n style={{ width: railWidth }}\n >\n <div className=\"flex items-center justify-between gap-2 border-b border-[var(--color-hairline)] px-3 py-1.5\">\n <span className=\"eyebrow\">{t('session.modified.col.file')}</span>\n {allFolders.length > 0 && (\n <button\n type=\"button\"\n onClick={() =>\n setCollapsed((prev) =>\n prev.size === 0 ? new Set(allFolders) : new Set(),\n )\n }\n className=\"font-mono text-[10px] uppercase tracking-[0.14em] text-[var(--color-fg-muted)] transition hover:text-[var(--color-accent-ink)] dark:hover:text-[var(--color-accent)]\"\n >\n {collapsed.size === 0\n ? t('session.modified.collapseAll')\n : t('session.modified.expandAll')}\n </button>\n )}\n </div>\n <div className=\"min-h-0 flex-1 overflow-auto py-1.5\">\n <ul role=\"tree\" className=\"w-max min-w-full select-none\">\n {tree.map((node) => (\n <TreeRow\n key={nodeKey(node)}\n node={node}\n depth={0}\n collapsed={collapsed}\n onToggleFolder={toggleFolder}\n selected={selected}\n onSelectFile={setSelected}\n onOpenFile={onOpenFile}\n />\n ))}\n </ul>\n </div>\n </motion.div>\n </div>\n )}\n </motion.aside>\n </>\n );\n}\n\n/* ── Draggable column splitter ──────────────────────────────────────────── */\n\n// 竖向分割线:拖动时把指针的 clientX 连同容器矩形回传,由调用方换算出该侧栏宽度。\n// 用 setPointerCapture 锁住指针,拖出分割线也不丢事件。\nfunction Splitter({\n getRect,\n onResize,\n}: {\n getRect: () => DOMRect | null;\n onResize: (clientX: number, rect: DOMRect) => void;\n}) {\n const dragging = useRef(false);\n return (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n onPointerDown={(e: ReactPointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.currentTarget.setPointerCapture(e.pointerId);\n dragging.current = true;\n }}\n onPointerMove={(e: ReactPointerEvent<HTMLDivElement>) => {\n if (!dragging.current) return;\n const rect = getRect();\n if (rect) onResize(e.clientX, rect);\n }}\n onPointerUp={(e: ReactPointerEvent<HTMLDivElement>) => {\n dragging.current = false;\n e.currentTarget.releasePointerCapture(e.pointerId);\n }}\n className=\"relative w-px shrink-0 cursor-col-resize touch-none bg-[var(--color-hairline)] transition-colors hover:bg-[var(--color-accent)]\"\n >\n {/* 加宽命中区,但不挤占布局。 */}\n <span className=\"absolute inset-y-0 -left-1.5 -right-1.5\" aria-hidden />\n </div>\n );\n}\n\n// 把某侧栏宽夹在 [220px, available − 内容保底] 之间。available = 容器宽 − 另一侧栏宽。\nfunction clampWidth(value: number, available: number): number {\n const min = 220;\n const max = Math.max(min, available - CONTENT_MIN_PX);\n return Math.min(max, Math.max(min, value));\n}\n\n/* ── File tree ──────────────────────────────────────────────────────────── */\n\ninterface FolderNode {\n kind: 'folder';\n name: string;\n path: string;\n children: TreeNode[];\n}\ninterface FileNode {\n kind: 'file';\n name: string;\n file: ModifiedFileSummary;\n}\ntype TreeNode = FolderNode | FileNode;\n\nfunction nodeKey(node: TreeNode): string {\n return node.kind === 'folder' ? `d:${node.path}` : `f:${node.file.filePath}`;\n}\n\n/** 本会话内对该文件的「变更类型」,仅用于文件名着色(Git 习惯:A 绿 / M 琥珀)。\n * added = 本会话首个操作就是「创建」(Write/NotebookEdit 且 structuredPatch 为空数组,\n * 这是 Claude Code 记录全新文件的信号);否则一律按 modified。本工具集没有删除语义,故无 deleted。 */\ntype FileChangeType = 'added' | 'modified';\nfunction fileChangeType(file: ModifiedFileSummary): FileChangeType {\n const first = file.operations[0];\n const created =\n !!first &&\n (first.toolName === 'Write' || first.toolName === 'NotebookEdit') &&\n Array.isArray(first.structuredPatch) &&\n first.structuredPatch.length === 0;\n return created ? 'added' : 'modified';\n}\n\n/** 变更类型 → 文件名/图标的前景色 token。errored 在调用处优先用 danger,不走这里。 */\nfunction changeToneClass(type: FileChangeType): string {\n return type === 'added'\n ? 'text-[var(--color-moss)]'\n : 'text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]';\n}\n\n// 把扁平文件列表按目录段建成树。displayPath = relativePath ?? filePath,\n// 末段是文件名,其余是文件夹。文件夹内:文件夹优先、各自字母序。\n// 最后把\"只有一个子文件夹\"的单链折叠成 a/b 一行(IDE 习惯,省纵深)。\nfunction buildTree(files: ModifiedFileSummary[]): TreeNode[] {\n interface Building {\n folders: Map<string, Building>;\n files: { name: string; file: ModifiedFileSummary }[];\n path: string;\n }\n const root: Building = { folders: new Map(), files: [], path: '' };\n\n for (const file of files) {\n const display = file.relativePath ?? file.filePath;\n const segs = display.split(/[\\\\/]+/).filter(Boolean);\n const name = segs.pop() ?? display;\n let cur = root;\n for (const seg of segs) {\n let next = cur.folders.get(seg);\n if (!next) {\n next = { folders: new Map(), files: [], path: cur.path ? `${cur.path}/${seg}` : seg };\n cur.folders.set(seg, next);\n }\n cur = next;\n }\n cur.files.push({ name, file });\n }\n\n function materialize(b: Building): TreeNode[] {\n const folders: FolderNode[] = [];\n for (const [name, child] of b.folders) {\n folders.push(collapseChain({ kind: 'folder', name, path: child.path, children: materialize(child) }));\n }\n folders.sort((a, z) => a.name.localeCompare(z.name, undefined, { sensitivity: 'base' }));\n const fileNodes: FileNode[] = b.files\n .map((f) => ({ kind: 'file' as const, name: f.name, file: f.file }))\n .sort((a, z) => a.name.localeCompare(z.name, undefined, { sensitivity: 'base' }));\n return [...folders, ...fileNodes];\n }\n\n // a → (only child folder b) ⇒ \"a/b\"\n function collapseChain(folder: FolderNode): FolderNode {\n let node = folder;\n while (node.children.length === 1 && node.children[0]!.kind === 'folder') {\n const only = node.children[0] as FolderNode;\n node = { kind: 'folder', name: `${node.name}/${only.name}`, path: only.path, children: only.children };\n }\n return node;\n }\n\n return materialize(root);\n}\n\nfunction collectFolderPaths(nodes: TreeNode[]): string[] {\n const out: string[] = [];\n const walk = (ns: TreeNode[]) => {\n for (const n of ns) {\n if (n.kind === 'folder') {\n out.push(n.path);\n walk(n.children);\n }\n }\n };\n walk(nodes);\n return out;\n}\n\nfunction TreeRow({\n node,\n depth,\n collapsed,\n onToggleFolder,\n selected,\n onSelectFile,\n onOpenFile,\n}: {\n node: TreeNode;\n depth: number;\n collapsed: ReadonlySet<string>;\n onToggleFolder: (path: string) => void;\n selected: string | null;\n onSelectFile: (path: string) => void;\n onOpenFile: (filePath: string) => void;\n}) {\n const t = useT();\n const indent = { paddingLeft: `${depth * 14 + 10}px` };\n\n if (node.kind === 'folder') {\n const isCollapsed = collapsed.has(node.path);\n return (\n <li role=\"treeitem\" aria-expanded={!isCollapsed}>\n <button\n type=\"button\"\n onClick={() => onToggleFolder(node.path)}\n style={indent}\n className=\"flex w-full items-center gap-1.5 py-1 pr-2 text-left transition hover:bg-[var(--color-sunken)]\"\n >\n <Caret open={!isCollapsed} />\n <FolderIcon open={!isCollapsed} />\n <span className=\"whitespace-nowrap font-mono text-[12px] text-[var(--color-fg-secondary)]\">\n {node.name}\n </span>\n </button>\n {!isCollapsed && (\n <ul role=\"group\">\n {node.children.map((child) => (\n <TreeRow\n key={nodeKey(child)}\n node={child}\n depth={depth + 1}\n collapsed={collapsed}\n onToggleFolder={onToggleFolder}\n selected={selected}\n onSelectFile={onSelectFile}\n onOpenFile={onOpenFile}\n />\n ))}\n </ul>\n )}\n </li>\n );\n }\n\n const f = node.file;\n const isSelected = selected === f.filePath;\n const openLabel = t('session.modified.openFile');\n const changeType = fileChangeType(f);\n // 文件名/图标着色按变更类型走;errored 是另一根轴,红点单独标,不抢文件名的语义色。\n const nameTone = changeToneClass(changeType);\n return (\n <li\n role=\"treeitem\"\n aria-selected={isSelected}\n className={\n 'group flex w-full items-center transition ' +\n (isSelected\n ? 'bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\n : 'hover:bg-[var(--color-sunken)]')\n }\n >\n {/* flex-1 但不加 min-w-0:min-width:auto 保证按钮不缩到内容以下,\n 文件名 whitespace-nowrap 撑宽行 → 外层 ul(w-max) 横向出滚动条。 */}\n <button\n type=\"button\"\n onClick={() => onSelectFile(f.filePath)}\n style={indent}\n title={f.filePath}\n className=\"flex flex-1 items-center gap-1.5 py-1 pr-2 text-left\"\n >\n <span className=\"w-[11px] shrink-0\" aria-hidden />\n <FileIcon errored={f.errorCount > 0} tone={changeType} />\n <span\n className={\n 'whitespace-nowrap font-mono text-[12px] ' +\n nameTone +\n (isSelected ? ' font-medium' : '')\n }\n >\n {node.name}\n </span>\n </button>\n <span className=\"flex shrink-0 items-center gap-1 pl-1 pr-2\">\n {f.errorCount > 0 && <span className=\"h-1.5 w-1.5 rounded-full bg-[var(--color-danger)]\" />}\n <span\n className={`font-mono text-[10px] font-semibold leading-none ${nameTone}`}\n title={t(changeType === 'added' ? 'session.modified.added' : 'session.modified.modified')}\n >\n {changeType === 'added' ? 'A' : 'M'}\n </span>\n <span className=\"font-mono text-[10px] tabular-nums text-[var(--color-fg-muted)] group-hover:hidden\">\n {f.totalCount}\n </span>\n <button\n type=\"button\"\n onClick={() => onOpenFile(f.filePath)}\n title={openLabel}\n aria-label={openLabel}\n className=\"hidden rounded-[var(--radius-control)] p-0.5 text-[var(--color-fg-muted)] transition hover:text-[var(--color-accent-ink)] group-hover:inline-flex dark:hover:text-[var(--color-accent)]\"\n >\n <ExternalIcon />\n </button>\n </span>\n </li>\n );\n}\n\n/* ── File detail (operations + content) ─────────────────────────────────── */\n\nfunction FileDetail({\n file,\n editLookup,\n onOpenFile,\n}: {\n file: ModifiedFileSummary;\n editLookup: EditLookup;\n onOpenFile: (filePath: string) => void;\n}) {\n const t = useT();\n const display = file.relativePath ?? file.filePath;\n const tail = display.split(/[\\\\/]+/).pop() ?? display;\n const changeType = fileChangeType(file);\n const { rows, newFile } = useMemo(() => buildFileRows(file, editLookup), [file, editLookup]);\n\n return (\n <div className=\"flex flex-col\">\n <div className=\"sticky top-0 z-10 border-b border-[var(--color-hairline)] bg-[var(--color-surface)]/95 px-5 py-3 backdrop-blur\">\n <div className=\"flex items-center gap-2\">\n <FileIcon errored={file.errorCount > 0} tone={changeType} />\n <h3\n className={\n 'min-w-0 flex-1 truncate font-mono text-[13.5px] font-medium ' +\n (file.errorCount > 0 ? 'text-[var(--color-danger)]' : changeToneClass(changeType))\n }\n title={file.filePath}\n >\n {tail}\n </h3>\n <button\n type=\"button\"\n onClick={() => onOpenFile(file.filePath)}\n title={t('session.modified.openFile')}\n className=\"inline-flex shrink-0 items-center gap-1 rounded-[var(--radius-control)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-2 py-0.5 font-mono text-[10px] uppercase tracking-[0.12em] text-[var(--color-fg-secondary)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent-ink)] dark:hover:text-[var(--color-accent)]\"\n >\n <ExternalIcon />\n {t('session.modified.openFile')}\n </button>\n </div>\n <p className=\"mt-0.5 truncate font-mono text-[10.5px] text-[var(--color-fg-faint)]\" title={file.filePath}>\n {file.relativePath ?? `${t('session.modified.absolutePath')} · ${file.filePath}`}\n </p>\n <div className=\"mt-2 flex flex-wrap items-center gap-1.5\">\n {file.editCount > 0 && <ToolChip name=\"Edit\" count={file.editCount} />}\n {file.writeCount > 0 && <ToolChip name=\"Write\" count={file.writeCount} />}\n {file.multiEditCount > 0 && <ToolChip name=\"MultiEdit\" count={file.multiEditCount} />}\n {file.notebookEditCount > 0 && <ToolChip name=\"NotebookEdit\" count={file.notebookEditCount} />}\n {file.errorCount > 0 && (\n <span className=\"rounded-[var(--radius-control)] border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-2 py-0.5 font-mono text-[10px] uppercase tracking-[0.1em] text-[var(--color-danger)]\">\n {t('session.modified.errorBadge', { n: file.errorCount })}\n </span>\n )}\n </div>\n </div>\n\n <div className=\"px-5 py-4\">\n <SplitDiff rows={rows} label={newFile ? t('session.modified.newContent') : undefined} />\n </div>\n </div>\n );\n}\n\nconst strOf = (v: unknown): string => (typeof v === 'string' ? v : '');\n\n/** 把一个文件在本会话里的全部操作合并成「单文件」统一视图行:左旧右新。\n * 首选带真实文件行号的 structuredPatch——把各次操作的 hunk 全收齐、按新文件行号排序,\n * 交给 rowsFromHunks 渲染(hunk 间的未改动区折叠成一行 gap),于是同一文件的多次改动\n * 拼成一份连贯的左右对照。注意:各次操作的行号是「该次操作落地后」坐标,跨操作不共享\n * 同一坐标系;同一处被多次改写会按操作先后逐次呈现(中间态),这是只有 diff、没有整文件\n * 快照时能做到的最忠实拼接。\n * 完全没有 structuredPatch(全新文件 create / 结果尚未落地的 pending)时,退回用 tool\n * 输入的原文按操作顺序拼接;newFile=本会话全程只有 Write/NotebookEdit 的创建写入。 */\nfunction buildFileRows(\n file: ModifiedFileSummary,\n editLookup: EditLookup,\n): { rows: UnifiedRow[]; newFile: boolean } {\n const hunks = file.operations.flatMap((op) => op.structuredPatch ?? []);\n if (hunks.length > 0) {\n const sorted = [...hunks].sort((a, b) => a.newStart - b.newStart);\n return { rows: rowsFromHunks(sorted), newFile: false };\n }\n\n const rows: UnifiedRow[] = [];\n let newFile = true;\n for (const op of file.operations) {\n const rec = asRecord(editLookup.get(op.toolUseId)?.input);\n if (op.toolName === 'Write') {\n rows.push(...rowsFromStrings('', strOf(rec.content)));\n } else if (op.toolName === 'NotebookEdit') {\n rows.push(...rowsFromStrings('', strOf(rec.new_source)));\n } else if (op.toolName === 'MultiEdit') {\n newFile = false;\n const edits = Array.isArray(rec.edits) ? rec.edits : [];\n for (const e of edits) {\n const er = asRecord(e);\n rows.push(...rowsFromStrings(strOf(er.old_string), strOf(er.new_string)));\n }\n } else {\n newFile = false;\n rows.push(...rowsFromStrings(strOf(rec.old_string), strOf(rec.new_string)));\n }\n }\n return { rows, newFile };\n}\n\n/** 分屏(左右两栏)一侧的一行。kind=empty 时本侧无对应行(对侧是纯增 / 纯删),\n * 渲染成留白占位行,保证两栏行高一一对齐。 */\ninterface SplitCell {\n no: number | null;\n kind: 'context' | 'del' | 'add' | 'empty';\n text: string | null;\n segs: Seg[] | null;\n}\n/** 分屏一行:pair=左右各一格;gap=折叠的未改动区间,两栏同高同文。 */\ninterface SplitRow {\n kind: 'pair' | 'gap';\n gap?: number;\n left?: SplitCell;\n right?: SplitCell;\n}\n\n/** 统一视图行 → 分屏行。rowsFrom* 在一个改动块里先发全部 del 再发全部 add,\n * 这里按序号把 del[x]↔add[x] 配成一行(多出的一侧用 empty 占位),未改动行左右同文。 */\nfunction toSplitRows(rows: UnifiedRow[]): SplitRow[] {\n const out: SplitRow[] = [];\n let i = 0;\n while (i < rows.length) {\n const r = rows[i]!;\n if (r.kind === 'gap') {\n out.push({ kind: 'gap', gap: r.gap ?? 0 });\n i++;\n continue;\n }\n if (r.kind === 'context') {\n out.push({\n kind: 'pair',\n left: { no: r.oldNo, kind: 'context', text: r.text, segs: null },\n right: { no: r.newNo, kind: 'context', text: r.text, segs: null },\n });\n i++;\n continue;\n }\n const dels: UnifiedRow[] = [];\n const adds: UnifiedRow[] = [];\n while (i < rows.length && rows[i]!.kind === 'del') dels.push(rows[i++]!);\n while (i < rows.length && rows[i]!.kind === 'add') adds.push(rows[i++]!);\n const max = Math.max(dels.length, adds.length);\n for (let x = 0; x < max; x++) {\n const d = dels[x];\n const a = adds[x];\n out.push({\n kind: 'pair',\n left: d\n ? { no: d.oldNo, kind: 'del', text: d.text, segs: d.segs }\n : { no: null, kind: 'empty', text: null, segs: null },\n right: a\n ? { no: a.newNo, kind: 'add', text: a.text, segs: a.segs }\n : { no: null, kind: 'empty', text: null, segs: null },\n });\n }\n }\n return out;\n}\n\n/** GitHub 风格的分屏 diff:左栏旧(含删除),右栏新(含新增),两栏行级对齐。\n * 两栏各自横向滚动;因每行 whitespace-pre 恒为一行高、左右行数相同,纵向天然对齐。 */\nfunction SplitDiff({ rows, label }: { rows: UnifiedRow[]; label?: string }) {\n if (rows.length === 0) return <EmptyBody />;\n const split = toSplitRows(rows);\n return (\n <div className=\"overflow-hidden rounded-lg border border-[var(--color-hairline)]\">\n {label && (\n <div className=\"border-b border-[var(--color-hairline)] bg-[var(--color-sunken)] px-3 py-1 font-mono text-[9.5px] uppercase tracking-[0.16em] text-[var(--color-fg-muted)]\">\n {label}\n </div>\n )}\n <div className=\"flex\">\n <div className=\"w-1/2 overflow-x-auto border-r border-[var(--color-hairline)]\">\n <div className=\"w-max min-w-full\">\n {split.map((r, i) => (\n <SplitLine key={i} row={r} side=\"left\" />\n ))}\n </div>\n </div>\n <div className=\"w-1/2 overflow-x-auto\">\n <div className=\"w-max min-w-full\">\n {split.map((r, i) => (\n <SplitLine key={i} row={r} side=\"right\" />\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction SplitLine({ row, side }: { row: SplitRow; side: 'left' | 'right' }) {\n const t = useT();\n if (row.kind === 'gap') {\n return (\n <div className=\"flex bg-[var(--color-sunken)] leading-[1.65] text-[var(--color-fg-faint)]\">\n <span className=\"w-[3em] shrink-0 select-none border-r border-[var(--color-hairline)] bg-[inherit] px-1.5 text-center font-mono text-[11px]\">\n ⋯\n </span>\n <span className=\"whitespace-nowrap px-3 font-mono text-[10px] italic tracking-[0.04em]\">\n {t('session.modified.linesOmitted', { n: row.gap ?? 0 })}\n </span>\n </div>\n );\n }\n const cell = side === 'left' ? row.left! : row.right!;\n const bg =\n cell.kind === 'del'\n ? 'bg-[var(--color-danger-soft)]'\n : cell.kind === 'add'\n ? 'bg-[var(--color-moss-soft)]'\n : cell.kind === 'empty'\n ? 'bg-[var(--color-sunken)]/40'\n : 'bg-[var(--color-surface)]';\n const marker = cell.kind === 'del' ? '−' : cell.kind === 'add' ? '+' : '';\n const markerColor =\n cell.kind === 'del'\n ? 'text-[var(--color-danger)]'\n : cell.kind === 'add'\n ? 'text-[var(--color-moss)]'\n : 'text-transparent';\n // 改动 token 的强调底色:在整行 -soft 底色上再叠一层更饱和的同色(GitHub 行内高亮)。\n const hl = cell.kind === 'del' ? 'bg-[var(--color-danger)]/25' : 'bg-[var(--color-moss)]/30';\n return (\n <div className={`flex leading-[1.65] ${bg}`}>\n <span className=\"w-[3em] shrink-0 select-none border-r border-[var(--color-hairline)] bg-[inherit] px-1.5 text-right font-mono text-[10px] tabular-nums text-[var(--color-fg-faint)]\">\n {cell.no ?? ''}\n </span>\n <span className={`w-4 shrink-0 select-none text-center font-mono text-[11.5px] ${markerColor}`}>\n {marker}\n </span>\n <div className=\"whitespace-pre pr-3 font-mono text-[11.5px] text-[var(--color-fg-primary)]\">\n {cell.segs && cell.segs.length > 0\n ? cell.segs.map((s, i) =>\n s.changed ? (\n <span key={i} className={hl}>\n {s.text}\n </span>\n ) : (\n <span key={i}>{s.text}</span>\n ),\n )\n : cell.text == null || cell.text === ''\n ? ' '\n : cell.text}\n </div>\n </div>\n );\n}\n\nfunction EmptyBody() {\n const t = useT();\n return (\n <p className=\"px-3 py-2.5 text-[12px] italic text-[var(--color-fg-muted)]\">\n {t('session.modified.noContent')}\n </p>\n );\n}\n\nfunction asRecord(x: unknown): Record<string, unknown> {\n return x && typeof x === 'object' ? (x as Record<string, unknown>) : {};\n}\n\nfunction ToolChip({ name, count }: { name: ModifiedFileToolName; count: number }) {\n return (\n <span className=\"rounded-[var(--radius-control)] border border-[var(--color-hairline-strong)] bg-[var(--color-surface)] px-2 py-0.5 font-mono text-[10px] uppercase tracking-[0.1em] text-[var(--color-fg-secondary)]\">\n {name}\n <span className=\"ml-1 tabular-nums text-[var(--color-fg-muted)]\">×{count}</span>\n </span>\n );\n}\n\n/* ── Icons ──────────────────────────────────────────────────────────────── */\n\nfunction Caret({ open }: { open: boolean }) {\n return (\n <svg\n width=\"9\"\n height=\"9\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={'shrink-0 text-[var(--color-fg-muted)] transition-transform ' + (open ? 'rotate-90' : '')}\n aria-hidden\n >\n <path d=\"M9 6l6 6-6 6\" />\n </svg>\n );\n}\n\nfunction FolderIcon({ open }: { open: boolean }) {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className=\"shrink-0 text-[var(--color-accent)]\" aria-hidden>\n {open ? (\n <path d=\"M3 7a2 2 0 0 1 2-2h3.5l2 2H19a2 2 0 0 1 2 2v1H6.5a2 2 0 0 0-1.9 1.4L3 18z\" />\n ) : (\n <path d=\"M3 7a2 2 0 0 1 2-2h3.5l2 2H19a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\" />\n )}\n </svg>\n );\n}\n\nfunction FileIcon({ errored, tone }: { errored?: boolean; tone?: FileChangeType }) {\n const color = errored\n ? 'text-[var(--color-danger)]'\n : tone\n ? changeToneClass(tone)\n : 'text-[var(--color-fg-muted)]';\n return (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={'shrink-0 ' + color}\n aria-hidden\n >\n <path d=\"M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z\" />\n <path d=\"M14 3v5h5\" />\n </svg>\n );\n}\n\nfunction TreeGlyph() {\n return (\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M3 5h7l2 2h9\" />\n <path d=\"M3 5v14h18V9\" />\n <path d=\"M8 13h8M8 16h5\" opacity=\"0.5\" />\n </svg>\n );\n}\n\nfunction ExternalIcon() {\n return (\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className=\"shrink-0\" aria-hidden>\n <path d=\"M14 4h6v6\" />\n <path d=\"M20 4l-9 9\" />\n <path d=\"M19 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5\" />\n </svg>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M6 6l12 12M18 6L6 18\" />\n </svg>\n );\n}\n","import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { AnimatePresence, motion } from 'motion/react';\nimport {\n useDeferredValue,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from 'react';\nimport { useNavigate, useParams, useSearchParams } from 'react-router-dom';\nimport Breadcrumbs, { BreadcrumbFolderIcon } from '../components/Breadcrumbs.tsx';\nimport DeleteDialog from '../components/DeleteDialog.tsx';\nimport { Loading } from '../components/Loading.tsx';\nimport MessageBubble, { WorkingIndicator } from '../components/MessageBubble.tsx';\nimport ModifiedFilesDrawer, { type EditLookup } from '../components/ModifiedFilesDrawer.tsx';\nimport {\n api,\n type Block,\n type Message,\n type ModifiedFilesResponse,\n type OpenFileResult,\n type ProjectSummary,\n type SessionDetail,\n type SessionSummary,\n} from '../lib/api.ts';\nimport {\n INTERRUPTED_MARKER_RE,\n MAX_SESSION_MESSAGES,\n RECENT_ACTIVITY_WINDOW_MIN,\n} from '../lib/constants.ts';\nimport { formatBytes, formatDateTime, formatRelativeTime } from '../lib/format.ts';\nimport { useT } from '../lib/i18n.ts';\nimport { fadeUpItem, staggerParent } from '../lib/motion.ts';\nimport { queryKeys } from '../lib/query-keys.ts';\n\ninterface IndexedMessage {\n message: Message;\n haystack: string;\n}\n\nconst INITIAL_WINDOW = 50;\nconst LOAD_STEP = 50;\n\n// Live tail: while a session is still being written, poll the detail endpoint so\n// new messages append on their own. \"Still being written\" = the session's lastAt\n// (max of latest record ts and file mtime) sits inside the same recent-activity\n// window the rest of the app uses for \"active\". Polling self-terminates: once the\n// file stops changing, lastAt goes stale and refetchInterval returns false.\nconst LIVE_POLL_INTERVAL_MS = 2000;\nconst LIVE_WINDOW_MS = RECENT_ACTIVITY_WINDOW_MIN * 60 * 1000;\n// Auto-follow only kicks in when the viewport is within this many px of the\n// bottom — so watching the tail follows new messages, but scrolling up to read\n// history is never yanked back down.\nconst BOTTOM_STICK_THRESHOLD_PX = 120;\n\nfunction isWithinLiveWindow(lastAt: string | null | undefined): boolean {\n if (!lastAt) return false;\n const ms = new Date(lastAt).getTime();\n if (Number.isNaN(ms)) return false;\n return Date.now() - ms < LIVE_WINDOW_MS;\n}\n\n// Mirror of the server's `lastTurnIncomplete` over the parsed message list: the\n// last turn is unfinished when the final record is a `user` message (Claude owes\n// a reply) that isn't an abort marker, or an `assistant` message that ends on a\n// pending `tool_use`. Combined with the live window this drives the \"working\" UI.\nfunction lastTurnIncomplete(messages: Message[]): boolean {\n const last = messages[messages.length - 1];\n if (!last) return false;\n if (last.type === 'assistant') {\n return last.blocks[last.blocks.length - 1]?.type === 'tool_use';\n }\n const text = last.blocks.find((b) => b.type === 'text');\n return !(text && INTERRUPTED_MARKER_RE.test(text.text));\n}\n\nexport default function SessionDetailRoute() {\n const t = useT();\n const navigate = useNavigate();\n const { projectId, sessionId } = useParams<{ projectId: string; sessionId: string }>();\n const [searchParams] = useSearchParams();\n const pid = projectId ?? '';\n const sid = sessionId ?? '';\n const urlFocus = searchParams.get('focus');\n const urlQuery = searchParams.get('q');\n\n const [showMeta, setShowMeta] = useState(false);\n const [onlyUser, setOnlyUser] = useState(false);\n const [onlyError, setOnlyError] = useState(false);\n const [query, setQuery] = useState('');\n const deferredQuery = useDeferredValue(query);\n const [windowSize, setWindowSize] = useState(INITIAL_WINDOW);\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [showModifiedDrawer, setShowModifiedDrawer] = useState(false);\n // Sticky masthead follower: while reading down the timeline, a compact\n // breadcrumb + card overlay pins above the (already sticky) search bar. The\n // overlay is absolutely positioned so revealing it never reflows the page;\n // `pinTop` reserves room above the search for it, `stuck` toggles its fade-in.\n const railRef = useRef<HTMLDivElement>(null);\n const followerRef = useRef<HTMLDivElement>(null);\n const [pinTop, setPinTop] = useState(0);\n const [stuck, setStuck] = useState(false);\n const urlAppliedRef = useRef<string | null>(null);\n const flashedKeyRef = useRef<string | null>(null);\n // Live-tail follow state: whether the reader is parked at the bottom, and the\n // message count from the previous render so we only follow genuine new arrivals.\n const stickToBottomRef = useRef(true);\n const prevMsgCountRef = useRef<number | null>(null);\n // Previous \"working\" flag, so the tail follows the moment the working indicator\n // appears (the indicator grows the page without bumping messageCount).\n const prevIsWorkingRef = useRef(false);\n\n useEffect(() => {\n setWindowSize(INITIAL_WINDOW);\n prevMsgCountRef.current = null;\n }, [pid, sid]);\n\n const { data, isLoading, error } = useQuery({\n queryKey: queryKeys.session(pid, sid),\n queryFn: () =>\n api<SessionDetail>(\n `/api/sessions/${encodeURIComponent(pid)}/${encodeURIComponent(sid)}`,\n ),\n enabled: !!pid && !!sid,\n // Re-read the jsonl on an interval while the session is live so newly written\n // messages stream in on their own. The function form is re-evaluated with a\n // fresh Date.now() after every poll, so it stops on its own once activity\n // stops — even when the response is byte-identical and never re-renders us.\n refetchInterval: (query) =>\n isWithinLiveWindow(query.state.data?.meta.lastAt) ? LIVE_POLL_INTERVAL_MS : false,\n });\n const isLive = isWithinLiveWindow(data?.meta.lastAt);\n // Recomputed off the polled message list every 2s, so the working indicator\n // appears while Claude generates and clears the moment its reply lands.\n const isWorking = isLive && !!data && lastTurnIncomplete(data.messages);\n\n const projectsQuery = useQuery({\n queryKey: queryKeys.projects(),\n queryFn: () => api<ProjectSummary[]>('/api/projects'),\n });\n const project = useMemo(\n () => projectsQuery.data?.find((p) => p.id === pid),\n [projectsQuery.data, pid],\n );\n\n const projectSessionsQuery = useQuery({\n queryKey: queryKeys.projectSessions(pid),\n queryFn: () => api<SessionSummary[]>(`/api/projects/${encodeURIComponent(pid)}/sessions`),\n enabled: !!pid,\n });\n const currentSummary = useMemo(\n () => projectSessionsQuery.data?.find((s) => s.id === sid) ?? null,\n [projectSessionsQuery.data, sid],\n );\n const deleteTooltip = !currentSummary\n ? projectSessionsQuery.isLoading\n ? t('common.loading')\n : t('session.action.deleteTooltipBlocked')\n : undefined;\n\n // Modified-files summary (aggregated from a full jsonl scan, server-side). Fetched\n // eagerly at route level so the masthead trigger can show the count; the drawer\n // reuses this same data instead of querying again.\n const modifiedFilesQuery = useQuery({\n queryKey: queryKeys.sessionModifiedFiles(pid, sid),\n queryFn: () =>\n api<ModifiedFilesResponse>(\n `/api/sessions/${encodeURIComponent(pid)}/${encodeURIComponent(sid)}/modified-files`,\n ),\n enabled: !!pid && !!sid,\n });\n const modifiedFiles = modifiedFilesQuery.data?.files ?? [];\n\n // 在系统默认程序里打开会话改过的某个文件(后端校验该路径属于本会话)。\n const openFileMutation = useMutation({\n mutationFn: (filePath: string) =>\n api<OpenFileResult>(\n `/api/sessions/${encodeURIComponent(pid)}/${encodeURIComponent(sid)}/open-file`,\n { method: 'POST', body: JSON.stringify({ filePath }) },\n ),\n onError: (err: Error) => {\n window.alert(t('session.modified.openFailed', { msg: err.message }));\n },\n });\n\n const indexed: IndexedMessage[] = useMemo(() => {\n if (!data) return [];\n return data.messages.map((message) => ({\n message,\n haystack: indexMessage(message),\n }));\n }, [data]);\n\n // tool_use id → { name, input } for the loaded messages, so the drawer can render\n // each edit's actual content (Write body / Edit diff) without a second request.\n const editLookup: EditLookup = useMemo(() => {\n const map: EditLookup = new Map();\n if (!data) return map;\n for (const m of data.messages) {\n for (const b of m.blocks) {\n if (b.type === 'tool_use' && b.id) map.set(b.id, { name: b.name, input: b.input });\n }\n }\n return map;\n }, [data]);\n\n // 时间线 tool_result 头部标注来源工具:toolUseId → 工具名(tool_use 与其\n // result 分属两条消息,需跨消息反查)。\n const toolNames = useMemo(() => {\n const m = new Map<string, string>();\n for (const [id, v] of editLookup) m.set(id, v.name);\n return m;\n }, [editLookup]);\n\n // 抽屉左栏的对话:默认隐去 meta/system 噪声行,留下真正的对话与工具调用。\n // 跳转目标都是 assistant 的 tool_use 消息,不在 meta 之列,过滤后仍可定位。\n const conversationMessages = useMemo(\n () => data?.messages.filter((m) => !m.isMeta) ?? [],\n [data],\n );\n\n const visibleMessages = useMemo(() => {\n let list = indexed;\n if (!showMeta) list = list.filter((m) => !m.message.isMeta);\n if (onlyUser) list = list.filter((m) => isUserTyped(m.message));\n if (onlyError) list = list.filter((m) => hasError(m.message));\n if (deferredQuery) {\n const q = deferredQuery.toLowerCase();\n list = list.filter((m) => m.haystack.includes(q));\n }\n return list;\n }, [indexed, showMeta, onlyUser, onlyError, deferredQuery]);\n\n const skipWindowing = !!deferredQuery || onlyUser || onlyError;\n const renderList = useMemo(() => {\n if (skipWindowing) return visibleMessages;\n return visibleMessages.slice(-windowSize);\n }, [visibleMessages, skipWindowing, windowSize]);\n\n const hasMoreEarlier = !skipWindowing && renderList.length < visibleMessages.length;\n\n useEffect(() => {\n if (!data) return;\n const key = `${sid}|${urlFocus ?? ''}|${urlQuery ?? ''}`;\n if (urlAppliedRef.current === key) return;\n urlAppliedRef.current = key;\n if (urlQuery) setQuery(urlQuery);\n if (urlFocus) {\n const target = data.messages.find((m) => m.uuid === urlFocus);\n if (target?.isMeta) setShowMeta(true);\n }\n }, [data, sid, urlFocus, urlQuery]);\n\n useEffect(() => {\n if (!urlFocus || !data || skipWindowing) return;\n const idx = visibleMessages.findIndex((m) => m.message.uuid === urlFocus);\n if (idx === -1) return;\n const needed = visibleMessages.length - idx;\n if (needed > windowSize) setWindowSize(needed);\n }, [urlFocus, visibleMessages, windowSize, skipWindowing, data]);\n\n useEffect(() => {\n if (!urlFocus || !data) return;\n const key = `${sid}|${urlFocus}`;\n if (flashedKeyRef.current === key) return;\n if (!renderList.some((m) => m.message.uuid === urlFocus)) return;\n flashedKeyRef.current = key;\n const rafId = requestAnimationFrame(() => {\n const el = document.querySelector<HTMLElement>(\n `[data-uuid=\"${CSS.escape(urlFocus)}\"]`,\n );\n if (!el) return;\n el.scrollIntoView({ block: 'center', behavior: 'smooth' });\n const flashTarget = el.closest('li') ?? el;\n flashTarget.classList.add('flash-focus');\n window.setTimeout(() => flashTarget.classList.remove('flash-focus'), 1300);\n });\n return () => cancelAnimationFrame(rafId);\n }, [urlFocus, renderList, data, sid]);\n\n // Track whether the reader is parked at the bottom of the page, so live appends\n // can follow the tail without hijacking an upward scroll through history.\n useEffect(() => {\n const onScroll = () => {\n const distance =\n document.documentElement.scrollHeight - (window.scrollY + window.innerHeight);\n stickToBottomRef.current = distance < BOTTOM_STICK_THRESHOLD_PX;\n };\n onScroll();\n window.addEventListener('scroll', onScroll, { passive: true });\n return () => window.removeEventListener('scroll', onScroll);\n }, []);\n\n // Measure the compact follower so the search bar can pin low enough to leave\n // room for it above (the follower sits flush atop the search; its own `pb-2`\n // is the gap). ResizeObserver keeps it correct across locale/title changes and\n // when the card mounts.\n useLayoutEffect(() => {\n const el = followerRef.current;\n if (!el) return;\n const measure = () => setPinTop(el.offsetHeight);\n measure();\n const ro = new ResizeObserver(measure);\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n // The rail is \"stuck\" once it has scrolled up to its pin line — that's when the\n // compact follower fades in above the search. Compared against the lg pin\n // offset; on smaller screens the follower is hidden so the exact value is moot.\n useEffect(() => {\n let frame = 0;\n const check = () => {\n frame = 0;\n const el = railRef.current;\n if (el) setStuck(el.getBoundingClientRect().top <= pinTop + 1);\n };\n const schedule = () => {\n if (!frame) frame = requestAnimationFrame(check);\n };\n check();\n window.addEventListener('scroll', schedule, { passive: true });\n window.addEventListener('resize', schedule, { passive: true });\n return () => {\n window.removeEventListener('scroll', schedule);\n window.removeEventListener('resize', schedule);\n if (frame) cancelAnimationFrame(frame);\n };\n }, [pinTop]);\n\n // When a live poll appends new messages and the reader is at the bottom — and not\n // mid-search or deep-linked to a specific message — follow the tail downward.\n useEffect(() => {\n const count = data?.meta.messageCount ?? null;\n const prev = prevMsgCountRef.current;\n prevMsgCountRef.current = count;\n if (prev === null || count === null || count <= prev) return;\n if (urlFocus || skipWindowing || !stickToBottomRef.current) return;\n const rafId = requestAnimationFrame(() =>\n window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' }),\n );\n return () => cancelAnimationFrame(rafId);\n }, [data?.meta.messageCount, urlFocus, skipWindowing]);\n\n // The working indicator appears below the last message without bumping\n // messageCount — follow the tail the moment it shows up so it stays in view.\n useEffect(() => {\n const startedWorking = isWorking && !prevIsWorkingRef.current;\n prevIsWorkingRef.current = isWorking;\n if (!startedWorking) return;\n if (urlFocus || skipWindowing || !stickToBottomRef.current) return;\n const rafId = requestAnimationFrame(() =>\n window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' }),\n );\n return () => cancelAnimationFrame(rafId);\n }, [isWorking, urlFocus, skipWindowing]);\n\n const projectTail = useMemo(() => {\n const cwd = project?.decodedCwd;\n if (!cwd) return pid.slice(-12);\n const parts = cwd.split(/[\\\\/]+/).filter(Boolean);\n return parts.at(-1) ?? cwd;\n }, [project, pid]);\n\n const sessionTitle = useMemo(() => {\n if (!data) return null;\n return data.meta.customTitle ?? data.meta.title;\n }, [data]);\n\n const queryClient = useQueryClient();\n const renameMutation = useMutation({\n mutationFn: (next: string) =>\n api<{ customTitle: string }>(\n `/api/sessions/${encodeURIComponent(pid)}/${encodeURIComponent(sid)}`,\n { method: 'PATCH', body: JSON.stringify({ customTitle: next }) },\n ),\n onSuccess: ({ customTitle }) => {\n // Patch caches synchronously so the read-only title doesn't flash the\n // pre-rename value while the background refetch is in flight.\n queryClient.setQueryData<SessionDetail>(queryKeys.session(pid, sid), (prev) =>\n prev ? { ...prev, meta: { ...prev.meta, customTitle } } : prev,\n );\n queryClient.setQueryData<SessionSummary[]>(queryKeys.projectSessions(pid), (prev) =>\n prev?.map((s) => (s.id === sid ? { ...s, customTitle } : s)),\n );\n void queryClient.invalidateQueries({ queryKey: queryKeys.session(pid, sid) });\n void queryClient.invalidateQueries({ queryKey: queryKeys.projectSessions(pid) });\n },\n });\n\n const taglineBranchPart = data?.meta.gitBranch\n ? t('session.tagline.branch', { branch: data.meta.gitBranch })\n : '';\n\n const crumbs = [\n { label: t('session.crumbProjects'), to: '/' },\n {\n label: projectTail,\n to: `/projects/${encodeURIComponent(pid)}`,\n mono: true,\n icon: <BreadcrumbFolderIcon />,\n },\n {\n label: sessionTitle ?? sid.slice(0, 8),\n mono: !sessionTitle,\n icon: <BreadcrumbFolderIcon />,\n },\n ];\n const compactTitle = sessionTitle ?? sid.slice(0, 12) + '…';\n\n return (\n <section>\n <Breadcrumbs items={crumbs} />\n\n {data && (\n <div className=\"surface-card mt-4 p-6\">\n <SessionMasthead\n sid={sid}\n isLive={isLive}\n isWorking={isWorking}\n title={sessionTitle}\n tagline={t('session.tagline', {\n started: formatRelativeTime(data.meta.firstAt),\n lastTouched: formatRelativeTime(data.meta.lastAt),\n branchPart: taglineBranchPart,\n })}\n firstAt={data.meta.firstAt}\n messageCount={data.meta.messageCount}\n bytes={data.meta.bytes}\n version={data.meta.version}\n branch={data.meta.gitBranch}\n editableValue={sessionTitle ?? ''}\n onTitleEdit={async (next) => {\n await renameMutation.mutateAsync(next);\n }}\n renameDisabled={currentSummary?.isLivePid === true}\n renameTooltip={\n currentSummary?.isLivePid === true\n ? t('session.action.renameTooltipLive', {\n pid: currentSummary.livePid ?? '?',\n })\n : undefined\n }\n onDelete={currentSummary ? () => setShowDeleteDialog(true) : undefined}\n deleteDisabled={!currentSummary}\n deleteTooltip={deleteTooltip}\n deleteLabel={t('session.action.delete')}\n onOpenModified={() => setShowModifiedDrawer(true)}\n modifiedCount={modifiedFiles.length}\n modifiedLoading={modifiedFilesQuery.isLoading}\n />\n </div>\n )}\n\n {showDeleteDialog && currentSummary && (\n <DeleteDialog\n projectId={pid}\n selected={[currentSummary]}\n onClose={() => setShowDeleteDialog(false)}\n onDeleted={(deletedIds) => {\n if (deletedIds.includes(sid)) {\n setShowDeleteDialog(false);\n navigate(`/projects/${encodeURIComponent(pid)}`, { replace: true });\n }\n }}\n />\n )}\n\n <AnimatePresence>\n {showModifiedDrawer && (\n <ModifiedFilesDrawer\n key=\"modified-files-drawer\"\n files={modifiedFiles}\n cwd={modifiedFilesQuery.data?.cwd ?? data?.meta.cwd ?? null}\n editLookup={editLookup}\n messages={conversationMessages}\n query={deferredQuery}\n isWorking={isWorking}\n loading={modifiedFilesQuery.isLoading}\n error={modifiedFilesQuery.error as Error | null}\n onOpenFile={(filePath) => openFileMutation.mutate(filePath)}\n onClose={() => setShowModifiedDrawer(false)}\n />\n )}\n </AnimatePresence>\n\n <div\n ref={railRef}\n className=\"sticky top-2 z-30 mt-6 lg:top-[var(--ccsm-pin-top)]\"\n style={{ '--ccsm-pin-top': `${pinTop}px` } as CSSProperties}\n >\n {/* Compact follower (lg+): the breadcrumb + a one-line card pinned above\n the search. Absolutely positioned so toggling it never reflows the\n timeline; it just fades in once the rail sticks. */}\n <div\n ref={followerRef}\n aria-hidden={!stuck}\n className={\n 'topbar-glass absolute inset-x-0 bottom-full pb-2 hidden flex-col gap-2 transition-opacity duration-200 lg:flex ' +\n (stuck ? 'opacity-100' : 'pointer-events-none opacity-0')\n }\n >\n <Breadcrumbs items={crumbs} />\n {data && (\n <CompactMasthead\n title={compactTitle}\n isLive={isLive}\n isWorking={isWorking}\n onOpenModified={() => setShowModifiedDrawer(true)}\n modifiedCount={modifiedFiles.length}\n modifiedLoading={modifiedFilesQuery.isLoading}\n onDelete={currentSummary ? () => setShowDeleteDialog(true) : undefined}\n deleteDisabled={!currentSummary}\n deleteTooltip={deleteTooltip}\n deleteLabel={t('session.action.delete')}\n />\n )}\n </div>\n\n <FilterLedger\n query={query}\n onQuery={setQuery}\n showMeta={showMeta}\n onShowMeta={setShowMeta}\n onlyUser={onlyUser}\n onOnlyUser={setOnlyUser}\n onlyError={onlyError}\n onOnlyError={setOnlyError}\n shown={renderList.length}\n total={visibleMessages.length}\n hasData={!!data}\n />\n </div>\n\n <div className=\"mt-6\">\n {data?.truncated && (\n <Admonition tone=\"warn\" className=\"mb-6\">\n {t('session.truncated', { n: MAX_SESSION_MESSAGES.toLocaleString() })}\n </Admonition>\n )}\n\n {isLoading && <Loading label={t('common.loadingSession')} />}\n {error && (\n <Admonition tone=\"danger\">\n {t('common.failedSession')}: {(error as Error).message}\n </Admonition>\n )}\n\n {data && visibleMessages.length === 0 && (\n <p className=\"mt-2 max-w-2xl font-display text-[15px] italic text-[var(--color-fg-muted)]\">\n {t('common.noMessagesMatch')}\n </p>\n )}\n\n {data && visibleMessages.length > 0 && (\n <ol className=\"border-t border-[var(--color-hairline-strong)]\">\n {hasMoreEarlier && (\n <li className=\"flex justify-center border-b border-[var(--color-hairline)] py-3\">\n <button\n type=\"button\"\n onClick={() =>\n setWindowSize((w) => Math.min(w + LOAD_STEP, visibleMessages.length))\n }\n className=\"rounded-[var(--radius-control)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-4 py-1.5 font-mono text-[11px] uppercase tracking-[0.14em] text-[var(--color-fg-secondary)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent-ink)] dark:hover:text-[var(--color-accent)]\"\n >\n {t('common.loadEarlier', {\n n: Math.min(LOAD_STEP, visibleMessages.length - renderList.length),\n })}\n </button>\n </li>\n )}\n\n <motion.div\n key={renderList.length === 0 ? 'empty' : 'list'}\n initial=\"hidden\"\n animate=\"show\"\n variants={staggerParent}\n >\n {renderList.map((m, i) => {\n const isMeta = m.message.isMeta;\n return (\n <motion.li\n key={m.message.uuid || m.message.ts || String(i)}\n variants={fadeUpItem}\n className={isMeta ? 'py-2' : 'py-3'}\n >\n <MessageBubble\n message={m.message}\n query={deferredQuery}\n toolNames={toolNames}\n />\n </motion.li>\n );\n })}\n </motion.div>\n\n {isWorking && !skipWindowing && <WorkingIndicator />}\n </ol>\n )}\n </div>\n\n {data && <ScrollToEdges />}\n </section>\n );\n}\n\n/* ─────────────────────────────────────────────────────────────────── */\n\nfunction SessionMasthead({\n sid,\n isLive,\n isWorking,\n title,\n tagline,\n firstAt,\n messageCount,\n bytes,\n version,\n branch,\n editableValue,\n onTitleEdit,\n renameDisabled,\n renameTooltip,\n onDelete,\n deleteDisabled,\n deleteTooltip,\n deleteLabel,\n onOpenModified,\n modifiedCount,\n modifiedLoading,\n}: {\n sid: string;\n isLive: boolean;\n isWorking: boolean;\n title: string | null;\n tagline: string;\n firstAt: string | null;\n messageCount: number;\n bytes: number;\n version: string | null;\n branch: string | null;\n editableValue: string;\n onTitleEdit: (next: string) => Promise<void>;\n renameDisabled?: boolean;\n renameTooltip?: string;\n onDelete?: () => void;\n deleteDisabled?: boolean;\n deleteTooltip?: string;\n deleteLabel?: string;\n onOpenModified: () => void;\n modifiedCount: number;\n modifiedLoading: boolean;\n}) {\n const t = useT();\n const dateline = formatDateline(firstAt);\n\n return (\n <header className=\"relative\">\n <div className=\"flex items-center justify-between gap-4 border-b border-[var(--color-hairline)] pb-3\">\n <div className=\"flex min-w-0 items-center gap-3 font-mono text-[10px] uppercase tracking-[0.22em] text-[var(--color-fg-muted)]\">\n <span className=\"text-[var(--color-accent)]\">●</span>\n <span>§ SESSION</span>\n {isWorking ? (\n <span\n title={t('session.working.tooltip')}\n className=\"inline-flex items-center gap-1.5 rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 normal-case tracking-[0.14em] text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\"\n >\n <span aria-hidden className=\"loading-dots text-[var(--color-accent)]\">\n <span />\n <span />\n <span />\n </span>\n {t('session.working')}\n </span>\n ) : isLive ? (\n <span\n title={t('session.live.tooltip')}\n className=\"inline-flex items-center gap-1.5 rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 normal-case tracking-[0.14em] text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\"\n >\n <span aria-hidden className=\"relative inline-flex h-1.5 w-1.5\">\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)] pulse-amber\" />\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)]\" />\n </span>\n {t('session.live')}\n </span>\n ) : null}\n <span className=\"hidden h-3 w-px bg-[var(--color-hairline-strong)] sm:inline-block\" />\n <span className=\"hidden truncate normal-case tracking-[0.05em] text-[var(--color-fg-faint)] sm:inline\">\n {sid}\n </span>\n {branch && (\n <>\n <span className=\"hidden h-3 w-px bg-[var(--color-hairline-strong)] md:inline-block\" />\n <span className=\"hidden truncate md:inline\">{branch}</span>\n </>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-3\">\n <div className=\"hidden font-mono text-[10px] uppercase tracking-[0.22em] tabular-nums text-[var(--color-fg-muted)] sm:block\">\n {dateline}\n </div>\n <button\n type=\"button\"\n onClick={onOpenModified}\n disabled={modifiedLoading}\n aria-label={t('session.modified.openAria')}\n className=\"inline-flex items-center gap-1.5 rounded-[var(--radius-control)] border border-[var(--color-hairline-strong)] bg-[var(--color-surface)] px-3 py-1 text-[11px] font-medium uppercase tracking-[0.14em] text-[var(--color-fg-secondary)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent-ink)] disabled:cursor-not-allowed disabled:opacity-40 dark:hover:text-[var(--color-accent)]\"\n >\n <FilesIcon /> {t('session.modified.title')}\n {modifiedCount > 0 && (\n <span className=\"rounded-full bg-[var(--color-accent-soft)] px-1.5 font-mono text-[10px] tabular-nums text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\n {modifiedCount}\n </span>\n )}\n </button>\n {(onDelete || deleteDisabled) && (\n <button\n type=\"button\"\n onClick={onDelete}\n disabled={deleteDisabled || !onDelete}\n title={deleteTooltip}\n aria-label={deleteLabel}\n className=\"inline-flex items-center gap-1.5 rounded-[var(--radius-control)] border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-3 py-1 text-[11px] font-medium uppercase tracking-[0.14em] text-[var(--color-danger)] transition hover:border-[var(--color-danger)] disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <TrashIcon /> {deleteLabel}\n </button>\n )}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-x-10 gap-y-6 pt-5 pb-2 lg:grid-cols-12\">\n <div className=\"lg:col-span-8\">\n <TitleSlot\n title={title ?? sid.slice(0, 12) + '…'}\n editableValue={editableValue}\n onTitleEdit={onTitleEdit}\n isFallback={!title}\n disabled={renameDisabled}\n disabledTooltip={renameTooltip}\n />\n </div>\n\n <div className=\"lg:col-span-4 lg:pt-3\">\n <p className=\"border-l-2 border-[var(--color-accent)] pl-4 font-display text-[15px] italic leading-[1.55] text-[var(--color-fg-secondary)]\">\n {tagline}\n </p>\n </div>\n </div>\n\n <div className=\"rule-dotted mt-6\" aria-hidden />\n <dl className=\"mt-3 flex flex-wrap items-baseline gap-x-8 gap-y-2\">\n <Fact label={t('session.meta.messages')} value={messageCount.toLocaleString()} />\n <Fact label={t('session.meta.size')} value={formatBytes(bytes)} />\n {version && <Fact label={t('session.meta.version')} value={version} />}\n <Fact label={t('session.meta.started')} value={formatDateTime(firstAt)} />\n </dl>\n </header>\n );\n}\n\n// Condensed masthead shown in the sticky follower while scrolling: a single row\n// matching the breadcrumb / search-bar height, keeping the live status, title,\n// and the \"Modified files\" + delete actions; everything else collapses away.\nfunction CompactMasthead({\n title,\n isLive,\n isWorking,\n onOpenModified,\n modifiedCount,\n modifiedLoading,\n onDelete,\n deleteDisabled,\n deleteTooltip,\n deleteLabel,\n}: {\n title: string;\n isLive: boolean;\n isWorking: boolean;\n onOpenModified: () => void;\n modifiedCount: number;\n modifiedLoading: boolean;\n onDelete?: () => void;\n deleteDisabled?: boolean;\n deleteTooltip?: string;\n deleteLabel?: string;\n}) {\n const t = useT();\n return (\n <div className=\"flex items-center gap-3 rounded-[var(--radius-input)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-4 sm:px-5 py-2 shadow-[var(--shadow-rise)]\">\n <StatusBeacon isLive={isLive} isWorking={isWorking} />\n <span className=\"min-w-0 flex-1 truncate font-display text-[15px] font-light tracking-[-0.01em] text-[var(--color-fg-primary)]\">\n {title}\n </span>\n <button\n type=\"button\"\n onClick={onOpenModified}\n disabled={modifiedLoading}\n aria-label={t('session.modified.openAria')}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-[var(--radius-control)] border border-[var(--color-hairline-strong)] bg-[var(--color-surface)] px-3 py-1 text-[11px] font-medium uppercase tracking-[0.14em] text-[var(--color-fg-secondary)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent-ink)] disabled:cursor-not-allowed disabled:opacity-40 dark:hover:text-[var(--color-accent)]\"\n >\n <FilesIcon /> {t('session.modified.title')}\n {modifiedCount > 0 && (\n <span className=\"rounded-full bg-[var(--color-accent-soft)] px-1.5 font-mono text-[10px] tabular-nums text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\n {modifiedCount}\n </span>\n )}\n </button>\n {(onDelete || deleteDisabled) && (\n <button\n type=\"button\"\n onClick={onDelete}\n disabled={deleteDisabled || !onDelete}\n title={deleteTooltip}\n aria-label={deleteLabel}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-[var(--radius-control)] border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-3 py-1 text-[11px] font-medium uppercase tracking-[0.14em] text-[var(--color-danger)] transition hover:border-[var(--color-danger)] disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <TrashIcon /> {deleteLabel}\n </button>\n )}\n </div>\n );\n}\n\n// Compact live/working/idle indicator for the condensed masthead — mirrors the\n// full masthead's beacon (animated dots while working, pulsing dot while live).\nfunction StatusBeacon({ isLive, isWorking }: { isLive: boolean; isWorking: boolean }) {\n if (isWorking) {\n return (\n <span aria-hidden className=\"loading-dots shrink-0 text-[var(--color-accent)]\">\n <span />\n <span />\n <span />\n </span>\n );\n }\n if (isLive) {\n return (\n <span aria-hidden className=\"relative inline-flex h-2 w-2 shrink-0\">\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)] pulse-amber\" />\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)]\" />\n </span>\n );\n }\n return <span aria-hidden className=\"shrink-0 text-[10px] text-[var(--color-accent)]\">●</span>;\n}\n\nconst MASTHEAD_TITLE_CLASS =\n 'font-display text-[clamp(1.5rem,3vw,2rem)] font-light leading-[1.15] tracking-[-0.018em] text-[var(--color-fg-primary)]';\n\nfunction TitleSlot({\n title,\n editableValue,\n onTitleEdit,\n isFallback,\n disabled,\n disabledTooltip,\n}: {\n title: ReactNode;\n editableValue: string;\n onTitleEdit: (next: string) => Promise<void>;\n isFallback: boolean;\n disabled?: boolean;\n disabledTooltip?: string;\n}) {\n const [editing, setEditing] = useState(false);\n const [draft, setDraft] = useState(editableValue);\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n useEffect(() => {\n if (!editing) setDraft(editableValue);\n }, [editing, editableValue]);\n\n useEffect(() => {\n if (editing) inputRef.current?.select();\n }, [editing]);\n\n function startEdit() {\n setDraft(editableValue);\n setError(null);\n setEditing(true);\n }\n\n async function commit() {\n const next = draft.trim();\n if (!next || next === editableValue) {\n setEditing(false);\n return;\n }\n setSubmitting(true);\n setError(null);\n try {\n await onTitleEdit(next);\n setEditing(false);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n if (editing) {\n return (\n <div>\n <input\n ref={inputRef}\n value={draft}\n disabled={submitting}\n onChange={(e) => {\n setDraft(e.target.value);\n if (error) setError(null);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n void commit();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n setEditing(false);\n setError(null);\n }\n }}\n onBlur={() => {\n if (!submitting && !error) {\n setEditing(false);\n }\n }}\n maxLength={200}\n className={\n MASTHEAD_TITLE_CLASS +\n ' w-full bg-transparent border-b border-[var(--color-accent)] outline-none focus:outline-none disabled:opacity-60'\n }\n />\n {error && <p className=\"mt-1 text-xs text-[var(--color-danger)]\">{error}</p>}\n </div>\n );\n }\n\n return (\n <div className=\"group flex items-baseline gap-3\">\n <h1 className={MASTHEAD_TITLE_CLASS + (isFallback ? ' font-mono' : '')}>\n {title}\n <span className=\"text-[var(--color-accent)]\">.</span>\n </h1>\n <button\n type=\"button\"\n onClick={startEdit}\n aria-label=\"Rename\"\n title={disabled ? disabledTooltip ?? 'Rename unavailable' : 'Rename'}\n disabled={disabled}\n className=\"flex-shrink-0 rounded-md p-1.5 text-[var(--color-fg-muted)] opacity-0 transition hover:bg-[var(--color-sunken)] hover:text-[var(--color-fg-primary)] focus:opacity-100 group-hover:opacity-100 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-[var(--color-fg-muted)] disabled:opacity-40 disabled:group-hover:opacity-40\"\n >\n <PencilIcon />\n </button>\n </div>\n );\n}\n\nfunction Fact({ label, value }: { label: string; value: ReactNode }) {\n return (\n <div className=\"flex items-baseline gap-2\">\n <dt className=\"eyebrow\">{label}</dt>\n <dd className=\"font-mono text-[12px] tabular-nums text-[var(--color-fg-primary)]\">\n {value}\n </dd>\n </div>\n );\n}\n\nfunction formatDateline(iso: string | null): string {\n if (!iso) return '—';\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '—';\n return d\n .toLocaleDateString('en-GB', {\n weekday: 'short',\n day: '2-digit',\n month: 'short',\n year: 'numeric',\n })\n .toUpperCase()\n .replace(/,/g, ' ·');\n}\n\n/* ─────────────────────────────────────────────────────────────────── */\n\nfunction FilterLedger({\n query,\n onQuery,\n showMeta,\n onShowMeta,\n onlyUser,\n onOnlyUser,\n onlyError,\n onOnlyError,\n shown,\n total,\n hasData,\n}: {\n query: string;\n onQuery: (v: string) => void;\n showMeta: boolean;\n onShowMeta: (v: boolean) => void;\n onlyUser: boolean;\n onOnlyUser: (v: boolean) => void;\n onlyError: boolean;\n onOnlyError: (v: boolean) => void;\n shown: number;\n total: number;\n hasData: boolean;\n}) {\n const t = useT();\n return (\n <div className=\"rounded-[var(--radius-input)] border border-[var(--color-hairline)] bg-[var(--color-surface)] px-4 sm:px-5 py-2.5 shadow-[var(--shadow-rise)]\">\n <div className=\"flex flex-wrap items-center gap-4\">\n <div className=\"flex flex-1 min-w-[14rem] items-center gap-2 border-b border-[var(--color-hairline)] py-1 transition focus-within:border-[var(--color-accent)]\">\n <SearchIcon className=\"text-[var(--color-fg-muted)]\" />\n <input\n type=\"search\"\n value={query}\n onChange={(e) => onQuery(e.target.value)}\n placeholder={t('common.searchPlaceholder')}\n className=\"w-full bg-transparent text-sm text-[var(--color-fg-primary)] placeholder:text-[var(--color-fg-faint)] focus:outline-none\"\n />\n </div>\n\n <span className=\"hidden h-4 w-px bg-[var(--color-hairline-strong)] sm:inline-block\" />\n\n <div className=\"flex items-center gap-4\">\n <ToggleSwitch\n checked={showMeta}\n onChange={onShowMeta}\n label={t('common.system')}\n />\n <ToggleSwitch\n checked={onlyUser}\n onChange={onOnlyUser}\n label={t('common.onlyUser')}\n />\n <ToggleSwitch\n checked={onlyError}\n onChange={onOnlyError}\n label={t('common.onlyError')}\n />\n </div>\n\n {hasData && (\n <>\n <span className=\"hidden h-4 w-px bg-[var(--color-hairline-strong)] sm:inline-block\" />\n <span className=\"font-mono text-[11px] tabular-nums text-[var(--color-fg-muted)]\">\n {t('session.shown', { shown, total })}\n </span>\n </>\n )}\n </div>\n </div>\n );\n}\n\nfunction ToggleSwitch({\n checked,\n onChange,\n label,\n}: {\n checked: boolean;\n onChange: (next: boolean) => void;\n label: string;\n}) {\n return (\n <label className=\"inline-flex cursor-pointer items-center gap-1.5\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={(e) => onChange(e.target.checked)}\n className=\"sr-only\"\n />\n <span\n aria-hidden\n className={\n 'font-mono text-[11px] uppercase tracking-[0.16em] transition ' +\n (checked\n ? 'text-[var(--color-accent)] underline underline-offset-[6px] decoration-[var(--color-accent)]/50'\n : 'text-[var(--color-fg-faint)] hover:text-[var(--color-fg-secondary)]')\n }\n >\n {label}\n </span>\n </label>\n );\n}\n\n/* ─────────────────────────────────────────────────────────────────── */\n\nfunction Admonition({\n tone,\n className = '',\n children,\n}: {\n tone: 'warn' | 'danger';\n className?: string;\n children: ReactNode;\n}) {\n const colors =\n tone === 'warn'\n ? 'border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\n : 'border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] text-[var(--color-danger)]';\n return (\n <div className={`rounded-[10px] border px-4 py-3 text-sm ${colors} ${className}`}>\n {children}\n </div>\n );\n}\n\n/* ─────────────────────────────────────────────────────────────────── */\n\nfunction isUserTyped(m: Message): boolean {\n if (m.type !== 'user') return false;\n if (m.blocks.length === 0) return true;\n return m.blocks.some((b) => b.type !== 'tool_result');\n}\n\nfunction hasError(m: Message): boolean {\n return m.blocks.some((b) => b.type === 'tool_result' && b.isError);\n}\n\nfunction indexMessage(message: Message): string {\n return message.blocks.map(blockText).join('\\n').toLowerCase();\n}\n\nfunction blockText(block: Block): string {\n switch (block.type) {\n case 'text':\n case 'thinking':\n return block.text;\n case 'tool_use':\n return `${block.name} ${JSON.stringify(block.input)}`;\n case 'tool_result':\n return block.content;\n case 'image':\n return '';\n default:\n return JSON.stringify(block.raw);\n }\n}\n\nfunction SearchIcon({ className = '' }: { className?: string }) {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n className={className}\n aria-hidden\n >\n <circle cx=\"11\" cy=\"11\" r=\"6.2\" />\n <path d=\"M20 20l-4.3-4.3\" />\n </svg>\n );\n}\n\nfunction TrashIcon() {\n return (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M3 6h18\" />\n <path d=\"M8 6V4.5A1.5 1.5 0 0 1 9.5 3h5A1.5 1.5 0 0 1 16 4.5V6\" />\n <path d=\"M5.5 6l1.1 13.2A1.5 1.5 0 0 0 8.1 20.5h7.8a1.5 1.5 0 0 0 1.5-1.3L18.5 6\" />\n </svg>\n );\n}\n\nfunction FilesIcon() {\n return (\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M3 5h6l2 2h10\" />\n <path d=\"M3 5v14h18V9H3\" />\n </svg>\n );\n}\n\nfunction PencilIcon() {\n return (\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M12 20h9\" />\n <path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z\" />\n </svg>\n );\n}\n\nconst EDGE_THRESHOLD = 320;\n\nfunction ScrollToEdges() {\n const t = useT();\n const [showTop, setShowTop] = useState(false);\n const [showBottom, setShowBottom] = useState(false);\n\n useEffect(() => {\n let frame = 0;\n const update = () => {\n frame = 0;\n const scrollY = window.scrollY;\n const viewport = window.innerHeight;\n const total = document.documentElement.scrollHeight;\n setShowTop(scrollY >= EDGE_THRESHOLD);\n setShowBottom(total - (scrollY + viewport) >= EDGE_THRESHOLD);\n };\n const schedule = () => {\n if (frame) return;\n frame = requestAnimationFrame(update);\n };\n update();\n window.addEventListener('scroll', schedule, { passive: true });\n window.addEventListener('resize', schedule, { passive: true });\n return () => {\n window.removeEventListener('scroll', schedule);\n window.removeEventListener('resize', schedule);\n if (frame) cancelAnimationFrame(frame);\n };\n }, []);\n\n if (!showTop && !showBottom) return null;\n\n const buttonClass =\n 'rounded-full border border-[var(--color-hairline)] bg-[var(--color-surface)] p-2.5 text-[var(--color-fg-secondary)] shadow-[var(--shadow-pop)] transition hover:border-[var(--color-accent)] hover:text-[var(--color-accent)]';\n\n return (\n <div className=\"fixed bottom-6 right-6 z-30 flex flex-col gap-2\">\n {showTop && (\n <button\n type=\"button\"\n aria-label={t('common.scrollToTop')}\n title={t('common.scrollToTop')}\n onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}\n className={buttonClass}\n >\n <ChevronIcon direction=\"up\" />\n </button>\n )}\n {showBottom && (\n <button\n type=\"button\"\n aria-label={t('common.scrollToBottom')}\n title={t('common.scrollToBottom')}\n onClick={() =>\n window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' })\n }\n className={buttonClass}\n >\n <ChevronIcon direction=\"down\" />\n </button>\n )}\n </div>\n );\n}\n\nfunction ChevronIcon({ direction }: { direction: 'up' | 'down' }) {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d={direction === 'up' ? 'M6 15l6-6 6 6' : 'M6 9l6 6 6-6'} />\n </svg>\n );\n}\n","import { lazy, Suspense, useCallback, useState } from 'react';\nimport { Route, Routes } from 'react-router-dom';\nimport { Loading } from './components/Loading.tsx';\nimport SearchModal from './components/SearchModal.tsx';\nimport Sidebar from './components/Sidebar.tsx';\nimport { useGlobalHotkey } from './lib/hotkeys.ts';\nimport { useT } from './lib/i18n.ts';\nimport ProjectDetail from './routes/ProjectDetail.tsx';\nimport ProjectsList from './routes/ProjectsList.tsx';\nimport SessionDetail from './routes/SessionDetail.tsx';\n\nconst DiskUsage = lazy(() => import('./routes/DiskUsage.tsx'));\nconst ProjectMemory = lazy(() => import('./routes/ProjectMemory.tsx'));\nconst ImportPage = lazy(() => import('./routes/ImportPage.tsx'));\n\nexport default function App() {\n const [searchOpen, setSearchOpen] = useState(false);\n const toggleSearch = useCallback(() => setSearchOpen((v) => !v), []);\n const openSearch = useCallback(() => setSearchOpen(true), []);\n const closeSearch = useCallback(() => setSearchOpen(false), []);\n useGlobalHotkey('mod+k', toggleSearch);\n\n return (\n <div className=\"flex min-h-dvh\">\n <Sidebar onSearchOpen={openSearch} />\n <main className=\"flex-1 min-w-0\">\n <div className=\"mx-auto w-full max-w-6xl px-5 py-8 sm:px-8 lg:px-12\">\n <Routes>\n <Route path=\"/\" element={<ProjectsList />} />\n <Route path=\"/projects/:projectId\" element={<ProjectDetail />} />\n <Route\n path=\"/projects/:projectId/memory\"\n element={\n <Suspense fallback={<RouteFallback />}>\n <ProjectMemory />\n </Suspense>\n }\n />\n <Route\n path=\"/projects/:projectId/sessions/:sessionId\"\n element={<SessionDetail />}\n />\n <Route\n path=\"/disk\"\n element={\n <Suspense fallback={<RouteFallback />}>\n <DiskUsage />\n </Suspense>\n }\n />\n <Route\n path=\"/import\"\n element={\n <Suspense fallback={<RouteFallback />}>\n <ImportPage />\n </Suspense>\n }\n />\n </Routes>\n </div>\n </main>\n <SearchModal open={searchOpen} onClose={closeSearch} />\n </div>\n );\n}\n\nfunction RouteFallback() {\n const t = useT();\n return (\n <div className=\"flex h-40 items-center justify-center\">\n <Loading label={t('common.loading')} className=\"items-center\" />\n </div>\n );\n}\n","import { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport { BrowserRouter } from 'react-router-dom';\nimport App from './App.tsx';\nimport './index.css';\n\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: { staleTime: 30_000, refetchOnWindowFocus: false },\n },\n});\n\nconst rootEl = document.getElementById('root');\nif (!rootEl) throw new Error('#root not found');\n\nReactDOM.createRoot(rootEl).render(\n <React.StrictMode>\n <QueryClientProvider client={queryClient}>\n <BrowserRouter>\n <App />\n </BrowserRouter>\n </QueryClientProvider>\n </React.StrictMode>,\n);\n"],"file":"assets/index-DTbWl1jb.js"}
|