@chatcode/cco-better-sidebar 0.18.0-alpha.0
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 -0
- package/README.md +554 -0
- package/README_EN.md +563 -0
- package/assets/logo.svg +1 -0
- package/cordis.patch.yml +79 -0
- package/lib/client-editor.js +55744 -0
- package/lib/client-mermaid.js +208256 -0
- package/lib/client-registry.js +30378 -0
- package/lib/client-terminal.js +16894 -0
- package/lib/client.js +30378 -0
- package/lib/index.js +6897 -0
- package/lib/invariant.js +22 -0
- package/lib/types/agent-opens.d.ts +65 -0
- package/lib/types/agent-pty.d.ts +226 -0
- package/lib/types/browser-probe.d.ts +15 -0
- package/lib/types/bundle-route.d.ts +12 -0
- package/lib/types/chatcode-update.d.ts +47 -0
- package/lib/types/client/AccountMenu.d.ts +1 -0
- package/lib/types/client/AiRatioSection.d.ts +3 -0
- package/lib/types/client/BrowserView.d.ts +39 -0
- package/lib/types/client/ChatcodeGate.d.ts +6 -0
- package/lib/types/client/CodingPlanModelsSettings.d.ts +9 -0
- package/lib/types/client/DiffTab.d.ts +6 -0
- package/lib/types/client/DiffView.d.ts +51 -0
- package/lib/types/client/EditorHost.d.ts +13 -0
- package/lib/types/client/FenceErrorNotice.d.ts +5 -0
- package/lib/types/client/FileTree.d.ts +41 -0
- package/lib/types/client/FreeWindow.d.ts +29 -0
- package/lib/types/client/GitView.d.ts +12 -0
- package/lib/types/client/MarkdownHtml.d.ts +32 -0
- package/lib/types/client/OrphanedTab.d.ts +11 -0
- package/lib/types/client/PdfView.d.ts +6 -0
- package/lib/types/client/ReleaseNotesDialog.d.ts +11 -0
- package/lib/types/client/RenderBoundary.d.ts +32 -0
- package/lib/types/client/SandboxStatusBar.d.ts +10 -0
- package/lib/types/client/SideCardSection.d.ts +74 -0
- package/lib/types/client/Sidebar.d.ts +8 -0
- package/lib/types/client/TabBar.d.ts +54 -0
- package/lib/types/client/TerminalView.d.ts +24 -0
- package/lib/types/client/TextEditor.d.ts +10 -0
- package/lib/types/client/TreePanel.d.ts +27 -0
- package/lib/types/client/UpdateDialog.d.ts +10 -0
- package/lib/types/client/UpdateSettingsPanel.d.ts +1 -0
- package/lib/types/client/UpdateSettingsSection.d.ts +10 -0
- package/lib/types/client/UploadOverlay.d.ts +20 -0
- package/lib/types/client/UsageHeatmapSection.d.ts +4 -0
- package/lib/types/client/add-plugin-modal.d.ts +15 -0
- package/lib/types/client/api.d.ts +345 -0
- package/lib/types/client/binary-download.d.ts +12 -0
- package/lib/types/client/breakpoints.d.ts +17 -0
- package/lib/types/client/browser.d.ts +60 -0
- package/lib/types/client/builtins/index.d.ts +18 -0
- package/lib/types/client/builtins/tabs.d.ts +11 -0
- package/lib/types/client/builtins/viewers.d.ts +3 -0
- package/lib/types/client/chat-presentation.d.ts +13 -0
- package/lib/types/client/chunk-loader.d.ts +124 -0
- package/lib/types/client/chunks/editor.d.ts +9 -0
- package/lib/types/client/chunks/mermaid.d.ts +10 -0
- package/lib/types/client/chunks/terminal.d.ts +9 -0
- package/lib/types/client/cm-themes.d.ts +24 -0
- package/lib/types/client/conversation-draft.d.ts +14 -0
- package/lib/types/client/desktop-env.d.ts +35 -0
- package/lib/types/client/editor-load.d.ts +66 -0
- package/lib/types/client/frame-batcher.d.ts +24 -0
- package/lib/types/client/icons.d.ts +66 -0
- package/lib/types/client/image-types.d.ts +3 -0
- package/lib/types/client/ime-guard.d.ts +36 -0
- package/lib/types/client/index.d.ts +19 -0
- package/lib/types/client/intercept.d.ts +44 -0
- package/lib/types/client/lang.d.ts +14 -0
- package/lib/types/client/layout-push.d.ts +15 -0
- package/lib/types/client/lazy-chunk.d.ts +25 -0
- package/lib/types/client/link-intercept.d.ts +42 -0
- package/lib/types/client/locales-ar.d.ts +18 -0
- package/lib/types/client/locales-de.d.ts +4 -0
- package/lib/types/client/locales-fr.d.ts +10 -0
- package/lib/types/client/locales-hi.d.ts +17 -0
- package/lib/types/client/locales-id.d.ts +15 -0
- package/lib/types/client/locales-it.d.ts +8 -0
- package/lib/types/client/locales-ja.d.ts +17 -0
- package/lib/types/client/locales-ko.d.ts +9 -0
- package/lib/types/client/locales-nl.d.ts +16 -0
- package/lib/types/client/locales-pl.d.ts +19 -0
- package/lib/types/client/locales-pt.d.ts +1 -0
- package/lib/types/client/locales-ru.d.ts +13 -0
- package/lib/types/client/locales-sv.d.ts +1 -0
- package/lib/types/client/locales-th.d.ts +17 -0
- package/lib/types/client/locales-tr.d.ts +17 -0
- package/lib/types/client/locales-vi.d.ts +17 -0
- package/lib/types/client/locales-zh-HK.d.ts +32 -0
- package/lib/types/client/locales-zh-MO.d.ts +32 -0
- package/lib/types/client/locales-zh-TW.d.ts +32 -0
- package/lib/types/client/locales.d.ts +571 -0
- package/lib/types/client/markdown-html.d.ts +96 -0
- package/lib/types/client/markdown-images.d.ts +45 -0
- package/lib/types/client/markdown-labels.d.ts +21 -0
- package/lib/types/client/md-toc.d.ts +4 -0
- package/lib/types/client/mermaid-blocks.d.ts +47 -0
- package/lib/types/client/mermaid-sanitize.d.ts +2 -0
- package/lib/types/client/mermaid.d.ts +12 -0
- package/lib/types/client/open-when-sized.d.ts +20 -0
- package/lib/types/client/open-with-settings.d.ts +4 -0
- package/lib/types/client/open-with.d.ts +87 -0
- package/lib/types/client/openpath-intercept.d.ts +52 -0
- package/lib/types/client/paths.d.ts +43 -0
- package/lib/types/client/pdf-types.d.ts +2 -0
- package/lib/types/client/pinned.d.ts +86 -0
- package/lib/types/client/plugin-settings.d.ts +8 -0
- package/lib/types/client/plugins-shared.d.ts +27 -0
- package/lib/types/client/plugins-tabs.d.ts +3 -0
- package/lib/types/client/plugins-viewers.d.ts +3 -0
- package/lib/types/client/prefs.d.ts +41 -0
- package/lib/types/client/produced-files.d.ts +29 -0
- package/lib/types/client/selection-payload.d.ts +27 -0
- package/lib/types/client/service.d.ts +445 -0
- package/lib/types/client/session-pins.d.ts +14 -0
- package/lib/types/client/settings-nav-icon.d.ts +27 -0
- package/lib/types/client/shell-presets.d.ts +48 -0
- package/lib/types/client/split-pane.d.ts +38 -0
- package/lib/types/client/state.d.ts +480 -0
- package/lib/types/client/tab-content-memo.d.ts +40 -0
- package/lib/types/client/terminal-font.d.ts +78 -0
- package/lib/types/client/terminal-links.d.ts +119 -0
- package/lib/types/client/theme.d.ts +45 -0
- package/lib/types/client/titlebar-strip.d.ts +23 -0
- package/lib/types/client/ui-tweaks/appicons.d.ts +16 -0
- package/lib/types/client/ui-tweaks/archive.d.ts +42 -0
- package/lib/types/client/ui-tweaks/branding.d.ts +45 -0
- package/lib/types/client/ui-tweaks/cachehit.d.ts +41 -0
- package/lib/types/client/ui-tweaks/gitbar.d.ts +103 -0
- package/lib/types/client/ui-tweaks/index.d.ts +279 -0
- package/lib/types/client/ui-tweaks/mcp.d.ts +28 -0
- package/lib/types/client/ui-tweaks/notifier.d.ts +125 -0
- package/lib/types/client/ui-tweaks/whale.d.ts +55 -0
- package/lib/types/client/upload.d.ts +69 -0
- package/lib/types/client/useChatcodeAccount.d.ts +10 -0
- package/lib/types/client/wco.d.ts +47 -0
- package/lib/types/codingplan.d.ts +37 -0
- package/lib/types/config.d.ts +95 -0
- package/lib/types/context-types.d.ts +540 -0
- package/lib/types/fs-operations.d.ts +30 -0
- package/lib/types/fs-search.d.ts +24 -0
- package/lib/types/fs-tree.d.ts +51 -0
- package/lib/types/git.d.ts +124 -0
- package/lib/types/html-route.d.ts +59 -0
- package/lib/types/index.d.ts +47 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/login-route.d.ts +2 -0
- package/lib/types/open-external.d.ts +24 -0
- package/lib/types/path-security.d.ts +26 -0
- package/lib/types/prefs-shared.d.ts +257 -0
- package/lib/types/pty-deps.d.ts +79 -0
- package/lib/types/pty-manager.d.ts +168 -0
- package/lib/types/tools.d.ts +34 -0
- package/lib/types/trust-fence.d.ts +24 -0
- package/lib/types/ui-tweaks/archive.d.ts +71 -0
- package/lib/types/ui-tweaks/config.d.ts +186 -0
- package/lib/types/ui-tweaks/git-web.d.ts +43 -0
- package/lib/types/ui-tweaks/git.d.ts +311 -0
- package/lib/types/ui-tweaks/index.d.ts +17 -0
- package/lib/types/ui-tweaks/mcp.d.ts +103 -0
- package/lib/types/ui-tweaks/turn-outcome.d.ts +57 -0
- package/lib/types/ui-tweaks/web.d.ts +55 -0
- package/lib/types/wire.d.ts +38 -0
- package/package.json +213 -0
- package/scripts/install.ps1 +310 -0
- package/scripts/install.sh +287 -0
- package/src/agent-opens.ts +265 -0
- package/src/agent-pty.ts +527 -0
- package/src/browser-probe.ts +26 -0
- package/src/bundle-route.ts +129 -0
- package/src/chatcode-update.ts +193 -0
- package/src/client/AccountMenu.tsx +177 -0
- package/src/client/AiRatioSection.module.css +329 -0
- package/src/client/AiRatioSection.tsx +292 -0
- package/src/client/BrowserView.tsx +289 -0
- package/src/client/ChatcodeGate.tsx +35 -0
- package/src/client/CodingPlanModelsSettings.module.css +118 -0
- package/src/client/CodingPlanModelsSettings.tsx +101 -0
- package/src/client/DiffTab.tsx +114 -0
- package/src/client/DiffView.tsx +305 -0
- package/src/client/EditorHost.tsx +563 -0
- package/src/client/FenceErrorNotice.tsx +40 -0
- package/src/client/FileTree.tsx +701 -0
- package/src/client/FreeWindow.tsx +337 -0
- package/src/client/GitView.tsx +722 -0
- package/src/client/MarkdownHtml.tsx +296 -0
- package/src/client/OrphanedTab.tsx +27 -0
- package/src/client/PdfView.tsx +110 -0
- package/src/client/ReleaseNotesDialog.tsx +91 -0
- package/src/client/RenderBoundary.tsx +49 -0
- package/src/client/SandboxStatusBar.tsx +60 -0
- package/src/client/SideCardSection.module.css +932 -0
- package/src/client/SideCardSection.tsx +1169 -0
- package/src/client/Sidebar.tsx +1857 -0
- package/src/client/TabBar.tsx +365 -0
- package/src/client/TerminalView.tsx +424 -0
- package/src/client/TextEditor.tsx +468 -0
- package/src/client/TreePanel.tsx +279 -0
- package/src/client/UpdateDialog.tsx +186 -0
- package/src/client/UpdateSettingsPanel.tsx +29 -0
- package/src/client/UpdateSettingsSection.tsx +22 -0
- package/src/client/UploadOverlay.tsx +62 -0
- package/src/client/UsageHeatmapSection.module.css +440 -0
- package/src/client/UsageHeatmapSection.tsx +301 -0
- package/src/client/add-plugin-modal.tsx +199 -0
- package/src/client/api.ts +433 -0
- package/src/client/binary-download.tsx +23 -0
- package/src/client/breakpoints.ts +61 -0
- package/src/client/browser.ts +167 -0
- package/src/client/builtins/index.ts +37 -0
- package/src/client/builtins/tabs.tsx +275 -0
- package/src/client/builtins/viewers.tsx +125 -0
- package/src/client/chat-presentation.ts +95 -0
- package/src/client/chunk-loader.ts +350 -0
- package/src/client/chunks/editor.tsx +9 -0
- package/src/client/chunks/mermaid.tsx +10 -0
- package/src/client/chunks/terminal.tsx +9 -0
- package/src/client/cm-themes.ts +129 -0
- package/src/client/conversation-draft.ts +29 -0
- package/src/client/css-modules.d.ts +5 -0
- package/src/client/desktop-env.ts +72 -0
- package/src/client/editor-load.ts +92 -0
- package/src/client/frame-batcher.ts +56 -0
- package/src/client/icons.tsx +170 -0
- package/src/client/image-types.ts +8 -0
- package/src/client/ime-guard.ts +47 -0
- package/src/client/index.tsx +501 -0
- package/src/client/intercept.tsx +172 -0
- package/src/client/lang.ts +225 -0
- package/src/client/layout-push.ts +37 -0
- package/src/client/layout.css +156 -0
- package/src/client/lazy-chunk.tsx +89 -0
- package/src/client/link-intercept.ts +73 -0
- package/src/client/locales-ar.ts +387 -0
- package/src/client/locales-de.ts +372 -0
- package/src/client/locales-fr.ts +379 -0
- package/src/client/locales-hi.ts +386 -0
- package/src/client/locales-id.ts +384 -0
- package/src/client/locales-it.ts +377 -0
- package/src/client/locales-ja.ts +486 -0
- package/src/client/locales-ko.ts +378 -0
- package/src/client/locales-nl.ts +384 -0
- package/src/client/locales-pl.ts +388 -0
- package/src/client/locales-pt.ts +369 -0
- package/src/client/locales-ru.ts +384 -0
- package/src/client/locales-sv.ts +369 -0
- package/src/client/locales-th.ts +386 -0
- package/src/client/locales-tr.ts +386 -0
- package/src/client/locales-vi.ts +386 -0
- package/src/client/locales-zh-HK.ts +401 -0
- package/src/client/locales-zh-MO.ts +401 -0
- package/src/client/locales-zh-TW.ts +401 -0
- package/src/client/locales.ts +1229 -0
- package/src/client/markdown-html.ts +331 -0
- package/src/client/markdown-images.ts +137 -0
- package/src/client/markdown-labels.tsx +31 -0
- package/src/client/md-toc.tsx +127 -0
- package/src/client/mermaid-blocks.ts +110 -0
- package/src/client/mermaid-sanitize.ts +93 -0
- package/src/client/mermaid.tsx +406 -0
- package/src/client/open-when-sized.ts +43 -0
- package/src/client/open-with-settings.tsx +135 -0
- package/src/client/open-with.ts +221 -0
- package/src/client/openpath-intercept.ts +79 -0
- package/src/client/paths.ts +63 -0
- package/src/client/pdf-types.ts +4 -0
- package/src/client/pinned.ts +184 -0
- package/src/client/plugin-settings.ts +43 -0
- package/src/client/plugins-shared.ts +29 -0
- package/src/client/plugins-tabs.ts +153 -0
- package/src/client/plugins-viewers.ts +44 -0
- package/src/client/prefs.ts +216 -0
- package/src/client/produced-files.ts +120 -0
- package/src/client/selection-payload.ts +86 -0
- package/src/client/service.ts +873 -0
- package/src/client/session-pins.tsx +334 -0
- package/src/client/settings-nav-icon.ts +55 -0
- package/src/client/shell-presets.ts +83 -0
- package/src/client/sidebar.module.css +3607 -0
- package/src/client/split-pane.tsx +333 -0
- package/src/client/state.ts +1646 -0
- package/src/client/tab-content-memo.ts +55 -0
- package/src/client/terminal-font.ts +224 -0
- package/src/client/terminal-links.ts +194 -0
- package/src/client/theme.ts +110 -0
- package/src/client/titlebar-strip.ts +37 -0
- package/src/client/ui-tweaks/appicons.ts +24 -0
- package/src/client/ui-tweaks/archive.tsx +281 -0
- package/src/client/ui-tweaks/branding.tsx +232 -0
- package/src/client/ui-tweaks/cachehit.tsx +122 -0
- package/src/client/ui-tweaks/gitbar.tsx +2559 -0
- package/src/client/ui-tweaks/index.tsx +1696 -0
- package/src/client/ui-tweaks/mcp.tsx +582 -0
- package/src/client/ui-tweaks/notifier.ts +514 -0
- package/src/client/ui-tweaks/whale.tsx +171 -0
- package/src/client/upload.ts +187 -0
- package/src/client/useChatcodeAccount.ts +103 -0
- package/src/client/wco.ts +120 -0
- package/src/codingplan.ts +146 -0
- package/src/config.ts +207 -0
- package/src/context-types.ts +559 -0
- package/src/fs-operations.ts +97 -0
- package/src/fs-search.ts +113 -0
- package/src/fs-tree.ts +158 -0
- package/src/git.ts +446 -0
- package/src/html-route.ts +106 -0
- package/src/index.ts +1524 -0
- package/src/invariant.ts +32 -0
- package/src/login-route.ts +26 -0
- package/src/open-external.ts +90 -0
- package/src/path-security.ts +81 -0
- package/src/prefs-shared.ts +303 -0
- package/src/pty-deps.ts +241 -0
- package/src/pty-manager.ts +376 -0
- package/src/tools.ts +482 -0
- package/src/trust-fence.ts +84 -0
- package/src/ui-tweaks/archive.ts +293 -0
- package/src/ui-tweaks/config.ts +257 -0
- package/src/ui-tweaks/git-web.ts +421 -0
- package/src/ui-tweaks/git.ts +1524 -0
- package/src/ui-tweaks/index.ts +61 -0
- package/src/ui-tweaks/mcp.ts +535 -0
- package/src/ui-tweaks/turn-outcome.ts +105 -0
- package/src/ui-tweaks/web.ts +225 -0
- package/src/wire.ts +110 -0
- package/vendor/addon-fit.js +2 -0
- package/vendor/xterm.css +218 -0
- package/vendor/xterm.js +5 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-ui-tweaks — archive manager (host half).
|
|
3
|
+
*
|
|
4
|
+
* The browser Archive manager (the "归档" settings section, shown when the
|
|
5
|
+
* archiveManagerEnabled setting is on) lists archived sessions and offers
|
|
6
|
+
* two per-session actions plus batch actions through this same-origin route:
|
|
7
|
+
*
|
|
8
|
+
* - **restore** / **restore-all**: move a session out of the archive set. The
|
|
9
|
+
* session log and its workspace accounting slot are kept untouched, so the
|
|
10
|
+
* conversation reappears in the normal sidebar list.
|
|
11
|
+
* - **delete** / **delete-all**: PERMANENTLY delete a session — its durable
|
|
12
|
+
* JSONL log is removed from disk, it is detached from every workspace's
|
|
13
|
+
* accounting, removed from the archive set, dropped from the workspace
|
|
14
|
+
* registry's in-memory header index, and best-effort-cleaned from the
|
|
15
|
+
* session projection cache. Only a genuinely RUNNING agent is refused;
|
|
16
|
+
* opened-but-idle sessions are also removed from the in-memory SessionStore
|
|
17
|
+
* so they vanish from the live list immediately (the host relays
|
|
18
|
+
* `session/disposed` as `host/session-removed`).
|
|
19
|
+
*
|
|
20
|
+
* DSH has no public API for either operation. The archive set lives in the
|
|
21
|
+
* `workspace` storage domain's global singleton (the same record
|
|
22
|
+
* `WorkspaceRegistry` keeps in memory), so this backend writes the filtered
|
|
23
|
+
* set through the live domain handle (`ctx.storageDomain.get('workspace')`)
|
|
24
|
+
* and keeps the registry's in-memory cache coherent in the same write, so the
|
|
25
|
+
* registry's next mutation cannot resurrect a restored id. Session-log
|
|
26
|
+
* deletion uses the persistence backend's own locator (`findLog`) so only the
|
|
27
|
+
* exact encoded session directory is removed; workspace accounting removal
|
|
28
|
+
* uses the registry's public `WorkspaceEntity.detachSession`.
|
|
29
|
+
* @module dsh-ui-tweaks/archive
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { rm } from 'node:fs/promises'
|
|
33
|
+
import { dirname } from 'node:path'
|
|
34
|
+
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
35
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
36
|
+
// Type-only imports activate the webServer / storageDomain Context declarations.
|
|
37
|
+
import type {} from '@deepseek-ai/dsh-host-webserver'
|
|
38
|
+
import type {} from '@deepseek-ai/dsh-storage-domain'
|
|
39
|
+
import { workspaceDomainState } from '@deepseek-ai/dsh-workspace'
|
|
40
|
+
import { isRecord, json, messageOf, readJson, requestError, sameOriginPost } from './web.ts'
|
|
41
|
+
|
|
42
|
+
/** Exact route used by the browser Archive panel. */
|
|
43
|
+
export const ARCHIVE_ROUTE = '/_dsh/ui-tweaks/archive'
|
|
44
|
+
|
|
45
|
+
/** Public Archive snapshot. Only session ids cross this boundary — the browser
|
|
46
|
+
* enriches them with titles / paths from its own session list feed. */
|
|
47
|
+
export interface ArchiveSnapshot {
|
|
48
|
+
archivedSessionIds: string[]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface SessionRequest {
|
|
52
|
+
action: 'restore' | 'delete'
|
|
53
|
+
sessionId: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface BatchRequest {
|
|
57
|
+
action: 'restore-all' | 'delete-all'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type ArchiveRequest = SessionRequest | BatchRequest
|
|
61
|
+
|
|
62
|
+
/** Structural face of the workspace registry this backend needs. The registry
|
|
63
|
+
* caches the domain global in `state` (a private field — the cast is
|
|
64
|
+
* deliberate, see {@link ArchiveBackend.writeArchiveSet}); the entities expose
|
|
65
|
+
* the public {@link WorkspaceEntity} API. */
|
|
66
|
+
interface WorkspaceRegistryLike {
|
|
67
|
+
readonly archivedSessionIds: readonly string[]
|
|
68
|
+
readonly list: () => ReadonlyArray<{
|
|
69
|
+
readonly sessionIds: readonly string[]
|
|
70
|
+
detachSession(sessionId: string): Promise<void>
|
|
71
|
+
}>
|
|
72
|
+
state: unknown
|
|
73
|
+
/** Private in-memory header index; cleaned so deleted sessions vanish without a restart. */
|
|
74
|
+
headers?: Map<string, unknown>
|
|
75
|
+
sessionPaths?: Map<string, unknown>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Structural face of the storage-domain facility's live domain handles. */
|
|
79
|
+
interface StorageDomainLike {
|
|
80
|
+
get(name: string):
|
|
81
|
+
| {
|
|
82
|
+
global: { get(): unknown; set(value: unknown): Promise<void> }
|
|
83
|
+
table?(name: string): { delete?(key: string): Promise<void> }
|
|
84
|
+
}
|
|
85
|
+
| undefined
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Structural face of the session-persistence backend's log locator. */
|
|
89
|
+
interface PersistenceLike {
|
|
90
|
+
findLog?(id: string, signal?: AbortSignal): Promise<string | undefined>
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A live session was targeted by a permanent delete. */
|
|
94
|
+
export class SessionLiveError extends Error {
|
|
95
|
+
readonly code = 'session-live'
|
|
96
|
+
constructor() {
|
|
97
|
+
super('cannot permanently delete a live session: close it first')
|
|
98
|
+
this.name = 'SessionLiveError'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseRequest(value: unknown): ArchiveRequest {
|
|
103
|
+
if (!isRecord(value)) throw new TypeError('request body must be an object')
|
|
104
|
+
if (value.action === 'restore' || value.action === 'delete') {
|
|
105
|
+
const sessionId = value.sessionId
|
|
106
|
+
if (typeof sessionId !== 'string' || sessionId.length === 0) throw new TypeError('sessionId must be a non-empty string')
|
|
107
|
+
return { action: value.action, sessionId }
|
|
108
|
+
}
|
|
109
|
+
if (value.action === 'restore-all' || value.action === 'delete-all') return { action: value.action }
|
|
110
|
+
throw new TypeError('action must be "restore", "delete", "restore-all" or "delete-all"')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Same-origin Archive read/write handler. */
|
|
114
|
+
export class ArchiveBackend {
|
|
115
|
+
constructor(private readonly ctx: Context) {}
|
|
116
|
+
|
|
117
|
+
private registry(): WorkspaceRegistryLike | undefined {
|
|
118
|
+
return this.ctx.get('workspaceRegistry') as unknown as WorkspaceRegistryLike | undefined
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private storageDomain(): StorageDomainLike | undefined {
|
|
122
|
+
return this.ctx.get('storageDomain') as unknown as StorageDomainLike | undefined
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private snapshot(): ArchiveSnapshot {
|
|
126
|
+
const archivedSessionIds = [...(this.registry()?.archivedSessionIds ?? [])]
|
|
127
|
+
return { archivedSessionIds }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Remove one (or all) ids from the durable archive set, syncing the
|
|
131
|
+
* registry's in-memory cache in the same write. */
|
|
132
|
+
private async writeArchiveSet(
|
|
133
|
+
registry: WorkspaceRegistryLike,
|
|
134
|
+
keep: (id: string) => boolean,
|
|
135
|
+
): Promise<void> {
|
|
136
|
+
const global = this.storageDomain()?.get('workspace')?.global
|
|
137
|
+
if (global === undefined) throw new Error('workspace storage domain is unavailable')
|
|
138
|
+
const state = workspaceDomainState.parse(global.get())
|
|
139
|
+
const next = {
|
|
140
|
+
...state,
|
|
141
|
+
archivedSessionIds: state.archivedSessionIds.filter(keep),
|
|
142
|
+
}
|
|
143
|
+
if (next.archivedSessionIds.length === state.archivedSessionIds.length) return
|
|
144
|
+
await global.set(next)
|
|
145
|
+
// Keep the registry's in-memory cache coherent so its next write (e.g.
|
|
146
|
+
// another archive, a workspace create) does not resurrect the ids.
|
|
147
|
+
registry.state = next
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Restore one session: remove it from the durable archive set. */
|
|
151
|
+
private async restore(sessionId: string): Promise<void> {
|
|
152
|
+
const registry = this.registry()
|
|
153
|
+
if (registry === undefined) throw new Error('workspace registry is unavailable')
|
|
154
|
+
if (!registry.archivedSessionIds.includes(sessionId)) return
|
|
155
|
+
await this.writeArchiveSet(registry, id => id !== sessionId)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Restore every archived session at once. */
|
|
159
|
+
private async restoreAll(): Promise<void> {
|
|
160
|
+
const registry = this.registry()
|
|
161
|
+
if (registry === undefined) throw new Error('workspace registry is unavailable')
|
|
162
|
+
if (registry.archivedSessionIds.length === 0) return
|
|
163
|
+
await this.writeArchiveSet(registry, () => false)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Permanently delete one session: log, accounting, archive set, and caches. */
|
|
167
|
+
private async deleteSession(sessionId: string): Promise<void> {
|
|
168
|
+
// Refuse only a genuinely RUNNING agent. Sessions that were merely opened
|
|
169
|
+
// (and archived) stay attached to the in-memory SessionStore while idle —
|
|
170
|
+
// refusing every attached session would make most archived sessions
|
|
171
|
+
// undeletable.
|
|
172
|
+
const agents = this.ctx.get('agents') as unknown as {
|
|
173
|
+
get(id: string): { status: 'idle' | 'running'; cancel(cause: unknown): void; whenIdle(): Promise<void> } | undefined
|
|
174
|
+
} | undefined
|
|
175
|
+
const agent = agents?.get(sessionId)
|
|
176
|
+
if (agent !== undefined && agent.status === 'running') throw new SessionLiveError()
|
|
177
|
+
if (agent !== undefined) {
|
|
178
|
+
// Clear any queued work and let the (idle) agent settle before the log
|
|
179
|
+
// is removed.
|
|
180
|
+
agent.cancel({ kind: 'user' })
|
|
181
|
+
await agent.whenIdle()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 1. Remove the durable session log (its parent directory) from disk.
|
|
185
|
+
const persistence = this.ctx.get('sessionPersistence') as unknown as PersistenceLike | undefined
|
|
186
|
+
if (persistence?.findLog !== undefined) {
|
|
187
|
+
const logPath = await persistence.findLog(sessionId)
|
|
188
|
+
if (logPath !== undefined) await rm(dirname(logPath), { recursive: true, force: true })
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 2. Detach from every workspace's accounting (public entity API), then
|
|
192
|
+
// remove from the archive set, then forget it in the registry's
|
|
193
|
+
// in-memory header index so membership checks drop it without restart.
|
|
194
|
+
const registry = this.registry()
|
|
195
|
+
if (registry !== undefined) {
|
|
196
|
+
for (const workspace of registry.list()) {
|
|
197
|
+
if (workspace.sessionIds.includes(sessionId)) await workspace.detachSession(sessionId)
|
|
198
|
+
}
|
|
199
|
+
await this.writeArchiveSet(registry, id => id !== sessionId)
|
|
200
|
+
registry.headers?.delete(sessionId)
|
|
201
|
+
registry.sessionPaths?.delete(sessionId)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 3. Best-effort: drop the session's projection-cache checkpoint.
|
|
205
|
+
const table = this.storageDomain()?.get('session_projcache')?.table?.('sessions')
|
|
206
|
+
if (table?.delete !== undefined) await table.delete(sessionId)
|
|
207
|
+
|
|
208
|
+
// 4. Drop the (idle) session from the in-memory SessionStore so it
|
|
209
|
+
// vanishes from the live list immediately: the store's entry detach
|
|
210
|
+
// fires `session/disposed`, which the host relays as
|
|
211
|
+
// `host/session-removed` and the browser forgets the session. The
|
|
212
|
+
// agent's own lifecycle stays registered until process end — it is
|
|
213
|
+
// idle, unreachable, and cleared by the next restart.
|
|
214
|
+
const sessions = this.ctx.get('sessions') as unknown as { store?: Map<string, { detach?(): void }> } | undefined
|
|
215
|
+
sessions?.store?.get(sessionId)?.detach?.()
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Permanently delete every archived session (live ones are skipped). */
|
|
219
|
+
private async deleteAll(): Promise<{ deleted: number; skipped: number }> {
|
|
220
|
+
const registry = this.registry()
|
|
221
|
+
if (registry === undefined) throw new Error('workspace registry is unavailable')
|
|
222
|
+
const archived = [...registry.archivedSessionIds]
|
|
223
|
+
let deleted = 0
|
|
224
|
+
let skipped = 0
|
|
225
|
+
for (const id of archived) {
|
|
226
|
+
try {
|
|
227
|
+
await this.deleteSession(id)
|
|
228
|
+
deleted += 1
|
|
229
|
+
} catch (error) {
|
|
230
|
+
skipped += 1
|
|
231
|
+
this.ctx.logger.warn('dsh-ui-tweaks archive delete skipped %s: %s', id, messageOf(error))
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return { deleted, skipped }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Handle the exact Archive route. */
|
|
238
|
+
async handle(req: IncomingMessage, res: ServerResponse): Promise<void> {
|
|
239
|
+
if (req.method === 'GET') {
|
|
240
|
+
try {
|
|
241
|
+
json(res, 200, { ok: true, value: this.snapshot() })
|
|
242
|
+
} catch (error) {
|
|
243
|
+
this.ctx.logger.warn('dsh-ui-tweaks Archive snapshot failed: %s', messageOf(error))
|
|
244
|
+
requestError(res, 503, 'archive-unavailable', 'UI Tweaks archive is unavailable')
|
|
245
|
+
}
|
|
246
|
+
return
|
|
247
|
+
}
|
|
248
|
+
if (req.method !== 'POST') {
|
|
249
|
+
res.setHeader('Allow', 'GET, POST')
|
|
250
|
+
requestError(res, 405, 'method-not-allowed', 'Use GET or POST')
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
if (!sameOriginPost(req)) {
|
|
254
|
+
requestError(res, 403, 'origin-rejected', 'The request must originate from this ChatCode CLI Web application')
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
let request: ArchiveRequest
|
|
258
|
+
try {
|
|
259
|
+
request = parseRequest(await readJson(req))
|
|
260
|
+
} catch (error) {
|
|
261
|
+
requestError(res, 400, 'invalid-request', messageOf(error))
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
if (request.action === 'restore') await this.restore(request.sessionId)
|
|
266
|
+
else if (request.action === 'restore-all') await this.restoreAll()
|
|
267
|
+
else if (request.action === 'delete') await this.deleteSession(request.sessionId)
|
|
268
|
+
else await this.deleteAll()
|
|
269
|
+
json(res, 200, { ok: true, value: this.snapshot() })
|
|
270
|
+
} catch (error) {
|
|
271
|
+
const live = error instanceof SessionLiveError
|
|
272
|
+
this.ctx.logger.warn('dsh-ui-tweaks Archive mutation failed: %s', messageOf(error))
|
|
273
|
+
requestError(res, live ? 409 : 400, live ? 'session-live' : 'archive-rejected', messageOf(error))
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Attach the Archive route whenever a webServer service is present.
|
|
280
|
+
* @param ctx - plugin context owning route effects.
|
|
281
|
+
* @param backend - Archive handler.
|
|
282
|
+
*/
|
|
283
|
+
export function installArchiveWeb(ctx: Context, backend: ArchiveBackend): void {
|
|
284
|
+
ctx.inject(['webServer'], (webCtx) => {
|
|
285
|
+
webCtx.effect(() => {
|
|
286
|
+
return webCtx.webServer.register({
|
|
287
|
+
kind: 'exact',
|
|
288
|
+
path: ARCHIVE_ROUTE,
|
|
289
|
+
handler: (req, res) => backend.handle(req, res),
|
|
290
|
+
})
|
|
291
|
+
}, 'dsh-ui-tweaks: Archive routes')
|
|
292
|
+
})
|
|
293
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-ui-tweaks configuration: conversation font size (px), markdown table
|
|
3
|
+
* style, and feature toggles. Every field
|
|
4
|
+
* defaults at the schema boundary, so a hand-edited settings document and the
|
|
5
|
+
* Settings panel stay consistent.
|
|
6
|
+
* @module dsh-ui-tweaks/config
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import z from '@deepseek-ai/schemastery'
|
|
10
|
+
import type Schema from '@deepseek-ai/schemastery'
|
|
11
|
+
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings'
|
|
12
|
+
|
|
13
|
+
/** Settings document namespace owned by this plugin. */
|
|
14
|
+
// `settingsNamespace()` was removed in DSH 0.1.2-alpha.2. Keep the opaque
|
|
15
|
+
// namespace type for source compatibility with older DSH declarations, while
|
|
16
|
+
// emitting the plain validated literal required by the current runtime.
|
|
17
|
+
export const UI_TWEAKS_SETTINGS_NAMESPACE = 'ui-tweaks' as SettingsNamespace
|
|
18
|
+
|
|
19
|
+
/** Raw user-facing configuration (partial inputs receive schema defaults). */
|
|
20
|
+
export interface UITweaksConfig {
|
|
21
|
+
/** Message content font size in px. */
|
|
22
|
+
fontSize?: number
|
|
23
|
+
/**
|
|
24
|
+
* Code font size as a percentage of the message font size. 81 is the stock
|
|
25
|
+
* ratio (13px code block at a 16px body); 100 makes code match the body.
|
|
26
|
+
*
|
|
27
|
+
* @deprecated Legacy percent input, kept for migration; prefer
|
|
28
|
+
* `codeFontSize`. Ignored once `codeFontSize` is set.
|
|
29
|
+
*/
|
|
30
|
+
codeFontScale?: number
|
|
31
|
+
/**
|
|
32
|
+
* Absolute code font size in px (8–32). 13 matches the stock DSH code block
|
|
33
|
+
* at a 16px body. Drives the code block directly; inline code and the small
|
|
34
|
+
* code font follow proportionally. When unset, falls back to the legacy
|
|
35
|
+
* `codeFontScale` percentage, then to the stock default.
|
|
36
|
+
*/
|
|
37
|
+
codeFontSize?: number
|
|
38
|
+
/**
|
|
39
|
+
* The "行高" base unit in px: the vertical rhythm of the reply area. It
|
|
40
|
+
* drives the gap between message rows (Think ↔ tool cards, user ↔
|
|
41
|
+
* assistant), the gap between content blocks inside one assistant reply
|
|
42
|
+
* (Think ↔ text), the markdown text line-height, paragraph bottom margins,
|
|
43
|
+
* and list margins — all scaled proportionally. 16 matches the stock DSH
|
|
44
|
+
* spacing.
|
|
45
|
+
*/
|
|
46
|
+
lineHeight?: number
|
|
47
|
+
/** Markdown table presentation style. */
|
|
48
|
+
tableStyle?: 'default' | 'claude'
|
|
49
|
+
/**
|
|
50
|
+
* Whether the GitBar (branch / diff / commit-message pills above the input)
|
|
51
|
+
* is shown. Off by default; the bar hides itself when the session has no
|
|
52
|
+
* cwd or the directory is not a git repository.
|
|
53
|
+
*/
|
|
54
|
+
gitBarEnabled?: boolean
|
|
55
|
+
/**
|
|
56
|
+
* Whether the Archive manager is shown: the "归档" Settings page that lists
|
|
57
|
+
* archived sessions and can restore or permanently delete them. Off by
|
|
58
|
+
* default; users turn it on in Settings.
|
|
59
|
+
*/
|
|
60
|
+
archiveManagerEnabled?: boolean
|
|
61
|
+
/**
|
|
62
|
+
* Whether the MCP manager is shown: the "MCP 管理" Settings page that lists
|
|
63
|
+
* the configured MCP servers (status, tool count) and can restart them. Off
|
|
64
|
+
* by default; users turn it on in Settings.
|
|
65
|
+
*/
|
|
66
|
+
mcpManagerEnabled?: boolean
|
|
67
|
+
/**
|
|
68
|
+
* Whether the `/init` slash command is registered: picking a prompt language
|
|
69
|
+
* submits an AGENTS.md bootstrap prompt into the session. Off by default;
|
|
70
|
+
* users turn it on in Settings.
|
|
71
|
+
*/
|
|
72
|
+
initCommandEnabled?: boolean
|
|
73
|
+
/**
|
|
74
|
+
* Whether the whale working indicator is shown: a little brand whale
|
|
75
|
+
* centered above the input box — translucent and still while the model is
|
|
76
|
+
* idle, swimming while it works. Off by default; users turn it on in
|
|
77
|
+
* Settings.
|
|
78
|
+
*/
|
|
79
|
+
whaleIndicatorEnabled?: boolean
|
|
80
|
+
/**
|
|
81
|
+
* Whether the composer stats line's cache-hit figure keeps two decimal
|
|
82
|
+
* places ("缓存命中 96.35%"), computed client-side from the raw
|
|
83
|
+
* cached-read / cached-write / uncached-input token buckets instead of
|
|
84
|
+
* DSH's rounded integer. Off by default; users turn it on in Settings.
|
|
85
|
+
*/
|
|
86
|
+
preciseCacheHitEnabled?: boolean
|
|
87
|
+
/**
|
|
88
|
+
* Master switch for task notifications: browser-side alerts (tab-title
|
|
89
|
+
* flash, system notification, chime) raised when a session finishes its
|
|
90
|
+
* turn or starts waiting on the user. Off by default; users turn it on in
|
|
91
|
+
* Settings.
|
|
92
|
+
*/
|
|
93
|
+
notificationsEnabled?: boolean
|
|
94
|
+
/** Stay quiet while the page is visible and focused; alert only once hidden or unfocused. */
|
|
95
|
+
notifyOnlyWhenHidden?: boolean
|
|
96
|
+
/** Alert when a session ends its turn — completed, interrupted or failed. */
|
|
97
|
+
notifyOnComplete?: boolean
|
|
98
|
+
/** Alert when a session starts blocking on an approval, plan review or question. */
|
|
99
|
+
notifyOnInteraction?: boolean
|
|
100
|
+
/** Blink an unread counter into the tab title until the user returns. */
|
|
101
|
+
notifyTitleFlash?: boolean
|
|
102
|
+
/** Fire desktop-level Web Notifications; clicking one opens that session. */
|
|
103
|
+
notifySystemNotification?: boolean
|
|
104
|
+
/** Play a synthesized two-note chime (rising = done, falling = needs you). */
|
|
105
|
+
notifySound?: boolean
|
|
106
|
+
/**
|
|
107
|
+
* Optional explicit `provider:model` for generating commit messages with the
|
|
108
|
+
* LLM (e.g. `jiyuanlvdong:deepseek-v4-flash-0731`). When unset, the first
|
|
109
|
+
* registered provider/model is used; when the LLM service is unavailable or
|
|
110
|
+
* the call fails, a heuristic commit message is generated instead.
|
|
111
|
+
*/
|
|
112
|
+
suggestModel?: string
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const MIN_FONT_SIZE = 10
|
|
116
|
+
export const MAX_FONT_SIZE = 32
|
|
117
|
+
export const DEFAULT_FONT_SIZE = 16
|
|
118
|
+
|
|
119
|
+
/** 16px matches the stock DSH vertical rhythm (message/block gaps, etc.). */
|
|
120
|
+
export const MIN_LINE_HEIGHT = 0
|
|
121
|
+
export const MAX_LINE_HEIGHT = 64
|
|
122
|
+
export const DEFAULT_LINE_HEIGHT = 16
|
|
123
|
+
|
|
124
|
+
/** 81% = the stock code ratio (13px code block at a 16px body). Legacy input. */
|
|
125
|
+
export const MIN_CODE_FONT_SCALE = 50
|
|
126
|
+
export const MAX_CODE_FONT_SCALE = 150
|
|
127
|
+
export const DEFAULT_CODE_FONT_SCALE = 81
|
|
128
|
+
|
|
129
|
+
/** Absolute code font size (px): 13 is the stock DSH code block at a 16px body. */
|
|
130
|
+
export const MIN_CODE_FONT_SIZE = 8
|
|
131
|
+
export const MAX_CODE_FONT_SIZE = 32
|
|
132
|
+
export const DEFAULT_CODE_FONT_SIZE = 13
|
|
133
|
+
|
|
134
|
+
/** GitBar defaults to off; users turn it on in Settings. */
|
|
135
|
+
export const DEFAULT_GITBAR_ENABLED = false
|
|
136
|
+
|
|
137
|
+
/** Archive manager defaults to off; users turn it on in Settings. */
|
|
138
|
+
export const DEFAULT_ARCHIVE_MANAGER_ENABLED = false
|
|
139
|
+
|
|
140
|
+
/** MCP manager defaults to off; users turn it on in Settings. */
|
|
141
|
+
export const DEFAULT_MCP_MANAGER_ENABLED = false
|
|
142
|
+
|
|
143
|
+
/** The /init slash command defaults to off; users turn it on in Settings. */
|
|
144
|
+
export const DEFAULT_INIT_COMMAND_ENABLED = false
|
|
145
|
+
|
|
146
|
+
/** The whale working indicator defaults to off; users turn it on in Settings. */
|
|
147
|
+
export const DEFAULT_WHALE_INDICATOR_ENABLED = false
|
|
148
|
+
|
|
149
|
+
/** The precise cache-hit readout defaults to off; users turn it on in Settings. */
|
|
150
|
+
export const DEFAULT_PRECISE_CACHE_HIT_ENABLED = false
|
|
151
|
+
|
|
152
|
+
/** Task notifications default to off; users turn them on in Settings. */
|
|
153
|
+
export const DEFAULT_NOTIFICATIONS_ENABLED = false
|
|
154
|
+
|
|
155
|
+
/** Notification behavior defaults: quiet while watched, both event kinds on. */
|
|
156
|
+
export const DEFAULT_NOTIFY_ONLY_WHEN_HIDDEN = true
|
|
157
|
+
export const DEFAULT_NOTIFY_ON_COMPLETE = true
|
|
158
|
+
export const DEFAULT_NOTIFY_ON_INTERACTION = true
|
|
159
|
+
|
|
160
|
+
/** Notification channel defaults: title flash + system notification on, chime opt-in. */
|
|
161
|
+
export const DEFAULT_NOTIFY_TITLE_FLASH = true
|
|
162
|
+
export const DEFAULT_NOTIFY_SYSTEM_NOTIFICATION = true
|
|
163
|
+
export const DEFAULT_NOTIFY_SOUND = false
|
|
164
|
+
|
|
165
|
+
/** Configuration schema with documented defaults. */
|
|
166
|
+
export const Config: Schema<UITweaksConfig> = z.object({
|
|
167
|
+
fontSize: z.number().min(10).max(32).default(16),
|
|
168
|
+
codeFontScale: z.number().min(MIN_CODE_FONT_SCALE).max(MAX_CODE_FONT_SCALE).default(DEFAULT_CODE_FONT_SCALE),
|
|
169
|
+
codeFontSize: z.number().min(MIN_CODE_FONT_SIZE).max(MAX_CODE_FONT_SIZE),
|
|
170
|
+
lineHeight: z.number().min(MIN_LINE_HEIGHT).max(MAX_LINE_HEIGHT).default(DEFAULT_LINE_HEIGHT),
|
|
171
|
+
tableStyle: z.union(['default', 'claude'] as const).default('default'),
|
|
172
|
+
gitBarEnabled: z.boolean().default(DEFAULT_GITBAR_ENABLED),
|
|
173
|
+
archiveManagerEnabled: z.boolean().default(DEFAULT_ARCHIVE_MANAGER_ENABLED),
|
|
174
|
+
mcpManagerEnabled: z.boolean().default(DEFAULT_MCP_MANAGER_ENABLED),
|
|
175
|
+
initCommandEnabled: z.boolean().default(DEFAULT_INIT_COMMAND_ENABLED),
|
|
176
|
+
whaleIndicatorEnabled: z.boolean().default(DEFAULT_WHALE_INDICATOR_ENABLED),
|
|
177
|
+
preciseCacheHitEnabled: z.boolean().default(DEFAULT_PRECISE_CACHE_HIT_ENABLED),
|
|
178
|
+
notificationsEnabled: z.boolean().default(DEFAULT_NOTIFICATIONS_ENABLED),
|
|
179
|
+
notifyOnlyWhenHidden: z.boolean().default(DEFAULT_NOTIFY_ONLY_WHEN_HIDDEN),
|
|
180
|
+
notifyOnComplete: z.boolean().default(DEFAULT_NOTIFY_ON_COMPLETE),
|
|
181
|
+
notifyOnInteraction: z.boolean().default(DEFAULT_NOTIFY_ON_INTERACTION),
|
|
182
|
+
notifyTitleFlash: z.boolean().default(DEFAULT_NOTIFY_TITLE_FLASH),
|
|
183
|
+
notifySystemNotification: z.boolean().default(DEFAULT_NOTIFY_SYSTEM_NOTIFICATION),
|
|
184
|
+
notifySound: z.boolean().default(DEFAULT_NOTIFY_SOUND),
|
|
185
|
+
suggestModel: z.string(),
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
/** Configuration after static validation, with every default materialized. */
|
|
189
|
+
export interface ResolvedUITweaksConfig {
|
|
190
|
+
fontSize: number
|
|
191
|
+
/** Code font size as a percentage of the message font size (legacy input). */
|
|
192
|
+
codeFontScale: number
|
|
193
|
+
/** Effective absolute code font size in px (codeFontSize, else legacy %, else stock). */
|
|
194
|
+
codeFontSize: number
|
|
195
|
+
/** Base vertical spacing (px) for the reply area ("行高"). */
|
|
196
|
+
lineHeight: number
|
|
197
|
+
tableStyle: 'default' | 'claude'
|
|
198
|
+
/** Whether the GitBar pills above the input are shown. */
|
|
199
|
+
gitBarEnabled: boolean
|
|
200
|
+
/** Whether the Archive manager sidebar entry is shown. */
|
|
201
|
+
archiveManagerEnabled: boolean
|
|
202
|
+
/** Whether the MCP manager Settings page is shown. */
|
|
203
|
+
mcpManagerEnabled: boolean
|
|
204
|
+
/** Whether the /init slash command is registered. */
|
|
205
|
+
initCommandEnabled: boolean
|
|
206
|
+
/** Whether the whale working indicator above the input is shown. */
|
|
207
|
+
whaleIndicatorEnabled: boolean
|
|
208
|
+
/** Whether the stats line's cache-hit figure keeps two decimals. */
|
|
209
|
+
preciseCacheHitEnabled: boolean
|
|
210
|
+
/** Whether task notifications (title flash / system notification / chime) are active. */
|
|
211
|
+
notificationsEnabled: boolean
|
|
212
|
+
/** Stay quiet while the page is visible and focused. */
|
|
213
|
+
notifyOnlyWhenHidden: boolean
|
|
214
|
+
/** Alert when a session ends its turn — completed, interrupted or failed. */
|
|
215
|
+
notifyOnComplete: boolean
|
|
216
|
+
/** Alert when a session blocks on an approval, plan review or question. */
|
|
217
|
+
notifyOnInteraction: boolean
|
|
218
|
+
/** Blink an unread counter into the tab title. */
|
|
219
|
+
notifyTitleFlash: boolean
|
|
220
|
+
/** Fire desktop-level Web Notifications. */
|
|
221
|
+
notifySystemNotification: boolean
|
|
222
|
+
/** Play the synthesized two-note chime. */
|
|
223
|
+
notifySound: boolean
|
|
224
|
+
/** Optional `provider:model` override for LLM commit-message generation. */
|
|
225
|
+
suggestModel?: string
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Resolve a partial config into a fully defaulted value. */
|
|
229
|
+
export function resolveConfig(config: UITweaksConfig = {}): ResolvedUITweaksConfig {
|
|
230
|
+
const fontSize = config.fontSize ?? DEFAULT_FONT_SIZE
|
|
231
|
+
const codeFontScale = config.codeFontScale ?? DEFAULT_CODE_FONT_SCALE
|
|
232
|
+
// Effective code size: the absolute px input wins; otherwise derive px from
|
|
233
|
+
// the legacy percentage at the resolved body size; otherwise stock.
|
|
234
|
+
const codeFontSize = typeof config.codeFontSize === 'number'
|
|
235
|
+
? Math.min(MAX_CODE_FONT_SIZE, Math.max(MIN_CODE_FONT_SIZE, config.codeFontSize))
|
|
236
|
+
: Math.max(8, Math.round(fontSize * (13 / 16) * (codeFontScale / DEFAULT_CODE_FONT_SCALE)))
|
|
237
|
+
const lineHeight = config.lineHeight ?? DEFAULT_LINE_HEIGHT
|
|
238
|
+
const tableStyle = config.tableStyle ?? 'default'
|
|
239
|
+
const gitBarEnabled = config.gitBarEnabled ?? DEFAULT_GITBAR_ENABLED
|
|
240
|
+
const archiveManagerEnabled = config.archiveManagerEnabled ?? DEFAULT_ARCHIVE_MANAGER_ENABLED
|
|
241
|
+
const mcpManagerEnabled = config.mcpManagerEnabled ?? DEFAULT_MCP_MANAGER_ENABLED
|
|
242
|
+
const initCommandEnabled = config.initCommandEnabled ?? DEFAULT_INIT_COMMAND_ENABLED
|
|
243
|
+
const whaleIndicatorEnabled = config.whaleIndicatorEnabled ?? DEFAULT_WHALE_INDICATOR_ENABLED
|
|
244
|
+
const preciseCacheHitEnabled = config.preciseCacheHitEnabled ?? DEFAULT_PRECISE_CACHE_HIT_ENABLED
|
|
245
|
+
const notificationsEnabled = config.notificationsEnabled ?? DEFAULT_NOTIFICATIONS_ENABLED
|
|
246
|
+
const notifyOnlyWhenHidden = config.notifyOnlyWhenHidden ?? DEFAULT_NOTIFY_ONLY_WHEN_HIDDEN
|
|
247
|
+
const notifyOnComplete = config.notifyOnComplete ?? DEFAULT_NOTIFY_ON_COMPLETE
|
|
248
|
+
const notifyOnInteraction = config.notifyOnInteraction ?? DEFAULT_NOTIFY_ON_INTERACTION
|
|
249
|
+
const notifyTitleFlash = config.notifyTitleFlash ?? DEFAULT_NOTIFY_TITLE_FLASH
|
|
250
|
+
const notifySystemNotification = config.notifySystemNotification ?? DEFAULT_NOTIFY_SYSTEM_NOTIFICATION
|
|
251
|
+
const notifySound = config.notifySound ?? DEFAULT_NOTIFY_SOUND
|
|
252
|
+
const resolved: ResolvedUITweaksConfig = { fontSize, codeFontScale, codeFontSize, lineHeight, tableStyle, gitBarEnabled, archiveManagerEnabled, mcpManagerEnabled, initCommandEnabled, whaleIndicatorEnabled, preciseCacheHitEnabled, notificationsEnabled, notifyOnlyWhenHidden, notifyOnComplete, notifyOnInteraction, notifyTitleFlash, notifySystemNotification, notifySound }
|
|
253
|
+
if (typeof config.suggestModel === 'string' && config.suggestModel !== '') {
|
|
254
|
+
resolved.suggestModel = config.suggestModel
|
|
255
|
+
}
|
|
256
|
+
return resolved
|
|
257
|
+
}
|