dsh-plugin-core 0.20.4 → 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.
- package/dist/design/tokens-css.generated.d.ts +1 -1
- package/dist/design/tokens-css.generated.d.ts.map +1 -1
- package/dist/design/tokens-css.generated.js +1 -1
- package/dist/design/tokens-css.generated.js.map +1 -1
- package/dist/explorer/FileBrowser.d.ts +13 -6
- package/dist/explorer/FileBrowser.d.ts.map +1 -1
- package/dist/explorer/FileBrowser.js +190 -305
- package/dist/explorer/FileBrowser.js.map +1 -1
- package/dist/explorer/crumb-nav.d.ts +45 -0
- package/dist/explorer/crumb-nav.d.ts.map +1 -0
- package/dist/explorer/crumb-nav.js +75 -0
- package/dist/explorer/crumb-nav.js.map +1 -0
- package/dist/explorer/crumbs-menu.d.ts +37 -0
- package/dist/explorer/crumbs-menu.d.ts.map +1 -0
- package/dist/explorer/crumbs-menu.js +102 -0
- package/dist/explorer/crumbs-menu.js.map +1 -0
- package/dist/explorer/explorer-css.generated.d.ts +1 -1
- package/dist/explorer/explorer-css.generated.d.ts.map +1 -1
- package/dist/explorer/explorer-css.generated.js +1 -1
- package/dist/explorer/explorer-css.generated.js.map +1 -1
- package/dist/explorer/fs-face.d.ts +36 -0
- package/dist/explorer/fs-face.d.ts.map +1 -0
- package/dist/explorer/fs-face.js +74 -0
- package/dist/explorer/fs-face.js.map +1 -0
- package/dist/explorer/index.d.ts +2 -1
- package/dist/explorer/index.d.ts.map +1 -1
- package/dist/explorer/index.js +2 -1
- package/dist/explorer/index.js.map +1 -1
- package/dist/explorer/landing.d.ts +26 -0
- package/dist/explorer/landing.d.ts.map +1 -0
- package/dist/explorer/landing.js +43 -0
- package/dist/explorer/landing.js.map +1 -0
- package/dist/explorer/open.d.ts +35 -38
- package/dist/explorer/open.d.ts.map +1 -1
- package/dist/explorer/open.js +124 -156
- package/dist/explorer/open.js.map +1 -1
- package/dist/explorer/preview/BytesPreview.d.ts +2 -3
- package/dist/explorer/preview/BytesPreview.d.ts.map +1 -1
- package/dist/explorer/preview/BytesPreview.js +3 -3
- package/dist/explorer/preview/BytesPreview.js.map +1 -1
- package/dist/explorer/preview/HtmlPreview.d.ts +2 -3
- package/dist/explorer/preview/HtmlPreview.d.ts.map +1 -1
- package/dist/explorer/preview/HtmlPreview.js +4 -5
- package/dist/explorer/preview/HtmlPreview.js.map +1 -1
- package/dist/explorer/preview/OfficePreview.d.ts +2 -1
- package/dist/explorer/preview/OfficePreview.d.ts.map +1 -1
- package/dist/explorer/preview/OfficePreview.js +9 -2
- package/dist/explorer/preview/OfficePreview.js.map +1 -1
- package/dist/explorer/preview/TextPreview.d.ts +2 -3
- package/dist/explorer/preview/TextPreview.d.ts.map +1 -1
- package/dist/explorer/preview/TextPreview.js +3 -3
- package/dist/explorer/preview/TextPreview.js.map +1 -1
- package/dist/explorer/preview/index.d.ts +6 -3
- package/dist/explorer/preview/index.d.ts.map +1 -1
- package/dist/explorer/preview/index.js +3 -6
- package/dist/explorer/preview/index.js.map +1 -1
- package/dist/explorer/preview-title-bar.d.ts +26 -0
- package/dist/explorer/preview-title-bar.d.ts.map +1 -0
- package/dist/explorer/preview-title-bar.js +48 -0
- package/dist/explorer/preview-title-bar.js.map +1 -0
- package/dist/explorer/types.d.ts +25 -23
- package/dist/explorer/types.d.ts.map +1 -1
- package/dist/explorer/types.js +61 -120
- package/dist/explorer/types.js.map +1 -1
- package/dist/host/fs-route.d.ts +25 -0
- package/dist/host/fs-route.d.ts.map +1 -0
- package/dist/host/fs-route.js +243 -0
- package/dist/host/fs-route.js.map +1 -0
- package/dist/host/index.d.ts +2 -0
- package/dist/host/index.d.ts.map +1 -1
- package/dist/host/index.js +1 -0
- package/dist/host/index.js.map +1 -1
- package/dist/session/session-css.generated.d.ts +1 -1
- package/dist/session/session-css.generated.d.ts.map +1 -1
- package/dist/session/session-css.generated.js +1 -1
- package/dist/session/session-css.generated.js.map +1 -1
- package/dist/ui/DateTimePicker.d.ts.map +1 -1
- package/dist/ui/DateTimePicker.js +5 -1
- package/dist/ui/DateTimePicker.js.map +1 -1
- package/dist/ui/Modal.d.ts.map +1 -1
- package/dist/ui/Modal.js +7 -2
- package/dist/ui/Modal.js.map +1 -1
- package/dist/ui/Picker.d.ts.map +1 -1
- package/dist/ui/Picker.js +6 -3
- package/dist/ui/Picker.js.map +1 -1
- package/dist/ui/controls/datetime-css.generated.d.ts +1 -1
- package/dist/ui/controls/datetime-css.generated.d.ts.map +1 -1
- package/dist/ui/controls/datetime-css.generated.js +1 -1
- package/dist/ui/controls/datetime-css.generated.js.map +1 -1
- package/dist/ui/controls/field-css.generated.d.ts +1 -1
- package/dist/ui/controls/field-css.generated.d.ts.map +1 -1
- package/dist/ui/controls/field-css.generated.js +1 -1
- package/dist/ui/controls/field-css.generated.js.map +1 -1
- package/dist/ui/controls/loading-css.generated.d.ts +1 -1
- package/dist/ui/controls/loading-css.generated.d.ts.map +1 -1
- package/dist/ui/controls/loading-css.generated.js +1 -1
- package/dist/ui/controls/loading-css.generated.js.map +1 -1
- package/dist/ui/feedback-css.generated.d.ts +1 -1
- package/dist/ui/feedback-css.generated.d.ts.map +1 -1
- package/dist/ui/feedback-css.generated.js +1 -1
- package/dist/ui/feedback-css.generated.js.map +1 -1
- package/dist/ui/marquee-css.generated.d.ts +1 -1
- package/dist/ui/marquee-css.generated.d.ts.map +1 -1
- package/dist/ui/marquee-css.generated.js +1 -1
- package/dist/ui/marquee-css.generated.js.map +1 -1
- package/dist/ui/overlay-css.generated.d.ts +1 -1
- package/dist/ui/overlay-css.generated.d.ts.map +1 -1
- package/dist/ui/overlay-css.generated.js +1 -1
- package/dist/ui/overlay-css.generated.js.map +1 -1
- package/dist/ui/overlay.d.ts +8 -2
- package/dist/ui/overlay.d.ts.map +1 -1
- package/dist/ui/overlay.js +7 -6
- package/dist/ui/overlay.js.map +1 -1
- package/dist/ui/panel-css.d.ts +2 -0
- package/dist/ui/panel-css.d.ts.map +1 -0
- package/dist/ui/panel-css.generated.d.ts +2 -0
- package/dist/ui/panel-css.generated.d.ts.map +1 -0
- package/dist/ui/panel-css.generated.js +4 -0
- package/dist/ui/panel-css.generated.js.map +1 -0
- package/dist/ui/panel-css.js +2 -0
- package/dist/ui/panel-css.js.map +1 -0
- package/dist/ui/panel.d.ts +41 -0
- package/dist/ui/panel.d.ts.map +1 -0
- package/dist/ui/panel.js +37 -0
- package/dist/ui/panel.js.map +1 -0
- package/dist/ui/picker-css.generated.d.ts +1 -1
- package/dist/ui/picker-css.generated.d.ts.map +1 -1
- package/dist/ui/picker-css.generated.js +1 -1
- package/dist/ui/picker-css.generated.js.map +1 -1
- package/dist/utils/i18n.d.ts +1 -1
- package/dist/utils/i18n.d.ts.map +1 -1
- package/dist/utils/i18n.js +6 -0
- package/dist/utils/i18n.js.map +1 -1
- package/docs/design/architecture.md +9 -4
- package/docs/design/external/dsh-host.md +60 -0
- package/docs/design/style.md +26 -1
- package/docs/help/explorer.doc.ts +35 -30
- package/docs/help/explorer.md +23 -19
- package/docs/help/integration.doc.ts +15 -10
- package/docs/help/integration.md +15 -10
- package/docs/requirements.md +2 -1
- package/package.json +1 -1
package/docs/design/style.md
CHANGED
|
@@ -172,7 +172,8 @@ border-right: 1px solid transparent;
|
|
|
172
172
|
缝 + 圆角)当场被否:用户要的就是「让出 1 像素」,不是换一套滚动条外观。所以口径只有一条 ——
|
|
173
173
|
让位量固定 1px,颜色 / 粗细 / 圆角全部归浏览器。
|
|
174
174
|
|
|
175
|
-
- 已有实色边框的容器(如 Picker 菜单的 `border:1px solid
|
|
175
|
+
- 已有实色边框的容器(如 Picker 菜单的 `border:1px solid`,或挂 `.dshpc-panel` 后由面板提供的边框)
|
|
176
|
+
滚动条贴的就是那条线,算已让位,**不要叠加**。
|
|
176
177
|
- 消费方自己的滚动容器(主区 / 侧栏)不在本仓代码里,配方相同:自己加那条 `border-right`。
|
|
177
178
|
2026-11-02 起 `--dshpc-dock-gap` 已删,dock 不再替主区让 margin,改由主区容器自己让。
|
|
178
179
|
- 自检:`test/quality.test.mjs` 扫 `src/**/*.css`,滚动规则缺边框即红。
|
|
@@ -185,6 +186,30 @@ a `::-webkit-scrollbar` thumb (3px gap + radius) was tried on 2026-11-02 and rej
|
|
|
185
186
|
Containers that already have a solid border count as done. Consumer-owned scroll containers use the same
|
|
186
187
|
one-liner; `--dshpc-dock-gap` was removed on 2026-11-02. A test enforces the border.
|
|
187
188
|
|
|
189
|
+
## 浮层三档:模态 / 锚点面板 / 菜单
|
|
190
|
+
|
|
191
|
+
> EN: Three tiers for floating surfaces — modal / popover / menu
|
|
192
|
+
|
|
193
|
+
所有「浮起来的框」的**表面**(底色 · 描边宽 · 圆角 · 阴影)只在 `src/ui/panel.css` 定义,档位只有三个
|
|
194
|
+
(2026-11-03 用户定:样式统一,交互各家自己管):
|
|
195
|
+
|
|
196
|
+
| 档位 | 用在 | 底色 | 描边 | 圆角 | 阴影 |
|
|
197
|
+
|---|---|---|---|---|---|
|
|
198
|
+
| `--modal` | `Modal` / `ConfirmModal` / `AlertModal` | `surface-1` | 3px | `radius-xl` | `shadow-2` |
|
|
199
|
+
| `--popover` | 选文件弹窗(`pickWorkspaceFile`) | `surface-1` | 1px | `radius-lg` | `shadow-2` |
|
|
200
|
+
| `--menu` | ▾ 选层、`Picker` 下拉 | `surface-menu` | 1px | `radius-sm` | `shadow-1` |
|
|
201
|
+
|
|
202
|
+
- 组件里**不许**再写这四个值;要改外观只改 `panel.css`。
|
|
203
|
+
- **定位不归档位管**:Modal 居中、选文件弹窗贴锚点并上下翻转、菜单挂触发器下方,各自实现。
|
|
204
|
+
- 关闭钮只有一枚 `.dshpc-panel__close`;旧类名 `.dshpc-overlay-head__close` 保留作别名(消费方覆盖不失效)。
|
|
205
|
+
- 层序只许用 `--dshpc-z-panel`(面板本体)/ `--dshpc-z-popmenu`(叠在面板上的菜单与其透明遮罩),不许写字面量。
|
|
206
|
+
- **不并入三档**:`PanelDock` 与 `SideSheet` 属贴边家族(`side-panel.css` 的 `.dshpc-side-surface`,
|
|
207
|
+
2026-11-02 拍板两者皮肤一模一样),一个是布局成员、一个是贴边抽屉,都不是浮层。
|
|
208
|
+
- 组件**内部**的微层序(缩略按钮 5、拖拽条 7、dock 关闭钮 8、Segmented 的 0/1)不走 token ——
|
|
209
|
+
它们只在各自 stacking context 里排序,跟页面浮层层级无关。
|
|
210
|
+
- 自检:`test/css-discipline.test.mjs` 盯「色值只进 `tokens.css`」「`--dshpc-panel-*` 只在 `panel.css`」;
|
|
211
|
+
`test/css-contract.test.mjs` 盯「一个类名一套皮肤」。
|
|
212
|
+
|
|
188
213
|
## 三条硬规矩
|
|
189
214
|
|
|
190
215
|
> EN: Three hard rules
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
// explorer 子包的使用帮助 —— 真源(TS 数据模块)。内容全在此一处,demo 与 md 都从这份派生。
|
|
2
2
|
import type { DocTopic } from '../design/doc-format.js'
|
|
3
3
|
|
|
4
|
-
const explorerUsage = `import { openFilePreview, pickWorkspaceFile } from 'dsh-plugin-core/explorer'
|
|
4
|
+
const explorerUsage = `import { connectFs, openFilePreview, pickWorkspaceFile } from 'dsh-plugin-core/explorer'
|
|
5
|
+
import { serveFsRoute } from 'dsh-plugin-core/host'
|
|
6
|
+
import { FS_PATH } from './paths' // 你自己的常量:宿主与 client 必须用同一个
|
|
5
7
|
|
|
6
|
-
//
|
|
8
|
+
// ── 宿主半边(apply):挂一条只读文件路由(不限工作区;读文件/列目录都在它上面) ──
|
|
9
|
+
serveFsRoute(ctx, { path: FS_PATH })
|
|
10
|
+
|
|
11
|
+
// ── client 半边(apply):接一次(connectHost 照旧负责工作区快照) ──
|
|
7
12
|
connectHost(ctx)
|
|
13
|
+
connectFs({ path: FS_PATH })
|
|
8
14
|
|
|
9
15
|
// 右侧分栏预览(占宽度、挤窄主区):一个容器 + 一个路径就够
|
|
10
|
-
openFilePreview({ mount, path: '/abs/ledger' })
|
|
11
|
-
openFilePreview({ mount, path: 'docs/README.md' }) //
|
|
12
|
-
|
|
13
|
-
// 覆盖项:指定工作区快照 / 指定会话 / Office 转 PDF / 与别处共用的根缓存
|
|
14
|
-
openFilePreview({ mount, path, workspaceAnchors, sessionId, officeToPdf, rootCache })
|
|
16
|
+
openFilePreview({ mount, path: '/abs/ledger' }) // 文件夹 ⇒ 目录树;文件 ⇒ 直接预览
|
|
17
|
+
openFilePreview({ mount, path: 'docs/README.md' }) // 工作区相对路径也行(相对谁见下)
|
|
18
|
+
openFilePreview({ mount }) // 不给路径 ⇒ 根节点(工作区清单 + 盘符)
|
|
15
19
|
|
|
16
|
-
// 锚定弹窗选文件(Promise
|
|
17
|
-
const r = await pickWorkspaceFile({ anchor, filter: { exts: ['md'] } })
|
|
20
|
+
// 锚定弹窗选文件(Promise 返回**绝对路径**,不自己挂 dock)
|
|
21
|
+
const r = await pickWorkspaceFile({ anchor, filter: { exts: ['md'] } })
|
|
22
|
+
const dir = await pickWorkspaceFile({ anchor, filter: { dirs: true } }) // 只选文件夹`
|
|
18
23
|
|
|
19
24
|
const doc: DocTopic = {
|
|
20
25
|
title: { zh: 'explorer —— 工作区浏览', en: 'explorer — Workspace browsing' },
|
|
@@ -25,20 +30,20 @@ const doc: DocTopic = {
|
|
|
25
30
|
heading: { zh: '概览', en: 'Overview' },
|
|
26
31
|
prose: [
|
|
27
32
|
{
|
|
28
|
-
zh: '
|
|
29
|
-
en: '
|
|
33
|
+
zh: '提供文件浏览与选择;数据走宿主侧只读 FS 路由,**不限工作区**(工作区之外也能浏览)。界面与原版一致。',
|
|
34
|
+
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.',
|
|
30
35
|
},
|
|
31
36
|
{
|
|
32
|
-
zh: '
|
|
33
|
-
en: '**
|
|
37
|
+
zh: '**接线归核心,但要两条**:宿主 `apply` 调 `serveFsRoute({ path })`,client `apply` 用同一个常量调 `connectFs({ path })`(`connectHost(ctx)` 照旧负责工作区快照)。浏览**不需要会话**。',
|
|
38
|
+
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**.',
|
|
34
39
|
},
|
|
35
40
|
{
|
|
36
41
|
zh: '落点只给一个元素:页面里**包住主区**的那个(核心会补成 flex 行、让主区伸展,关闭时还原),不要传 `document.body` 再自己套 fixed——那是浮层,不是分栏。',
|
|
37
42
|
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.',
|
|
38
43
|
},
|
|
39
44
|
{
|
|
40
|
-
zh: '
|
|
41
|
-
en: '
|
|
45
|
+
zh: '面包屑口径不变:路径所属工作区名作根名,其下按相对层级展开。▾ 下拉**根层置顶**(其余仍列各工作区):POSIX 是一条「根目录」,**Windows 没有根目录这东西,列的是盘符**(`C:/`、`D:/`…,由宿主 `op:roots` 探出来)。点其他工作区 = 直接列该区根目录(不需要会话)。',
|
|
46
|
+
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”.',
|
|
42
47
|
},
|
|
43
48
|
],
|
|
44
49
|
},
|
|
@@ -84,29 +89,23 @@ const doc: DocTopic = {
|
|
|
84
89
|
en: '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`',
|
|
85
90
|
},
|
|
86
91
|
},
|
|
87
|
-
{
|
|
88
|
-
name: 'opts.workspaceFiles',
|
|
89
|
-
type: 'WorkspaceFilesFace',
|
|
90
|
-
def: '—',
|
|
91
|
-
desc: {
|
|
92
|
-
zh: '读文件服务。**一般不用传**:`connectHost(ctx)` 接线后核心自己持有;传了以传入为准。传 `content` 时可省',
|
|
93
|
-
en: 'File service. **Usually unnecessary**: core holds it after `connectHost(ctx)`; when passed it wins. Optional when passing `content`',
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
92
|
{
|
|
97
93
|
name: 'opts.sessionId',
|
|
98
94
|
type: 'string',
|
|
99
95
|
def: '—',
|
|
100
96
|
desc: {
|
|
101
|
-
zh: '
|
|
102
|
-
en: '
|
|
97
|
+
zh: '**只在两种情况下需要**:① 相对路径的基准工作区;② Office 预览(转 PDF 要一条有效会话,缺省由核心取最近有会话的工作区)',
|
|
98
|
+
en: '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)',
|
|
103
99
|
},
|
|
104
100
|
},
|
|
105
101
|
{
|
|
106
102
|
name: 'opts.path',
|
|
107
103
|
type: 'string',
|
|
108
104
|
def: '—',
|
|
109
|
-
desc: {
|
|
105
|
+
desc: {
|
|
106
|
+
zh: '绝对路径或工作区相对路径(相对路径要有可解析的基准,否则返回 `null` 并 warn);不传 ⇒ 根节点。与 `content` 二选一',
|
|
107
|
+
en: 'Absolute or workspace-relative (a relative one needs a resolvable base, else `null` + a warning); omitted ⇒ the root node. Mutually exclusive with `content`',
|
|
108
|
+
},
|
|
110
109
|
},
|
|
111
110
|
{
|
|
112
111
|
name: 'opts.content',
|
|
@@ -128,9 +127,12 @@ const doc: DocTopic = {
|
|
|
128
127
|
},
|
|
129
128
|
{
|
|
130
129
|
name: 'pickWorkspaceFile(opts)',
|
|
131
|
-
type: 'PickWorkspaceFileOptions => Promise<
|
|
130
|
+
type: 'PickWorkspaceFileOptions => Promise<PickedPath | null>',
|
|
132
131
|
def: '—',
|
|
133
|
-
desc: {
|
|
132
|
+
desc: {
|
|
133
|
+
zh: '锚定按钮弹出模态选文件,解析出 `{ path, isDir, workspaceRel?, workspace? }`;`path` 是**绝对路径**',
|
|
134
|
+
en: 'Anchored modal picker resolving `{ path, isDir, workspaceRel?, workspace? }`; `path` is **absolute**',
|
|
135
|
+
},
|
|
134
136
|
},
|
|
135
137
|
{
|
|
136
138
|
name: 'opts.anchor',
|
|
@@ -143,7 +145,10 @@ const doc: DocTopic = {
|
|
|
143
145
|
name: 'opts.filter',
|
|
144
146
|
type: 'PickFilter',
|
|
145
147
|
def: '—',
|
|
146
|
-
desc: {
|
|
148
|
+
desc: {
|
|
149
|
+
zh: '`exts` 只要这些扩展名(目录仍可展开导航);`dirs: true` 只允许选文件夹(文件不渲染)',
|
|
150
|
+
en: '`exts` limits files (folders still expand); `dirs: true` picks folders only (files hidden, plus a “Select this folder” action in the crumb bar)',
|
|
151
|
+
},
|
|
147
152
|
},
|
|
148
153
|
],
|
|
149
154
|
},
|
package/docs/help/explorer.md
CHANGED
|
@@ -10,21 +10,21 @@
|
|
|
10
10
|
|
|
11
11
|
> EN: Overview
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
提供文件浏览与选择;数据走宿主侧只读 FS 路由,**不限工作区**(工作区之外也能浏览)。界面与原版一致。
|
|
14
14
|
|
|
15
|
-
> EN:
|
|
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
|
-
|
|
17
|
+
**接线归核心,但要两条**:宿主 `apply` 调 `serveFsRoute({ path })`,client `apply` 用同一个常量调 `connectFs({ path })`(`connectHost(ctx)` 照旧负责工作区快照)。浏览**不需要会话**。
|
|
18
18
|
|
|
19
|
-
> EN: **
|
|
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
|
-
|
|
25
|
+
面包屑口径不变:路径所属工作区名作根名,其下按相对层级展开。▾ 下拉**根层置顶**(其余仍列各工作区):POSIX 是一条「根目录」,**Windows 没有根目录这东西,列的是盘符**(`C:/`、`D:/`…,由宿主 `op:roots` 探出来)。点其他工作区 = 直接列该区根目录(不需要会话)。
|
|
26
26
|
|
|
27
|
-
> EN:
|
|
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
|
-
//
|
|
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
|
|
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.
|
|
71
|
-
| `opts.
|
|
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<
|
|
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` | — | — |
|
|
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
|
-
//
|
|
32
|
-
|
|
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,
|
|
38
|
+
// 预览某文件:右侧分栏(挤窄 row 左边的主区,不是浮在上面);绝对 / 相对都收
|
|
39
|
+
openFilePreview({ mount: pageRowEl, path: '/abs/ws/docs/requirements.md' })
|
|
36
40
|
|
|
37
|
-
//
|
|
38
|
-
const picked = await pickWorkspaceFile({
|
|
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,
|
|
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
|
|
73
|
-
en: '§4 Wire
|
|
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()`。',
|
package/docs/help/integration.md
CHANGED
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
|
|
29
29
|
> EN: §3 Use helpers
|
|
30
30
|
|
|
31
|
-
§4
|
|
31
|
+
§4 接文件浏览:`mount` 必填(预览面板是布局成员,占宽挤窄主区),传页面那层 flex 行;数据面要在宿主 `serveFsRoute` + client `connectFs` 用同一个 path 常量接上,浏览本身不需要会话。
|
|
32
32
|
|
|
33
|
-
> EN: §4 Wire
|
|
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
|
-
//
|
|
84
|
-
|
|
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,
|
|
90
|
+
// 预览某文件:右侧分栏(挤窄 row 左边的主区,不是浮在上面);绝对 / 相对都收
|
|
91
|
+
openFilePreview({ mount: pageRowEl, path: '/abs/ws/docs/requirements.md' })
|
|
88
92
|
|
|
89
|
-
//
|
|
90
|
-
const picked = await pickWorkspaceFile({
|
|
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,
|
|
108
|
+
onOpenFile: (path) => openFilePreview({ mount: pageRowEl, path, sessionId }),
|
|
104
109
|
})
|
|
105
110
|
}
|
|
106
111
|
```
|
package/docs/requirements.md
CHANGED
|
@@ -247,7 +247,8 @@ dsh-plugin-core/
|
|
|
247
247
|
| 文档 | README 接入指南 | ✅ 已完成 |
|
|
248
248
|
|
|
249
249
|
**未做**(按重要性):消费者插件接入 / 会话 UI 镜像 / 类名隔离 /
|
|
250
|
-
设置页骨架(等第二个消费者)/ npm
|
|
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