@zzusp/ccsm 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/bin/cli.mjs +52 -52
- package/dist/assets/DiskUsage-CKhggLs5.js +2 -0
- package/dist/assets/DiskUsage-CKhggLs5.js.map +1 -0
- package/dist/assets/{ImportPage-b8NORa8b.js → ImportPage-wge4VhZ-.js} +2 -2
- package/dist/assets/{ImportPage-b8NORa8b.js.map → ImportPage-wge4VhZ-.js.map} +1 -1
- package/dist/assets/{ProjectMemory-aSV8UzQ9.js → ProjectMemory-Q4XX40j_.js} +2 -2
- package/dist/assets/{ProjectMemory-aSV8UzQ9.js.map → ProjectMemory-Q4XX40j_.js.map} +1 -1
- package/dist/assets/{charts-A5eNHLjX.js → charts-jxJqXXUr.js} +2 -2
- package/dist/assets/{charts-A5eNHLjX.js.map → charts-jxJqXXUr.js.map} +1 -1
- package/dist/assets/index-7aMrnHJG.js +7 -0
- package/dist/assets/index-7aMrnHJG.js.map +1 -0
- package/dist/assets/index-BOeI_J4B.css +1 -0
- package/dist/assets/{query-C1K1uQRu.js → query-CS7JQ86v.js} +2 -2
- package/dist/assets/{query-C1K1uQRu.js.map → query-CS7JQ86v.js.map} +1 -1
- package/dist/assets/{react-W0jzChlo.js → react-CPkiFScu.js} +10 -10
- package/dist/assets/{react-W0jzChlo.js.map → react-CPkiFScu.js.map} +1 -1
- package/dist/assets/{router-DfbutHY3.js → router-DwaHAh1G.js} +2 -2
- package/dist/assets/{router-DfbutHY3.js.map → router-DwaHAh1G.js.map} +1 -1
- package/dist/assets/vendor-Cs8vYp-N.js +27 -0
- package/dist/assets/vendor-Cs8vYp-N.js.map +1 -0
- package/dist/favicon.svg +7 -7
- package/dist/index.html +6 -6
- package/package.json +83 -72
- package/server/index.ts +130 -126
- package/server/lib/active-sessions.test.ts +119 -0
- package/server/lib/bundle.test.ts +182 -0
- package/server/lib/claude-paths.test.ts +126 -0
- package/server/lib/claude-paths.ts +19 -12
- package/server/lib/cleanup-suggestions.ts +131 -0
- package/server/lib/constants.ts +1 -0
- package/server/lib/delete.test.ts +244 -0
- package/server/lib/delete.ts +5 -16
- package/server/lib/disk-usage.ts +6 -8
- package/server/lib/export-import-bundle.test.ts +337 -0
- package/server/lib/modified-files.test.ts +280 -0
- package/server/lib/modified-files.ts +228 -0
- package/server/lib/open-folder.ts +22 -15
- package/server/lib/parse-jsonl.ts +35 -3
- package/server/lib/safe-id.test.ts +41 -0
- package/server/lib/safe-remove.test.ts +73 -0
- package/server/lib/safe-remove.ts +25 -0
- package/server/lib/scan.ts +103 -0
- package/server/lib/update.ts +67 -0
- package/server/lib/version.test.ts +39 -0
- package/server/lib/version.ts +117 -0
- package/server/routes/disk-cleanup.ts +54 -0
- package/server/routes/sessions.ts +49 -0
- package/server/routes/version.ts +34 -0
- package/shared/constants.ts +5 -0
- package/shared/types.ts +152 -0
- package/dist/assets/DiskUsage-Bq4VaoUA.js +0 -2
- package/dist/assets/DiskUsage-Bq4VaoUA.js.map +0 -1
- package/dist/assets/index-DLATR3tZ.js +0 -5
- package/dist/assets/index-DLATR3tZ.js.map +0 -1
- package/dist/assets/index-DLDtbkux.css +0 -1
- package/dist/assets/vendor-CH80ylbS.js +0 -19
- package/dist/assets/vendor-CH80ylbS.js.map +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","VersionNotice","setOpen","data","useQuery","Fragment","createPortal","AnimatePresence","VersionModal","info","mutation","useMutation","isPendingRef","result","hasUpdate","notes","published","updateDone","showUpdateAction","motion","UpdateOutcome","ExternalLink","Spinner","OutputBlock","CommandHint","CopyButton","output","copied","setCopied","href","NAV","FolderIcon","p","DiskIcon","ImportIcon","Sidebar","onSearchOpen","pathname","useLocation","Brand","MenuIcon","isActive","Link","Glyph","ITEM_BASE","Breadcrumbs","items","c","last","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","PREVIEW_CHARS","ToolUseBlock","block","inputJson","Caret","ToolResultBlock","long","visible","ThinkingBlock","hasText","common","MessageBubble","message","SystemMessage","b","AssistantMessage","UserMessage","isTool","borderClass","Avatar","Header","Blocks","align","model","ts","accent","blocks","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","m","Splitter","clientX","rect","clampWidth","FileDetail","node","TreeRow","nodeKey","getRect","onResize","dragging","available","max","fileChangeType","file","first","changeToneClass","type","root","display","segs","name","cur","materialize","folders","child","collapseChain","z","fileNodes","folder","only","nodes","walk","ns","depth","onToggleFolder","onSelectFile","indent","isCollapsed","isSelected","openLabel","changeType","nameTone","FileIcon","ExternalIcon","rows","newFile","buildFileRows","ToolChip","SplitDiff","strOf","hunks","op","sorted","rowsFromHunks","rec","asRecord","rowsFromStrings","edits","er","toSplitRows","r","dels","adds","EmptyBody","split","SplitLine","row","side","cell","bg","marker","markerColor","hl","prevNewEnd","h","gap","oldNo","newNo","lines","delStart","addStart","paired","wordSegments","oldStr","newStr","ops","diffOps","WORD_RE","oldLine","newLine","left","right","pushSeg","arr","changed","n","dp","j","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","deferredQuery","useDeferredValue","windowSize","setWindowSize","showDeleteDialog","setShowDeleteDialog","showModifiedDrawer","setShowModifiedDrawer","urlAppliedRef","flashedKeyRef","stickToBottomRef","prevMsgCountRef","prevIsWorkingRef","isLoading","isLive","projectSessionsQuery","currentSummary","deleteTooltip","modifiedFilesQuery","modifiedFiles","openFileMutation","filePath","indexed","indexMessage","map","conversationMessages","isUserTyped","skipWindowing","renderList","hasMoreEarlier","target","needed","rafId","flashTarget","onScroll","distance","sessionTitle","renameMutation","customTitle","taglineBranchPart","SessionMasthead","deletedIds","_b","FilterLedger","w","isMeta","ScrollToEdges","firstAt","messageCount","version","branch","renameDisabled","renameTooltip","deleteDisabled","deleteLabel","onOpenModified","modifiedCount","modifiedLoading","dateline","formatDateline","FilesIcon","Fact","MASTHEAD_TITLE_CLASS","isFallback","disabled","disabledTooltip","onQuery","onShowMeta","onOnlyUser","shown","total","hasData","ToggleSwitch","checked","onChange","blockText","EDGE_THRESHOLD","showTop","setShowTop","showBottom","setShowBottom","frame","update","scrollY","viewport","schedule","buttonClass","ChevronIcon","direction","DiskUsage","lazy","ProjectMemory","ImportPage","App","searchOpen","setSearchOpen","toggleSearch","openSearch","closeSearch","Routes","Route","Suspense","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/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 = {\r\n label: string;\r\n className?: string;\r\n};\r\n\r\nexport function Loading({ label, className }: LoadingProps) {\r\n return (\r\n <div\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n className={['flex flex-col gap-2.5', className].filter(Boolean).join(' ')}\r\n >\r\n <span className=\"eyebrow tabular-nums\">{label}</span>\r\n <span aria-hidden className=\"loading-rule\" />\r\n </div>\r\n );\r\n}\r\n\r\nexport function LoadingDots({ className }: { className?: string }) {\r\n return (\r\n <span\r\n aria-hidden\r\n className={['loading-dots', className].filter(Boolean).join(' ')}\r\n >\r\n <span />\r\n <span />\r\n <span />\r\n </span>\r\n );\r\n}\r\n\r\nexport default Loading;\r\n","const KB = 1024;\r\nconst MB = KB * 1024;\r\nconst GB = MB * 1024;\r\n\r\nexport function formatBytes(bytes: number): string {\r\n if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';\r\n if (bytes >= GB) return `${(bytes / GB).toFixed(2)} GB`;\r\n if (bytes >= MB) return `${(bytes / MB).toFixed(1)} MB`;\r\n if (bytes >= KB) return `${(bytes / KB).toFixed(1)} KB`;\r\n return `${bytes} B`;\r\n}\r\n\r\nexport function formatRelativeTime(iso: string | null): string {\r\n if (!iso) return '—';\r\n const t = new Date(iso).getTime();\r\n if (Number.isNaN(t)) return iso;\r\n const diffMs = Date.now() - t;\r\n const sec = Math.round(diffMs / 1000);\r\n if (sec < 60) return `${sec}s ago`;\r\n const min = Math.round(sec / 60);\r\n if (min < 60) return `${min}m ago`;\r\n const hr = Math.round(min / 60);\r\n if (hr < 48) return `${hr}h ago`;\r\n const day = Math.round(hr / 24);\r\n if (day < 30) return `${day}d ago`;\r\n const mo = Math.round(day / 30);\r\n if (mo < 12) return `${mo}mo ago`;\r\n const yr = Math.round(mo / 12);\r\n return `${yr}y ago`;\r\n}\r\n\r\nexport function formatDateTime(iso: string | null): string {\r\n if (!iso) return '—';\r\n const d = new Date(iso);\r\n if (Number.isNaN(d.getTime())) return iso;\r\n return d.toLocaleString();\r\n}\r\n","import { useEffect } from 'react';\r\n\r\nconst isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform);\r\n\r\nexport function useGlobalHotkey(combo: 'mod+k', handler: () => void): void {\r\n useEffect(() => {\r\n function onKey(e: KeyboardEvent) {\r\n if (combo !== 'mod+k') return;\r\n const mod = isMac ? e.metaKey : e.ctrlKey;\r\n if (!mod) return;\r\n if (e.key !== 'k' && e.key !== 'K') return;\r\n e.preventDefault();\r\n handler();\r\n }\r\n window.addEventListener('keydown', onKey);\r\n return () => window.removeEventListener('keydown', onKey);\r\n }, [combo, handler]);\r\n}\r\n\r\nexport const HOTKEY_HINT = isMac ? '⌘K' : 'Ctrl+K';\r\n","import { useCallback, useEffect, useState } from 'react';\r\n\r\nexport type Locale = 'en' | 'zh';\r\n\r\nconst STORAGE_KEY = 'locale';\r\n\r\nconst DICT = {\r\n en: {\r\n 'app.brand.title': 'Claude Sessions',\r\n 'app.brand.subtitle': 'local archive',\r\n 'app.brand.footnote': 'local · read-only by default',\r\n 'nav.workspace': 'Workspace',\r\n 'nav.projects': 'Projects',\r\n 'nav.disk': 'Disk usage',\r\n 'nav.import': 'Import',\r\n 'nav.theme': 'Theme',\r\n 'nav.language': 'Language',\r\n 'nav.toggleNav': 'Toggle navigation',\r\n 'nav.closeNav': 'Close navigation',\r\n\r\n 'version.label': 'Version',\r\n 'version.badge.new': 'Update {{v}}',\r\n 'version.upToDate': 'Up to date',\r\n 'version.checkFailed': 'Update check failed',\r\n 'version.modal.eyebrowUpdate': 'Update available',\r\n 'version.modal.eyebrowLatest': 'Version',\r\n 'version.modal.titleUpdate': 'A new version is available',\r\n 'version.modal.titleLatest': \"You're on the latest version\",\r\n 'version.current': 'Current',\r\n 'version.latest': 'Latest',\r\n 'version.published': 'Released {{date}}',\r\n 'version.notes': \"What's new\",\r\n 'version.notesEmpty': 'No release notes were provided for this release.',\r\n 'version.btn.update': 'Update now',\r\n 'version.btn.updating': 'Updating…',\r\n 'version.btn.releasePage': 'Release page',\r\n 'version.btn.repo': 'Repository',\r\n 'version.btn.close': 'Close',\r\n 'version.update.eyebrow': 'Result',\r\n 'version.update.successTitle': 'Update complete',\r\n 'version.update.success': 'Updated to {{v}}. Restart ccsm to load the new version.',\r\n 'version.update.failTitle': 'Update failed',\r\n 'version.update.fail':\r\n 'npm could not complete the update — see the output below, or run the command manually.',\r\n 'version.update.command': 'Or run manually',\r\n 'version.update.output': 'Command output',\r\n\r\n 'common.loading': 'loading',\r\n 'common.scanning': 'scanning ~/.claude/projects/…',\r\n 'common.computing': 'computing…',\r\n 'common.readingSessions': 'reading sessions…',\r\n 'common.loadingSession': 'loading session…',\r\n 'common.noData': 'no data',\r\n 'common.entries': 'entries',\r\n 'common.entry': 'entry',\r\n 'common.selectAll': 'select all',\r\n 'common.deselectAll': 'deselect all',\r\n 'common.cancel': 'Cancel',\r\n 'common.done': 'Done',\r\n 'common.expand': 'expand',\r\n 'common.collapse': 'collapse',\r\n 'common.system': 'system',\r\n 'common.onlyUser': 'only me',\r\n 'common.missing': 'missing',\r\n 'common.noSessions': 'No sessions in this project.',\r\n 'common.noProjects': 'No projects found.',\r\n 'common.noMessagesMatch': 'No messages match.',\r\n 'common.failedProjects': 'Failed to load projects',\r\n 'common.failedSessions': 'Failed to load sessions',\r\n 'common.failedSession': 'Failed to load',\r\n 'common.failedMemory': 'Failed to load memory',\r\n 'common.failed': 'Failed',\r\n 'common.searchPlaceholder': 'Search this session…',\r\n 'common.loadEarlier': 'Load earlier (+{{n}})',\r\n 'common.scrollToTop': 'Jump to top',\r\n 'common.scrollToBottom': 'Jump to bottom',\r\n\r\n 'projects.title': 'Projects',\r\n 'projects.tagline':\r\n \"Every directory you've touched with Claude Code, sorted by recency. Open one to inspect or prune its sessions.\",\r\n 'projects.indexHeading': 'Index',\r\n 'projects.stat.projects': 'Projects',\r\n 'projects.stat.sessions': 'Sessions',\r\n 'projects.stat.onDisk': 'On disk',\r\n 'projects.card.eyebrow': 'project',\r\n 'projects.card.sessions': 'Sessions',\r\n 'projects.card.onDisk': 'On disk',\r\n 'projects.card.lastSeen': 'Last seen',\r\n 'projects.warn.rootMissing':\r\n \"Claude root {{root}} doesn't exist on this machine — nothing to display.\",\r\n\r\n 'project.eyebrow': 'Project',\r\n 'project.warn.missingDir': 'Directory missing on disk',\r\n 'project.action.openFolder': 'Open folder',\r\n 'project.action.openFolderTooltipMissing': 'Directory no longer exists on disk',\r\n 'project.action.openFolderFailed': 'Failed to open folder: {{msg}}',\r\n 'project.action.delete': 'Delete · {{n}}',\r\n 'project.action.select': 'Select',\r\n 'project.action.exitSelect': 'Exit',\r\n 'project.bulk.selectedCount': '{{n}} selected',\r\n 'project.bulk.deleteCta': 'Delete selected',\r\n 'project.bulk.deletePending': 'Deleting {{i}}/{{total}}…',\r\n 'project.bulk.resultOk': 'Deleted {{ok}}',\r\n 'project.bulk.resultSkipped': 'skipped {{n}}',\r\n 'project.bulk.resultFailed': 'failed {{n}}',\r\n 'project.bulk.viewDetail': 'Details',\r\n 'project.bulk.hideDetail': 'Hide',\r\n 'project.bulk.detailSkipped': 'Skipped',\r\n 'project.bulk.detailFailed': 'Failed',\r\n 'project.bulk.dismiss': 'Dismiss',\r\n 'project.meta.sessions': 'Sessions',\r\n 'project.meta.onDisk': 'On disk',\r\n 'project.meta.working': 'Working',\r\n 'project.meta.live': 'Live',\r\n 'project.meta.recent': 'Recent',\r\n 'project.heading': 'Sessions',\r\n 'project.col.title': 'Title',\r\n 'project.col.msgs': 'Msgs',\r\n 'project.col.last': 'Last',\r\n 'project.col.size': 'Size',\r\n 'project.col.status': 'Status',\r\n\r\n 'session.crumbProjects': 'Projects',\r\n 'session.action.delete': 'Delete',\r\n 'session.action.deleteTooltipBlocked': 'Session metadata not available yet',\r\n 'session.action.rename': 'Rename',\r\n 'session.action.renameTooltipLive': 'Live PID {{pid}} owns this session — close the running claude first',\r\n 'session.tagline':\r\n 'Started {{started}} · last touched {{lastTouched}}{{branchPart}}',\r\n 'session.tagline.branch': ' · branch {{branch}}',\r\n 'session.meta.messages': 'Messages',\r\n 'session.meta.size': 'Size',\r\n 'session.meta.version': 'Version',\r\n 'session.meta.started': 'Started',\r\n 'session.truncated': 'Session truncated to first {{n}} messages.',\r\n 'session.shown': '{{shown}} / {{total}}',\r\n 'session.live': 'Live',\r\n 'session.live.tooltip': \"Auto-updating — new messages stream in as they're written\",\r\n 'session.working': 'Working',\r\n 'session.working.tooltip': 'Claude is processing this turn — generating a reply or running a tool',\r\n 'session.working.indicator': 'Claude is working…',\r\n\r\n 'session.modified.title': 'Modified files',\r\n 'session.modified.empty': 'No files were modified in this session.',\r\n 'session.modified.loading': 'scanning for file edits…',\r\n 'session.modified.failed': 'Failed to load modified files',\r\n 'session.modified.count': '{{n}} file',\r\n 'session.modified.countPlural': '{{n}} files',\r\n 'session.modified.openAria': 'Open modified files',\r\n 'session.modified.close': 'Close',\r\n 'session.modified.openFile': 'open file',\r\n 'session.modified.openFailed': 'Failed to open file: {{msg}}',\r\n 'session.modified.col.file': 'Files',\r\n 'session.modified.col.conversation': 'Conversation',\r\n 'session.modified.collapseAll': 'collapse all',\r\n 'session.modified.expandAll': 'expand all',\r\n 'session.modified.selectFile': 'Select a file to view its changes in this session.',\r\n 'session.modified.errorBadge': '{{n}} errored',\r\n 'session.modified.absolutePath': 'absolute path',\r\n 'session.modified.jump': 'Jump to',\r\n 'session.modified.opErrored': 'errored',\r\n 'session.modified.opPending': 'pending',\r\n 'session.modified.newContent': 'new content',\r\n 'session.modified.linesOmitted': '{{n}} unchanged lines',\r\n 'session.modified.editN': 'edit {{n}}',\r\n 'session.modified.contentUnavailable':\r\n 'Edit content unavailable (session truncated past this point).',\r\n 'session.modified.noContent': 'No content recorded for this operation.',\r\n 'session.modified.showEarlier': 'show {{n}} earlier messages',\r\n 'session.modified.added': 'added',\r\n 'session.modified.modified': 'modified',\r\n\r\n 'message.role.you': 'You',\r\n 'message.role.claude': 'Claude',\r\n 'message.role.tool': 'Tool',\r\n 'message.role.system': 'system',\r\n\r\n 'tool.use': 'tool',\r\n 'tool.result': 'tool result',\r\n 'tool.error': 'tool error',\r\n 'tool.thinking': 'thinking',\r\n 'tool.thinkingEncrypted': 'Content is encrypted, no readable text.',\r\n 'tool.image': 'image',\r\n\r\n 'status.working': 'working',\r\n 'status.live': 'live · pid {{pid}}',\r\n 'status.recent': 'recent',\r\n 'status.idle': 'idle',\r\n 'status.tooltip.working': 'Claude is actively processing this session right now',\r\n 'status.tooltip.live': 'PID {{pid}} is alive and registered for this session',\r\n 'status.tooltip.recent': 'jsonl modified within the last {{n}} minutes',\r\n 'status.tooltip.idle': 'idle — safe to delete',\r\n\r\n 'disk.tagline':\r\n \"How `~/.claude/` is using your disk: which projects carry the most weight, and which sessions are heaviest.\",\r\n 'disk.title': 'Disk usage',\r\n 'disk.meta.total': 'Total',\r\n 'disk.meta.projects': 'Projects',\r\n 'disk.meta.sessions': 'Sessions',\r\n 'disk.stat.total': 'Total on disk',\r\n 'disk.stat.acrossProjects': 'across {{n}} projects',\r\n 'disk.stat.sessions': 'Sessions',\r\n 'disk.stat.largest': 'largest {{size}}',\r\n 'disk.stat.months': 'Months tracked',\r\n 'disk.composition.title': 'Composition',\r\n 'disk.composition.subtitle': 'bytes per project',\r\n 'disk.composition.total': 'Total',\r\n 'disk.cadence.title': 'Cadence',\r\n 'disk.cadence.subtitle': 'MB per month',\r\n 'disk.heaviest.title': 'Heaviest sessions',\r\n 'disk.heaviest.top': 'top {{n}}',\r\n 'disk.col.num': '#',\r\n 'disk.col.title': 'Title',\r\n 'disk.col.project': 'Project',\r\n 'disk.col.last': 'Last',\r\n 'disk.col.size': 'Size',\r\n\r\n 'cleanup.title': 'Cleanup suggestions',\r\n 'cleanup.tagline':\r\n 'Things worth pruning. Review each row, then act — nothing here runs automatically.',\r\n 'cleanup.section.largeSessions': 'Largest sessions',\r\n 'cleanup.section.largeSessions.hint': 'Top {{n}} by total bytes on disk',\r\n 'cleanup.section.orphanFileHistory': 'Orphan file-history',\r\n 'cleanup.section.orphanFileHistory.hint':\r\n 'file-history/<sid>/ folders with no surviving session',\r\n 'cleanup.section.orphanSessionEnv': 'Orphan session-env',\r\n 'cleanup.section.orphanSessionEnv.hint':\r\n 'session-env/<sid>/ folders with no surviving session',\r\n 'cleanup.empty.largeSessions': 'No sessions on disk yet.',\r\n 'cleanup.empty.orphan': 'No orphans — nothing to prune.',\r\n 'cleanup.col.session': 'Session',\r\n 'cleanup.col.project': 'Project',\r\n 'cleanup.col.last': 'Last',\r\n 'cleanup.col.size': 'Size',\r\n 'cleanup.col.sid': 'Session id',\r\n 'cleanup.col.actions': 'Actions',\r\n 'cleanup.action.view': 'View',\r\n 'cleanup.action.delete': 'Delete',\r\n 'cleanup.action.deleting': 'Deleting…',\r\n 'cleanup.confirm.title': 'Delete orphan?',\r\n 'cleanup.confirm.body':\r\n 'This removes {{kind}}/{{sid}}/ ({{size}}) permanently. The session jsonl is already gone, so nothing else is affected.',\r\n 'cleanup.confirm.confirm': 'Delete',\r\n 'cleanup.confirm.cancel': 'Cancel',\r\n 'cleanup.failed': 'Failed: {{msg}}',\r\n\r\n 'memory.title': 'Memory',\r\n 'memory.empty': 'No memory recorded yet for this project.',\r\n 'memory.action.open': 'Memory',\r\n 'memory.action.openCount': 'Memory · {{n}}',\r\n 'memory.meta.entries': 'Entries',\r\n 'memory.meta.types': 'Types',\r\n 'memory.meta.lastUpdate': 'Last update',\r\n 'memory.type.user': 'User',\r\n 'memory.type.feedback': 'Feedback',\r\n 'memory.type.project': 'Project',\r\n 'memory.type.reference': 'Reference',\r\n 'memory.type.other': 'Untyped',\r\n 'memory.loading': 'reading memory…',\r\n 'memory.search.placeholder': 'Search title, hook, or body…',\r\n 'memory.search.hint': 'Press {{hotkey}} to focus',\r\n 'memory.filter.all': 'All',\r\n 'memory.sort.label': 'Sort',\r\n 'memory.sort.index': 'MEMORY.md order',\r\n 'memory.sort.recent': 'Recent first',\r\n 'memory.sort.name': 'Name A→Z',\r\n 'memory.sort.size': 'Largest first',\r\n 'memory.list.noResults': 'No entries match.',\r\n 'memory.list.count': '{{n}} of {{total}}',\r\n 'memory.cover.title': 'Memory index',\r\n 'memory.cover.subtitle': 'MEMORY.md',\r\n 'memory.cover.viewRaw': 'view raw',\r\n 'memory.cover.showAll': 'Show all ({{n}})',\r\n 'memory.cover.collapse': 'Collapse',\r\n 'memory.cover.missingTooltip': 'No matching entry file',\r\n 'memory.cover.orphan': 'Not in index',\r\n 'memory.empty.title': 'No memory yet',\r\n 'memory.empty.body':\r\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/.',\r\n 'memory.empty.orphanIndex': 'The index references {{n}} entries, but none of them have memory files yet.',\r\n 'memory.section.untyped': 'Untyped',\r\n 'memory.drawer.close': 'Close',\r\n 'memory.drawer.indexHook': 'Index hook',\r\n 'memory.drawer.notFound.title': 'Entry not found',\r\n 'memory.drawer.notFound.body':\r\n 'The selected memory file may have been removed. It might still appear in the curated index above.',\r\n 'memory.raw.title': 'MEMORY.md',\r\n 'memory.raw.subtitle': 'Curated index — raw source',\r\n\r\n 'delete.eyebrow.confirm': 'Confirm deletion',\r\n 'delete.eyebrow.result': 'Result',\r\n 'delete.title.confirm': 'Delete sessions',\r\n 'delete.title.result': 'Sessions removed',\r\n 'delete.summary':\r\n '{{n}} will be removed · {{skipped}} skipped · ~{{free}} to free',\r\n 'delete.skipped.heading': 'These {{n}} will be skipped:',\r\n 'delete.skipped.reasonLive': 'live PID {{pid}}',\r\n 'delete.skipped.reasonRecent': 'modified within last {{n}} minutes',\r\n 'delete.skipped.reasonUnknown': 'unknown',\r\n 'delete.success':\r\n 'Deleted {{n}} session(s). {{free}} freed · {{lines}} history lines removed',\r\n 'delete.btn.confirm': 'Delete {{n}} {{label}}',\r\n 'delete.btn.confirmPending': 'Deleting…',\r\n 'delete.label.session': 'session',\r\n 'delete.label.sessions': 'sessions',\r\n 'delete.close': 'Close',\r\n\r\n 'deleteProject.row.action': 'Delete project',\r\n 'deleteProject.eyebrow.confirm': 'Confirm deletion',\r\n 'deleteProject.eyebrow.result': 'Result',\r\n 'deleteProject.title.confirm': 'Delete project',\r\n 'deleteProject.title.result': 'Project removed',\r\n 'deleteProject.summary':\r\n '{{n}} session(s) · ~{{free}} to free · directory will be removed',\r\n 'deleteProject.warning':\r\n 'This removes every session in {{cwd}} along with its history.jsonl entries. Cannot be undone.',\r\n 'deleteProject.blocked.heading':\r\n '{{n}} session(s) are live or were modified in the last 5 minutes — nothing will be deleted. Try again later.',\r\n 'deleteProject.success':\r\n 'Removed {{n}} session(s) and the project directory. {{free}} freed · {{lines}} history lines removed',\r\n 'deleteProject.successKept':\r\n 'Removed {{n}} session(s). {{free}} freed · {{lines}} history lines removed · directory kept',\r\n 'deleteProject.btn.confirm': 'Delete project',\r\n 'deleteProject.btn.confirmPending': 'Deleting…',\r\n\r\n 'search.action.open': 'Search sessions',\r\n 'search.placeholder': 'Search across all sessions…',\r\n 'search.empty': 'Type at least 2 characters to search across every session on disk.',\r\n 'search.refineQuery': 'Type a longer query to refine the search.',\r\n 'search.scanning': 'scanning…',\r\n 'search.noResults': 'No matches.',\r\n 'search.error': 'Search failed: {{msg}}',\r\n 'search.moreInSession': '+more in this session',\r\n 'search.moreSessions': 'Showing first {{n}} sessions — refine your query for more.',\r\n 'search.summary': '{{matched}} session(s) · scanned {{scanned}} · {{ms}}ms',\r\n 'search.shortcut': '{{hint}} to open',\r\n 'search.escapeHint': 'esc to close',\r\n 'search.kindText': 'text',\r\n 'search.kindToolUse': 'tool',\r\n 'search.kindToolResult': 'tool result',\r\n 'search.kindThinking': 'thinking',\r\n 'search.role.user': 'You',\r\n 'search.role.assistant': 'Claude',\r\n\r\n 'export.action': 'Export',\r\n 'export.eyebrow.confirm': 'Share across devices',\r\n 'export.eyebrow.result': 'Result',\r\n 'export.title.confirm': 'Export project',\r\n 'export.title.result': 'Export complete',\r\n 'export.summary': 'Bundle {{n}} session(s) + memory into a portable folder.',\r\n 'export.destLabel': 'Destination folder',\r\n 'export.destPlaceholder': 'e.g. D:\\\\sync\\\\claude-shared',\r\n 'export.destHint':\r\n 'An absolute path outside ~/.claude. Drop it into a git repo or cloud drive to sync.',\r\n 'export.privacy':\r\n 'This writes your conversations and memory outside the app. If you push the folder to a remote, those contents leave your machine.',\r\n 'export.btn.confirm': 'Export',\r\n 'export.btn.pending': 'Exporting…',\r\n 'export.success':\r\n 'Exported {{sessions}} session(s) · {{memory}} memory file(s) · {{lines}} history line(s)',\r\n 'export.successDest': 'Written to {{dest}}',\r\n 'export.close': 'Close',\r\n\r\n 'import.title': 'Import bundle',\r\n 'import.tagline':\r\n \"Bring a project's sessions and memory from another device. Paths are remapped to this machine.\",\r\n 'import.bundleLabel': 'Bundle folder',\r\n 'import.bundlePlaceholder': 'Path to an exported bundle',\r\n 'import.btn.load': 'Load bundle',\r\n 'import.btn.recheck': 'Re-check',\r\n 'import.btn.loading': 'Loading…',\r\n 'import.source': 'From {{platform}} · {{cwd}}',\r\n 'import.targetLabel': 'Import into (this device)',\r\n 'import.targetHint':\r\n 'Absolute path on this machine. Sessions are associated with this folder.',\r\n 'import.targetId': 'Project id: {{id}}',\r\n 'import.suggestions': 'Suggestions',\r\n 'import.suggestion.existing-project': 'existing project',\r\n 'import.suggestion.original-path': 'original path',\r\n 'import.suggestion.same-basename': 'same folder name',\r\n 'import.policyLabel': 'If a session already exists',\r\n 'import.policy.skip': 'Skip',\r\n 'import.policy.overwrite-if-newer': 'Overwrite if newer',\r\n 'import.policy.keep-both': 'Keep both',\r\n 'import.sessions.heading': 'Sessions',\r\n 'import.sessions.empty': 'No sessions in this bundle.',\r\n 'import.action.create': 'create',\r\n 'import.action.overwrite': 'overwrite',\r\n 'import.action.keep-both': 'keep both',\r\n 'import.action.skip': 'skip',\r\n 'import.memory.heading': 'Memory',\r\n 'import.memory.create': 'create',\r\n 'import.memory.skip': 'identical',\r\n 'import.memory.conflict': 'conflict → {{name}}',\r\n 'import.history': '{{n}} history line(s) to add',\r\n 'import.btn.commit': 'Import',\r\n 'import.btn.committing': 'Importing…',\r\n 'import.result.title': 'Import complete',\r\n 'import.result.summary':\r\n 'Imported {{n}} session(s) · {{skipped}} skipped · {{memory}} memory file(s) · {{lines}} history line(s)',\r\n 'import.result.viewProject': 'Open imported project',\r\n 'import.empty': 'Enter a bundle folder path to begin.',\r\n },\r\n\r\n zh: {\r\n 'app.brand.title': 'Claude 会话',\r\n 'app.brand.subtitle': '本地归档',\r\n 'app.brand.footnote': '本地 · 默认只读',\r\n 'nav.workspace': '工作区',\r\n 'nav.projects': '项目',\r\n 'nav.disk': '磁盘占用',\r\n 'nav.import': '导入',\r\n 'nav.theme': '主题',\r\n 'nav.language': '语言',\r\n 'nav.toggleNav': '切换导航',\r\n 'nav.closeNav': '关闭导航',\r\n\r\n 'version.label': '版本',\r\n 'version.badge.new': '新版本 {{v}}',\r\n 'version.upToDate': '已是最新',\r\n 'version.checkFailed': '检查更新失败',\r\n 'version.modal.eyebrowUpdate': '有可用更新',\r\n 'version.modal.eyebrowLatest': '版本',\r\n 'version.modal.titleUpdate': '发现新版本',\r\n 'version.modal.titleLatest': '已是最新版本',\r\n 'version.current': '当前版本',\r\n 'version.latest': '最新版本',\r\n 'version.published': '发布于 {{date}}',\r\n 'version.notes': '更新内容',\r\n 'version.notesEmpty': '本次发布没有提供更新说明。',\r\n 'version.btn.update': '立即更新',\r\n 'version.btn.updating': '更新中…',\r\n 'version.btn.releasePage': '发布页',\r\n 'version.btn.repo': '代码仓库',\r\n 'version.btn.close': '关闭',\r\n 'version.update.eyebrow': '结果',\r\n 'version.update.successTitle': '更新完成',\r\n 'version.update.success': '已更新到 {{v}}。请重启 ccsm 以加载新版本。',\r\n 'version.update.failTitle': '更新失败',\r\n 'version.update.fail': 'npm 未能完成更新 — 请查看下方输出,或手动执行更新命令。',\r\n 'version.update.command': '或手动执行',\r\n 'version.update.output': '命令输出',\r\n\r\n 'common.loading': '加载中',\r\n 'common.scanning': '正在扫描 ~/.claude/projects/…',\r\n 'common.computing': '统计中…',\r\n 'common.readingSessions': '读取会话中…',\r\n 'common.loadingSession': '加载会话中…',\r\n 'common.noData': '暂无数据',\r\n 'common.entries': '项',\r\n 'common.entry': '项',\r\n 'common.selectAll': '全选',\r\n 'common.deselectAll': '取消全选',\r\n 'common.cancel': '取消',\r\n 'common.done': '完成',\r\n 'common.expand': '展开',\r\n 'common.collapse': '收起',\r\n 'common.system': '系统',\r\n 'common.onlyUser': '仅我',\r\n 'common.missing': '已不存在',\r\n 'common.noSessions': '该项目下暂无会话。',\r\n 'common.noProjects': '未发现项目。',\r\n 'common.noMessagesMatch': '没有匹配的消息。',\r\n 'common.failedProjects': '加载项目失败',\r\n 'common.failedSessions': '加载会话列表失败',\r\n 'common.failedSession': '加载失败',\r\n 'common.failedMemory': '加载记忆失败',\r\n 'common.failed': '失败',\r\n 'common.searchPlaceholder': '在此会话中搜索…',\r\n 'common.loadEarlier': '加载更早 (+{{n}})',\r\n 'common.scrollToTop': '回到顶部',\r\n 'common.scrollToBottom': '回到底部',\r\n\r\n 'projects.title': '项目',\r\n 'projects.tagline':\r\n '所有曾用 Claude Code 的工作目录,按最近活跃度排序。点击进入查看或清理其会话。',\r\n 'projects.indexHeading': '索引',\r\n 'projects.stat.projects': '项目数',\r\n 'projects.stat.sessions': '会话数',\r\n 'projects.stat.onDisk': '占用',\r\n 'projects.card.eyebrow': '项目',\r\n 'projects.card.sessions': '会话',\r\n 'projects.card.onDisk': '占用',\r\n 'projects.card.lastSeen': '最近',\r\n 'projects.warn.rootMissing':\r\n 'Claude 根目录 {{root}} 在本机不存在 — 无可显示内容。',\r\n\r\n 'project.eyebrow': '项目',\r\n 'project.warn.missingDir': '目录已从磁盘删除',\r\n 'project.action.openFolder': '打开目录',\r\n 'project.action.openFolderTooltipMissing': '目录已不在本机磁盘上',\r\n 'project.action.openFolderFailed': '打开目录失败:{{msg}}',\r\n 'project.action.delete': '删除 · {{n}}',\r\n 'project.action.select': '选择',\r\n 'project.action.exitSelect': '退出',\r\n 'project.bulk.selectedCount': '已选 {{n}} 条',\r\n 'project.bulk.deleteCta': '删除选中',\r\n 'project.bulk.deletePending': '正在删除 {{i}}/{{total}}…',\r\n 'project.bulk.resultOk': '成功 {{ok}} 条',\r\n 'project.bulk.resultSkipped': '跳过 {{n}} 条',\r\n 'project.bulk.resultFailed': '失败 {{n}} 条',\r\n 'project.bulk.viewDetail': '查看明细',\r\n 'project.bulk.hideDetail': '收起',\r\n 'project.bulk.detailSkipped': '跳过',\r\n 'project.bulk.detailFailed': '失败',\r\n 'project.bulk.dismiss': '关闭',\r\n 'project.meta.sessions': '会话',\r\n 'project.meta.onDisk': '占用',\r\n 'project.meta.working': '工作中',\r\n 'project.meta.live': '运行中',\r\n 'project.meta.recent': '近期',\r\n 'project.heading': '会话',\r\n 'project.col.title': '标题',\r\n 'project.col.msgs': '消息',\r\n 'project.col.last': '最近',\r\n 'project.col.size': '大小',\r\n 'project.col.status': '状态',\r\n\r\n 'session.crumbProjects': '项目',\r\n 'session.action.delete': '删除',\r\n 'session.action.deleteTooltipBlocked': '暂无法获取该会话状态',\r\n 'session.action.rename': '重命名',\r\n 'session.action.renameTooltipLive': '运行中 PID {{pid}} 占用此会话 — 请先关闭对应的 claude',\r\n 'session.tagline': '开始于 {{started}} · 最后触达 {{lastTouched}}{{branchPart}}',\r\n 'session.tagline.branch': ' · 分支 {{branch}}',\r\n 'session.meta.messages': '消息数',\r\n 'session.meta.size': '大小',\r\n 'session.meta.version': '版本',\r\n 'session.meta.started': '开始时间',\r\n 'session.truncated': '会话已截断至前 {{n}} 条消息。',\r\n 'session.shown': '{{shown}} / {{total}}',\r\n 'session.live': '实时',\r\n 'session.live.tooltip': '自动更新 — 新消息写入时实时追加',\r\n 'session.working': '工作中',\r\n 'session.working.tooltip': 'Claude 正在处理这一轮 — 生成回复或执行工具',\r\n 'session.working.indicator': 'Claude 正在处理…',\r\n\r\n 'session.modified.title': '修改的文件',\r\n 'session.modified.empty': '本次会话没有修改任何文件。',\r\n 'session.modified.loading': '扫描文件改动…',\r\n 'session.modified.failed': '加载修改文件清单失败',\r\n 'session.modified.count': '{{n}} 个文件',\r\n 'session.modified.countPlural': '{{n}} 个文件',\r\n 'session.modified.openAria': '打开修改的文件',\r\n 'session.modified.close': '关闭',\r\n 'session.modified.openFile': '打开文件',\r\n 'session.modified.openFailed': '打开文件失败:{{msg}}',\r\n 'session.modified.col.file': '文件',\r\n 'session.modified.col.conversation': '对话',\r\n 'session.modified.collapseAll': '全部收起',\r\n 'session.modified.expandAll': '全部展开',\r\n 'session.modified.selectFile': '从左侧选择一个文件,查看本次会话对它的改动。',\r\n 'session.modified.errorBadge': '{{n}} 次失败',\r\n 'session.modified.absolutePath': '绝对路径',\r\n 'session.modified.jump': '跳转到',\r\n 'session.modified.opErrored': '失败',\r\n 'session.modified.opPending': '未完成',\r\n 'session.modified.newContent': '新内容',\r\n 'session.modified.linesOmitted': '省略 {{n}} 行未改动',\r\n 'session.modified.editN': '改动 {{n}}',\r\n 'session.modified.contentUnavailable': '改动内容不可用(会话在此处之后被截断)。',\r\n 'session.modified.noContent': '该操作没有记录到内容。',\r\n 'session.modified.showEarlier': '显示更早的 {{n}} 条消息',\r\n 'session.modified.added': '新增',\r\n 'session.modified.modified': '修改',\r\n\r\n 'message.role.you': '我',\r\n 'message.role.claude': 'Claude',\r\n 'message.role.tool': '工具',\r\n 'message.role.system': '系统',\r\n\r\n 'tool.use': '工具',\r\n 'tool.result': '工具返回',\r\n 'tool.error': '工具错误',\r\n 'tool.thinking': '思考',\r\n 'tool.thinkingEncrypted': '内容被加密,无可读文本。',\r\n 'tool.image': '图片',\r\n\r\n 'status.working': '工作中',\r\n 'status.live': '运行中 · pid {{pid}}',\r\n 'status.recent': '近期',\r\n 'status.idle': '空闲',\r\n 'status.tooltip.working': 'Claude 正在这个会话里处理任务',\r\n 'status.tooltip.live': 'PID {{pid}} 仍在运行并绑定此会话',\r\n 'status.tooltip.recent': 'jsonl 在过去 {{n}} 分钟内被修改',\r\n 'status.tooltip.idle': '空闲 — 可安全删除',\r\n\r\n 'disk.tagline':\r\n '`~/.claude/` 占用磁盘的全貌:哪些项目最沉,哪些会话最大。',\r\n 'disk.title': '磁盘占用',\r\n 'disk.meta.total': '总计',\r\n 'disk.meta.projects': '项目',\r\n 'disk.meta.sessions': '会话',\r\n 'disk.stat.total': '总占用',\r\n 'disk.stat.acrossProjects': '覆盖 {{n}} 个项目',\r\n 'disk.stat.sessions': '会话',\r\n 'disk.stat.largest': '最大 {{size}}',\r\n 'disk.stat.months': '月份覆盖',\r\n 'disk.composition.title': '构成',\r\n 'disk.composition.subtitle': '按项目占用',\r\n 'disk.composition.total': '合计',\r\n 'disk.cadence.title': '节奏',\r\n 'disk.cadence.subtitle': '每月 MB',\r\n 'disk.heaviest.title': '最沉的会话',\r\n 'disk.heaviest.top': '前 {{n}}',\r\n 'disk.col.num': '#',\r\n 'disk.col.title': '标题',\r\n 'disk.col.project': '项目',\r\n 'disk.col.last': '最近',\r\n 'disk.col.size': '大小',\r\n\r\n 'cleanup.title': '清理建议',\r\n 'cleanup.tagline':\r\n '值得清理的目标。逐条审视并手动操作 — 这里没有任何自动清理。',\r\n 'cleanup.section.largeSessions': '最大的会话',\r\n 'cleanup.section.largeSessions.hint': '按总占用前 {{n}} 名',\r\n 'cleanup.section.orphanFileHistory': '孤儿 file-history',\r\n 'cleanup.section.orphanFileHistory.hint':\r\n 'file-history/<sid>/ 目录但无对应会话',\r\n 'cleanup.section.orphanSessionEnv': '孤儿 session-env',\r\n 'cleanup.section.orphanSessionEnv.hint':\r\n 'session-env/<sid>/ 目录但无对应会话',\r\n 'cleanup.empty.largeSessions': '磁盘上还没有会话。',\r\n 'cleanup.empty.orphan': '无孤儿 — 无可清理。',\r\n 'cleanup.col.session': '会话',\r\n 'cleanup.col.project': '项目',\r\n 'cleanup.col.last': '最近',\r\n 'cleanup.col.size': '大小',\r\n 'cleanup.col.sid': '会话 id',\r\n 'cleanup.col.actions': '操作',\r\n 'cleanup.action.view': '查看',\r\n 'cleanup.action.delete': '删除',\r\n 'cleanup.action.deleting': '删除中…',\r\n 'cleanup.confirm.title': '确认删除孤儿?',\r\n 'cleanup.confirm.body':\r\n '将永久删除 {{kind}}/{{sid}}/({{size}})。其会话主体已不存在,不会影响其他数据。',\r\n 'cleanup.confirm.confirm': '删除',\r\n 'cleanup.confirm.cancel': '取消',\r\n 'cleanup.failed': '失败:{{msg}}',\r\n\r\n 'memory.title': '记忆',\r\n 'memory.empty': '该项目尚未沉淀任何记忆。',\r\n 'memory.action.open': '记忆',\r\n 'memory.action.openCount': '记忆 · {{n}}',\r\n 'memory.meta.entries': '条目数',\r\n 'memory.meta.types': '类型',\r\n 'memory.meta.lastUpdate': '最近更新',\r\n 'memory.type.user': '用户',\r\n 'memory.type.feedback': '反馈',\r\n 'memory.type.project': '项目',\r\n 'memory.type.reference': '外链',\r\n 'memory.type.other': '未分类',\r\n 'memory.loading': '读取记忆中…',\r\n 'memory.search.placeholder': '搜索标题、摘要或正文…',\r\n 'memory.search.hint': '按 {{hotkey}} 聚焦',\r\n 'memory.filter.all': '全部',\r\n 'memory.sort.label': '排序',\r\n 'memory.sort.index': 'MEMORY.md 顺序',\r\n 'memory.sort.recent': '最近修改',\r\n 'memory.sort.name': '名称 A→Z',\r\n 'memory.sort.size': '体积大→小',\r\n 'memory.list.noResults': '没有匹配的条目。',\r\n 'memory.list.count': '{{n}} / {{total}}',\r\n 'memory.cover.title': '记忆索引',\r\n 'memory.cover.subtitle': 'MEMORY.md',\r\n 'memory.cover.viewRaw': '查看原文',\r\n 'memory.cover.showAll': '展开全部({{n}})',\r\n 'memory.cover.collapse': '收起',\r\n 'memory.cover.missingTooltip': '未找到对应条目文件',\r\n 'memory.cover.orphan': '未列入索引',\r\n 'memory.empty.title': '尚未沉淀记忆',\r\n 'memory.empty.body':\r\n 'Claude 会在你明确要求记住或观察到稳定偏好时,把记忆存到 ~/.claude/projects/<id>/memory/ 下。',\r\n 'memory.empty.orphanIndex': '索引中引用了 {{n}} 条记忆,但目前一条都不存在。',\r\n 'memory.section.untyped': '未分类',\r\n 'memory.drawer.close': '关闭',\r\n 'memory.drawer.indexHook': '在索引中的呈现',\r\n 'memory.drawer.notFound.title': '未找到条目',\r\n 'memory.drawer.notFound.body':\r\n '该记忆文件可能已被移除;如果索引里还有,会以灰化的形态出现在上方。',\r\n 'memory.raw.title': 'MEMORY.md',\r\n 'memory.raw.subtitle': '精选索引 —— 原文',\r\n\r\n 'delete.eyebrow.confirm': '确认删除',\r\n 'delete.eyebrow.result': '结果',\r\n 'delete.title.confirm': '删除会话',\r\n 'delete.title.result': '已删除会话',\r\n 'delete.summary': '将删除 {{n}} 项 · 跳过 {{skipped}} 项 · 释放约 {{free}}',\r\n 'delete.skipped.heading': '将跳过这 {{n}} 项:',\r\n 'delete.skipped.reasonLive': '运行中 PID {{pid}}',\r\n 'delete.skipped.reasonRecent': '过去 {{n}} 分钟内有修改',\r\n 'delete.skipped.reasonUnknown': '未知',\r\n 'delete.success': '已删除 {{n}} 个会话。释放 {{free}} · 清理 {{lines}} 条历史记录',\r\n 'delete.btn.confirm': '删除 {{n}} {{label}}',\r\n 'delete.btn.confirmPending': '删除中…',\r\n 'delete.label.session': '个会话',\r\n 'delete.label.sessions': '个会话',\r\n 'delete.close': '关闭',\r\n\r\n 'deleteProject.row.action': '删除项目',\r\n 'deleteProject.eyebrow.confirm': '确认删除',\r\n 'deleteProject.eyebrow.result': '结果',\r\n 'deleteProject.title.confirm': '删除项目',\r\n 'deleteProject.title.result': '项目已删除',\r\n 'deleteProject.summary': '{{n}} 个会话 · 约释放 {{free}} · 项目目录将被删除',\r\n 'deleteProject.warning':\r\n '将删除 {{cwd}} 下的全部会话及其在 history.jsonl 的记录,操作不可撤销。',\r\n 'deleteProject.blocked.heading':\r\n '{{n}} 个会话正在运行或最近 5 分钟内被修改 — 本次不会删除任何内容,请稍后再试。',\r\n 'deleteProject.success':\r\n '已删除 {{n}} 个会话及项目目录。释放 {{free}} · 清理 {{lines}} 条历史记录',\r\n 'deleteProject.successKept':\r\n '已删除 {{n}} 个会话。释放 {{free}} · 清理 {{lines}} 条历史记录 · 项目目录已保留',\r\n 'deleteProject.btn.confirm': '删除项目',\r\n 'deleteProject.btn.confirmPending': '删除中…',\r\n\r\n 'search.action.open': '搜索会话',\r\n 'search.placeholder': '在所有会话中搜索…',\r\n 'search.empty': '至少输入 2 个字符,将在磁盘上的所有会话中搜索。',\r\n 'search.refineQuery': '请输入更长的关键词以缩小范围。',\r\n 'search.scanning': '扫描中…',\r\n 'search.noResults': '没有匹配项。',\r\n 'search.error': '搜索失败:{{msg}}',\r\n 'search.moreInSession': '该会话中还有更多',\r\n 'search.moreSessions': '仅显示前 {{n}} 个会话 — 请细化关键词。',\r\n 'search.summary': '{{matched}} 个会话 · 扫描 {{scanned}} 项 · {{ms}}ms',\r\n 'search.shortcut': '{{hint}} 唤起',\r\n 'search.escapeHint': 'esc 关闭',\r\n 'search.kindText': '文本',\r\n 'search.kindToolUse': '工具',\r\n 'search.kindToolResult': '工具返回',\r\n 'search.kindThinking': '思考',\r\n 'search.role.user': '我',\r\n 'search.role.assistant': 'Claude',\r\n\r\n 'export.action': '导出',\r\n 'export.eyebrow.confirm': '跨设备共享',\r\n 'export.eyebrow.result': '结果',\r\n 'export.title.confirm': '导出项目',\r\n 'export.title.result': '导出完成',\r\n 'export.summary': '将 {{n}} 个会话 + 记忆打包到可移植文件夹。',\r\n 'export.destLabel': '目标文件夹',\r\n 'export.destPlaceholder': '例如 D:\\\\sync\\\\claude-shared',\r\n 'export.destHint': '~/.claude 之外的绝对路径。放进 git 仓库或云盘即可同步。',\r\n 'export.privacy':\r\n '这会把你的对话与记忆写到应用之外。如果把该文件夹推送到远端,这些内容将离开本机。',\r\n 'export.btn.confirm': '导出',\r\n 'export.btn.pending': '导出中…',\r\n 'export.success': '已导出 {{sessions}} 个会话 · {{memory}} 个记忆文件 · {{lines}} 条历史',\r\n 'export.successDest': '写入到 {{dest}}',\r\n 'export.close': '关闭',\r\n\r\n 'import.title': '导入归档',\r\n 'import.tagline': '从另一台设备导入项目的会话与记忆。路径会被重映射到本机。',\r\n 'import.bundleLabel': '归档文件夹',\r\n 'import.bundlePlaceholder': '已导出归档的路径',\r\n 'import.btn.load': '加载归档',\r\n 'import.btn.recheck': '重新检查',\r\n 'import.btn.loading': '加载中…',\r\n 'import.source': '来自 {{platform}} · {{cwd}}',\r\n 'import.targetLabel': '导入到(本机)',\r\n 'import.targetHint': '本机绝对路径。会话将与该目录关联。',\r\n 'import.targetId': '项目 id:{{id}}',\r\n 'import.suggestions': '建议',\r\n 'import.suggestion.existing-project': '已有项目',\r\n 'import.suggestion.original-path': '原始路径',\r\n 'import.suggestion.same-basename': '同名目录',\r\n 'import.policyLabel': '当会话已存在时',\r\n 'import.policy.skip': '跳过',\r\n 'import.policy.overwrite-if-newer': '较新则覆盖',\r\n 'import.policy.keep-both': '两者都留',\r\n 'import.sessions.heading': '会话',\r\n 'import.sessions.empty': '此归档没有会话。',\r\n 'import.action.create': '新建',\r\n 'import.action.overwrite': '覆盖',\r\n 'import.action.keep-both': '另存',\r\n 'import.action.skip': '跳过',\r\n 'import.memory.heading': '记忆',\r\n 'import.memory.create': '新建',\r\n 'import.memory.skip': '相同',\r\n 'import.memory.conflict': '冲突 → {{name}}',\r\n 'import.history': '将新增 {{n}} 条历史',\r\n 'import.btn.commit': '导入',\r\n 'import.btn.committing': '导入中…',\r\n 'import.result.title': '导入完成',\r\n 'import.result.summary':\r\n '已导入 {{n}} 个会话 · 跳过 {{skipped}} · {{memory}} 个记忆文件 · {{lines}} 条历史',\r\n 'import.result.viewProject': '打开已导入项目',\r\n 'import.empty': '输入归档文件夹路径以开始。',\r\n },\r\n} as const;\r\n\r\ntype Key = keyof (typeof DICT)['en'];\r\n\r\nfunction readInitial(): Locale {\r\n if (typeof window === 'undefined') return 'en';\r\n try {\r\n const saved = localStorage.getItem(STORAGE_KEY);\r\n if (saved === 'en' || saved === 'zh') return saved;\r\n } catch {\r\n /* fall through */\r\n }\r\n return navigator.language.toLowerCase().startsWith('zh') ? 'zh' : 'en';\r\n}\r\n\r\nlet _locale: Locale = typeof window !== 'undefined' ? readInitial() : 'en';\r\nconst listeners = new Set<(l: Locale) => void>();\r\n\r\nexport function getLocale(): Locale {\r\n return _locale;\r\n}\r\n\r\nexport function setLocale(next: Locale) {\r\n if (next === _locale) return;\r\n _locale = next;\r\n try {\r\n localStorage.setItem(STORAGE_KEY, next);\r\n } catch {\r\n /* storage might be blocked */\r\n }\r\n document.documentElement.setAttribute('lang', next);\r\n listeners.forEach((fn) => fn(next));\r\n}\r\n\r\nexport function useLocale(): { locale: Locale; setLocale: (l: Locale) => void; toggle: () => void } {\r\n const [locale, setLocaleState] = useState(_locale);\r\n\r\n useEffect(() => {\r\n const handler = (l: Locale) => setLocaleState(l);\r\n listeners.add(handler);\r\n return () => {\r\n listeners.delete(handler);\r\n };\r\n }, []);\r\n\r\n const setNext = useCallback((l: Locale) => {\r\n setLocale(l);\r\n }, []);\r\n\r\n const toggle = useCallback(() => {\r\n setLocale(_locale === 'en' ? 'zh' : 'en');\r\n }, []);\r\n\r\n return { locale, setLocale: setNext, toggle };\r\n}\r\n\r\nexport function useT(): (key: Key, params?: Record<string, string | number>) => string {\r\n const { locale } = useLocale();\r\n return useCallback(\r\n (key: Key, params?: Record<string, string | number>) => translate(locale, key, params),\r\n [locale],\r\n );\r\n}\r\n\r\nexport function translate(\r\n locale: Locale,\r\n key: Key,\r\n params?: Record<string, string | number>,\r\n): string {\r\n const dict = DICT[locale];\r\n let str: string = dict[key] ?? DICT.en[key] ?? key;\r\n if (params) {\r\n for (const [k, v] of Object.entries(params)) {\r\n str = str.replaceAll(`{{${k}}}`, String(v));\r\n }\r\n }\r\n return str;\r\n}\r\n","import type { SearchEvent } from './api.ts';\r\n\r\nexport interface StreamSearchOpts {\r\n query: string;\r\n perSession?: number;\r\n maxSessions?: number;\r\n signal?: AbortSignal;\r\n}\r\n\r\nexport async function* streamSearch(opts: StreamSearchOpts): AsyncGenerator<SearchEvent> {\r\n const params = new URLSearchParams({ q: opts.query });\r\n if (opts.perSession !== undefined) params.set('perSession', String(opts.perSession));\r\n if (opts.maxSessions !== undefined) params.set('maxSessions', String(opts.maxSessions));\r\n\r\n const res = await fetch(`/api/search?${params.toString()}`, {\r\n signal: opts.signal,\r\n headers: { accept: 'application/x-ndjson' },\r\n });\r\n\r\n if (!res.ok) {\r\n const text = await res.text().catch(() => '');\r\n throw new Error(`${res.status} ${res.statusText}${text ? ` — ${text}` : ''}`);\r\n }\r\n if (!res.body) {\r\n throw new Error('search stream returned no body');\r\n }\r\n\r\n const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();\r\n let buffer = '';\r\n\r\n try {\r\n while (true) {\r\n const { value, done } = await reader.read();\r\n if (done) {\r\n if (buffer.trim()) {\r\n const event = parseLine(buffer);\r\n if (event) yield event;\r\n }\r\n return;\r\n }\r\n buffer += value;\r\n let nl: number;\r\n while ((nl = buffer.indexOf('\\n')) !== -1) {\r\n const raw = buffer.slice(0, nl);\r\n buffer = buffer.slice(nl + 1);\r\n const event = parseLine(raw);\r\n if (event) yield event;\r\n }\r\n }\r\n } finally {\r\n try {\r\n await reader.cancel();\r\n } catch {\r\n /* ignore */\r\n }\r\n }\r\n}\r\n\r\nfunction parseLine(raw: string): SearchEvent | null {\r\n const line = raw.trim();\r\n if (!line) return null;\r\n try {\r\n return JSON.parse(line) as SearchEvent;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n","import {\r\n useCallback,\r\n useEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n} from 'react';\r\nimport { useNavigate } from 'react-router-dom';\r\nimport { LoadingDots } from './Loading.tsx';\r\nimport type { SearchBlockKind, SearchDone, SearchSessionHit, SearchSnippet } from '../lib/api.ts';\r\nimport { formatRelativeTime } from '../lib/format.ts';\r\nimport { HOTKEY_HINT } from '../lib/hotkeys.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport { streamSearch } from '../lib/search-stream.ts';\r\n\r\nconst MIN_QUERY = 2;\r\nconst DEBOUNCE_MS = 220;\r\n\r\ninterface FlatItem {\r\n hit: SearchSessionHit;\r\n snippet: SearchSnippet;\r\n hitIndex: number;\r\n snippetIndex: number;\r\n flatIndex: number;\r\n}\r\n\r\nexport default function SearchModal({\r\n open,\r\n onClose,\r\n}: {\r\n open: boolean;\r\n onClose: () => void;\r\n}) {\r\n const t = useT();\r\n const navigate = useNavigate();\r\n const [query, setQuery] = useState('');\r\n const [hits, setHits] = useState<SearchSessionHit[]>([]);\r\n const [done, setDone] = useState<SearchDone | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const [activeIndex, setActiveIndex] = useState(0);\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n const listRef = useRef<HTMLDivElement | null>(null);\r\n const controllerRef = useRef<AbortController | null>(null);\r\n\r\n const flatItems: FlatItem[] = useMemo(() => {\r\n const out: FlatItem[] = [];\r\n let flat = 0;\r\n hits.forEach((hit, hitIndex) => {\r\n hit.snippets.forEach((snippet, snippetIndex) => {\r\n out.push({ hit, snippet, hitIndex, snippetIndex, flatIndex: flat++ });\r\n });\r\n });\r\n return out;\r\n }, [hits]);\r\n\r\n const trimmedQuery = query.trim();\r\n\r\n useEffect(() => {\r\n if (!open) return;\r\n const id = window.setTimeout(() => {\r\n runSearch(trimmedQuery);\r\n }, DEBOUNCE_MS);\r\n return () => window.clearTimeout(id);\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [trimmedQuery, open]);\r\n\r\n useEffect(() => {\r\n setActiveIndex(0);\r\n }, [hits.length]);\r\n\r\n useEffect(() => {\r\n if (!open) {\r\n controllerRef.current?.abort();\r\n controllerRef.current = null;\r\n setQuery('');\r\n setHits([]);\r\n setDone(null);\r\n setError(null);\r\n setLoading(false);\r\n setActiveIndex(0);\r\n return;\r\n }\r\n // Focus input on open. requestAnimationFrame ensures the dialog is in the DOM.\r\n let raf = 0;\r\n raf = requestAnimationFrame(() => {\r\n inputRef.current?.focus();\r\n });\r\n function onWindowKey(e: KeyboardEvent) {\r\n if (e.key === 'Escape') {\r\n e.preventDefault();\r\n onClose();\r\n }\r\n }\r\n window.addEventListener('keydown', onWindowKey);\r\n return () => {\r\n cancelAnimationFrame(raf);\r\n window.removeEventListener('keydown', onWindowKey);\r\n };\r\n }, [open, onClose]);\r\n\r\n const runSearch = useCallback((q: string) => {\r\n controllerRef.current?.abort();\r\n if (q.length < MIN_QUERY) {\r\n setHits([]);\r\n setDone(null);\r\n setError(null);\r\n setLoading(false);\r\n return;\r\n }\r\n const controller = new AbortController();\r\n controllerRef.current = controller;\r\n setHits([]);\r\n setDone(null);\r\n setError(null);\r\n setLoading(true);\r\n (async () => {\r\n try {\r\n for await (const event of streamSearch({ query: q, signal: controller.signal })) {\r\n if (controller.signal.aborted) return;\r\n if (event.type === 'session') {\r\n setHits((prev) => [...prev, event]);\r\n } else if (event.type === 'done') {\r\n setDone(event);\r\n }\r\n }\r\n } catch (err) {\r\n if (controller.signal.aborted) return;\r\n setError((err as Error).message);\r\n } finally {\r\n if (!controller.signal.aborted) setLoading(false);\r\n }\r\n })();\r\n }, []);\r\n\r\n const navigateToSnippet = useCallback(\r\n (hit: SearchSessionHit, snippet: SearchSnippet) => {\r\n const params = new URLSearchParams({ focus: snippet.uuid, q: trimmedQuery });\r\n navigate(\r\n `/projects/${encodeURIComponent(hit.projectId)}/sessions/${encodeURIComponent(hit.sessionId)}?${params.toString()}`,\r\n );\r\n onClose();\r\n },\r\n [navigate, onClose, trimmedQuery],\r\n );\r\n\r\n function onKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {\r\n // Escape is handled at window level (works regardless of focus).\r\n if (flatItems.length === 0) return;\r\n if (e.key === 'ArrowDown') {\r\n e.preventDefault();\r\n setActiveIndex((i) => Math.min(i + 1, flatItems.length - 1));\r\n } else if (e.key === 'ArrowUp') {\r\n e.preventDefault();\r\n setActiveIndex((i) => Math.max(i - 1, 0));\r\n } else if (e.key === 'Home') {\r\n e.preventDefault();\r\n setActiveIndex(0);\r\n } else if (e.key === 'End') {\r\n e.preventDefault();\r\n setActiveIndex(flatItems.length - 1);\r\n } else if (e.key === 'Enter') {\r\n e.preventDefault();\r\n const item = flatItems[activeIndex];\r\n if (item) navigateToSnippet(item.hit, item.snippet);\r\n }\r\n }\r\n\r\n useEffect(() => {\r\n if (!listRef.current) return;\r\n const el = listRef.current.querySelector<HTMLElement>(`[data-flat-index=\"${activeIndex}\"]`);\r\n el?.scrollIntoView({ block: 'nearest' });\r\n }, [activeIndex]);\r\n\r\n if (!open) return null;\r\n\r\n const showEmpty = trimmedQuery.length === 0;\r\n const showRefine = !showEmpty && trimmedQuery.length < MIN_QUERY;\r\n const showNoResults = !loading && !error && trimmedQuery.length >= MIN_QUERY && hits.length === 0 && done !== null;\r\n\r\n return (\r\n <div className=\"fixed inset-0 z-50 flex items-start justify-center px-4 pt-[12vh] sm:px-6\">\r\n <button\r\n type=\"button\"\r\n aria-label={t('delete.close')}\r\n onClick={onClose}\r\n className=\"fixed inset-0 bg-[var(--color-canvas)]/65 backdrop-blur-sm\"\r\n />\r\n <div\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label={t('search.action.open')}\r\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)]\"\r\n onKeyDown={onKeyDown}\r\n >\r\n <header className=\"flex items-center gap-3 border-b border-[var(--color-hairline)] px-5 py-4\">\r\n <SearchIcon className=\"text-[var(--color-fg-muted)]\" />\r\n <input\r\n ref={inputRef}\r\n type=\"search\"\r\n autoFocus\r\n value={query}\r\n onChange={(e) => setQuery(e.target.value)}\r\n placeholder={t('search.placeholder')}\r\n className=\"flex-1 bg-transparent text-[15px] text-[var(--color-fg-primary)] placeholder:text-[var(--color-fg-faint)] focus:outline-none\"\r\n />\r\n {loading && (\r\n <span className=\"inline-flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--color-fg-muted)]\">\r\n {t('search.scanning')}\r\n <LoadingDots />\r\n </span>\r\n )}\r\n </header>\r\n\r\n <div ref={listRef} className=\"max-h-[60vh] overflow-y-auto px-1 py-2\">\r\n {showEmpty && (\r\n <Hint>{t('search.empty')}</Hint>\r\n )}\r\n {showRefine && (\r\n <Hint>{t('search.refineQuery')}</Hint>\r\n )}\r\n {error && (\r\n <Hint tone=\"danger\">{t('search.error', { msg: error })}</Hint>\r\n )}\r\n {showNoResults && (\r\n <Hint>{t('search.noResults')}</Hint>\r\n )}\r\n\r\n {hits.map((hit, hitIndex) => (\r\n <SessionGroup\r\n key={`${hit.projectId}/${hit.sessionId}`}\r\n hit={hit}\r\n hitIndex={hitIndex}\r\n flatItems={flatItems}\r\n activeIndex={activeIndex}\r\n query={trimmedQuery}\r\n onPick={navigateToSnippet}\r\n onHover={setActiveIndex}\r\n />\r\n ))}\r\n\r\n {done?.truncated && (\r\n <Hint>{t('search.moreSessions', { n: done.matched })}</Hint>\r\n )}\r\n </div>\r\n\r\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)]\">\r\n <span>{t('search.shortcut', { hint: HOTKEY_HINT })} · {t('search.escapeHint')}</span>\r\n {done && hits.length > 0 && (\r\n <span>\r\n {t('search.summary', {\r\n matched: done.matched,\r\n scanned: done.scanned,\r\n ms: done.durationMs,\r\n })}\r\n </span>\r\n )}\r\n </footer>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction SessionGroup({\r\n hit,\r\n hitIndex,\r\n flatItems,\r\n activeIndex,\r\n query,\r\n onPick,\r\n onHover,\r\n}: {\r\n hit: SearchSessionHit;\r\n hitIndex: number;\r\n flatItems: FlatItem[];\r\n activeIndex: number;\r\n query: string;\r\n onPick: (hit: SearchSessionHit, snippet: SearchSnippet) => void;\r\n onHover: (flatIndex: number) => void;\r\n}) {\r\n const t = useT();\r\n const title = hit.customTitle ?? hit.title;\r\n const projectTail = useMemo(() => {\r\n const parts = hit.projectDecodedCwd.split(/[\\\\/]+/).filter(Boolean);\r\n return parts.at(-1) ?? hit.projectDecodedCwd;\r\n }, [hit.projectDecodedCwd]);\r\n\r\n return (\r\n <div className=\"px-2 pb-1 pt-3\">\r\n <div className=\"flex items-baseline justify-between gap-3 px-2\">\r\n <div className=\"min-w-0 flex items-baseline gap-2\">\r\n <span className=\"font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--color-accent)]\">\r\n {projectTail}\r\n </span>\r\n <span className=\"truncate font-display text-[13px] text-[var(--color-fg-primary)]\">\r\n {title}\r\n </span>\r\n </div>\r\n <span className=\"shrink-0 font-mono text-[10px] tabular-nums text-[var(--color-fg-muted)]\">\r\n {formatRelativeTime(hit.lastAt)}\r\n </span>\r\n </div>\r\n <ul className=\"mt-1.5 space-y-0.5\">\r\n {hit.snippets.map((snippet, i) => {\r\n const item = flatItems.find(\r\n (f) => f.hitIndex === hitIndex && f.snippetIndex === i,\r\n );\r\n const flat = item?.flatIndex ?? -1;\r\n const active = flat === activeIndex;\r\n return (\r\n <li key={`${snippet.uuid}-${i}`}>\r\n <button\r\n type=\"button\"\r\n data-flat-index={flat}\r\n onMouseEnter={() => onHover(flat)}\r\n onClick={() => onPick(hit, snippet)}\r\n className={\r\n 'block w-full rounded-[var(--radius-input)] px-3 py-2 text-left transition ' +\r\n (active\r\n ? 'bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\r\n : 'text-[var(--color-fg-secondary)] hover:bg-[var(--color-sunken)]')\r\n }\r\n >\r\n <div className=\"flex items-baseline gap-2 font-mono text-[10px] uppercase tracking-[0.16em] text-[var(--color-fg-muted)]\">\r\n <span>{t(`search.role.${snippet.role}` as const)}</span>\r\n <span>·</span>\r\n <span>{kindLabel(snippet.blockKind, t)}</span>\r\n </div>\r\n <p className=\"mt-1 truncate text-[13px] leading-relaxed\">\r\n <span className=\"text-[var(--color-fg-faint)]\">{snippet.before}</span>\r\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)]\">\r\n {snippet.match}\r\n </mark>\r\n <span className=\"text-[var(--color-fg-faint)]\">{snippet.after}</span>\r\n </p>\r\n </button>\r\n </li>\r\n );\r\n })}\r\n </ul>\r\n {hit.hasMore && (\r\n <p className=\"px-3 pt-1 font-mono text-[10px] uppercase tracking-[0.16em] text-[var(--color-fg-faint)]\">\r\n {t('search.moreInSession')}\r\n </p>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nfunction kindLabel(\r\n kind: SearchBlockKind,\r\n t: ReturnType<typeof useT>,\r\n): string {\r\n switch (kind) {\r\n case 'text':\r\n return t('search.kindText');\r\n case 'tool_use':\r\n return t('search.kindToolUse');\r\n case 'tool_result':\r\n return t('search.kindToolResult');\r\n case 'thinking':\r\n return t('search.kindThinking');\r\n }\r\n}\r\n\r\nfunction Hint({\r\n children,\r\n tone = 'normal',\r\n}: {\r\n children: React.ReactNode;\r\n tone?: 'normal' | 'danger';\r\n}) {\r\n const cls =\r\n tone === 'danger'\r\n ? 'text-[var(--color-danger)]'\r\n : 'text-[var(--color-fg-muted)]';\r\n return (\r\n <p className={`px-5 py-6 text-center text-sm ${cls}`}>{children}</p>\r\n );\r\n}\r\n\r\nfunction SearchIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.7\"\r\n strokeLinecap=\"round\"\r\n className={className}\r\n aria-hidden\r\n >\r\n <circle cx=\"11\" cy=\"11\" r=\"6.2\" />\r\n <path d=\"M20 20l-4.3-4.3\" />\r\n </svg>\r\n );\r\n}\r\n","import { useLocale } from '../lib/i18n.ts';\r\n\r\nexport default function LocaleToggle({ className = '' }: { className?: string }) {\r\n const { locale, setLocale } = useLocale();\r\n return (\r\n <div\r\n role=\"group\"\r\n aria-label=\"Language\"\r\n className={\r\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 ' +\r\n className\r\n }\r\n >\r\n <Pill active={locale === 'en'} onClick={() => setLocale('en')} label=\"EN\" />\r\n <Pill active={locale === 'zh'} onClick={() => setLocale('zh')} label=\"中\" />\r\n </div>\r\n );\r\n}\r\n\r\nfunction Pill({\r\n active,\r\n onClick,\r\n label,\r\n}: {\r\n active: boolean;\r\n onClick: () => void;\r\n label: string;\r\n}) {\r\n return (\r\n <button\r\n type=\"button\"\r\n onClick={onClick}\r\n aria-pressed={active}\r\n className={\r\n 'h-7 min-w-[2rem] rounded-full px-2 transition ' +\r\n (active\r\n ? 'bg-[var(--color-surface)] text-[var(--color-fg-primary)] shadow-[var(--shadow-rise)]'\r\n : 'text-[var(--color-fg-muted)] hover:text-[var(--color-fg-primary)]')\r\n }\r\n >\r\n {label}\r\n </button>\r\n );\r\n}\r\n","import { useCallback, useEffect, useState } from 'react';\r\n\r\nexport type Theme = 'light' | 'dark';\r\n\r\nconst STORAGE_KEY = 'theme';\r\n\r\nfunction readInitial(): Theme {\r\n if (typeof document === 'undefined') return 'light';\r\n return document.documentElement.classList.contains('dark') ? 'dark' : 'light';\r\n}\r\n\r\nexport function useTheme(): {\r\n theme: Theme;\r\n setTheme: (next: Theme) => void;\r\n toggle: () => void;\r\n} {\r\n const [theme, setThemeState] = useState<Theme>(readInitial);\r\n\r\n const setTheme = useCallback((next: Theme) => {\r\n setThemeState(next);\r\n document.documentElement.classList.toggle('dark', next === 'dark');\r\n try {\r\n localStorage.setItem(STORAGE_KEY, next);\r\n } catch {\r\n /* storage might be blocked — silently fall back to in-memory */\r\n }\r\n }, []);\r\n\r\n const toggle = useCallback(() => {\r\n setTheme(theme === 'dark' ? 'light' : 'dark');\r\n }, [theme, setTheme]);\r\n\r\n // Sync if the system preference changes and the user hasn't explicitly chosen\r\n useEffect(() => {\r\n const mq = window.matchMedia('(prefers-color-scheme: dark)');\r\n const handler = (e: MediaQueryListEvent) => {\r\n if (localStorage.getItem(STORAGE_KEY)) return;\r\n setTheme(e.matches ? 'dark' : 'light');\r\n };\r\n mq.addEventListener('change', handler);\r\n return () => mq.removeEventListener('change', handler);\r\n }, [setTheme]);\r\n\r\n return { theme, setTheme, toggle };\r\n}\r\n","import { useTheme } from '../lib/theme.ts';\r\n\r\nexport default function ThemeToggle({ className = '' }: { className?: string }) {\r\n const { theme, toggle } = useTheme();\r\n const isDark = theme === 'dark';\r\n\r\n return (\r\n <button\r\n type=\"button\"\r\n onClick={toggle}\r\n aria-label={isDark ? 'Switch to light theme' : 'Switch to dark theme'}\r\n title={isDark ? 'Light' : 'Dark'}\r\n className={\r\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)] ' +\r\n className\r\n }\r\n >\r\n <span\r\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\"\r\n style={{ transform: isDark ? 'translateX(28px)' : 'translateX(0)' }}\r\n >\r\n {isDark ? <MoonIcon /> : <SunIcon />}\r\n </span>\r\n <span className=\"sr-only\">Toggle theme</span>\r\n </button>\r\n );\r\n}\r\n\r\nfunction SunIcon() {\r\n return (\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <circle cx=\"12\" cy=\"12\" r=\"3.6\" />\r\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\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction MoonIcon() {\r\n return (\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\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\" />\r\n </svg>\r\n );\r\n}\r\n","export * from '../../../shared/types.ts';\r\n\r\nexport async function api<T>(path: string, init?: RequestInit): Promise<T> {\r\n const res = await fetch(path, {\r\n ...init,\r\n headers: { 'content-type': 'application/json', ...(init?.headers ?? {}) },\r\n });\r\n if (!res.ok) {\r\n const text = await res.text().catch(() => '');\r\n let detail = text;\r\n if (text) {\r\n try {\r\n const parsed = JSON.parse(text) as { error?: unknown };\r\n if (typeof parsed.error === 'string') detail = parsed.error;\r\n } catch {\r\n /* keep raw text */\r\n }\r\n }\r\n throw new Error(`${res.status} ${res.statusText}${detail ? ` — ${detail}` : ''}`);\r\n }\r\n return res.json() as Promise<T>;\r\n}\r\n","export const queryKeys = {\r\n health: () => ['health'] as const,\r\n projects: () => ['projects'] as const,\r\n projectSessions: (projectId: string) => ['project-sessions', projectId] as const,\r\n projectMemory: (projectId: string) => ['project-memory', projectId] as const,\r\n session: (projectId: string, sessionId: string) =>\r\n ['session', projectId, sessionId] as const,\r\n sessionModifiedFiles: (projectId: string, sessionId: string) =>\r\n ['session-modified-files', projectId, sessionId] as const,\r\n diskUsage: () => ['disk-usage'] as const,\r\n diskCleanupSuggestions: () => ['disk-cleanup-suggestions'] as const,\r\n search: (query: string) => ['search', query] as const,\r\n version: () => ['version'] as const,\r\n};\r\n","import { useMutation, useQuery } from '@tanstack/react-query';\r\nimport { AnimatePresence, motion } from 'motion/react';\r\nimport { useEffect, useRef, useState } from 'react';\r\nimport { createPortal } from 'react-dom';\r\nimport { api, type VersionInfo, type VersionUpdateResult } from '../lib/api.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport { queryKeys } from '../lib/query-keys.ts';\r\n\r\nconst UPDATE_COMMAND = 'npm install -g @zzusp/ccsm@latest';\r\n\r\n/** Sidebar footer row: current version, or an amber \"new version\" pill. Opens the modal. */\r\nexport default function VersionNotice() {\r\n const t = useT();\r\n const [open, setOpen] = useState(false);\r\n const { data } = useQuery({\r\n queryKey: queryKeys.version(),\r\n queryFn: () => api<VersionInfo>('/api/version'),\r\n staleTime: 30 * 60 * 1000,\r\n refetchOnWindowFocus: false,\r\n });\r\n\r\n if (!data) return null;\r\n\r\n return (\r\n <>\r\n <button\r\n type=\"button\"\r\n onClick={() => setOpen(true)}\r\n className=\"group flex w-full items-center justify-between gap-2 text-left\"\r\n >\r\n <span className=\"eyebrow\">{t('version.label')}</span>\r\n {data.hasUpdate ? (\r\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)]\">\r\n <span aria-hidden className=\"relative inline-flex h-1.5 w-1.5\">\r\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)] pulse-amber\" />\r\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)]\" />\r\n </span>\r\n {t('version.badge.new', { v: `v${data.latest}` })}\r\n </span>\r\n ) : (\r\n <span className=\"font-mono text-[11px] text-[var(--color-fg-faint)] transition-colors group-hover:text-[var(--color-fg-secondary)]\">\r\n v{data.current}\r\n </span>\r\n )}\r\n </button>\r\n {createPortal(\r\n <AnimatePresence>\r\n {open && <VersionModal info={data} onClose={() => setOpen(false)} />}\r\n </AnimatePresence>,\r\n document.body,\r\n )}\r\n </>\r\n );\r\n}\r\n\r\nfunction VersionModal({ info, onClose }: { info: VersionInfo; onClose: () => void }) {\r\n const t = useT();\r\n const mutation = useMutation({\r\n mutationFn: () => api<VersionUpdateResult>('/api/version/update', { method: 'POST' }),\r\n });\r\n\r\n const isPendingRef = useRef(mutation.isPending);\r\n isPendingRef.current = mutation.isPending;\r\n useEffect(() => {\r\n function onKey(e: KeyboardEvent) {\r\n if (e.key === 'Escape' && !isPendingRef.current) onClose();\r\n }\r\n window.addEventListener('keydown', onKey);\r\n return () => window.removeEventListener('keydown', onKey);\r\n }, [onClose]);\r\n\r\n const result = mutation.data;\r\n const hasUpdate = info.hasUpdate;\r\n const notes = info.releaseNotes?.trim();\r\n const published = info.publishedAt ? new Date(info.publishedAt).toLocaleDateString() : null;\r\n const updateDone = !!result?.ok;\r\n // After a successful update the page should stop offering it; a failure keeps \"retry\" available.\r\n const showUpdateAction = hasUpdate && !updateDone;\r\n\r\n return (\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n exit={{ opacity: 0 }}\r\n transition={{ duration: 0.18 }}\r\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]\"\r\n onClick={() => !mutation.isPending && onClose()}\r\n >\r\n <motion.div\r\n initial={{ y: 8, opacity: 0 }}\r\n animate={{ y: 0, opacity: 1 }}\r\n transition={{ duration: 0.24, ease: [0.16, 1, 0.3, 1] }}\r\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)]\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <header className=\"flex items-start justify-between gap-4 border-b border-[var(--color-hairline)] px-6 py-5\">\r\n <div className=\"min-w-0\">\r\n <p className=\"eyebrow text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\r\n {hasUpdate ? t('version.modal.eyebrowUpdate') : t('version.modal.eyebrowLatest')}\r\n </p>\r\n <h2 className=\"mt-1 font-display text-2xl font-light tracking-tight text-[var(--color-fg-primary)]\">\r\n {hasUpdate ? t('version.modal.titleUpdate') : t('version.modal.titleLatest')}\r\n </h2>\r\n <div className=\"mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 font-mono text-xs text-[var(--color-fg-muted)]\">\r\n <span>\r\n {t('version.current')} <span className=\"text-[var(--color-fg-primary)]\">v{info.current}</span>\r\n </span>\r\n {hasUpdate && info.latest && (\r\n <>\r\n <span aria-hidden className=\"text-[var(--color-fg-faint)]\">→</span>\r\n <span>\r\n {t('version.latest')}{' '}\r\n <span className=\"text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\r\n v{info.latest}\r\n </span>\r\n </span>\r\n </>\r\n )}\r\n {published && <span className=\"text-[var(--color-fg-faint)]\">· {t('version.published', { date: published })}</span>}\r\n </div>\r\n {info.checkError && (\r\n <p className=\"mt-1.5 text-xs text-[var(--color-fg-faint)]\">{t('version.checkFailed')}</p>\r\n )}\r\n </div>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n disabled={mutation.isPending}\r\n aria-label={t('version.btn.close')}\r\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\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\">\r\n <path d=\"M6 6l12 12M18 6L6 18\" />\r\n </svg>\r\n </button>\r\n </header>\r\n\r\n <div className=\"min-h-0 flex-1 space-y-4 overflow-y-auto px-6 py-5\">\r\n {result ? (\r\n <UpdateOutcome result={result} />\r\n ) : (\r\n <section>\r\n <p className=\"eyebrow mb-1.5\">{t('version.notes')}</p>\r\n {notes ? (\r\n <pre className=\"max-h-64 overflow-y-auto whitespace-pre-wrap break-words rounded-[var(--radius-input)] border border-[var(--color-hairline)] bg-[var(--color-sunken)] px-4 py-3 font-mono text-[12.5px] leading-[1.65] text-[var(--color-fg-primary)]\">\r\n {notes}\r\n </pre>\r\n ) : (\r\n <p className=\"text-sm text-[var(--color-fg-muted)]\">{t('version.notesEmpty')}</p>\r\n )}\r\n </section>\r\n )}\r\n\r\n {mutation.error && (\r\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)]\">\r\n {(mutation.error as Error).message}\r\n </p>\r\n )}\r\n </div>\r\n\r\n <footer className=\"flex items-center justify-between gap-2 border-t border-[var(--color-hairline)] px-6 py-4\">\r\n <div className=\"flex items-center gap-1\">\r\n {info.releaseUrl && (\r\n <ExternalLink href={info.releaseUrl} label={t('version.btn.releasePage')} />\r\n )}\r\n <ExternalLink href={info.repositoryUrl} label={t('version.btn.repo')} />\r\n </div>\r\n <div className=\"flex items-center gap-2\">\r\n {showUpdateAction ? (\r\n <>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n disabled={mutation.isPending}\r\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\"\r\n >\r\n {t('common.cancel')}\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => mutation.mutate()}\r\n disabled={mutation.isPending}\r\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\"\r\n >\r\n {mutation.isPending && <Spinner />}\r\n {mutation.isPending ? t('version.btn.updating') : t('version.btn.update')}\r\n </button>\r\n </>\r\n ) : (\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\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\"\r\n >\r\n {t('common.done')}\r\n </button>\r\n )}\r\n </div>\r\n </footer>\r\n </motion.div>\r\n </motion.div>\r\n );\r\n}\r\n\r\nfunction UpdateOutcome({ result }: { result: VersionUpdateResult }) {\r\n const t = useT();\r\n if (result.ok) {\r\n return (\r\n <section className=\"space-y-3\">\r\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)]\">\r\n <p className=\"font-medium\">{t('version.update.successTitle')}</p>\r\n <p className=\"mt-0.5 text-[var(--color-fg-secondary)]\">\r\n {t('version.update.success', { v: `v${result.toVersion ?? ''}` })}\r\n </p>\r\n </div>\r\n {result.output && <OutputBlock output={result.output} />}\r\n </section>\r\n );\r\n }\r\n return (\r\n <section className=\"space-y-3\">\r\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\">\r\n <p className=\"font-medium text-[var(--color-danger)]\">{t('version.update.failTitle')}</p>\r\n <p className=\"mt-0.5 text-[var(--color-fg-secondary)]\">{t('version.update.fail')}</p>\r\n </div>\r\n <CommandHint />\r\n {result.output && <OutputBlock output={result.output} />}\r\n </section>\r\n );\r\n}\r\n\r\nfunction CommandHint() {\r\n const t = useT();\r\n return (\r\n <div>\r\n <p className=\"eyebrow mb-1.5\">{t('version.update.command')}</p>\r\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\">\r\n <code className=\"min-w-0 flex-1 truncate font-mono text-[12.5px] text-[var(--color-fg-primary)]\">\r\n {UPDATE_COMMAND}\r\n </code>\r\n <CopyButton text={UPDATE_COMMAND} />\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction OutputBlock({ output }: { output: string }) {\r\n const t = useT();\r\n return (\r\n <div>\r\n <p className=\"eyebrow mb-1.5\">{t('version.update.output')}</p>\r\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)]\">\r\n {output}\r\n </pre>\r\n </div>\r\n );\r\n}\r\n\r\nfunction CopyButton({ text }: { text: string }) {\r\n const [copied, setCopied] = useState(false);\r\n return (\r\n <button\r\n type=\"button\"\r\n onClick={async () => {\r\n try {\r\n await navigator.clipboard.writeText(text);\r\n setCopied(true);\r\n window.setTimeout(() => setCopied(false), 1500);\r\n } catch {\r\n /* clipboard may be blocked */\r\n }\r\n }}\r\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)]\"\r\n aria-label=\"copy\"\r\n >\r\n {copied ? (\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\r\n <path d=\"M5 12l5 5L20 6\" />\r\n </svg>\r\n ) : (\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\r\n <rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" />\r\n <path d=\"M5 15V5a2 2 0 0 1 2-2h10\" />\r\n </svg>\r\n )}\r\n </button>\r\n );\r\n}\r\n\r\nfunction ExternalLink({ href, label }: { href: string; label: string }) {\r\n return (\r\n <a\r\n href={href}\r\n target=\"_blank\"\r\n rel=\"noreferrer\"\r\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)]\"\r\n >\r\n {label}\r\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <path d=\"M7 17L17 7\" />\r\n <path d=\"M8 7h9v9\" />\r\n </svg>\r\n </a>\r\n );\r\n}\r\n\r\nfunction Spinner() {\r\n return (\r\n <svg className=\"animate-spin\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden>\r\n <circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" strokeWidth=\"2.5\" opacity=\"0.25\" />\r\n <path d=\"M21 12a9 9 0 0 0-9-9\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" />\r\n </svg>\r\n );\r\n}\r\n","import { useEffect, useState, type ReactNode } from 'react';\r\nimport { Link, useLocation } from 'react-router-dom';\r\nimport { HOTKEY_HINT } from '../lib/hotkeys.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport LocaleToggle from './LocaleToggle.tsx';\r\nimport ThemeToggle from './ThemeToggle.tsx';\r\nimport VersionNotice from './VersionNotice.tsx';\r\n\r\ninterface NavItem {\r\n to: string;\r\n labelKey: 'nav.projects' | 'nav.disk' | 'nav.import';\r\n icon: ReactNode;\r\n match: (pathname: string) => boolean;\r\n}\r\n\r\nconst NAV: NavItem[] = [\r\n {\r\n to: '/',\r\n labelKey: 'nav.projects',\r\n icon: <FolderIcon />,\r\n match: (p) => p === '/' || p.startsWith('/projects/'),\r\n },\r\n {\r\n to: '/disk',\r\n labelKey: 'nav.disk',\r\n icon: <DiskIcon />,\r\n match: (p) => p === '/disk' || p.startsWith('/disk/'),\r\n },\r\n {\r\n to: '/import',\r\n labelKey: 'nav.import',\r\n icon: <ImportIcon />,\r\n match: (p) => p === '/import' || p.startsWith('/import/'),\r\n },\r\n];\r\n\r\nexport default function Sidebar({ onSearchOpen }: { onSearchOpen?: () => void }) {\r\n const t = useT();\r\n const { pathname } = useLocation();\r\n const [open, setOpen] = useState(false);\r\n\r\n useEffect(() => {\r\n const handler = () => setOpen(false);\r\n window.addEventListener('resize', handler);\r\n return () => window.removeEventListener('resize', handler);\r\n }, []);\r\n\r\n return (\r\n <>\r\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\">\r\n <Brand />\r\n <div className=\"flex items-center gap-2\">\r\n {onSearchOpen && (\r\n <button\r\n type=\"button\"\r\n onClick={onSearchOpen}\r\n aria-label={t('search.action.open')}\r\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)]\"\r\n >\r\n <SearchIcon />\r\n </button>\r\n )}\r\n <button\r\n type=\"button\"\r\n onClick={() => setOpen((v) => !v)}\r\n aria-label={t('nav.toggleNav')}\r\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)]\"\r\n >\r\n <MenuIcon open={open} />\r\n </button>\r\n </div>\r\n </div>\r\n\r\n {open && (\r\n <button\r\n type=\"button\"\r\n aria-label={t('nav.closeNav')}\r\n onClick={() => setOpen(false)}\r\n className=\"fixed inset-0 z-40 bg-[var(--color-canvas)]/70 backdrop-blur-sm lg:hidden\"\r\n />\r\n )}\r\n\r\n <aside\r\n className={\r\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 ' +\r\n (open ? 'translate-x-0' : '-translate-x-full')\r\n }\r\n >\r\n <div className=\"flex h-[68px] items-center px-5\">\r\n <Brand />\r\n </div>\r\n\r\n {onSearchOpen && (\r\n <div className=\"px-4 pb-2\">\r\n <button\r\n type=\"button\"\r\n onClick={() => {\r\n setOpen(false);\r\n onSearchOpen();\r\n }}\r\n aria-label={t('search.action.open')}\r\n className=\"surface-card is-interactive flex w-full items-center gap-2.5 px-3 py-3 text-left\"\r\n style={{ borderRadius: 'var(--radius-input)' }}\r\n >\r\n <SearchIcon className=\"text-[var(--color-fg-muted)]\" />\r\n <span className=\"flex-1 truncate text-[13px] text-[var(--color-fg-muted)]\">\r\n {t('search.action.open')}\r\n </span>\r\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)]\">\r\n {HOTKEY_HINT}\r\n </kbd>\r\n </button>\r\n </div>\r\n )}\r\n\r\n <nav className=\"flex-1 overflow-y-auto px-4 py-3\">\r\n <p className=\"eyebrow px-2 pb-2\">{t('nav.workspace')}</p>\r\n <ul className=\"space-y-1\">\r\n {NAV.map((item) => {\r\n const isActive = item.match(pathname);\r\n return (\r\n <li key={item.to}>\r\n <Link\r\n to={item.to}\r\n aria-current={isActive ? 'page' : undefined}\r\n onClick={() => setOpen(false)}\r\n className={\r\n 'group flex items-center gap-3 rounded-[var(--radius-input)] border px-4 py-3 text-sm transition ' +\r\n (isActive\r\n ? 'border-[var(--color-hairline)] bg-[var(--color-surface)] text-[var(--color-fg-primary)] shadow-[var(--shadow-rise)]'\r\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)]')\r\n }\r\n >\r\n <span\r\n className={\r\n 'transition-colors ' +\r\n (isActive\r\n ? 'text-[var(--color-accent)]'\r\n : 'text-[var(--color-fg-muted)] group-hover:text-[var(--color-accent)]')\r\n }\r\n >\r\n {item.icon}\r\n </span>\r\n <span className=\"font-medium tracking-tight\">{t(item.labelKey)}</span>\r\n </Link>\r\n </li>\r\n );\r\n })}\r\n </ul>\r\n </nav>\r\n\r\n <div className=\"surface-card mx-4 mb-4 space-y-3 p-4\">\r\n <div className=\"flex items-center justify-between\">\r\n <span className=\"eyebrow\">{t('nav.language')}</span>\r\n <LocaleToggle />\r\n </div>\r\n <div className=\"flex items-center justify-between\">\r\n <span className=\"eyebrow\">{t('nav.theme')}</span>\r\n <ThemeToggle />\r\n </div>\r\n <VersionNotice />\r\n <p className=\"font-mono text-[10px] leading-snug text-[var(--color-fg-faint)]\">\r\n {t('app.brand.footnote')}\r\n </p>\r\n </div>\r\n </aside>\r\n </>\r\n );\r\n}\r\n\r\nfunction Brand() {\r\n const t = useT();\r\n return (\r\n <div className=\"flex items-center gap-2.5\">\r\n <span\r\n aria-hidden\r\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)]\"\r\n >\r\n <Glyph />\r\n </span>\r\n <span className=\"flex flex-col leading-tight\">\r\n <span className=\"font-display text-[15px] font-medium tracking-tight text-[var(--color-fg-primary)]\">\r\n {t('app.brand.title')}\r\n </span>\r\n <span className=\"font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--color-fg-muted)]\">\r\n {t('app.brand.subtitle')}\r\n </span>\r\n </span>\r\n </div>\r\n );\r\n}\r\n\r\nfunction Glyph() {\r\n return (\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" aria-hidden>\r\n <path d=\"M19 7.5A8 8 0 1 0 19 16.5\" />\r\n <path d=\"M15.5 9.5a4.5 4.5 0 1 0 0 5\" opacity=\"0.45\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction FolderIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\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\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction DiskIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <ellipse cx=\"12\" cy=\"6.5\" rx=\"8\" ry=\"2.5\" />\r\n <path d=\"M4 6.5v5c0 1.4 3.6 2.5 8 2.5s8-1.1 8-2.5v-5\" />\r\n <path d=\"M4 11.5v5c0 1.4 3.6 2.5 8 2.5s8-1.1 8-2.5v-5\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction ImportIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <path d=\"M12 14V3\" />\r\n <path d=\"M8 10l4 4 4-4\" />\r\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\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction SearchIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.7\"\r\n strokeLinecap=\"round\"\r\n className={className}\r\n aria-hidden\r\n >\r\n <circle cx=\"11\" cy=\"11\" r=\"6.2\" />\r\n <path d=\"M20 20l-4.3-4.3\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction MenuIcon({ open }: { open: boolean }) {\r\n return (\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" aria-hidden>\r\n {open ? (\r\n <>\r\n <path d=\"M6 6l12 12\" />\r\n <path d=\"M18 6L6 18\" />\r\n </>\r\n ) : (\r\n <>\r\n <path d=\"M4 7h16\" />\r\n <path d=\"M4 12h16\" />\r\n <path d=\"M4 17h12\" />\r\n </>\r\n )}\r\n </svg>\r\n );\r\n}\r\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';\r\nimport { motion } from 'motion/react';\r\nimport { useEffect, useRef, useState } from 'react';\r\nimport { api, type ExportResult, type SessionSummary } from '../lib/api.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\n\r\ninterface Props {\r\n projectId: string;\r\n sessions: SessionSummary[];\r\n onClose: () => void;\r\n}\r\n\r\nexport default function ExportDialog({ projectId, sessions, onClose }: Props) {\r\n const t = useT();\r\n const [destDir, setDestDir] = useState('');\r\n\r\n const mutation = useMutation({\r\n mutationFn: () =>\r\n api<ExportResult>(`/api/projects/${encodeURIComponent(projectId)}/export`, {\r\n method: 'POST',\r\n body: JSON.stringify({ sessionIds: sessions.map((s) => s.id), destDir: destDir.trim() }),\r\n }),\r\n });\r\n\r\n const isPendingRef = useRef(mutation.isPending);\r\n isPendingRef.current = mutation.isPending;\r\n useEffect(() => {\r\n function onKey(e: KeyboardEvent) {\r\n if (e.key === 'Escape' && !isPendingRef.current) onClose();\r\n }\r\n window.addEventListener('keydown', onKey);\r\n return () => window.removeEventListener('keydown', onKey);\r\n }, [onClose]);\r\n\r\n const result = mutation.data;\r\n const showResult = !!result;\r\n const canSubmit = destDir.trim() !== '' && !mutation.isPending;\r\n\r\n return (\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n exit={{ opacity: 0 }}\r\n transition={{ duration: 0.18 }}\r\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]\"\r\n onClick={() => !mutation.isPending && onClose()}\r\n >\r\n <motion.div\r\n initial={{ y: 8, opacity: 0 }}\r\n animate={{ y: 0, opacity: 1 }}\r\n transition={{ duration: 0.24, ease: [0.16, 1, 0.3, 1] }}\r\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)]\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <header className=\"flex items-start justify-between gap-4 border-b border-[var(--color-hairline)] px-6 py-5\">\r\n <div>\r\n <p className=\"eyebrow text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\r\n {showResult ? t('export.eyebrow.result') : t('export.eyebrow.confirm')}\r\n </p>\r\n <h2 className=\"mt-1 font-display text-2xl font-light tracking-tight text-[var(--color-fg-primary)]\">\r\n {showResult ? t('export.title.result') : t('export.title.confirm')}\r\n </h2>\r\n {!showResult && (\r\n <p className=\"mt-1.5 text-sm text-[var(--color-fg-muted)]\">\r\n {t('export.summary', { n: sessions.length })}\r\n </p>\r\n )}\r\n </div>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n disabled={mutation.isPending}\r\n aria-label={t('export.close')}\r\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\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\">\r\n <path d=\"M6 6l12 12M18 6L6 18\" />\r\n </svg>\r\n </button>\r\n </header>\r\n\r\n {!showResult && (\r\n <div className=\"space-y-4 px-6 py-5\">\r\n <label className=\"block\">\r\n <span className=\"eyebrow\">{t('export.destLabel')}</span>\r\n <input\r\n type=\"text\"\r\n value={destDir}\r\n onChange={(e) => setDestDir(e.target.value)}\r\n placeholder={t('export.destPlaceholder')}\r\n spellCheck={false}\r\n autoFocus\r\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)]\"\r\n />\r\n <span className=\"mt-1.5 block text-xs text-[var(--color-fg-muted)]\">\r\n {t('export.destHint')}\r\n </span>\r\n </label>\r\n\r\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)]\">\r\n {t('export.privacy')}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {showResult && result && (\r\n <div className=\"space-y-3 px-6 py-5 text-sm\">\r\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)]\">\r\n {t('export.success', {\r\n sessions: result.sessionsExported,\r\n memory: result.memoryFilesExported,\r\n lines: result.historyLinesExported,\r\n })}\r\n </div>\r\n <p className=\"break-all font-mono text-xs text-[var(--color-fg-muted)]\">\r\n {t('export.successDest', { dest: result.destDir })}\r\n </p>\r\n </div>\r\n )}\r\n\r\n {mutation.error && (\r\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)]\">\r\n {(mutation.error as Error).message}\r\n </p>\r\n )}\r\n\r\n <footer className=\"flex justify-end gap-2 border-t border-[var(--color-hairline)] px-6 py-4\">\r\n {!showResult ? (\r\n <>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n disabled={mutation.isPending}\r\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\"\r\n >\r\n {t('common.cancel')}\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => mutation.mutate()}\r\n disabled={!canSubmit}\r\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\"\r\n >\r\n {mutation.isPending ? t('export.btn.pending') : t('export.btn.confirm')}\r\n </button>\r\n </>\r\n ) : (\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\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\"\r\n >\r\n {t('common.done')}\r\n </button>\r\n )}\r\n </footer>\r\n </motion.div>\r\n </motion.div>\r\n );\r\n}\r\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\r\n\r\ninterface Props {\r\n eyebrow?: ReactNode;\r\n title: ReactNode;\r\n actions?: ReactNode;\r\n meta?: ReactNode;\r\n /** Plain-text version of the title used as the input's initial value during edit. */\r\n editableValue?: string;\r\n /** When provided, an edit affordance appears next to the title. */\r\n onTitleEdit?: (next: string) => Promise<void>;\r\n}\r\n\r\nexport default function PageHeader({\r\n eyebrow,\r\n title,\r\n actions,\r\n meta,\r\n editableValue,\r\n onTitleEdit,\r\n}: Props) {\r\n return (\r\n <header className=\"relative\">\r\n {eyebrow && <div className=\"eyebrow mb-1\">{eyebrow}</div>}\r\n <div className=\"flex flex-wrap items-center justify-between gap-x-6 gap-y-2\">\r\n <div className=\"min-w-0 flex-1\">\r\n <TitleSlot\r\n title={title}\r\n editableValue={editableValue}\r\n onTitleEdit={onTitleEdit}\r\n />\r\n </div>\r\n {actions && <div className=\"flex shrink-0 items-center gap-2\">{actions}</div>}\r\n </div>\r\n {meta && (\r\n <div className=\"mt-2 flex flex-wrap items-baseline gap-x-3 gap-y-1 text-xs\">\r\n {meta}\r\n </div>\r\n )}\r\n </header>\r\n );\r\n}\r\n\r\nconst TITLE_CLASS =\r\n 'font-display text-[clamp(1.375rem,2.4vw,1.75rem)] font-light leading-[1.15] tracking-[-0.015em] text-[var(--color-fg-primary)]';\r\n\r\nfunction TitleSlot({\r\n title,\r\n editableValue,\r\n onTitleEdit,\r\n}: {\r\n title: ReactNode;\r\n editableValue?: string;\r\n onTitleEdit?: Props['onTitleEdit'];\r\n}) {\r\n const [editing, setEditing] = useState(false);\r\n const [draft, setDraft] = useState(editableValue ?? '');\r\n const [submitting, setSubmitting] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n\r\n useEffect(() => {\r\n if (!editing && editableValue !== undefined) setDraft(editableValue);\r\n }, [editing, editableValue]);\r\n\r\n useEffect(() => {\r\n if (editing) inputRef.current?.select();\r\n }, [editing]);\r\n\r\n if (!onTitleEdit) {\r\n return <h1 className={TITLE_CLASS}>{title}</h1>;\r\n }\r\n\r\n function startEdit() {\r\n setDraft(editableValue ?? '');\r\n setError(null);\r\n setEditing(true);\r\n }\r\n\r\n async function commit() {\r\n const next = draft.trim();\r\n if (!onTitleEdit || !next || next === (editableValue ?? '')) {\r\n setEditing(false);\r\n return;\r\n }\r\n setSubmitting(true);\r\n setError(null);\r\n try {\r\n await onTitleEdit(next);\r\n setEditing(false);\r\n } catch (err) {\r\n setError((err as Error).message);\r\n } finally {\r\n setSubmitting(false);\r\n }\r\n }\r\n\r\n if (editing) {\r\n return (\r\n <div>\r\n <input\r\n ref={inputRef}\r\n value={draft}\r\n disabled={submitting}\r\n onChange={(e) => setDraft(e.target.value)}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Enter') {\r\n e.preventDefault();\r\n void commit();\r\n } else if (e.key === 'Escape') {\r\n e.preventDefault();\r\n setEditing(false);\r\n setError(null);\r\n }\r\n }}\r\n onBlur={() => {\r\n if (!submitting) {\r\n setEditing(false);\r\n setError(null);\r\n }\r\n }}\r\n maxLength={200}\r\n className={\r\n TITLE_CLASS +\r\n ' w-full bg-transparent border-b border-[var(--color-accent)] outline-none focus:outline-none disabled:opacity-60'\r\n }\r\n />\r\n {error && (\r\n <p className=\"mt-1 text-xs text-[var(--color-danger)]\">{error}</p>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"group flex items-center gap-3\">\r\n <h1 className={TITLE_CLASS}>{title}</h1>\r\n <button\r\n type=\"button\"\r\n onClick={startEdit}\r\n aria-label=\"Rename\"\r\n title=\"Rename\"\r\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\"\r\n >\r\n <PencilIcon />\r\n </button>\r\n </div>\r\n );\r\n}\r\n\r\nfunction PencilIcon() {\r\n return (\r\n <svg\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.7\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden\r\n >\r\n <path d=\"M12 20h9\" />\r\n <path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z\" />\r\n </svg>\r\n );\r\n}\r\n\r\nexport function MetaItem({ label, value }: { label: string; value: ReactNode }) {\r\n return (\r\n <span className=\"inline-flex items-baseline gap-1.5\">\r\n <span className=\"eyebrow\">{label}</span>\r\n <span className=\"font-mono text-[13px] tabular-nums text-[var(--color-fg-primary)]\">{value}</span>\r\n </span>\r\n );\r\n}\r\n\r\nexport function Sep() {\r\n return <span aria-hidden className=\"text-[var(--color-fg-faint)]\">·</span>;\r\n}\r\n","export const RECENT_ACTIVITY_WINDOW_MIN = 5;\r\nexport const MAX_SESSION_MESSAGES = 5000;\r\n\r\n// Claude Code writes this synthetic `user` record when the operator aborts a turn\r\n// (Esc / Ctrl-C). It means the turn was *stopped*, not that Claude is still\r\n// working — so the \"working\" heuristic treats a trailing interrupt as idle.\r\nexport const INTERRUPTED_MARKER_RE = /^\\s*\\[Request interrupted by user/;\r\n","import type { SessionSummary } from '../lib/api.ts';\r\nimport { RECENT_ACTIVITY_WINDOW_MIN } from '../lib/constants.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\n\r\ntype Variant = 'working' | 'live' | 'recent' | 'idle';\r\n\r\nfunction variantOf(s: SessionSummary): Variant {\r\n if (s.isWorking) return 'working';\r\n if (s.isLivePid) return 'live';\r\n if (s.isRecentlyActive) return 'recent';\r\n return 'idle';\r\n}\r\n\r\nexport function StatusDot({ session, withLabel = true }: { session: SessionSummary; withLabel?: boolean }) {\r\n const t = useT();\r\n const v = variantOf(session);\r\n const title =\r\n v === 'working'\r\n ? t('status.tooltip.working')\r\n : v === 'live'\r\n ? t('status.tooltip.live', { pid: session.livePid ?? '?' })\r\n : v === 'recent'\r\n ? t('status.tooltip.recent', { n: RECENT_ACTIVITY_WINDOW_MIN })\r\n : t('status.tooltip.idle');\r\n\r\n const label =\r\n v === 'working'\r\n ? t('status.working')\r\n : v === 'live'\r\n ? t('status.live', { pid: session.livePid ?? '?' })\r\n : v === 'recent'\r\n ? t('status.recent')\r\n : t('status.idle');\r\n\r\n // working + live share the accent ink; recent stays secondary; idle is faintest.\r\n const labelClass =\r\n v === 'idle'\r\n ? 'text-[var(--color-fg-faint)]'\r\n : v === 'recent'\r\n ? 'text-[var(--color-fg-secondary)]'\r\n : 'text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]';\r\n\r\n return (\r\n <span title={title} className=\"inline-flex items-center gap-2 text-xs\">\r\n <Dot variant={v} />\r\n {withLabel && (\r\n <span className={'font-mono uppercase tracking-[0.14em] ' + labelClass}>{label}</span>\r\n )}\r\n </span>\r\n );\r\n}\r\n\r\nfunction Dot({ variant }: { variant: Variant }) {\r\n if (variant === 'working') {\r\n // Marching three-dot breather — reads as \"actively doing something\", distinct\r\n // from the live pulse (idle-but-alive).\r\n return (\r\n <span aria-hidden className=\"loading-dots text-[var(--color-accent)]\">\r\n <span />\r\n <span />\r\n <span />\r\n </span>\r\n );\r\n }\r\n if (variant === 'live') {\r\n return (\r\n <span aria-hidden className=\"relative inline-flex h-2.5 w-2.5\">\r\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)] pulse-amber\" />\r\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)]\" />\r\n </span>\r\n );\r\n }\r\n if (variant === 'recent') {\r\n return (\r\n <span\r\n aria-hidden\r\n className=\"inline-block h-2.5 w-2.5 rounded-full bg-[var(--color-accent)]\"\r\n />\r\n );\r\n }\r\n return (\r\n <span\r\n aria-hidden\r\n className=\"inline-block h-2.5 w-2.5 rounded-full border border-[var(--color-hairline-strong)]\"\r\n />\r\n );\r\n}\r\n\r\nexport default StatusDot;\r\n","import type { Variants } from 'motion/react';\r\n\r\nexport const staggerParent: Variants = {\r\n hidden: {},\r\n show: {\r\n transition: { staggerChildren: 0.035, delayChildren: 0.04 },\r\n },\r\n};\r\n\r\nexport const fadeUpItem: Variants = {\r\n hidden: { opacity: 0, y: 8 },\r\n show: {\r\n opacity: 1,\r\n y: 0,\r\n transition: { duration: 0.36, ease: [0.16, 1, 0.3, 1] },\r\n },\r\n};\r\n\r\nexport const subtleFade: Variants = {\r\n hidden: { opacity: 0 },\r\n show: { opacity: 1, transition: { duration: 0.4 } },\r\n};\r\n","import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\r\nimport { motion } from 'motion/react';\r\nimport { useMemo, useState } from 'react';\r\nimport { Link, useParams } from 'react-router-dom';\r\nimport Breadcrumbs, { BreadcrumbFolderIcon } from '../components/Breadcrumbs.tsx';\r\nimport ExportDialog from '../components/ExportDialog.tsx';\r\nimport { Loading } from '../components/Loading.tsx';\r\nimport PageHeader, { MetaItem, Sep } from '../components/PageHeader.tsx';\r\nimport StatusDot from '../components/StatusDot.tsx';\r\nimport {\r\n api,\r\n type DeleteResult,\r\n type MemoryResponse,\r\n type ProjectSummary,\r\n type RevealProjectResult,\r\n type SessionSummary,\r\n} from '../lib/api.ts';\r\nimport { formatBytes, formatRelativeTime } from '../lib/format.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport { fadeUpItem, staggerParent } from '../lib/motion.ts';\r\nimport { queryKeys } from '../lib/query-keys.ts';\r\n\r\ninterface BulkProgress {\r\n index: number;\r\n total: number;\r\n}\r\n\r\ninterface BulkOutcome {\r\n ok: { sessionId: string; title: string }[];\r\n skipped: { sessionId: string; title: string; reason: string }[];\r\n failed: { sessionId: string; title: string; error: string }[];\r\n}\r\n\r\nexport default function ProjectDetail() {\r\n const t = useT();\r\n const queryClient = useQueryClient();\r\n const { projectId } = useParams<{ projectId: string }>();\r\n const id = projectId ?? '';\r\n const [selected, setSelected] = useState<Set<string>>(new Set());\r\n const [selectMode, setSelectMode] = useState(false);\r\n const [showExport, setShowExport] = useState(false);\r\n // 批量删除运行态:null 表示空闲;progress 推进时显示「正在删除 i/total」\r\n const [bulkProgress, setBulkProgress] = useState<BulkProgress | null>(null);\r\n const [bulkOutcome, setBulkOutcome] = useState<BulkOutcome | null>(null);\r\n const [bulkDetailOpen, setBulkDetailOpen] = useState(false);\r\n\r\n const sessionsQuery = useQuery({\r\n queryKey: queryKeys.projectSessions(id),\r\n queryFn: () => api<SessionSummary[]>(`/api/projects/${encodeURIComponent(id)}/sessions`),\r\n enabled: !!id,\r\n });\r\n\r\n const projectsQuery = useQuery({\r\n queryKey: queryKeys.projects(),\r\n queryFn: () => api<ProjectSummary[]>('/api/projects'),\r\n });\r\n\r\n const memoryQuery = useQuery({\r\n queryKey: queryKeys.projectMemory(id),\r\n queryFn: () => api<MemoryResponse>(`/api/projects/${encodeURIComponent(id)}/memory`),\r\n enabled: !!id,\r\n });\r\n const memoryCount = memoryQuery.data?.entries.length ?? 0;\r\n\r\n const revealMutation = useMutation({\r\n mutationFn: () =>\r\n api<RevealProjectResult>(`/api/projects/${encodeURIComponent(id)}/reveal`, {\r\n method: 'POST',\r\n }),\r\n onError: (err: Error) => {\r\n window.alert(t('project.action.openFolderFailed', { msg: err.message }));\r\n },\r\n });\r\n\r\n const project = useMemo(\r\n () => projectsQuery.data?.find((p) => p.id === id),\r\n [projectsQuery.data, id],\r\n );\r\n\r\n const sessions = sessionsQuery.data ?? [];\r\n const selectedSessions = useMemo(\r\n () => sessions.filter((s) => selected.has(s.id)),\r\n [sessions, selected],\r\n );\r\n const sessionsToExport = selected.size > 0 ? selectedSessions : sessions;\r\n const projectBytes = useMemo(() => sessions.reduce((a, s) => a + totalBytes(s), 0), [sessions]);\r\n // Disjoint buckets: working ⊂ live, so subtract working from the live count to\r\n // keep the three meta tallies mutually exclusive (matches the per-row StatusDot).\r\n const workingCount = useMemo(() => sessions.filter((s) => s.isWorking).length, [sessions]);\r\n const liveCount = useMemo(\r\n () => sessions.filter((s) => s.isLivePid && !s.isWorking).length,\r\n [sessions],\r\n );\r\n const recentCount = useMemo(\r\n () => sessions.filter((s) => s.isRecentlyActive && !s.isLivePid).length,\r\n [sessions],\r\n );\r\n\r\n function toggle(sid: string) {\r\n const next = new Set(selected);\r\n if (next.has(sid)) next.delete(sid);\r\n else next.add(sid);\r\n setSelected(next);\r\n }\r\n\r\n function toggleAll() {\r\n if (selected.size === sessions.length) setSelected(new Set());\r\n else setSelected(new Set(sessions.map((s) => s.id)));\r\n }\r\n\r\n function enterSelectMode() {\r\n setSelectMode(true);\r\n setBulkOutcome(null);\r\n setBulkDetailOpen(false);\r\n }\r\n\r\n function exitSelectMode() {\r\n setSelectMode(false);\r\n setSelected(new Set());\r\n }\r\n\r\n // 串行调用 DELETE /api/sessions(每次 items 长度 1),失败继续推进,\r\n // 完成后写入 outcome 并退出选择模式;不在完成时跳转,留给用户查看反馈。\r\n async function runBulkDelete() {\r\n const targets = selectedSessions;\r\n if (targets.length === 0) return;\r\n const outcome: BulkOutcome = { ok: [], skipped: [], failed: [] };\r\n setBulkOutcome(null);\r\n setBulkDetailOpen(false);\r\n let i = 0;\r\n for (const s of targets) {\r\n i++;\r\n const title = s.customTitle ?? s.title;\r\n setBulkProgress({ index: i, total: targets.length });\r\n try {\r\n const res = await api<DeleteResult>('/api/sessions', {\r\n method: 'DELETE',\r\n body: JSON.stringify({ items: [{ projectId: id, sessionId: s.id }] }),\r\n });\r\n if (res.deleted.length > 0) {\r\n outcome.ok.push({ sessionId: s.id, title });\r\n } else if (res.skipped.length > 0) {\r\n outcome.skipped.push({\r\n sessionId: s.id,\r\n title,\r\n reason: res.skipped[0]?.reason ?? 'unknown',\r\n });\r\n } else {\r\n // 后端既未删也未跳过的边界情况,记为失败避免静默\r\n outcome.failed.push({ sessionId: s.id, title, error: 'no-op' });\r\n }\r\n } catch (err) {\r\n outcome.failed.push({\r\n sessionId: s.id,\r\n title,\r\n error: err instanceof Error ? err.message : String(err),\r\n });\r\n }\r\n }\r\n setBulkProgress(null);\r\n setBulkOutcome(outcome);\r\n setBulkDetailOpen(outcome.skipped.length + outcome.failed.length > 0);\r\n setSelectMode(false);\r\n setSelected(new Set());\r\n // 刷新列表 / 项目元数据 / 磁盘统计\r\n queryClient.invalidateQueries({ queryKey: queryKeys.projects() });\r\n queryClient.invalidateQueries({ queryKey: queryKeys.projectSessions(id) });\r\n queryClient.invalidateQueries({ queryKey: queryKeys.diskUsage() });\r\n }\r\n\r\n const cwd = project?.decodedCwd ?? id;\r\n const parts = cwd.split(/[\\\\/]+/).filter(Boolean);\r\n const tail = parts.at(-1) ?? cwd;\r\n const head = parts.slice(0, -1).join('/');\r\n\r\n return (\r\n <section>\r\n <Breadcrumbs\r\n items={[\r\n { label: t('session.crumbProjects'), to: '/' },\r\n { label: tail, mono: true, icon: <BreadcrumbFolderIcon /> },\r\n ]}\r\n />\r\n\r\n <div className=\"surface-card mt-6 p-6\">\r\n <PageHeader\r\n eyebrow={\r\n <span className=\"inline-flex items-center gap-2\">\r\n {head ? (\r\n <span className=\"font-mono normal-case tracking-normal\">{head}/</span>\r\n ) : (\r\n t('project.eyebrow')\r\n )}\r\n {project?.cwdResolved === false && (\r\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)]\">\r\n <span aria-hidden className=\"h-1.5 w-1.5 rounded-full bg-[var(--color-danger)]\" />\r\n {t('project.warn.missingDir')}\r\n </span>\r\n )}\r\n </span>\r\n }\r\n title={<span className=\"font-mono\">{tail}</span>}\r\n actions={\r\n <>\r\n <button\r\n type=\"button\"\r\n onClick={() => revealMutation.mutate()}\r\n disabled={\r\n revealMutation.isPending || project?.cwdResolved === false\r\n }\r\n title={\r\n project?.cwdResolved === false\r\n ? t('project.action.openFolderTooltipMissing')\r\n : cwd\r\n }\r\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)]\"\r\n >\r\n <FolderOpenIcon />\r\n {t('project.action.openFolder')}\r\n </button>\r\n <Link\r\n to={`/projects/${encodeURIComponent(id)}/memory`}\r\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)]\"\r\n >\r\n <BrainIcon />\r\n {memoryCount > 0\r\n ? t('memory.action.openCount', { n: memoryCount })\r\n : t('memory.action.open')}\r\n </Link>\r\n <button\r\n type=\"button\"\r\n onClick={() => setShowExport(true)}\r\n disabled={sessions.length === 0}\r\n title={selected.size > 0 ? `${selected.size}` : undefined}\r\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)]\"\r\n >\r\n <ExportIcon />\r\n {selected.size > 0\r\n ? `${t('export.action')} · ${selected.size}`\r\n : t('export.action')}\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => (selectMode ? exitSelectMode() : enterSelectMode())}\r\n disabled={sessions.length === 0 || bulkProgress !== null}\r\n aria-pressed={selectMode}\r\n className={\r\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 ' +\r\n (selectMode\r\n ? 'border-[var(--color-accent)] bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\r\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)]')\r\n }\r\n >\r\n <CheckSquareIcon />\r\n {selectMode ? t('project.action.exitSelect') : t('project.action.select')}\r\n </button>\r\n </>\r\n }\r\n meta={\r\n sessions.length > 0 ? (\r\n <>\r\n <MetaItem label={t('project.meta.sessions')} value={sessions.length} />\r\n <Sep />\r\n <MetaItem label={t('project.meta.onDisk')} value={formatBytes(projectBytes)} />\r\n {workingCount > 0 && (\r\n <>\r\n <Sep />\r\n <MetaItem\r\n label={t('project.meta.working')}\r\n value={\r\n <span className=\"text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\r\n {workingCount}\r\n </span>\r\n }\r\n />\r\n </>\r\n )}\r\n <Sep />\r\n <MetaItem\r\n label={t('project.meta.live')}\r\n value={\r\n liveCount > 0 ? (\r\n <span className=\"text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\r\n {liveCount}\r\n </span>\r\n ) : (\r\n 0\r\n )\r\n }\r\n />\r\n <Sep />\r\n <MetaItem label={t('project.meta.recent')} value={recentCount} />\r\n </>\r\n ) : null\r\n }\r\n />\r\n </div>\r\n\r\n {sessionsQuery.isLoading && <Loading label={t('common.readingSessions')} className=\"mt-10\" />}\r\n {sessionsQuery.error && (\r\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)]\">\r\n {t('common.failedSessions')}: {(sessionsQuery.error as Error).message}\r\n </p>\r\n )}\r\n {sessionsQuery.data && sessionsQuery.data.length === 0 && (\r\n <p className=\"mt-10 text-sm text-[var(--color-fg-muted)]\">{t('common.noSessions')}</p>\r\n )}\r\n\r\n {sessions.length > 0 && (\r\n <div className=\"surface-card mt-6 p-6\">\r\n {(selectMode || bulkProgress || bulkOutcome) && (\r\n <BulkBar\r\n selectMode={selectMode}\r\n selectedCount={selected.size}\r\n totalCount={sessions.length}\r\n progress={bulkProgress}\r\n outcome={bulkOutcome}\r\n detailOpen={bulkDetailOpen}\r\n onToggleAll={toggleAll}\r\n onDelete={runBulkDelete}\r\n onCancel={exitSelectMode}\r\n onToggleDetail={() => setBulkDetailOpen((v) => !v)}\r\n onDismiss={() => {\r\n setBulkOutcome(null);\r\n setBulkDetailOpen(false);\r\n }}\r\n t={t}\r\n />\r\n )}\r\n <div className=\"flex items-baseline justify-between\">\r\n <h2 className=\"font-display text-xl font-light tracking-tight text-[var(--color-fg-primary)]\">\r\n {t('project.heading')}\r\n </h2>\r\n </div>\r\n <div className=\"rule-dotted mt-3\" aria-hidden />\r\n\r\n <div className=\"mt-4 -mx-6 overflow-x-auto px-6\">\r\n <table className=\"w-full text-sm\">\r\n <thead>\r\n <tr className=\"text-left\">\r\n {selectMode && <th className=\"w-9 px-2 py-3\" />}\r\n <th className=\"px-2 py-3 eyebrow\">{t('project.col.title')}</th>\r\n <th className=\"px-2 py-3 eyebrow text-right\">{t('project.col.msgs')}</th>\r\n <th className=\"px-2 py-3 eyebrow text-right\">{t('project.col.last')}</th>\r\n <th className=\"px-2 py-3 eyebrow text-right\">{t('project.col.size')}</th>\r\n <th className=\"px-2 py-3 eyebrow\">{t('project.col.status')}</th>\r\n </tr>\r\n </thead>\r\n <motion.tbody\r\n initial=\"hidden\"\r\n animate=\"show\"\r\n variants={staggerParent}\r\n className=\"border-t border-[var(--color-hairline)]\"\r\n >\r\n {sessions.map((s) => {\r\n const isSel = selected.has(s.id);\r\n const displayTitle = s.customTitle ?? s.title;\r\n return (\r\n <motion.tr\r\n key={s.id}\r\n variants={fadeUpItem}\r\n data-active={isSel && selectMode ? 'true' : undefined}\r\n className={\r\n 'ribbon-row border-b border-[var(--color-hairline)] transition-colors ' +\r\n (isSel && selectMode\r\n ? 'bg-[var(--color-accent-soft)]/40'\r\n : 'hover:bg-[var(--color-sunken)]')\r\n }\r\n >\r\n {selectMode && (\r\n <td className=\"px-2 py-3 align-top\">\r\n <input\r\n type=\"checkbox\"\r\n aria-label={displayTitle}\r\n checked={isSel}\r\n onChange={() => toggle(s.id)}\r\n disabled={bulkProgress !== null}\r\n className=\"mt-0.5 h-3.5 w-3.5 cursor-pointer accent-[var(--color-accent)] disabled:cursor-not-allowed\"\r\n />\r\n </td>\r\n )}\r\n <td className=\"px-2 py-3 align-top\">\r\n <Link\r\n to={`/projects/${encodeURIComponent(id)}/sessions/${s.id}`}\r\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)]\"\r\n title={displayTitle}\r\n >\r\n {displayTitle}\r\n </Link>\r\n <div className=\"mt-1 truncate font-mono text-[10.5px] tracking-[0.04em] text-[var(--color-fg-faint)]\">\r\n {s.id}\r\n </div>\r\n </td>\r\n <td className=\"px-2 py-3 text-right align-top font-mono tabular-nums text-[var(--color-fg-secondary)]\">\r\n {s.messageCount.toLocaleString()}\r\n </td>\r\n <td className=\"px-2 py-3 text-right align-top font-mono text-[12.5px] text-[var(--color-fg-secondary)]\">\r\n {formatRelativeTime(s.lastAt)}\r\n </td>\r\n <td\r\n className=\"px-2 py-3 text-right align-top font-mono tabular-nums text-[var(--color-fg-secondary)]\"\r\n title={breakdown(s)}\r\n >\r\n {formatBytes(totalBytes(s))}\r\n </td>\r\n <td className=\"px-2 py-3 align-top\">\r\n <StatusDot session={s} />\r\n </td>\r\n </motion.tr>\r\n );\r\n })}\r\n </motion.tbody>\r\n </table>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {showExport && (\r\n <ExportDialog\r\n projectId={id}\r\n sessions={sessionsToExport}\r\n onClose={() => setShowExport(false)}\r\n />\r\n )}\r\n </section>\r\n );\r\n}\r\n\r\nfunction totalBytes(s: SessionSummary): number {\r\n const r = s.relatedBytes;\r\n return r.jsonl + r.subdir + r.fileHistory + r.sessionEnv;\r\n}\r\n\r\nfunction breakdown(s: SessionSummary): string {\r\n const r = s.relatedBytes;\r\n return [\r\n `jsonl ${formatBytes(r.jsonl)}`,\r\n `subdir ${formatBytes(r.subdir)}`,\r\n `file-history ${formatBytes(r.fileHistory)}`,\r\n `session-env ${formatBytes(r.sessionEnv)}`,\r\n ].join(' · ');\r\n}\r\n\r\nfunction TrashIcon() {\r\n return (\r\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <path d=\"M3 6h18\" />\r\n <path d=\"M8 6V4.5A1.5 1.5 0 0 1 9.5 3h5A1.5 1.5 0 0 1 16 4.5V6\" />\r\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\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction CheckSquareIcon() {\r\n return (\r\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <rect x=\"3.5\" y=\"3.5\" width=\"17\" height=\"17\" rx=\"2.5\" />\r\n <path d=\"M8 12.2l3 3 5.5-6\" />\r\n </svg>\r\n );\r\n}\r\n\r\ninterface BulkBarProps {\r\n selectMode: boolean;\r\n selectedCount: number;\r\n totalCount: number;\r\n progress: BulkProgress | null;\r\n outcome: BulkOutcome | null;\r\n detailOpen: boolean;\r\n onToggleAll: () => void;\r\n onDelete: () => void;\r\n onCancel: () => void;\r\n onToggleDetail: () => void;\r\n onDismiss: () => void;\r\n t: (key: Parameters<ReturnType<typeof useT>>[0], params?: Record<string, string | number>) => string;\r\n}\r\n\r\nfunction BulkBar({\r\n selectMode,\r\n selectedCount,\r\n totalCount,\r\n progress,\r\n outcome,\r\n detailOpen,\r\n onToggleAll,\r\n onDelete,\r\n onCancel,\r\n onToggleDetail,\r\n onDismiss,\r\n t,\r\n}: BulkBarProps) {\r\n const allSelected = selectedCount === totalCount && totalCount > 0;\r\n const isBusy = progress !== null;\r\n return (\r\n // sticky 顶部:被外层 card 的 padding 抵消;用 -mx-6 让条目贴齐 card 边缘\r\n <div className=\"sticky top-2 z-30 -mx-6 mb-4 px-6\">\r\n {selectMode && (\r\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)]\">\r\n <div className=\"flex items-center gap-3\">\r\n <span className=\"font-mono text-[12px] font-medium uppercase tracking-[0.14em] text-[var(--color-fg-primary)]\">\r\n {isBusy\r\n ? t('project.bulk.deletePending', {\r\n i: progress!.index,\r\n total: progress!.total,\r\n })\r\n : t('project.bulk.selectedCount', { n: selectedCount })}\r\n </span>\r\n <button\r\n type=\"button\"\r\n onClick={onToggleAll}\r\n disabled={isBusy}\r\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\"\r\n >\r\n {allSelected ? t('common.deselectAll') : t('common.selectAll')}\r\n </button>\r\n </div>\r\n <div className=\"flex items-center gap-2\">\r\n <button\r\n type=\"button\"\r\n onClick={onCancel}\r\n disabled={isBusy}\r\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\"\r\n >\r\n {t('common.cancel')}\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={onDelete}\r\n disabled={isBusy || selectedCount === 0}\r\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\"\r\n >\r\n <TrashIcon />\r\n {t('project.bulk.deleteCta')}\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n {!selectMode && outcome && (\r\n <BulkOutcomeRibbon\r\n outcome={outcome}\r\n detailOpen={detailOpen}\r\n onToggleDetail={onToggleDetail}\r\n onDismiss={onDismiss}\r\n t={t}\r\n />\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\ninterface BulkOutcomeRibbonProps {\r\n outcome: BulkOutcome;\r\n detailOpen: boolean;\r\n onToggleDetail: () => void;\r\n onDismiss: () => void;\r\n t: BulkBarProps['t'];\r\n}\r\n\r\nfunction BulkOutcomeRibbon({\r\n outcome,\r\n detailOpen,\r\n onToggleDetail,\r\n onDismiss,\r\n t,\r\n}: BulkOutcomeRibbonProps) {\r\n // 整体观感:成功为主时走 moss;有失败/跳过时走 accent\r\n const hasIssue = outcome.skipped.length + outcome.failed.length > 0;\r\n const tone = hasIssue\r\n ? 'border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)]'\r\n : 'border-[var(--color-moss)]/40 bg-[var(--color-moss-soft)]';\r\n const detailToggleable = hasIssue;\r\n return (\r\n <div className={`rounded-[var(--radius-input)] border ${tone} px-4 py-2.5`}>\r\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\r\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-[var(--color-fg-primary)]\">\r\n <span className=\"font-medium\">\r\n {t('project.bulk.resultOk', { ok: outcome.ok.length })}\r\n </span>\r\n {outcome.skipped.length > 0 && (\r\n <span className=\"font-mono text-[12px] text-[var(--color-fg-secondary)]\">\r\n · {t('project.bulk.resultSkipped', { n: outcome.skipped.length })}\r\n </span>\r\n )}\r\n {outcome.failed.length > 0 && (\r\n <span className=\"font-mono text-[12px] text-[var(--color-danger)]\">\r\n · {t('project.bulk.resultFailed', { n: outcome.failed.length })}\r\n </span>\r\n )}\r\n </div>\r\n <div className=\"flex items-center gap-2\">\r\n {detailToggleable && (\r\n <button\r\n type=\"button\"\r\n onClick={onToggleDetail}\r\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)]\"\r\n >\r\n {detailOpen ? t('project.bulk.hideDetail') : t('project.bulk.viewDetail')}\r\n </button>\r\n )}\r\n <button\r\n type=\"button\"\r\n onClick={onDismiss}\r\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)]\"\r\n >\r\n {t('project.bulk.dismiss')}\r\n </button>\r\n </div>\r\n </div>\r\n {detailOpen && detailToggleable && (\r\n <div className=\"mt-3 space-y-2\">\r\n {outcome.skipped.length > 0 && (\r\n <div>\r\n <div className=\"eyebrow text-[var(--color-fg-muted)]\">\r\n {t('project.bulk.detailSkipped')}\r\n </div>\r\n <ul className=\"mt-1 space-y-0.5 font-mono text-[11px] text-[var(--color-fg-secondary)]\">\r\n {outcome.skipped.map((s) => (\r\n <li key={s.sessionId}>\r\n <span className=\"text-[var(--color-fg-primary)]\">{s.title}</span>\r\n <span className=\"ml-2 text-[var(--color-fg-faint)]\">{s.sessionId}</span>\r\n <span className=\"ml-2\">— {s.reason}</span>\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n )}\r\n {outcome.failed.length > 0 && (\r\n <div>\r\n <div className=\"eyebrow text-[var(--color-danger)]\">\r\n {t('project.bulk.detailFailed')}\r\n </div>\r\n <ul className=\"mt-1 space-y-0.5 font-mono text-[11px] text-[var(--color-fg-secondary)]\">\r\n {outcome.failed.map((s) => (\r\n <li key={s.sessionId}>\r\n <span className=\"text-[var(--color-fg-primary)]\">{s.title}</span>\r\n <span className=\"ml-2 text-[var(--color-fg-faint)]\">{s.sessionId}</span>\r\n <span className=\"ml-2 text-[var(--color-danger)]\">— {s.error}</span>\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nfunction ExportIcon() {\r\n return (\r\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <path d=\"M12 3v12\" />\r\n <path d=\"M8 7l4-4 4 4\" />\r\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\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction FolderOpenIcon() {\r\n return (\r\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\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\" />\r\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\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction BrainIcon() {\r\n return (\r\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\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\" />\r\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\" />\r\n </svg>\r\n );\r\n}\r\n","import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\r\nimport { motion } from 'motion/react';\r\nimport { useEffect, useRef } from 'react';\r\nimport {\r\n api,\r\n type DeleteProjectResult,\r\n type ProjectSummary,\r\n type SessionSummary,\r\n} from '../lib/api.ts';\r\nimport { formatBytes } from '../lib/format.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport { queryKeys } from '../lib/query-keys.ts';\r\n\r\ninterface Props {\r\n project: ProjectSummary;\r\n onClose: () => void;\r\n}\r\n\r\nexport default function DeleteProjectDialog({ project, onClose }: Props) {\r\n const t = useT();\r\n const queryClient = useQueryClient();\r\n\r\n const sessionsQuery = useQuery({\r\n queryKey: queryKeys.projectSessions(project.id),\r\n queryFn: () =>\r\n api<SessionSummary[]>(`/api/projects/${encodeURIComponent(project.id)}/sessions`),\r\n });\r\n const sessions = sessionsQuery.data ?? [];\r\n const blockers = sessions.filter((s) => s.isLivePid || s.isRecentlyActive);\r\n const hasBlockers = blockers.length > 0;\r\n\r\n const mutation = useMutation({\r\n mutationFn: () =>\r\n api<DeleteProjectResult>(`/api/projects/${encodeURIComponent(project.id)}`, {\r\n method: 'DELETE',\r\n }),\r\n onSuccess: () => {\r\n queryClient.invalidateQueries({ queryKey: queryKeys.projects() });\r\n queryClient.invalidateQueries({ queryKey: queryKeys.projectSessions(project.id) });\r\n queryClient.invalidateQueries({ queryKey: queryKeys.diskUsage() });\r\n },\r\n });\r\n\r\n const isPendingRef = useRef(mutation.isPending);\r\n isPendingRef.current = mutation.isPending;\r\n\r\n useEffect(() => {\r\n function onKey(e: KeyboardEvent) {\r\n if (e.key === 'Escape' && !isPendingRef.current) onClose();\r\n }\r\n window.addEventListener('keydown', onKey);\r\n return () => window.removeEventListener('keydown', onKey);\r\n }, [onClose]);\r\n\r\n const result = mutation.data;\r\n const showResult = !!result;\r\n const totalDeletedBytes = result?.deleted.reduce((a, d) => a + d.freedBytes, 0) ?? 0;\r\n\r\n return (\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n exit={{ opacity: 0 }}\r\n transition={{ duration: 0.18 }}\r\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\"\r\n onClick={() => !mutation.isPending && onClose()}\r\n >\r\n <motion.div\r\n initial={{ y: 8, opacity: 0 }}\r\n animate={{ y: 0, opacity: 1 }}\r\n transition={{ duration: 0.24, ease: [0.16, 1, 0.3, 1] }}\r\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)]\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <header className=\"flex items-start justify-between gap-4 border-b border-[var(--color-hairline)] px-6 py-5\">\r\n <div className=\"min-w-0\">\r\n <p className=\"eyebrow text-[var(--color-danger)]\">\r\n {showResult ? t('deleteProject.eyebrow.result') : t('deleteProject.eyebrow.confirm')}\r\n </p>\r\n <h2 className=\"mt-1 font-display text-2xl font-light tracking-tight text-[var(--color-fg-primary)]\">\r\n {showResult ? t('deleteProject.title.result') : t('deleteProject.title.confirm')}\r\n </h2>\r\n <p className=\"mt-1.5 truncate font-mono text-[12px] text-[var(--color-fg-muted)]\" title={project.decodedCwd}>\r\n {project.decodedCwd}\r\n </p>\r\n {!showResult && (\r\n <p className=\"mt-2 text-sm text-[var(--color-fg-muted)]\">\r\n {t('deleteProject.summary', {\r\n n: project.sessionCount,\r\n free: formatBytes(project.totalBytes),\r\n })}\r\n </p>\r\n )}\r\n </div>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n disabled={mutation.isPending}\r\n aria-label={t('delete.close')}\r\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\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\">\r\n <path d=\"M6 6l12 12M18 6L6 18\" />\r\n </svg>\r\n </button>\r\n </header>\r\n\r\n {!showResult && (\r\n <div className=\"space-y-3 px-6 py-4 text-sm\">\r\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)]\">\r\n {t('deleteProject.warning', { cwd: project.decodedCwd })}\r\n </p>\r\n {hasBlockers && (\r\n <div className=\"rounded-md border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-xs\">\r\n <div className=\"mb-1.5 font-medium text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\r\n {t('deleteProject.blocked.heading', { n: blockers.length })}\r\n </div>\r\n <ul className=\"space-y-0.5\">\r\n {blockers.map((s) => (\r\n <li key={s.id} className=\"font-mono text-[11px] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-secondary)]\">\r\n {s.id} — {s.isLivePid ? `live PID ${s.livePid ?? '?'}` : 'recent'}\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {showResult && result && (\r\n <div className=\"max-h-[50vh] space-y-3 overflow-auto px-6 py-4 text-sm\">\r\n {result.projectDirRemoved ? (\r\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)]\">\r\n {t('deleteProject.success', {\r\n n: result.deleted.length,\r\n free: formatBytes(totalDeletedBytes),\r\n lines: result.historyLinesRemoved,\r\n })}\r\n </div>\r\n ) : result.deleted.length > 0 ? (\r\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)]\">\r\n {t('deleteProject.successKept', {\r\n n: result.deleted.length,\r\n free: formatBytes(totalDeletedBytes),\r\n lines: result.historyLinesRemoved,\r\n })}\r\n </div>\r\n ) : null}\r\n {result.skipped.length > 0 && (\r\n <div className=\"rounded-md border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-xs\">\r\n <div className=\"mb-1 font-medium text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\r\n {t('deleteProject.blocked.heading', { n: result.skipped.length })}\r\n </div>\r\n <ul className=\"space-y-0.5\">\r\n {result.skipped.map((s) => (\r\n <li key={`${s.sessionId}-${s.reason}`} className=\"font-mono text-[11px] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-secondary)]\">\r\n {s.sessionId || '(project)'} — {s.reason}\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {mutation.error && (\r\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)]\">\r\n {(mutation.error as Error).message}\r\n </p>\r\n )}\r\n\r\n <footer className=\"flex justify-end gap-2 border-t border-[var(--color-hairline)] px-6 py-4\">\r\n {!showResult ? (\r\n <>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n disabled={mutation.isPending}\r\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\"\r\n >\r\n {t('common.cancel')}\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() => mutation.mutate()}\r\n disabled={mutation.isPending || sessionsQuery.isLoading || hasBlockers}\r\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\"\r\n >\r\n {mutation.isPending\r\n ? t('deleteProject.btn.confirmPending')\r\n : t('deleteProject.btn.confirm')}\r\n </button>\r\n </>\r\n ) : (\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\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\"\r\n >\r\n {t('common.done')}\r\n </button>\r\n )}\r\n </footer>\r\n </motion.div>\r\n </motion.div>\r\n );\r\n}\r\n","import { useQuery } from '@tanstack/react-query';\r\nimport { motion } from 'motion/react';\r\nimport { useState } from 'react';\r\nimport { Link } from 'react-router-dom';\r\nimport DeleteProjectDialog from '../components/DeleteProjectDialog.tsx';\r\nimport { Loading } from '../components/Loading.tsx';\r\nimport { MetaItem, Sep } from '../components/PageHeader.tsx';\r\nimport { api, type HealthResponse, type ProjectSummary } from '../lib/api.ts';\r\nimport { formatBytes, formatRelativeTime } from '../lib/format.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport { fadeUpItem, staggerParent } from '../lib/motion.ts';\r\nimport { queryKeys } from '../lib/query-keys.ts';\r\n\r\nexport default function ProjectsList() {\r\n const t = useT();\r\n const [pendingDelete, setPendingDelete] = useState<ProjectSummary | null>(null);\r\n const health = useQuery({\r\n queryKey: queryKeys.health(),\r\n queryFn: () => api<HealthResponse>('/api/health'),\r\n });\r\n\r\n const projects = useQuery({\r\n queryKey: queryKeys.projects(),\r\n queryFn: () => api<ProjectSummary[]>('/api/projects'),\r\n });\r\n\r\n const list = projects.data ?? [];\r\n const totalBytes = list.reduce((acc, p) => acc + p.totalBytes, 0);\r\n const totalSessions = list.reduce((acc, p) => acc + p.sessionCount, 0);\r\n const lastActive = list\r\n .map((p) => p.lastActiveAt)\r\n .filter((x): x is string => !!x)\r\n .sort()\r\n .at(-1) ?? null;\r\n\r\n return (\r\n <section>\r\n <div className=\"surface-card p-6\">\r\n <Masthead\r\n title={t('projects.title')}\r\n tagline={t('projects.tagline')}\r\n stats={\r\n list.length > 0\r\n ? { totalBytes, totalSessions, projectCount: list.length, lastActive }\r\n : null\r\n }\r\n />\r\n </div>\r\n\r\n {health.data && !health.data.claudeRootExists && (\r\n <Admonition tone=\"warn\" className=\"mt-6\">\r\n {t('projects.warn.rootMissing', { root: health.data.claudeRoot })}\r\n </Admonition>\r\n )}\r\n\r\n {projects.isLoading && <Loading label={t('common.scanning')} className=\"mt-10\" />}\r\n {projects.error && (\r\n <Admonition tone=\"danger\" className=\"mt-6\">\r\n {t('common.failedProjects')}: {(projects.error as Error).message}\r\n </Admonition>\r\n )}\r\n {projects.data && projects.data.length === 0 && (\r\n <p className=\"mt-10 text-sm text-[var(--color-fg-muted)]\">{t('common.noProjects')}</p>\r\n )}\r\n\r\n {list.length > 0 && (\r\n <div className=\"surface-card mt-6 p-6\">\r\n <div className=\"flex items-baseline justify-between\">\r\n <h2 className=\"font-display text-xl font-light tracking-tight text-[var(--color-fg-primary)]\">\r\n {t('projects.indexHeading')}\r\n </h2>\r\n <span className=\"font-mono text-[11px] uppercase tracking-[0.16em] tabular-nums text-[var(--color-fg-muted)]\">\r\n {String(list.length).padStart(2, '0')}{' '}\r\n {list.length === 1 ? t('common.entry') : t('common.entries')}\r\n </span>\r\n </div>\r\n <div className=\"rule-dotted mt-3\" aria-hidden />\r\n <Ledger projects={list} onRequestDelete={(p) => setPendingDelete(p)} />\r\n </div>\r\n )}\r\n\r\n {pendingDelete && (\r\n <DeleteProjectDialog\r\n project={pendingDelete}\r\n onClose={() => setPendingDelete(null)}\r\n />\r\n )}\r\n </section>\r\n );\r\n}\r\n\r\n/* ─────────────────────────────────────────────────────────────────── */\r\n\r\nfunction Masthead({\r\n title,\r\n tagline,\r\n stats,\r\n}: {\r\n title: string;\r\n tagline: string;\r\n stats: {\r\n totalBytes: number;\r\n totalSessions: number;\r\n projectCount: number;\r\n lastActive: string | null;\r\n } | null;\r\n}) {\r\n const t = useT();\r\n return (\r\n <header className=\"relative\">\r\n <div className=\"flex flex-wrap items-baseline gap-x-4 gap-y-1\">\r\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)]\">\r\n {title}\r\n <span className=\"text-[var(--color-accent)]\">.</span>\r\n </h1>\r\n <p className=\"min-w-0 flex-1 font-display text-[13px] italic leading-snug text-[var(--color-fg-muted)]\">\r\n {tagline}\r\n </p>\r\n </div>\r\n {stats && (\r\n <div className=\"mt-3 flex flex-wrap items-baseline gap-x-3 gap-y-1 text-xs\">\r\n <MetaItem label={t('projects.stat.onDisk')} value={formatBytes(stats.totalBytes)} />\r\n <Sep />\r\n <MetaItem label={t('projects.stat.projects')} value={stats.projectCount} />\r\n <Sep />\r\n <MetaItem label={t('projects.stat.sessions')} value={stats.totalSessions.toLocaleString()} />\r\n <Sep />\r\n <MetaItem label={t('projects.card.lastSeen')} value={formatRelativeTime(stats.lastActive)} />\r\n </div>\r\n )}\r\n </header>\r\n );\r\n}\r\n\r\n/* ─────────────────────────────────────────────────────────────────── */\r\n\r\nfunction Ledger({\r\n projects,\r\n onRequestDelete,\r\n}: {\r\n projects: ProjectSummary[];\r\n onRequestDelete: (p: ProjectSummary) => void;\r\n}) {\r\n const t = useT();\r\n return (\r\n <motion.ol\r\n initial=\"hidden\"\r\n animate=\"show\"\r\n variants={staggerParent}\r\n className=\"mt-4\"\r\n >\r\n <li\r\n aria-hidden\r\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]\"\r\n >\r\n <span className=\"eyebrow text-right\">№</span>\r\n <span className=\"eyebrow\">{t('projects.card.eyebrow')}</span>\r\n <span className=\"eyebrow text-right\">{t('projects.card.sessions')}</span>\r\n <span className=\"eyebrow text-right\">{t('projects.card.onDisk')}</span>\r\n <span className=\"eyebrow text-right\">{t('projects.card.lastSeen')}</span>\r\n <span className=\"eyebrow\" />\r\n </li>\r\n\r\n {projects.map((p, i) => (\r\n <motion.li key={p.id} variants={fadeUpItem}>\r\n <LedgerRow project={p} index={i} onRequestDelete={onRequestDelete} />\r\n </motion.li>\r\n ))}\r\n </motion.ol>\r\n );\r\n}\r\n\r\nfunction LedgerRow({\r\n project,\r\n index,\r\n onRequestDelete,\r\n}: {\r\n project: ProjectSummary;\r\n index: number;\r\n onRequestDelete: (p: ProjectSummary) => void;\r\n}) {\r\n const t = useT();\r\n const cwd = project.decodedCwd;\r\n const parts = cwd.split(/[\\\\/]+/).filter(Boolean);\r\n const tail = parts.slice(-2).join('/');\r\n const head = parts.slice(0, -2).join('/');\r\n\r\n return (\r\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]\">\r\n <Link\r\n to={`/projects/${encodeURIComponent(project.id)}`}\r\n aria-label={cwd}\r\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)]\"\r\n >\r\n <span className=\"sr-only\">{cwd}</span>\r\n </Link>\r\n\r\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)]\">\r\n {String(index + 1).padStart(2, '0')}\r\n </span>\r\n\r\n <div className=\"pointer-events-none min-w-0\">\r\n <div\r\n className=\"flex items-baseline gap-2 truncate font-mono text-[13px] text-[var(--color-fg-primary)]\"\r\n title={cwd}\r\n >\r\n {head && <span className=\"text-[var(--color-fg-faint)]\">{head}/</span>}\r\n <span className=\"font-medium\">{tail}</span>\r\n {!project.cwdResolved && (\r\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)]\">\r\n {t('common.missing')}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <span className=\"pointer-events-none text-right font-mono text-sm tabular-nums text-[var(--color-fg-primary)]\">\r\n {project.sessionCount.toLocaleString()}\r\n </span>\r\n <span className=\"pointer-events-none text-right font-mono text-sm tabular-nums text-[var(--color-fg-secondary)]\">\r\n {formatBytes(project.totalBytes)}\r\n </span>\r\n <span className=\"pointer-events-none text-right font-mono text-[12.5px] tabular-nums text-[var(--color-fg-secondary)]\">\r\n {formatRelativeTime(project.lastActiveAt)}\r\n </span>\r\n\r\n <div className=\"relative z-[2] flex items-center justify-end gap-1 pr-1\">\r\n <button\r\n type=\"button\"\r\n onClick={(e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n onRequestDelete(project);\r\n }}\r\n aria-label={t('deleteProject.row.action')}\r\n title={t('deleteProject.row.action')}\r\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)]\"\r\n >\r\n <TrashIcon />\r\n </button>\r\n <span\r\n aria-hidden\r\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)]\"\r\n >\r\n <ChevronRight />\r\n </span>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction TrashIcon() {\r\n return (\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <path d=\"M3 6h18\" />\r\n <path d=\"M8 6V4.5A1.5 1.5 0 0 1 9.5 3h5A1.5 1.5 0 0 1 16 4.5V6\" />\r\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\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction ChevronRight() {\r\n return (\r\n <svg\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.6\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden\r\n >\r\n <path d=\"M9 6l6 6-6 6\" />\r\n </svg>\r\n );\r\n}\r\n\r\n/* ─────────────────────────────────────────────────────────────────── */\r\n\r\nfunction Admonition({\r\n tone,\r\n className = '',\r\n children,\r\n}: {\r\n tone: 'warn' | 'danger';\r\n className?: string;\r\n children: React.ReactNode;\r\n}) {\r\n const colors =\r\n tone === 'warn'\r\n ? 'border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\r\n : 'border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] text-[var(--color-danger)]';\r\n return (\r\n <div className={`rounded-[10px] border px-4 py-3 text-sm ${colors} ${className}`}>\r\n {children}\r\n </div>\r\n );\r\n}\r\n","import { useMutation, useQueryClient } from '@tanstack/react-query';\r\nimport { motion } from 'motion/react';\r\nimport { useEffect, useRef } from 'react';\r\nimport {\r\n api,\r\n type DeleteRequestItem,\r\n type DeleteResult,\r\n type SessionSummary,\r\n} from '../lib/api.ts';\r\nimport { RECENT_ACTIVITY_WINDOW_MIN } from '../lib/constants.ts';\r\nimport { formatBytes } from '../lib/format.ts';\r\nimport { translate, useT, type Locale } from '../lib/i18n.ts';\r\nimport { useLocale } from '../lib/i18n.ts';\r\nimport { queryKeys } from '../lib/query-keys.ts';\r\n\r\ninterface Props {\r\n projectId: string;\r\n selected: SessionSummary[];\r\n onClose: () => void;\r\n onDeleted?: (deletedSessionIds: string[]) => void;\r\n}\r\n\r\nexport default function DeleteDialog({ projectId, selected, onClose, onDeleted }: Props) {\r\n const t = useT();\r\n const { locale } = useLocale();\r\n const queryClient = useQueryClient();\r\n\r\n const willSkip = selected.filter((s) => s.isLivePid || s.isRecentlyActive);\r\n const willDelete = selected.filter((s) => !s.isLivePid && !s.isRecentlyActive);\r\n const totalFree = willDelete.reduce(\r\n (acc, s) =>\r\n acc +\r\n s.relatedBytes.jsonl +\r\n s.relatedBytes.subdir +\r\n s.relatedBytes.fileHistory +\r\n s.relatedBytes.sessionEnv,\r\n 0,\r\n );\r\n\r\n const mutation = useMutation({\r\n mutationFn: (items: DeleteRequestItem[]) =>\r\n api<DeleteResult>('/api/sessions', {\r\n method: 'DELETE',\r\n body: JSON.stringify({ items }),\r\n }),\r\n onSuccess: (data) => {\r\n // Drop the deleted ids from the cached session list synchronously so the\r\n // list under the dialog (or after navigate-back from SessionDetail) doesn't\r\n // flash the just-deleted rows while the background refetch is in flight.\r\n if (data.deleted.length > 0) {\r\n const removed = new Set(data.deleted.map((d) => d.sessionId));\r\n queryClient.setQueryData<SessionSummary[]>(\r\n queryKeys.projectSessions(projectId),\r\n (prev) => prev?.filter((s) => !removed.has(s.id)),\r\n );\r\n }\r\n queryClient.invalidateQueries({ queryKey: queryKeys.projects() });\r\n queryClient.invalidateQueries({ queryKey: queryKeys.projectSessions(projectId) });\r\n queryClient.invalidateQueries({ queryKey: queryKeys.diskUsage() });\r\n if (data.deleted.length > 0) {\r\n onDeleted?.(data.deleted.map((d) => d.sessionId));\r\n }\r\n },\r\n });\r\n\r\n const isPendingRef = useRef(mutation.isPending);\r\n isPendingRef.current = mutation.isPending;\r\n\r\n useEffect(() => {\r\n function onKey(e: KeyboardEvent) {\r\n if (e.key === 'Escape' && !isPendingRef.current) onClose();\r\n }\r\n window.addEventListener('keydown', onKey);\r\n return () => window.removeEventListener('keydown', onKey);\r\n }, [onClose]);\r\n\r\n const result = mutation.data;\r\n const showResult = !!result;\r\n\r\n return (\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n exit={{ opacity: 0 }}\r\n transition={{ duration: 0.18 }}\r\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\"\r\n onClick={() => !mutation.isPending && onClose()}\r\n >\r\n <motion.div\r\n initial={{ y: 8, opacity: 0 }}\r\n animate={{ y: 0, opacity: 1 }}\r\n transition={{ duration: 0.24, ease: [0.16, 1, 0.3, 1] }}\r\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)]\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <header className=\"flex items-start justify-between gap-4 border-b border-[var(--color-hairline)] px-6 py-5\">\r\n <div>\r\n <p className=\"eyebrow text-[var(--color-danger)]\">\r\n {showResult ? t('delete.eyebrow.result') : t('delete.eyebrow.confirm')}\r\n </p>\r\n <h2 className=\"mt-1 font-display text-2xl font-light tracking-tight text-[var(--color-fg-primary)]\">\r\n {showResult ? t('delete.title.result') : t('delete.title.confirm')}\r\n </h2>\r\n {!showResult && (\r\n <p className=\"mt-1.5 text-sm text-[var(--color-fg-muted)]\">\r\n {t('delete.summary', {\r\n n: willDelete.length,\r\n skipped: willSkip.length,\r\n free: formatBytes(totalFree),\r\n })}\r\n </p>\r\n )}\r\n </div>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n disabled={mutation.isPending}\r\n aria-label={t('delete.close')}\r\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\"\r\n >\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\">\r\n <path d=\"M6 6l12 12M18 6L6 18\" />\r\n </svg>\r\n </button>\r\n </header>\r\n\r\n {!showResult && (\r\n <div className=\"max-h-[50vh] space-y-2 overflow-auto px-6 py-4\">\r\n {willDelete.map((s) => (\r\n <div\r\n key={s.id}\r\n className=\"rounded-md border border-[var(--color-hairline)] bg-[var(--color-canvas)] px-3 py-2 text-xs\"\r\n >\r\n <div className=\"font-medium text-[var(--color-fg-primary)]\">{s.title}</div>\r\n <div className=\"mt-0.5 truncate font-mono text-[10.5px] text-[var(--color-fg-faint)]\">{s.id}</div>\r\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\">\r\n <span>jsonl <span className=\"text-[var(--color-fg-secondary)]\">{formatBytes(s.relatedBytes.jsonl)}</span></span>\r\n <span>subdir <span className=\"text-[var(--color-fg-secondary)]\">{formatBytes(s.relatedBytes.subdir)}</span></span>\r\n <span>file-history <span className=\"text-[var(--color-fg-secondary)]\">{formatBytes(s.relatedBytes.fileHistory)}</span></span>\r\n <span>session-env <span className=\"text-[var(--color-fg-secondary)]\">{formatBytes(s.relatedBytes.sessionEnv)}</span></span>\r\n </div>\r\n </div>\r\n ))}\r\n {willSkip.length > 0 && (\r\n <div className=\"rounded-md border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-xs\">\r\n <div className=\"mb-1.5 font-medium text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\r\n {t('delete.skipped.heading', { n: willSkip.length })}\r\n </div>\r\n <ul className=\"space-y-0.5\">\r\n {willSkip.map((s) => (\r\n <li key={s.id} className=\"font-mono text-[11px] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-secondary)]\">\r\n {s.id} — {skipReason(s, locale)}\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {showResult && result && (\r\n <div className=\"max-h-[50vh] space-y-3 overflow-auto px-6 py-4 text-sm\">\r\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)]\">\r\n {t('delete.success', {\r\n n: result.deleted.length,\r\n free: formatBytes(result.deleted.reduce((a, d) => a + d.freedBytes, 0)),\r\n lines: result.historyLinesRemoved,\r\n })}\r\n </div>\r\n {result.skipped.length > 0 && (\r\n <div className=\"rounded-md border border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] px-3 py-2.5 text-xs\">\r\n <div className=\"mb-1 font-medium text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\">\r\n {t('delete.skipped.heading', { n: result.skipped.length })}\r\n </div>\r\n <ul className=\"space-y-0.5\">\r\n {result.skipped.map((s) => (\r\n <li key={s.sessionId} className=\"font-mono text-[11px] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-secondary)]\">\r\n {s.sessionId} — {s.reason}\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {mutation.error && (\r\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)]\">\r\n {(mutation.error as Error).message}\r\n </p>\r\n )}\r\n\r\n <footer className=\"flex justify-end gap-2 border-t border-[var(--color-hairline)] px-6 py-4\">\r\n {!showResult ? (\r\n <>\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n disabled={mutation.isPending}\r\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\"\r\n >\r\n {t('common.cancel')}\r\n </button>\r\n <button\r\n type=\"button\"\r\n onClick={() =>\r\n mutation.mutate(\r\n selected.map((s) => ({ projectId, sessionId: s.id })),\r\n )\r\n }\r\n disabled={mutation.isPending || willDelete.length === 0}\r\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\"\r\n >\r\n {mutation.isPending\r\n ? t('delete.btn.confirmPending')\r\n : t('delete.btn.confirm', {\r\n n: willDelete.length,\r\n label:\r\n willDelete.length === 1\r\n ? t('delete.label.session')\r\n : t('delete.label.sessions'),\r\n })}\r\n </button>\r\n </>\r\n ) : (\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\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\"\r\n >\r\n {t('common.done')}\r\n </button>\r\n )}\r\n </footer>\r\n </motion.div>\r\n </motion.div>\r\n );\r\n}\r\n\r\nfunction skipReason(s: SessionSummary, locale: Locale): string {\r\n if (s.isLivePid) return translate(locale, 'delete.skipped.reasonLive', { pid: s.livePid ?? '?' });\r\n if (s.isRecentlyActive)\r\n return translate(locale, 'delete.skipped.reasonRecent', { n: RECENT_ACTIVITY_WINDOW_MIN });\r\n return translate(locale, 'delete.skipped.reasonUnknown');\r\n}\r\n","export interface Segment {\r\n text: string;\r\n match: boolean;\r\n}\r\n\r\nexport function highlight(text: string, query: string): Segment[] {\r\n if (!query) return [{ text, match: false }];\r\n const lowerText = text.toLowerCase();\r\n const lowerQuery = query.toLowerCase();\r\n const segments: Segment[] = [];\r\n let cursor = 0;\r\n\r\n while (cursor < text.length) {\r\n const idx = lowerText.indexOf(lowerQuery, cursor);\r\n if (idx === -1) {\r\n segments.push({ text: text.slice(cursor), match: false });\r\n break;\r\n }\r\n if (idx > cursor) {\r\n segments.push({ text: text.slice(cursor, idx), match: false });\r\n }\r\n segments.push({ text: text.slice(idx, idx + query.length), match: true });\r\n cursor = idx + query.length;\r\n }\r\n\r\n return segments;\r\n}\r\n\r\nexport function messageMatchesQuery(text: string, query: string): boolean {\r\n if (!query) return true;\r\n return text.toLowerCase().includes(query.toLowerCase());\r\n}\r\n","import { highlight } from '../lib/highlight.ts';\r\n\r\nexport default function HighlightedText({\r\n text,\r\n query,\r\n className,\r\n}: {\r\n text: string;\r\n query: string;\r\n className?: string;\r\n}) {\r\n const segments = highlight(text, query);\r\n return (\r\n <span className={className}>\r\n {segments.map((seg, i) =>\r\n seg.match ? (\r\n <mark\r\n key={i}\r\n className=\"rounded-sm bg-[var(--color-accent-soft)] px-0.5 text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]\"\r\n >\r\n {seg.text}\r\n </mark>\r\n ) : (\r\n <span key={i}>{seg.text}</span>\r\n ),\r\n )}\r\n </span>\r\n );\r\n}\r\n","import { useState } from 'react';\r\nimport type { Block } from '../lib/api.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport HighlightedText from './HighlightedText.tsx';\r\n\r\nconst PREVIEW_CHARS = 280;\r\n\r\nexport function ToolUseBlock({\r\n block,\r\n query,\r\n}: {\r\n block: Extract<Block, { type: 'tool_use' }>;\r\n query: string;\r\n}) {\r\n const [open, setOpen] = useState(false);\r\n const inputJson = JSON.stringify(block.input, null, 2);\r\n return (\r\n <div className=\"overflow-hidden rounded-xl border border-[var(--color-hairline)] bg-[var(--color-sunken)] text-sm\">\r\n <button\r\n type=\"button\"\r\n onClick={() => setOpen(!open)}\r\n className=\"flex w-full items-center justify-between gap-2 px-3 py-2 text-left transition hover:bg-[var(--color-canvas)]\"\r\n >\r\n <span className=\"flex items-center gap-2 font-mono text-[11.5px] font-medium uppercase tracking-[0.06em] text-[var(--color-fg-secondary)]\">\r\n <Glyph kind=\"tool\" /> {block.name}\r\n </span>\r\n <Caret open={open} />\r\n </button>\r\n {open && (\r\n <pre className=\"overflow-x-auto border-t border-[var(--color-hairline)] bg-[var(--color-surface)] px-3 py-2 font-mono text-[11.5px] text-[var(--color-fg-primary)]\">\r\n <HighlightedText text={inputJson} query={query} />\r\n </pre>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport function ToolResultBlock({\r\n block,\r\n query,\r\n}: {\r\n block: Extract<Block, { type: 'tool_result' }>;\r\n query: string;\r\n}) {\r\n const t = useT();\r\n const [open, setOpen] = useState(false);\r\n const long = block.content.length > PREVIEW_CHARS;\r\n const visible = open || !long ? block.content : block.content.slice(0, PREVIEW_CHARS) + '…';\r\n\r\n const tone = block.isError\r\n ? 'border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] text-[var(--color-danger)]'\r\n : 'border-[var(--color-hairline)] bg-[var(--color-sunken)] text-[var(--color-fg-primary)]';\r\n\r\n return (\r\n <div className={`overflow-hidden rounded-xl border text-sm ${tone}`}>\r\n <div className=\"flex items-center justify-between gap-2 px-3 py-2\">\r\n <span className=\"flex items-center gap-2 font-mono text-[11.5px] font-medium uppercase tracking-[0.06em]\">\r\n <Glyph kind={block.isError ? 'error' : 'result'} />\r\n {block.isError ? t('tool.error') : t('tool.result')}\r\n </span>\r\n {long && (\r\n <button\r\n type=\"button\"\r\n onClick={() => setOpen(!open)}\r\n className=\"font-mono text-[10.5px] uppercase tracking-[0.16em] underline-offset-2 hover:underline\"\r\n >\r\n {open ? t('common.collapse') : t('common.expand')}\r\n </button>\r\n )}\r\n </div>\r\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)]'}`}>\r\n <HighlightedText text={visible} query={query} />\r\n </pre>\r\n </div>\r\n );\r\n}\r\n\r\nexport function ThinkingBlock({\r\n block,\r\n query,\r\n}: {\r\n block: Extract<Block, { type: 'thinking' }>;\r\n query: string;\r\n}) {\r\n const t = useT();\r\n const [open, setOpen] = useState(false);\r\n const hasText = block.text.trim() !== '';\r\n return (\r\n <div className=\"overflow-hidden rounded-xl border border-[var(--color-hairline-strong)] bg-[var(--color-sunken)] text-sm text-[var(--color-fg-secondary)]\">\r\n <button\r\n type=\"button\"\r\n onClick={() => setOpen(!open)}\r\n className=\"flex w-full items-center justify-between gap-2 px-3 py-2 text-left\"\r\n >\r\n <span className=\"flex items-center gap-2 font-mono text-[11.5px] font-medium uppercase tracking-[0.06em]\">\r\n <Glyph kind=\"thinking\" /> {t('tool.thinking')}\r\n </span>\r\n <Caret open={open} />\r\n </button>\r\n {open && (\r\n hasText ? (\r\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)]\">\r\n <HighlightedText text={block.text} query={query} />\r\n </div>\r\n ) : (\r\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)]\">\r\n {t('tool.thinkingEncrypted')}\r\n </p>\r\n )\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nfunction Caret({ open }: { open: boolean }) {\r\n return (\r\n <svg\r\n width=\"11\"\r\n height=\"11\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className=\"text-[var(--color-fg-muted)] transition-transform\"\r\n style={{ transform: open ? 'rotate(90deg)' : 'rotate(0)' }}\r\n aria-hidden\r\n >\r\n <path d=\"M9 6l6 6-6 6\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction Glyph({ kind }: { kind: 'tool' | 'result' | 'error' | 'thinking' }) {\r\n const common = {\r\n width: 11,\r\n height: 11,\r\n viewBox: '0 0 24 24',\r\n fill: 'none',\r\n stroke: 'currentColor',\r\n strokeWidth: 1.8,\r\n strokeLinecap: 'round' as const,\r\n strokeLinejoin: 'round' as const,\r\n 'aria-hidden': true,\r\n };\r\n if (kind === 'tool') {\r\n return (\r\n <svg {...common}>\r\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\" />\r\n </svg>\r\n );\r\n }\r\n if (kind === 'result') {\r\n return (\r\n <svg {...common}>\r\n <path d=\"M5 12h13\" />\r\n <path d=\"M13 7l5 5-5 5\" />\r\n </svg>\r\n );\r\n }\r\n if (kind === 'error') {\r\n return (\r\n <svg {...common}>\r\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\r\n <path d=\"M12 8v4.5\" />\r\n <path d=\"M12 16h.01\" />\r\n </svg>\r\n );\r\n }\r\n return (\r\n <svg {...common}>\r\n <path d=\"M9 18h6\" />\r\n <path d=\"M10 21h4\" />\r\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\" />\r\n </svg>\r\n );\r\n}\r\n","import type { Block, Message } from '../lib/api.ts';\r\nimport { formatDateTime } from '../lib/format.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport HighlightedText from './HighlightedText.tsx';\r\nimport { ThinkingBlock, ToolResultBlock, ToolUseBlock } from './ToolBlock.tsx';\r\n\r\nexport default function MessageBubble({\r\n message,\r\n query,\r\n}: {\r\n message: Message;\r\n query: string;\r\n}) {\r\n if (message.isMeta) return <SystemMessage message={message} query={query} />;\r\n if (\r\n message.type === 'user' &&\r\n message.blocks.length > 0 &&\r\n message.blocks.every((b) => b.type === 'tool_result')\r\n ) {\r\n return <AssistantMessage message={message} query={query} variant=\"tool\" />;\r\n }\r\n if (message.type === 'user') return <UserMessage message={message} query={query} />;\r\n return <AssistantMessage message={message} query={query} />;\r\n}\r\n\r\nfunction AssistantMessage({\r\n message,\r\n query,\r\n variant = 'assistant',\r\n}: {\r\n message: Message;\r\n query: string;\r\n variant?: 'assistant' | 'tool';\r\n}) {\r\n const t = useT();\r\n const isTool = variant === 'tool';\r\n const label = isTool ? t('message.role.tool') : t('message.role.claude');\r\n const borderClass = isTool\r\n ? 'border-l-[var(--color-hairline-strong)]'\r\n : 'border-l-[var(--color-accent)]';\r\n return (\r\n <div className=\"flex items-start gap-3\" data-uuid={message.uuid}>\r\n <Avatar role=\"assistant\" />\r\n <div className=\"min-w-0 flex-1 max-w-[min(54rem,calc(100%-3rem))]\">\r\n <Header\r\n align=\"left\"\r\n label={label}\r\n model={message.model}\r\n ts={message.ts}\r\n accent={!isTool}\r\n />\r\n <article\r\n className={\r\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)] ' +\r\n borderClass\r\n }\r\n >\r\n <Blocks blocks={message.blocks} query={query} />\r\n </article>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction UserMessage({ message, query }: { message: Message; query: string }) {\r\n const t = useT();\r\n return (\r\n <div className=\"flex items-start justify-end gap-3\" data-uuid={message.uuid}>\r\n <div className=\"min-w-0 max-w-[min(46rem,calc(100%-3rem))]\">\r\n <Header\r\n align=\"right\"\r\n label={t('message.role.you')}\r\n model={message.model}\r\n ts={message.ts}\r\n />\r\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)]\">\r\n <Blocks blocks={message.blocks} query={query} />\r\n </article>\r\n </div>\r\n <Avatar role=\"user\" />\r\n </div>\r\n );\r\n}\r\n\r\nfunction SystemMessage({ message, query }: { message: Message; query: string }) {\r\n const t = useT();\r\n return (\r\n <div className=\"my-2 flex items-center gap-3 px-4\" data-uuid={message.uuid}>\r\n <span className=\"h-px flex-1 bg-[var(--color-hairline)]\" />\r\n <div className=\"max-w-2xl text-center\">\r\n <p className=\"font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--color-fg-muted)]\">\r\n {t('message.role.system')} · {formatDateTime(message.ts)}\r\n </p>\r\n <div className=\"mt-1 space-y-1 text-xs italic text-[var(--color-fg-muted)]\">\r\n {message.blocks.map((block, i) => {\r\n if (block.type === 'text') {\r\n const text = block.text.length > 200 ? block.text.slice(0, 200) + '…' : block.text;\r\n return (\r\n <p key={i} className=\"whitespace-pre-wrap break-words\">\r\n <HighlightedText text={text} query={query} />\r\n </p>\r\n );\r\n }\r\n if (block.type === 'tool_use') return <p key={i}>{t('tool.use')} · {block.name}</p>;\r\n if (block.type === 'tool_result') return <p key={i}>{t('tool.result')}</p>;\r\n return null;\r\n })}\r\n </div>\r\n </div>\r\n <span className=\"h-px flex-1 bg-[var(--color-hairline)]\" />\r\n </div>\r\n );\r\n}\r\n\r\nfunction Header({\r\n align,\r\n label,\r\n model,\r\n ts,\r\n accent,\r\n}: {\r\n align: 'left' | 'right';\r\n label: string;\r\n model: string | null;\r\n ts: string | null;\r\n accent?: boolean;\r\n}) {\r\n return (\r\n <div\r\n className={\r\n 'flex items-baseline gap-2 text-[11px] ' +\r\n (align === 'right' ? 'flex-row-reverse text-right' : '')\r\n }\r\n >\r\n <span\r\n className={\r\n 'font-display text-[14px] font-medium tracking-tight ' +\r\n (accent\r\n ? 'text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]'\r\n : 'text-[var(--color-fg-primary)]')\r\n }\r\n >\r\n {label}\r\n </span>\r\n {model && (\r\n <span className=\"truncate font-mono text-[10px] uppercase tracking-[0.14em] text-[var(--color-fg-faint)]\">\r\n {model}\r\n </span>\r\n )}\r\n <time className=\"font-mono tabular-nums text-[var(--color-fg-muted)]\">\r\n {formatDateTime(ts)}\r\n </time>\r\n </div>\r\n );\r\n}\r\n\r\nfunction Blocks({ blocks, query }: { blocks: Block[]; query: string }) {\r\n const t = useT();\r\n return (\r\n <div className=\"space-y-2.5\">\r\n {blocks.map((block, i) => {\r\n switch (block.type) {\r\n case 'text':\r\n return (\r\n <p\r\n key={i}\r\n className=\"whitespace-pre-wrap break-words text-[14.5px] leading-relaxed\"\r\n >\r\n <HighlightedText text={block.text} query={query} />\r\n </p>\r\n );\r\n case 'tool_use':\r\n return <ToolUseBlock key={i} block={block} query={query} />;\r\n case 'tool_result':\r\n return <ToolResultBlock key={i} block={block} query={query} />;\r\n case 'thinking':\r\n return <ThinkingBlock key={i} block={block} query={query} />;\r\n case 'image':\r\n return (\r\n <div\r\n key={i}\r\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)]\"\r\n >\r\n {t('tool.image')}{block.mediaType ? ` · ${block.mediaType}` : ''}\r\n </div>\r\n );\r\n default:\r\n return (\r\n <pre\r\n key={i}\r\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)]\"\r\n >\r\n {JSON.stringify(block.raw, null, 2)}\r\n </pre>\r\n );\r\n }\r\n })}\r\n </div>\r\n );\r\n}\r\n\r\n// Trailing \"Claude is working…\" row — sits at the tail of a timeline while the\r\n// live poll keeps `isWorking` true, then unmounts when the reply lands. Shared by\r\n// the session detail timeline and the modified-files drawer's conversation column.\r\nexport function WorkingIndicator() {\r\n const t = useT();\r\n return (\r\n <li className=\"py-3\" aria-live=\"polite\">\r\n <div className=\"flex items-start gap-3\">\r\n <span\r\n aria-hidden\r\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)]\"\r\n >\r\n <span className=\"relative inline-flex h-2.5 w-2.5\">\r\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)] pulse-amber\" />\r\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)]\" />\r\n </span>\r\n </span>\r\n <div className=\"min-w-0 flex-1\">\r\n <span className=\"block font-display text-[14px] font-medium tracking-tight text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]\">\r\n {t('message.role.claude')}\r\n </span>\r\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\">\r\n <span aria-hidden className=\"loading-dots text-[var(--color-accent)]\">\r\n <span />\r\n <span />\r\n <span />\r\n </span>\r\n <span className=\"font-display text-[13.5px] italic text-[var(--color-fg-muted)]\">\r\n {t('session.working.indicator')}\r\n </span>\r\n </article>\r\n </div>\r\n </div>\r\n </li>\r\n );\r\n}\r\n\r\nfunction Avatar({ role }: { role: 'user' | 'assistant' }) {\r\n if (role === 'assistant') {\r\n return (\r\n <span\r\n aria-hidden\r\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)]\"\r\n >\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" aria-hidden>\r\n <path d=\"M19 7.5A8 8 0 1 0 19 16.5\" />\r\n <path d=\"M15.5 9.5a4.5 4.5 0 1 0 0 5\" opacity=\"0.45\" />\r\n </svg>\r\n </span>\r\n );\r\n }\r\n return (\r\n <span\r\n aria-hidden\r\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)]\"\r\n >\r\n Y\r\n </span>\r\n );\r\n}\r\n","import { motion } from 'motion/react';\r\nimport {\r\n useEffect,\r\n useLayoutEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type PointerEvent as ReactPointerEvent,\r\n} from 'react';\r\nimport type {\r\n DiffHunk,\r\n Message,\r\n ModifiedFileSummary,\r\n ModifiedFileToolName,\r\n} from '../lib/api.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport { Loading } from './Loading.tsx';\r\nimport MessageBubble, { WorkingIndicator } from './MessageBubble.tsx';\r\n\r\n// 三栏布局的最小内容区宽度:拖动任一分割线时,给中间文件内容栏保底的像素宽。\r\nconst CONTENT_MIN_PX = 320;\r\n\r\n// 对话栏初始只渲染最新的这么多条消息(默认落点在底部最新一条),更早的折叠在\r\n// 顶部「显示更早」后按这个步长逐批展开——会话动辄上百条,全量渲染既慢又埋没最新动态。\r\nconst CONV_INITIAL_VISIBLE = 20;\r\nconst CONV_LOAD_STEP = 40;\r\n\r\n// 对话栏「贴底跟随」阈值:滚动位置离底部小于这个像素时,视作用户在追看最新动态——\r\n// 实时轮询追加新消息(或 Claude 开始处理)后自动滚到底;超过则认为在往上翻历史,不打断。\r\nconst CONV_BOTTOM_STICK_PX = 120;\r\n\r\n/** Lookup from a tool_use id → the issuing tool's name + raw input, built from the\r\n * already-loaded session messages. Lets the detail pane render the actual edit\r\n * content (Write body / Edit diff) without a second backend round-trip. */\r\nexport type EditLookup = Map<string, { name: string; input: unknown }>;\r\n\r\ninterface Props {\r\n files: ModifiedFileSummary[];\r\n cwd: string | null;\r\n editLookup: EditLookup;\r\n /** Session conversation, rendered in the drawer's left column so edits can be\r\n * read alongside the dialogue that drove them. Already meta-filtered upstream. */\r\n messages: Message[];\r\n /** Active search query, forwarded to MessageBubble for in-message highlight. */\r\n query: string;\r\n /** Live poll says Claude is mid-turn — append the working indicator to the\r\n * conversation column's tail, mirroring the session timeline. */\r\n isWorking: boolean;\r\n loading: boolean;\r\n error: Error | null;\r\n onClose: () => void;\r\n /** Open the real file on disk in the OS default app. */\r\n onOpenFile: (filePath: string) => void;\r\n}\r\n\r\nexport default function ModifiedFilesDrawer({\r\n files,\r\n cwd,\r\n editLookup,\r\n messages,\r\n query,\r\n isWorking,\r\n loading,\r\n error,\r\n onClose,\r\n onOpenFile,\r\n}: Props) {\r\n const t = useT();\r\n const tree = useMemo(() => buildTree(files), [files]);\r\n\r\n // 选中文件路径(明细对象用 filePath 作为稳定 key)。打开时自动选第一个,\r\n // 避免右侧空白;files 变化后若当前选中已不在则回落到第一个。\r\n const [selected, setSelected] = useState<string | null>(null);\r\n useEffect(() => {\r\n setSelected((prev) =>\r\n prev && files.some((f) => f.filePath === prev) ? prev : files[0]?.filePath ?? null,\r\n );\r\n }, [files]);\r\n\r\n const selectedFile = useMemo(\r\n () => files.find((f) => f.filePath === selected) ?? null,\r\n [files, selected],\r\n );\r\n\r\n // 折叠的文件夹集合(默认全展开——修改文件清单通常不长,铺开更利于一眼扫)。\r\n const allFolders = useMemo(() => collectFolderPaths(tree), [tree]);\r\n const [collapsed, setCollapsed] = useState<ReadonlySet<string>>(() => new Set());\r\n const toggleFolder = (path: string) =>\r\n setCollapsed((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(path)) next.delete(path);\r\n else next.add(path);\r\n return next;\r\n });\r\n\r\n // 三栏(对话 | 内容 | 文件树)的两条可拖拽分割线:对话栏与文件树栏各持一个像素\r\n // 宽度,中间内容栏吃掉剩余空间。拖动时实时改、并各自给内容区留 CONTENT_MIN_PX 保底。\r\n const splitRef = useRef<HTMLDivElement>(null);\r\n const convScrollRef = useRef<HTMLDivElement>(null);\r\n const [convWidth, setConvWidth] = useState(420);\r\n const [railWidth, setRailWidth] = useState(280);\r\n\r\n // 对话栏可见消息:默认只展示尾部最新 CONV_INITIAL_VISIBLE 条,更早的折叠。\r\n const [visibleCount, setVisibleCount] = useState(CONV_INITIAL_VISIBLE);\r\n const startIndex = Math.max(0, messages.length - visibleCount);\r\n const visibleMessages = useMemo(() => messages.slice(startIndex), [messages, startIndex]);\r\n const hiddenCount = startIndex;\r\n\r\n // 展开更早消息时需要在重排后修正滚动位置:restoreFromBottom 保持「离底部的距离」\r\n // 不变,避免视口往上跳。\r\n const restoreFromBottom = useRef<number | null>(null);\r\n\r\n // 实时跟随状态:用户是否停在对话栏底部(决定要不要追新),以及上一次的消息数 /\r\n // 处理中标志(只在「真有新消息到达」或「刚开始处理」时才追,避免每次轮询都滚动)。\r\n const stickToBottom = useRef(true);\r\n const prevMsgCount = useRef(messages.length);\r\n const prevIsWorking = useRef(isWorking);\r\n\r\n // 打开抽屉时把对话栏滚到底——最新一条消息就是落点。messages 已就绪,commit 后\r\n // scrollHeight 即为准确值,用 layout effect 在绘制前定位,避免可见的跳动。\r\n useLayoutEffect(() => {\r\n const el = convScrollRef.current;\r\n if (el) el.scrollTop = el.scrollHeight;\r\n // 只在首次挂载(抽屉打开)时落到底部。\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, []);\r\n\r\n // visibleCount 变化(展开更早)后修正滚动位置。\r\n useLayoutEffect(() => {\r\n const el = convScrollRef.current;\r\n if (!el) return;\r\n if (restoreFromBottom.current != null) {\r\n el.scrollTop = el.scrollHeight - restoreFromBottom.current;\r\n restoreFromBottom.current = null;\r\n }\r\n }, [visibleCount]);\r\n\r\n // 滚动时记录是否停在底部,供下面的实时跟随判断「该不该追新」。\r\n function onConvScroll() {\r\n const el = convScrollRef.current;\r\n if (!el) return;\r\n stickToBottom.current = el.scrollHeight - (el.scrollTop + el.clientHeight) < CONV_BOTTOM_STICK_PX;\r\n }\r\n\r\n // 实时轮询追加了新消息、或 Claude 刚开始处理(WorkingIndicator 出现)时,若用户停在\r\n // 底部就跟随到最新——和会话时间线的自动跟随同义。展开更早触发的重排由上面的\r\n // visibleCount layout effect 负责,这里给 restoreFromBottom 让位,避免互相打架。\r\n useLayoutEffect(() => {\r\n const grew = messages.length > prevMsgCount.current;\r\n const startedWorking = isWorking && !prevIsWorking.current;\r\n prevMsgCount.current = messages.length;\r\n prevIsWorking.current = isWorking;\r\n if (!grew && !startedWorking) return;\r\n if (restoreFromBottom.current != null) return;\r\n if (!stickToBottom.current) return;\r\n const el = convScrollRef.current;\r\n if (el) el.scrollTop = el.scrollHeight;\r\n }, [messages.length, isWorking]);\r\n\r\n function showEarlier() {\r\n const el = convScrollRef.current;\r\n restoreFromBottom.current = el ? el.scrollHeight - el.scrollTop : null;\r\n setVisibleCount((c) => Math.min(messages.length, c + CONV_LOAD_STEP));\r\n }\r\n\r\n // Esc 关闭 + 背景滚动锁。\r\n useEffect(() => {\r\n function onKey(e: KeyboardEvent) {\r\n if (e.key === 'Escape') onClose();\r\n }\r\n window.addEventListener('keydown', onKey);\r\n const prevOverflow = document.body.style.overflow;\r\n document.body.style.overflow = 'hidden';\r\n return () => {\r\n window.removeEventListener('keydown', onKey);\r\n document.body.style.overflow = prevOverflow;\r\n };\r\n }, [onClose]);\r\n\r\n const count = files.length;\r\n const countLabel =\r\n count === 1\r\n ? t('session.modified.count', { n: count })\r\n : t('session.modified.countPlural', { n: count });\r\n\r\n return (\r\n <>\r\n <motion.div\r\n initial={{ opacity: 0 }}\r\n animate={{ opacity: 1 }}\r\n exit={{ opacity: 0 }}\r\n transition={{ duration: 0.18 }}\r\n onClick={onClose}\r\n className=\"fixed inset-0 z-[55] bg-[oklch(0.16_0.006_85_/_0.5)] backdrop-blur-[2px]\"\r\n aria-hidden\r\n />\r\n <motion.aside\r\n initial={{ x: '100%' }}\r\n animate={{ x: 0 }}\r\n exit={{ x: '100%' }}\r\n transition={{ duration: 0.26, ease: [0.16, 1, 0.3, 1] }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label={t('session.modified.title')}\r\n className=\"fixed inset-0 z-[60] flex w-full flex-col bg-[var(--color-surface)] shadow-[var(--shadow-pop)]\"\r\n >\r\n <header className=\"flex items-center justify-between gap-3 border-b border-[var(--color-hairline)] px-5 py-1.5\">\r\n <div className=\"flex min-w-0 items-center gap-2.5\">\r\n <span className=\"text-[var(--color-accent)]\">\r\n <TreeGlyph />\r\n </span>\r\n <div className=\"min-w-0\">\r\n <h2 className=\"font-display text-[14px] font-light leading-tight tracking-tight text-[var(--color-fg-primary)]\">\r\n {t('session.modified.title')}\r\n </h2>\r\n {cwd && (\r\n <p\r\n className=\"truncate font-mono text-[10.5px] tracking-[0.02em] text-[var(--color-fg-faint)]\"\r\n title={cwd}\r\n >\r\n {cwd}\r\n </p>\r\n )}\r\n </div>\r\n </div>\r\n <div className=\"flex shrink-0 items-center gap-2.5\">\r\n {count > 0 && (\r\n <span className=\"font-mono text-[10.5px] uppercase tracking-[0.16em] tabular-nums text-[var(--color-fg-muted)]\">\r\n {countLabel}\r\n </span>\r\n )}\r\n <button\r\n type=\"button\"\r\n onClick={onClose}\r\n aria-label={t('session.modified.close')}\r\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)]\"\r\n >\r\n <CloseIcon />\r\n </button>\r\n </div>\r\n </header>\r\n\r\n {loading && <Loading label={t('session.modified.loading')} className=\"m-6\" />}\r\n\r\n {error && !loading && (\r\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)]\">\r\n {t('session.modified.failed')}: {error.message}\r\n </p>\r\n )}\r\n\r\n {!loading && !error && count === 0 && (\r\n <p className=\"m-6 text-sm italic text-[var(--color-fg-muted)]\">\r\n {t('session.modified.empty')}\r\n </p>\r\n )}\r\n\r\n {!loading && !error && count > 0 && (\r\n <div ref={splitRef} className=\"flex min-h-0 flex-1\">\r\n {/* ① 对话栏:左侧,入场时从左缘滑入——读作「对话流进了弹窗」。 */}\r\n <motion.div\r\n initial={{ opacity: 0, x: -32 }}\r\n animate={{ opacity: 1, x: 0 }}\r\n transition={{ duration: 0.34, ease: [0.16, 1, 0.3, 1], delay: 0.06 }}\r\n className=\"flex shrink-0 flex-col border-r border-[var(--color-hairline)]\"\r\n style={{ width: convWidth }}\r\n >\r\n <div className=\"flex items-center gap-2 border-b border-[var(--color-hairline)] px-4 py-1.5\">\r\n <span className=\"eyebrow\">{t('session.modified.col.conversation')}</span>\r\n <span className=\"ml-auto font-mono text-[10px] tabular-nums text-[var(--color-fg-muted)]\">\r\n {messages.length}\r\n </span>\r\n </div>\r\n <div\r\n ref={convScrollRef}\r\n onScroll={onConvScroll}\r\n className=\"min-h-0 flex-1 overflow-auto px-4 py-2\"\r\n >\r\n {messages.length === 0 && !isWorking ? (\r\n <p className=\"px-1 py-3 text-sm italic text-[var(--color-fg-muted)]\">\r\n {t('common.noMessagesMatch')}\r\n </p>\r\n ) : (\r\n <>\r\n {hiddenCount > 0 && (\r\n <button\r\n type=\"button\"\r\n onClick={showEarlier}\r\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)]\"\r\n >\r\n {t('session.modified.showEarlier', { n: hiddenCount })}\r\n </button>\r\n )}\r\n <ol>\r\n {visibleMessages.map((m, i) => (\r\n <li key={m.uuid || m.ts || String(startIndex + i)} className=\"py-3\">\r\n <MessageBubble message={m} query={query} />\r\n </li>\r\n ))}\r\n {isWorking && <WorkingIndicator />}\r\n </ol>\r\n </>\r\n )}\r\n </div>\r\n </motion.div>\r\n\r\n <Splitter\r\n getRect={() => splitRef.current?.getBoundingClientRect() ?? null}\r\n onResize={(clientX, rect) =>\r\n setConvWidth(clampWidth(clientX - rect.left, rect.width - railWidth))\r\n }\r\n />\r\n\r\n {/* ② 文件内容栏:中间,吃掉剩余空间。 */}\r\n <div className=\"min-w-0 flex-1 overflow-auto\">\r\n {selectedFile ? (\r\n <FileDetail\r\n key={selectedFile.filePath}\r\n file={selectedFile}\r\n editLookup={editLookup}\r\n onOpenFile={onOpenFile}\r\n />\r\n ) : (\r\n <div className=\"flex h-full items-center justify-center px-6\">\r\n <p className=\"text-center text-sm italic text-[var(--color-fg-muted)]\">\r\n {t('session.modified.selectFile')}\r\n </p>\r\n </div>\r\n )}\r\n </div>\r\n\r\n <Splitter\r\n getRect={() => splitRef.current?.getBoundingClientRect() ?? null}\r\n onResize={(clientX, rect) =>\r\n setRailWidth(clampWidth(rect.right - clientX, rect.width - convWidth))\r\n }\r\n />\r\n\r\n {/* ③ 文件树栏:右侧,入场时从右缘滑入。 */}\r\n <motion.div\r\n initial={{ opacity: 0, x: 32 }}\r\n animate={{ opacity: 1, x: 0 }}\r\n transition={{ duration: 0.34, ease: [0.16, 1, 0.3, 1], delay: 0.06 }}\r\n className=\"flex shrink-0 flex-col border-l border-[var(--color-hairline)]\"\r\n style={{ width: railWidth }}\r\n >\r\n <div className=\"flex items-center justify-between gap-2 border-b border-[var(--color-hairline)] px-3 py-1.5\">\r\n <span className=\"eyebrow\">{t('session.modified.col.file')}</span>\r\n {allFolders.length > 0 && (\r\n <button\r\n type=\"button\"\r\n onClick={() =>\r\n setCollapsed((prev) =>\r\n prev.size === 0 ? new Set(allFolders) : new Set(),\r\n )\r\n }\r\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)]\"\r\n >\r\n {collapsed.size === 0\r\n ? t('session.modified.collapseAll')\r\n : t('session.modified.expandAll')}\r\n </button>\r\n )}\r\n </div>\r\n <div className=\"min-h-0 flex-1 overflow-auto py-1.5\">\r\n <ul role=\"tree\" className=\"w-max min-w-full select-none\">\r\n {tree.map((node) => (\r\n <TreeRow\r\n key={nodeKey(node)}\r\n node={node}\r\n depth={0}\r\n collapsed={collapsed}\r\n onToggleFolder={toggleFolder}\r\n selected={selected}\r\n onSelectFile={setSelected}\r\n onOpenFile={onOpenFile}\r\n />\r\n ))}\r\n </ul>\r\n </div>\r\n </motion.div>\r\n </div>\r\n )}\r\n </motion.aside>\r\n </>\r\n );\r\n}\r\n\r\n/* ── Draggable column splitter ──────────────────────────────────────────── */\r\n\r\n// 竖向分割线:拖动时把指针的 clientX 连同容器矩形回传,由调用方换算出该侧栏宽度。\r\n// 用 setPointerCapture 锁住指针,拖出分割线也不丢事件。\r\nfunction Splitter({\r\n getRect,\r\n onResize,\r\n}: {\r\n getRect: () => DOMRect | null;\r\n onResize: (clientX: number, rect: DOMRect) => void;\r\n}) {\r\n const dragging = useRef(false);\r\n return (\r\n <div\r\n role=\"separator\"\r\n aria-orientation=\"vertical\"\r\n onPointerDown={(e: ReactPointerEvent<HTMLDivElement>) => {\r\n e.preventDefault();\r\n e.currentTarget.setPointerCapture(e.pointerId);\r\n dragging.current = true;\r\n }}\r\n onPointerMove={(e: ReactPointerEvent<HTMLDivElement>) => {\r\n if (!dragging.current) return;\r\n const rect = getRect();\r\n if (rect) onResize(e.clientX, rect);\r\n }}\r\n onPointerUp={(e: ReactPointerEvent<HTMLDivElement>) => {\r\n dragging.current = false;\r\n e.currentTarget.releasePointerCapture(e.pointerId);\r\n }}\r\n className=\"relative w-px shrink-0 cursor-col-resize touch-none bg-[var(--color-hairline)] transition-colors hover:bg-[var(--color-accent)]\"\r\n >\r\n {/* 加宽命中区,但不挤占布局。 */}\r\n <span className=\"absolute inset-y-0 -left-1.5 -right-1.5\" aria-hidden />\r\n </div>\r\n );\r\n}\r\n\r\n// 把某侧栏宽夹在 [220px, available − 内容保底] 之间。available = 容器宽 − 另一侧栏宽。\r\nfunction clampWidth(value: number, available: number): number {\r\n const min = 220;\r\n const max = Math.max(min, available - CONTENT_MIN_PX);\r\n return Math.min(max, Math.max(min, value));\r\n}\r\n\r\n/* ── File tree ──────────────────────────────────────────────────────────── */\r\n\r\ninterface FolderNode {\r\n kind: 'folder';\r\n name: string;\r\n path: string;\r\n children: TreeNode[];\r\n}\r\ninterface FileNode {\r\n kind: 'file';\r\n name: string;\r\n file: ModifiedFileSummary;\r\n}\r\ntype TreeNode = FolderNode | FileNode;\r\n\r\nfunction nodeKey(node: TreeNode): string {\r\n return node.kind === 'folder' ? `d:${node.path}` : `f:${node.file.filePath}`;\r\n}\r\n\r\n/** 本会话内对该文件的「变更类型」,仅用于文件名着色(Git 习惯:A 绿 / M 琥珀)。\r\n * added = 本会话首个操作就是「创建」(Write/NotebookEdit 且 structuredPatch 为空数组,\r\n * 这是 Claude Code 记录全新文件的信号);否则一律按 modified。本工具集没有删除语义,故无 deleted。 */\r\ntype FileChangeType = 'added' | 'modified';\r\nfunction fileChangeType(file: ModifiedFileSummary): FileChangeType {\r\n const first = file.operations[0];\r\n const created =\r\n !!first &&\r\n (first.toolName === 'Write' || first.toolName === 'NotebookEdit') &&\r\n Array.isArray(first.structuredPatch) &&\r\n first.structuredPatch.length === 0;\r\n return created ? 'added' : 'modified';\r\n}\r\n\r\n/** 变更类型 → 文件名/图标的前景色 token。errored 在调用处优先用 danger,不走这里。 */\r\nfunction changeToneClass(type: FileChangeType): string {\r\n return type === 'added'\r\n ? 'text-[var(--color-moss)]'\r\n : 'text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]';\r\n}\r\n\r\n// 把扁平文件列表按目录段建成树。displayPath = relativePath ?? filePath,\r\n// 末段是文件名,其余是文件夹。文件夹内:文件夹优先、各自字母序。\r\n// 最后把\"只有一个子文件夹\"的单链折叠成 a/b 一行(IDE 习惯,省纵深)。\r\nfunction buildTree(files: ModifiedFileSummary[]): TreeNode[] {\r\n interface Building {\r\n folders: Map<string, Building>;\r\n files: { name: string; file: ModifiedFileSummary }[];\r\n path: string;\r\n }\r\n const root: Building = { folders: new Map(), files: [], path: '' };\r\n\r\n for (const file of files) {\r\n const display = file.relativePath ?? file.filePath;\r\n const segs = display.split(/[\\\\/]+/).filter(Boolean);\r\n const name = segs.pop() ?? display;\r\n let cur = root;\r\n for (const seg of segs) {\r\n let next = cur.folders.get(seg);\r\n if (!next) {\r\n next = { folders: new Map(), files: [], path: cur.path ? `${cur.path}/${seg}` : seg };\r\n cur.folders.set(seg, next);\r\n }\r\n cur = next;\r\n }\r\n cur.files.push({ name, file });\r\n }\r\n\r\n function materialize(b: Building): TreeNode[] {\r\n const folders: FolderNode[] = [];\r\n for (const [name, child] of b.folders) {\r\n folders.push(collapseChain({ kind: 'folder', name, path: child.path, children: materialize(child) }));\r\n }\r\n folders.sort((a, z) => a.name.localeCompare(z.name, undefined, { sensitivity: 'base' }));\r\n const fileNodes: FileNode[] = b.files\r\n .map((f) => ({ kind: 'file' as const, name: f.name, file: f.file }))\r\n .sort((a, z) => a.name.localeCompare(z.name, undefined, { sensitivity: 'base' }));\r\n return [...folders, ...fileNodes];\r\n }\r\n\r\n // a → (only child folder b) ⇒ \"a/b\"\r\n function collapseChain(folder: FolderNode): FolderNode {\r\n let node = folder;\r\n while (node.children.length === 1 && node.children[0]!.kind === 'folder') {\r\n const only = node.children[0] as FolderNode;\r\n node = { kind: 'folder', name: `${node.name}/${only.name}`, path: only.path, children: only.children };\r\n }\r\n return node;\r\n }\r\n\r\n return materialize(root);\r\n}\r\n\r\nfunction collectFolderPaths(nodes: TreeNode[]): string[] {\r\n const out: string[] = [];\r\n const walk = (ns: TreeNode[]) => {\r\n for (const n of ns) {\r\n if (n.kind === 'folder') {\r\n out.push(n.path);\r\n walk(n.children);\r\n }\r\n }\r\n };\r\n walk(nodes);\r\n return out;\r\n}\r\n\r\nfunction TreeRow({\r\n node,\r\n depth,\r\n collapsed,\r\n onToggleFolder,\r\n selected,\r\n onSelectFile,\r\n onOpenFile,\r\n}: {\r\n node: TreeNode;\r\n depth: number;\r\n collapsed: ReadonlySet<string>;\r\n onToggleFolder: (path: string) => void;\r\n selected: string | null;\r\n onSelectFile: (path: string) => void;\r\n onOpenFile: (filePath: string) => void;\r\n}) {\r\n const t = useT();\r\n const indent = { paddingLeft: `${depth * 14 + 10}px` };\r\n\r\n if (node.kind === 'folder') {\r\n const isCollapsed = collapsed.has(node.path);\r\n return (\r\n <li role=\"treeitem\" aria-expanded={!isCollapsed}>\r\n <button\r\n type=\"button\"\r\n onClick={() => onToggleFolder(node.path)}\r\n style={indent}\r\n className=\"flex w-full items-center gap-1.5 py-1 pr-2 text-left transition hover:bg-[var(--color-sunken)]\"\r\n >\r\n <Caret open={!isCollapsed} />\r\n <FolderIcon open={!isCollapsed} />\r\n <span className=\"whitespace-nowrap font-mono text-[12px] text-[var(--color-fg-secondary)]\">\r\n {node.name}\r\n </span>\r\n </button>\r\n {!isCollapsed && (\r\n <ul role=\"group\">\r\n {node.children.map((child) => (\r\n <TreeRow\r\n key={nodeKey(child)}\r\n node={child}\r\n depth={depth + 1}\r\n collapsed={collapsed}\r\n onToggleFolder={onToggleFolder}\r\n selected={selected}\r\n onSelectFile={onSelectFile}\r\n onOpenFile={onOpenFile}\r\n />\r\n ))}\r\n </ul>\r\n )}\r\n </li>\r\n );\r\n }\r\n\r\n const f = node.file;\r\n const isSelected = selected === f.filePath;\r\n const openLabel = t('session.modified.openFile');\r\n const changeType = fileChangeType(f);\r\n // 文件名/图标着色按变更类型走;errored 是另一根轴,红点单独标,不抢文件名的语义色。\r\n const nameTone = changeToneClass(changeType);\r\n return (\r\n <li\r\n role=\"treeitem\"\r\n aria-selected={isSelected}\r\n className={\r\n 'group flex w-full items-center transition ' +\r\n (isSelected\r\n ? 'bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\r\n : 'hover:bg-[var(--color-sunken)]')\r\n }\r\n >\r\n {/* flex-1 但不加 min-w-0:min-width:auto 保证按钮不缩到内容以下,\r\n 文件名 whitespace-nowrap 撑宽行 → 外层 ul(w-max) 横向出滚动条。 */}\r\n <button\r\n type=\"button\"\r\n onClick={() => onSelectFile(f.filePath)}\r\n style={indent}\r\n title={f.filePath}\r\n className=\"flex flex-1 items-center gap-1.5 py-1 pr-2 text-left\"\r\n >\r\n <span className=\"w-[11px] shrink-0\" aria-hidden />\r\n <FileIcon errored={f.errorCount > 0} tone={changeType} />\r\n <span\r\n className={\r\n 'whitespace-nowrap font-mono text-[12px] ' +\r\n nameTone +\r\n (isSelected ? ' font-medium' : '')\r\n }\r\n >\r\n {node.name}\r\n </span>\r\n </button>\r\n <span className=\"flex shrink-0 items-center gap-1 pl-1 pr-2\">\r\n {f.errorCount > 0 && <span className=\"h-1.5 w-1.5 rounded-full bg-[var(--color-danger)]\" />}\r\n <span\r\n className={`font-mono text-[10px] font-semibold leading-none ${nameTone}`}\r\n title={t(changeType === 'added' ? 'session.modified.added' : 'session.modified.modified')}\r\n >\r\n {changeType === 'added' ? 'A' : 'M'}\r\n </span>\r\n <span className=\"font-mono text-[10px] tabular-nums text-[var(--color-fg-muted)] group-hover:hidden\">\r\n {f.totalCount}\r\n </span>\r\n <button\r\n type=\"button\"\r\n onClick={() => onOpenFile(f.filePath)}\r\n title={openLabel}\r\n aria-label={openLabel}\r\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)]\"\r\n >\r\n <ExternalIcon />\r\n </button>\r\n </span>\r\n </li>\r\n );\r\n}\r\n\r\n/* ── File detail (operations + content) ─────────────────────────────────── */\r\n\r\nfunction FileDetail({\r\n file,\r\n editLookup,\r\n onOpenFile,\r\n}: {\r\n file: ModifiedFileSummary;\r\n editLookup: EditLookup;\r\n onOpenFile: (filePath: string) => void;\r\n}) {\r\n const t = useT();\r\n const display = file.relativePath ?? file.filePath;\r\n const tail = display.split(/[\\\\/]+/).pop() ?? display;\r\n const changeType = fileChangeType(file);\r\n const { rows, newFile } = useMemo(() => buildFileRows(file, editLookup), [file, editLookup]);\r\n\r\n return (\r\n <div className=\"flex flex-col\">\r\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\">\r\n <div className=\"flex items-center gap-2\">\r\n <FileIcon errored={file.errorCount > 0} tone={changeType} />\r\n <h3\r\n className={\r\n 'min-w-0 flex-1 truncate font-mono text-[13.5px] font-medium ' +\r\n (file.errorCount > 0 ? 'text-[var(--color-danger)]' : changeToneClass(changeType))\r\n }\r\n title={file.filePath}\r\n >\r\n {tail}\r\n </h3>\r\n <button\r\n type=\"button\"\r\n onClick={() => onOpenFile(file.filePath)}\r\n title={t('session.modified.openFile')}\r\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)]\"\r\n >\r\n <ExternalIcon />\r\n {t('session.modified.openFile')}\r\n </button>\r\n </div>\r\n <p className=\"mt-0.5 truncate font-mono text-[10.5px] text-[var(--color-fg-faint)]\" title={file.filePath}>\r\n {file.relativePath ?? `${t('session.modified.absolutePath')} · ${file.filePath}`}\r\n </p>\r\n <div className=\"mt-2 flex flex-wrap items-center gap-1.5\">\r\n {file.editCount > 0 && <ToolChip name=\"Edit\" count={file.editCount} />}\r\n {file.writeCount > 0 && <ToolChip name=\"Write\" count={file.writeCount} />}\r\n {file.multiEditCount > 0 && <ToolChip name=\"MultiEdit\" count={file.multiEditCount} />}\r\n {file.notebookEditCount > 0 && <ToolChip name=\"NotebookEdit\" count={file.notebookEditCount} />}\r\n {file.errorCount > 0 && (\r\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)]\">\r\n {t('session.modified.errorBadge', { n: file.errorCount })}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <div className=\"px-5 py-4\">\r\n <SplitDiff rows={rows} label={newFile ? t('session.modified.newContent') : undefined} />\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nconst strOf = (v: unknown): string => (typeof v === 'string' ? v : '');\r\n\r\n/** 把一个文件在本会话里的全部操作合并成「单文件」统一视图行:左旧右新。\r\n * 首选带真实文件行号的 structuredPatch——把各次操作的 hunk 全收齐、按新文件行号排序,\r\n * 交给 rowsFromHunks 渲染(hunk 间的未改动区折叠成一行 gap),于是同一文件的多次改动\r\n * 拼成一份连贯的左右对照。注意:各次操作的行号是「该次操作落地后」坐标,跨操作不共享\r\n * 同一坐标系;同一处被多次改写会按操作先后逐次呈现(中间态),这是只有 diff、没有整文件\r\n * 快照时能做到的最忠实拼接。\r\n * 完全没有 structuredPatch(全新文件 create / 结果尚未落地的 pending)时,退回用 tool\r\n * 输入的原文按操作顺序拼接;newFile=本会话全程只有 Write/NotebookEdit 的创建写入。 */\r\nfunction buildFileRows(\r\n file: ModifiedFileSummary,\r\n editLookup: EditLookup,\r\n): { rows: UnifiedRow[]; newFile: boolean } {\r\n const hunks = file.operations.flatMap((op) => op.structuredPatch ?? []);\r\n if (hunks.length > 0) {\r\n const sorted = [...hunks].sort((a, b) => a.newStart - b.newStart);\r\n return { rows: rowsFromHunks(sorted), newFile: false };\r\n }\r\n\r\n const rows: UnifiedRow[] = [];\r\n let newFile = true;\r\n for (const op of file.operations) {\r\n const rec = asRecord(editLookup.get(op.toolUseId)?.input);\r\n if (op.toolName === 'Write') {\r\n rows.push(...rowsFromStrings('', strOf(rec.content)));\r\n } else if (op.toolName === 'NotebookEdit') {\r\n rows.push(...rowsFromStrings('', strOf(rec.new_source)));\r\n } else if (op.toolName === 'MultiEdit') {\r\n newFile = false;\r\n const edits = Array.isArray(rec.edits) ? rec.edits : [];\r\n for (const e of edits) {\r\n const er = asRecord(e);\r\n rows.push(...rowsFromStrings(strOf(er.old_string), strOf(er.new_string)));\r\n }\r\n } else {\r\n newFile = false;\r\n rows.push(...rowsFromStrings(strOf(rec.old_string), strOf(rec.new_string)));\r\n }\r\n }\r\n return { rows, newFile };\r\n}\r\n\r\n/** 行内 word-level 片段:changed=该 token 仅存在于本侧(GitHub 行内高亮)。 */\r\ninterface Seg {\r\n text: string;\r\n changed: boolean;\r\n}\r\n\r\n/** 统一视图的一行:context=两侧都在的未改动行;del/add=删除/新增行;\r\n * gap=被折叠省略的未改动区间(只显示省略了多少行,不渲染正文)。 */\r\ntype RowKind = 'context' | 'del' | 'add' | 'gap';\r\ninterface UnifiedRow {\r\n oldNo: number | null;\r\n newNo: number | null;\r\n kind: RowKind;\r\n text: string | null;\r\n /** 仅 gap 行有值:被折叠省略的行数。 */\r\n gap?: number;\r\n /** 仅在「删除 ↔ 新增」配对行上有值,用于行内高亮;其余为 null(整行着色)。 */\r\n segs: Seg[] | null;\r\n}\r\n\r\n/** 分屏(左右两栏)一侧的一行。kind=empty 时本侧无对应行(对侧是纯增 / 纯删),\r\n * 渲染成留白占位行,保证两栏行高一一对齐。 */\r\ninterface SplitCell {\r\n no: number | null;\r\n kind: 'context' | 'del' | 'add' | 'empty';\r\n text: string | null;\r\n segs: Seg[] | null;\r\n}\r\n/** 分屏一行:pair=左右各一格;gap=折叠的未改动区间,两栏同高同文。 */\r\ninterface SplitRow {\r\n kind: 'pair' | 'gap';\r\n gap?: number;\r\n left?: SplitCell;\r\n right?: SplitCell;\r\n}\r\n\r\n/** 统一视图行 → 分屏行。rowsFrom* 在一个改动块里先发全部 del 再发全部 add,\r\n * 这里按序号把 del[x]↔add[x] 配成一行(多出的一侧用 empty 占位),未改动行左右同文。 */\r\nfunction toSplitRows(rows: UnifiedRow[]): SplitRow[] {\r\n const out: SplitRow[] = [];\r\n let i = 0;\r\n while (i < rows.length) {\r\n const r = rows[i]!;\r\n if (r.kind === 'gap') {\r\n out.push({ kind: 'gap', gap: r.gap ?? 0 });\r\n i++;\r\n continue;\r\n }\r\n if (r.kind === 'context') {\r\n out.push({\r\n kind: 'pair',\r\n left: { no: r.oldNo, kind: 'context', text: r.text, segs: null },\r\n right: { no: r.newNo, kind: 'context', text: r.text, segs: null },\r\n });\r\n i++;\r\n continue;\r\n }\r\n const dels: UnifiedRow[] = [];\r\n const adds: UnifiedRow[] = [];\r\n while (i < rows.length && rows[i]!.kind === 'del') dels.push(rows[i++]!);\r\n while (i < rows.length && rows[i]!.kind === 'add') adds.push(rows[i++]!);\r\n const max = Math.max(dels.length, adds.length);\r\n for (let x = 0; x < max; x++) {\r\n const d = dels[x];\r\n const a = adds[x];\r\n out.push({\r\n kind: 'pair',\r\n left: d\r\n ? { no: d.oldNo, kind: 'del', text: d.text, segs: d.segs }\r\n : { no: null, kind: 'empty', text: null, segs: null },\r\n right: a\r\n ? { no: a.newNo, kind: 'add', text: a.text, segs: a.segs }\r\n : { no: null, kind: 'empty', text: null, segs: null },\r\n });\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/** GitHub 风格的分屏 diff:左栏旧(含删除),右栏新(含新增),两栏行级对齐。\r\n * 两栏各自横向滚动;因每行 whitespace-pre 恒为一行高、左右行数相同,纵向天然对齐。 */\r\nfunction SplitDiff({ rows, label }: { rows: UnifiedRow[]; label?: string }) {\r\n if (rows.length === 0) return <EmptyBody />;\r\n const split = toSplitRows(rows);\r\n return (\r\n <div className=\"overflow-hidden rounded-lg border border-[var(--color-hairline)]\">\r\n {label && (\r\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)]\">\r\n {label}\r\n </div>\r\n )}\r\n <div className=\"flex\">\r\n <div className=\"w-1/2 overflow-x-auto border-r border-[var(--color-hairline)]\">\r\n <div className=\"w-max min-w-full\">\r\n {split.map((r, i) => (\r\n <SplitLine key={i} row={r} side=\"left\" />\r\n ))}\r\n </div>\r\n </div>\r\n <div className=\"w-1/2 overflow-x-auto\">\r\n <div className=\"w-max min-w-full\">\r\n {split.map((r, i) => (\r\n <SplitLine key={i} row={r} side=\"right\" />\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction SplitLine({ row, side }: { row: SplitRow; side: 'left' | 'right' }) {\r\n const t = useT();\r\n if (row.kind === 'gap') {\r\n return (\r\n <div className=\"flex bg-[var(--color-sunken)] leading-[1.65] text-[var(--color-fg-faint)]\">\r\n <span className=\"sticky left-0 z-[1] w-[3em] shrink-0 select-none border-r border-[var(--color-hairline)] bg-[inherit] px-1.5 text-center font-mono text-[11px]\">\r\n ⋯\r\n </span>\r\n <span className=\"whitespace-nowrap px-3 font-mono text-[10px] italic tracking-[0.04em]\">\r\n {t('session.modified.linesOmitted', { n: row.gap ?? 0 })}\r\n </span>\r\n </div>\r\n );\r\n }\r\n const cell = side === 'left' ? row.left! : row.right!;\r\n const bg =\r\n cell.kind === 'del'\r\n ? 'bg-[var(--color-danger-soft)]'\r\n : cell.kind === 'add'\r\n ? 'bg-[var(--color-moss-soft)]'\r\n : cell.kind === 'empty'\r\n ? 'bg-[var(--color-sunken)]/40'\r\n : 'bg-[var(--color-surface)]';\r\n const marker = cell.kind === 'del' ? '−' : cell.kind === 'add' ? '+' : '';\r\n const markerColor =\r\n cell.kind === 'del'\r\n ? 'text-[var(--color-danger)]'\r\n : cell.kind === 'add'\r\n ? 'text-[var(--color-moss)]'\r\n : 'text-transparent';\r\n // 改动 token 的强调底色:在整行 -soft 底色上再叠一层更饱和的同色(GitHub 行内高亮)。\r\n const hl = cell.kind === 'del' ? 'bg-[var(--color-danger)]/25' : 'bg-[var(--color-moss)]/30';\r\n return (\r\n <div className={`flex leading-[1.65] ${bg}`}>\r\n <span className=\"sticky left-0 z-[1] 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)]\">\r\n {cell.no ?? ''}\r\n </span>\r\n <span className={`w-4 shrink-0 select-none text-center font-mono text-[11.5px] ${markerColor}`}>\r\n {marker}\r\n </span>\r\n <div className=\"whitespace-pre pr-3 font-mono text-[11.5px] text-[var(--color-fg-primary)]\">\r\n {cell.segs && cell.segs.length > 0\r\n ? cell.segs.map((s, i) =>\r\n s.changed ? (\r\n <span key={i} className={hl}>\r\n {s.text}\r\n </span>\r\n ) : (\r\n <span key={i}>{s.text}</span>\r\n ),\r\n )\r\n : cell.text == null || cell.text === ''\r\n ? ' '\r\n : cell.text}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction EmptyBody() {\r\n const t = useT();\r\n return (\r\n <p className=\"px-3 py-2.5 text-[12px] italic text-[var(--color-fg-muted)]\">\r\n {t('session.modified.noContent')}\r\n </p>\r\n );\r\n}\r\n\r\n/** structuredPatch(带真实行号的 hunk)→ 统一视图行。hunk 之间的未改动区折叠成一行 gap。 */\r\nfunction rowsFromHunks(hunks: DiffHunk[]): UnifiedRow[] {\r\n const rows: UnifiedRow[] = [];\r\n let prevNewEnd = 0; // 上一 hunk 结束时的新文件行号(0=文件开头)\r\n for (const h of hunks) {\r\n const gap = h.newStart - prevNewEnd - 1;\r\n if (gap > 0) rows.push({ oldNo: null, newNo: null, kind: 'gap', text: null, gap, segs: null });\r\n let oldNo = h.oldStart;\r\n let newNo = h.newStart;\r\n const lines = h.lines;\r\n let i = 0;\r\n while (i < lines.length) {\r\n const c = lines[i]![0];\r\n if (c === '-' || c === '+') {\r\n // 收集相邻的一段删除 + 新增,按序号配对做行内高亮(GitHub 习惯)。\r\n const dels: string[] = [];\r\n const adds: string[] = [];\r\n const delStart = oldNo;\r\n const addStart = newNo;\r\n while (i < lines.length && lines[i]![0] === '-') {\r\n dels.push(lines[i]!.slice(1));\r\n oldNo++;\r\n i++;\r\n }\r\n while (i < lines.length && lines[i]![0] === '+') {\r\n adds.push(lines[i]!.slice(1));\r\n newNo++;\r\n i++;\r\n }\r\n for (let x = 0; x < dels.length; x++) {\r\n const paired = x < adds.length ? wordSegments(dels[x]!, adds[x]!) : null;\r\n rows.push({ oldNo: delStart + x, newNo: null, kind: 'del', text: dels[x]!, segs: paired?.left ?? null });\r\n }\r\n for (let x = 0; x < adds.length; x++) {\r\n const paired = x < dels.length ? wordSegments(dels[x]!, adds[x]!) : null;\r\n rows.push({ oldNo: null, newNo: addStart + x, kind: 'add', text: adds[x]!, segs: paired?.right ?? null });\r\n }\r\n } else {\r\n // 上下文行(前导空格);异常前缀(如 \"\\")并入上下文不影响对齐。\r\n rows.push({ oldNo, newNo, kind: 'context', text: lines[i]!.slice(1), segs: null });\r\n oldNo++;\r\n newNo++;\r\n i++;\r\n }\r\n }\r\n prevNewEnd = newNo - 1;\r\n }\r\n return rows;\r\n}\r\n\r\n/** old/new 原文 → 统一视图行(行号从 1 起、无真实文件行号,用于 create 全新内容\r\n * 或结果尚未落地的兜底;一旦 structuredPatch 到位即被真实行号版本取代)。 */\r\nfunction rowsFromStrings(oldStr: string, newStr: string): UnifiedRow[] {\r\n // 空串视为 0 行(而非 ['']),避免纯新增 / 纯删除时出现一个幻影空行。\r\n const ops = diffOps(oldStr === '' ? [] : oldStr.split('\\n'), newStr === '' ? [] : newStr.split('\\n'));\r\n const rows: UnifiedRow[] = [];\r\n let oldNo = 0;\r\n let newNo = 0;\r\n let i = 0;\r\n while (i < ops.length) {\r\n const op = ops[i]!;\r\n if (op.type === 'equal') {\r\n oldNo++;\r\n newNo++;\r\n rows.push({ oldNo, newNo, kind: 'context', text: op.text, segs: null });\r\n i++;\r\n continue;\r\n }\r\n const dels: string[] = [];\r\n const adds: string[] = [];\r\n while (i < ops.length && ops[i]!.type !== 'equal') {\r\n const cur = ops[i]!;\r\n if (cur.type === 'del') dels.push(cur.text);\r\n else adds.push(cur.text);\r\n i++;\r\n }\r\n for (let x = 0; x < dels.length; x++) {\r\n const paired = x < adds.length ? wordSegments(dels[x]!, adds[x]!) : null;\r\n oldNo++;\r\n rows.push({ oldNo, newNo: null, kind: 'del', text: dels[x]!, segs: paired?.left ?? null });\r\n }\r\n for (let x = 0; x < adds.length; x++) {\r\n const paired = x < dels.length ? wordSegments(dels[x]!, adds[x]!) : null;\r\n newNo++;\r\n rows.push({ oldNo: null, newNo, kind: 'add', text: adds[x]!, segs: paired?.right ?? null });\r\n }\r\n }\r\n return rows;\r\n}\r\n\r\n// token 粒度:连续空白 / 连续单词字符 / 连续标点各成一段,贴近 GitHub 的行内分词。\r\nconst WORD_RE = /\\s+|\\w+|[^\\w\\s]+/g;\r\n\r\n/** 一行内的 word-level diff:复用 diffOps(行级 LCS)在 token 上再跑一次。\r\n * 返回左右两侧的 token 段(changed=仅本侧独有)。两行毫无公共 token、空行或过长行时返回 null(退回整行着色)。 */\r\nfunction wordSegments(oldLine: string, newLine: string): { left: Seg[]; right: Seg[] } | null {\r\n const a = oldLine.match(WORD_RE) ?? [];\r\n const b = newLine.match(WORD_RE) ?? [];\r\n if (a.length === 0 || b.length === 0) return null;\r\n if (a.length * b.length > 20000) return null; // O(n·m) 兜底,避免超长/压缩行卡顿\r\n const ops = diffOps(a, b);\r\n const left: Seg[] = [];\r\n const right: Seg[] = [];\r\n for (const op of ops) {\r\n if (op.type === 'equal') {\r\n pushSeg(left, op.text, false);\r\n pushSeg(right, op.text, false);\r\n } else if (op.type === 'del') {\r\n pushSeg(left, op.text, true);\r\n } else {\r\n pushSeg(right, op.text, true);\r\n }\r\n }\r\n // 两侧全是 changed → 没有公共 token,高亮等于整行,没意义,退回整行着色。\r\n if (left.every((s) => s.changed) && right.every((s) => s.changed)) return null;\r\n return { left, right };\r\n}\r\n\r\nfunction pushSeg(arr: Seg[], text: string, changed: boolean): void {\r\n const last = arr[arr.length - 1];\r\n if (last && last.changed === changed) last.text += text;\r\n else arr.push({ text, changed });\r\n}\r\n\r\ninterface DiffOp {\r\n type: 'equal' | 'del' | 'add';\r\n text: string;\r\n}\r\n\r\n// 经典 LCS 行级 diff:dp[i][j] = a[i:] 与 b[j:] 的最长公共子序列长度,\r\n// 回溯得到 equal / del / add 操作序列。编辑片段通常很短,O(n·m) 足够。\r\nfunction diffOps(a: string[], b: string[]): DiffOp[] {\r\n const n = a.length;\r\n const m = b.length;\r\n const dp: number[][] = [];\r\n for (let i = 0; i <= n; i++) dp.push(new Array<number>(m + 1).fill(0));\r\n for (let i = n - 1; i >= 0; i--) {\r\n const row = dp[i]!;\r\n const next = dp[i + 1]!;\r\n for (let j = m - 1; j >= 0; j--) {\r\n row[j] = a[i] === b[j] ? next[j + 1]! + 1 : Math.max(next[j]!, row[j + 1]!);\r\n }\r\n }\r\n const ops: DiffOp[] = [];\r\n let i = 0;\r\n let j = 0;\r\n while (i < n && j < m) {\r\n if (a[i] === b[j]) {\r\n ops.push({ type: 'equal', text: a[i]! });\r\n i++;\r\n j++;\r\n } else if (dp[i + 1]![j]! >= dp[i]![j + 1]!) {\r\n ops.push({ type: 'del', text: a[i]! });\r\n i++;\r\n } else {\r\n ops.push({ type: 'add', text: b[j]! });\r\n j++;\r\n }\r\n }\r\n while (i < n) ops.push({ type: 'del', text: a[i++]! });\r\n while (j < m) ops.push({ type: 'add', text: b[j++]! });\r\n return ops;\r\n}\r\n\r\nfunction asRecord(x: unknown): Record<string, unknown> {\r\n return x && typeof x === 'object' ? (x as Record<string, unknown>) : {};\r\n}\r\n\r\nfunction ToolChip({ name, count }: { name: ModifiedFileToolName; count: number }) {\r\n return (\r\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)]\">\r\n {name}\r\n <span className=\"ml-1 tabular-nums text-[var(--color-fg-muted)]\">×{count}</span>\r\n </span>\r\n );\r\n}\r\n\r\n/* ── Icons ──────────────────────────────────────────────────────────────── */\r\n\r\nfunction Caret({ open }: { open: boolean }) {\r\n return (\r\n <svg\r\n width=\"9\"\r\n height=\"9\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={'shrink-0 text-[var(--color-fg-muted)] transition-transform ' + (open ? 'rotate-90' : '')}\r\n aria-hidden\r\n >\r\n <path d=\"M9 6l6 6-6 6\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction FolderIcon({ open }: { open: boolean }) {\r\n return (\r\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>\r\n {open ? (\r\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\" />\r\n ) : (\r\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\" />\r\n )}\r\n </svg>\r\n );\r\n}\r\n\r\nfunction FileIcon({ errored, tone }: { errored?: boolean; tone?: FileChangeType }) {\r\n const color = errored\r\n ? 'text-[var(--color-danger)]'\r\n : tone\r\n ? changeToneClass(tone)\r\n : 'text-[var(--color-fg-muted)]';\r\n return (\r\n <svg\r\n width=\"12\"\r\n height=\"12\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.6\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className={'shrink-0 ' + color}\r\n aria-hidden\r\n >\r\n <path d=\"M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z\" />\r\n <path d=\"M14 3v5h5\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction TreeGlyph() {\r\n return (\r\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\r\n <path d=\"M3 5h7l2 2h9\" />\r\n <path d=\"M3 5v14h18V9\" />\r\n <path d=\"M8 13h8M8 16h5\" opacity=\"0.5\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction ExternalIcon() {\r\n return (\r\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>\r\n <path d=\"M14 4h6v6\" />\r\n <path d=\"M20 4l-9 9\" />\r\n <path d=\"M19 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction CloseIcon() {\r\n return (\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" aria-hidden>\r\n <path d=\"M6 6l12 12M18 6L6 18\" />\r\n </svg>\r\n );\r\n}\r\n","import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\r\nimport { AnimatePresence, motion } from 'motion/react';\r\nimport {\r\n useDeferredValue,\r\n useEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from 'react';\r\nimport { useNavigate, useParams, useSearchParams } from 'react-router-dom';\r\nimport Breadcrumbs, { BreadcrumbFolderIcon } from '../components/Breadcrumbs.tsx';\r\nimport DeleteDialog from '../components/DeleteDialog.tsx';\r\nimport { Loading } from '../components/Loading.tsx';\r\nimport MessageBubble, { WorkingIndicator } from '../components/MessageBubble.tsx';\r\nimport ModifiedFilesDrawer, { type EditLookup } from '../components/ModifiedFilesDrawer.tsx';\r\nimport {\r\n api,\r\n type Block,\r\n type Message,\r\n type ModifiedFilesResponse,\r\n type OpenFileResult,\r\n type ProjectSummary,\r\n type SessionDetail,\r\n type SessionSummary,\r\n} from '../lib/api.ts';\r\nimport {\r\n INTERRUPTED_MARKER_RE,\r\n MAX_SESSION_MESSAGES,\r\n RECENT_ACTIVITY_WINDOW_MIN,\r\n} from '../lib/constants.ts';\r\nimport { formatBytes, formatDateTime, formatRelativeTime } from '../lib/format.ts';\r\nimport { useT } from '../lib/i18n.ts';\r\nimport { fadeUpItem, staggerParent } from '../lib/motion.ts';\r\nimport { queryKeys } from '../lib/query-keys.ts';\r\n\r\ninterface IndexedMessage {\r\n message: Message;\r\n haystack: string;\r\n}\r\n\r\nconst INITIAL_WINDOW = 50;\r\nconst LOAD_STEP = 50;\r\n\r\n// Live tail: while a session is still being written, poll the detail endpoint so\r\n// new messages append on their own. \"Still being written\" = the session's lastAt\r\n// (max of latest record ts and file mtime) sits inside the same recent-activity\r\n// window the rest of the app uses for \"active\". Polling self-terminates: once the\r\n// file stops changing, lastAt goes stale and refetchInterval returns false.\r\nconst LIVE_POLL_INTERVAL_MS = 2000;\r\nconst LIVE_WINDOW_MS = RECENT_ACTIVITY_WINDOW_MIN * 60 * 1000;\r\n// Auto-follow only kicks in when the viewport is within this many px of the\r\n// bottom — so watching the tail follows new messages, but scrolling up to read\r\n// history is never yanked back down.\r\nconst BOTTOM_STICK_THRESHOLD_PX = 120;\r\n\r\nfunction isWithinLiveWindow(lastAt: string | null | undefined): boolean {\r\n if (!lastAt) return false;\r\n const ms = new Date(lastAt).getTime();\r\n if (Number.isNaN(ms)) return false;\r\n return Date.now() - ms < LIVE_WINDOW_MS;\r\n}\r\n\r\n// Mirror of the server's `lastTurnIncomplete` over the parsed message list: the\r\n// last turn is unfinished when the final record is a `user` message (Claude owes\r\n// a reply) that isn't an abort marker, or an `assistant` message that ends on a\r\n// pending `tool_use`. Combined with the live window this drives the \"working\" UI.\r\nfunction lastTurnIncomplete(messages: Message[]): boolean {\r\n const last = messages[messages.length - 1];\r\n if (!last) return false;\r\n if (last.type === 'assistant') {\r\n return last.blocks[last.blocks.length - 1]?.type === 'tool_use';\r\n }\r\n const text = last.blocks.find((b) => b.type === 'text');\r\n return !(text && INTERRUPTED_MARKER_RE.test(text.text));\r\n}\r\n\r\nexport default function SessionDetailRoute() {\r\n const t = useT();\r\n const navigate = useNavigate();\r\n const { projectId, sessionId } = useParams<{ projectId: string; sessionId: string }>();\r\n const [searchParams] = useSearchParams();\r\n const pid = projectId ?? '';\r\n const sid = sessionId ?? '';\r\n const urlFocus = searchParams.get('focus');\r\n const urlQuery = searchParams.get('q');\r\n\r\n const [showMeta, setShowMeta] = useState(false);\r\n const [onlyUser, setOnlyUser] = useState(false);\r\n const [query, setQuery] = useState('');\r\n const deferredQuery = useDeferredValue(query);\r\n const [windowSize, setWindowSize] = useState(INITIAL_WINDOW);\r\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\r\n const [showModifiedDrawer, setShowModifiedDrawer] = useState(false);\r\n const urlAppliedRef = useRef<string | null>(null);\r\n const flashedKeyRef = useRef<string | null>(null);\r\n // Live-tail follow state: whether the reader is parked at the bottom, and the\r\n // message count from the previous render so we only follow genuine new arrivals.\r\n const stickToBottomRef = useRef(true);\r\n const prevMsgCountRef = useRef<number | null>(null);\r\n // Previous \"working\" flag, so the tail follows the moment the working indicator\r\n // appears (the indicator grows the page without bumping messageCount).\r\n const prevIsWorkingRef = useRef(false);\r\n\r\n useEffect(() => {\r\n setWindowSize(INITIAL_WINDOW);\r\n prevMsgCountRef.current = null;\r\n }, [pid, sid]);\r\n\r\n const { data, isLoading, error } = useQuery({\r\n queryKey: queryKeys.session(pid, sid),\r\n queryFn: () =>\r\n api<SessionDetail>(\r\n `/api/sessions/${encodeURIComponent(pid)}/${encodeURIComponent(sid)}`,\r\n ),\r\n enabled: !!pid && !!sid,\r\n // Re-read the jsonl on an interval while the session is live so newly written\r\n // messages stream in on their own. The function form is re-evaluated with a\r\n // fresh Date.now() after every poll, so it stops on its own once activity\r\n // stops — even when the response is byte-identical and never re-renders us.\r\n refetchInterval: (query) =>\r\n isWithinLiveWindow(query.state.data?.meta.lastAt) ? LIVE_POLL_INTERVAL_MS : false,\r\n });\r\n const isLive = isWithinLiveWindow(data?.meta.lastAt);\r\n // Recomputed off the polled message list every 2s, so the working indicator\r\n // appears while Claude generates and clears the moment its reply lands.\r\n const isWorking = isLive && !!data && lastTurnIncomplete(data.messages);\r\n\r\n const projectsQuery = useQuery({\r\n queryKey: queryKeys.projects(),\r\n queryFn: () => api<ProjectSummary[]>('/api/projects'),\r\n });\r\n const project = useMemo(\r\n () => projectsQuery.data?.find((p) => p.id === pid),\r\n [projectsQuery.data, pid],\r\n );\r\n\r\n const projectSessionsQuery = useQuery({\r\n queryKey: queryKeys.projectSessions(pid),\r\n queryFn: () => api<SessionSummary[]>(`/api/projects/${encodeURIComponent(pid)}/sessions`),\r\n enabled: !!pid,\r\n });\r\n const currentSummary = useMemo(\r\n () => projectSessionsQuery.data?.find((s) => s.id === sid) ?? null,\r\n [projectSessionsQuery.data, sid],\r\n );\r\n const deleteTooltip = !currentSummary\r\n ? projectSessionsQuery.isLoading\r\n ? t('common.loading')\r\n : t('session.action.deleteTooltipBlocked')\r\n : undefined;\r\n\r\n // Modified-files summary (aggregated from a full jsonl scan, server-side). Fetched\r\n // eagerly at route level so the masthead trigger can show the count; the drawer\r\n // reuses this same data instead of querying again.\r\n const modifiedFilesQuery = useQuery({\r\n queryKey: queryKeys.sessionModifiedFiles(pid, sid),\r\n queryFn: () =>\r\n api<ModifiedFilesResponse>(\r\n `/api/sessions/${encodeURIComponent(pid)}/${encodeURIComponent(sid)}/modified-files`,\r\n ),\r\n enabled: !!pid && !!sid,\r\n });\r\n const modifiedFiles = modifiedFilesQuery.data?.files ?? [];\r\n\r\n // 在系统默认程序里打开会话改过的某个文件(后端校验该路径属于本会话)。\r\n const openFileMutation = useMutation({\r\n mutationFn: (filePath: string) =>\r\n api<OpenFileResult>(\r\n `/api/sessions/${encodeURIComponent(pid)}/${encodeURIComponent(sid)}/open-file`,\r\n { method: 'POST', body: JSON.stringify({ filePath }) },\r\n ),\r\n onError: (err: Error) => {\r\n window.alert(t('session.modified.openFailed', { msg: err.message }));\r\n },\r\n });\r\n\r\n const indexed: IndexedMessage[] = useMemo(() => {\r\n if (!data) return [];\r\n return data.messages.map((message) => ({\r\n message,\r\n haystack: indexMessage(message),\r\n }));\r\n }, [data]);\r\n\r\n // tool_use id → { name, input } for the loaded messages, so the drawer can render\r\n // each edit's actual content (Write body / Edit diff) without a second request.\r\n const editLookup: EditLookup = useMemo(() => {\r\n const map: EditLookup = new Map();\r\n if (!data) return map;\r\n for (const m of data.messages) {\r\n for (const b of m.blocks) {\r\n if (b.type === 'tool_use' && b.id) map.set(b.id, { name: b.name, input: b.input });\r\n }\r\n }\r\n return map;\r\n }, [data]);\r\n\r\n // 抽屉左栏的对话:默认隐去 meta/system 噪声行,留下真正的对话与工具调用。\r\n // 跳转目标都是 assistant 的 tool_use 消息,不在 meta 之列,过滤后仍可定位。\r\n const conversationMessages = useMemo(\r\n () => data?.messages.filter((m) => !m.isMeta) ?? [],\r\n [data],\r\n );\r\n\r\n const visibleMessages = useMemo(() => {\r\n let list = indexed;\r\n if (!showMeta) list = list.filter((m) => !m.message.isMeta);\r\n if (onlyUser) list = list.filter((m) => isUserTyped(m.message));\r\n if (deferredQuery) {\r\n const q = deferredQuery.toLowerCase();\r\n list = list.filter((m) => m.haystack.includes(q));\r\n }\r\n return list;\r\n }, [indexed, showMeta, onlyUser, deferredQuery]);\r\n\r\n const skipWindowing = !!deferredQuery || onlyUser;\r\n const renderList = useMemo(() => {\r\n if (skipWindowing) return visibleMessages;\r\n return visibleMessages.slice(-windowSize);\r\n }, [visibleMessages, skipWindowing, windowSize]);\r\n\r\n const hasMoreEarlier = !skipWindowing && renderList.length < visibleMessages.length;\r\n\r\n useEffect(() => {\r\n if (!data) return;\r\n const key = `${sid}|${urlFocus ?? ''}|${urlQuery ?? ''}`;\r\n if (urlAppliedRef.current === key) return;\r\n urlAppliedRef.current = key;\r\n if (urlQuery) setQuery(urlQuery);\r\n if (urlFocus) {\r\n const target = data.messages.find((m) => m.uuid === urlFocus);\r\n if (target?.isMeta) setShowMeta(true);\r\n }\r\n }, [data, sid, urlFocus, urlQuery]);\r\n\r\n useEffect(() => {\r\n if (!urlFocus || !data || skipWindowing) return;\r\n const idx = visibleMessages.findIndex((m) => m.message.uuid === urlFocus);\r\n if (idx === -1) return;\r\n const needed = visibleMessages.length - idx;\r\n if (needed > windowSize) setWindowSize(needed);\r\n }, [urlFocus, visibleMessages, windowSize, skipWindowing, data]);\r\n\r\n useEffect(() => {\r\n if (!urlFocus || !data) return;\r\n const key = `${sid}|${urlFocus}`;\r\n if (flashedKeyRef.current === key) return;\r\n if (!renderList.some((m) => m.message.uuid === urlFocus)) return;\r\n flashedKeyRef.current = key;\r\n const rafId = requestAnimationFrame(() => {\r\n const el = document.querySelector<HTMLElement>(\r\n `[data-uuid=\"${CSS.escape(urlFocus)}\"]`,\r\n );\r\n if (!el) return;\r\n el.scrollIntoView({ block: 'center', behavior: 'smooth' });\r\n const flashTarget = el.closest('li') ?? el;\r\n flashTarget.classList.add('flash-focus');\r\n window.setTimeout(() => flashTarget.classList.remove('flash-focus'), 1300);\r\n });\r\n return () => cancelAnimationFrame(rafId);\r\n }, [urlFocus, renderList, data, sid]);\r\n\r\n // Track whether the reader is parked at the bottom of the page, so live appends\r\n // can follow the tail without hijacking an upward scroll through history.\r\n useEffect(() => {\r\n const onScroll = () => {\r\n const distance =\r\n document.documentElement.scrollHeight - (window.scrollY + window.innerHeight);\r\n stickToBottomRef.current = distance < BOTTOM_STICK_THRESHOLD_PX;\r\n };\r\n onScroll();\r\n window.addEventListener('scroll', onScroll, { passive: true });\r\n return () => window.removeEventListener('scroll', onScroll);\r\n }, []);\r\n\r\n // When a live poll appends new messages and the reader is at the bottom — and not\r\n // mid-search or deep-linked to a specific message — follow the tail downward.\r\n useEffect(() => {\r\n const count = data?.meta.messageCount ?? null;\r\n const prev = prevMsgCountRef.current;\r\n prevMsgCountRef.current = count;\r\n if (prev === null || count === null || count <= prev) return;\r\n if (urlFocus || skipWindowing || !stickToBottomRef.current) return;\r\n const rafId = requestAnimationFrame(() =>\r\n window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' }),\r\n );\r\n return () => cancelAnimationFrame(rafId);\r\n }, [data?.meta.messageCount, urlFocus, skipWindowing]);\r\n\r\n // The working indicator appears below the last message without bumping\r\n // messageCount — follow the tail the moment it shows up so it stays in view.\r\n useEffect(() => {\r\n const startedWorking = isWorking && !prevIsWorkingRef.current;\r\n prevIsWorkingRef.current = isWorking;\r\n if (!startedWorking) return;\r\n if (urlFocus || skipWindowing || !stickToBottomRef.current) return;\r\n const rafId = requestAnimationFrame(() =>\r\n window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' }),\r\n );\r\n return () => cancelAnimationFrame(rafId);\r\n }, [isWorking, urlFocus, skipWindowing]);\r\n\r\n const projectTail = useMemo(() => {\r\n const cwd = project?.decodedCwd;\r\n if (!cwd) return pid.slice(-12);\r\n const parts = cwd.split(/[\\\\/]+/).filter(Boolean);\r\n return parts.at(-1) ?? cwd;\r\n }, [project, pid]);\r\n\r\n const sessionTitle = useMemo(() => {\r\n if (!data) return null;\r\n return data.meta.customTitle ?? data.meta.title;\r\n }, [data]);\r\n\r\n const queryClient = useQueryClient();\r\n const renameMutation = useMutation({\r\n mutationFn: (next: string) =>\r\n api<{ customTitle: string }>(\r\n `/api/sessions/${encodeURIComponent(pid)}/${encodeURIComponent(sid)}`,\r\n { method: 'PATCH', body: JSON.stringify({ customTitle: next }) },\r\n ),\r\n onSuccess: ({ customTitle }) => {\r\n // Patch caches synchronously so the read-only title doesn't flash the\r\n // pre-rename value while the background refetch is in flight.\r\n queryClient.setQueryData<SessionDetail>(queryKeys.session(pid, sid), (prev) =>\r\n prev ? { ...prev, meta: { ...prev.meta, customTitle } } : prev,\r\n );\r\n queryClient.setQueryData<SessionSummary[]>(queryKeys.projectSessions(pid), (prev) =>\r\n prev?.map((s) => (s.id === sid ? { ...s, customTitle } : s)),\r\n );\r\n void queryClient.invalidateQueries({ queryKey: queryKeys.session(pid, sid) });\r\n void queryClient.invalidateQueries({ queryKey: queryKeys.projectSessions(pid) });\r\n },\r\n });\r\n\r\n const taglineBranchPart = data?.meta.gitBranch\r\n ? t('session.tagline.branch', { branch: data.meta.gitBranch })\r\n : '';\r\n\r\n return (\r\n <section>\r\n <Breadcrumbs\r\n items={[\r\n { label: t('session.crumbProjects'), to: '/' },\r\n {\r\n label: projectTail,\r\n to: `/projects/${encodeURIComponent(pid)}`,\r\n mono: true,\r\n icon: <BreadcrumbFolderIcon />,\r\n },\r\n {\r\n label: sessionTitle ?? sid.slice(0, 8),\r\n mono: !sessionTitle,\r\n icon: <BreadcrumbFolderIcon />,\r\n },\r\n ]}\r\n />\r\n\r\n {data && (\r\n <div className=\"surface-card mt-4 p-6\">\r\n <SessionMasthead\r\n sid={sid}\r\n isLive={isLive}\r\n isWorking={isWorking}\r\n title={sessionTitle}\r\n tagline={t('session.tagline', {\r\n started: formatRelativeTime(data.meta.firstAt),\r\n lastTouched: formatRelativeTime(data.meta.lastAt),\r\n branchPart: taglineBranchPart,\r\n })}\r\n firstAt={data.meta.firstAt}\r\n messageCount={data.meta.messageCount}\r\n bytes={data.meta.bytes}\r\n version={data.meta.version}\r\n branch={data.meta.gitBranch}\r\n editableValue={sessionTitle ?? ''}\r\n onTitleEdit={async (next) => {\r\n await renameMutation.mutateAsync(next);\r\n }}\r\n renameDisabled={currentSummary?.isLivePid === true}\r\n renameTooltip={\r\n currentSummary?.isLivePid === true\r\n ? t('session.action.renameTooltipLive', {\r\n pid: currentSummary.livePid ?? '?',\r\n })\r\n : undefined\r\n }\r\n onDelete={currentSummary ? () => setShowDeleteDialog(true) : undefined}\r\n deleteDisabled={!currentSummary}\r\n deleteTooltip={deleteTooltip}\r\n deleteLabel={t('session.action.delete')}\r\n onOpenModified={() => setShowModifiedDrawer(true)}\r\n modifiedCount={modifiedFiles.length}\r\n modifiedLoading={modifiedFilesQuery.isLoading}\r\n />\r\n </div>\r\n )}\r\n\r\n {showDeleteDialog && currentSummary && (\r\n <DeleteDialog\r\n projectId={pid}\r\n selected={[currentSummary]}\r\n onClose={() => setShowDeleteDialog(false)}\r\n onDeleted={(deletedIds) => {\r\n if (deletedIds.includes(sid)) {\r\n setShowDeleteDialog(false);\r\n navigate(`/projects/${encodeURIComponent(pid)}`, { replace: true });\r\n }\r\n }}\r\n />\r\n )}\r\n\r\n <AnimatePresence>\r\n {showModifiedDrawer && (\r\n <ModifiedFilesDrawer\r\n key=\"modified-files-drawer\"\r\n files={modifiedFiles}\r\n cwd={modifiedFilesQuery.data?.cwd ?? data?.meta.cwd ?? null}\r\n editLookup={editLookup}\r\n messages={conversationMessages}\r\n query={deferredQuery}\r\n isWorking={isWorking}\r\n loading={modifiedFilesQuery.isLoading}\r\n error={modifiedFilesQuery.error as Error | null}\r\n onOpenFile={(filePath) => openFileMutation.mutate(filePath)}\r\n onClose={() => setShowModifiedDrawer(false)}\r\n />\r\n )}\r\n </AnimatePresence>\r\n\r\n <FilterLedger\r\n query={query}\r\n onQuery={setQuery}\r\n showMeta={showMeta}\r\n onShowMeta={setShowMeta}\r\n onlyUser={onlyUser}\r\n onOnlyUser={setOnlyUser}\r\n shown={renderList.length}\r\n total={visibleMessages.length}\r\n hasData={!!data}\r\n />\r\n\r\n <div className=\"mt-6\">\r\n {data?.truncated && (\r\n <Admonition tone=\"warn\" className=\"mb-6\">\r\n {t('session.truncated', { n: MAX_SESSION_MESSAGES.toLocaleString() })}\r\n </Admonition>\r\n )}\r\n\r\n {isLoading && <Loading label={t('common.loadingSession')} />}\r\n {error && (\r\n <Admonition tone=\"danger\">\r\n {t('common.failedSession')}: {(error as Error).message}\r\n </Admonition>\r\n )}\r\n\r\n {data && visibleMessages.length === 0 && (\r\n <p className=\"mt-2 max-w-2xl font-display text-[15px] italic text-[var(--color-fg-muted)]\">\r\n {t('common.noMessagesMatch')}\r\n </p>\r\n )}\r\n\r\n {data && visibleMessages.length > 0 && (\r\n <ol className=\"border-t border-[var(--color-hairline-strong)]\">\r\n {hasMoreEarlier && (\r\n <li className=\"flex justify-center border-b border-[var(--color-hairline)] py-3\">\r\n <button\r\n type=\"button\"\r\n onClick={() =>\r\n setWindowSize((w) => Math.min(w + LOAD_STEP, visibleMessages.length))\r\n }\r\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)]\"\r\n >\r\n {t('common.loadEarlier', {\r\n n: Math.min(LOAD_STEP, visibleMessages.length - renderList.length),\r\n })}\r\n </button>\r\n </li>\r\n )}\r\n\r\n <motion.div\r\n key={renderList.length === 0 ? 'empty' : 'list'}\r\n initial=\"hidden\"\r\n animate=\"show\"\r\n variants={staggerParent}\r\n >\r\n {renderList.map((m, i) => {\r\n const isMeta = m.message.isMeta;\r\n return (\r\n <motion.li\r\n key={m.message.uuid || m.message.ts || String(i)}\r\n variants={fadeUpItem}\r\n className={isMeta ? 'py-2' : 'py-3'}\r\n >\r\n <MessageBubble message={m.message} query={deferredQuery} />\r\n </motion.li>\r\n );\r\n })}\r\n </motion.div>\r\n\r\n {isWorking && !skipWindowing && <WorkingIndicator />}\r\n </ol>\r\n )}\r\n </div>\r\n\r\n {data && <ScrollToEdges />}\r\n </section>\r\n );\r\n}\r\n\r\n/* ─────────────────────────────────────────────────────────────────── */\r\n\r\nfunction SessionMasthead({\r\n sid,\r\n isLive,\r\n isWorking,\r\n title,\r\n tagline,\r\n firstAt,\r\n messageCount,\r\n bytes,\r\n version,\r\n branch,\r\n editableValue,\r\n onTitleEdit,\r\n renameDisabled,\r\n renameTooltip,\r\n onDelete,\r\n deleteDisabled,\r\n deleteTooltip,\r\n deleteLabel,\r\n onOpenModified,\r\n modifiedCount,\r\n modifiedLoading,\r\n}: {\r\n sid: string;\r\n isLive: boolean;\r\n isWorking: boolean;\r\n title: string | null;\r\n tagline: string;\r\n firstAt: string | null;\r\n messageCount: number;\r\n bytes: number;\r\n version: string | null;\r\n branch: string | null;\r\n editableValue: string;\r\n onTitleEdit: (next: string) => Promise<void>;\r\n renameDisabled?: boolean;\r\n renameTooltip?: string;\r\n onDelete?: () => void;\r\n deleteDisabled?: boolean;\r\n deleteTooltip?: string;\r\n deleteLabel?: string;\r\n onOpenModified: () => void;\r\n modifiedCount: number;\r\n modifiedLoading: boolean;\r\n}) {\r\n const t = useT();\r\n const dateline = formatDateline(firstAt);\r\n\r\n return (\r\n <header className=\"relative\">\r\n <div className=\"flex items-center justify-between gap-4 border-b border-[var(--color-hairline)] pb-3\">\r\n <div className=\"flex min-w-0 items-center gap-3 font-mono text-[10px] uppercase tracking-[0.22em] text-[var(--color-fg-muted)]\">\r\n <span className=\"text-[var(--color-accent)]\">●</span>\r\n <span>§ SESSION</span>\r\n {isWorking ? (\r\n <span\r\n title={t('session.working.tooltip')}\r\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)]\"\r\n >\r\n <span aria-hidden className=\"loading-dots text-[var(--color-accent)]\">\r\n <span />\r\n <span />\r\n <span />\r\n </span>\r\n {t('session.working')}\r\n </span>\r\n ) : isLive ? (\r\n <span\r\n title={t('session.live.tooltip')}\r\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)]\"\r\n >\r\n <span aria-hidden className=\"relative inline-flex h-1.5 w-1.5\">\r\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)] pulse-amber\" />\r\n <span className=\"absolute inset-0 rounded-full bg-[var(--color-accent)]\" />\r\n </span>\r\n {t('session.live')}\r\n </span>\r\n ) : null}\r\n <span className=\"hidden h-3 w-px bg-[var(--color-hairline-strong)] sm:inline-block\" />\r\n <span className=\"hidden truncate normal-case tracking-[0.05em] text-[var(--color-fg-faint)] sm:inline\">\r\n {sid}\r\n </span>\r\n {branch && (\r\n <>\r\n <span className=\"hidden h-3 w-px bg-[var(--color-hairline-strong)] md:inline-block\" />\r\n <span className=\"hidden truncate md:inline\">{branch}</span>\r\n </>\r\n )}\r\n </div>\r\n <div className=\"flex shrink-0 items-center gap-3\">\r\n <div className=\"hidden font-mono text-[10px] uppercase tracking-[0.22em] tabular-nums text-[var(--color-fg-muted)] sm:block\">\r\n {dateline}\r\n </div>\r\n <button\r\n type=\"button\"\r\n onClick={onOpenModified}\r\n disabled={modifiedLoading}\r\n aria-label={t('session.modified.openAria')}\r\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)]\"\r\n >\r\n <FilesIcon /> {t('session.modified.title')}\r\n {modifiedCount > 0 && (\r\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)]\">\r\n {modifiedCount}\r\n </span>\r\n )}\r\n </button>\r\n {(onDelete || deleteDisabled) && (\r\n <button\r\n type=\"button\"\r\n onClick={onDelete}\r\n disabled={deleteDisabled || !onDelete}\r\n title={deleteTooltip}\r\n aria-label={deleteLabel}\r\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\"\r\n >\r\n <TrashIcon /> {deleteLabel}\r\n </button>\r\n )}\r\n </div>\r\n </div>\r\n\r\n <div className=\"grid grid-cols-1 gap-x-10 gap-y-6 pt-5 pb-2 lg:grid-cols-12\">\r\n <div className=\"lg:col-span-8\">\r\n <TitleSlot\r\n title={title ?? sid.slice(0, 12) + '…'}\r\n editableValue={editableValue}\r\n onTitleEdit={onTitleEdit}\r\n isFallback={!title}\r\n disabled={renameDisabled}\r\n disabledTooltip={renameTooltip}\r\n />\r\n </div>\r\n\r\n <div className=\"lg:col-span-4 lg:pt-3\">\r\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)]\">\r\n {tagline}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <div className=\"rule-dotted mt-6\" aria-hidden />\r\n <dl className=\"mt-3 flex flex-wrap items-baseline gap-x-8 gap-y-2\">\r\n <Fact label={t('session.meta.messages')} value={messageCount.toLocaleString()} />\r\n <Fact label={t('session.meta.size')} value={formatBytes(bytes)} />\r\n {version && <Fact label={t('session.meta.version')} value={version} />}\r\n <Fact label={t('session.meta.started')} value={formatDateTime(firstAt)} />\r\n </dl>\r\n </header>\r\n );\r\n}\r\n\r\nconst MASTHEAD_TITLE_CLASS =\r\n 'font-display text-[clamp(1.5rem,3vw,2rem)] font-light leading-[1.15] tracking-[-0.018em] text-[var(--color-fg-primary)]';\r\n\r\nfunction TitleSlot({\r\n title,\r\n editableValue,\r\n onTitleEdit,\r\n isFallback,\r\n disabled,\r\n disabledTooltip,\r\n}: {\r\n title: ReactNode;\r\n editableValue: string;\r\n onTitleEdit: (next: string) => Promise<void>;\r\n isFallback: boolean;\r\n disabled?: boolean;\r\n disabledTooltip?: string;\r\n}) {\r\n const [editing, setEditing] = useState(false);\r\n const [draft, setDraft] = useState(editableValue);\r\n const [submitting, setSubmitting] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const inputRef = useRef<HTMLInputElement | null>(null);\r\n\r\n useEffect(() => {\r\n if (!editing) setDraft(editableValue);\r\n }, [editing, editableValue]);\r\n\r\n useEffect(() => {\r\n if (editing) inputRef.current?.select();\r\n }, [editing]);\r\n\r\n function startEdit() {\r\n setDraft(editableValue);\r\n setError(null);\r\n setEditing(true);\r\n }\r\n\r\n async function commit() {\r\n const next = draft.trim();\r\n if (!next || next === editableValue) {\r\n setEditing(false);\r\n return;\r\n }\r\n setSubmitting(true);\r\n setError(null);\r\n try {\r\n await onTitleEdit(next);\r\n setEditing(false);\r\n } catch (err) {\r\n setError((err as Error).message);\r\n } finally {\r\n setSubmitting(false);\r\n }\r\n }\r\n\r\n if (editing) {\r\n return (\r\n <div>\r\n <input\r\n ref={inputRef}\r\n value={draft}\r\n disabled={submitting}\r\n onChange={(e) => {\r\n setDraft(e.target.value);\r\n if (error) setError(null);\r\n }}\r\n onKeyDown={(e) => {\r\n if (e.key === 'Enter') {\r\n e.preventDefault();\r\n void commit();\r\n } else if (e.key === 'Escape') {\r\n e.preventDefault();\r\n setEditing(false);\r\n setError(null);\r\n }\r\n }}\r\n onBlur={() => {\r\n if (!submitting && !error) {\r\n setEditing(false);\r\n }\r\n }}\r\n maxLength={200}\r\n className={\r\n MASTHEAD_TITLE_CLASS +\r\n ' w-full bg-transparent border-b border-[var(--color-accent)] outline-none focus:outline-none disabled:opacity-60'\r\n }\r\n />\r\n {error && <p className=\"mt-1 text-xs text-[var(--color-danger)]\">{error}</p>}\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"group flex items-baseline gap-3\">\r\n <h1 className={MASTHEAD_TITLE_CLASS + (isFallback ? ' font-mono' : '')}>\r\n {title}\r\n <span className=\"text-[var(--color-accent)]\">.</span>\r\n </h1>\r\n <button\r\n type=\"button\"\r\n onClick={startEdit}\r\n aria-label=\"Rename\"\r\n title={disabled ? disabledTooltip ?? 'Rename unavailable' : 'Rename'}\r\n disabled={disabled}\r\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\"\r\n >\r\n <PencilIcon />\r\n </button>\r\n </div>\r\n );\r\n}\r\n\r\nfunction Fact({ label, value }: { label: string; value: ReactNode }) {\r\n return (\r\n <div className=\"flex items-baseline gap-2\">\r\n <dt className=\"eyebrow\">{label}</dt>\r\n <dd className=\"font-mono text-[12px] tabular-nums text-[var(--color-fg-primary)]\">\r\n {value}\r\n </dd>\r\n </div>\r\n );\r\n}\r\n\r\nfunction formatDateline(iso: string | null): string {\r\n if (!iso) return '—';\r\n const d = new Date(iso);\r\n if (Number.isNaN(d.getTime())) return '—';\r\n return d\r\n .toLocaleDateString('en-GB', {\r\n weekday: 'short',\r\n day: '2-digit',\r\n month: 'short',\r\n year: 'numeric',\r\n })\r\n .toUpperCase()\r\n .replace(/,/g, ' ·');\r\n}\r\n\r\n/* ─────────────────────────────────────────────────────────────────── */\r\n\r\nfunction FilterLedger({\r\n query,\r\n onQuery,\r\n showMeta,\r\n onShowMeta,\r\n onlyUser,\r\n onOnlyUser,\r\n shown,\r\n total,\r\n hasData,\r\n}: {\r\n query: string;\r\n onQuery: (v: string) => void;\r\n showMeta: boolean;\r\n onShowMeta: (v: boolean) => void;\r\n onlyUser: boolean;\r\n onOnlyUser: (v: boolean) => void;\r\n shown: number;\r\n total: number;\r\n hasData: boolean;\r\n}) {\r\n const t = useT();\r\n return (\r\n <div className=\"sticky top-2 z-30 mt-6 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)]\">\r\n <div className=\"flex flex-wrap items-center gap-4\">\r\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)]\">\r\n <SearchIcon className=\"text-[var(--color-fg-muted)]\" />\r\n <input\r\n type=\"search\"\r\n value={query}\r\n onChange={(e) => onQuery(e.target.value)}\r\n placeholder={t('common.searchPlaceholder')}\r\n className=\"w-full bg-transparent text-sm text-[var(--color-fg-primary)] placeholder:text-[var(--color-fg-faint)] focus:outline-none\"\r\n />\r\n </div>\r\n\r\n <span className=\"hidden h-4 w-px bg-[var(--color-hairline-strong)] sm:inline-block\" />\r\n\r\n <div className=\"flex items-center gap-4\">\r\n <ToggleSwitch\r\n checked={showMeta}\r\n onChange={onShowMeta}\r\n label={t('common.system')}\r\n />\r\n <ToggleSwitch\r\n checked={onlyUser}\r\n onChange={onOnlyUser}\r\n label={t('common.onlyUser')}\r\n />\r\n </div>\r\n\r\n {hasData && (\r\n <>\r\n <span className=\"hidden h-4 w-px bg-[var(--color-hairline-strong)] sm:inline-block\" />\r\n <span className=\"font-mono text-[11px] tabular-nums text-[var(--color-fg-muted)]\">\r\n {t('session.shown', { shown, total })}\r\n </span>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n}\r\n\r\nfunction ToggleSwitch({\r\n checked,\r\n onChange,\r\n label,\r\n}: {\r\n checked: boolean;\r\n onChange: (next: boolean) => void;\r\n label: string;\r\n}) {\r\n return (\r\n <label className=\"inline-flex cursor-pointer items-center gap-1.5\">\r\n <input\r\n type=\"checkbox\"\r\n checked={checked}\r\n onChange={(e) => onChange(e.target.checked)}\r\n className=\"sr-only\"\r\n />\r\n <span\r\n aria-hidden\r\n className={\r\n 'font-mono text-[11px] uppercase tracking-[0.16em] transition ' +\r\n (checked\r\n ? 'text-[var(--color-accent)] underline underline-offset-[6px] decoration-[var(--color-accent)]/50'\r\n : 'text-[var(--color-fg-faint)] hover:text-[var(--color-fg-secondary)]')\r\n }\r\n >\r\n {label}\r\n </span>\r\n </label>\r\n );\r\n}\r\n\r\n/* ─────────────────────────────────────────────────────────────────── */\r\n\r\nfunction Admonition({\r\n tone,\r\n className = '',\r\n children,\r\n}: {\r\n tone: 'warn' | 'danger';\r\n className?: string;\r\n children: ReactNode;\r\n}) {\r\n const colors =\r\n tone === 'warn'\r\n ? 'border-[var(--color-accent)]/40 bg-[var(--color-accent-soft)] text-[var(--color-accent-ink)] dark:text-[var(--color-fg-primary)]'\r\n : 'border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] text-[var(--color-danger)]';\r\n return (\r\n <div className={`rounded-[10px] border px-4 py-3 text-sm ${colors} ${className}`}>\r\n {children}\r\n </div>\r\n );\r\n}\r\n\r\n/* ─────────────────────────────────────────────────────────────────── */\r\n\r\nfunction isUserTyped(m: Message): boolean {\r\n if (m.type !== 'user') return false;\r\n if (m.blocks.length === 0) return true;\r\n return m.blocks.some((b) => b.type !== 'tool_result');\r\n}\r\n\r\nfunction indexMessage(message: Message): string {\r\n return message.blocks.map(blockText).join('\\n').toLowerCase();\r\n}\r\n\r\nfunction blockText(block: Block): string {\r\n switch (block.type) {\r\n case 'text':\r\n case 'thinking':\r\n return block.text;\r\n case 'tool_use':\r\n return `${block.name} ${JSON.stringify(block.input)}`;\r\n case 'tool_result':\r\n return block.content;\r\n case 'image':\r\n return '';\r\n default:\r\n return JSON.stringify(block.raw);\r\n }\r\n}\r\n\r\nfunction SearchIcon({ className = '' }: { className?: string }) {\r\n return (\r\n <svg\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.7\"\r\n strokeLinecap=\"round\"\r\n className={className}\r\n aria-hidden\r\n >\r\n <circle cx=\"11\" cy=\"11\" r=\"6.2\" />\r\n <path d=\"M20 20l-4.3-4.3\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction TrashIcon() {\r\n return (\r\n <svg\r\n width=\"12\"\r\n height=\"12\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.7\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden\r\n >\r\n <path d=\"M3 6h18\" />\r\n <path d=\"M8 6V4.5A1.5 1.5 0 0 1 9.5 3h5A1.5 1.5 0 0 1 16 4.5V6\" />\r\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\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction FilesIcon() {\r\n return (\r\n <svg\r\n width=\"12\"\r\n height=\"12\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.7\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden\r\n >\r\n <path d=\"M3 5h6l2 2h10\" />\r\n <path d=\"M3 5v14h18V9H3\" />\r\n </svg>\r\n );\r\n}\r\n\r\nfunction PencilIcon() {\r\n return (\r\n <svg\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.7\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden\r\n >\r\n <path d=\"M12 20h9\" />\r\n <path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z\" />\r\n </svg>\r\n );\r\n}\r\n\r\nconst EDGE_THRESHOLD = 320;\r\n\r\nfunction ScrollToEdges() {\r\n const t = useT();\r\n const [showTop, setShowTop] = useState(false);\r\n const [showBottom, setShowBottom] = useState(false);\r\n\r\n useEffect(() => {\r\n let frame = 0;\r\n const update = () => {\r\n frame = 0;\r\n const scrollY = window.scrollY;\r\n const viewport = window.innerHeight;\r\n const total = document.documentElement.scrollHeight;\r\n setShowTop(scrollY >= EDGE_THRESHOLD);\r\n setShowBottom(total - (scrollY + viewport) >= EDGE_THRESHOLD);\r\n };\r\n const schedule = () => {\r\n if (frame) return;\r\n frame = requestAnimationFrame(update);\r\n };\r\n update();\r\n window.addEventListener('scroll', schedule, { passive: true });\r\n window.addEventListener('resize', schedule, { passive: true });\r\n return () => {\r\n window.removeEventListener('scroll', schedule);\r\n window.removeEventListener('resize', schedule);\r\n if (frame) cancelAnimationFrame(frame);\r\n };\r\n }, []);\r\n\r\n if (!showTop && !showBottom) return null;\r\n\r\n const buttonClass =\r\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)]';\r\n\r\n return (\r\n <div className=\"fixed bottom-6 right-6 z-30 flex flex-col gap-2\">\r\n {showTop && (\r\n <button\r\n type=\"button\"\r\n aria-label={t('common.scrollToTop')}\r\n title={t('common.scrollToTop')}\r\n onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}\r\n className={buttonClass}\r\n >\r\n <ChevronIcon direction=\"up\" />\r\n </button>\r\n )}\r\n {showBottom && (\r\n <button\r\n type=\"button\"\r\n aria-label={t('common.scrollToBottom')}\r\n title={t('common.scrollToBottom')}\r\n onClick={() =>\r\n window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' })\r\n }\r\n className={buttonClass}\r\n >\r\n <ChevronIcon direction=\"down\" />\r\n </button>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nfunction ChevronIcon({ direction }: { direction: 'up' | 'down' }) {\r\n return (\r\n <svg\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.7\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden\r\n >\r\n <path d={direction === 'up' ? 'M6 15l6-6 6 6' : 'M6 9l6 6 6-6'} />\r\n </svg>\r\n );\r\n}\r\n","import { lazy, Suspense, useCallback, useState } from 'react';\r\nimport { Route, Routes } from 'react-router-dom';\r\nimport { Loading } from './components/Loading.tsx';\r\nimport SearchModal from './components/SearchModal.tsx';\r\nimport Sidebar from './components/Sidebar.tsx';\r\nimport { useGlobalHotkey } from './lib/hotkeys.ts';\r\nimport { useT } from './lib/i18n.ts';\r\nimport ProjectDetail from './routes/ProjectDetail.tsx';\r\nimport ProjectsList from './routes/ProjectsList.tsx';\r\nimport SessionDetail from './routes/SessionDetail.tsx';\r\n\r\nconst DiskUsage = lazy(() => import('./routes/DiskUsage.tsx'));\r\nconst ProjectMemory = lazy(() => import('./routes/ProjectMemory.tsx'));\r\nconst ImportPage = lazy(() => import('./routes/ImportPage.tsx'));\r\n\r\nexport default function App() {\r\n const [searchOpen, setSearchOpen] = useState(false);\r\n const toggleSearch = useCallback(() => setSearchOpen((v) => !v), []);\r\n const openSearch = useCallback(() => setSearchOpen(true), []);\r\n const closeSearch = useCallback(() => setSearchOpen(false), []);\r\n useGlobalHotkey('mod+k', toggleSearch);\r\n\r\n return (\r\n <div className=\"flex min-h-dvh\">\r\n <Sidebar onSearchOpen={openSearch} />\r\n <main className=\"flex-1 min-w-0\">\r\n <div className=\"mx-auto w-full max-w-6xl px-5 py-8 sm:px-8 lg:px-12\">\r\n <Routes>\r\n <Route path=\"/\" element={<ProjectsList />} />\r\n <Route path=\"/projects/:projectId\" element={<ProjectDetail />} />\r\n <Route\r\n path=\"/projects/:projectId/memory\"\r\n element={\r\n <Suspense fallback={<RouteFallback />}>\r\n <ProjectMemory />\r\n </Suspense>\r\n }\r\n />\r\n <Route\r\n path=\"/projects/:projectId/sessions/:sessionId\"\r\n element={<SessionDetail />}\r\n />\r\n <Route\r\n path=\"/disk\"\r\n element={\r\n <Suspense fallback={<RouteFallback />}>\r\n <DiskUsage />\r\n </Suspense>\r\n }\r\n />\r\n <Route\r\n path=\"/import\"\r\n element={\r\n <Suspense fallback={<RouteFallback />}>\r\n <ImportPage />\r\n </Suspense>\r\n }\r\n />\r\n </Routes>\r\n </div>\r\n </main>\r\n <SearchModal open={searchOpen} onClose={closeSearch} />\r\n </div>\r\n );\r\n}\r\n\r\nfunction RouteFallback() {\r\n const t = useT();\r\n return (\r\n <div className=\"flex h-40 items-center justify-center\">\r\n <Loading label={t('common.loading')} className=\"items-center\" />\r\n </div>\r\n );\r\n}\r\n","import { QueryClient, QueryClientProvider } from '@tanstack/react-query';\r\nimport React from 'react';\r\nimport ReactDOM from 'react-dom/client';\r\nimport { BrowserRouter } from 'react-router-dom';\r\nimport App from './App.tsx';\r\nimport './index.css';\r\n\r\nconst queryClient = new QueryClient({\r\n defaultOptions: {\r\n queries: { staleTime: 30_000, refetchOnWindowFocus: false },\r\n },\r\n});\r\n\r\nconst rootEl = document.getElementById('root');\r\nif (!rootEl) throw new Error('#root not found');\r\n\r\nReactDOM.createRoot(rootEl).render(\r\n <React.StrictMode>\r\n <QueryClientProvider client={queryClient}>\r\n <BrowserRouter>\r\n <App />\r\n </BrowserRouter>\r\n </QueryClientProvider>\r\n </React.StrictMode>,\r\n);\r\n"],"file":"assets/index-7aMrnHJG.js"}
|