dsh-plugin-core 0.10.1 → 0.11.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 (102) hide show
  1. package/README.md +4 -3
  2. package/dist/explorer/FileBrowser.d.ts.map +1 -1
  3. package/dist/explorer/FileBrowser.js +3 -2
  4. package/dist/explorer/FileBrowser.js.map +1 -1
  5. package/dist/explorer/open.d.ts.map +1 -1
  6. package/dist/explorer/open.js +7 -3
  7. package/dist/explorer/open.js.map +1 -1
  8. package/dist/explorer/preview/BytesPreview.d.ts.map +1 -1
  9. package/dist/explorer/preview/BytesPreview.js +3 -2
  10. package/dist/explorer/preview/BytesPreview.js.map +1 -1
  11. package/dist/explorer/preview/HtmlPreview.d.ts.map +1 -1
  12. package/dist/explorer/preview/HtmlPreview.js +3 -2
  13. package/dist/explorer/preview/HtmlPreview.js.map +1 -1
  14. package/dist/explorer/preview/OfficePreview.d.ts.map +1 -1
  15. package/dist/explorer/preview/OfficePreview.js +3 -2
  16. package/dist/explorer/preview/OfficePreview.js.map +1 -1
  17. package/dist/explorer/preview/TextPreview.d.ts.map +1 -1
  18. package/dist/explorer/preview/TextPreview.js +3 -2
  19. package/dist/explorer/preview/TextPreview.js.map +1 -1
  20. package/dist/session/SessionViewModal.d.ts.map +1 -1
  21. package/dist/session/SessionViewModal.js +3 -2
  22. package/dist/session/SessionViewModal.js.map +1 -1
  23. package/dist/session/mirror/ChatView.d.ts.map +1 -1
  24. package/dist/session/mirror/ChatView.js +3 -0
  25. package/dist/session/mirror/ChatView.js.map +1 -1
  26. package/dist/ui/AlertModal.d.ts.map +1 -1
  27. package/dist/ui/AlertModal.js +3 -0
  28. package/dist/ui/AlertModal.js.map +1 -1
  29. package/dist/ui/BackToTop.d.ts.map +1 -1
  30. package/dist/ui/BackToTop.js +4 -3
  31. package/dist/ui/BackToTop.js.map +1 -1
  32. package/dist/ui/Button.d.ts.map +1 -1
  33. package/dist/ui/Button.js +4 -3
  34. package/dist/ui/Button.js.map +1 -1
  35. package/dist/ui/Card.d.ts.map +1 -1
  36. package/dist/ui/Card.js +3 -2
  37. package/dist/ui/Card.js.map +1 -1
  38. package/dist/ui/Chip.d.ts.map +1 -1
  39. package/dist/ui/Chip.js +3 -2
  40. package/dist/ui/Chip.js.map +1 -1
  41. package/dist/ui/CodeViewer.d.ts.map +1 -1
  42. package/dist/ui/CodeViewer.js +3 -0
  43. package/dist/ui/CodeViewer.js.map +1 -1
  44. package/dist/ui/DateTimePicker.d.ts.map +1 -1
  45. package/dist/ui/DateTimePicker.js +4 -2
  46. package/dist/ui/DateTimePicker.js.map +1 -1
  47. package/dist/ui/Field.d.ts +10 -0
  48. package/dist/ui/Field.d.ts.map +1 -1
  49. package/dist/ui/Field.js +5 -4
  50. package/dist/ui/Field.js.map +1 -1
  51. package/dist/ui/FieldHint.d.ts.map +1 -1
  52. package/dist/ui/FieldHint.js +3 -2
  53. package/dist/ui/FieldHint.js.map +1 -1
  54. package/dist/ui/Loading.d.ts +17 -0
  55. package/dist/ui/Loading.d.ts.map +1 -1
  56. package/dist/ui/Loading.js +8 -7
  57. package/dist/ui/Loading.js.map +1 -1
  58. package/dist/ui/MarqueeText.d.ts.map +1 -1
  59. package/dist/ui/MarqueeText.js +3 -0
  60. package/dist/ui/MarqueeText.js.map +1 -1
  61. package/dist/ui/Modal.d.ts.map +1 -1
  62. package/dist/ui/Modal.js +3 -2
  63. package/dist/ui/Modal.js.map +1 -1
  64. package/dist/ui/ModelPicker.d.ts.map +1 -1
  65. package/dist/ui/ModelPicker.js +3 -2
  66. package/dist/ui/ModelPicker.js.map +1 -1
  67. package/dist/ui/PanelDock.d.ts.map +1 -1
  68. package/dist/ui/PanelDock.js +5 -2
  69. package/dist/ui/PanelDock.js.map +1 -1
  70. package/dist/ui/Picker.d.ts.map +1 -1
  71. package/dist/ui/Picker.js +5 -2
  72. package/dist/ui/Picker.js.map +1 -1
  73. package/dist/ui/Segmented.d.ts +24 -0
  74. package/dist/ui/Segmented.d.ts.map +1 -1
  75. package/dist/ui/Segmented.js +4 -27
  76. package/dist/ui/Segmented.js.map +1 -1
  77. package/dist/ui/SplitGroup.d.ts.map +1 -1
  78. package/dist/ui/SplitGroup.js +3 -2
  79. package/dist/ui/SplitGroup.js.map +1 -1
  80. package/dist/ui/Textarea.d.ts.map +1 -1
  81. package/dist/ui/Textarea.js +3 -2
  82. package/dist/ui/Textarea.js.map +1 -1
  83. package/dist/ui/css.d.ts +28 -5
  84. package/dist/ui/css.d.ts.map +1 -1
  85. package/dist/ui/css.js +31 -34
  86. package/dist/ui/css.js.map +1 -1
  87. package/dist/ui/index.d.ts.map +1 -1
  88. package/dist/ui/index.js +3 -2
  89. package/dist/ui/index.js.map +1 -1
  90. package/dist/ui/loading-host.d.ts +5 -0
  91. package/dist/ui/loading-host.d.ts.map +1 -1
  92. package/dist/ui/loading-host.js +8 -0
  93. package/dist/ui/loading-host.js.map +1 -1
  94. package/docs/design/architecture.md +9 -5
  95. package/docs/design/style.md +50 -0
  96. package/docs/help/integration.doc.ts +2 -2
  97. package/docs/help/integration.md +2 -2
  98. package/docs/help/style.md +35 -0
  99. package/docs/help/ui.doc.ts +3 -2
  100. package/docs/help/ui.md +3 -2
  101. package/docs/requirements.md +7 -0
  102. package/package.json +1 -1
