@zzusp/ccsm 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +236 -232
- package/dist/assets/DiskUsage-BY6XwffG.js +2 -0
- package/dist/assets/DiskUsage-BY6XwffG.js.map +1 -0
- package/dist/assets/{ImportPage-b8NORa8b.js → ImportPage-Cwq5bx7G.js} +2 -2
- package/dist/assets/ImportPage-Cwq5bx7G.js.map +1 -0
- package/dist/assets/MarkdownContent-BFu7Nkk_.js +2 -0
- package/dist/assets/MarkdownContent-BFu7Nkk_.js.map +1 -0
- package/dist/assets/{ProjectMemory-aSV8UzQ9.js → ProjectMemory-CcE3KbUK.js} +2 -2
- package/dist/assets/ProjectMemory-CcE3KbUK.js.map +1 -0
- 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-CrWxV6sb.css +1 -0
- package/dist/assets/index-DTbWl1jb.js +11 -0
- package/dist/assets/index-DTbWl1jb.js.map +1 -0
- package/dist/assets/markdown-Bag5rX3T.js +30 -0
- package/dist/assets/markdown-Bag5rX3T.js.map +1 -0
- package/dist/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 +30 -30
- package/package.json +24 -11
- package/server/index.ts +4 -0
- package/server/lib/active-sessions.test.ts +119 -0
- package/server/lib/active-sessions.ts +95 -95
- package/server/lib/bundle.test.ts +182 -0
- package/server/lib/bundle.ts +86 -86
- package/server/lib/claude-paths.test.ts +126 -0
- package/server/lib/claude-paths.ts +43 -36
- package/server/lib/cleanup-suggestions.ts +131 -0
- package/server/lib/constants.ts +8 -7
- package/server/lib/delete-project.ts +100 -100
- package/server/lib/delete.test.ts +244 -0
- package/server/lib/delete.ts +192 -203
- package/server/lib/disk-usage.ts +81 -83
- package/server/lib/encode-cwd.ts +24 -24
- package/server/lib/export-bundle.ts +236 -236
- package/server/lib/export-import-bundle.test.ts +337 -0
- package/server/lib/fs-size.ts +38 -38
- package/server/lib/import-bundle.ts +488 -488
- package/server/lib/load-memory.ts +120 -120
- package/server/lib/load-session.ts +209 -209
- package/server/lib/modified-files.test.ts +280 -0
- package/server/lib/modified-files.ts +228 -0
- package/server/lib/open-folder.ts +47 -40
- package/server/lib/parse-jsonl.ts +160 -107
- package/server/lib/port.ts +23 -23
- package/server/lib/safe-id.test.ts +41 -0
- package/server/lib/safe-id.ts +6 -6
- package/server/lib/safe-remove.test.ts +73 -0
- package/server/lib/safe-remove.ts +25 -0
- package/server/lib/scan.ts +289 -183
- package/server/lib/search-all.ts +130 -130
- package/server/lib/search-session.ts +203 -203
- package/server/lib/system-tags.ts +20 -20
- package/server/lib/update.ts +67 -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/disk.ts +9 -9
- package/server/routes/import.ts +87 -87
- package/server/routes/projects.ts +104 -104
- package/server/routes/search.ts +79 -79
- package/server/routes/sessions.ts +130 -81
- package/server/routes/version.ts +34 -0
- package/server/types.ts +1 -1
- package/shared/constants.ts +7 -2
- package/shared/types.ts +513 -359
- package/dist/assets/DiskUsage-Bq4VaoUA.js +0 -2
- package/dist/assets/DiskUsage-Bq4VaoUA.js.map +0 -1
- package/dist/assets/ImportPage-b8NORa8b.js.map +0 -1
- package/dist/assets/ProjectMemory-aSV8UzQ9.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
|
@@ -1 +0,0 @@
|
|
|
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","active","kindLabel","kind","children","tone","cls","LocaleToggle","Pill","onClick","useTheme","theme","setThemeState","setTheme","mq","ThemeToggle","isDark","MoonIcon","SunIcon","NAV","FolderIcon","p","DiskIcon","ImportIcon","Sidebar","onSearchOpen","pathname","useLocation","setOpen","Fragment","Brand","MenuIcon","isActive","Link","Glyph","ITEM_BASE","Breadcrumbs","items","c","last","family","inner","ChevronSep","BreadcrumbFolderIcon","api","path","init","detail","parsed","RECENT_ACTIVITY_WINDOW_MIN","MAX_SESSION_MESSAGES","queryKeys","projectId","sessionId","DeleteDialog","selected","onDeleted","queryClient","useQueryClient","willSkip","s","willDelete","totalFree","acc","mutation","useMutation","data","removed","isPendingRef","result","showResult","motion","skipReason","a","ExportDialog","sessions","destDir","setDestDir","canSubmit","PageHeader","eyebrow","actions","meta","editableValue","onTitleEdit","TitleSlot","TITLE_CLASS","editing","setEditing","draft","setDraft","submitting","setSubmitting","startEdit","commit","PencilIcon","MetaItem","Sep","variantOf","StatusDot","session","withLabel","Dot","variant","staggerParent","fadeUpItem","ProjectDetail","useParams","setSelected","showDialog","setShowDialog","showExport","setShowExport","sessionsQuery","useQuery","projectsQuery","memoryCount","revealMutation","project","selectedSessions","sessionsToExport","projectBytes","totalBytes","liveCount","recentCount","sid","toggleAll","cwd","parts","tail","head","FolderOpenIcon","BrainIcon","ExportIcon","TrashIcon","isSel","displayTitle","breakdown","DeleteProjectDialog","blockers","hasBlockers","totalDeletedBytes","ProjectsList","pendingDelete","setPendingDelete","health","projects","list","totalSessions","lastActive","x","Masthead","Admonition","Ledger","tagline","stats","onRequestDelete","LedgerRow","index","ChevronRight","colors","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","role","INITIAL_WINDOW","LOAD_STEP","SessionDetailRoute","searchParams","useSearchParams","pid","urlFocus","urlQuery","showMeta","setShowMeta","onlyUser","setOnlyUser","deferredQuery","useDeferredValue","windowSize","setWindowSize","showDeleteDialog","setShowDeleteDialog","urlAppliedRef","flashedKeyRef","isLoading","projectSessionsQuery","currentSummary","deleteTooltip","indexed","indexMessage","visibleMessages","m","isUserTyped","skipWindowing","renderList","hasMoreEarlier","target","needed","rafId","flashTarget","sessionTitle","renameMutation","customTitle","taglineBranchPart","SessionMasthead","deletedIds","FilterLedger","w","isMeta","ScrollToEdges","firstAt","messageCount","version","branch","renameDisabled","renameTooltip","onDelete","deleteDisabled","deleteLabel","dateline","formatDateline","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/components/Sidebar.tsx","../../web/src/components/Breadcrumbs.tsx","../../web/src/lib/api.ts","../../shared/constants.ts","../../web/src/lib/query-keys.ts","../../web/src/components/DeleteDialog.tsx","../../web/src/components/ExportDialog.tsx","../../web/src/components/PageHeader.tsx","../../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/lib/highlight.ts","../../web/src/components/HighlightedText.tsx","../../web/src/components/ToolBlock.tsx","../../web/src/components/MessageBubble.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 '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.meta.sessions': 'Sessions',\r\n 'project.meta.onDisk': 'On disk',\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\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.live': 'live · pid {{pid}}',\r\n 'status.recent': 'recent',\r\n 'status.idle': 'idle',\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 '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 '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.meta.sessions': '会话',\r\n 'project.meta.onDisk': '占用',\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\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.live': '运行中 · pid {{pid}}',\r\n 'status.recent': '近期',\r\n 'status.idle': '空闲',\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 '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","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\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 <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';\r\nimport { Link } from 'react-router-dom';\r\n\r\nexport interface Crumb {\r\n label: string;\r\n to?: string;\r\n mono?: boolean;\r\n icon?: ReactNode;\r\n}\r\n\r\nconst ITEM_BASE = 'inline-flex min-w-0 items-center gap-1.5 rounded-lg px-3 py-1';\r\n\r\nexport default function Breadcrumbs({ items }: { items: Crumb[] }) {\r\n return (\r\n <nav\r\n aria-label=\"Breadcrumb\"\r\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)]\"\r\n >\r\n {items.map((c, i) => {\r\n const last = i === items.length - 1;\r\n const family = c.mono ? 'font-mono' : 'font-sans';\r\n const tone = last\r\n ? 'text-[var(--color-fg-primary)]'\r\n : 'text-[var(--color-fg-secondary)]';\r\n const inner = (\r\n <>\r\n {c.icon && <span className=\"shrink-0 text-[var(--color-accent)]\">{c.icon}</span>}\r\n <span className=\"truncate\">{c.label}</span>\r\n </>\r\n );\r\n return (\r\n <Fragment key={`${c.label}-${i}`}>\r\n {c.to && !last ? (\r\n <Link\r\n to={c.to}\r\n className={`${ITEM_BASE} ${family} ${tone} transition-colors duration-150 hover:bg-[var(--color-accent-soft)] hover:text-[var(--color-accent-ink)]`}\r\n >\r\n {inner}\r\n </Link>\r\n ) : (\r\n <span\r\n className={`${ITEM_BASE} ${family} ${tone}`}\r\n aria-current={last ? 'page' : undefined}\r\n >\r\n {inner}\r\n </span>\r\n )}\r\n {!last && <ChevronSep />}\r\n </Fragment>\r\n );\r\n })}\r\n </nav>\r\n );\r\n}\r\n\r\nfunction ChevronSep() {\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=\"2.4\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n className=\"shrink-0 text-[var(--color-accent)]\"\r\n aria-hidden\r\n >\r\n <polyline points=\"9 6 15 12 9 18\" />\r\n </svg>\r\n );\r\n}\r\n\r\nexport function BreadcrumbFolderIcon() {\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=\"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","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 RECENT_ACTIVITY_WINDOW_MIN = 5;\r\nexport const MAX_SESSION_MESSAGES = 5000;\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 diskUsage: () => ['disk-usage'] as const,\r\n search: (query: string) => ['search', query] as const,\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","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","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 = 'live' | 'recent' | 'idle';\r\n\r\nfunction variantOf(s: SessionSummary): Variant {\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 === '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 === '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 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\r\n className={\r\n v === 'idle'\r\n ? 'font-mono uppercase tracking-[0.14em] text-[var(--color-fg-faint)]'\r\n : v === 'live'\r\n ? 'font-mono uppercase tracking-[0.14em] text-[var(--color-accent-ink)] dark:text-[var(--color-accent)]'\r\n : 'font-mono uppercase tracking-[0.14em] text-[var(--color-fg-secondary)]'\r\n }\r\n >\r\n {label}\r\n </span>\r\n )}\r\n </span>\r\n );\r\n}\r\n\r\nfunction Dot({ variant }: { variant: Variant }) {\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 } 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 DeleteDialog from '../components/DeleteDialog.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 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\nexport default function ProjectDetail() {\r\n const t = useT();\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 [showDialog, setShowDialog] = useState(false);\r\n const [showExport, setShowExport] = 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 const liveCount = useMemo(() => sessions.filter((s) => s.isLivePid).length, [sessions]);\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 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={() => setShowDialog(true)}\r\n disabled={selected.size === 0}\r\n className=\"inline-flex items-center gap-2 rounded-[var(--radius-control)] border border-[var(--color-danger)]/40 bg-[var(--color-danger-soft)] px-4 py-1.5 text-xs 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 /> {t('project.action.delete', { n: selected.size })}\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 <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 <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 <button\r\n type=\"button\"\r\n onClick={toggleAll}\r\n className=\"font-mono text-[11px] uppercase tracking-[0.16em] text-[var(--color-fg-muted)] hover:text-[var(--color-fg-primary)]\"\r\n >\r\n {selected.size === sessions.length ? t('common.deselectAll') : t('common.selectAll')}\r\n </button>\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 <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 ? 'true' : undefined}\r\n className={\r\n 'ribbon-row border-b border-[var(--color-hairline)] transition-colors ' +\r\n (isSel\r\n ? 'bg-[var(--color-accent-soft)]/40'\r\n : 'hover:bg-[var(--color-sunken)]')\r\n }\r\n >\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 className=\"mt-0.5 h-3.5 w-3.5 cursor-pointer accent-[var(--color-accent)]\"\r\n />\r\n </td>\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 {showDialog && (\r\n <DeleteDialog\r\n projectId={id}\r\n selected={selectedSessions}\r\n onClose={() => {\r\n setShowDialog(false);\r\n setSelected(new Set());\r\n }}\r\n />\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 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","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\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 { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\r\nimport { 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 from '../components/MessageBubble.tsx';\r\nimport {\r\n api,\r\n type Block,\r\n type Message,\r\n type ProjectSummary,\r\n type SessionDetail,\r\n type SessionSummary,\r\n} from '../lib/api.ts';\r\nimport { MAX_SESSION_MESSAGES } 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\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 urlAppliedRef = useRef<string | null>(null);\r\n const flashedKeyRef = useRef<string | null>(null);\r\n\r\n useEffect(() => {\r\n setWindowSize(INITIAL_WINDOW);\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 });\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 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 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 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 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 />\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 <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 </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 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}: {\r\n sid: string;\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}) {\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 <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=\"font-mono text-[10px] uppercase tracking-[0.22em] tabular-nums text-[var(--color-fg-muted)]\">\r\n {dateline}\r\n </div>\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 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-DLATR3tZ.js"}
|