dsh-coding-sidebar 1.0.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 (277) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +524 -0
  3. package/cordis.patch.yml +49 -0
  4. package/lib/client-editor.js +48229 -0
  5. package/lib/client-mermaid.js +200745 -0
  6. package/lib/client-registry.js +21867 -0
  7. package/lib/client-terminal.js +9361 -0
  8. package/lib/client.js +21867 -0
  9. package/lib/index.js +4556 -0
  10. package/lib/invariant.js +22 -0
  11. package/lib/types/agent-opens.d.ts +65 -0
  12. package/lib/types/agent-pty.d.ts +226 -0
  13. package/lib/types/browser-probe.d.ts +15 -0
  14. package/lib/types/bundle-route.d.ts +12 -0
  15. package/lib/types/client/BrowserView.d.ts +39 -0
  16. package/lib/types/client/DiffTab.d.ts +6 -0
  17. package/lib/types/client/DiffView.d.ts +51 -0
  18. package/lib/types/client/EditorHost.d.ts +13 -0
  19. package/lib/types/client/FileTree.d.ts +38 -0
  20. package/lib/types/client/FreeWindow.d.ts +29 -0
  21. package/lib/types/client/GitView.d.ts +10 -0
  22. package/lib/types/client/MarkdownHtml.d.ts +32 -0
  23. package/lib/types/client/OrphanedTab.d.ts +11 -0
  24. package/lib/types/client/PdfView.d.ts +6 -0
  25. package/lib/types/client/RenderBoundary.d.ts +32 -0
  26. package/lib/types/client/SandboxStatusBar.d.ts +10 -0
  27. package/lib/types/client/SideCardSection.d.ts +74 -0
  28. package/lib/types/client/SideChatView.d.ts +16 -0
  29. package/lib/types/client/Sidebar.d.ts +6 -0
  30. package/lib/types/client/SubagentView.d.ts +16 -0
  31. package/lib/types/client/TabBar.d.ts +54 -0
  32. package/lib/types/client/TerminalView.d.ts +24 -0
  33. package/lib/types/client/TextEditor.d.ts +10 -0
  34. package/lib/types/client/TreePanel.d.ts +24 -0
  35. package/lib/types/client/UploadOverlay.d.ts +20 -0
  36. package/lib/types/client/add-plugin-modal.d.ts +15 -0
  37. package/lib/types/client/api.d.ts +275 -0
  38. package/lib/types/client/binary-download.d.ts +12 -0
  39. package/lib/types/client/breakpoints.d.ts +17 -0
  40. package/lib/types/client/browser.d.ts +60 -0
  41. package/lib/types/client/builtins/index.d.ts +18 -0
  42. package/lib/types/client/builtins/tabs.d.ts +11 -0
  43. package/lib/types/client/builtins/viewers.d.ts +3 -0
  44. package/lib/types/client/chunk-loader.d.ts +131 -0
  45. package/lib/types/client/chunks/editor.d.ts +9 -0
  46. package/lib/types/client/chunks/mermaid.d.ts +10 -0
  47. package/lib/types/client/chunks/terminal.d.ts +9 -0
  48. package/lib/types/client/cm-themes.d.ts +24 -0
  49. package/lib/types/client/conversation-draft.d.ts +14 -0
  50. package/lib/types/client/desktop-env.d.ts +35 -0
  51. package/lib/types/client/editor-load.d.ts +66 -0
  52. package/lib/types/client/frame-batcher.d.ts +24 -0
  53. package/lib/types/client/icons.d.ts +59 -0
  54. package/lib/types/client/image-types.d.ts +3 -0
  55. package/lib/types/client/ime-guard.d.ts +36 -0
  56. package/lib/types/client/index.d.ts +25 -0
  57. package/lib/types/client/intercept.d.ts +46 -0
  58. package/lib/types/client/lang.d.ts +14 -0
  59. package/lib/types/client/layout-push.d.ts +15 -0
  60. package/lib/types/client/lazy-chunk.d.ts +25 -0
  61. package/lib/types/client/link-intercept.d.ts +42 -0
  62. package/lib/types/client/locales-ar.d.ts +18 -0
  63. package/lib/types/client/locales-de.d.ts +4 -0
  64. package/lib/types/client/locales-fr.d.ts +10 -0
  65. package/lib/types/client/locales-hi.d.ts +17 -0
  66. package/lib/types/client/locales-id.d.ts +15 -0
  67. package/lib/types/client/locales-it.d.ts +8 -0
  68. package/lib/types/client/locales-ja.d.ts +17 -0
  69. package/lib/types/client/locales-ko.d.ts +9 -0
  70. package/lib/types/client/locales-nl.d.ts +16 -0
  71. package/lib/types/client/locales-pl.d.ts +19 -0
  72. package/lib/types/client/locales-pt.d.ts +1 -0
  73. package/lib/types/client/locales-ru.d.ts +13 -0
  74. package/lib/types/client/locales-sv.d.ts +1 -0
  75. package/lib/types/client/locales-th.d.ts +17 -0
  76. package/lib/types/client/locales-tr.d.ts +17 -0
  77. package/lib/types/client/locales-vi.d.ts +17 -0
  78. package/lib/types/client/locales-zh-HK.d.ts +32 -0
  79. package/lib/types/client/locales-zh-MO.d.ts +32 -0
  80. package/lib/types/client/locales-zh-TW.d.ts +32 -0
  81. package/lib/types/client/locales.d.ts +452 -0
  82. package/lib/types/client/markdown-html.d.ts +96 -0
  83. package/lib/types/client/markdown-images.d.ts +45 -0
  84. package/lib/types/client/markdown-labels.d.ts +38 -0
  85. package/lib/types/client/md-toc.d.ts +4 -0
  86. package/lib/types/client/mermaid-blocks.d.ts +47 -0
  87. package/lib/types/client/mermaid-sanitize.d.ts +2 -0
  88. package/lib/types/client/mermaid.d.ts +12 -0
  89. package/lib/types/client/open-when-sized.d.ts +20 -0
  90. package/lib/types/client/open-with-settings.d.ts +4 -0
  91. package/lib/types/client/open-with.d.ts +87 -0
  92. package/lib/types/client/openpath-intercept.d.ts +85 -0
  93. package/lib/types/client/paths.d.ts +43 -0
  94. package/lib/types/client/pdf-types.d.ts +2 -0
  95. package/lib/types/client/pinned.d.ts +86 -0
  96. package/lib/types/client/plugin-settings.d.ts +8 -0
  97. package/lib/types/client/plugins-shared.d.ts +27 -0
  98. package/lib/types/client/plugins-tabs.d.ts +3 -0
  99. package/lib/types/client/plugins-viewers.d.ts +3 -0
  100. package/lib/types/client/prefs.d.ts +41 -0
  101. package/lib/types/client/produced-files.d.ts +29 -0
  102. package/lib/types/client/selection-payload.d.ts +27 -0
  103. package/lib/types/client/service.d.ts +439 -0
  104. package/lib/types/client/settings-nav-icon.d.ts +19 -0
  105. package/lib/types/client/shell-presets.d.ts +48 -0
  106. package/lib/types/client/sidechat-transcript.d.ts +103 -0
  107. package/lib/types/client/split-pane.d.ts +36 -0
  108. package/lib/types/client/state.d.ts +432 -0
  109. package/lib/types/client/subagent-detect.d.ts +54 -0
  110. package/lib/types/client/subagent-jobs.d.ts +63 -0
  111. package/lib/types/client/tab-content-memo.d.ts +40 -0
  112. package/lib/types/client/terminal-font.d.ts +78 -0
  113. package/lib/types/client/terminal-links.d.ts +119 -0
  114. package/lib/types/client/theme.d.ts +45 -0
  115. package/lib/types/client/titlebar-strip.d.ts +23 -0
  116. package/lib/types/client/upload.d.ts +69 -0
  117. package/lib/types/client/wco.d.ts +47 -0
  118. package/lib/types/config.d.ts +64 -0
  119. package/lib/types/context-types.d.ts +605 -0
  120. package/lib/types/fs-operations.d.ts +28 -0
  121. package/lib/types/fs-search.d.ts +24 -0
  122. package/lib/types/fs-tree.d.ts +51 -0
  123. package/lib/types/git.d.ts +124 -0
  124. package/lib/types/html-route.d.ts +59 -0
  125. package/lib/types/index.d.ts +47 -0
  126. package/lib/types/invariant.d.ts +15 -0
  127. package/lib/types/jobs-routes.d.ts +45 -0
  128. package/lib/types/open-external.d.ts +24 -0
  129. package/lib/types/path-security.d.ts +20 -0
  130. package/lib/types/prefs-shared.d.ts +236 -0
  131. package/lib/types/pty-deps.d.ts +78 -0
  132. package/lib/types/pty-manager.d.ts +168 -0
  133. package/lib/types/sidechat-core.d.ts +172 -0
  134. package/lib/types/sidechat-routes.d.ts +30 -0
  135. package/lib/types/subagent-activity.d.ts +44 -0
  136. package/lib/types/subagent-live-route.d.ts +44 -0
  137. package/lib/types/tools.d.ts +34 -0
  138. package/lib/types/trust-fence.d.ts +24 -0
  139. package/lib/types/wire.d.ts +38 -0
  140. package/package.json +172 -0
  141. package/scripts/install.ps1 +306 -0
  142. package/scripts/install.sh +287 -0
  143. package/src/agent-opens.ts +265 -0
  144. package/src/agent-pty.ts +527 -0
  145. package/src/browser-probe.ts +26 -0
  146. package/src/bundle-route.ts +129 -0
  147. package/src/client/BrowserView.tsx +289 -0
  148. package/src/client/DiffTab.tsx +114 -0
  149. package/src/client/DiffView.tsx +305 -0
  150. package/src/client/EditorHost.tsx +558 -0
  151. package/src/client/FileTree.tsx +669 -0
  152. package/src/client/FreeWindow.tsx +337 -0
  153. package/src/client/GitView.tsx +717 -0
  154. package/src/client/MarkdownHtml.tsx +296 -0
  155. package/src/client/OrphanedTab.tsx +27 -0
  156. package/src/client/PdfView.tsx +110 -0
  157. package/src/client/RenderBoundary.tsx +49 -0
  158. package/src/client/SandboxStatusBar.tsx +60 -0
  159. package/src/client/SideCardSection.module.css +960 -0
  160. package/src/client/SideCardSection.tsx +1163 -0
  161. package/src/client/SideChatView.module.css +437 -0
  162. package/src/client/SideChatView.tsx +645 -0
  163. package/src/client/Sidebar.tsx +1370 -0
  164. package/src/client/SubagentView.module.css +547 -0
  165. package/src/client/SubagentView.tsx +883 -0
  166. package/src/client/TabBar.tsx +365 -0
  167. package/src/client/TerminalView.tsx +424 -0
  168. package/src/client/TextEditor.tsx +468 -0
  169. package/src/client/TreePanel.tsx +275 -0
  170. package/src/client/UploadOverlay.tsx +62 -0
  171. package/src/client/add-plugin-modal.tsx +199 -0
  172. package/src/client/api.ts +357 -0
  173. package/src/client/binary-download.tsx +23 -0
  174. package/src/client/breakpoints.ts +61 -0
  175. package/src/client/browser.ts +167 -0
  176. package/src/client/builtins/index.ts +37 -0
  177. package/src/client/builtins/tabs.tsx +340 -0
  178. package/src/client/builtins/viewers.tsx +125 -0
  179. package/src/client/chunk-loader.ts +358 -0
  180. package/src/client/chunks/editor.tsx +9 -0
  181. package/src/client/chunks/mermaid.tsx +10 -0
  182. package/src/client/chunks/terminal.tsx +9 -0
  183. package/src/client/cm-themes.ts +129 -0
  184. package/src/client/conversation-draft.ts +29 -0
  185. package/src/client/css-modules.d.ts +5 -0
  186. package/src/client/desktop-env.ts +72 -0
  187. package/src/client/editor-load.ts +92 -0
  188. package/src/client/frame-batcher.ts +56 -0
  189. package/src/client/icons.tsx +152 -0
  190. package/src/client/image-types.ts +8 -0
  191. package/src/client/ime-guard.ts +47 -0
  192. package/src/client/index.tsx +427 -0
  193. package/src/client/intercept.tsx +188 -0
  194. package/src/client/lang.ts +225 -0
  195. package/src/client/layout-push.ts +24 -0
  196. package/src/client/layout.css +90 -0
  197. package/src/client/lazy-chunk.tsx +89 -0
  198. package/src/client/link-intercept.ts +73 -0
  199. package/src/client/locales-ar.ts +368 -0
  200. package/src/client/locales-de.ts +353 -0
  201. package/src/client/locales-fr.ts +360 -0
  202. package/src/client/locales-hi.ts +367 -0
  203. package/src/client/locales-id.ts +365 -0
  204. package/src/client/locales-it.ts +358 -0
  205. package/src/client/locales-ja.ts +367 -0
  206. package/src/client/locales-ko.ts +359 -0
  207. package/src/client/locales-nl.ts +365 -0
  208. package/src/client/locales-pl.ts +369 -0
  209. package/src/client/locales-pt.ts +350 -0
  210. package/src/client/locales-ru.ts +365 -0
  211. package/src/client/locales-sv.ts +350 -0
  212. package/src/client/locales-th.ts +367 -0
  213. package/src/client/locales-tr.ts +367 -0
  214. package/src/client/locales-vi.ts +367 -0
  215. package/src/client/locales-zh-HK.ts +382 -0
  216. package/src/client/locales-zh-MO.ts +382 -0
  217. package/src/client/locales-zh-TW.ts +382 -0
  218. package/src/client/locales.ts +894 -0
  219. package/src/client/markdown-html.ts +331 -0
  220. package/src/client/markdown-images.ts +137 -0
  221. package/src/client/markdown-labels.tsx +52 -0
  222. package/src/client/md-toc.tsx +127 -0
  223. package/src/client/mermaid-blocks.ts +110 -0
  224. package/src/client/mermaid-sanitize.ts +93 -0
  225. package/src/client/mermaid.tsx +406 -0
  226. package/src/client/open-when-sized.ts +43 -0
  227. package/src/client/open-with-settings.tsx +135 -0
  228. package/src/client/open-with.ts +221 -0
  229. package/src/client/openpath-intercept.ts +143 -0
  230. package/src/client/paths.ts +63 -0
  231. package/src/client/pdf-types.ts +4 -0
  232. package/src/client/pinned.ts +183 -0
  233. package/src/client/plugin-settings.ts +43 -0
  234. package/src/client/plugins-shared.ts +29 -0
  235. package/src/client/plugins-tabs.ts +96 -0
  236. package/src/client/plugins-viewers.ts +30 -0
  237. package/src/client/prefs.ts +210 -0
  238. package/src/client/produced-files.ts +120 -0
  239. package/src/client/selection-payload.ts +86 -0
  240. package/src/client/service.ts +859 -0
  241. package/src/client/settings-nav-icon.ts +45 -0
  242. package/src/client/shell-presets.ts +83 -0
  243. package/src/client/sidebar.module.css +2788 -0
  244. package/src/client/sidechat-transcript.ts +321 -0
  245. package/src/client/split-pane.tsx +331 -0
  246. package/src/client/state.ts +1497 -0
  247. package/src/client/subagent-detect.ts +137 -0
  248. package/src/client/subagent-jobs.ts +161 -0
  249. package/src/client/tab-content-memo.ts +55 -0
  250. package/src/client/terminal-font.ts +224 -0
  251. package/src/client/terminal-links.ts +194 -0
  252. package/src/client/theme.ts +110 -0
  253. package/src/client/titlebar-strip.ts +37 -0
  254. package/src/client/upload.ts +187 -0
  255. package/src/client/wco.ts +120 -0
  256. package/src/config.ts +154 -0
  257. package/src/context-types.ts +611 -0
  258. package/src/fs-operations.ts +95 -0
  259. package/src/fs-search.ts +113 -0
  260. package/src/fs-tree.ts +158 -0
  261. package/src/git.ts +446 -0
  262. package/src/html-route.ts +106 -0
  263. package/src/index.ts +1291 -0
  264. package/src/invariant.ts +32 -0
  265. package/src/jobs-routes.ts +257 -0
  266. package/src/open-external.ts +90 -0
  267. package/src/path-security.ts +75 -0
  268. package/src/prefs-shared.ts +280 -0
  269. package/src/pty-deps.ts +240 -0
  270. package/src/pty-manager.ts +376 -0
  271. package/src/sidechat-core.ts +468 -0
  272. package/src/sidechat-routes.ts +344 -0
  273. package/src/subagent-activity.ts +91 -0
  274. package/src/subagent-live-route.ts +93 -0
  275. package/src/tools.ts +482 -0
  276. package/src/trust-fence.ts +84 -0
  277. package/src/wire.ts +100 -0
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Shared "Side card" preference vocabulary (types + constants), consumed by
3
+ * BOTH halves: the host registers the schemastery schema over these values
4
+ * (config.ts) and the client reads/writes them through the settings RPC
5
+ * (client/prefs.ts, client/SideCardSection.tsx). Kept free of schemastery so
6
+ * the browser bundle never pulls the schema runtime in.
7
+ */
8
+
9
+ /** The user-settings namespace holding the side card preferences. */
10
+ export const SIDEBAR_PREFS_NS = 'dsh-coding-sidebar'
11
+
12
+ /** User-facing side card preferences (new-conversation defaults). */
13
+ export interface SidebarPrefs {
14
+ /** Whether a brand-new conversation opens the side card by default. */
15
+ openByDefault: boolean
16
+ /** Default panel width as a percent of the window width (20–60). */
17
+ defaultWidthPercent: number
18
+ /**
19
+ * Whether the sidebar auto-activates (opens the panel) and expands the
20
+ * Subagent page when the current conversation spawns a new subagent.
21
+ */
22
+ autoOpenSubagent: boolean
23
+ /**
24
+ * Whether the sidebar auto-activates (opens the panel) and expands the
25
+ * Jobs page when a NEW background job appears for the current
26
+ * conversation (any new job id, not just the first one).
27
+ */
28
+ autoOpenJobs: boolean
29
+ /**
30
+ * Whether the model-facing agent terminal tools (terminal_create / list /
31
+ * send / read / wait_for / resize / signal / close) are injected into the
32
+ * model's toolset. Off by default: the feature stays dormant until the
33
+ * user explicitly enables it in the side card settings.
34
+ */
35
+ agentTerminalTools: boolean
36
+ /**
37
+ * Whether the model-facing `sidebar_open` tool is injected into the
38
+ * model's toolset — one tool that lets the model actively open a local
39
+ * file, a local folder (as a tree rooted there), or an HTTP(S) page in
40
+ * the calling session's sidebar. Off by default: the feature stays
41
+ * dormant until the user explicitly enables it in the side card settings.
42
+ */
43
+ agentOpenTools: boolean
44
+ /**
45
+ * Custom terminal font-family stack (a CSS font-family value, e.g.
46
+ * `'JetBrains Mono', monospace`). Empty string follows the app's theme
47
+ * monospace font (`--ds-font-family-code`). Applied live to every
48
+ * terminal tab; configured under the terminal card's secondary settings.
49
+ */
50
+ terminalFontFamily: string
51
+ /**
52
+ * Custom terminal font size in px (9–32). Applied live to every terminal
53
+ * tab; configured under the terminal card's secondary settings.
54
+ */
55
+ terminalFontSize: number
56
+ /**
57
+ * Whether chat-side file opens (tool-row path links, the produced-files
58
+ * row, prose file mentions — every path that funnels through the client
59
+ * runtime's `ctx.workspaces.openPath`) open in the sidebar editor instead
60
+ * of the Host OS's default application. On by default; the editor tab's
61
+ * own enable switch gates it too (both must be on for the takeover).
62
+ */
63
+ interceptOpenPath: boolean
64
+ /**
65
+ * Whether the editor tab runs in merged mode: a path input replaces the
66
+ * plain header and a toggleable file-tree panel (with a global name
67
+ * search) docks at the tab's right edge. On by default; also makes brand
68
+ * new sessions seed an empty editor tab (tree panel open) instead of the
69
+ * explorer tab. The switch lives under the editor card's gear in the
70
+ * Side card settings; off restores the pre-merge editor exactly.
71
+ */
72
+ editorExplorer: boolean
73
+ /**
74
+ * The shell the UI and agent terminals spawn (absolute path or bare
75
+ * executable name). Empty (default) keeps the legacy resolution order:
76
+ * `cordis.patch.yml` `config.shell`, then `$SHELL` / login shell /
77
+ * `powershell.exe` on Windows. Set it from the terminal card's gear in
78
+ * the Side card settings (or the yaml) to pin a specific shell — takes
79
+ * effect for terminals opened afterwards.
80
+ */
81
+ terminalShell: string
82
+ /**
83
+ * Explicit arguments for `terminalShell`, space-separated (empty keeps
84
+ * the platform defaults; when set, they fully replace them — same
85
+ * contract as the yaml `shellArgs`).
86
+ */
87
+ terminalShellArgs: string
88
+ /**
89
+ * Title-bar / shell compatibility scheme (the "位置兼容模式" setting):
90
+ * - `auto` (default): CONSERVATIVE — only the standard Window Controls
91
+ * Overlay API (present in frameless Chromium shells that draw the
92
+ * native caption buttons over web content) contributes real geometry;
93
+ * without it nothing is modified, so plain-browser (web) behavior is
94
+ * untouched.
95
+ * - `web`: EXPLICIT "DSH official web" — never adapt, not even WCO
96
+ * geometry (the user declares they run the plain web UI).
97
+ * - `preset`: apply the built-in shell preset named by
98
+ * `titleBarPresetId` (data-driven, opt-in — see shell-presets.ts).
99
+ * - `custom`: apply the free-form `customCss` (and the legacy
100
+ * `titleBarStripPx` strip).
101
+ */
102
+ titleBarScheme: TitleBarScheme
103
+ /**
104
+ * The built-in shell preset id applied while `titleBarScheme` is
105
+ * `preset` ('' = no preset — nothing extra is applied).
106
+ */
107
+ titleBarPresetId: string
108
+ /**
109
+ * Free-form CSS injected into the page (last in the cascade, so it can
110
+ * override the plugin's styles; use `!important` to override JS-written
111
+ * inline CSS variables). Applied while `titleBarScheme` is `custom`.
112
+ */
113
+ customCss: string
114
+ /**
115
+ * LEGACY (kept for read-migration and downgrade mirroring only): position
116
+ * compatibility mode flag. The UI writes `titleBarScheme` instead; a
117
+ * stored `true` without a scheme migrates to the `custom` scheme (with
118
+ * `titleBarStripPx` preserved).
119
+ */
120
+ titleBarCompat: boolean
121
+ /**
122
+ * LEGACY (kept for read-migration and downgrade mirroring only): the
123
+ * reserved top strip height in px used by the `custom` scheme (0–120,
124
+ * default 40). Drives the `--dsh-title-bar-strip` CSS variable: the
125
+ * toggle cluster drops `strip + 3px` and the right panel's content
126
+ * starts `strip` px below its top edge.
127
+ */
128
+ titleBarStripPx: number
129
+ /**
130
+ * Whether the HTML previewer drops its sandboxed iframe. Sandbox ON (the
131
+ * default) renders previewed HTML in an opaque-origin iframe that cannot
132
+ * touch the GUI; turning it OFF runs the previewed page with the GUI's
133
+ * own origin — full read/write access to session files and internal
134
+ * APIs. Only for trusted local content; the setting copy warns.
135
+ */
136
+ htmlViewerNoSandbox: boolean
137
+ /**
138
+ * Whether a newly opened HTML preview starts UNSANDBOXED (the per-surface
139
+ * temporary unlock pre-applied). Off by default: previews open sandboxed
140
+ * and the status row offers the one-tap unlock; when on, previews open
141
+ * in the red unsandboxed state and the status row offers a one-tap
142
+ * restore for the current file.
143
+ */
144
+ htmlViewerDefaultUnsafe: boolean
145
+ /**
146
+ * Whether the browser tab drops its sandboxed iframe. Sandbox ON (the
147
+ * default) keeps browsed sites in an opaque origin with no GUI access;
148
+ * turning it OFF runs any visited site with the GUI's own origin — it
149
+ * can read session data and act as the logged-in GUI. Only for trusted
150
+ * sites; the setting copy warns.
151
+ */
152
+ browserNoSandbox: boolean
153
+ /**
154
+ * MASTER switch: whether clicking an EXTERNAL link in the GUI (chat
155
+ * messages, tool rows, prose mentions) is taken over into the sidebar at
156
+ * all. On by default; the per-protocol granularity lives in
157
+ * `browserInterceptHttp` / `browserInterceptHttps` (the protocol flag
158
+ * must also be on), and the target tab's own enable switch gates it too.
159
+ * Ctrl/Cmd+click always bypasses the takeover. Kept as the master so old
160
+ * documents keep their meaning with no migration (an explicit `false`
161
+ * stays "never take over").
162
+ */
163
+ browserInterceptLinks: boolean
164
+ /**
165
+ * Whether clicking an http EXTERNAL link in the GUI opens the sidebar
166
+ * (the built-in browser tab, or a plugin tab that declares `urlTarget`)
167
+ * instead of a new browser tab. On by default; gated on the
168
+ * `browserInterceptLinks` master and the target tab's own enable switch.
169
+ */
170
+ browserInterceptHttp: boolean
171
+ /**
172
+ * Whether clicking an https EXTERNAL link in the GUI opens the sidebar
173
+ * instead of a new browser tab. OFF by default — most https sites (e.g.
174
+ * GitHub) refuse iframe embedding, so the system browser is the smoother
175
+ * default; gated on the `browserInterceptLinks` master and the target
176
+ * tab's own enable switch.
177
+ */
178
+ browserInterceptHttps: boolean
179
+ /**
180
+ * Comma-separated allowlist of local (loopback) authorities the browser
181
+ * tab may navigate to — `localhost`, `127.0.0.1`, `127.0.0.1:5174`, or
182
+ * host:port pairs. Empty by default: loopback addresses stay blocked so a
183
+ * browsed page cannot probe local services. Each entry is either a bare
184
+ * hostname (all ports) or host:port; the GUI's own origin is always
185
+ * allowed regardless. The iframe sandbox still renders allowed local
186
+ * pages in an opaque origin, exactly like any other site.
187
+ */
188
+ browserAllowedLoopback: string
189
+ /**
190
+ * Per-tab enable switches, keyed by tab descriptor id (`'explorer'`,
191
+ * `'my-plugin:db'`). An ABSENT key means enabled — only an explicit
192
+ * `false` disables a tab type (hidden from the + menu, `openTab` refuses,
193
+ * and derived flows like subagent auto-open / agent-terminal tabs stop).
194
+ * Already-open tabs of a disabled type keep rendering (closing one
195
+ * prevents reopening), matching the "existing conversations keep their
196
+ * own layouts" rule.
197
+ */
198
+ tabsEnabled: Record<string, boolean>
199
+ /**
200
+ * Per-viewer enable switches, keyed by file viewer descriptor id
201
+ * (`'image'`, `'my-plugin:csv'`). An ABSENT key means enabled; a disabled
202
+ * viewer is skipped by `matchFileViewer` so files fall through to the
203
+ * next matching viewer (or the download button when none match).
204
+ */
205
+ viewersEnabled: Record<string, boolean>
206
+ /**
207
+ * Plugin-owned settings blobs (v0.12.0+), keyed by descriptor id: each
208
+ * registered tab/viewer that declares `settings.pluginToggles` (or writes
209
+ * through `settings.render`'s `updatePluginSetting`) persists its values
210
+ * here — an open map, so third-party keys need no host PrefsSchema field.
211
+ * Values are JSON-serializable (the row controls produce strings /
212
+ * numbers / booleans; custom panels are responsible for their own).
213
+ */
214
+ pluginSettings: Record<string, Record<string, unknown>>
215
+ }
216
+
217
+ /** Range contract of {@link SidebarPrefs.defaultWidthPercent}. */
218
+ export const WIDTH_PERCENT_MIN = 20
219
+ export const WIDTH_PERCENT_MAX = 60
220
+ export const WIDTH_PERCENT_DEFAULT = 35
221
+
222
+ /** Range contract of {@link SidebarPrefs.terminalFontSize}. */
223
+ export const TERMINAL_FONT_SIZE_MIN = 9
224
+ export const TERMINAL_FONT_SIZE_MAX = 32
225
+ export const TERMINAL_FONT_SIZE_DEFAULT = 13
226
+
227
+ /** Range contract of {@link SidebarPrefs.titleBarStripPx}. */
228
+ export const TITLE_BAR_STRIP_MIN = 0
229
+ export const TITLE_BAR_STRIP_MAX = 120
230
+ export const TITLE_BAR_STRIP_DEFAULT = 40
231
+
232
+ /** The title-bar / shell compatibility schemes (see {@link SidebarPrefs.titleBarScheme}). */
233
+ export const TITLE_BAR_SCHEMES = ['auto', 'web', 'preset', 'custom'] as const
234
+ export type TitleBarScheme = typeof TITLE_BAR_SCHEMES[number]
235
+
236
+ /** Fallback prefs used whenever the settings document is unreachable or malformed. */
237
+ export const SIDEBAR_PREFS_DEFAULTS: SidebarPrefs = {
238
+ openByDefault: false,
239
+ defaultWidthPercent: WIDTH_PERCENT_DEFAULT,
240
+ autoOpenSubagent: true,
241
+ autoOpenJobs: true,
242
+ agentTerminalTools: false,
243
+ agentOpenTools: false,
244
+ terminalFontFamily: '',
245
+ terminalFontSize: TERMINAL_FONT_SIZE_DEFAULT,
246
+ interceptOpenPath: true,
247
+ editorExplorer: false,
248
+ terminalShell: '',
249
+ terminalShellArgs: '',
250
+ titleBarScheme: 'auto',
251
+ titleBarPresetId: '',
252
+ customCss: '',
253
+ titleBarCompat: false,
254
+ titleBarStripPx: TITLE_BAR_STRIP_DEFAULT,
255
+ htmlViewerNoSandbox: false,
256
+ htmlViewerDefaultUnsafe: false,
257
+ browserNoSandbox: false,
258
+ browserInterceptLinks: true,
259
+ browserInterceptHttp: true,
260
+ browserInterceptHttps: false,
261
+ browserAllowedLoopback: '',
262
+ tabsEnabled: {},
263
+ viewersEnabled: {},
264
+ pluginSettings: {},
265
+ }
266
+
267
+ /** Clamp one width percent into the contract range (shared by schema and client reads). */
268
+ export function clampWidthPercent(value: number): number {
269
+ return Math.min(WIDTH_PERCENT_MAX, Math.max(WIDTH_PERCENT_MIN, Math.round(value)))
270
+ }
271
+
272
+ /** Clamp one terminal font size into the contract range (shared by schema and client reads). */
273
+ export function clampTerminalFontSize(value: number): number {
274
+ return Math.min(TERMINAL_FONT_SIZE_MAX, Math.max(TERMINAL_FONT_SIZE_MIN, Math.round(value)))
275
+ }
276
+
277
+ /** Clamp one title-bar strip height into the contract range (shared by schema and client reads). */
278
+ export function clampTitleBarStrip(value: number): number {
279
+ return Math.min(TITLE_BAR_STRIP_MAX, Math.max(TITLE_BAR_STRIP_MIN, Math.round(value)))
280
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * node-pty dependency loading for the host half (issue #140, plugin side).
3
+ *
4
+ * The terminal surfaces (UI tabs + model-facing terminal_* tools) need
5
+ * node-pty, but the package must NEVER be imported statically at module
6
+ * top level: a missing or broken install (pnpm 11's strict-dep-builds
7
+ * skipping node-pty's install script, a pruned store entry, a failed
8
+ * prebuilt-binary download…) would then fail the plugin module load and —
9
+ * because a loader entry apply failure aborts the boot — take the whole
10
+ * `dsh web` server down with it.
11
+ *
12
+ * Instead the host half loads node-pty lazily (synchronously, via
13
+ * createRequire — the same resolution `ensureSpawnHelper` already uses in
14
+ * production). When the load fails the plugin stays mounted in a degraded
15
+ * state: the terminal tab shows a friendly error carrying a pasteable
16
+ * repair command (see scripts/install.sh / install.ps1 `--repair`), and the
17
+ * agent terminal tools are simply not registered.
18
+ *
19
+ * Version contract: the plugin must stay in sync with DSH core —
20
+ * `@deepseek-ai/dsh-subprocess-local` declares `"node-pty": "^1.1.0"` in
21
+ * its `dependencies`. Both sides then resolve the SAME pnpm store entry
22
+ * (same range, same integrity → one native binding, no drift). Do NOT
23
+ * switch to a fork (e.g. @lydell/node-pty) or a different range without
24
+ * re-checking the core declaration.
25
+ */
26
+ import { existsSync, readFileSync, realpathSync } from 'node:fs'
27
+ import { createRequire } from 'node:module'
28
+ import { homedir } from 'node:os'
29
+ import { basename, dirname, join } from 'node:path'
30
+ import { fileURLToPath } from 'node:url'
31
+ import type * as nodePtyNs from 'node-pty'
32
+ import { SidebarError } from './wire.ts'
33
+
34
+ /** The node-pty module surface the registries consume (spawn/kill/resize/…). */
35
+ export type NodePtyModule = typeof nodePtyNs
36
+
37
+ /**
38
+ * The node-pty version range this plugin ships. MUST stay identical to the
39
+ * range DSH core declares (`@deepseek-ai/dsh-subprocess-local`): the same
40
+ * range keeps pnpm resolving both to one physical package.
41
+ */
42
+ export const DSH_NODE_PTY_RANGE = '^1.1.0'
43
+
44
+ /**
45
+ * The WebSocket close-code-1011 reason the host sends when node-pty is
46
+ * unavailable. The client recognizes this exact marker and fetches the full
47
+ * repair details from `/sidebar/api/terminal.deps` (a WS close reason is
48
+ * capped at 123 bytes, so the command itself cannot ride the close frame).
49
+ */
50
+ export const PTY_DEPS_MISSING = 'pty-deps-missing'
51
+
52
+ /** A require-compatible loader, injectable for tests. */
53
+ export type NodePtyRequire = (id: string) => unknown
54
+
55
+ const defaultRequire: NodePtyRequire = createRequire(import.meta.url)
56
+
57
+ type LoadResult = { ok: true; module: NodePtyModule } | { ok: false; cause: unknown }
58
+
59
+ let cached: LoadResult | undefined
60
+
61
+ /**
62
+ * Load node-pty once (synchronously) and cache the outcome. Returns null
63
+ * when the package or its native binding cannot be loaded; the cause stays
64
+ * queryable through {@link nodePtyLoadCause}. Never throws.
65
+ */
66
+ export function loadNodePty(requireImpl: NodePtyRequire = defaultRequire): NodePtyModule | null {
67
+ if (cached === undefined) {
68
+ try {
69
+ cached = { ok: true, module: requireImpl('node-pty') as NodePtyModule }
70
+ } catch (cause) {
71
+ cached = { ok: false, cause }
72
+ }
73
+ }
74
+ return cached.ok ? cached.module : null
75
+ }
76
+
77
+ /** The recorded load failure (undefined when the load succeeded or never ran). */
78
+ export function nodePtyLoadCause(): unknown {
79
+ return cached !== undefined && !cached.ok ? cached.cause : undefined
80
+ }
81
+
82
+ /** Forget the cached outcome (tests only — a real reload is otherwise one-shot). */
83
+ export function resetNodePtyCache(): void {
84
+ cached = undefined
85
+ }
86
+
87
+ /** Load node-pty or throw the canonical degraded-mode error (class-constructor default). */
88
+ export function loadRequiredNodePty(): NodePtyModule {
89
+ const module = loadNodePty()
90
+ if (module === null) {
91
+ const cause = describeCause(nodePtyLoadCause())
92
+ throw new SidebarError(
93
+ 'pty-deps-missing',
94
+ `node-pty (${DSH_NODE_PTY_RANGE}) failed to load: ${cause} — run the repair command shown in the terminal tab`,
95
+ 503,
96
+ )
97
+ }
98
+ return module
99
+ }
100
+
101
+ /** Resolve a directory to its physical location (symlinked/link: installs). */
102
+ function realDir(file: string): string {
103
+ try {
104
+ return dirname(realpathSync(file))
105
+ } catch {
106
+ return dirname(file)
107
+ }
108
+ }
109
+
110
+ /** Walk up from `dir` looking for a DSH profile root (package.json + pnpm-workspace.yaml). */
111
+ function walkUp(dir: string, isRoot: (dir: string) => boolean): string | null {
112
+ let current = dir
113
+ for (let depth = 0; depth < 16; depth += 1) {
114
+ if (isRoot(current)) return current
115
+ const parent = dirname(current)
116
+ if (parent === current) break
117
+ current = parent
118
+ }
119
+ return null
120
+ }
121
+
122
+ /** Whether `dir` looks like a DSH profile root (the plugin lives under its node_modules). */
123
+ function isProfileRoot(dir: string): boolean {
124
+ return existsSync(join(dir, 'package.json')) && existsSync(join(dir, 'pnpm-workspace.yaml'))
125
+ }
126
+
127
+ /**
128
+ * Detect the DSH profile directory this plugin is installed into: the
129
+ * nearest ancestor of the plugin module that carries both `package.json`
130
+ * and `pnpm-workspace.yaml` (the profile root; the plugin resolves from the
131
+ * profile's node_modules). Falls back to `$DSH_HOME/profiles/web` (the
132
+ * standard web profile), then null.
133
+ */
134
+ export function findProfileDir(fromFile: string = fileURLToPath(import.meta.url)): string | null {
135
+ const detected = walkUp(realDir(fromFile), isProfileRoot)
136
+ if (detected !== null) return detected
137
+ const home = process.env.DSH_HOME !== undefined && process.env.DSH_HOME.trim() !== ''
138
+ ? process.env.DSH_HOME
139
+ : join(homedir(), '.dsh')
140
+ const web = join(home, 'profiles', 'web')
141
+ return isProfileRoot(web) ? realpathSync(web) : null
142
+ }
143
+
144
+ /** Whether `dir`'s package.json declares this plugin's name. */
145
+ function isPluginRoot(dir: string): boolean {
146
+ const file = join(dir, 'package.json')
147
+ if (!existsSync(file)) return false
148
+ try {
149
+ const parsed = JSON.parse(readFileSync(file, 'utf8')) as { name?: unknown }
150
+ return parsed.name === 'dsh-coding-sidebar'
151
+ } catch {
152
+ return false
153
+ }
154
+ }
155
+
156
+ /** The plugin package root (walk-up from the module; works for lib/ and src/ layouts). */
157
+ export function findPluginRoot(fromFile: string = fileURLToPath(import.meta.url)): string | null {
158
+ return walkUp(realDir(fromFile), isPluginRoot)
159
+ }
160
+
161
+ /** Options for {@link buildRepairCommand}. */
162
+ export interface RepairCommandOptions {
163
+ /** The plugin package root (where scripts/install.sh / install.ps1 live). */
164
+ pluginRoot: string | null
165
+ /** The detected profile directory (null → the standard `web` profile). */
166
+ profileDir: string | null
167
+ /** Platform override for tests; defaults to the live process. */
168
+ platform?: NodeJS.Platform
169
+ }
170
+
171
+ /**
172
+ * The pasteable repair command for a broken node-pty install: rerun the
173
+ * plugin's own installer in `--repair` mode (idempotent: it re-writes the
174
+ * profile's `allowBuilds: node-pty: true` and re-installs/rebuilds the
175
+ * dependency). Falls back to DSH's plugin command when the scripts are not
176
+ * shipped (exotic layouts).
177
+ */
178
+ export function buildRepairCommand(options: RepairCommandOptions): { command: string; note?: string } {
179
+ const { pluginRoot, profileDir } = options
180
+ const platform = options.platform ?? process.platform
181
+ const profileName = profileDir !== null ? basename(profileDir) : null
182
+ const profileArg = profileName !== null
183
+ ? (platform === 'win32' ? ` -Profile "${profileName}"` : ` --profile "${profileName}"`)
184
+ : ''
185
+ if (pluginRoot !== null) {
186
+ if (platform === 'win32') {
187
+ const script = join(pluginRoot, 'scripts', 'install.ps1')
188
+ if (existsSync(script)) {
189
+ return { command: `powershell -ExecutionPolicy Bypass -File "${script}" -Repair${profileArg}` }
190
+ }
191
+ } else {
192
+ const script = join(pluginRoot, 'scripts', 'install.sh')
193
+ if (existsSync(script)) {
194
+ return { command: `bash "${script}" --repair${profileArg}` }
195
+ }
196
+ }
197
+ }
198
+ const name = profileName ?? 'web'
199
+ return {
200
+ command: `dsh plugin --profile "${name}" install`,
201
+ note: 'If pnpm 11 blocked node-pty\'s build script, ensure `allowBuilds: node-pty: true` in the profile\'s pnpm-workspace.yaml (the plugin\'s scripts/install.sh / install.ps1 --repair does this automatically).',
202
+ }
203
+ }
204
+
205
+ /** One-line human description of the recorded load cause. */
206
+ function describeCause(cause: unknown): string {
207
+ if (cause instanceof Error) return cause.message
208
+ return String(cause)
209
+ }
210
+
211
+ /** Structured status served by the `/sidebar/api/terminal.deps` endpoint. */
212
+ export type NodePtyDepsStatus =
213
+ | { ok: true }
214
+ | {
215
+ ok: false
216
+ /** The require-time error message (module missing, native binding broken…). */
217
+ cause: string
218
+ /** The pasteable repair command (terminal/cmd). */
219
+ command: string
220
+ /** The detected profile name (null when undetected → the command defaults to web). */
221
+ profile: string | null
222
+ /** Optional supplementary hint (fallback command only). */
223
+ note?: string
224
+ }
225
+
226
+ /** Current node-pty dependency status (loaded vs degraded + repair info). */
227
+ export function depsStatus(options: { fromFile?: string } = {}): NodePtyDepsStatus {
228
+ const module = loadNodePty()
229
+ if (module !== null) return { ok: true }
230
+ const pluginRoot = findPluginRoot(options.fromFile)
231
+ const profileDir = findProfileDir(options.fromFile)
232
+ const { command, note } = buildRepairCommand({ pluginRoot, profileDir })
233
+ return {
234
+ ok: false,
235
+ cause: describeCause(nodePtyLoadCause()),
236
+ command,
237
+ profile: profileDir !== null ? basename(profileDir) : null,
238
+ ...(note !== undefined ? { note } : {}),
239
+ }
240
+ }