@@ -17,6 +17,56 @@ What stays here is what you must honour when **changing styles in this repo**.
17
17
  EN: The client bundle is a CJS closure loaded by `window.__ModuleLoader__.load`, so `import './x.css'` produces a
18
18
  separate asset the kernel never loads — CSS must be injected at runtime as a TS string.
19
19
 
20
+ ## 控件样式按需:每个控件只注入自己那份
21
+
22
+ > EN: Per-control styles: every control injects only its own
23
+
24
+ (2026-10-30 起)**注入是按控件的,不是一把全量**:每个控件在 render 里调
25
+ `ensureComponentStyles('<域>', <自己那份 CSS>)`(幂等,每次 render 调都安全),用不到的控件其 CSS
26
+ 不进消费方的包 —— 实测只用 `Button` 时样式文本从 71.7 KB 降到 9.4 KB。
27
+
28
+ EN: Injection is per control, not one big blob. Each control calls `ensureComponentStyles('<domain>', <its own CSS>)`
29
+ during render (idempotent); CSS of controls you never use never reaches your bundle.
30
+
31
+ 新增控件照做:import 自己那份 `*_CSS` 常量 + 在组件里调一次 `ensureComponentStyles`。
32
+
33
+ 1. **不要顺手把别人的 CSS 一起传进去** —— 传了就退化成全量,包体省不下来。
34
+ 2. **跨控件的修饰一律用更高特异性表达**(`.dshpc-split .dshpc-seg`),由提供上下文的那个控件
35
+ 负责它自己那份;**绝不靠注入顺序** —— 顺序 = 控件首次渲染顺序,不固定。
36
+ 有自检盯着:`test/css-contract.test.mjs` 禁止跨常量出现同名选择器(含 `@media` 内)。
37
+ 3. **CSS 必须是 `export const X_CSS` 的字符串常量**(被显式引用的值):`package.json` 的
38
+ `sideEffects: false` 既不会误删它、又能让用不到的那份被摇掉。
39
+ **绝不能退回 `import './x.css'`** —— 那是副作用式导入,webpack 官方点名它会在 `sideEffects:false`
40
+ 下被连样式一起摇掉(组件渲染出来没样式)。
41
+
42
+ EN: (1) never bundle someone else's CSS into your call; (2) express cross-control overrides with higher
43
+ specificity, never with injection order (order = first-render order, not fixed) — the contract test forbids
44
+ same-name selectors across constants; (3) keep CSS as exported string constants — never go back to
45
+ `import './x.css'`, which `sideEffects:false` would drop along with the styles.
46
+
47
+ ### 为什么不用 `@layer` 分层(评估过,有意不做)
48
+
49
+ > EN: Why we deliberately do not use `@layer`
50
+
51
+ `@layer`(CSS Cascading and Inheritance L5,Baseline 2022-03)能让层序显式固定、不受注入时机影响,
52
+ 听上去正好治「顺序不确定」。**但它在这里会适得其反**:
53
+
54
+ - **未分层样式压过任何层**:层级裁决优先于特异性。一旦把 dshpc 样式包进 `@layer dshpc`,
55
+ 宿主任何**未分层的元素级重置**(`button { font-size: 14px }` 之类)就会反过来压过我们,
56
+ 层内特异性再高也没用 ⇒ 控件被宿主皮肤吃掉。
57
+ - **不分层反而更稳**:现状下我们的 `.dshpc-btn`(0,1,0)靠特异性就能压过宿主的 `button`(0,0,1)。
58
+
59
+ 所以「不分层」是**有意为之,不是遗漏**。真要分层,必须先把宿主的元素级样式摸清楚;而且只能整套一层
60
+ (`@layer dshpc`),**不能按组件拆层** —— 层间忽略特异性会直接打掉 `.dshpc-split .dshpc-seg` 这类
61
+ 靠特异性生效的拼接定制。
62
+
63
+ EN: `@layer` would let layer order be explicit and immune to injection timing, but here it backfires:
64
+ unlayered styles beat anything inside a layer, and layer judgement precedes specificity. Wrapping our styles
65
+ in `@layer dshpc` would let the host's unlayered element resets (`button { font-size }`) override our rules no
66
+ matter how specific they are. Staying unlayered is deliberate — `.dshpc-btn` (0,1,0) already beats the host's
67
+ `button` (0,0,1) on specificity. If layering ever becomes necessary: one layer for the whole kit, never per
68
+ control.
69
+
20
70
  ## 交互态颜色(hover / active)
