dsh-plugin-core 0.20.3 → 0.20.6

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 (159) hide show
  1. package/dist/design/tokens-css.generated.d.ts +1 -1
  2. package/dist/design/tokens-css.generated.d.ts.map +1 -1
  3. package/dist/design/tokens-css.generated.js +1 -1
  4. package/dist/design/tokens-css.generated.js.map +1 -1
  5. package/dist/explorer/FileBrowser.d.ts +13 -6
  6. package/dist/explorer/FileBrowser.d.ts.map +1 -1
  7. package/dist/explorer/FileBrowser.js +190 -305
  8. package/dist/explorer/FileBrowser.js.map +1 -1
  9. package/dist/explorer/crumb-nav.d.ts +45 -0
  10. package/dist/explorer/crumb-nav.d.ts.map +1 -0
  11. package/dist/explorer/crumb-nav.js +75 -0
  12. package/dist/explorer/crumb-nav.js.map +1 -0
  13. package/dist/explorer/crumbs-menu.d.ts +37 -0
  14. package/dist/explorer/crumbs-menu.d.ts.map +1 -0
  15. package/dist/explorer/crumbs-menu.js +102 -0
  16. package/dist/explorer/crumbs-menu.js.map +1 -0
  17. package/dist/explorer/explorer-css.generated.d.ts +1 -1
  18. package/dist/explorer/explorer-css.generated.d.ts.map +1 -1
  19. package/dist/explorer/explorer-css.generated.js +1 -1
  20. package/dist/explorer/explorer-css.generated.js.map +1 -1
  21. package/dist/explorer/fs-face.d.ts +36 -0
  22. package/dist/explorer/fs-face.d.ts.map +1 -0
  23. package/dist/explorer/fs-face.js +74 -0
  24. package/dist/explorer/fs-face.js.map +1 -0
  25. package/dist/explorer/index.d.ts +2 -1
  26. package/dist/explorer/index.d.ts.map +1 -1
  27. package/dist/explorer/index.js +2 -1
  28. package/dist/explorer/index.js.map +1 -1
  29. package/dist/explorer/landing.d.ts +26 -0
  30. package/dist/explorer/landing.d.ts.map +1 -0
  31. package/dist/explorer/landing.js +43 -0
  32. package/dist/explorer/landing.js.map +1 -0
  33. package/dist/explorer/open.d.ts +35 -38
  34. package/dist/explorer/open.d.ts.map +1 -1
  35. package/dist/explorer/open.js +124 -156
  36. package/dist/explorer/open.js.map +1 -1
  37. package/dist/explorer/preview/BytesPreview.d.ts +2 -3
  38. package/dist/explorer/preview/BytesPreview.d.ts.map +1 -1
  39. package/dist/explorer/preview/BytesPreview.js +3 -3
  40. package/dist/explorer/preview/BytesPreview.js.map +1 -1
  41. package/dist/explorer/preview/HtmlPreview.d.ts +2 -3
  42. package/dist/explorer/preview/HtmlPreview.d.ts.map +1 -1
  43. package/dist/explorer/preview/HtmlPreview.js +4 -5
  44. package/dist/explorer/preview/HtmlPreview.js.map +1 -1
  45. package/dist/explorer/preview/OfficePreview.d.ts +2 -1
  46. package/dist/explorer/preview/OfficePreview.d.ts.map +1 -1
  47. package/dist/explorer/preview/OfficePreview.js +9 -2
  48. package/dist/explorer/preview/OfficePreview.js.map +1 -1
  49. package/dist/explorer/preview/TextPreview.d.ts +2 -3
  50. package/dist/explorer/preview/TextPreview.d.ts.map +1 -1
  51. package/dist/explorer/preview/TextPreview.js +3 -3
  52. package/dist/explorer/preview/TextPreview.js.map +1 -1
  53. package/dist/explorer/preview/index.d.ts +6 -3
  54. package/dist/explorer/preview/index.d.ts.map +1 -1
  55. package/dist/explorer/preview/index.js +3 -6
  56. package/dist/explorer/preview/index.js.map +1 -1
  57. package/dist/explorer/preview-title-bar.d.ts +26 -0
  58. package/dist/explorer/preview-title-bar.d.ts.map +1 -0
  59. package/dist/explorer/preview-title-bar.js +48 -0
  60. package/dist/explorer/preview-title-bar.js.map +1 -0
  61. package/dist/explorer/types.d.ts +25 -23
  62. package/dist/explorer/types.d.ts.map +1 -1
  63. package/dist/explorer/types.js +61 -120
  64. package/dist/explorer/types.js.map +1 -1
  65. package/dist/host/fs-route.d.ts +25 -0
  66. package/dist/host/fs-route.d.ts.map +1 -0
  67. package/dist/host/fs-route.js +243 -0
  68. package/dist/host/fs-route.js.map +1 -0
  69. package/dist/host/index.d.ts +2 -0
  70. package/dist/host/index.d.ts.map +1 -1
  71. package/dist/host/index.js +1 -0
  72. package/dist/host/index.js.map +1 -1
  73. package/dist/session/session-css.generated.d.ts +1 -1
  74. package/dist/session/session-css.generated.d.ts.map +1 -1
  75. package/dist/session/session-css.generated.js +1 -1
  76. package/dist/session/session-css.generated.js.map +1 -1
  77. package/dist/ui/DateTimePicker.d.ts.map +1 -1
  78. package/dist/ui/DateTimePicker.js +5 -1
  79. package/dist/ui/DateTimePicker.js.map +1 -1
  80. package/dist/ui/Modal.d.ts.map +1 -1
  81. package/dist/ui/Modal.js +7 -2
  82. package/dist/ui/Modal.js.map +1 -1
  83. package/dist/ui/PanelDock.d.ts.map +1 -1
  84. package/dist/ui/PanelDock.js +4 -8
  85. package/dist/ui/PanelDock.js.map +1 -1
  86. package/dist/ui/Picker.d.ts.map +1 -1
  87. package/dist/ui/Picker.js +6 -3
  88. package/dist/ui/Picker.js.map +1 -1
  89. package/dist/ui/code-viewer-css.generated.d.ts +1 -1
  90. package/dist/ui/code-viewer-css.generated.d.ts.map +1 -1
  91. package/dist/ui/code-viewer-css.generated.js +1 -1
  92. package/dist/ui/code-viewer-css.generated.js.map +1 -1
  93. package/dist/ui/controls/datetime-css.generated.d.ts +1 -1
  94. package/dist/ui/controls/datetime-css.generated.d.ts.map +1 -1
  95. package/dist/ui/controls/datetime-css.generated.js +1 -1
  96. package/dist/ui/controls/datetime-css.generated.js.map +1 -1
  97. package/dist/ui/controls/field-css.generated.d.ts +1 -1
  98. package/dist/ui/controls/field-css.generated.d.ts.map +1 -1
  99. package/dist/ui/controls/field-css.generated.js +1 -1
  100. package/dist/ui/controls/field-css.generated.js.map +1 -1
  101. package/dist/ui/controls/loading-css.generated.d.ts +1 -1
  102. package/dist/ui/controls/loading-css.generated.d.ts.map +1 -1
  103. package/dist/ui/controls/loading-css.generated.js +1 -1
  104. package/dist/ui/controls/loading-css.generated.js.map +1 -1
  105. package/dist/ui/feedback-css.generated.d.ts +1 -1
  106. package/dist/ui/feedback-css.generated.d.ts.map +1 -1
  107. package/dist/ui/feedback-css.generated.js +1 -1
  108. package/dist/ui/feedback-css.generated.js.map +1 -1
  109. package/dist/ui/marquee-css.generated.d.ts +1 -1
  110. package/dist/ui/marquee-css.generated.d.ts.map +1 -1
  111. package/dist/ui/marquee-css.generated.js +1 -1
  112. package/dist/ui/marquee-css.generated.js.map +1 -1
  113. package/dist/ui/overlay-css.generated.d.ts +1 -1
  114. package/dist/ui/overlay-css.generated.d.ts.map +1 -1
  115. package/dist/ui/overlay-css.generated.js +1 -1
  116. package/dist/ui/overlay-css.generated.js.map +1 -1
  117. package/dist/ui/overlay.d.ts +8 -2
  118. package/dist/ui/overlay.d.ts.map +1 -1
  119. package/dist/ui/overlay.js +7 -6
  120. package/dist/ui/overlay.js.map +1 -1
  121. package/dist/ui/panel-css.d.ts +2 -0
  122. package/dist/ui/panel-css.d.ts.map +1 -0
  123. package/dist/ui/panel-css.generated.d.ts +2 -0
  124. package/dist/ui/panel-css.generated.d.ts.map +1 -0
  125. package/dist/ui/panel-css.generated.js +4 -0
  126. package/dist/ui/panel-css.generated.js.map +1 -0
  127. package/dist/ui/panel-css.js +2 -0
  128. package/dist/ui/panel-css.js.map +1 -0
  129. package/dist/ui/panel-dock-css.generated.d.ts +1 -1
  130. package/dist/ui/panel-dock-css.generated.d.ts.map +1 -1
  131. package/dist/ui/panel-dock-css.generated.js +1 -1
  132. package/dist/ui/panel-dock-css.generated.js.map +1 -1
  133. package/dist/ui/panel.d.ts +41 -0
  134. package/dist/ui/panel.d.ts.map +1 -0
  135. package/dist/ui/panel.js +37 -0
  136. package/dist/ui/panel.js.map +1 -0
  137. package/dist/ui/picker-css.generated.d.ts +1 -1
  138. package/dist/ui/picker-css.generated.d.ts.map +1 -1
  139. package/dist/ui/picker-css.generated.js +1 -1
  140. package/dist/ui/picker-css.generated.js.map +1 -1
  141. package/dist/ui/side-panel-css.generated.d.ts +1 -1
  142. package/dist/ui/side-panel-css.generated.d.ts.map +1 -1
  143. package/dist/ui/side-panel-css.generated.js +1 -1
  144. package/dist/ui/side-panel-css.generated.js.map +1 -1
  145. package/dist/utils/i18n.d.ts +1 -1
  146. package/dist/utils/i18n.d.ts.map +1 -1
  147. package/dist/utils/i18n.js +6 -0
  148. package/dist/utils/i18n.js.map +1 -1
  149. package/docs/design/architecture.md +9 -4
  150. package/docs/design/external/dsh-host.md +60 -0
  151. package/docs/design/style.md +54 -0
  152. package/docs/help/explorer.doc.ts +35 -30
  153. package/docs/help/explorer.md +23 -19
  154. package/docs/help/integration.doc.ts +15 -10
  155. package/docs/help/integration.md +15 -10
  156. package/docs/help/ui.doc.ts +2 -2
  157. package/docs/help/ui.md +2 -2
  158. package/docs/requirements.md +2 -1
  159. package/package.json +1 -1
