@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.
Files changed (329) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +554 -0
  3. package/README_EN.md +563 -0
  4. package/assets/logo.svg +1 -0
  5. package/cordis.patch.yml +79 -0
  6. package/lib/client-editor.js +55744 -0
  7. package/lib/client-mermaid.js +208256 -0
  8. package/lib/client-registry.js +30378 -0
  9. package/lib/client-terminal.js +16894 -0
  10. package/lib/client.js +30378 -0
  11. package/lib/index.js +6897 -0
  12. package/lib/invariant.js +22 -0
  13. package/lib/types/agent-opens.d.ts +65 -0
  14. package/lib/types/agent-pty.d.ts +226 -0
  15. package/lib/types/browser-probe.d.ts +15 -0
  16. package/lib/types/bundle-route.d.ts +12 -0
  17. package/lib/types/chatcode-update.d.ts +47 -0
  18. package/lib/types/client/AccountMenu.d.ts +1 -0
  19. package/lib/types/client/AiRatioSection.d.ts +3 -0
  20. package/lib/types/client/BrowserView.d.ts +39 -0
  21. package/lib/types/client/ChatcodeGate.d.ts +6 -0
  22. package/lib/types/client/CodingPlanModelsSettings.d.ts +9 -0
  23. package/lib/types/client/DiffTab.d.ts +6 -0
  24. package/lib/types/client/DiffView.d.ts +51 -0
  25. package/lib/types/client/EditorHost.d.ts +13 -0
  26. package/lib/types/client/FenceErrorNotice.d.ts +5 -0
  27. package/lib/types/client/FileTree.d.ts +41 -0
  28. package/lib/types/client/FreeWindow.d.ts +29 -0
  29. package/lib/types/client/GitView.d.ts +12 -0
  30. package/lib/types/client/MarkdownHtml.d.ts +32 -0
  31. package/lib/types/client/OrphanedTab.d.ts +11 -0
  32. package/lib/types/client/PdfView.d.ts +6 -0
  33. package/lib/types/client/ReleaseNotesDialog.d.ts +11 -0
  34. package/lib/types/client/RenderBoundary.d.ts +32 -0
  35. package/lib/types/client/SandboxStatusBar.d.ts +10 -0
  36. package/lib/types/client/SideCardSection.d.ts +74 -0
  37. package/lib/types/client/Sidebar.d.ts +8 -0
  38. package/lib/types/client/TabBar.d.ts +54 -0
  39. package/lib/types/client/TerminalView.d.ts +24 -0
  40. package/lib/types/client/TextEditor.d.ts +10 -0
  41. package/lib/types/client/TreePanel.d.ts +27 -0
  42. package/lib/types/client/UpdateDialog.d.ts +10 -0
  43. package/lib/types/client/UpdateSettingsPanel.d.ts +1 -0
  44. package/lib/types/client/UpdateSettingsSection.d.ts +10 -0
  45. package/lib/types/client/UploadOverlay.d.ts +20 -0
  46. package/lib/types/client/UsageHeatmapSection.d.ts +4 -0
  47. package/lib/types/client/add-plugin-modal.d.ts +15 -0
  48. package/lib/types/client/api.d.ts +345 -0
  49. package/lib/types/client/binary-download.d.ts +12 -0
  50. package/lib/types/client/breakpoints.d.ts +17 -0
  51. package/lib/types/client/browser.d.ts +60 -0
  52. package/lib/types/client/builtins/index.d.ts +18 -0
  53. package/lib/types/client/builtins/tabs.d.ts +11 -0
  54. package/lib/types/client/builtins/viewers.d.ts +3 -0
  55. package/lib/types/client/chat-presentation.d.ts +13 -0
  56. package/lib/types/client/chunk-loader.d.ts +124 -0
  57. package/lib/types/client/chunks/editor.d.ts +9 -0
  58. package/lib/types/client/chunks/mermaid.d.ts +10 -0
  59. package/lib/types/client/chunks/terminal.d.ts +9 -0
  60. package/lib/types/client/cm-themes.d.ts +24 -0
  61. package/lib/types/client/conversation-draft.d.ts +14 -0
  62. package/lib/types/client/desktop-env.d.ts +35 -0
  63. package/lib/types/client/editor-load.d.ts +66 -0
  64. package/lib/types/client/frame-batcher.d.ts +24 -0
  65. package/lib/types/client/icons.d.ts +66 -0
  66. package/lib/types/client/image-types.d.ts +3 -0
  67. package/lib/types/client/ime-guard.d.ts +36 -0
  68. package/lib/types/client/index.d.ts +19 -0
  69. package/lib/types/client/intercept.d.ts +44 -0
  70. package/lib/types/client/lang.d.ts +14 -0
  71. package/lib/types/client/layout-push.d.ts +15 -0
  72. package/lib/types/client/lazy-chunk.d.ts +25 -0
  73. package/lib/types/client/link-intercept.d.ts +42 -0
  74. package/lib/types/client/locales-ar.d.ts +18 -0
  75. package/lib/types/client/locales-de.d.ts +4 -0
  76. package/lib/types/client/locales-fr.d.ts +10 -0
  77. package/lib/types/client/locales-hi.d.ts +17 -0
  78. package/lib/types/client/locales-id.d.ts +15 -0
  79. package/lib/types/client/locales-it.d.ts +8 -0
  80. package/lib/types/client/locales-ja.d.ts +17 -0
  81. package/lib/types/client/locales-ko.d.ts +9 -0
  82. package/lib/types/client/locales-nl.d.ts +16 -0
  83. package/lib/types/client/locales-pl.d.ts +19 -0
  84. package/lib/types/client/locales-pt.d.ts +1 -0
  85. package/lib/types/client/locales-ru.d.ts +13 -0
  86. package/lib/types/client/locales-sv.d.ts +1 -0
  87. package/lib/types/client/locales-th.d.ts +17 -0
  88. package/lib/types/client/locales-tr.d.ts +17 -0
  89. package/lib/types/client/locales-vi.d.ts +17 -0
  90. package/lib/types/client/locales-zh-HK.d.ts +32 -0
  91. package/lib/types/client/locales-zh-MO.d.ts +32 -0
  92. package/lib/types/client/locales-zh-TW.d.ts +32 -0
  93. package/lib/types/client/locales.d.ts +571 -0
  94. package/lib/types/client/markdown-html.d.ts +96 -0
  95. package/lib/types/client/markdown-images.d.ts +45 -0
  96. package/lib/types/client/markdown-labels.d.ts +21 -0
  97. package/lib/types/client/md-toc.d.ts +4 -0
  98. package/lib/types/client/mermaid-blocks.d.ts +47 -0
  99. package/lib/types/client/mermaid-sanitize.d.ts +2 -0
  100. package/lib/types/client/mermaid.d.ts +12 -0
  101. package/lib/types/client/open-when-sized.d.ts +20 -0
  102. package/lib/types/client/open-with-settings.d.ts +4 -0
  103. package/lib/types/client/open-with.d.ts +87 -0
  104. package/lib/types/client/openpath-intercept.d.ts +52 -0
  105. package/lib/types/client/paths.d.ts +43 -0
  106. package/lib/types/client/pdf-types.d.ts +2 -0
  107. package/lib/types/client/pinned.d.ts +86 -0
  108. package/lib/types/client/plugin-settings.d.ts +8 -0
  109. package/lib/types/client/plugins-shared.d.ts +27 -0
  110. package/lib/types/client/plugins-tabs.d.ts +3 -0
  111. package/lib/types/client/plugins-viewers.d.ts +3 -0
  112. package/lib/types/client/prefs.d.ts +41 -0
  113. package/lib/types/client/produced-files.d.ts +29 -0
  114. package/lib/types/client/selection-payload.d.ts +27 -0
  115. package/lib/types/client/service.d.ts +445 -0
  116. package/lib/types/client/session-pins.d.ts +14 -0
  117. package/lib/types/client/settings-nav-icon.d.ts +27 -0
  118. package/lib/types/client/shell-presets.d.ts +48 -0
  119. package/lib/types/client/split-pane.d.ts +38 -0
  120. package/lib/types/client/state.d.ts +480 -0
  121. package/lib/types/client/tab-content-memo.d.ts +40 -0
  122. package/lib/types/client/terminal-font.d.ts +78 -0
  123. package/lib/types/client/terminal-links.d.ts +119 -0
  124. package/lib/types/client/theme.d.ts +45 -0
  125. package/lib/types/client/titlebar-strip.d.ts +23 -0
  126. package/lib/types/client/ui-tweaks/appicons.d.ts +16 -0
  127. package/lib/types/client/ui-tweaks/archive.d.ts +42 -0
  128. package/lib/types/client/ui-tweaks/branding.d.ts +45 -0
  129. package/lib/types/client/ui-tweaks/cachehit.d.ts +41 -0
  130. package/lib/types/client/ui-tweaks/gitbar.d.ts +103 -0
  131. package/lib/types/client/ui-tweaks/index.d.ts +279 -0
  132. package/lib/types/client/ui-tweaks/mcp.d.ts +28 -0
  133. package/lib/types/client/ui-tweaks/notifier.d.ts +125 -0
  134. package/lib/types/client/ui-tweaks/whale.d.ts +55 -0
  135. package/lib/types/client/upload.d.ts +69 -0
  136. package/lib/types/client/useChatcodeAccount.d.ts +10 -0
  137. package/lib/types/client/wco.d.ts +47 -0
  138. package/lib/types/codingplan.d.ts +37 -0
  139. package/lib/types/config.d.ts +95 -0
  140. package/lib/types/context-types.d.ts +540 -0
  141. package/lib/types/fs-operations.d.ts +30 -0
  142. package/lib/types/fs-search.d.ts +24 -0
  143. package/lib/types/fs-tree.d.ts +51 -0
  144. package/lib/types/git.d.ts +124 -0
  145. package/lib/types/html-route.d.ts +59 -0
  146. package/lib/types/index.d.ts +47 -0
  147. package/lib/types/invariant.d.ts +15 -0
  148. package/lib/types/login-route.d.ts +2 -0
  149. package/lib/types/open-external.d.ts +24 -0
  150. package/lib/types/path-security.d.ts +26 -0
  151. package/lib/types/prefs-shared.d.ts +257 -0
  152. package/lib/types/pty-deps.d.ts +79 -0
  153. package/lib/types/pty-manager.d.ts +168 -0
  154. package/lib/types/tools.d.ts +34 -0
  155. package/lib/types/trust-fence.d.ts +24 -0
  156. package/lib/types/ui-tweaks/archive.d.ts +71 -0
  157. package/lib/types/ui-tweaks/config.d.ts +186 -0
  158. package/lib/types/ui-tweaks/git-web.d.ts +43 -0
  159. package/lib/types/ui-tweaks/git.d.ts +311 -0
  160. package/lib/types/ui-tweaks/index.d.ts +17 -0
  161. package/lib/types/ui-tweaks/mcp.d.ts +103 -0
  162. package/lib/types/ui-tweaks/turn-outcome.d.ts +57 -0
  163. package/lib/types/ui-tweaks/web.d.ts +55 -0
  164. package/lib/types/wire.d.ts +38 -0
  165. package/package.json +213 -0
  166. package/scripts/install.ps1 +310 -0
  167. package/scripts/install.sh +287 -0
  168. package/src/agent-opens.ts +265 -0
  169. package/src/agent-pty.ts +527 -0
  170. package/src/browser-probe.ts +26 -0
  171. package/src/bundle-route.ts +129 -0
  172. package/src/chatcode-update.ts +193 -0
  173. package/src/client/AccountMenu.tsx +177 -0
  174. package/src/client/AiRatioSection.module.css +329 -0
  175. package/src/client/AiRatioSection.tsx +292 -0
  176. package/src/client/BrowserView.tsx +289 -0
  177. package/src/client/ChatcodeGate.tsx +35 -0
  178. package/src/client/CodingPlanModelsSettings.module.css +118 -0
  179. package/src/client/CodingPlanModelsSettings.tsx +101 -0
  180. package/src/client/DiffTab.tsx +114 -0
  181. package/src/client/DiffView.tsx +305 -0
  182. package/src/client/EditorHost.tsx +563 -0
  183. package/src/client/FenceErrorNotice.tsx +40 -0
  184. package/src/client/FileTree.tsx +701 -0
  185. package/src/client/FreeWindow.tsx +337 -0
  186. package/src/client/GitView.tsx +722 -0
  187. package/src/client/MarkdownHtml.tsx +296 -0
  188. package/src/client/OrphanedTab.tsx +27 -0
  189. package/src/client/PdfView.tsx +110 -0
  190. package/src/client/ReleaseNotesDialog.tsx +91 -0
  191. package/src/client/RenderBoundary.tsx +49 -0
  192. package/src/client/SandboxStatusBar.tsx +60 -0
  193. package/src/client/SideCardSection.module.css +932 -0
  194. package/src/client/SideCardSection.tsx +1169 -0
  195. package/src/client/Sidebar.tsx +1857 -0
  196. package/src/client/TabBar.tsx +365 -0
  197. package/src/client/TerminalView.tsx +424 -0
  198. package/src/client/TextEditor.tsx +468 -0
  199. package/src/client/TreePanel.tsx +279 -0
  200. package/src/client/UpdateDialog.tsx +186 -0
  201. package/src/client/UpdateSettingsPanel.tsx +29 -0
  202. package/src/client/UpdateSettingsSection.tsx +22 -0
  203. package/src/client/UploadOverlay.tsx +62 -0
  204. package/src/client/UsageHeatmapSection.module.css +440 -0
  205. package/src/client/UsageHeatmapSection.tsx +301 -0
  206. package/src/client/add-plugin-modal.tsx +199 -0
  207. package/src/client/api.ts +433 -0
  208. package/src/client/binary-download.tsx +23 -0
  209. package/src/client/breakpoints.ts +61 -0
  210. package/src/client/browser.ts +167 -0
  211. package/src/client/builtins/index.ts +37 -0
  212. package/src/client/builtins/tabs.tsx +275 -0
  213. package/src/client/builtins/viewers.tsx +125 -0
  214. package/src/client/chat-presentation.ts +95 -0
  215. package/src/client/chunk-loader.ts +350 -0
  216. package/src/client/chunks/editor.tsx +9 -0
  217. package/src/client/chunks/mermaid.tsx +10 -0
  218. package/src/client/chunks/terminal.tsx +9 -0
  219. package/src/client/cm-themes.ts +129 -0
  220. package/src/client/conversation-draft.ts +29 -0
  221. package/src/client/css-modules.d.ts +5 -0
  222. package/src/client/desktop-env.ts +72 -0
  223. package/src/client/editor-load.ts +92 -0
  224. package/src/client/frame-batcher.ts +56 -0
  225. package/src/client/icons.tsx +170 -0
  226. package/src/client/image-types.ts +8 -0
  227. package/src/client/ime-guard.ts +47 -0
  228. package/src/client/index.tsx +501 -0
  229. package/src/client/intercept.tsx +172 -0
  230. package/src/client/lang.ts +225 -0
  231. package/src/client/layout-push.ts +37 -0
  232. package/src/client/layout.css +156 -0
  233. package/src/client/lazy-chunk.tsx +89 -0
  234. package/src/client/link-intercept.ts +73 -0
  235. package/src/client/locales-ar.ts +387 -0
  236. package/src/client/locales-de.ts +372 -0
  237. package/src/client/locales-fr.ts +379 -0
  238. package/src/client/locales-hi.ts +386 -0
  239. package/src/client/locales-id.ts +384 -0
  240. package/src/client/locales-it.ts +377 -0
  241. package/src/client/locales-ja.ts +486 -0
  242. package/src/client/locales-ko.ts +378 -0
  243. package/src/client/locales-nl.ts +384 -0
  244. package/src/client/locales-pl.ts +388 -0
  245. package/src/client/locales-pt.ts +369 -0
  246. package/src/client/locales-ru.ts +384 -0
  247. package/src/client/locales-sv.ts +369 -0
  248. package/src/client/locales-th.ts +386 -0
  249. package/src/client/locales-tr.ts +386 -0
  250. package/src/client/locales-vi.ts +386 -0
  251. package/src/client/locales-zh-HK.ts +401 -0
  252. package/src/client/locales-zh-MO.ts +401 -0
  253. package/src/client/locales-zh-TW.ts +401 -0
  254. package/src/client/locales.ts +1229 -0
  255. package/src/client/markdown-html.ts +331 -0
  256. package/src/client/markdown-images.ts +137 -0
  257. package/src/client/markdown-labels.tsx +31 -0
  258. package/src/client/md-toc.tsx +127 -0
  259. package/src/client/mermaid-blocks.ts +110 -0
  260. package/src/client/mermaid-sanitize.ts +93 -0
  261. package/src/client/mermaid.tsx +406 -0
  262. package/src/client/open-when-sized.ts +43 -0
  263. package/src/client/open-with-settings.tsx +135 -0
  264. package/src/client/open-with.ts +221 -0
  265. package/src/client/openpath-intercept.ts +79 -0
  266. package/src/client/paths.ts +63 -0
  267. package/src/client/pdf-types.ts +4 -0
  268. package/src/client/pinned.ts +184 -0
  269. package/src/client/plugin-settings.ts +43 -0
  270. package/src/client/plugins-shared.ts +29 -0
  271. package/src/client/plugins-tabs.ts +153 -0
  272. package/src/client/plugins-viewers.ts +44 -0
  273. package/src/client/prefs.ts +216 -0
  274. package/src/client/produced-files.ts +120 -0
  275. package/src/client/selection-payload.ts +86 -0
  276. package/src/client/service.ts +873 -0
  277. package/src/client/session-pins.tsx +334 -0
  278. package/src/client/settings-nav-icon.ts +55 -0
  279. package/src/client/shell-presets.ts +83 -0
  280. package/src/client/sidebar.module.css +3607 -0
  281. package/src/client/split-pane.tsx +333 -0
  282. package/src/client/state.ts +1646 -0
  283. package/src/client/tab-content-memo.ts +55 -0
  284. package/src/client/terminal-font.ts +224 -0
  285. package/src/client/terminal-links.ts +194 -0
  286. package/src/client/theme.ts +110 -0
  287. package/src/client/titlebar-strip.ts +37 -0
  288. package/src/client/ui-tweaks/appicons.ts +24 -0
  289. package/src/client/ui-tweaks/archive.tsx +281 -0
  290. package/src/client/ui-tweaks/branding.tsx +232 -0
  291. package/src/client/ui-tweaks/cachehit.tsx +122 -0
  292. package/src/client/ui-tweaks/gitbar.tsx +2559 -0
  293. package/src/client/ui-tweaks/index.tsx +1696 -0
  294. package/src/client/ui-tweaks/mcp.tsx +582 -0
  295. package/src/client/ui-tweaks/notifier.ts +514 -0
  296. package/src/client/ui-tweaks/whale.tsx +171 -0
  297. package/src/client/upload.ts +187 -0
  298. package/src/client/useChatcodeAccount.ts +103 -0
  299. package/src/client/wco.ts +120 -0
  300. package/src/codingplan.ts +146 -0
  301. package/src/config.ts +207 -0
  302. package/src/context-types.ts +559 -0
  303. package/src/fs-operations.ts +97 -0
  304. package/src/fs-search.ts +113 -0
  305. package/src/fs-tree.ts +158 -0
  306. package/src/git.ts +446 -0
  307. package/src/html-route.ts +106 -0
  308. package/src/index.ts +1524 -0
  309. package/src/invariant.ts +32 -0
  310. package/src/login-route.ts +26 -0
  311. package/src/open-external.ts +90 -0
  312. package/src/path-security.ts +81 -0
  313. package/src/prefs-shared.ts +303 -0
  314. package/src/pty-deps.ts +241 -0
  315. package/src/pty-manager.ts +376 -0
  316. package/src/tools.ts +482 -0
  317. package/src/trust-fence.ts +84 -0
  318. package/src/ui-tweaks/archive.ts +293 -0
  319. package/src/ui-tweaks/config.ts +257 -0
  320. package/src/ui-tweaks/git-web.ts +421 -0
  321. package/src/ui-tweaks/git.ts +1524 -0
  322. package/src/ui-tweaks/index.ts +61 -0
  323. package/src/ui-tweaks/mcp.ts +535 -0
  324. package/src/ui-tweaks/turn-outcome.ts +105 -0
  325. package/src/ui-tweaks/web.ts +225 -0
  326. package/src/wire.ts +110 -0
  327. package/vendor/addon-fit.js +2 -0
  328. package/vendor/xterm.css +218 -0
  329. 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
+ }