21
71
 
22
72
  > EN: Interaction colors (hover / active)
@@ -4,11 +4,11 @@ import type { DocTopic } from '../design/doc-format.js'
4
4
  const install = `pnpm add dsh-plugin-core`
5
5
  const initStyle = `// 插件 client 入口
6
6
  import { createStyleKit } from 'dsh-plugin-core/design'
7
- import { ensureControls, setStyleKit } from 'dsh-plugin-core/ui'
7
+ import { setStyleKit } from 'dsh-plugin-core/ui'
8
8
 
9
9
  const kit = createStyleKit({ id: 'dsh-task-reminder-ui' })
10
10
  setStyleKit(kit)
11
- ensureControls()`
11
+ // 只需这一行:控件首次渲染时自注入自己那份 CSS,token 也一并登记。`
12
12
  const useControl = `import { Button, Input, Picker, showToast } from 'dsh-plugin-core/ui'
13
13
 
14
14
  h(Input, { value: name, onChange: setName, placeholder: '任务名', error: nameError })
@@ -47,11 +47,11 @@ pnpm add dsh-plugin-core
47
47
  ```ts id="integration-init-style"
48
48
  // 插件 client 入口
49
49
  import { createStyleKit } from 'dsh-plugin-core/design'
50
- import { ensureControls, setStyleKit } from 'dsh-plugin-core/ui'
50
+ import { setStyleKit } from 'dsh-plugin-core/ui'
51
51
 