@@ -10,21 +10,21 @@
10
10
 
11
11
  > EN: Overview
12
12
 
13
- 提供工作区文件浏览与选择,数据来自宿主提供的工作区文件快照。
13
+ 提供文件浏览与选择;数据走宿主侧只读 FS 路由,**不限工作区**(工作区之外也能浏览)。界面与原版一致。
14
14
 
15
- > EN: Workspace file browsing and picking, driven by the host-provided workspace file snapshot.
15
+ > EN: File browsing and picking **not limited to workspaces**: one root tree (workspace nodes + the real root), served by a host-side read-only FS route.
16
16
 
17
- **接线归核心**:client `apply` 里调一次 `connectHost(ctx)`,读文件服务、会话 id、工作区快照都由核心自己解,调用时只给容器和路径。
17
+ **接线归核心,但要两条**:宿主 `apply` 调 `serveFsRoute({ path })`,client `apply` 用同一个常量调 `connectFs({ path })`(`connectHost(ctx)` 照旧负责工作区快照)。浏览**不需要会话**。
18
18
 
19
- > EN: **Wiring belongs to core**: call `connectHost(ctx)` once in the client apply; the file service, session id and workspace snapshot are resolved by core — you only pass a container and a path.
19
+ > EN: **Core does the wiring, but with two calls**: `serveFsRoute({ path })` on the host side and `connectFs({ path })` with the same constant on the client side (`connectHost(ctx)` still covers the workspace snapshot). Browsing needs **no session**.
20
20
 
