dsh-plugin-core 0.35.3 → 0.35.4

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 (41) 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/ui/actions/Button.d.ts +4 -0
  6. package/dist/ui/actions/Button.d.ts.map +1 -1
  7. package/dist/ui/actions/Button.js +7 -5
  8. package/dist/ui/actions/Button.js.map +1 -1
  9. package/dist/ui/actions/button-css.generated.d.ts +1 -1
  10. package/dist/ui/actions/button-css.generated.d.ts.map +1 -1
  11. package/dist/ui/actions/button-css.generated.js +1 -1
  12. package/dist/ui/actions/button-css.generated.js.map +1 -1
  13. package/dist/ui/display/Card.d.ts +4 -0
  14. package/dist/ui/display/Card.d.ts.map +1 -1
  15. package/dist/ui/display/Card.js +2 -0
  16. package/dist/ui/display/Card.js.map +1 -1
  17. package/dist/ui/display/ListRow.d.ts +38 -0
  18. package/dist/ui/display/ListRow.d.ts.map +1 -0
  19. package/dist/ui/display/ListRow.js +69 -0
  20. package/dist/ui/display/ListRow.js.map +1 -0
  21. package/dist/ui/display/card-css.generated.d.ts +1 -1
  22. package/dist/ui/display/card-css.generated.d.ts.map +1 -1
  23. package/dist/ui/display/card-css.generated.js +1 -1
  24. package/dist/ui/display/card-css.generated.js.map +1 -1
  25. package/dist/ui/display/list-row-css.d.ts +2 -0
  26. package/dist/ui/display/list-row-css.d.ts.map +1 -0
  27. package/dist/ui/display/list-row-css.generated.d.ts +2 -0
  28. package/dist/ui/display/list-row-css.generated.d.ts.map +1 -0
  29. package/dist/ui/display/list-row-css.generated.js +4 -0
  30. package/dist/ui/display/list-row-css.generated.js.map +1 -0
  31. package/dist/ui/display/list-row-css.js +7 -0
  32. package/dist/ui/display/list-row-css.js.map +1 -0
  33. package/dist/ui/index.d.ts +1 -0
  34. package/dist/ui/index.d.ts.map +1 -1
  35. package/dist/ui/index.js +2 -0
  36. package/dist/ui/index.js.map +1 -1
  37. package/docs/design/structure.md +3 -3
  38. package/docs/design/style.md +32 -0
  39. package/docs/help/ui.doc.ts +66 -1
  40. package/docs/help/ui.md +61 -1
  41. package/package.json +1 -1
@@ -28,6 +28,7 @@ const cardExample = `import { Card, Chip, StripCard } from 'dsh-plugin-core/ui'
28
28
  h(Card, {
29
29
  accent: 'warning',
30
30
  bar: { side: 'left', inset: 12, gap: 12 }, // 色条可放 left / right / top / bottom
31
+ hoverable: true, // 整卡 hover 变色(默认关)
31
32
  action: h(Button, { size: 'sm', variant: 'ghost' }, '跳过'),
32
33
  },
33
34
  h('div', null, '标题'),
@@ -36,6 +37,7 @@ h(Card, {
36
37
 
37
38
  h(StripCard, {
38
39
  accent: 'success',
40
+ hoverable: true, filled: false, // 底色关掉 = 透明(默认有底)
39
41
  expanded: open, onToggle: () => setOpen(!open),
40
42
  panel: h('div', null, '展开详情'),
41
43
  }, h('div', null, '标题 + 元信息'))`
@@ -51,6 +53,25 @@ h(Surface, { border: 'faint', padding: 'lg' }, h(Card, null, '框里再放卡'))
51
53
  // 虚线占位框也走它,不要自己写 1px dashed:
