dsh-over-ssh 0.0.0-stage → 0.1.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 (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -2
  3. package/agents/agent-manifest.json +20 -0
  4. package/agents/linux-amd64/dsh-over-ssh-agent +0 -0
  5. package/agents/linux-arm64/dsh-over-ssh-agent +0 -0
  6. package/cordis.patch.yml +84 -0
  7. package/lib/client.js +6611 -0
  8. package/lib/file-references.js +366 -0
  9. package/lib/fs.js +1368 -0
  10. package/lib/index.js +19034 -0
  11. package/lib/local-bridge-refs.js +16 -0
  12. package/lib/local-bridge.js +16 -0
  13. package/lib/terminal-wrapper.js +294 -0
  14. package/lib/types/src/client/controls.d.ts +131 -0
  15. package/lib/types/src/client/core/card-focus-trap.d.ts +45 -0
  16. package/lib/types/src/client/core/controls.d.ts +164 -0
  17. package/lib/types/src/client/core/latest.d.ts +48 -0
  18. package/lib/types/src/client/core/locale.d.ts +38 -0
  19. package/lib/types/src/client/core/message.d.ts +37 -0
  20. package/lib/types/src/client/core/panel-model.d.ts +134 -0
  21. package/lib/types/src/client/core/portal.d.ts +266 -0
  22. package/lib/types/src/client/core/remote.d.ts +84 -0
  23. package/lib/types/src/client/core/styles.d.ts +458 -0
  24. package/lib/types/src/client/file-tree-io.d.ts +19 -0
  25. package/lib/types/src/client/file-tree.d.ts +271 -0
  26. package/lib/types/src/client/flows/directory-browser.d.ts +587 -0
  27. package/lib/types/src/client/flows/session-directory.d.ts +415 -0
  28. package/lib/types/src/client/flows/workspace-directory-flow.d.ts +480 -0
  29. package/lib/types/src/client/index.d.ts +49 -0
  30. package/lib/types/src/client/locale.d.ts +31 -0
  31. package/lib/types/src/client/panel-model.d.ts +37 -0
  32. package/lib/types/src/client/panel.d.ts +77 -0
  33. package/lib/types/src/client/roots/connection-test.d.ts +69 -0
  34. package/lib/types/src/client/roots/roots-panel-state.d.ts +307 -0
  35. package/lib/types/src/client/roots/roots-panel-view.d.ts +280 -0
  36. package/lib/types/src/client/roots/roots-panel.d.ts +364 -0
  37. package/lib/types/src/client/roots/transfer-panel-view.d.ts +35 -0
  38. package/lib/types/src/client/roots/transfer-panel.d.ts +241 -0
  39. package/lib/types/src/client/roots/transfer-progress.d.ts +67 -0
  40. package/lib/types/src/client/roots-panel-view.d.ts +236 -0
  41. package/lib/types/src/client/roots-panel.d.ts +347 -0
  42. package/lib/types/src/client/session/session-panel-view.d.ts +336 -0
  43. package/lib/types/src/client/session/session-panel.d.ts +322 -0
  44. package/lib/types/src/client/session/session-remote-mark.d.ts +110 -0
  45. package/lib/types/src/client/session/session-row-actions.d.ts +398 -0
  46. package/lib/types/src/client/session-panel.d.ts +152 -0
  47. package/lib/types/src/client/styles.d.ts +252 -0
  48. package/lib/types/src/client/workspace-directory-flow.d.ts +342 -0
  49. package/lib/types/src/config.d.ts +312 -0
  50. package/lib/types/src/file-references.d.ts +25 -0
  51. package/lib/types/src/fs.d.ts +46 -0
  52. package/lib/types/src/host/app/backend.d.ts +170 -0
  53. package/lib/types/src/host/app/recycle-bin/recycle-bin-contract.d.ts +247 -0
  54. package/lib/types/src/host/app/recycle-bin/recycle-bin-move.d.ts +18 -0
  55. package/lib/types/src/host/app/recycle-bin/recycle-bin-service.d.ts +45 -0
  56. package/lib/types/src/host/app/recycle-bin/recycle-bin-world.d.ts +38 -0
  57. package/lib/types/src/host/app/recycle-bin-contract.d.ts +215 -0
  58. package/lib/types/src/host/app/recycle-bin-move.d.ts +18 -0
  59. package/lib/types/src/host/app/recycle-bin-service.d.ts +45 -0
  60. package/lib/types/src/host/app/recycle-bin-world.d.ts +35 -0
  61. package/lib/types/src/host/app/roots/roots-api-connections.d.ts +49 -0
  62. package/lib/types/src/host/app/roots/roots-api-contract.d.ts +369 -0
  63. package/lib/types/src/host/app/roots/roots-api-directories.d.ts +13 -0
  64. package/lib/types/src/host/app/roots/roots-api-http.d.ts +77 -0
  65. package/lib/types/src/host/app/roots/roots-api-long-poll.d.ts +130 -0
  66. package/lib/types/src/host/app/roots/roots-api-projections.d.ts +102 -0
  67. package/lib/types/src/host/app/roots/roots-api-roots.d.ts +13 -0
  68. package/lib/types/src/host/app/roots/roots-api-state.d.ts +22 -0
  69. package/lib/types/src/host/app/roots/roots-api-transfers.d.ts +26 -0
  70. package/lib/types/src/host/app/roots/roots-api.d.ts +55 -0
  71. package/lib/types/src/host/app/roots/user-roots.d.ts +552 -0
  72. package/lib/types/src/host/app/roots-api-connections.d.ts +49 -0
  73. package/lib/types/src/host/app/roots-api-contract.d.ts +339 -0
  74. package/lib/types/src/host/app/roots-api-directories.d.ts +13 -0
  75. package/lib/types/src/host/app/roots-api-http.d.ts +77 -0
  76. package/lib/types/src/host/app/roots-api-projections.d.ts +102 -0
  77. package/lib/types/src/host/app/roots-api-roots.d.ts +13 -0
  78. package/lib/types/src/host/app/roots-api-state.d.ts +22 -0
  79. package/lib/types/src/host/app/roots-api-support.d.ts +182 -0
  80. package/lib/types/src/host/app/roots-api-transfers.d.ts +13 -0
  81. package/lib/types/src/host/app/roots-api.d.ts +55 -0
  82. package/lib/types/src/host/app/routed-fs.d.ts +239 -0
  83. package/lib/types/src/host/app/serialize.d.ts +37 -0
  84. package/lib/types/src/host/app/session-admin/session-admin-context.d.ts +36 -0
  85. package/lib/types/src/host/app/session-admin/session-admin-contract.d.ts +221 -0
  86. package/lib/types/src/host/app/session-admin/session-admin-delete.d.ts +34 -0
  87. package/lib/types/src/host/app/session-admin/session-admin-service.d.ts +40 -0
  88. package/lib/types/src/host/app/session-admin-context.d.ts +36 -0
  89. package/lib/types/src/host/app/session-admin-contract.d.ts +214 -0
  90. package/lib/types/src/host/app/session-admin-delete.d.ts +34 -0
  91. package/lib/types/src/host/app/session-admin-service.d.ts +38 -0
  92. package/lib/types/src/host/app/takeover/takeover-plan.d.ts +227 -0
  93. package/lib/types/src/host/app/takeover/takeover-rows.d.ts +55 -0
  94. package/lib/types/src/host/app/transport/transport-selector.d.ts +98 -0
  95. package/lib/types/src/host/app/transport/transport-service.d.ts +82 -0
  96. package/lib/types/src/host/app/transport-selector.d.ts +98 -0
  97. package/lib/types/src/host/app/transport-service.d.ts +75 -0
  98. package/lib/types/src/host/app/user-roots.d.ts +461 -0
  99. package/lib/types/src/host/domain/bin-location.d.ts +79 -0
  100. package/lib/types/src/host/domain/capability.d.ts +78 -0
  101. package/lib/types/src/host/domain/connection.d.ts +95 -0
  102. package/lib/types/src/host/domain/errors.d.ts +55 -0
  103. package/lib/types/src/host/domain/glob-pattern.d.ts +62 -0
  104. package/lib/types/src/host/domain/literal-text.d.ts +63 -0
  105. package/lib/types/src/host/domain/path-grammar.d.ts +84 -0
  106. package/lib/types/src/host/domain/recycle-bin.d.ts +337 -0
  107. package/lib/types/src/host/domain/root.d.ts +74 -0
  108. package/lib/types/src/host/domain/roots/bin-location.d.ts +91 -0
  109. package/lib/types/src/host/domain/roots/connection.d.ts +106 -0
  110. package/lib/types/src/host/domain/roots/root.d.ts +108 -0
  111. package/lib/types/src/host/domain/roots/route-table.d.ts +102 -0
  112. package/lib/types/src/host/domain/roots/session-roots.d.ts +151 -0
  113. package/lib/types/src/host/domain/roots/ssh-config.d.ts +73 -0
  114. package/lib/types/src/host/domain/route-table.d.ts +80 -0
  115. package/lib/types/src/host/domain/session/session-admin.d.ts +345 -0
  116. package/lib/types/src/host/domain/session/terminal-target.d.ts +136 -0
  117. package/lib/types/src/host/domain/session-admin.d.ts +330 -0
  118. package/lib/types/src/host/domain/shell.d.ts +47 -0
  119. package/lib/types/src/host/domain/ssh-config.d.ts +73 -0
  120. package/lib/types/src/host/domain/terminal-target.d.ts +132 -0
  121. package/lib/types/src/host/domain/tool-requests.d.ts +369 -0
  122. package/lib/types/src/host/domain/transfer/recycle-bin.d.ts +337 -0
  123. package/lib/types/src/host/domain/transfer/transfer.d.ts +260 -0
  124. package/lib/types/src/host/domain/transfer.d.ts +260 -0
  125. package/lib/types/src/host/infra/backend/local-backend.d.ts +84 -0
  126. package/lib/types/src/host/infra/backend/local-version.d.ts +43 -0
  127. package/lib/types/src/host/infra/backend/platform-local-backend.d.ts +89 -0
  128. package/lib/types/src/host/infra/backend/remote-backend.d.ts +239 -0
  129. package/lib/types/src/host/infra/cache/lru.d.ts +52 -0
  130. package/lib/types/src/host/infra/cache/versioned.d.ts +77 -0
  131. package/lib/types/src/host/infra/temp-name.d.ts +19 -0
  132. package/lib/types/src/host/infra/transport/agent/agent-installer.d.ts +121 -0
  133. package/lib/types/src/host/infra/transport/agent/agent-protocol-client.d.ts +132 -0
  134. package/lib/types/src/host/infra/transport/agent/agent-protocol-codec.d.ts +140 -0
  135. package/lib/types/src/host/infra/transport/agent/agent-protocol-errors.d.ts +41 -0
  136. package/lib/types/src/host/infra/transport/agent/agent-protocol-frames.d.ts +197 -0
  137. package/lib/types/src/host/infra/transport/agent/agent-protocol-types.d.ts +564 -0
  138. package/lib/types/src/host/infra/transport/agent/agent-transport.d.ts +418 -0
  139. package/lib/types/src/host/infra/transport/agent-installer.d.ts +121 -0
  140. package/lib/types/src/host/infra/transport/agent-protocol-client.d.ts +123 -0
  141. package/lib/types/src/host/infra/transport/agent-protocol-codec.d.ts +131 -0
  142. package/lib/types/src/host/infra/transport/agent-protocol-errors.d.ts +41 -0
  143. package/lib/types/src/host/infra/transport/agent-protocol-frames.d.ts +197 -0
  144. package/lib/types/src/host/infra/transport/agent-protocol-types.d.ts +509 -0
  145. package/lib/types/src/host/infra/transport/agent-transport.d.ts +353 -0
  146. package/lib/types/src/host/infra/transport/alias-transport.d.ts +206 -0
  147. package/lib/types/src/host/infra/transport/askpass-fs.d.ts +20 -0
  148. package/lib/types/src/host/infra/transport/askpass.d.ts +133 -0
  149. package/lib/types/src/host/infra/transport/command-pool.d.ts +61 -0
  150. package/lib/types/src/host/infra/transport/fs-commands.d.ts +140 -0
  151. package/lib/types/src/host/infra/transport/git-status-route.d.ts +63 -0
  152. package/lib/types/src/host/infra/transport/git-status.d.ts +101 -0
  153. package/lib/types/src/host/infra/transport/node-spawn.d.ts +26 -0
  154. package/lib/types/src/host/infra/transport/session-window.d.ts +51 -0
  155. package/lib/types/src/host/infra/transport/shell-quote.d.ts +59 -0
  156. package/lib/types/src/host/infra/transport/ssh/alias-transport.d.ts +206 -0
  157. package/lib/types/src/host/infra/transport/ssh/askpass-fs.d.ts +20 -0
  158. package/lib/types/src/host/infra/transport/ssh/askpass.d.ts +133 -0
  159. package/lib/types/src/host/infra/transport/ssh/shell-quote.d.ts +62 -0
  160. package/lib/types/src/host/infra/transport/ssh/ssh-argv.d.ts +43 -0
  161. package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +328 -0
  162. package/lib/types/src/host/infra/transport/ssh-argv.d.ts +43 -0
  163. package/lib/types/src/host/infra/transport/ssh2-transport.d.ts +302 -0
  164. package/lib/types/src/host/infra/transport/transport-base.d.ts +34 -0
  165. package/lib/types/src/host/infra/transport/transport.d.ts +527 -0
  166. package/lib/types/src/host/integration/agent/agent-install-io.d.ts +33 -0
  167. package/lib/types/src/host/integration/agent/agent-preflight.d.ts +56 -0
  168. package/lib/types/src/host/integration/agent/askpass-registry.d.ts +66 -0
  169. package/lib/types/src/host/integration/agent-install-io.d.ts +33 -0
  170. package/lib/types/src/host/integration/agent-preflight.d.ts +56 -0
  171. package/lib/types/src/host/integration/anchor-directory.d.ts +56 -0
  172. package/lib/types/src/host/integration/askpass-registry.d.ts +59 -0
  173. package/lib/types/src/host/integration/bin-root-resolver.d.ts +81 -0
  174. package/lib/types/src/host/integration/connection-test.d.ts +73 -0
  175. package/lib/types/src/host/integration/file-reference-provider.d.ts +115 -0
  176. package/lib/types/src/host/integration/fs/file-reference-provider.d.ts +142 -0
  177. package/lib/types/src/host/integration/fs/fs-provider.d.ts +368 -0
  178. package/lib/types/src/host/integration/fs/git-status-wiring.d.ts +21 -0
  179. package/lib/types/src/host/integration/fs/platform-errors.d.ts +94 -0
  180. package/lib/types/src/host/integration/fs/remote-file-tools.d.ts +369 -0
  181. package/lib/types/src/host/integration/fs/remote-index.d.ts +177 -0
  182. package/lib/types/src/host/integration/fs-provider.d.ts +226 -0
  183. package/lib/types/src/host/integration/lsp/lsp-framing.d.ts +38 -0
  184. package/lib/types/src/host/integration/lsp/lsp-registry.d.ts +67 -0
  185. package/lib/types/src/host/integration/lsp/lsp-session.d.ts +130 -0
  186. package/lib/types/src/host/integration/management-tools.d.ts +64 -0
  187. package/lib/types/src/host/integration/recycle-bin-service.d.ts +247 -0
  188. package/lib/types/src/host/integration/remote/connection-test.d.ts +73 -0
  189. package/lib/types/src/host/integration/remote/remote-face.d.ts +127 -0
  190. package/lib/types/src/host/integration/remote/remote-tool-backend.d.ts +108 -0
  191. package/lib/types/src/host/integration/remote-face.d.ts +127 -0
  192. package/lib/types/src/host/integration/remote-file-tools.d.ts +241 -0
  193. package/lib/types/src/host/integration/remote-home.d.ts +69 -0
  194. package/lib/types/src/host/integration/remote-index.d.ts +165 -0
  195. package/lib/types/src/host/integration/remote-job-tools.d.ts +295 -0
  196. package/lib/types/src/host/integration/remote-tool-backend.d.ts +97 -0
  197. package/lib/types/src/host/integration/remote-tools.d.ts +206 -0
  198. package/lib/types/src/host/integration/root-registry.d.ts +115 -0
  199. package/lib/types/src/host/integration/roots/anchor-directory.d.ts +56 -0
  200. package/lib/types/src/host/integration/roots/bin-root-resolver.d.ts +81 -0
  201. package/lib/types/src/host/integration/roots/remote-home.d.ts +69 -0
  202. package/lib/types/src/host/integration/roots/root-registry.d.ts +144 -0
  203. package/lib/types/src/host/integration/roots/roots-wiring.d.ts +219 -0
  204. package/lib/types/src/host/integration/roots/session-root-index.d.ts +52 -0
  205. package/lib/types/src/host/integration/roots-api.d.ts +407 -0
  206. package/lib/types/src/host/integration/roots-wiring.d.ts +211 -0
  207. package/lib/types/src/host/integration/session/session-api.d.ts +88 -0
  208. package/lib/types/src/host/integration/session/session-bin-lock.d.ts +80 -0
  209. package/lib/types/src/host/integration/session/session-header.d.ts +151 -0
  210. package/lib/types/src/host/integration/session/session-labels.d.ts +56 -0
  211. package/lib/types/src/host/integration/session/session-route-wiring.d.ts +50 -0
  212. package/lib/types/src/host/integration/session-admin-service.d.ts +236 -0
  213. package/lib/types/src/host/integration/session-api.d.ts +81 -0
  214. package/lib/types/src/host/integration/session-bin-lock.d.ts +80 -0
  215. package/lib/types/src/host/integration/session-header.d.ts +118 -0
  216. package/lib/types/src/host/integration/session-labels.d.ts +56 -0
  217. package/lib/types/src/host/integration/session-route-wiring.d.ts +50 -0
  218. package/lib/types/src/host/integration/session-scope.d.ts +113 -0
  219. package/lib/types/src/host/integration/session-tools.d.ts +44 -0
  220. package/lib/types/src/host/integration/strict-arguments.d.ts +79 -0
  221. package/lib/types/src/host/integration/takeover/takeover-api.d.ts +62 -0
  222. package/lib/types/src/host/integration/takeover/takeover-route-wiring.d.ts +60 -0
  223. package/lib/types/src/host/integration/takeover/takeover-service.d.ts +167 -0
  224. package/lib/types/src/host/integration/terminal-manifest.d.ts +94 -0
  225. package/lib/types/src/host/integration/tools/management-tools.d.ts +245 -0
  226. package/lib/types/src/host/integration/tools/remote-job-tools.d.ts +316 -0
  227. package/lib/types/src/host/integration/tools/remote-lsp-tools.d.ts +43 -0
  228. package/lib/types/src/host/integration/tools/remote-tools.d.ts +236 -0
  229. package/lib/types/src/host/integration/tools/session-target.d.ts +52 -0
  230. package/lib/types/src/host/integration/tools/session-tools.d.ts +46 -0
  231. package/lib/types/src/host/integration/tools/strict-arguments.d.ts +79 -0
  232. package/lib/types/src/host/integration/tools/terminal-manifest.d.ts +94 -0
  233. package/lib/types/src/host/integration/transfer/remote-copy.d.ts +66 -0
  234. package/lib/types/src/host/integration/transfer/transfer-adapters.d.ts +48 -0
  235. package/lib/types/src/host/integration/transfer/transfer-service.d.ts +212 -0
  236. package/lib/types/src/host/integration/transfer/transfer-tracker.d.ts +106 -0
  237. package/lib/types/src/host/integration/transfer-adapters.d.ts +44 -0
  238. package/lib/types/src/host/integration/transfer-service.d.ts +164 -0
  239. package/lib/types/src/host/integration/transfer-tracker.d.ts +106 -0
  240. package/lib/types/src/host/integration/user-roots.d.ts +461 -0
  241. package/lib/types/src/index.d.ts +66 -0
  242. package/lib/types/src/local-bridge-refs.d.ts +26 -0
  243. package/lib/types/src/local-bridge.d.ts +36 -0
  244. package/lib/types/src/shared/format.d.ts +13 -0
  245. package/lib/types/src/wiring/connections.d.ts +189 -0
  246. package/lib/types/src/wiring/fs.d.ts +13 -0
  247. package/lib/types/src/wiring/helpers.d.ts +144 -0
  248. package/lib/types/src/wiring/install-memo.d.ts +32 -0
  249. package/lib/types/src/wiring/legacy-dirs.d.ts +62 -0
  250. package/lib/types/src/wiring/package-paths.d.ts +35 -0
  251. package/lib/types/src/wiring/roots.d.ts +21 -0
  252. package/lib/types/src/wiring/state-dir.d.ts +29 -0
  253. package/lib/types/src/wiring/tools.d.ts +35 -0
  254. package/package.json +120 -4
@@ -0,0 +1,252 @@
1
+ /**
2
+ * The visual vocabulary shared by this plugin's three web surfaces.
3
+ *
4
+ * **Everything comes from the platform's own theme tokens.** The token names were read off the runtime
5
+ * theme (`@deepseek-ai/dsh-client-ui-theme/lib/client.js`, the `--dsw-*` custom properties it defines); the
6
+ * names this plugin used before — `--dsw-alias-text-primary`, `--dsw-alias-border-subtle`,
7
+ * `--dsw-alias-surface-subtle`, `--dsw-alias-surface-warning` — **are not in the theme at all**, so every
8
+ * declaration silently fell back to `inherit` / `currentColor` / a hard-coded amber, and the commit button
9
+ * had no `background` whatsoever. That is why the panel looked like bare HTML with grey default buttons.
10
+ *
11
+ * Why a stylesheet and not React components:
12
+ *
13
+ * 1. The platform's own atoms (`dsh-client-ui-primitives`: `Button` / `Input` / `Alert`) are **not**
14
+ * reusable here — their factories import per-component CSS modules, which a third-party bundle cannot
15
+ * supply (the loader materialises only registered factories), and the package is not a dependency of
16
+ * this repository. The *tokens* those atoms are built from are on every page, so this module rebuilds
17
+ * the same vocabulary.
18
+ * 2. A React component carrying `useState` (for `:hover` / `:focus`) cannot be rendered by this
19
+ * repository's tests: they call the section functions **directly**, outside any React render, where a
20
+ * hook throws `Invalid hook call`. Classes + one injected stylesheet keep the templates pure
21
+ * `createElement` trees that the offline tests walk, while still giving the browser real `:hover`,
22
+ * `:active`, `:focus-visible` and `:disabled` states.
23
+ *
24
+ * The stylesheet is injected by the containers that mount these controls ({@link CONTROL_STYLE_ID} makes it
25
+ * idempotent), so importing this module on the host side has no side effect.
26
+ *
27
+ * @module dsh-remote-workspace/client/styles
28
+ */
29
+ /**
30
+ * Semantic tokens of the shipped theme, each with a neutral fallback so a page whose theme failed to load
31
+ * still renders something readable rather than transparent text.
32
+ */
33
+ export declare const TOKENS: {
34
+ /** Primary/body text. */
35
+ readonly textPrimary: "var(--dsw-alias-label-primary, inherit)";
36
+ /** Secondary text: descriptions, hints, row subtitles. */
37
+ readonly textSecondary: "var(--dsw-alias-label-secondary, inherit)";
38
+ /** Tertiary text: the quietest tier. */
39
+ readonly textTertiary: "var(--dsw-alias-label-tertiary, currentColor)";
40
+ /** Hairline around inputs and cards. */
41
+ readonly border: "var(--dsw-alias-border-l2, currentColor)";
42
+ /** Page background. */
43
+ readonly bgBase: "var(--dsw-alias-bg-base, transparent)";
44
+ /** Level-1 surface: the blocks and cards this plugin draws. */
45
+ readonly bgLayer1: "var(--dsw-alias-bg-layer-1, rgba(127, 127, 127, 0.06))";
46
+ /** Level-2 surface: controls that must read as inset inside a block. */
47
+ readonly bgLayer2: "var(--dsw-alias-bg-layer-2, transparent)";
48
+ /** Level-3 surface: chips and badges. */
49
+ readonly bgLayer3: "var(--dsw-alias-bg-layer-3, rgba(127, 127, 127, 0.12))";
50
+ /** The brand fill of a primary button. */
51
+ readonly primaryFill: "var(--dsw-alias-button-primary-fill, currentColor)";
52
+ /** Its hover step. */
53
+ readonly primaryHover: "var(--dsw-alias-button-primary-hover, currentColor)";
54
+ /** Its active/pressed step (the theme exposes one dimmed step for both). */
55
+ readonly primaryActive: "var(--dsw-alias-button-primary-dimmed, currentColor)";
56
+ /** Text on a primary fill. */
57
+ readonly onPrimary: "var(--dsw-alias-label-primary-foreground, currentColor)";
58
+ /** The neutral fill of a secondary (outline) button. */
59
+ readonly secondaryFill: "var(--dsw-alias-button-elevated-fill, transparent)";
60
+ /** A hover wash for quiet controls. */
61
+ readonly hoverWash: "var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.12))";
62
+ /** The focus ring the platform draws for keyboard focus. */
63
+ readonly focusRing: "var(--dsw-focus-ring-color, currentColor)";
64
+ /** Inline warning surface (the theme's warning tier, not an amber literal). */
65
+ readonly warnSurface: "var(--dsw-alias-state-warn-tertiary, rgba(255, 196, 0, 0.12))";
66
+ /** Inline warning text. */
67
+ readonly warnLabel: "var(--dsw-alias-state-warn-label, inherit)";
68
+ /** Error text. */
69
+ readonly errorPrimary: "var(--dsw-alias-state-error-primary, inherit)";
70
+ /** Successful state. */
71
+ readonly successPrimary: "var(--dsw-alias-state-success-primary, currentColor)";
72
+ /**
73
+ * The floating surface of a panel that is not inside the sidebar's own cards (the session entry).
74
+ * Read off the shipped theme's `design-platform.css` (`--dsw-alias-bg-overlay`), not invented.
75
+ */
76
+ readonly overlaySurface: "var(--dsw-alias-bg-overlay, Canvas)";
77
+ /** Panel elevation: the theme's own panel shadow, which already carries its hairline stroke. */
78
+ readonly panelElevation: "var(--dsw-elevation-panel, none)";
79
+ /** A stronger hairline, for the edge of a floating card over arbitrary content. */
80
+ readonly borderStrong: "var(--dsw-alias-border-l4, currentColor)";
81
+ /** The pressed/selected wash, one step past `hoverWash`. */
82
+ readonly activeWash: "var(--dsw-alias-interactive-bg-active, rgba(127, 127, 127, 0.18))";
83
+ /** Corner radii, straight from the theme so this plugin matches the surrounding UI. */
84
+ readonly radiusSm: "var(--dsw-radius-sm, 4px)";
85
+ readonly radiusMd: "var(--dsw-radius-md, 6px)";
86
+ readonly radiusLg: "var(--dsw-radius-lg, 8px)";
87
+ /** The large radius the theme uses for panels; the session entry's floating card. */
88
+ readonly radiusXl: "var(--dsw-radius-xl, 12px)";
89
+ };
90
+ /** The `id` of the injected `<style>` element; a second mount reuses it. */
91
+ export declare const CONTROL_STYLE_ID = "dsh-remote-workspace-controls";
92
+ /** Class names of the control vocabulary. Templates carry them; {@link controlStylesheet} styles them. */
93
+ export declare const CLASS: {
94
+ /** A titled block: one form or group. */
95
+ readonly block: "dsh-rw-block";
96
+ /** A section heading (`h2`/`h3`). */
97
+ readonly sectionTitle: "dsh-rw-title";
98
+ /** A block heading (`h4`). */
99
+ readonly blockTitle: "dsh-rw-subtitle";
100
+ /** A description paragraph. */
101
+ readonly description: "dsh-rw-description";
102
+ /** A hint/caption paragraph. */
103
+ readonly hint: "dsh-rw-hint";
104
+ /** A vertical stack. */
105
+ readonly stack: "dsh-rw-stack";
106
+ /** A list of rows. */
107
+ readonly list: "dsh-rw-list";
108
+ /** One row (root or connection). */
109
+ readonly row: "dsh-rw-row";
110
+ /** A row's title. */
111
+ readonly rowTitle: "dsh-rw-row-title";
112
+ /** A row's secondary text (path, note). */
113
+ readonly rowMeta: "dsh-rw-row-meta";
114
+ /** A badge/chip. */
115
+ readonly badge: "dsh-rw-badge";
116
+ /** A field column: caption above control. */
117
+ readonly field: "dsh-rw-field";
118
+ /** A row of fields plus its commit button. */
119
+ readonly form: "dsh-rw-form";
120
+ /**
121
+ * The directory-flow dialog root. Besides the card, this is the **container** for the inline-size query
122
+ * below (`container-type: inline-size`), so the same component reflows by the width it is *given* rather
123
+ * than by the viewport.
124
+ */
125
+ readonly dialog: "dsh-rw-dialog";
126
+ /** The dialog's title line: flex-wrap so a long title and its badge share one row when there is space. */
127
+ readonly dialogHeader: "dsh-rw-dialog-header";
128
+ /** The dialog's body: one column by default, two columns once the container says there is room. */
129
+ readonly dialogBody: "dsh-rw-dialog-body";
130
+ /** A field that spans the whole form row (`grid-column: 1 / -1`). */
131
+ readonly fieldSpan: "dsh-rw-field-span";
132
+ /** A button that owns the whole form row. */
133
+ readonly spanFull: "dsh-rw-span-full";
134
+ /** A field whose control shares its line with one button (the remote path and its browse button). */
135
+ readonly inputRow: "dsh-rw-input-row";
136
+ /** The scroll guard for long paths inside a narrow sidebar. */
137
+ readonly scrollX: "dsh-rw-scroll-x";
138
+ /** The button base. Every button also carries one variant class. */
139
+ readonly button: "dsh-rw-button";
140
+ /** Primary (brand-filled) button. */
141
+ readonly buttonPrimary: "dsh-rw-button-primary";
142
+ /** Secondary (outlined) button. */
143
+ readonly buttonSecondary: "dsh-rw-button-secondary";
144
+ /** Ghost button: quiet until hovered (per-row destructive actions). */
145
+ readonly buttonGhost: "dsh-rw-button-ghost";
146
+ /**
147
+ * Row button: a **navigable** list row (the directory browser's enterable entries).
148
+ *
149
+ * It exists because the ghost variant carries the theme's **error** tone (`--dsw-alias-state-error-primary`,
150
+ * which is `--dsw-static-red-600` in the light theme), and that is right for a delete action but wrong for a
151
+ * directory name: the first release of the browser rendered every directory in red. A row therefore declares
152
+ * its own colour instead of inheriting the destructive one, so the reading does not depend on rule order.
153
+ */
154
+ readonly buttonRow: "dsh-rw-button-row";
155
+ /**
156
+ * Chip: a control that sits in a row of the platform's own chrome (the composer tool row) and must not read
157
+ * as a stray bordered button next to them. Mirrors the platform's own control there: transparent, ~28px tall,
158
+ * a 12px label, `--dsw-radius-sm`, and the hover/active washes.
159
+ */
160
+ readonly buttonChip: "dsh-rw-button-chip";
161
+ /** Small size modifier. */
162
+ readonly compact: "dsh-rw-compact";
163
+ /** A text input. */
164
+ readonly input: "dsh-rw-input";
165
+ /** A `<select>`. */
166
+ readonly select: "dsh-rw-select";
167
+ /** An inline notice block. */
168
+ readonly notice: "dsh-rw-notice";
169
+ /** The neutral (info) notice tone. */
170
+ readonly noticeInfo: "dsh-rw-notice-info";
171
+ /** A status dot. */
172
+ readonly statusDot: "dsh-rw-status-dot";
173
+ /** A wrapped row of actions (buttons that may share a line when there is room). */
174
+ readonly actions: "dsh-rw-actions";
175
+ /** The directory browser: one listing level, one column whatever width it is given. */
176
+ readonly browser: "dsh-rw-browser";
177
+ /** The browser's current-path line. */
178
+ readonly browserPath: "dsh-rw-browser-path";
179
+ /** The browser's row list. */
180
+ readonly browserList: "dsh-rw-browser-list";
181
+ /** One `<li>` of the browser's list. */
182
+ readonly browserItem: "dsh-rw-browser-item";
183
+ /** A browser row that can be entered (a directory). */
184
+ readonly browserRow: "dsh-rw-browser-row";
185
+ /** A browser row that cannot be entered (a file or a symlink), rendered as text. */
186
+ readonly browserQuiet: "dsh-rw-browser-quiet";
187
+ /** The browser's committing action; owns its row. */
188
+ readonly browserCommit: "dsh-rw-browser-commit";
189
+ /** The session entry's mount point in the composer tool row. */
190
+ readonly sessionEntry: "dsh-rw-session-entry";
191
+ /** The session entry's trigger button. */
192
+ readonly sessionTrigger: "dsh-rw-session-trigger";
193
+ /**
194
+ * The session entry's card surface (the floating panel of `session-directory.ts`).
195
+ *
196
+ * Since the portal (`CLASS.portal`) owns the positioning, this class is the *surface*: the theme's overlay
197
+ * colour, its panel elevation and the hairline a card needs when it floats over arbitrary content.
198
+ */
199
+ readonly floating: "dsh-rw-floating";
200
+ /**
201
+ * The body-level container a card is portalled into (`src/client/portal.ts`): `position: fixed`, so no
202
+ * ancestor's `overflow`/`transform`/`contain` can clip or re-anchor the card, and viewport-bounded so it
203
+ * still fits a narrow window. See {@link controlStylesheet} for the placement it is combined with.
204
+ */
205
+ readonly portal: "dsh-rw-portal";
206
+ /** Placement of the directory-flow dialog's portal: the centre of the window. */
207
+ readonly portalTop: "dsh-rw-portal-top";
208
+ /** Placement of the session entry's portal: the centre of the window (its own surface, same placement). */
209
+ readonly portalComposer: "dsh-rw-portal-composer";
210
+ };
211
+ /**
212
+ * One class attribute from a class name plus optional modifiers.
213
+ *
214
+ * @param names - the classes, `false`/`undefined` entries are dropped.
215
+ * @returns the `className` value.
216
+ */
217
+ export declare function classes(...names: readonly (string | false | undefined)[]): string;
218
+ /**
219
+ * The complete stylesheet for the vocabulary above.
220
+ *
221
+ * Exported as a pure string so a test can assert the token contract offline (which tokens are used, that the
222
+ * primary button really declares a brand `background`, and that hover/active/disabled each have a rule)
223
+ * without a browser or a CSS engine.
224
+ *
225
+ * @returns CSS text, ready for a `<style>` element.
226
+ */
227
+ export declare function controlStylesheet(): string;
228
+ /** The subset of `Document` the injection needs; structural so no DOM lib is required to type it. */
229
+ export interface StyleDocument {
230
+ readonly head: {
231
+ appendChild(node: unknown): unknown;
232
+ };
233
+ getElementById(id: string): unknown;
234
+ createElement(tag: 'style'): {
235
+ id: string;
236
+ textContent: string;
237
+ };
238
+ }
239
+ /**
240
+ * Mount the stylesheet once per document.
241
+ *
242
+ * Idempotent through {@link CONTROL_STYLE_ID}, so two containers (the settings section and a directory-flow
243
+ * hole) share one element. Returns a disposer that removes it — the container calls this from an effect, so
244
+ * the stylesheet leaves with the last mounted surface.
245
+ *
246
+ * @param doc - the page's `document`, or `undefined` off-browser (tests) where the call is a no-op.
247
+ * @returns a disposer; never throws.
248
+ */
249
+ export declare function injectControlStyles(doc: StyleDocument | undefined): () => void;
250
+ /** The idempotent stylesheet mount for a React effect: `useEffect(injectControlStylesInBrowser, [])`. */
251
+ export declare function injectControlStylesInBrowser(): () => void;
252
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1,342 @@
1
+ /**
2
+ * The **directory-flow occupant**: "add a local or remote directory to a workspace".
3
+ *
4
+ * Both workspace surfaces of the platform declare one `single` hole — `sidebar.workspaces.directoryFlow` and
5
+ * `conversation.hero.workspace.directoryFlow` — and each renders its "add workspace" action **only while that
6
+ * hole is occupied** (a composition without a picker has no such action at all). This module fills both holes
7
+ * with one dialog offering two entries:
8
+ *
9
+ * - **local**: the platform's own chooser (`ctx.uiWorkspace.pickDirectory()`); the absolute path it answers
10
+ * is handed straight back through `onPicked`;
11
+ * - **remote**: a connection plus a remote path, POSTed to this plugin's own route (`add-root`); the host
12
+ * answers with the **anchor path** it registered for that root, and *that* path is what goes back through
13
+ * `onPicked`. The platform creates a workspace on a **host path**, and the router resolves a path under
14
+ * the anchor back to the remote root (`doc/02-platform/roots-http-route.md`), so the anchor — not the
15
+ * remote path — is the only answer that puts a remote directory in a workspace.
16
+ *
17
+ * Three layers, mirroring `roots-panel-view.ts`:
18
+ *
19
+ * 1. {@link workspaceDirectoryFlowReducer} — a pure state machine over the dialog's drafts, the connection
20
+ * list and the owner conversation. No DOM, no I/O, no React.
21
+ * 2. {@link createWorkspaceDirectoryFlow} — the same state plus the injected calls, the owner conversation
22
+ * and the **one result per open** gate.
23
+ * 3. {@link WorkspaceDirectoryFlowSection} — a pure template (`createElement`, never JSX: Node's native type
24
+ * stripping cannot transform JSX) and {@link WorkspaceDirectoryFlowContainer}, the thin React shell.
25
+ *
26
+ * Rules this file is responsible for:
27
+ *
28
+ * - **Exactly one outcome per open.** The owner contract says so; {@link canReport} is the single gate, and
29
+ * a settlement that arrives after the owner withdrew `open` (or after an earlier settlement) is dropped
30
+ * rather than reported.
31
+ * - **No submission while `busy`.** The owner is adopting a path; every commit affordance is disabled and
32
+ * every handler re-checks the gate, so a double submit is impossible even if a caller bypasses the view.
33
+ * - **Nothing is POSTed before validation.** `validateRootDraft` (the same authority the settings panel
34
+ * uses) is the only way a draft becomes a request, and its refusal is rendered through the dictionaries.
35
+ * - **No credentials.** The connection list comes from `parseRootsSnapshot`, which copies field by field.
36
+ *
37
+ * **Unverified** (see `doc/02-platform/workspace-directory-flow.md`): this flow has never been rendered or
38
+ * clicked in a real GUI, and it is not verified that the host accepts an anchor path as a workspace path.
39
+ *
40
+ * @module dsh-remote-workspace/client/workspace-directory-flow
41
+ */
42
+ import { type ReactNode } from 'react';
43
+ import type { SshDevKey } from './locale.ts';
44
+ import { type ApiResult, type ConnectionSummary, type FetchLike, type MessageKey } from './roots-panel.ts';
45
+ import { type RootsPanelCopy } from './roots-panel-view.ts';
46
+ /**
47
+ * The owner share of both directory-flow holes, quoted from the platform (`DirectoryFlowOwnerProps` in
48
+ * `@deepseek-ai/dsh-client-ui-workspace/client`, `packages/client/ui-workspace/src/client/contract/slots.ts`).
49
+ *
50
+ * Mirrored instead of imported: that package is not a dependency of this repository (its types are only in
51
+ * the DSH checkout), and the merge below has to compile here.
52
+ */
53
+ export interface DirectoryFlowOwnerProps {
54
+ /** True while a picking interaction is requested; flipping back to false withdraws the request. */
55
+ open: boolean;
56
+ /** True while the owner adopts a picked path (`createWorkspace` in flight); occupants disable their commit affordances. */
57
+ busy: boolean;
58
+ /** The operator picked a directory (absolute host path); the owner adopts it. */
59
+ onPicked: (path: string) => void;
60
+ /** The operator dismissed the interaction; the owner just closes the flow. */
61
+ onCancel: () => void;
62
+ /** The interaction itself failed (chooser missing, listing denied); the owner shows its error surface. */
63
+ onError: (message: string) => void;
64
+ }
65
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
66
+ interface SlotMap {
67
+ /** Directory-flow hole under the conversation empty-state picker. */
68
+ 'conversation.hero.workspace.directoryFlow': {
69
+ kind: 'single';
70
+ scope: 'root';
71
+ owner: DirectoryFlowOwnerProps;
72
+ };
73
+ /** Directory-flow hole under the sidebar browsing region. */
74
+ 'sidebar.workspaces.directoryFlow': {
75
+ kind: 'single';
76
+ scope: 'root';
77
+ owner: DirectoryFlowOwnerProps;
78
+ };
79
+ }
80
+ }
81
+ /** The two holes, in registration order; one occupant fills both. */
82
+ export declare const WORKSPACE_DIRECTORY_FLOW_SLOTS: readonly ["sidebar.workspaces.directoryFlow", "conversation.hero.workspace.directoryFlow"];
83
+ /** One of the two holes. */
84
+ export type WorkspaceDirectoryFlowSlotName = (typeof WORKSPACE_DIRECTORY_FLOW_SLOTS)[number];
85
+ /**
86
+ * Shadowing rank of this occupant inside the single-cell hole.
87
+ *
88
+ * A `single` slot holds one entry **per priority**: a second registration at the same priority throws
89
+ * (`SlotCore.register`), while a different priority shadows. The platform ships a picker for this hole
90
+ * (`ui-directory-picker-native` / `-browse`) whose profile row mounts it at the default priority 0, so a
91
+ * plain registration would throw — or, worse, fail the whole client half — in exactly the compositions that
92
+ * already have a picker. `-1` renders lower (ascending: lowest renders) and therefore replaces the shipped
93
+ * picker instead of colliding with it. The local entry below still reaches the same platform chooser.
94
+ */
95
+ export declare const WORKSPACE_DIRECTORY_FLOW_PRIORITY = -1;
96
+ /** The error {@link WorkspaceDirectoryFlowIo.addRemoteRoot} answers when the host did not report an anchor. */
97
+ export declare const NO_ANCHOR_ERROR = "no-anchor-path";
98
+ /** One shipped dictionary of this plugin's namespace, as the template consumes it. */
99
+ export type WorkspaceDirectoryFlowCopy = RootsPanelCopy;
100
+ /** The remote form's contents. Flat, so a patch is one spread and a test can name a single field. */
101
+ export interface WorkspaceDirectoryFlowDrafts {
102
+ /** Connection the remote root belongs to. */
103
+ readonly connectionId: string;
104
+ /** Remote path as typed; validated by the controller so a bad value is refused, not coerced. */
105
+ readonly path: string;
106
+ }
107
+ /** A change to the drafts; every field is optional so one handler can serve every input. */
108
+ export type WorkspaceDirectoryFlowDraftPatch = Partial<WorkspaceDirectoryFlowDrafts>;
109
+ /** Everything the dialog renders from. */
110
+ export interface WorkspaceDirectoryFlowState {
111
+ /** The owner's `open`, as last synced. */
112
+ readonly open: boolean;
113
+ /** The owner's `busy`, as last synced. */
114
+ readonly busy: boolean;
115
+ /** Bumped on every rising `open` edge; every call and every settlement is addressed by it. */
116
+ readonly generation: number;
117
+ /** True once this generation reported its one outcome (or the owner withdrew the request). */
118
+ readonly settled: boolean;
119
+ /** True while one of the flow's own calls is in flight. */
120
+ readonly pending: boolean;
121
+ /** The connections the remote form offers. */
122
+ readonly connections: readonly ConnectionSummary[];
123
+ /** The last connection-list failure, kept so the window can be re-opened without a stale list. */
124
+ readonly connectionsError?: string;
125
+ /** The remote form's contents. */
126
+ readonly drafts: WorkspaceDirectoryFlowDrafts;
127
+ /** The last refusal or call failure, rendered through the dictionaries. */
128
+ readonly notice?: MessageKey;
129
+ }
130
+ /** What the reducer accepts. */
131
+ export type WorkspaceDirectoryFlowAction =
132
+ /** The owner asked for the interaction (an edge: `open` false → true). */
133
+ {
134
+ readonly type: 'opened';
135
+ }
136
+ /** The owner withdrew the request without an outcome; nothing may be reported for that generation. */
137
+ | {
138
+ readonly type: 'withdrawn';
139
+ }
140
+ /** The owner's adoption flag moved. */
141
+ | {
142
+ readonly type: 'busy-changed';
143
+ readonly busy: boolean;
144
+ }
145
+ /** The connection list arrived. */
146
+ | {
147
+ readonly type: 'connections-loaded';
148
+ readonly connections: readonly ConnectionSummary[];
149
+ }
150
+ /** The connection list failed. */
151
+ | {
152
+ readonly type: 'connections-failed';
153
+ readonly error: string;
154
+ }
155
+ /** One draft field changed: clears the inline message, because the user is fixing it. */
156
+ | {
157
+ readonly type: 'draft-changed';
158
+ readonly patch: WorkspaceDirectoryFlowDraftPatch;
159
+ }
160
+ /** A call is in flight; the reducer itself refuses it when the gate is closed. */
161
+ | {
162
+ readonly type: 'submitted';
163
+ }
164
+ /** A draft was refused before any call; nothing was sent. */
165
+ | {
166
+ readonly type: 'refused';
167
+ readonly message: MessageKey;
168
+ }
169
+ /** One outcome was reported to the owner. */
170
+ | {
171
+ readonly type: 'settled';
172
+ };
173
+ /** The empty remote form. */
174
+ export declare function emptyWorkspaceDirectoryFlowDrafts(): WorkspaceDirectoryFlowDrafts;
175
+ /** @returns the state a freshly created flow holds. */
176
+ export declare function initialWorkspaceDirectoryFlowState(): WorkspaceDirectoryFlowState;
177
+ /**
178
+ * The dialog's state machine. Pure: same state + same action ⇒ same result, and an action that changes
179
+ * nothing returns the **same object**, so a subscriber is never woken for a no-op.
180
+ *
181
+ * @param state - the current state.
182
+ * @param action - what happened.
183
+ * @returns the next state.
184
+ */
185
+ export declare function workspaceDirectoryFlowReducer(state: WorkspaceDirectoryFlowState, action: WorkspaceDirectoryFlowAction): WorkspaceDirectoryFlowState;
186
+ /** The dialog's complete render input. */
187
+ export interface WorkspaceDirectoryFlowView {
188
+ /** True while the owner asks for the interaction. */
189
+ readonly visible: boolean;
190
+ /** True while one of the flow's own calls is in flight. */
191
+ readonly pending: boolean;
192
+ /** True while the owner adopts a picked path. */
193
+ readonly busy: boolean;
194
+ /** True once this generation reported its outcome. */
195
+ readonly settled: boolean;
196
+ /** May the local entry start the platform chooser? */
197
+ readonly canPickLocal: boolean;
198
+ /** May the remote form be submitted? */
199
+ readonly canSubmitRemote: boolean;
200
+ /** May the interaction be dismissed? (Never while the owner is adopting.) */
201
+ readonly canCancel: boolean;
202
+ /** The connections the remote form offers. */
203
+ readonly connections: readonly ConnectionSummary[];
204
+ /** The last refusal or call failure. */
205
+ readonly notice?: MessageKey;
206
+ /** The last connection-list failure. */
207
+ readonly connectionsError?: string;
208
+ }
209
+ /**
210
+ * Turn the state into render input.
211
+ *
212
+ * @param state - the flow state.
213
+ * @returns the visible flags, the connection list, the messages and the three gates.
214
+ */
215
+ export declare function selectWorkspaceDirectoryFlowView(state: WorkspaceDirectoryFlowState): WorkspaceDirectoryFlowView;
216
+ /** True when the interaction may still be driven: open, not adopting, no call in flight, nothing reported. */
217
+ export declare function canPickLocal(state: WorkspaceDirectoryFlowState): boolean;
218
+ /** True when the remote form is complete enough to submit (the path rules are checked on submit). */
219
+ export declare function canSubmitRemote(state: WorkspaceDirectoryFlowState): boolean;
220
+ /** True when `cancel()` may report the dismissal. */
221
+ export declare function canCancel(state: WorkspaceDirectoryFlowState): boolean;
222
+ /**
223
+ * True when a settlement may still be reported to the owner: this generation is open and nothing was
224
+ * reported for it yet. The one gate behind the owner contract's "exactly one outcome per open".
225
+ *
226
+ * @param state - the flow state.
227
+ * @returns whether a result may be reported now.
228
+ */
229
+ export declare function canReport(state: WorkspaceDirectoryFlowState): boolean;
230
+ /**
231
+ * The three calls the flow makes. All of them are injected, so the state machine is testable offline
232
+ * (红线 3) and no test starts a server or opens a chooser.
233
+ */
234
+ export interface WorkspaceDirectoryFlowIo {
235
+ /** The connections the remote form offers (this plugin's own `GET`, `connections` field). */
236
+ loadConnections(): Promise<ApiResult<readonly ConnectionSummary[]>>;
237
+ /**
238
+ * Add one remote root and answer the **anchor path** the host registered for it.
239
+ * `{ ok: false, error: NO_ANCHOR_ERROR }` means the host answered without one, which is not a path the
240
+ * owner may create a workspace on.
241
+ */
242
+ addRemoteRoot(input: {
243
+ readonly connectionId: string;
244
+ readonly path: string;
245
+ }): Promise<ApiResult<string>>;
246
+ /** The platform's local chooser; `null` means the operator dismissed it. */
247
+ pickDirectory(): Promise<string | null>;
248
+ }
249
+ /**
250
+ * Bind the flow's three calls to the browser and the platform.
251
+ *
252
+ * `loadConnections` reuses `createRootsApi`'s `GET` (and therefore its field-by-field credential filter);
253
+ * `addRemoteRoot` posts the same body the settings panel posts, but reads the answer's `anchorPath`, which
254
+ * the snapshot projection drops because it belongs to the *live* root, not to the persisted entry.
255
+ *
256
+ * @param options - `fetch` (same-origin in the app), the platform chooser, and a path override for tests.
257
+ * @returns the calls the flow uses.
258
+ */
259
+ export declare function createWorkspaceDirectoryFlowIo(options: {
260
+ readonly fetchImpl: FetchLike;
261
+ readonly pickDirectory: () => Promise<string | null>;
262
+ readonly path?: string;
263
+ }): WorkspaceDirectoryFlowIo;
264
+ /** What the stateful half needs: the injected calls plus the active dictionary, read at report time. */
265
+ export interface WorkspaceDirectoryFlowDeps {
266
+ readonly io: WorkspaceDirectoryFlowIo;
267
+ /** The dictionary of the active locale. A thunk, because `onError` needs text and the locale can change. */
268
+ readonly copy: () => WorkspaceDirectoryFlowCopy;
269
+ }
270
+ /** The calls the container makes. `createWorkspaceDirectoryFlow` owns how they are implemented. */
271
+ export interface WorkspaceDirectoryFlowController {
272
+ /** The current state; a stable reference until something changes (safe for `useSyncExternalStore`). */
273
+ readonly getState: () => WorkspaceDirectoryFlowState;
274
+ /** Watch for changes; returns the disposer. */
275
+ readonly subscribe: (listener: () => void) => () => void;
276
+ /** Push the owner's current conversation in; called by the shell on every render. */
277
+ readonly syncOwner: (props: DirectoryFlowOwnerProps) => void;
278
+ /** Change one draft field. */
279
+ readonly changeDraft: (patch: WorkspaceDirectoryFlowDraftPatch) => void;
280
+ /** Run the platform chooser and report what it answered. */
281
+ readonly pickLocal: () => Promise<void>;
282
+ /** Validate the remote form, add the root and report its anchor path. */
283
+ readonly submitRemote: () => Promise<void>;
284
+ /** Report the operator's dismissal. */
285
+ readonly cancel: () => void;
286
+ /** Re-arm after a mount (React's development replay runs the cleanup once per lifetime). */
287
+ readonly activate: () => void;
288
+ /** Stop accepting results and drop listeners. */
289
+ readonly dispose: () => void;
290
+ }
291
+ /**
292
+ * Build the stateful half of the directory flow.
293
+ *
294
+ * The one gate that matters lives here: {@link canReport} is consulted **before** the owner is told anything,
295
+ * so a call that settles after the owner withdrew `open` — or a second settlement racing the first — is
296
+ * dropped instead of driving `onPicked` / `onCancel` / `onError` twice.
297
+ *
298
+ * @param deps - the injected calls and the active dictionary.
299
+ * @returns the controller the container subscribes to.
300
+ */
301
+ export declare function createWorkspaceDirectoryFlow(deps: WorkspaceDirectoryFlowDeps): WorkspaceDirectoryFlowController;
302
+ /** What the dialog's controls call. All of them are no-ops when the gate is closed. */
303
+ export interface WorkspaceDirectoryFlowSectionActions {
304
+ readonly onDraftChange: (patch: WorkspaceDirectoryFlowDraftPatch) => void;
305
+ readonly onPickLocal: () => void;
306
+ readonly onSubmitRemote: () => void;
307
+ readonly onCancel: () => void;
308
+ }
309
+ /** Props of the pure template: state in, callbacks out, no hooks and no store access. */
310
+ export interface WorkspaceDirectoryFlowSectionProps {
311
+ readonly copy: WorkspaceDirectoryFlowCopy;
312
+ readonly state: WorkspaceDirectoryFlowState;
313
+ readonly actions: WorkspaceDirectoryFlowSectionActions;
314
+ }
315
+ /**
316
+ * Render the directory flow. Pure: calling it twice with the same props yields the same tree, and a closed
317
+ * flow renders nothing (the owner keeps the occupant mounted and drives it through `open`).
318
+ *
319
+ * @param props - copy, state and callbacks.
320
+ * @returns the dialog element, or `null` while closed.
321
+ */
322
+ export declare function WorkspaceDirectoryFlowSection({ copy, state, actions }: WorkspaceDirectoryFlowSectionProps): ReactNode;
323
+ /** Props of the React shell: the injected face plus the owner's conversation, which arrives as normal props. */
324
+ export interface WorkspaceDirectoryFlowContainerProps extends DirectoryFlowOwnerProps {
325
+ /** The active locale's dictionary; the shell resolves its own dynamic keys through it. */
326
+ readonly copy: WorkspaceDirectoryFlowCopy;
327
+ /** The injected calls. Created once by the entry (`index.ts`), never by the component. */
328
+ readonly io: WorkspaceDirectoryFlowIo;
329
+ }
330
+ /**
331
+ * Subscribe to a per-mount controller and render {@link WorkspaceDirectoryFlowSection}.
332
+ *
333
+ * One controller **per mount**, not per plugin load: the two holes are two independent surfaces with two
334
+ * independent owner conversations, and both can be mounted at the same time.
335
+ *
336
+ * @param props - the owner conversation plus the injected dictionary and calls.
337
+ * @returns the dialog element, or `null` while the owner has not asked for it.
338
+ */
339
+ export declare function WorkspaceDirectoryFlowContainer(props: WorkspaceDirectoryFlowContainerProps): ReactNode;
340
+ /** The dictionary keys this module renders, so a test can assert the two locales carry them without a second list. */
341
+ export type WorkspaceDirectoryFlowKey = Extract<SshDevKey, 'flowTitle' | 'flowDescription' | 'flowLocalHeading' | 'flowLocalHint' | 'flowPickLocal' | 'flowRemoteHeading' | 'flowRemoteHint' | 'flowConfirmRemote' | 'flowCancel' | 'flowPending' | 'flowBusy' | 'flowNoConnections' | 'flowNoAnchor' | 'flowUnavailable' | 'rootsConnection' | 'rootsConnectionPlaceholder' | 'rootsRemotePathLabel'>;
342
+ //# sourceMappingURL=workspace-directory-flow.d.ts.map