21
21
  落点只给一个元素:页面里**包住主区**的那个(核心会补成 flex 行、让主区伸展,关闭时还原),不要传 `document.body` 再自己套 fixed——那是浮层,不是分栏。
22
22
 
23
23
  > EN: Pass one element: the one wrapping the main area. Core makes it a flex row and lets the main area stretch, restoring it on close. Never pass `document.body` with your own fixed positioning — that is an overlay, not a split.
24
24
 
25
- 绝对路径按最长前缀匹配所属工作区;落在任何工作区外时,文件照样能预览(官方 read 允许工作区外文件),只有目录树退化成只读路径栏。
25
+ 面包屑口径不变:路径所属工作区名作根名,其下按相对层级展开。▾ 下拉**根层置顶**(其余仍列各工作区):POSIX 是一条「根目录」,**Windows 没有根目录这东西,列的是盘符**(`C:/`、`D:/`…,由宿主 `op:roots` 探出来)。点其他工作区 = 直接列该区根目录(不需要会话)。
26
26
 
27
- > EN: Absolute paths are matched to their workspace by longest prefix. Outside every workspace the file still previews (official read allows it); only the directory tree degrades to a read-only path bar.
27
+ > EN: Inside a workspace the breadcrumb starts at its name and the ▾ menu nests it there; outside it starts at the real root (`/` or a drive). A workspace root still appears in the real path, tagged “Workspace”.
28
28
 