52
52
  const kit = createStyleKit({ id: 'dsh-task-reminder-ui' })
53
53
  setStyleKit(kit)
54
- ensureControls()
54
+ // 只需这一行:控件首次渲染时自注入自己那份 CSS,token 也一并登记。
55
55
  ```
56
56
 
57
57
  ```tsx id="integration-use-control"
@@ -78,3 +78,38 @@ or by grepping `tokens-css.ts`.
78
78
  EN: Reskin by reassigning the same variables inside your own style domain (no fork needed). Add a domain with
79
79
  `kit.registerStyle('widget', css)`; re-registering a domain overwrites it. The `tokens` domain always comes
80
80
  first — core maintains that ordering. Skin lives in the caller’s domain; components only read tokens.
81
+
82
+ ## 怎么应用:样式是按需的
83
+
84
+ > EN: How to apply it: styles are on demand
85
+
86
+ (2026-10-30 起)插件入口**只需 `setStyleKit` 一行**;控件首次渲染时自注入自己那份 CSS(幂等),
87
+ 没用到的控件其 CSS 不进你的包。`ensureControls()` 现只登记 token,是**可选**预热,不必再调。
88
+
89
+ ```ts
90
+ import { createStyleKit } from 'dsh-plugin-core/design'
91
+ import { setStyleKit } from 'dsh-plugin-core/ui'
92
+
93
+ setStyleKit(createStyleKit({ id: 'dsh-task-reminder-ui' }))
94
+ ```
95
+
96
+ EN: Since 2026-10-30 the entry needs only `setStyleKit`; each control self-injects its own CSS on first render,
97
+ and CSS of controls you never use stays out of your bundle. `ensureControls()` now only registers tokens and is
98
+ optional.
99
+
100
+ ## 避免什么
101
+
102
+ > EN: What to avoid
103
+
104
+ 1. **不要 `import * as core from 'dsh-plugin-core/ui'` / `require('dsh-plugin-core/ui')`** —— 打包器无法摇树,
105
+ 所有控件 CSS 全进你的包(实测差 62.3 KB)。一律**具名导入**。
106
+ 2. **不要把多份 CSS 自己拼起来注册成一个域** —— 那等于重建全量注入,按需收益归零。
107
+ 要加自己的皮肤,用 `kit.registerStyle('你的域', css)` 单独注册。
108
+ 3. **不要写「靠注入顺序才对」的规则** —— 顺序 = 控件首次渲染顺序,不固定。
109
+ 要覆盖就用更高特异性(多加一个类限定),跨来源的层次用 `@layer`。
110
+ 4. **不要退回 `import './x.css'`** —— 客户端产物是 JS 闭包,`.css` 内核不加载;且在
111
+ `sideEffects: false` 下会被连样式一起摇掉。
112
+
113
+ EN: (1) Never namespace-import or `require` the barrel − it defeats tree-shaking (62.3 KB difference); use named
114
+ imports. (2) Do not concatenate several CSS constants into one domain − that rebuilds full injection. (3) Never
115
+ rely on injection order; override with higher specificity. (4) Never go back to `import './x.css'`.
@@ -2,10 +2,11 @@
2
2
  import type { DocTopic } from '../design/doc-format.js'