52
54
  h(Surface, { tone: 'sunken', border: 'dashed', padding: 'lg' }, '还没做的东西')`
53
55
 
56
+ const listRowExample = `import { ListRow, Button, Chip } from 'dsh-plugin-core/ui'
57
+
58
+ // 一行一条:常态带底,点整行打开 / 选中,右侧小操作不连带触发整行
59
+ h(ListRow, {
60
+ onClick: () => openFile(entry),
61
+ selected: entry.path === currentPath,
62
+ actions: h(Button, {
63
+ size: 'sm', variant: 'ghost', onClick: () => removeEntry(entry),
64
+ }, '移除'),
65
+ },
66
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
67
+ h(Chip, null, 'TS'),
68
+ h('span', null, entry.name),
69
+ ),
70
+ )
71
+
72
+ // 带边 / 关掉 hover / 纯展示(不可点)
73
+ h(ListRow, { bordered: true, hoverable: false }, '只读的一行')`
74
+
54
75
  const panelDockExample = `const dock = createPanelDock({ maxPanels: 3 }) // 一次,挂在插件级
55
76
 
56
77
  // 页面里:结构就在这儿写,不塞进任何调用参数
@@ -284,7 +305,7 @@ const doc: DocTopic = {
284
305
  exports: [
285
306
  'Button', 'IconButton', 'Segmented', 'SplitGroup', 'Input', 'PrefixedInput', 'NumberInput', 'Textarea',
286
307
  'Checkbox', 'fieldMetricsOf', 'Picker', 'WorkspacePicker', 'ModelPicker', 'DateTimePicker', 'Surface', 'Card', 'StripCard',
287
- 'Chip', 'Table', 'Pagination', 'Modal', 'SideSheet', 'FieldHint', 'setTooltipRenderer', 'showToast', 'notify', 'showHostToast',
308
+ 'Chip', 'Table', 'ListRow', 'Pagination', 'Modal', 'SideSheet', 'FieldHint', 'setTooltipRenderer', 'showToast', 'notify', 'showHostToast',
288
309
  'Loader', 'LoaderBar', 'Loading', 'RunningBlocks', 'SectionHead', 'MarqueeText', 'FlipText', 'LiveText',
289
310
  'QuickToolbar', 'startResizeLayoutWidth', 'PanelDock', 'createPanelDock', 'DockPanel',
290
311
  'ensureControls', 'setStyleKit',
@@ -321,6 +342,10 @@ const doc: DocTopic = {
321
342
  zh: 'IconButton:variant: plain|outline|danger(默认 plain,**无底色**)、size(默认 lg)、icon(必填)、label(必填,无障碍名兼 title)、color(图标色)、background(底色,不传即无底色)。',
322
343
  en: 'IconButton: variant plain|outline|danger (default plain, **no background**), size (default lg), icon (required), label (required, doubles as a11y name and title), color (icon colour), background (fill; omitted = no fill).',
323
344
  },
345
+ {
346
+ zh: '`dense` 是两者共用的紧凑图标档:`size` / 高度一律不变,图标放大一档、内距收紧(Button 的 `--btn-pad` 从 10/12/14 收到 6,图标容器从 0.875em 到 1.25em;IconButton 把图标 `<svg>` 的 `width/height` 从 1em 提到 1.25em,正方形宽高不变、四周留白变小)。两者 dense 下图标都是 sm 15px / md·lg 16.25px。IconButton 的按钮显式 `padding:0`、图标 `flex:none`:svg 是 flex item,不禁止收缩会被内距压成窄条,此时改 `width` 或加 `!important` 都无效。给描边细、缩到 sm 看不清的图标用(如文件类型 / 语言图标);默认 false,原有按钮外观不变。',
347
+ en: '`dense` is a shared compact-icon tier for both: `size` and height stay put while the icon grows one step and padding tightens (Button: `--btn-pad` 10/12/14 → 6, icon container 0.875em → 1.25em; IconButton: the icon `<svg>` goes from `width/height:1em` to `1.25em`, same square box, less breathing room). Both give sm 15px / md·lg 16.25px icons. An IconButton sets `padding:0` on itself and `flex:none` on the icon: the svg is a flex item, and without disabling shrinking the padding squeezes it into a narrow strip — changing `width` or adding `!important` cannot fix that. Use it for thin-stroke icons that vanish at `sm` (file-type / language glyphs). Defaults to false, so existing buttons are unchanged.',
348
+ },
324
349
  {
325
350
  zh: '两者的 loading 语义一致:`onClick` 返回 Promise 就转圈(纯图标钮把图标位换成小圈,`aria-label` 不变、补 `aria-busy`),`onBusyChange` 把「在跑」报出去;受控 `loading` 与它取或 —— 别处刷新也能点亮同一颗按钮。转圈期间按钮自己的图标位(`icon` / `iconRight`)全部让位,只留转圈 + 文字。',
326
351
  en: 'Both share one loading semantics: a Promise-returning `onClick` spins (an icon button swaps its icon for the spinner, keeps `aria-label` and adds `aria-busy`), `onBusyChange` reports "running", and the controlled `loading` prop is OR-ed in — an external refresh lights the same button too. While spinning, the button’s own icon slots (`icon` / `iconRight`) all step aside: spinner plus label only.',
@@ -340,6 +365,7 @@ const doc: DocTopic = {
340
365
  rows: [
341
366
  { name: 'variant', type: "上表 18 档(primary/secondary/danger/success/warning/info/outline/danger-outline/ghost/link + 扩展 purple/pink/cyan/teal/orange/indigo/rose/slate)", def: "'outline'", desc: { zh: '外观档', en: 'Appearance tier' } },
342
367
  { name: 'size', type: "'sm' | 'md' | 'lg'", def: "'lg'", desc: { zh: '高度档(24 / 28 / 32,有边无边同高)', en: 'Height tier (24 / 28 / 32, same height with or without border)' } },
368
+ { name: 'dense', type: 'boolean', def: 'false', desc: { zh: '紧凑图标档:高度不变,图标放大、内距收紧(Button / IconButton 同名同义)', en: 'Compact-icon tier: same height, larger icon and tighter padding (same prop on Button and IconButton)' } },
343
369
  { name: 'icon', type: 'ReactNode', def: '—', desc: { zh: '前置图标(官方图标组件 / 字符串均可)', en: 'Leading icon (official icon component or string)' } },
344
370
  { name: 'iconRight', type: 'ReactNode', def: '—', desc: { zh: '后置图标(展开箭头、外链)', en: 'Trailing icon (expand arrow, external link)' } },
345
371
  { name: 'color', type: 'string', def: '—', desc: { zh: '自定义主色:实心档作底色,线框 / 无底档作主色', en: 'Custom main color: background for solid tiers, main color for outline/bare' } },
@@ -606,6 +632,10 @@ const doc: DocTopic = {
606
632
  zh: '头部整体垂直居中,`action` 里裸放一个元素即居中。颜色全走 token,明暗跟随宿主。',
607
633
  en: 'The header centers vertically as a whole; drop a bare element into `action` and it centers. Colors come from tokens, so light/dark follows the host.',
608
634
  },
635
+ {
636
+ zh: '`hoverable` 开启后**整卡**悬停变色、同时把光标改成 `pointer`,统一吃 `--dshpc-card-hover`(亮 7% 蓝 / 暗 13% 蓝),默认关;`filled` 切底色,默认有底(Card = surface-1,StripCard = accent 浅底),`false` 时底透明但边框仍在。两根轴都只改 `--surface-bg`,插件不用再自己写 `:hover` 规则;透明底 + hover 也照常变色。',
637
+ en: '`hoverable` (default off) changes the **whole card** on hover and switches the cursor to `pointer`, using `--dshpc-card-hover` (7% blue light / 13% blue dark). `filled` (default on) controls the background — Card uses surface-1, StripCard the accent tint; `false` makes it transparent but keeps the border. Both axes only rewrite `--surface-bg`, so plugins no longer need their own `:hover` rules; a transparent card still changes color on hover.',
638
+ },
609
639
  ],
610
640
  api: [
611
641
  {
@@ -618,12 +648,47 @@ const doc: DocTopic = {
618
648
  { name: 'action', type: 'ReactNode', def: '—', desc: { zh: '右上角操作区', en: 'Top-right action area' } },
619
649
  { name: 'children', type: 'ReactNode', def: '—', desc: { zh: '主内容,排版随你', en: 'Main content, layout is yours' } },
620
650
  { name: 'panel', type: 'ReactNode', def: '—', desc: { zh: '展开后才显示的额外内容', en: 'Extra content shown only when expanded' } },
651
+ { name: 'hoverable', type: 'boolean', def: "'false'", desc: { zh: '整卡 hover 变色 + 光标变 pointer,统一 --dshpc-card-hover;默认关', en: 'Whole-card hover plus a pointer cursor, using --dshpc-card-hover; default off' } },
652
+ { name: 'filled', type: 'boolean', def: "'true'", desc: { zh: '是否带底色;false 时底透明、边框仍在。默认有底(Card = surface-1 / StripCard = accent 浅底)', en: 'Background fill; false makes it transparent while keeping the border. Default on (Card = surface-1 / StripCard = accent tint)' } },
621
653
  { name: 'className / style', type: '—', def: '—', desc: { zh: '追加类 / 样式', en: 'Append class / style' } },
622
654
  ],
623
655
  },
624
656
  ],
625
657
  code: [{ id: 'ui-card-example', lang: 'tsx', src: cardExample }],
626
658
  },
659
+ {
660
+ id: 'list-row',
661
+ heading: { zh: 'ListRow(一行一条)', en: 'ListRow (one item per line)' },
662
+ prose: [
663
+ {
664
+ zh: '一行代表一个可选 / 可点的对象(文件、任务、候选项):常态就带底色,鼠标移上去按 Button 灰色档加深一档;选中换成 slate 的极浅蓝灰单色背景,不描边、不画竖条。左主内容、右侧一组小操作,点右侧不连带触发整行。底 / 边 / 圆角复用 Surface,颜色全走 token,插件不写第二套皮肤。',
665
+ en: 'One line per selectable / clickable object (file, task, candidate): a visible background at rest that darkens one step on hover using the same grey tokens as Button; selected rows swap to a single pale slate-blue-grey background — no border, no rail. Main content on the left, a group of small actions on the right that never trigger the row. Background / border / radius come from Surface and colors from tokens, so plugins write no second skin.',
666
+ },
667
+ {
668
+ zh: '默认中号(min-height 28,与 Button md 同档),不设 size 轴;四边内距 8px 等距;默认字号 13px / 行高 20px 由核心给(`--dshpc-font-md` / `--dshpc-line-md`,与 Table 同档),调用方不必自己设、可用内联 style 覆盖。`bordered` 切带边 / 不带边,无边档仍是 1px 透明占位,两档同高。`selected` 只改外观,状态由调用方管。',
669
+ en: 'One medium tier by default (min-height 28, same as Button md); no size axis; equal 8px padding on all four sides. The core sets a default 13px / 20px type scale (`--dshpc-font-md` / `--dshpc-line-md`, same as Table) — callers need not set it and can override it inline. `bordered` switches the edge on/off — the borderless tier keeps a transparent 1px placeholder so heights stay equal. `selected` only changes appearance; the caller owns the state.',
670
+ },
671
+ {
672
+ zh: '`hoverable` 默认跟随「整行可点」(可点行开、纯展示行关,与 Table 的 hover 口径一致),可显式开关;hover / 选中色取 `--dshpc-active` / `--dshpc-listrow-selected-bg`(由 `--dshpc-slate` 派生,亮色 14% / 暗色 26% 两档,暗色不发黑)。整行不是 `<button>`:调用方要往里放 Button / IconButton,嵌 button 是非法 HTML;键盘走 Enter / Space,右侧操作槽挡住冒泡。',
673
+ en: '`hoverable` follows row clickability by default (on for clickable rows, off for static ones, matching Table), and can be forced on/off; hover / selected use `--dshpc-active` / `--dshpc-listrow-selected-bg` (derived from `--dshpc-slate`, 14% light / 26% dark, so the dark theme never goes black). The row is not a `<button>`: callers put Buttons inside it and nested buttons are invalid HTML. Keyboard uses Enter / Space; the actions slot stops propagation.',
674
+ },
675
+ ],
676
+ api: [
677
+ {
678
+ id: 'ui-list-row',
679
+ rows: [
680
+ { name: 'children', type: 'ReactNode', def: '—', desc: { zh: '主内容:图标 / 名字 / 标签 / 左右分区自己排', en: 'Main content: icons, labels, tags, left/right layout are yours' } },
681
+ { name: 'actions', type: 'ReactNode', def: '—', desc: { zh: '右侧操作槽;核心挡住事件冒泡,不连带整行', en: 'Right-side actions; the core stops propagation so the row is not triggered' } },
682
+ { name: 'onClick', type: '() => void', def: '—', desc: { zh: '给了才让整行可点(role=button / tabIndex / Enter·Space)', en: 'Only makes the row clickable when provided (role=button / tabIndex / Enter·Space)' } },
683
+ { name: 'selected', type: 'boolean', def: "'false'", desc: { zh: '选中态:slate 极浅蓝灰单色背景(明暗各一档),不描边 / 不画竖条,压在 hover 之上', en: 'Selected: a single pale slate-blue-grey background (separate light / dark tiers), no border or rail, above hover' } },
684
+ { name: 'bordered', type: 'boolean', def: "'false'", desc: { zh: '带边 / 不带边,两档同高', en: 'Bordered / borderless; both tiers share the same height' } },
685
+ { name: 'hoverable', type: 'boolean', def: '自动', desc: { zh: 'hover 灰态加深开关;默认跟随整行可点', en: 'Grey-state hover switch; defaults to row clickability' } },
686
+ { name: 'className / style', type: '—', def: '—', desc: { zh: '追加类 / 样式,只允许补布局', en: 'Append class / style, layout patches only' } },
687
+ ],
688
+ },
689
+ ],
690
+ code: [{ id: 'ui-listrow-example', lang: 'tsx', src: listRowExample }],
691
+ },
627
692
  {
628
693
  id: 'chip',
629
694
  heading: { zh: 'Chip', en: 'Chip' },
package/docs/help/ui.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  > EN: ui — base controls
6
6
 
7
- 导出:Button、IconButton、Segmented、SplitGroup、Input、PrefixedInput、NumberInput、Textarea、Checkbox、fieldMetricsOf、Picker、WorkspacePicker、ModelPicker、DateTimePicker、Surface、Card、StripCard、Chip、Table、Pagination、Modal、SideSheet、FieldHint、setTooltipRenderer、showToast、notify、showHostToast、Loader、LoaderBar、Loading、RunningBlocks、SectionHead、MarqueeText、FlipText、LiveText、QuickToolbar、startResizeLayoutWidth、PanelDock、createPanelDock、DockPanel、ensureControls、setStyleKit
7
+ 导出:Button、IconButton、Segmented、SplitGroup、Input、PrefixedInput、NumberInput、Textarea、Checkbox、fieldMetricsOf、Picker、WorkspacePicker、ModelPicker、DateTimePicker、Surface、Card、StripCard、Chip、Table、ListRow、Pagination、Modal、SideSheet、FieldHint、setTooltipRenderer、showToast、notify、showHostToast、Loader、LoaderBar、Loading、RunningBlocks、SectionHead、MarqueeText、FlipText、LiveText、QuickToolbar、startResizeLayoutWidth、PanelDock、createPanelDock、DockPanel、ensureControls、setStyleKit
8
8
 
9
9
  ## 概览
10
10
 
@@ -43,6 +43,10 @@ IconButton:variant: plain|outline|danger(默认 plain,**无底色**)、s
43
43
 
44
44
  > EN: IconButton: variant plain|outline|danger (default plain, **no background**), size (default lg), icon (required), label (required, doubles as a11y name and title), color (icon colour), background (fill; omitted = no fill).
45
45
 
46
+ `dense` 是两者共用的紧凑图标档:`size` / 高度一律不变,图标放大一档、内距收紧(Button 的 `--btn-pad` 从 10/12/14 收到 6,图标容器从 0.875em 到 1.25em;IconButton 把图标 `<svg>` 的 `width/height` 从 1em 提到 1.25em,正方形宽高不变、四周留白变小)。两者 dense 下图标都是 sm 15px / md·lg 16.25px。IconButton 的按钮显式 `padding:0`、图标 `flex:none`:svg 是 flex item,不禁止收缩会被内距压成窄条,此时改 `width` 或加 `!important` 都无效。给描边细、缩到 sm 看不清的图标用(如文件类型 / 语言图标);默认 false,原有按钮外观不变。
47
+
48
+ > EN: `dense` is a shared compact-icon tier for both: `size` and height stay put while the icon grows one step and padding tightens (Button: `--btn-pad` 10/12/14 → 6, icon container 0.875em → 1.25em; IconButton: the icon `<svg>` goes from `width/height:1em` to `1.25em`, same square box, less breathing room). Both give sm 15px / md·lg 16.25px icons. An IconButton sets `padding:0` on itself and `flex:none` on the icon: the svg is a flex item, and without disabling shrinking the padding squeezes it into a narrow strip — changing `width` or adding `!important` cannot fix that. Use it for thin-stroke icons that vanish at `sm` (file-type / language glyphs). Defaults to false, so existing buttons are unchanged.
49
+
46
50
  两者的 loading 语义一致:`onClick` 返回 Promise 就转圈(纯图标钮把图标位换成小圈,`aria-label` 不变、补 `aria-busy`),`onBusyChange` 把「在跑」报出去;受控 `loading` 与它取或 —— 别处刷新也能点亮同一颗按钮。转圈期间按钮自己的图标位(`icon` / `iconRight`)全部让位,只留转圈 + 文字。
47
51
 
48
52
  > EN: Both share one loading semantics: a Promise-returning `onClick` spins (an icon button swaps its icon for the spinner, keeps `aria-label` and adds `aria-busy`), `onBusyChange` reports "running", and the controlled `loading` prop is OR-ed in — an external refresh lights the same button too. While spinning, the button’s own icon slots (`icon` / `iconRight`) all step aside: spinner plus label only.
@@ -59,6 +63,7 @@ Segmented:`items`(必填)支持单选 / 多选,`size`(默认 lg;三
59
63
  |---|---|---|---|---|
60
64
  | `variant` | `上表 18 档(primary/secondary/danger/success/warning/info/outline/danger-outline/ghost/link + 扩展 purple/pink/cyan/teal/orange/indigo/rose/slate)` | 'outline' | 外观档 | Appearance tier |
61
65
  | `size` | `'sm' \| 'md' \| 'lg'` | 'lg' | 高度档(24 / 28 / 32,有边无边同高) | Height tier (24 / 28 / 32, same height with or without border) |
66
+ | `dense` | `boolean` | false | 紧凑图标档:高度不变,图标放大、内距收紧(Button / IconButton 同名同义) | Compact-icon tier: same height, larger icon and tighter padding (same prop on Button and IconButton) |
62
67
  | `icon` | `ReactNode` | — | 前置图标(官方图标组件 / 字符串均可) | Leading icon (official icon component or string) |
63
68
  | `iconRight` | `ReactNode` | — | 后置图标(展开箭头、外链) | Trailing icon (expand arrow, external link) |
64
69
  | `color` | `string` | — | 自定义主色:实心档作底色,线框 / 无底档作主色 | Custom main color: background for solid tiers, main color for outline/bare |
@@ -360,6 +365,10 @@ h(Surface, { tone: 'sunken', border: 'dashed', padding: 'lg' }, '还没做的东
360
365
 
361
366
  > EN: The header centers vertically as a whole; drop a bare element into `action` and it centers. Colors come from tokens, so light/dark follows the host.
362
367
 
368
+ `hoverable` 开启后**整卡**悬停变色、同时把光标改成 `pointer`,统一吃 `--dshpc-card-hover`(亮 7% 蓝 / 暗 13% 蓝),默认关;`filled` 切底色,默认有底(Card = surface-1,StripCard = accent 浅底),`false` 时底透明但边框仍在。两根轴都只改 `--surface-bg`,插件不用再自己写 `:hover` 规则;透明底 + hover 也照常变色。
369
+
370
+ > EN: `hoverable` (default off) changes the **whole card** on hover and switches the cursor to `pointer`, using `--dshpc-card-hover` (7% blue light / 13% blue dark). `filled` (default on) controls the background — Card uses surface-1, StripCard the accent tint; `false` makes it transparent but keeps the border. Both axes only rewrite `--surface-bg`, so plugins no longer need their own `:hover` rules; a transparent card still changes color on hover.
371
+
363
372
  | 参数 | 类型 | 默认 | 说明(zh) | 说明(en) |
364
373
  |---|---|---|---|---|
365
374
  | `accent` | `13 档 or 任意 CSS 色值(business/success/warning/danger/neutral + 扩展 purple/pink/cyan/teal/orange/indigo/rose/slate)` | 'business' | 档位映射到 design 的 token;也可传 '#f59e0b' 这类自定义值 | Tiers map to design tokens; also accepts custom values like '#f59e0b' |
@@ -369,6 +378,8 @@ h(Surface, { tone: 'sunken', border: 'dashed', padding: 'lg' }, '还没做的东
369
378
  | `action` | `ReactNode` | — | 右上角操作区 | Top-right action area |
370
379
  | `children` | `ReactNode` | — | 主内容,排版随你 | Main content, layout is yours |
371
380
  | `panel` | `ReactNode` | — | 展开后才显示的额外内容 | Extra content shown only when expanded |
381
+ | `hoverable` | `boolean` | 'false' | 整卡 hover 变色 + 光标变 pointer,统一 --dshpc-card-hover;默认关 | Whole-card hover plus a pointer cursor, using --dshpc-card-hover; default off |
382
+ | `filled` | `boolean` | 'true' | 是否带底色;false 时底透明、边框仍在。默认有底(Card = surface-1 / StripCard = accent 浅底) | Background fill; false makes it transparent while keeping the border. Default on (Card = surface-1 / StripCard = accent tint) |
372
383
  | `className / style` | `—` | — | 追加类 / 样式 | Append class / style |
373
384
 
374
385
  ```tsx id="ui-card-example"
@@ -377,6 +388,7 @@ import { Card, Chip, StripCard } from 'dsh-plugin-core/ui'
377
388
  h(Card, {
378
389
  accent: 'warning',
379
390
  bar: { side: 'left', inset: 12, gap: 12 }, // 色条可放 left / right / top / bottom
391
+ hoverable: true, // 整卡 hover 变色(默认关)
380
392
  action: h(Button, { size: 'sm', variant: 'ghost' }, '跳过'),
381
393
  },
382
394
  h('div', null, '标题'),
@@ -385,11 +397,59 @@ h(Card, {
385
397
 
386
398
  h(StripCard, {
387
399
  accent: 'success',
400
+ hoverable: true, filled: false, // 底色关掉 = 透明(默认有底)
388
401
  expanded: open, onToggle: () => setOpen(!open),
389
402
  panel: h('div', null, '展开详情'),
390
403
  }, h('div', null, '标题 + 元信息'))
391
404
  ```
392
405
 
406
+ ## ListRow(一行一条)
407
+
408
+ > EN: ListRow (one item per line)
409
+
410
+ 一行代表一个可选 / 可点的对象(文件、任务、候选项):常态就带底色,鼠标移上去按 Button 灰色档加深一档;选中换成 slate 的极浅蓝灰单色背景,不描边、不画竖条。左主内容、右侧一组小操作,点右侧不连带触发整行。底 / 边 / 圆角复用 Surface,颜色全走 token,插件不写第二套皮肤。
411
+
412
+ > EN: One line per selectable / clickable object (file, task, candidate): a visible background at rest that darkens one step on hover using the same grey tokens as Button; selected rows swap to a single pale slate-blue-grey background — no border, no rail. Main content on the left, a group of small actions on the right that never trigger the row. Background / border / radius come from Surface and colors from tokens, so plugins write no second skin.
413
+
414
+ 默认中号(min-height 28,与 Button md 同档),不设 size 轴;四边内距 8px 等距;默认字号 13px / 行高 20px 由核心给(`--dshpc-font-md` / `--dshpc-line-md`,与 Table 同档),调用方不必自己设、可用内联 style 覆盖。`bordered` 切带边 / 不带边,无边档仍是 1px 透明占位,两档同高。`selected` 只改外观,状态由调用方管。
415
+
416
+ > EN: One medium tier by default (min-height 28, same as Button md); no size axis; equal 8px padding on all four sides. The core sets a default 13px / 20px type scale (`--dshpc-font-md` / `--dshpc-line-md`, same as Table) — callers need not set it and can override it inline. `bordered` switches the edge on/off — the borderless tier keeps a transparent 1px placeholder so heights stay equal. `selected` only changes appearance; the caller owns the state.
417
+
418
+ `hoverable` 默认跟随「整行可点」(可点行开、纯展示行关,与 Table 的 hover 口径一致),可显式开关;hover / 选中色取 `--dshpc-active` / `--dshpc-listrow-selected-bg`(由 `--dshpc-slate` 派生,亮色 14% / 暗色 26% 两档,暗色不发黑)。整行不是 `<button>`:调用方要往里放 Button / IconButton,嵌 button 是非法 HTML;键盘走 Enter / Space,右侧操作槽挡住冒泡。
419
+
420
+ > EN: `hoverable` follows row clickability by default (on for clickable rows, off for static ones, matching Table), and can be forced on/off; hover / selected use `--dshpc-active` / `--dshpc-listrow-selected-bg` (derived from `--dshpc-slate`, 14% light / 26% dark, so the dark theme never goes black). The row is not a `<button>`: callers put Buttons inside it and nested buttons are invalid HTML. Keyboard uses Enter / Space; the actions slot stops propagation.
421
+
422
+ | 参数 | 类型 | 默认 | 说明(zh) | 说明(en) |
423
+ |---|---|---|---|---|
424
+ | `children` | `ReactNode` | — | 主内容:图标 / 名字 / 标签 / 左右分区自己排 | Main content: icons, labels, tags, left/right layout are yours |
425
+ | `actions` | `ReactNode` | — | 右侧操作槽;核心挡住事件冒泡,不连带整行 | Right-side actions; the core stops propagation so the row is not triggered |
426
+ | `onClick` | `() => void` | — | 给了才让整行可点(role=button / tabIndex / Enter·Space) | Only makes the row clickable when provided (role=button / tabIndex / Enter·Space) |
427
+ | `selected` | `boolean` | 'false' | 选中态:slate 极浅蓝灰单色背景(明暗各一档),不描边 / 不画竖条,压在 hover 之上 | Selected: a single pale slate-blue-grey background (separate light / dark tiers), no border or rail, above hover |
428
+ | `bordered` | `boolean` | 'false' | 带边 / 不带边,两档同高 | Bordered / borderless; both tiers share the same height |
429
+ | `hoverable` | `boolean` | 自动 | hover 灰态加深开关;默认跟随整行可点 | Grey-state hover switch; defaults to row clickability |
430
+ | `className / style` | `—` | — | 追加类 / 样式,只允许补布局 | Append class / style, layout patches only |
431
+
432
+ ```tsx id="ui-listrow-example"
433
+ import { ListRow, Button, Chip } from 'dsh-plugin-core/ui'
434
+
435
+ // 一行一条:常态带底,点整行打开 / 选中,右侧小操作不连带触发整行
436
+ h(ListRow, {
437
+ onClick: () => openFile(entry),
438
+ selected: entry.path === currentPath,
439
+ actions: h(Button, {
440
+ size: 'sm', variant: 'ghost', onClick: () => removeEntry(entry),
441
+ }, '移除'),
442
+ },
443
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
444
+ h(Chip, null, 'TS'),
445
+ h('span', null, entry.name),
446
+ ),
447
+ )
448
+
449
+ // 带边 / 关掉 hover / 纯展示(不可点)
450
+ h(ListRow, { bordered: true, hoverable: false }, '只读的一行')
451
+ ```
452
+
393
453
  ## Chip
394
454
 
395
455
  > EN: Chip
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-core",
3
- "version": "0.35.3",
3
+ "version": "0.35.4",
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": [