29
29
  ## 0. 装包
30
30
 
@@ -43,20 +43,25 @@ pnpm add dsh-plugin-core
43
43
  > EN: `connectHost(ctx)` takes just that one slice; the whole ctx never enters core.
44
44
 
45
45
  ```tsx id="explorer-usage-code"
46
- import { openFilePreview, pickWorkspaceFile } from 'dsh-plugin-core/explorer'
46
+ import { connectFs, openFilePreview, pickWorkspaceFile } from 'dsh-plugin-core/explorer'
47
+ import { serveFsRoute } from 'dsh-plugin-core/host'
48
+ import { FS_PATH } from './paths' // 你自己的常量:宿主与 client 必须用同一个
47
49
 
48
- // 只在 client apply 里接线一次(接工作区快照 + 读文件服务),之后不用再碰服务
50
+ // ── 宿主半边(apply):挂一条只读文件路由(不限工作区;读文件/列目录都在它上面) ──
51
+ serveFsRoute(ctx, { path: FS_PATH })
52
+
53
+ // ── client 半边(apply):接一次(connectHost 照旧负责工作区快照) ──
49
54
  connectHost(ctx)
55
+ connectFs({ path: FS_PATH })
50
56
 
51
57
  // 右侧分栏预览(占宽度、挤窄主区):一个容器 + 一个路径就够
52
- openFilePreview({ mount, path: '/abs/ledger' }) // 文件夹 ⇒ 目录树;文件 ⇒ 直接预览
53
- openFilePreview({ mount, path: 'docs/README.md' }) // 工作区相对路径也行
54
-
55
- // 覆盖项:指定工作区快照 / 指定会话 / Office 转 PDF / 与别处共用的根缓存
56
- openFilePreview({ mount, path, workspaceAnchors, sessionId, officeToPdf, rootCache })
58
+ openFilePreview({ mount, path: '/abs/ledger' }) // 文件夹 ⇒ 目录树;文件 ⇒ 直接预览
59
+ openFilePreview({ mount, path: 'docs/README.md' }) // 工作区相对路径也行(相对谁见下)
60
+ openFilePreview({ mount }) // 不给路径 ⇒ 根节点(工作区清单 + 盘符)
57
61
 
58
- // 锚定弹窗选文件(Promise 返回路径,不自己挂 dock)
62
+ // 锚定弹窗选文件(Promise 返回**绝对路径**,不自己挂 dock)
59
63
  const r = await pickWorkspaceFile({ anchor, filter: { exts: ['md'] } })
64
+ const dir = await pickWorkspaceFile({ anchor, filter: { dirs: true } }) // 只选文件夹
60
65
  ```
61
66
 
62
67
  ## API
@@ -67,12 +72,11 @@ const r = await pickWorkspaceFile({ anchor, filter: { exts: ['md'] } })
67
72
  |---|---|---|---|---|---|
68
73
  | `openFilePreview(opts)` | `OpenFilePreviewOptions => FilePreviewHandle` | — | — | 在右侧分栏打开文件 / 文件夹预览,返回可关闭它的句柄。预览内容二选一:`path`(核心解析并渲染)或 `content`(由调用方给内容) | Opens a file/folder preview in the right-side dock and returns a handle to close it. Body is one of two: `path` (resolved and rendered by core) or `content` (rendered from your own node) |
69
74
  | `opts.mount` | `HTMLElement` | 是 | — | 页面里**包住主区**的那个元素;不必自己写 flex(核心补成 flex 行并让主区伸展,关闭时还原)。不要传 `document.body` | The element wrapping the main area; no flex needed (core turns it into a flex row, stretches the main area, and restores it on close). Never `document.body` |
