@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,37 @@
1
+ /**
2
+ * Built-in registration: the plugin registers its own tab pages and file
3
+ * previewers through the same {@link BetterSidebarService} external plugins
4
+ * use — eating its own dogfood. The descriptors live next to their feature
5
+ * modules (tabs.tsx / viewers.tsx); this module only aggregates them and
6
+ * owns the disposer lifecycle (cordis auto-invokes it on fiber disposal,
7
+ * HMR-safe).
8
+ */
9
+ import type { Context } from '../../context-types.ts'
10
+ import type { BetterSidebarService } from '../service.ts'
11
+ import { builtinTabs, type BuiltinTabOptions } from './tabs.tsx'
12
+ import { builtinViewers } from './viewers.tsx'
13
+
14
+ /**
15
+ * Register all built-in tabs and viewers with the service. Returns a
16
+ * disposer that unregisters everything (cordis auto-invokes it on fiber
17
+ * disposal). The `ctx` is threaded into tab descriptors that need it
18
+ * (EditorHost reads `ctx.betterSidebar` for file-viewer matching).
19
+ */
20
+ export function registerBuiltins(
21
+ ctx: Context,
22
+ service: BetterSidebarService,
23
+ options: BuiltinTabOptions = {},
24
+ ): () => void {
25
+ const disposers: (() => void)[] = []
26
+ for (const tab of builtinTabs(ctx, options)) {
27
+ disposers.push(service.registerTab(tab))
28
+ }
29
+ for (const viewer of builtinViewers()) {
30
+ disposers.push(service.registerFileViewer(viewer))
31
+ }
32
+ return () => {
33
+ for (const d of disposers) {
34
+ try { d() } catch { /* already disposed */ }
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,275 @@
1
+ /**
2
+ * The 7 built-in tab descriptors: the plugin registers its own pages
3
+ * (editor / git / terminal / browser / diff) through
4
+ * the same {@link BetterSidebarService} external plugins use — eating its
5
+ * own dogfood. The terminal descriptor owns its quota (`TERMINAL_LIMIT`)
6
+ * and mints `terminal:<uuid>` ids through `createTab`; the browser mints
7
+ * `browser:<n>` the same way (no quota). The editor IS the files window
8
+ * (the old standalone explorer merged into it).
9
+ */
10
+ import { IconBranchOutline16, IconCodeOutline16, IconFolderOpen16, IconPanelLeftOutline16 } from '@deepseek-ai/dsh-client-ui-primitives'
11
+ import type { Context } from '../../context-types.ts'
12
+ import { allLeaves, isAgentTabId, type SidebarState } from '../state.ts'
13
+ import { t } from '../locales.ts'
14
+ import { openSidebarFile } from '../intercept.tsx'
15
+ import { EditorHost } from '../EditorHost.tsx'
16
+ import { OpenWithSettings } from '../open-with-settings.tsx'
17
+ import { lazyChunkComponent } from '../lazy-chunk.tsx'
18
+ import { GitView } from '../GitView.tsx'
19
+ import { DiffTab } from '../DiffTab.tsx'
20
+ import { BrowserView } from '../BrowserView.tsx'
21
+ import { IconTerminalOutline16, IconDiffOutline16, IconGlobeOutline16 } from '../icons.tsx'
22
+ import { TERMINAL_FONT_SIZE_MAX, TERMINAL_FONT_SIZE_MIN } from '../../prefs-shared.ts'
23
+ import type { ComponentType } from 'react'
24
+ import type { SessionScope } from '../api.ts'
25
+ import type { SidebarStore } from '../state.ts'
26
+ import type { TabDescriptor } from '../service.ts'
27
+
28
+ /**
29
+ * Lazy wrapper over the terminal view: xterm (and its stylesheet) is fetched
30
+ * only when a terminal tab is first opened (see chunk-loader.ts). The
31
+ * wrapper keeps the descriptor contract `(props) => ReactNode` — Sidebar
32
+ * calls it as a plain function.
33
+ *
34
+ * TerminalView's props are { scope, tabId, store } — `tabId` is NOT part of
35
+ * TabComponentProps (it carries `tab: SidebarTab` instead), so the
36
+ * descriptor maps it explicitly; a bare pass-through would leave tabId
37
+ * undefined and TerminalView's isAgentTabId(tabId) would crash on
38
+ * `undefined.startsWith` (regression-pinned in tests/lazy-chunk.spec.tsx).
39
+ */
40
+ const LazyTerminal = lazyChunkComponent<TerminalViewProps>(
41
+ 'terminal',
42
+ (mod) => mod.TerminalView as ComponentType<TerminalViewProps> | undefined,
43
+ )
44
+
45
+ /** The terminal view's props (mirror of TerminalView's own signature). */
46
+ interface TerminalViewProps {
47
+ scope: SessionScope
48
+ tabId: string
49
+ store: SidebarStore
50
+ }
51
+
52
+ /** How many UI-owned terminals may be open at once (agent-owned ones are uncapped). */
53
+ export const TERMINAL_LIMIT = 3
54
+
55
+ /** Optional per-registration builtin behavior (currently terminal title). */
56
+ export interface BuiltinTabOptions {
57
+ /** Returns the display title for newly opened terminal tabs. */
58
+ terminalTitle?: () => string
59
+ }
60
+
61
+ /** A client-side uuid for terminal tab identity (not shown in the UI). */
62
+ function terminalUuid(): string {
63
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
64
+ return crypto.randomUUID()
65
+ }
66
+ return `t${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`
67
+ }
68
+
69
+ /** Count UI-owned terminals (agent:` tabs excluded — they are the model's). */
70
+ function uiTerminalCount(state: SidebarState): number {
71
+ return allLeaves(state.splits)
72
+ .flatMap(leaf => leaf.tabs)
73
+ .filter(tab => tab.type === 'terminal' && !isAgentTabId(tab.id)).length
74
+ }
75
+
76
+ /** The 5 built-in tab descriptors. */
77
+ export function builtinTabs(ctx: Context, options: BuiltinTabOptions = {}): readonly TabDescriptor[] {
78
+ return [
79
+ {
80
+ id: 'editor',
81
+ // The single files window: an editor tab with no path IS the file
82
+ // explorer (empty hint + docked tree); with a path it previews/edits
83
+ // the file. Visible in the + menu in the explorer's old slot.
84
+ title: () => t('files'),
85
+ icon: (size: number) => <IconFolderOpen16 size={size} />,
86
+ order: 10,
87
+ hidden: false,
88
+ dedupeKey: (tab) => tab.path,
89
+ // Declarative settings: the file-open behavior picker (in-place switch
90
+ // vs per-path windows) renders as an iconed select row under the
91
+ // editor card's gear in the Side card settings page, followed by the
92
+ // workspace fence switch (the host's containment guard over every
93
+ // sidebar filesystem route); the "open with" configuration (SSH host +
94
+ // custom editors) is the custom panel BELOW those rows — the settings
95
+ // seam renders rows first, custom panel after.
96
+ settings: {
97
+ toggles: [{
98
+ key: 'editorExplorer',
99
+ type: 'select',
100
+ title: () => t('editorExplorer'),
101
+ desc: () => t('editorExplorerDesc'),
102
+ options: [
103
+ {
104
+ value: true,
105
+ icon: (size: number) => <IconPanelLeftOutline16 size={size} />,
106
+ title: () => t('editorExplorerMerged'),
107
+ desc: () => t('editorExplorerMergedDesc'),
108
+ },
109
+ {
110
+ value: false,
111
+ icon: (size: number) => <IconCodeOutline16 size={size} />,
112
+ title: () => t('editorExplorerSplit'),
113
+ desc: () => t('editorExplorerSplitDesc'),
114
+ },
115
+ ],
116
+ }, {
117
+ key: 'workspaceFence',
118
+ title: () => t('settingsFenceTitle'),
119
+ desc: () => t('settingsFenceDesc'),
120
+ }],
121
+ render: ({ pluginSettings, updatePluginSetting }) => (
122
+ <OpenWithSettings pluginSettings={pluginSettings} updatePluginSetting={updatePluginSetting} />
123
+ ),
124
+ },
125
+ component: ({ ctx, store, scope, tab, expanded, revealed, onToggleDir, onReferenceFile }) => (
126
+ <EditorHost
127
+ ctx={ctx}
128
+ store={store}
129
+ scope={scope}
130
+ tab={tab}
131
+ expanded={expanded ?? []}
132
+ revealed={revealed ?? []}
133
+ onToggleDir={onToggleDir ?? (() => { /* no-op */ })}
134
+ onReferenceFile={onReferenceFile ?? (() => { /* no-op */ })}
135
+ />
136
+ ),
137
+ },
138
+ {
139
+ id: 'git',
140
+ title: () => t('git'),
141
+ icon: (size: number) => <IconBranchOutline16 size={size} />,
142
+ order: 20,
143
+ single: true,
144
+ component: ({ ctx, store, scope, visible, onOpenDiff }) => (
145
+ <GitView
146
+ scope={scope}
147
+ store={store}
148
+ visible={visible}
149
+ onOpenFile={(path) => { openSidebarFile(ctx, store, scope.sessionId, path) }}
150
+ onOpenDiff={onOpenDiff ?? (() => { /* no-op */ })}
151
+ />
152
+ ),
153
+ },
154
+ {
155
+ id: 'terminal',
156
+ title: () => t('terminal'),
157
+ icon: (size: number) => <IconTerminalOutline16 size={size} />,
158
+ order: 40,
159
+ available: (_ctx, _scope, state) => uiTerminalCount(state) < TERMINAL_LIMIT,
160
+ // Declarative settings: the model-facing terminal tools switch, the
161
+ // bottom-panel first-expansion auto-terminal switch, and the custom
162
+ // font family/size rows render under this card in the Side card
163
+ // settings page (the host gates the toolset on the tools one
164
+ // independently; the font rows apply live to every terminal).
165
+ settings: {
166
+ toggles: [{
167
+ key: 'agentTerminalTools',
168
+ title: () => t('settingsToolsTitle'),
169
+ desc: () => t('settingsToolsDesc'),
170
+ }, {
171
+ key: 'bottomPanelAutoTerminal',
172
+ title: () => t('settingsBottomTerminalTitle'),
173
+ desc: () => t('settingsBottomTerminalDesc'),
174
+ }, {
175
+ key: 'terminalShell',
176
+ type: 'text',
177
+ title: () => t('settingsShellTitle'),
178
+ desc: () => t('settingsShellDesc'),
179
+ placeholder: t('settingsShellPlaceholder'),
180
+ }, {
181
+ key: 'terminalShellArgs',
182
+ type: 'text',
183
+ title: () => t('settingsShellArgsTitle'),
184
+ desc: () => t('settingsShellArgsDesc'),
185
+ placeholder: t('settingsShellArgsPlaceholder'),
186
+ }, {
187
+ key: 'terminalFontFamily',
188
+ type: 'text',
189
+ title: () => t('settingsFontFamilyTitle'),
190
+ desc: () => t('settingsFontFamilyDesc'),
191
+ placeholder: t('settingsFontFamilyPlaceholder'),
192
+ }, {
193
+ key: 'terminalFontSize',
194
+ type: 'number',
195
+ title: () => t('settingsFontSizeTitle'),
196
+ desc: () => t('settingsFontSizeDesc'),
197
+ min: TERMINAL_FONT_SIZE_MIN,
198
+ max: TERMINAL_FONT_SIZE_MAX,
199
+ unit: 'px',
200
+ }],
201
+ },
202
+ createTab: (state) => {
203
+ const count = uiTerminalCount(state)
204
+ if (count >= TERMINAL_LIMIT) return null
205
+ return {
206
+ tab: {
207
+ id: `terminal:${terminalUuid()}`,
208
+ type: 'terminal',
209
+ title: options.terminalTitle?.() ?? t('terminal'),
210
+ },
211
+ // Keep the legacy counter advancing for compatibility with older
212
+ // persisted states; new ids no longer use it.
213
+ patch: { nextTerminal: state.nextTerminal + 1 },
214
+ }
215
+ },
216
+ component: ({ tab, scope, store }) => <LazyTerminal scope={scope} store={store} tabId={tab.id} />,
217
+ },
218
+ {
219
+ id: 'browser',
220
+ title: () => t('browser'),
221
+ icon: (size: number) => <IconGlobeOutline16 size={size} />,
222
+ order: 50,
223
+ // Declarative settings: the sandbox escape hatch, the link-takeover
224
+ // MASTER switch, and the per-protocol takeover switches (http on /
225
+ // https off by default) render under this tab's row in the Side card
226
+ // settings page (the sandbox one is warned on).
227
+ settings: {
228
+ toggles: [{
229
+ key: 'browserNoSandbox',
230
+ title: () => t('settingsBrowserSandboxTitle'),
231
+ desc: () => t('settingsBrowserSandboxDesc'),
232
+ }, {
233
+ key: 'browserInterceptLinks',
234
+ title: () => t('settingsBrowserLinksTitle'),
235
+ desc: () => t('settingsBrowserLinksDesc'),
236
+ }, {
237
+ key: 'browserInterceptHttp',
238
+ title: () => t('settingsBrowserHttpTitle'),
239
+ desc: () => t('settingsBrowserHttpDesc'),
240
+ }, {
241
+ key: 'browserInterceptHttps',
242
+ title: () => t('settingsBrowserHttpsTitle'),
243
+ desc: () => t('settingsBrowserHttpsDesc'),
244
+ }, {
245
+ key: 'browserAllowedLoopback',
246
+ type: 'text',
247
+ title: () => t('settingsBrowserLoopbackTitle'),
248
+ desc: () => t('settingsBrowserLoopbackDesc'),
249
+ placeholder: t('settingsBrowserLoopbackPlaceholder'),
250
+ }],
251
+ },
252
+ createTab: (state) => ({
253
+ tab: {
254
+ id: `browser:${state.nextBrowser}`,
255
+ type: 'browser',
256
+ title: t('browser'),
257
+ },
258
+ patch: { nextBrowser: state.nextBrowser + 1 },
259
+ }),
260
+ component: (props) => <BrowserView {...props} />,
261
+ },
262
+ {
263
+ id: 'diff',
264
+ title: () => t('git'),
265
+ icon: (size: number) => <IconDiffOutline16 size={size} />,
266
+ order: -1,
267
+ hidden: true,
268
+ dedupeKey: (tab) => tab.id,
269
+ component: ({ scope, tab }) => (
270
+ tab.diff === undefined ? null
271
+ : <DiffTab sessionId={scope.sessionId} cwd={scope.cwd} diff={tab.diff} />
272
+ ),
273
+ },
274
+ ]
275
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The 6 built-in file viewer descriptors: every preview surface is a
3
+ * registered viewer (image / pdf / markdown / html / code /
4
+ * binary-download), exactly like external plugins register theirs. Office
5
+ * previews (.docx / .xlsx / .pptx) are NOT built in anymore — they moved to
6
+ * the recommended office plugin (see plugins-viewers.ts), which registers
7
+ * the same ids through this service.
8
+ *
9
+ * The `binary-download` viewer sniffs NUL bytes via `detect` for unknown
10
+ * binaries and serves legacy doc/xls/ppt by extension; `code` is the
11
+ * catch-all (`exts: []`, lowest priority) that claims any file no other
12
+ * viewer did.
13
+ *
14
+ * The heavy viewers (the CodeMirror-backed markdown/html/code) render
15
+ * through {@link lazyChunkComponent} wrappers — their libraries are fetched
16
+ * only when such a file is first opened (see chunk-loader.ts). The
17
+ * descriptor metadata (id/exts/priority/detect) is identical either way,
18
+ * so matching semantics and external-plugin overrides are unaffected; the
19
+ * `component` wrapper keeps the descriptor contract `(props) => ReactNode`.
20
+ *
21
+ * Every viewer carries the declarative settings-surface fields — `title`
22
+ * and `icon` — so the Side card settings page can render the enable/disable
23
+ * inventory without hardcoding (eating our own dogfood).
24
+ */
25
+ import { IconCodeOutline16, IconDownloadOutline16 } from '@deepseek-ai/dsh-client-ui-primitives'
26
+ import { lazyChunkComponent } from '../lazy-chunk.tsx'
27
+ import { PdfView } from '../PdfView.tsx'
28
+ import { BinaryDownload } from '../binary-download.tsx'
29
+ import {
30
+ IconImageOutline16,
31
+ IconMarkdownOutline16,
32
+ IconPdfOutline16,
33
+ IconHtmlOutline16,
34
+ } from '../icons.tsx'
35
+ import type { ComponentType } from 'react'
36
+ import type { FileViewerDescriptor, FileViewerProps } from '../service.ts'
37
+ import { t } from '../locales.ts'
38
+ import css from '../sidebar.module.css'
39
+
40
+ /**
41
+ * Lazy wrapper over the chunk-resident viewer component. The `pick`
42
+ * function is module-level (stable identity — the wrapper effect depends
43
+ * on it); the cast bridges the chunk exports record to the descriptor prop
44
+ * shape (the view reads only its own subset of FileViewerProps).
45
+ */
46
+ const LazyTextEditor = lazyChunkComponent<FileViewerProps>('editor', (mod) => mod.TextEditor as ComponentType<FileViewerProps> | undefined)
47
+
48
+ /** The 6 built-in file viewer descriptors. */
49
+ export function builtinViewers(): readonly FileViewerDescriptor[] {
50
+ return [
51
+ {
52
+ id: 'image',
53
+ title: () => t('viewerImage'),
54
+ icon: (size: number) => <IconImageOutline16 size={size} />,
55
+ exts: ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif'],
56
+ fetchStrategy: 'mediaUrl',
57
+ component: ({ mediaUrl: url, title }) => (
58
+ <div className={css.editorImageWrap}>
59
+ <img className={css.editorImage} src={url} alt={title} />
60
+ </div>
61
+ ),
62
+ },
63
+ {
64
+ id: 'pdf',
65
+ title: () => t('viewerPdf'),
66
+ icon: (size: number) => <IconPdfOutline16 size={size} />,
67
+ exts: ['pdf'],
68
+ fetchStrategy: 'mediaUrl',
69
+ component: ({ scope, path, title }) => (
70
+ <PdfView scope={scope} path={path} title={title} />
71
+ ),
72
+ },
73
+ {
74
+ id: 'markdown',
75
+ title: () => t('viewerMarkdown'),
76
+ icon: (size: number) => <IconMarkdownOutline16 size={size} />,
77
+ exts: ['md', 'markdown'],
78
+ fetchStrategy: 'fsRead',
79
+ component: (props) => <LazyTextEditor {...props} />,
80
+ },
81
+ {
82
+ id: 'html',
83
+ title: () => t('viewerHtml'),
84
+ icon: (size: number) => <IconHtmlOutline16 size={size} />,
85
+ exts: ['html', 'htm'],
86
+ fetchStrategy: 'fsRead',
87
+ // Declarative settings: the sandbox escape hatch and the default-unsafe
88
+ // start state render under this viewer's row in the Side card settings
89
+ // page (both warned on).
90
+ settings: {
91
+ toggles: [{
92
+ key: 'htmlViewerNoSandbox',
93
+ title: () => t('settingsHtmlSandboxTitle'),
94
+ desc: () => t('settingsHtmlSandboxDesc'),
95
+ }, {
96
+ key: 'htmlViewerDefaultUnsafe',
97
+ title: () => t('settingsHtmlDefaultUnsafeTitle'),
98
+ desc: () => t('settingsHtmlDefaultUnsafeDesc'),
99
+ }],
100
+ },
101
+ component: (props) => <LazyTextEditor {...props} />,
102
+ },
103
+ {
104
+ id: 'code',
105
+ title: () => t('viewerCode'),
106
+ icon: (size: number) => <IconCodeOutline16 size={size} />,
107
+ exts: [],
108
+ priority: -100,
109
+ fetchStrategy: 'fsRead',
110
+ component: (props) => <LazyTextEditor {...props} />,
111
+ },
112
+ {
113
+ id: 'binary-download',
114
+ title: () => t('viewerBinary'),
115
+ icon: (size: number) => <IconDownloadOutline16 size={size} />,
116
+ exts: ['doc', 'xls', 'ppt'],
117
+ priority: -50,
118
+ fetchStrategy: 'binary-download',
119
+ // NUL probe: a file whose head bytes contain a NUL is binary — claimed
120
+ // before the catch-all code viewer on the head re-match.
121
+ detect: (_path, head) => head.includes(0),
122
+ component: ({ scope, path }) => <BinaryDownload scope={scope} path={path} />,
123
+ },
124
+ ]
125
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Narrow compatibility polish for the host Chat surface.
3
+ *
4
+ * DSH does not currently expose extension seats for the running-status copy
5
+ * or for suppressing one producer's context row. Keep the adaptation at the
6
+ * rendered DOM boundary, keyed by stable semantics (`role=status` and the
7
+ * context producer marker), and make every mutation reversible on unload.
8
+ */
9
+
10
+ const RUNTIME_CONTEXT_SOURCE = '@deepseek-ai/dsh-system-prompt'
11
+ const STATUS_SELECTOR = '[role="status"]'
12
+ const CONTEXT_SOURCE_SELECTOR = '[data-context-source]'
13
+ const HIDDEN_CONTEXT_ATTRIBUTE = 'data-dsh-hidden-runtime-context'
14
+
15
+ const THINKING_COPY = new Map([
16
+ ['深度求索中...', '思考中...'],
17
+ ['Deep diving...', 'Thinking...'],
18
+ ])
19
+
20
+ interface RewrittenStatusText {
21
+ readonly from: string
22
+ readonly to: string
23
+ }
24
+
25
+ function rewriteThinkingStatus(status: Element, rewritten: Map<Text, RewrittenStatusText>): void {
26
+ const pending = [...status.childNodes]
27
+ while (pending.length > 0) {
28
+ const node = pending.pop()!
29
+ if (node.nodeType === 3) {
30
+ const text = node as Text
31
+ const value = node.nodeValue ?? ''
32
+ for (const [from, to] of THINKING_COPY) {
33
+ if (!value.startsWith(from)) continue
34
+ rewritten.set(text, { from, to })
35
+ node.nodeValue = `${to}${value.slice(from.length)}`
36
+ }
37
+ continue
38
+ }
39
+ pending.push(...node.childNodes)
40
+ }
41
+ }
42
+
43
+ function hideRuntimeContext(source: Element, marked: Set<HTMLElement>): void {
44
+ if (source.textContent?.trim() !== RUNTIME_CONTEXT_SOURCE) return
45
+ const row = source.closest<HTMLElement>('[data-disclosure-row]')?.parentElement
46
+ if (row === null || row === undefined) return
47
+ row.setAttribute(HIDDEN_CONTEXT_ATTRIBUTE, '')
48
+ marked.add(row)
49
+ }
50
+
51
+ function reconcileNode(
52
+ node: Node,
53
+ marked: Set<HTMLElement>,
54
+ rewritten: Map<Text, RewrittenStatusText>,
55
+ ): void {
56
+ const element = node.nodeType === 1 ? node as Element : node.parentElement
57
+ if (element === null) return
58
+
59
+ const containingStatus = element.closest(STATUS_SELECTOR)
60
+ if (containingStatus !== null) rewriteThinkingStatus(containingStatus, rewritten)
61
+ if (element.matches(STATUS_SELECTOR)) rewriteThinkingStatus(element, rewritten)
62
+ for (const status of element.querySelectorAll(STATUS_SELECTOR)) rewriteThinkingStatus(status, rewritten)
63
+
64
+ const containingSource = element.closest(CONTEXT_SOURCE_SELECTOR)
65
+ if (containingSource !== null) hideRuntimeContext(containingSource, marked)
66
+ if (element.matches(CONTEXT_SOURCE_SELECTOR)) hideRuntimeContext(element, marked)
67
+ for (const source of element.querySelectorAll(CONTEXT_SOURCE_SELECTOR)) hideRuntimeContext(source, marked)
68
+ }
69
+
70
+ /**
71
+ * Apply the Chat presentation compatibility layer and observe late React
72
+ * mounts/updates. The disposer restores every row hidden by this activation.
73
+ */
74
+ export function registerChatPresentationCompatibility(): () => void {
75
+ const marked = new Set<HTMLElement>()
76
+ const rewritten = new Map<Text, RewrittenStatusText>()
77
+ reconcileNode(document.body, marked, rewritten)
78
+ const observer = new MutationObserver((records) => {
79
+ for (const record of records) {
80
+ if (record.type === 'characterData') reconcileNode(record.target, marked, rewritten)
81
+ for (const node of record.addedNodes) reconcileNode(node, marked, rewritten)
82
+ }
83
+ })
84
+ observer.observe(document.body, { childList: true, subtree: true, characterData: true })
85
+ return () => {
86
+ observer.disconnect()
87
+ for (const row of marked) row.removeAttribute(HIDDEN_CONTEXT_ATTRIBUTE)
88
+ for (const [text, copy] of rewritten) {
89
+ const value = text.nodeValue ?? ''
90
+ if (value.startsWith(copy.to)) text.nodeValue = `${copy.from}${value.slice(copy.to.length)}`
91
+ }
92
+ marked.clear()
93
+ rewritten.clear()
94
+ }
95
+ }