3
3
 
4
4
  const setup = `import { createStyleKit } from 'dsh-plugin-core/design'
5
- import { ensureControls, setStyleKit } from 'dsh-plugin-core/ui'
5
+ import { setStyleKit } from 'dsh-plugin-core/ui'
6
6
 
7
7
  setStyleKit(createStyleKit({ id: 'dsh-task-reminder-ui' }))
8
- ensureControls()`
8
+ // 控件样式不用你挂:每个控件首次渲染时自注入自己那份 CSS(幂等),
9
+ // 用不到的控件其 CSS 也不会进你的包。`
9
10
 
10
11
  const cardExample = `import { Card, Chip, StripCard } from 'dsh-plugin-core/ui'
11
12
 
package/docs/help/ui.md CHANGED
@@ -24,10 +24,11 @@
24
24
 
25
25
  ```ts id="ui-setup"
26
26
  import { createStyleKit } from 'dsh-plugin-core/design'
27
- import { ensureControls, setStyleKit } from 'dsh-plugin-core/ui'
27
+ import { setStyleKit } from 'dsh-plugin-core/ui'
28
28
 
29
29
  setStyleKit(createStyleKit({ id: 'dsh-task-reminder-ui' }))
30
- ensureControls()
30
+ // 控件样式不用你挂:每个控件首次渲染时自注入自己那份 CSS(幂等),
31
+ // 用不到的控件其 CSS 也不会进你的包。
31
32
  ```
32
33
 
33
34
  ## Button / IconButton / Segmented
@@ -120,6 +120,12 @@ dsh-plugin-core/
120
120
 
121
121
  ⇒ 必须是 `createStyleKit({ id })`:每个插件调一次领一套自己的(`<style>` 标签 id 按插件唯一)。
122
122
 
123
+ **(2026-10-30)注入按控件粒度**:每个控件自注入自己那份 CSS(`ensureComponentStyles(domain, css)`,幂等),
124
+ **不许再聚合成一把全量**。全量聚合会让入口模块静态依赖所有 CSS,打包器无法摇树 —— 插件只用 Button 也会
125
+ 背上全部 71.7 KB 样式文本。`ensureControls()` 因此收敛为「只登记 token」的可选预热。
126
+ 配套约束:消费方须**具名导入**(`import * as` / CJS `require` 会让按需收益归零);跨控件修饰只许用
127
+ 更高特异性表达,不许依赖注入顺序。
128
+
123
129
  ### 5.3 明暗色判据(别跟错)
124
130
 
125
131
  - 判据 = **`body[data-ds-dark-theme]`**(宿主启动脚本 `toggleAttribute` 写入,
@@ -175,6 +181,7 @@ dsh-plugin-core/
175
181
  | v2 | `explorer`(用户拍板要抽) | ✅ 已完成(浏览面板 + 预览;**选择器后移至 ui**,见 §2.4) |
176
182
  | v2+ | `explorer` 重做(2026-10-20) | ✅ 已完成(FileBrowser 面板 + `openFilePreview` / `pickWorkspaceFile` 两能力 + 全量预览:md / 代码高亮 / HTML / PDF / Office;官方件经 `./ui/official` 子路径) |
177
183
  | v3 | `session` | ⚠️ **半完成**:契约与三种承载已好,**官方会话 UI 镜像未搬**(只做最简渲染) |
184
+ | v3+ | 控件样式按需注入(2026-10-30) | ✅ 已完成(`css.ts` 停全量聚合;27 个组件各自注入自己那份;只 `import { Button }` 时 CSS 文本 71.7 KB → 9.4 KB。**不采用 `@layer`**:未分层样式压过任何层且层级裁决优先于特异性,包进层会被宿主元素级重置反压) |
178
185
  | 文档 | README 接入指南 | ✅ 已完成 |
179
186
 
180
187
  **未做**(按重要性):消费者插件接入 / 会话 UI 镜像 / 类名隔离 /
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-core",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
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": [