70
- | `opts.workspaceFiles` | `WorkspaceFilesFace` | — | — | 读文件服务。**一般不用传**:`connectHost(ctx)` 接线后核心自己持有;传了以传入为准。传 `content` 时可省 | File service. **Usually unnecessary**: core holds it after `connectHost(ctx)`; when passed it wins. Optional when passing `content` |
71
- | `opts.sessionId` | `string` | — | — | 该工作区的真实会话 id。**一般不用传**:按路径匹配所属工作区,匹配不出就取最近有会话的工作区;传 `content` 时可省 | A real session id. **Usually unnecessary**: resolved from the path’s workspace, else the most recently updated workspace with a session; optional when passing `content` |
72
- | `opts.path` | `string` | — | — | 工作区相对路径;绝对路径会自动按最长前缀匹配所属工作区。与 `content` 二选一 | Workspace-relative path; absolute paths are matched to their workspace by longest prefix. Mutually exclusive with `content` |
75
+ | `opts.sessionId` | `string` | — | — | **只在两种情况下需要**:① 相对路径的基准工作区;② Office 预览(转 PDF 要一条有效会话,缺省由核心取最近有会话的工作区) | Needed in exactly two cases: (1) the base workspace for a relative path; (2) Office preview (conversion needs one valid session; core falls back to the most recent workspace with one) |
76
+ | `opts.path` | `string` | — | — | 绝对路径或工作区相对路径(相对路径要有可解析的基准,否则返回 `null` 并 warn);不传 ⇒ 根节点。与 `content` 二选一 | Absolute or workspace-relative (a relative one needs a resolvable base, else `null` + a warning); omitted ⇒ the root node. Mutually exclusive with `content` |
73
77
  | `opts.content` | `() => ReactNode` | — | — | 自定义预览内容,塞进同一个右侧 dock;传它时不需要 `path` | Custom preview body rendered in the same dock; no `path` needed when set |
74
78
  | `opts.officeToPdf` | `OfficeToPdfFace` | — | — | Office 转 PDF 能力;缺失则该格式走「不支持」而不是报错 | Office→PDF capability; without it that format shows "unsupported" instead of erroring |
75
79
  | `opts.rootName` | `string` | — | — | 兜底显示名(路径匹配不出工作区时用) | Fallback display name, used when no workspace matches the path |
76
- | `pickWorkspaceFile(opts)` | `PickWorkspaceFileOptions => Promise<PickedFile>` | — | — | 锚定按钮弹出模态选文件,解析出 `{ path, isDir }` | Anchored modal picker that resolves `{ path, isDir }` |
80
+ | `pickWorkspaceFile(opts)` | `PickWorkspaceFileOptions => Promise<PickedPath \| null>` | — | — | 锚定按钮弹出模态选文件,解析出 `{ path, isDir, workspaceRel?, workspace? }`;`path` 是**绝对路径** | Anchored modal picker resolving `{ path, isDir, workspaceRel?, workspace? }`; `path` is **absolute** |
77
81
  | `opts.anchor` | `HTMLElement` | 是 | — | 模态贴着它弹 | The modal anchors next to it |
78
- | `opts.filter` | `PickFilter` | — | — | 按扩展名过滤 `{ exts: ['md'] }`;`dirs: true` 则只允许选文件夹 | Filter by extension `{ exts: ['md'] }`; `dirs: true` restricts picking to folders |
82
+ | `opts.filter` | `PickFilter` | — | — | `exts` 只要这些扩展名(目录仍可展开导航);`dirs: true` 只允许选文件夹(文件不渲染) | `exts` limits files (folders still expand); `dirs: true` picks folders only (files hidden, plus a “Select this folder” action in the crumb bar) |
@@ -26,16 +26,21 @@ const useUtils = `import { formatDateTime, relativeFuture, formatBytes } from 'd
26
26
  formatDateTime(task.runAt, { seconds: true }) // 2026-10-18 09:30:05
27
27
  relativeFuture(task.runAt, Date.now()) // 3 分钟后(未来时间用这个)
28
28
  formatBytes(file.size) // 12 KB`
29
- const useExplorer = `import { openFilePreview, pickWorkspaceFile } from 'dsh-plugin-core/explorer'
29
+ const useExplorer = `import { connectFs, openFilePreview, pickWorkspaceFile } from 'dsh-plugin-core/explorer'
30
+ import { serveFsRoute } from 'dsh-plugin-core/host'
31
+ import { FS_PATH } from './paths' // 自己的常量,宿主与 client 共用
30
32
 
31
- // 从 ctx 取出这一小块(不要传整个 ctx)
32
- const workspaceFiles = ctx.remote.workspaceFiles
33
+ // 宿主 apply:挂只读文件路由(列目录 / 读文件都在它上面,不限工作区)
34
+ serveFsRoute(ctx, { path: FS_PATH })
35
+ // client apply:接同一个常量(connectHost(ctx) 照旧负责工作区快照)
36
+ connectFs({ path: FS_PATH })
33
37
 
34
- // 预览某文件:右侧分栏(挤窄 row 左边的主区,不是浮在上面)
35
- openFilePreview({ mount: pageRowEl, workspaceFiles, sessionId, path: 'docs/requirements.md' })
38
+ // 预览某文件:右侧分栏(挤窄 row 左边的主区,不是浮在上面);绝对 / 相对都收
39
+ openFilePreview({ mount: pageRowEl, path: '/abs/ws/docs/requirements.md' })
36
40
 
37
- // 让用户选文件:锚定按钮旁弹模态,Promise 返回 { path, isDir }(工作区相对路径)
38
- const picked = await pickWorkspaceFile({ workspaceFiles, sessionId, anchor: btn, filter: { exts: ['ts'] } })`
41
+ // 让用户选:锚定按钮旁弹模态,Promise 返回 { path: 绝对路径, isDir, workspaceRel?, workspace? }
42
+ const picked = await pickWorkspaceFile({ anchor: btn, filter: { exts: ['ts'] } })
43
+ const folder = await pickWorkspaceFile({ anchor: btn, filter: { dirs: true } }) // 只选文件夹`
39
44
  const useSession = `import { openSessionView, SessionViewModal } from 'dsh-plugin-core/session'
40
45
 
41
46
  // sessions / uiConversation 同样从 ctx 取出(懒得取就用官方 client 服务)
@@ -45,7 +50,7 @@ if (view !== null) {
45
50
  sessionId, view,
46
51
  // 关闭必须释放 retain 引用
47
52
  onClose: () => { view.dispose(); setOpen(false) },
48
- onOpenFile: (path) => openFilePreview({ mount: pageRowEl, workspaceFiles, sessionId, path }),
53
+ onOpenFile: (path) => openFilePreview({ mount: pageRowEl, path, sessionId }),
49
54
  })
50
55
  }`
51
56
  const cleanup = `kit.dispose() // 撤掉 <style> 标签并清空登记域(插件卸载 / HMR)`
@@ -69,8 +74,8 @@ const doc: DocTopic = {
69
74
  { zh: '§2 用一个控件', en: '§2 Use a control' },
70
75
  { zh: '§3 用通用方法', en: '§3 Use helpers' },
71
76
  {
72
- zh: '§4 接工作区浏览:`mount` 必填(预览面板是布局成员,占宽挤窄主区),传页面那层 flex 行。`sessionId` 必须属于该工作区。',
73
- en: '§4 Wire workspace browsing: `mount` is required (the preview panel is a layout member that takes width); pass the page’s flex row. `sessionId` must belong to that workspace.',
77
+ zh: '§4 接文件浏览:`mount` 必填(预览面板是布局成员,占宽挤窄主区),传页面那层 flex 行;数据面要在宿主 `serveFsRoute` + client `connectFs` 用同一个 path 常量接上,浏览本身不需要会话。',
78
+ en: '§4 Wire file browsing: `mount` is required (the preview panel is a layout member that takes width); pass the page’s flex row. Wire the data face with the same path constant on both sides (`serveFsRoute` / `connectFs`) — browsing itself needs no session.',
74
79
  },
75
80
  {
76
81
  zh: '§5 接会话预览:`openSessionView` 返回 `null` 表示该会话不可解析;拿到 view 后交给 `SessionViewModal`,关闭时记得 `dispose()`。',
@@ -28,9 +28,9 @@
28
28
 
29
29
  > EN: §3 Use helpers
30
30
 
31
- §4 接工作区浏览:`mount` 必填(预览面板是布局成员,占宽挤窄主区),传页面那层 flex 行。`sessionId` 必须属于该工作区。
31
+ §4 接文件浏览:`mount` 必填(预览面板是布局成员,占宽挤窄主区),传页面那层 flex 行;数据面要在宿主 `serveFsRoute` + client `connectFs` 用同一个 path 常量接上,浏览本身不需要会话。
32
32
 
33
- > EN: §4 Wire workspace browsing: `mount` is required (the preview panel is a layout member that takes width); pass the page’s flex row. `sessionId` must belong to that workspace.
33
+ > EN: §4 Wire file browsing: `mount` is required (the preview panel is a layout member that takes width); pass the page’s flex row. Wire the data face with the same path constant on both sides (`serveFsRoute` / `connectFs`) — browsing itself needs no session.
34
34
 
35
35
  §5 接会话预览:`openSessionView` 返回 `null` 表示该会话不可解析;拿到 view 后交给 `SessionViewModal`,关闭时记得 `dispose()`。
36
36
 
@@ -78,16 +78,21 @@ formatBytes(file.size) // 12 KB
78
78
  ```
79
79
 
80
80
  ```tsx id="integration-use-explorer"
81
- import { openFilePreview, pickWorkspaceFile } from 'dsh-plugin-core/explorer'
81
+ import { connectFs, openFilePreview, pickWorkspaceFile } from 'dsh-plugin-core/explorer'
82
+ import { serveFsRoute } from 'dsh-plugin-core/host'
83
+ import { FS_PATH } from './paths' // 自己的常量,宿主与 client 共用
82
84
 
83
- // 从 ctx 取出这一小块(不要传整个 ctx)
84
- const workspaceFiles = ctx.remote.workspaceFiles
85
+ // 宿主 apply:挂只读文件路由(列目录 / 读文件都在它上面,不限工作区)
86
+ serveFsRoute(ctx, { path: FS_PATH })
87
+ // client apply:接同一个常量(connectHost(ctx) 照旧负责工作区快照)
88
+ connectFs({ path: FS_PATH })
85
89
 
86
- // 预览某文件:右侧分栏(挤窄 row 左边的主区,不是浮在上面)
87
- openFilePreview({ mount: pageRowEl, workspaceFiles, sessionId, path: 'docs/requirements.md' })
90
+ // 预览某文件:右侧分栏(挤窄 row 左边的主区,不是浮在上面);绝对 / 相对都收
91
+ openFilePreview({ mount: pageRowEl, path: '/abs/ws/docs/requirements.md' })
88
92
 
89
- // 让用户选文件:锚定按钮旁弹模态,Promise 返回 { path, isDir }(工作区相对路径)
90
- const picked = await pickWorkspaceFile({ workspaceFiles, sessionId, anchor: btn, filter: { exts: ['ts'] } })
93
+ // 让用户选:锚定按钮旁弹模态,Promise 返回 { path: 绝对路径, isDir, workspaceRel?, workspace? }
94
+ const picked = await pickWorkspaceFile({ anchor: btn, filter: { exts: ['ts'] } })
95
+ const folder = await pickWorkspaceFile({ anchor: btn, filter: { dirs: true } }) // 只选文件夹
91
96
  ```
92
97
 
93
98
  ```tsx id="integration-use-session"
@@ -100,7 +105,7 @@ if (view !== null) {
100
105
  sessionId, view,
101
106
  // 关闭必须释放 retain 引用
102
107
  onClose: () => { view.dispose(); setOpen(false) },
103
- onOpenFile: (path) => openFilePreview({ mount: pageRowEl, workspaceFiles, sessionId, path }),
108
+ onOpenFile: (path) => openFilePreview({ mount: pageRowEl, path, sessionId }),
104
109
  })
105
110
  }
106
111
  ```
@@ -848,8 +848,8 @@ const doc: DocTopic = {
848
848
  en: 'Panel height defaults to viewport height; in a fixed-height container the caller sets `--dshpc-dock-h`. Not supported: same-name duplicates, tab bars, auto-eviction — tab switching belongs in the consumer’s own panel content. (The collapsed ear is a standalone `fixed` overlay; the panel itself never floats.)',
849
849
  },
850
850
  {
851
- zh: '占宽契约:已开栏宽之和(外加开着时给主区滚动条留的 1px 缝,可用 `--dshpc-dock-gap` 调)实时写进根元素 `--dshpc-page-inset-right`(拖拽逐帧更新,全关即移除),全屏遮罩据此让位;同页多个 dock 求和。',
852
- en: 'Width contract: the sum of open widths plus a 1px scrollbar gutter while open (tunable via `--dshpc-dock-gap`) goes to `--dshpc-page-inset-right` on the root element (every frame while dragging, removed when all close), and full-screen masks yield to it; multiple docks on one page are summed.',
851
+ zh: '占宽契约:已开栏宽之和实时写进根元素 `--dshpc-page-inset-right`(拖拽逐帧更新,全关即移除),全屏遮罩据此让位;同页多个 dock 求和。滚动条与分隔线的 1px 缝隙不由 dock 让位(`--dshpc-dock-gap` 已删),由主区滚动容器自己的透明右边框提供。',
852
+ en: 'Width contract: the sum of open widths goes to `--dshpc-page-inset-right` on the root element (every frame while dragging, removed when all close), and full-screen masks yield to it; multiple docks on one page are summed. The 1px gap between the scrollbar and the divider is no longer dock-provided (`--dshpc-dock-gap` is gone) — the main area’s own scroll container supplies it with a transparent right border.',
853
853
  },
854
854
  {
855
855
  zh: '收起 / 展开:`dock.collapse(id)` 让面板退出布局(立刻不占宽)、在右侧出一只固定耳朵;`dock.expand(id)` 把耳朵收回、按记忆宽度还原;`dock.isCollapsed(id)`。耳朵是垂直居中的小图标页签(不放文字),开合带滑入 / 收拢动效;耳朵是独立浮层,与分栏不是一体;`close` 不留耳朵。',
package/docs/help/ui.md CHANGED
@@ -724,9 +724,9 @@ h(SideSheet, {
724
724
 
725
725
  > EN: Panel height defaults to viewport height; in a fixed-height container the caller sets `--dshpc-dock-h`. Not supported: same-name duplicates, tab bars, auto-eviction — tab switching belongs in the consumer’s own panel content. (The collapsed ear is a standalone `fixed` overlay; the panel itself never floats.)
726
726
 
727
- 占宽契约:已开栏宽之和(外加开着时给主区滚动条留的 1px 缝,可用 `--dshpc-dock-gap` 调)实时写进根元素 `--dshpc-page-inset-right`(拖拽逐帧更新,全关即移除),全屏遮罩据此让位;同页多个 dock 求和。
727
+ 占宽契约:已开栏宽之和实时写进根元素 `--dshpc-page-inset-right`(拖拽逐帧更新,全关即移除),全屏遮罩据此让位;同页多个 dock 求和。滚动条与分隔线的 1px 缝隙不由 dock 让位(`--dshpc-dock-gap` 已删),由主区滚动容器自己的透明右边框提供。
728
728
 
729
- > EN: Width contract: the sum of open widths plus a 1px scrollbar gutter while open (tunable via `--dshpc-dock-gap`) goes to `--dshpc-page-inset-right` on the root element (every frame while dragging, removed when all close), and full-screen masks yield to it; multiple docks on one page are summed.
729
+ > EN: Width contract: the sum of open widths goes to `--dshpc-page-inset-right` on the root element (every frame while dragging, removed when all close), and full-screen masks yield to it; multiple docks on one page are summed. The 1px gap between the scrollbar and the divider is no longer dock-provided (`--dshpc-dock-gap` is gone) — the main area’s own scroll container supplies it with a transparent right border.
730
730
 
731
731
  收起 / 展开:`dock.collapse(id)` 让面板退出布局(立刻不占宽)、在右侧出一只固定耳朵;`dock.expand(id)` 把耳朵收回、按记忆宽度还原;`dock.isCollapsed(id)`。耳朵是垂直居中的小图标页签(不放文字),开合带滑入 / 收拢动效;耳朵是独立浮层,与分栏不是一体;`close` 不留耳朵。
732
732
 
@@ -247,7 +247,8 @@ dsh-plugin-core/
247
247
  | 文档 | README 接入指南 | ✅ 已完成 |
248
248
 
249
249
  **未做**(按重要性):消费者插件接入 / 会话 UI 镜像 / 类名隔离 /
250
- 设置页骨架(等第二个消费者)/ npm 发布流程。详见 README「还没做的事」。
250
+ 设置页骨架(等第二个消费者)/ npm 发布流程。落地进度一律以任务台账(task-ledger)为准,
251
+ 本文只记需求与已拍板的结论,不跟进度。
251
252
 
252
253
  > 说明:`dsh-plugin-core-demo` 作为真实插件已在宿主内跑通配置面板、侧栏入口、整页预览,
253
254
  > 已证明 core 在宿主里可用;上面「消费者插件接入」专指把 `dsh-task-dispatch-table` 等迁到 core、
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-core",
3
- "version": "0.20.3",
3
+ "version": "0.20.6",
4
4
  "packageManager": "pnpm@12.3.4",
5
5
  "description": "Shared base for DSH plugins: design tokens, UI controls, utils, workspace explorer, session preview",
6
6
  "keywords": [