---
title: "原生 UI 组件：布局、控件与图表 · Metrix.Bar"
description: "Metrix.Bar .bar v0 支持的原生 UI 组件：Column、Row、Box、Text、Badge、Button、Switch、Select、Chart、属性反射规则、颜色系统与事件回调。"
canonical: "https://metrix.bar/zh/docs/components"
language: "zh-CN"
---

# 原生 UI 组件

> Metrix.Bar .bar v0 原生 UI 组件库、属性反射、布局与事件回调完整参考。

## 概述

在 `.bar` v0 中，Worker 的 `render(env)` 钩子直接返回 JSON 树形结构来描述界面：

```javascript
{
  type: "Column",
  props: { gap: 8, padding: 12 },
  children: [
    { type: "Text", props: { text: "你好", bold: true } },
    { type: "Button", props: { text: "点我", onClick: () => { ... } } }
  ]
}
```

每个 `type` 对应由 [mygo](https://github.com/egoist/mygo) 驱动的跨平台桌面原生组件。在 `props` 中传入的属性通过反射自动映射到底层原生组件的方法中。

---

## 组件目录

### 布局与容器

| 组件名                 | 描述                   | 常用属性                                                       |
| ---------------------- | ---------------------- | -------------------------------------------------------------- |
| `Box`                  | 通用矩形容器           | `padding`, `background`, `border`, `radius`, `width`, `height` |
| `Column`               | 垂直弹性布局           | `gap`, `padding`, `alignItems`, `justify`                      |
| `Row`                  | 水平弹性布局           | `gap`, `padding`, `alignItems`, `justify`                      |
| `Grid`                 | 网格布局               | `columns`, `columnTracks`, `rowTracks`, `gap`, `padding`       |
| `Scroll`               | 垂直滚动容器           | `height`, `maxHeight`, `padding`                               |
| `ScrollHorizontal`     | 水平滚动容器           | `width`, `maxWidth`, `padding`                                 |
| `ScrollBoth`           | 双向滚动容器           | `width`, `height`                                              |
| `Spacer`               | 弹性占位空白           | —                                                              |
| `Divider`              | 水平或垂直分割线       | `orientation` (`"horizontal"`, `"vertical"`)                   |
| `Form`                 | 表单分组容器           | `gap`, `onSubmit`                                              |
| `Accordion`            | 手风琴折叠面板         | `title`, `open`                                                |
| `AccordionItem`        | 手风琴折叠子项         | `title`, `open`, `onChange`                                    |
| `Field`                | 带标签的表单字段包装器 | `label`, `description`, `error`                                |
| `Fieldset`             | 表单控件集合           | `legend`                                                       |
| `Collapsible`          | 可展开/收起的内容面板  | `open`                                                         |
| `Overlay`              | 浮层遮罩容器           | `children`                                                     |
| `Split`                | 水平可调分栏容器       | `size`, `onChange`（接收左右两个子节点）                       |
| `SplitPercent`         | 水平百分比分栏容器     | `size`, `onChange`（接收左右两个子节点）                       |
| `SplitVertical`        | 垂直可调分栏容器       | `size`, `onChange`（接收上下两个子节点）                       |
| `SplitVerticalPercent` | 垂直百分比分栏容器     | `size`, `onChange`（接收上下两个子节点）                       |
| `Sidebar`              | 侧边栏导航容器         | `value`, `onChange`                                            |
| `SidebarSection`       | 侧边栏分组区块         | `title`, `open`                                                |
| `SidebarItem`          | 侧边栏导航条目         | `id`, `label`, `icon`, `badge`                                 |
| `Toolbar`              | 顶部或底部工具栏       | `gap`, `padding`                                               |
| `ToggleGroup`          | 互斥或多选切换按钮组   | `value`, `onChange`                                            |
| `CheckboxGroup`        | 复选框组合容器         | `label`, `gap`                                                 |

`Split` 与 `SplitVertical` 的 `size` 表示第一个面板的宽或高，单位为 DIP，默认 200。`onChange(size)` 返回调整后的 DIP 值。`SplitPercent` 与 `SplitVerticalPercent` 接收相同结构的两个子节点，`size` 为 0..100 的百分数（默认 50），`onChange(value)` 回传调整后的原生百分数值，面板随可用空间同比缩放。组件仅渲染前两个子节点；缺失的面板留空，额外子节点忽略。

### 展示与导航

| 组件名        | 描述                                   | 常用属性                                                               |
| ------------- | -------------------------------------- | ---------------------------------------------------------------------- |
| `Text`        | 文本标签                               | `text`, `fontSize`, `bold`, `italic`, `color`, `wrap`, `letterSpacing` |
| `RichText`    | 富文本块（支持多区间样式）             | `spans`, `fontSize`, `color`                                           |
| `Badge`       | 徽标胶囊标签                           | `text`, `color`, `background`                                          |
| `Link`        | 超链接文本                             | `text`, `url`, `color`                                                 |
| `Icon`        | 原生系统或远程图标                     | `name`, `svg`, `size`, `color`                                         |
| `Image`       | 图片展示（支持 URL 或内联数据）        | `src`, `svg`, `name`, `width`, `height`                                |
| `Avatar`      | 用户头像（支持缩写文本回退）           | `name`, `src`, `size`                                                  |
| `Breadcrumbs` | 面包屑导航                             | `items`, `value`, `onChange`                                           |
| `Progress`    | 进度条（负值表示不确定加载，支持反向） | `value`, `reverse`, `width`, `color`                                   |
| `Meter`       | 标量计量计                             | `value`, `min`, `max`, `low`, `high`, `optimum`                        |
| `Spinner`     | 加载动画指示器                         | `size`                                                                 |

`Image` 与 `Avatar` 让你可以在面板中直接显示远程图片或内联图标：

```javascript
{
  type: "Image",
  props: {
    src: "https://example.com/a.png",
    width: 64,
    height: 64
  }
}
```

- **来源与优先级**：`src` 支持 HTTPS 与已授权的局域网 HTTP 地址或 `data:image/...` 内联数据（PNG、JPEG、GIF、WebP、SVG）。属性优先级为 `src` > `svg` > `name`。
- **头像展示**：`Avatar` 支持位图格式（`src`）。加载中、加载失败或传入 SVG 时，会自动回退显示由 `name` 生成的首字母缩写。
- **网络白名单**：`Image`、`Avatar`、`Icon`（`name`）与 `tray.icon` 中的远程图片均必须在 `<permissions><network>` 声明的来源内。允许已授权的局域网 HTTP 来源，单张图片限制为 8 MB 且单边不超过 4096 像素（总像素不超过 1600 万）。`data:` 内联图片与 Iconify 名称（如 `mdi:...`）无需配置网络权限。

### 数据展示与表格

| 组件名         | 描述                                   | 常用属性                                                                       |
| -------------- | -------------------------------------- | ------------------------------------------------------------------------------ |
| `Table`        | 数据表格（支持列宽与表头排序）         | `columns`, `items`, `value`, `sort`, `onSort`, `onChange`, `onSubmit`          |
| `List`         | 原生列表视图（支持自定义行与拖拽重排） | `items`, `children`, `value`, `followEnd`, `onChange`, `onSubmit`, `onReorder` |
| `GridView`     | 虚拟化网格项目视图                     | `items`, `minWidth`, `height`, `value`, `onChange`, `onSubmit`, `onReorder`    |
| `Outline`      | 树形分层大纲                           | `items`, `open`, `onChange`                                                    |
| `OutlineTable` | 带多列属性的树形大纲表格               | `columns`, `items`, `open`, `onChange`                                         |
| `Tree`         | 树形结构容器                           | `children`                                                                     |
| `TreeItem`     | 树形可展开子节点                       | `label`, `open`, `onChange`                                                    |

#### List 自定义行与拖拽重排

`List` 支持通过子节点自定义行布局，且 `List` 与 `GridView` 均支持拖拽重排：

- **自定义子节点行**：向 `List` 传入子节点时，每个子节点渲染为独立的一行（此时 `items` 属性被忽略），并保留子节点自身声明的 `key`：
  ```javascript
  {
    type: "List",
    props: { onReorder: (rows, to) => handleReorder(rows, to) },
    children: items.map(item => ({
      type: "Row",
      props: { key: item.id, padding: 8, gap: 12, alignItems: "center" },
      children: [
        { type: "Text", props: { text: item.name } },
        { type: "Badge", props: { text: item.status } }
      ]
    }))
  }
  ```
- **受控拖拽重排 (`onReorder`)**：`onReorder(rows, to)` 回调接收被移动的行下标数组（`rows`）与目标插入下标（`to`）。排序为受控模式：宿主通知 Worker 而不擅自修改数据，由脚本更新状态后重新渲染。拖拽释放必须落在列表内容区域内。

### 交互控件

事件回调（`onClick`、`onChange`、`onSubmit` 等）可直接传入 JavaScript 函数。

| 组件名          | 描述                                       | 常用属性                                                                          |
| --------------- | ------------------------------------------ | --------------------------------------------------------------------------------- |
| `Button`        | 原生常规按钮（支持 children 自定义内容）   | `text`, `children`, `icon`, `onClick`, `disabled`                                 |
| `PrimaryButton` | 主操作强调按钮（支持 children 自定义内容） | `text`, `children`, `icon`, `onClick`, `disabled`                                 |
| `Switch`        | 原生滑动开关                               | `value`, `onChange`                                                               |
| `Checkbox`      | 带标签的复选框                             | `label`, `value`, `onChange`                                                      |
| `Toggle`        | 按压状态切换按钮                           | `label`, `value`, `onChange`                                                      |
| `Radio`         | 单选框选项                                 | `label`, `value`, `selected`, `onChange`                                          |
| `RadioGroup`    | 单选框组合                                 | `value`, `onChange`                                                               |
| `Select`        | 下拉选择器（mygo 原生组件）                | `options`, `items`, `value`, `onChange`                                           |
| `Combobox`      | 可搜索组合框                               | `options`, `value`, `onChange`                                                    |
| `Segmented`     | 分段控制器（mygo 原生组件）                | `options`, `items`, `value`, `onChange`                                           |
| `Tabs`          | 选项卡容器（mygo 原生组件）                | `items`, `labels`, `options`, `value`, `onChange`                                 |
| `TextInput`     | 单行文本输入框                             | `value`, `placeholder`, `onChange`, `onSubmit`                                    |
| `TextArea`      | 多行文本编辑框（支持行数约束）             | `value`, `rows`, `lines` (`[min, max]`), `onChange`                               |
| `SearchField`   | 原生搜索框                                 | `value`, `placeholder`, `onChange`                                                |
| `Slider`        | 连续数值滑块                               | `value`, `min`, `max`, `step`, `onChange`                                         |
| `StepSlider`    | 步进滑块                                   | `value`, `steps`, `onChange`                                                      |
| `Stepper`       | 增减步进器                                 | `value`, `min`, `max`, `step`, `onChange`                                         |
| `NumberInput`   | 数字输入框                                 | `value`, `min`, `max`, `step`, `onChange`                                         |
| `RangeSlider`   | 双手柄范围滑块                             | `value`, `min`, `max`, `onChange`                                                 |
| `Rating`        | 星级评分控件                               | `value`, `max`, `onChange`                                                        |
| `ColorPicker`   | 交互式颜色选择器                           | `color`, `onChange`                                                               |
| `ColorWell`     | 色块展示                                   | `color`                                                                           |
| `MenuButton`    | 弹出菜单按钮                               | `text`, `items`                                                                   |
| `EditableText`  | 行内可编辑文本                             | `value`, `placeholder`, `onChange`                                                |
| `Autocomplete`  | 自动补全输入框                             | `value`, `suggestions`, `onChange`, `onSubmit`                                    |
| `TokenField`    | 标签词输入框                               | `value`, `suggestions`, `onChange`                                                |
| `Editor`        | 原生代码编辑器                             | `value`, `language`, `readOnly`, `lineNumbers`, `softWrap`, `tabSize`, `onChange` |

`Tabs` 的 `items` 接受字符串或 `{ label }` 对象，也可通过 `labels` / `options` 传入字符串数组。`value` 和 `onChange` 的参数都是选中项的下标。

`Segmented` 和 `Select` 的 `items` 接受字符串或 `{ label, value }` 对象，也可通过 `options` 传入字符串数组。每项的 `value` 缺省等于 `label`；组件按每项的 `value` 匹配 `value` 属性，`onChange` 回传选中项原始的 JSON 值。选项内部使用下标标识，因此允许重复标签；标签改名或重排后，只要值仍匹配就能保持选择。值未匹配时，`Select` 显示空标签，`Segmented` 遵循 MyGO 显示首项。

这三种组件只支持普通文本标签，不支持选项图标、徽标、禁用状态、分组标题或分割线。

### 时间与日期控件

| 组件名      | 描述           | 常用属性                             |
| ----------- | -------------- | ------------------------------------ |
| `Calendar`  | 原生月历视图   | `value`, `onChange`                  |
| `DateInput` | 日期选择输入框 | `value` (`"YYYY-MM-DD"`), `onChange` |
| `TimeInput` | 时间选择输入框 | `value` (`"HH:mm"`), `onChange`      |

### 浮层与弹窗

| 组件名        | 描述             | 常用属性                                                       |
| ------------- | ---------------- | -------------------------------------------------------------- |
| `Modal`       | 原生模态浮层卡片 | `open`, `onDismiss`                                            |
| `Popover`     | 气泡弹出卡片     | `open`, `anchor`, `onDismiss`                                  |
| `TooltipBase` | 悬浮自定义提示   | `anchor`, `children`                                           |
| `AlertDialog` | 警告确认对话框   | `open`, `title`, `message`, `buttons`, `onAction`, `onDismiss` |
| `FindBar`     | 原生查找搜索条   | `open`, `value`, `current`, `matches`, `onChange`, `onDismiss` |
| `Toast`       | 浮动轻提示通知   | `open`, `title`, `description`, `type`, `timeout`, `onClose`   |

### 图表组件 (`Chart`)

`Chart` 组件可在面板中直接渲染折线图、面积图、柱状图与饼图：

```javascript
{
  type: "Chart",
  props: {
    type: "area",          // "line" | "area" | "bar" | "pie" | "radar" | "candlestick" | "sankey"
    height: 120,
    appear: true,          // 开启首次加载动效（默认 true）
    appearKey: "v1",       // 触发重播的业务 key（可选）
    theme: {
      colors: ["#3b82f6", "#10b981"], // 自定义调色板（可选）
      gridColor: "border",            // 网格线颜色（可选）
      appearDuration: 1000,           // 登场动效时长毫秒（0..5000，默认 1000）
      hoverDuration: 120,             // 悬停过渡与淡出时长毫秒（0..500，默认 120）
    },
    xKind: "time",         // "time" | "number" | "category"
    curve: "monotone",     // "linear" | "monotone" | "step" | "stepBefore" | "stepAfter"
    strokeWidth: 1.5,
    grid: true,
    gridStyle: "dashed",   // "dashed" | "solid"
    legend: false,         // pie 默认 true，其余默认 false
    dots: false,           // 为 line/area 绘制数据点
    hover: true,           // 开启原生 Overlay 悬停提示与键盘导览
    xTicks: 5,             // 2..12，基于文本测量动态抽稀
    yTicks: 4,             // 2..12
    referenceLines: [
      { value: 100, label: "Target", color: "accent" }
    ],
    series: [
      {
        name: "请求数",
        fill: { from: 0.28, to: 0.02 },
        points: [
          { x: 1700000000000, y: 120, valueLabel: "120 req/s" },
          { x: 1700003600000, y: 240, valueLabel: "240 req/s" },
          { x: 1700007200000, y: 190, valueLabel: "190 req/s" }
        ]
      }
    ]
  }
}
```

- **图表类型、主题与登场动效**：
  - `type`：`"line"`（默认）、`"area"`、`"bar"`、`"pie"`、`"radar"`、`"candlestick"` 或 `"sankey"`。
  - `theme`：图表主题样式对象（可选）：
    - `colors`：调色板颜色字符串数组；未显式指定系列/数据点颜色时依序轮转；
    - `gridColor`：网格线颜色，支持十六进制或主题语义色名，缺省由当前主题 `Border.Alpha(0.55)` 派生；
    - `riseColor` 与 `fallColor`：K 线图涨跌颜色，顶层显式 `riseColor`/`fallColor` 优先于 `theme` 对应字段，未配置时回退默认色（`#10b981` 与 `#ef4444`）；
    - 颜色优先级：系列/数据项/节点显式颜色 > `theme.colors` > 系统明暗默认调色板；
    - `appearDuration`：初始登场动效时长（毫秒），有限数值限制在 `0..5000` 区间，默认 1000；
    - `hoverDuration`：悬停过渡与淡出时长（毫秒），有限数值限制在 `0..500` 区间，默认 120；
    - 动效降级：时长设为 0 或系统开启减弱动态效果（ReduceMotion）时瞬间切换；系统浅深外观变化会更新图表颜色。
  - `appear`：布尔值（默认 `true`）。控制首次挂载时的登场动效（四次缓出曲线 `1 - (1 - t)^4`）和数据更新过渡。设为 `false` 时数据立即切换。
  - 数据更新：`line`、`area`、`bar` 与 `pie`（含圆环图）在系列、标签及点数等结构兼容时，以 300ms 同步过渡数值和坐标域；期间再次更新会从当前显示值继续。整张图超过 4096 个数据点、结构不兼容或系统开启 ReduceMotion 时立即切换。`radar`、`candlestick` 与 `sankey` 的数据更新直接生效。
  - `appearKey`：字符串（可选）。用于在特定业务 key 变化时重播登场动效；缺省时仅在首次挂载时播放。
  - `legend`：布尔值，控制是否在图表底部或右侧渲染图例。`pie` 默认 `true`，其他类型默认 `false`（`sankey` 设为 `true` 时渲染节点图例）。
  - `dots`：布尔值，默认 `false`。为 `line`、`area` 与 `radar` 在各数据点绘制点标记。
  - `axes`：布尔值或 `{ x: boolean, y: boolean }`。设置 `axes: false` 即可隐藏全部坐标轴与刻度标签，呈现极为紧凑的 sparkline 迷你走势图。
  - `height`：组件高度 DIP，默认 120（`pie` 正方形边长默认 120）。
- **坐标轴、刻度与参考线**：
  - `xKind`：`"number"`（默认，普通数值）、`"time"`（Unix 毫秒时间戳）或 `"category"`（离散分类）。
  - `labels`：分类轴标签字符串数组 `string[]`，配合 `xKind: "category"` 或 `type: "radar"` 使用。X 坐标为从 0 起的整数索引；缺省 `labels` 时自动生成从 0 到最大索引的数字字符串。
  - `xTicks` / `yTicks`：刻度目标数，默认分别为 5 与 4，有效范围在 2..12。X 轴刻度自动消除相邻重叠，在空间允许时尽量保留首尾刻度。
  - `xDomain` / `yDomain`：显示域范围 `[lo, hi]` 二元数组。仅接受有限且 `lo < hi` 的数值；提供显式域后不再执行 Nice 刻度扩展。柱状图自动 Y 域始终包含 0，正负柱基线均为 0。
  - `grid` 与 `gridStyle`：`grid` 布尔值控制是否绘制 Y 刻度网格线（默认 `true`）；`gridStyle` 支持 `"dashed"`（默认轻量虚线）与 `"solid"`（实线）。网格线颜色可由 `theme.gridColor` 自定义。
  - `referenceLines`：参考线列表 `[{ value, label?, color? }]`。以固定的 Y 轴值（横向柱状图对应 X 轴值）绘制实线，支持自定义标签文字与主题色。
- **柱状图扩展**：
  - `horizontal`：布尔值，默认 `false`。仅在 `type: "bar"` 时生效，渲染横向条形图，X 与 Y 轴互换，柱子沿水平方向延展，基线对齐 `X = 0`。
  - `barRadius`：柱子圆角半径 DIP，默认 3，非负数。
  - `barGap`：柱间间距比例，默认 0.25，范围在 0..0.9 之间。
- **系列填充与渐变**：
  - `series` 中的每个系列支持 `fill`。可设置为布尔值（`true` 为 0.24 透明度）、`0..1` 透明度数值，或 `{ from: number, to: number }` 垂直渐变填充（角度 180 度，顶部透明度为 `from`，底部为 `to`，缺省为 `{ from: 0.24, to: 0 }`）。渐变填充优先于数值 fill；`area` 默认采用垂直渐变填充。
  - `color`：系列颜色，可传十六进制色值或主题语义色；缺省时轮转使用 `theme.colors` 或内置调色板。
  - `points`：数据点集合，每个点支持 `label`（覆盖 X 轴/时间/分类提示文本）与 `valueLabel`（覆盖数值提示文本）。
- **饼图与环形图（Donut）**：
  - 当 `type: "pie"` 时，数据通过 `data: [{ label, value, valueLabel?, color? }]` 传入。`valueLabel` 仅覆盖浮动提示中的数值文本；外侧标签使用 `label`。
  - `innerRadius`：`0..1` 浮点数，大于 0 时将饼图变为环形图，默认 0。
  - `padAngle`：浮点数（默认 `0.02`，取值限制在 `0..π`），控制扇区之间的夹角间隔。修改 `padAngle` 仅重构饼图扇区 Path，不重算其他几何。
  - `pieLabels`：布尔值（默认 `false`）。开启后在饼图外侧绘制双列文字标签与连接引线，在左右列内夹紧排布，不进行循环回绕；空间不足时自动省略低优先级小扇区标签，优先保证大扇区可读性与边界留白，不强制挤满。
  - 饼图交互与占比：浮动提示统一展示扇区数值（或 `valueLabel`）与占比份额（`Share: XX%`）；分母仅累计有效有限正值（`value > 0`），所有有效正值均先按最大值缩放，再以同一缩放后总和计算占比和扇区，杜绝除零或 NaN。悬停时选中扇区沿径向平移 6 DIP，其余扇区透明度降为 0.35；支持键盘左右键、Home、End 与 Escape 键导览，映射安全索引回原始数据项。
  - 默认展示图例（`legend: true`）。
- **雷达图（Radar）**：
  - 当 `type: "radar"` 时，复用 `labels`、`series`、`yDomain`、`legend`、`dots`、`fill`、`strokeWidth`、`yTicks`、`grid`、`hover`、`appear`、`appearKey`、`height` 与 `theme`；忽略 `xDomain`、`xKind`、`horizontal` 与 `referenceLines`。
  - `labels` 至少声明 3 个维度（`labels.length >= 3`）；每个系列的 `points` 必须完整覆盖所有维度且 `x` 对应离散分类整数索引（`0..labels.length-1`），不允许重复或缺失。
  - 标签预算：雷达图外围标签使用固定布局预算：宽度为 `min(80 DIP, 绘图区宽度 × 0.4)`，高度为字号的 2.5 倍；标签盒按绘图区边界夹紧，超出盒子预算的文字被裁剪，长中文或多行标签可能仅显示一部分。
  - 默认 `fill` 为 `0.12`，显式指定的 `fill`（数值或渐变）优先；环网与辐条自适应绘图区。
  - 支持按角度分区的指针命中、原生 Overlay 悬停数值提示与键盘导览。
- **K 线图（Candlestick / OHLC）**：
  - 当 `type: "candlestick"` 时，通过 `candles: [{ x, open, high, low, close, label?, openLabel?, highLabel?, lowLabel?, closeLabel? }]` 传入数据，不消费 `series`。
  - `riseColor` 与 `fallColor`：涨跌颜色，支持主题语义色名或十六进制；顶层显式设置优先于 `theme` 对应字段，未配置时回退默认色（`#10b981` 与 `#ef4444`）。`close >= open` 时采用 `riseColor`，`close < open` 时采用 `fallColor`。
  - 复用 `xKind`（`"time"` | `"number"` | `"category"`）、`labels`、`xDomain`、`yDomain`、`xTicks`、`yTicks`、`axes`、`grid`、`gridStyle`、`referenceLines`、`barGap`、`hover`、`appear`、`appearKey`、`height` 与 `theme`。
  - 自动 Y 域由数据中 `low` 与 `high` 的极值决定，平值向两侧各延展 1；柱宽由最近正相邻间距计算（回退 12 DIP，限制在 1..30 DIP 之间）。
  - 不对输入数据重新排序；当 X 轴数据无序时自动关闭交互与选择。
- **桑基图（Sankey）**：
  - 当 `type: "sankey"` 时，通过 `sankey: { nodes, links }` 传入预布局数据。
  - 预布局设计原则：宿主与 UI 侧**不做原生 DAG 自动布局**。节点坐标 `[x0, y0, x1, y1]` 及连线几何 `y0, y1, width` 均为 `[0, 1]` 归一化浮点数，由 Worker 脚本使用前端布局库（如 `d3-sankey`）预计算，并将 `source`/`target` 投影为字符串，归一化几何作为纯数据交付给宿主。所有 Y 坐标和宽度按绘图区实际高度转换。
  - 节点与边契约：保留首个合法重复 `id` 节点；节点 `value` 缺省或无效时由关联有效连线自动计算 `max(in, out)`。
  - 标签留白预算：按两端节点标签的实际文本宽度保留带上限的水平留白，仅在标签文本或字体族改变且留白实际变化时才使连线几何失效。
  - 交互与高亮：节点优先命中；连线支持 24 次二分贝塞尔曲线命中检测（命中计算零堆分配），悬停高亮关联的入度/出度连线与相邻节点（$O(V + E)$）。
  - Worker 预布局示例（结合 `d3-sankey` 归一化）：

    ```javascript
    import { sankey as d3Sankey } from "https://esm.sh/d3-sankey@0.12.3";

    const rawGraph = {
      nodes: [
        { id: "direct", label: "Direct" },
        { id: "signup", label: "Sign Up" },
      ],
      links: [{ source: "direct", target: "signup", value: 30 }],
    };

    const layout = d3Sankey()
      .extent([
        [0, 0],
        [1, 1],
      ])
      .nodeWidth(0.03)
      .nodePadding(0.06)
      .nodeId((d) => d.id);

    const graph = layout(rawGraph);

    const sankeyData = {
      nodes: graph.nodes.map((n) => ({
        id: n.id,
        label: n.label,
        value: n.value,
        x0: n.x0,
        y0: Math.max(0, n.y0),
        x1: n.x1,
        y1: Math.min(1, n.y1),
      })),
      links: graph.links.map((l) => ({
        source: typeof l.source === "object" ? l.source.id : l.source,
        target: typeof l.target === "object" ? l.target.id : l.target,
        value: l.value,
        y0: l.y0,
        y1: l.y1,
        width: l.width,
      })),
    };
    ```

- **原生悬停提示（Overlay Tooltip）与交互**：
  - 开启 `hover: true` 时，7 类图表统一使用原生 `ui.Overlay` 与 `AttachTo` 呈现浮层，锚定在数据标记或指针位置，自动根据窗口边界向内翻转与夹紧（不被图表绘图区 Bounds 裁剪）。
  - 非干扰交互：使用 `PassThrough()` 与 `KeepFocus()`，不抢夺指针焦点，不干扰其他浮层或快捷键。
  - 布局与排版：两列分栏排版（标题、数据项名称、数值与色点），宽度按窗口可用空间上限夹紧至 320 DIP，高度按可用预算裁剪；超出预算时截断显示，但完整文本始终保存在无障碍 `Label` 与读屏公告中。
  - 平滑过渡与淡出：浮层在指针移动时平滑过渡至新锚点，指针移出时平滑淡出（时长由 `theme.hoverDuration` 控制，默认 120ms，duration 0 或 ReduceMotion 即时生效）；按 Escape 键立即清除浮层与选中标记。
  - 缺失点行为：首个非空系列作为导览基准轴，必须为有限且单调递增，否则安全关闭悬停选择。多系列悬停按首系列实际 X 坐标精确匹配（有序系列采用二分查找，无序系列精确线性查找）；**只有真实存在该 X 坐标数据点的系列才会出现在浮动提示与吸附高亮点中，缺失系列自动省略，不按下标对齐，亦不进行前后插值**。
  - 键盘与无障碍：支持键盘左右箭头、Home、End 与 Escape 键导览，焦点切换时触发无障碍读屏播报（`c.Announce`）。
  - 横向柱状图沿 Y 轴分类吸附最近柱；饼图/环形图通过指针位置进行扇区命中检测；雷达图按角度扇区吸附；桑基图执行节点优先及连线二分曲线命中。

---

## 状态样式与交互动画

请在 Worker 中限制图表数据数组的长度。密集折线与面积图可简化绘制，悬停查看仍使用原始读数。

### 伪状态样式（State Props）

组件支持声明多种交互状态下的覆盖样式。当元素处于对应状态时，嵌套对象中的属性会自动与基础属性合并：

| 状态属性       | 触发时机                                 |
| -------------- | ---------------------------------------- |
| `hover`        | 鼠标悬浮在组件上方时                     |
| `pressed`      | 鼠标按住该组件时                         |
| `focused`      | 组件获得键盘焦点时                       |
| `focusVisible` | 通过键盘导航等明确途径获得焦点时         |
| `focusWithin`  | 组件自身或其任一子元素获得焦点时         |
| `disabled`     | 组件处于禁用状态时（亦可直接传入布尔值） |
| `highlighted`  | 列表或菜单项处于高亮选择态时             |

```javascript
{
  type: "Box",
  props: {
    padding: 12,
    background: "surface",
    hover: { background: "surfaceHover" },
    pressed: { background: "surfacePressed" }
  }
}
```

### 动画与平滑过渡

- **过渡动画 (`transition`)**：配置 `{ duration: 200, ease: "easeInOut" }` 为属性变更添加平滑过渡。
- **姿态与弹簧 (`animatePose`)**：支持声明动画目标与阻尼预设（如 `animatePose: { pose: { scale: 1.1 }, animation: "bouncy" }`）。
- **逐帧时间线 (`keyframes`)**：采样驱动 Scale、Translate、Rotate 与 Opacity：
  ```javascript
  keyframes: {
    duration: 300,
    frames: [
      { at: 0, scale: 0.8, opacity: 0 },
      { at: 1, scale: 1, opacity: 1 }
    ]
  }
  ```
- **视觉变换 (`transform`)**：对象简写（如 `{ translate: [0, -4], scale: 1.05 }`）或按步骤组合变换。
- **背景与边框渐变**：
  - **线性渐变 (`linearGradient` / `borderLinearGradient`)**：配置 `{ from: "#3b82f6", to: "#8b5cf6", angle: 45 }`，或通过多断点 `{ stops: [{ color: "#3b82f6", at: 0 }, { color: "#8b5cf6", at: 1 }], angle: 90 }` 精确控制（`at` 为 `0..1` 的位置）。
  - **角度渐变 (`conicGradient` / `borderConicGradient`)**：配置 `{ stops: [{ color: "#3b82f6", at: 0 }, { color: "#8b5cf6", at: 1 }], center: { x: 0.5, y: 0.5 }, angle: 0 }`（`center` 相对宽高比例，默认居中；`angle` 顺时针旋转角度）。
- **布局方向与逻辑锚点 (`direction` / 逻辑 Anchor)**：
  - 支持 `direction: "ltr" | "rtl"` 控制元素及其子元素的阅读与布局方向。
  - `attach` 属性支持逻辑锚点（`start`、`end`、`topStart`、`topEnd`、`bottomStart`、`bottomEnd`）与物理锚点（`top`、`bottom`、`left`、`right` 等）；在 RTL 布局下，逻辑锚点会按方向自适应。
- **投影与阴影 (`shadow` / `insetShadow`)**：配置 `{ x: 0, y: 4, blur: 12, color: "#00000020" }`。

---

## 事件回调与快捷键

除了常规的 `onChange(value)` 与 `onSubmit(value)`，Metrix.Bar 还提供了丰富的桌面级交互事件：

- **增强点击事件 (`onClick`)**：回调参数携带点击次数与按键修饰符：
  ```javascript
  onClick: (e) => {
    // e = { clicks: 1, mods: { meta: true, ctrl: false, alt: false, shift: false } }
  };
  ```
- **键盘监听 (`onKeyDown`)**：组件绑定 `onKeyDown` 后会自动成为可聚焦元素（可配合 `autoFocus: true` 使用）：
  ```javascript
  onKeyDown: (e) => {
    // e = { key: "Enter", mods: { meta: false, ... } }
  };
  ```
- **局部快捷键 (`shortcuts`)**：在任意容器或组件上绑定快捷键字典。当该元素自身或其焦点子树内的任意子元素获得焦点时生效（可配合 `autoFocus: true` 绑定面板级快捷键）：
  ```javascript
  shortcuts: {
    "CmdOrCtrl+R": () => refresh(),
    "CmdOrCtrl+1": () => selectTab(0),
    "Escape": () => back()
  }
  ```
  `shortcuts` 优先于 `onKeyDown`：若按键匹配焦点链上声明的快捷键，将优先执行快捷键回调，且不会向 `onKeyDown` 重复派发。
- **文本输入观察事件**：`onComposingChange(composing)` 在 MyGO 输入法预编辑状态变化时触发，初始 `false` 不派发。`onTextSelectionChange({ start, end, anchor, caret })` 在首次观察和选区变化时触发，偏移量按 Unicode 码点计算。`onPaste(text)` 观察粘贴文本并保留原生插入行为，不能阻止粘贴。这些事件支持 `TextInput`、`TextArea` 及其 `Base`、`Buffer`、`BufferBase` 变体；`SearchField`、`Combobox`、`NumberInput` 等组合控件不支持。`Editor` 与 `RichTextEditor` 使用各自的文档协议。这些是状态观察，不提供浏览器 composition 事件；`onKeyDown` 不包含 `isComposing`。
- **滑动手势 (`onSwipe`)**：参数为 `"left" | "right" | "up" | "down"`。
- **其他鼠标与视口事件**：`onDoubleClick`（双击）、`onRightClick`（右键）、`onInView(visible)`（可见性切换）、`onPressedOutside`（外部点击）以及 `onDismiss`（模态关闭）。

---

## 属性映射与样式

### `tw` 原生样式工具类

在节点 `props.tw` 中用空白分隔工具类；也可用于 Vue `h()`、HTM 等构树结果。`tw` 在每次 render 时解析，支持条件表达式、拼接和模板字符串，无需 Tailwind 安装、CSS、扫描源码或构建步骤。它是借鉴 Tailwind 命名的独立原生样式语法，支持范围以此处为准。

```javascript
const tileSize = 106.25;
const selected = true;
const node = {
  type: "Box",
  props: {
    tw: `size-[${tileSize}px] p-2 rounded-lg ${selected ? "bg-accent" : "bg-surface"} hover:bg-accentHover active:opacity-80`,
    paddingX: 0,
    hover: { background: "surfaceHover" },
  },
  children: [{ type: "Text", props: { text: "42", tw: "text-2xl font-semibold tabular-nums" } }],
};
```

#### 数值与颜色

- 通常的长度数字乘以 **4 DIP**：`p-2.5` 为 10 DIP，`w-20` 为 80 DIP；支持非负小数，不限于预设整数刻度。`[npx]` 直接表示 DIP，可保留小数：`w-[106.25px]`。这里的 `px` 是原生逻辑像素，**1 px = 1 DIP**，不是屏幕物理像素，也不接受 `rem`、`em`、`pt`、CSS 表达式或变量。
- 尺寸、`basis`、定位边与 `inset` 支持 `[n%]`；尺寸与 `basis` 还支持 `n/d` 分数（`0 <= n <= d`，`d > 0`），如 `w-1/2`。`w-full`、`h-full`、`size-full` 为 100%。这些长度以及 gap 支持 `px` 简写（如 `w-px` 为 1 DIP）；padding / margin 用 `[1px]`。
- margin、定位边、inset、translate 支持负号前缀或括号内负数，如 `-mt-2`、`left-[-3px]`；tracking、scale、rotate、skew 也支持负值。padding、gap 与尺寸不接受负值。只有 margin 支持 `auto`，如 `mx-auto`、`mt-auto`；显式 margin 属性也接受 `"auto"`。
- 下表的 `n` 为数字；网格及行数必须是 **1..1024 的整数**。所有数值须为有限十进制数，能由原生 float32 表达；拒绝指数写法、溢出和非零下溢。倍率、角度、线宽等例外见表。
- 颜色前缀 `bg-`、`text-`、`border-`、`decoration-`、`selection-` 支持主题色或 `[#rgb]`、`[#rgba]`、`[#rrggbb]`、`[#rrggbbaa]`。主题色为 `background`、`surface`、`surfaceHover`、`surfacePressed`、`border`、`text`、`textMuted`、`accent`、`accentSubtle`、`accentHover`、`accentPressed`、`accentText`、`danger`、`warning`、`success`、`selection`、`focus`、`inverse`、`inverseText`、`scrollbar`；忽略大小写、横线与下划线差异，如 `text-text-muted`。不支持 `bg-red-500` 等 CSS 调色板。

#### 支持类别

| 类别         | 工具类与取值                                                                                                                                                                                                                                                                                          |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 间距         | `p/px/py/pt/pr/pb/pl-n`、`m/mx/my/mt/mr/mb/ml-n`；`gap-n`、`gap-x-n`、`gap-y-n`。x/y 为物理水平/垂直轴，t/r/b/l 为物理上/右/下/左                                                                                                                                                                     |
| 布局         | `flex-row`、`flex-col` 及各自的 `-reverse`；`flex-wrap`、`flex-wrap-reverse`、`grid`；`flex-1` 为 grow=1、shrink=1、basis=0；`grow`、`shrink` 为 1，`grow-n`、`shrink-n` 或 `[n]` 为非负原生倍率；`basis-n`                                                                                           |
| 对齐         | `items-start/center/end/stretch`；`justify-start/center/end/between/around/evenly`；`content-start/center/end/stretch/between/around/evenly`；`self-`、`justify-items-`、`justify-self-` 后接 `start/center/end/stretch`。`justify-*` 对应显式 `justify`                                              |
| 网格         | `grid-cols-n`、`grid-rows-n`、`col-span-n`、`row-span-n`、`col-start-n`、`row-start-n`；`col-span-full`、`row-span-full` 为原生全跨度                                                                                                                                                                 |
| 尺寸         | `w-n`、`h-n`、`size-n`（两轴）、`min-w-n`、`min-h-n`、`max-w-n`、`max-h-n`                                                                                                                                                                                                                            |
| 定位         | `absolute`、`relative`；`left-n`、`top-n`、`right-n`、`bottom-n`；`inset-n`、`inset-x-n`、`inset-y-n`                                                                                                                                                                                                 |
| 字号         | `text-xs/sm/base/lg/xl/2xl/3xl/4xl/5xl/6xl/7xl/8xl/9xl` = 12/14/16/18/20/24/30/36/48/60/72/96/128 DIP；`text-[npx]`                                                                                                                                                                                   |
| 字体与字重   | `font-sans/mono/serif` = system-ui/monospace/serif；`font-thin/extralight/light/normal/medium/semibold/bold/extrabold/black` = 100/200/300/400/500/600/700/800/900；`font-[n]` 为 100..900 整数；`italic`                                                                                             |
| 文字布局     | `text-left/center/right/start/end`，left/right 对应原生 start/end；`whitespace-nowrap`、`whitespace-normal`；`truncate` 为单行，`line-clamp-n` 为最大行数；`tracking-[npx]` 为字间距                                                                                                                  |
| 行高         | `leading-none/tight/snug/normal/relaxed/loose` = 1/1.25/1.375/1.5/1.625/2 倍；`leading-[n]` 为非负原生倍率；`leading-n` 为 n×4 DIP 固定行高；`leading-[npx]` 为固定 DIP 行高                                                                                                                          |
| 字形与装饰   | `tabular-nums`、`proportional-nums`、`ordinal`、`slashed-zero` 分别选择 tnum/pnum/ordn/zero；后写的整组替换前者。`underline`、`decoration-wavy`、`line-through`；`decoration-n` 或 `decoration-[npx]` 为线厚，裸 n 直接为 DIP；`decoration-颜色`、`selection-颜色`                                    |
| 背景与文字色 | `bg-颜色`、`text-颜色`                                                                                                                                                                                                                                                                                |
| 边框与圆角   | `border` = 1 DIP；`border-n` 或 `border-[npx]`，裸 n 直接为 DIP；`border-solid/dashed`、`border-颜色`。`rounded` = 4 DIP；`rounded-none/sm/md/lg/xl/2xl/3xl/full` = 0/2/6/8/12/16/24/9999 DIP；`rounded-[npx]`                                                                                        |
| 裁剪与外观   | `overflow-hidden`、`overflow-x-hidden`、`overflow-y-hidden`；`invisible`；`opacity-n` 为 0..100（可为小数）除以 100；`blur-n` 或 `blur-[npx]`，裸 n 为原生 sigma DIP                                                                                                                                  |
| 图像         | `object-contain/cover/fill/none/scale-down`；`aspect-square/video` = 1 与 16/9                                                                                                                                                                                                                        |
| 变换         | `translate-x-n`、`translate-y-n` 为 n×4 DIP，或 `[npx]`；`scale-n`、`scale-x-n`、`scale-y-n` 为 n/100，或 `[n]` 直接倍率；`rotate-n`、`rotate-[ndeg]`、`rotate-[n]` 为角度；`skew-x-n`、`skew-y-n` 或 `[n]` 为角度；`origin-center/top/bottom/left/right/top-left/top-right/bottom-left/bottom-right` |
| 阴影         | `shadow`、`shadow-sm/md/lg/xl/2xl`；`inset-shadow` 及同名等级。每个是单个原生阴影，参数见下表                                                                                                                                                                                                         |
| 鼠标         | `cursor-` 后接 `auto/default/pointer/text/move/grab/grabbing/not-allowed/crosshair/nwse-resize/nesw-resize/n-resize/e-resize/s-resize/w-resize/vertical-text/ew-resize/ns-resize/col-resize/row-resize/copy/alias/context-menu/none`；auto 等于 default                                               |

阴影参数顺序为 x、y、blur、spread（DIP）、color；内外阴影使用相同刻度，不等同于 CSS 多层阴影。

| 等级 | 参数                        |
| ---- | --------------------------- |
| 默认 | `0, 1, 3, 0, #00000033`     |
| sm   | `0, 1, 2, 0, #0000000d`     |
| md   | `0, 4, 6, -1, #0000001a`    |
| lg   | `0, 10, 15, -3, #0000001a`  |
| xl   | `0, 20, 25, -5, #0000001a`  |
| 2xl  | `0, 25, 50, -12, #00000040` |

#### 状态、覆盖与错误

- 支持单层 `hover:`、`active:` / `pressed:`、`focus:`、`focus-visible:`、`focus-within:`、`disabled:`、`highlighted:`，分别对应 `hover`、`pressed`、`focused`、`focusVisible`、`focusWithin`、`disabled`、`highlighted` 状态。状态仍遵循原生优先级，pressed 高于 hover。
- `tw` **只允许在节点 props**；`hover: { tw: "p-2" }` 等状态对象（包括嵌套状态对象）非法，应写为节点上的 `tw: "hover:p-2"`。普通业务数据里的同名 `tw` 字段不作为样式解析。
- 工具类从左到右，同一目标后写覆盖前写：`p-2 px-4` 为上/下 8、左/右 16 DIP。仅修改声明的边，保留其他边的组件默认值；`p-2 hover:px-4 active:pt-3` 同时 hover+pressed 时为上/右/下/左 12/16/8/16 DIP。
- 显式 props 覆盖对应工具类，显式状态对象覆盖该状态的工具类，数值 0 同样有效。上例 `paddingX: 0` 只覆盖左右边，`hover.background` 覆盖 hover 的背景色；显式 `padding: 0` 覆盖全部四边。显式属性之间仍按原生命名顺序应用（例如 `paddingX` 晚于 `paddingLeft`）；复合属性按对应字段覆盖，如 `border: [2, "danger"]` 覆盖工具类的边框宽度与颜色。
- 显式 logical spacing（`paddingStart/End`、`marginStart/End`）与物理边混用时，先应用工具类，再按既有原生属性名称顺序调用该组显式属性；LTR/RTL 由原生方向决定。例如 `paddingStart: 20, paddingX: 4` 最终遵循原生 `paddingStart` → `paddingX` 顺序。
- 同一基础层或同一状态层中，`truncate` / 为真的 `singleLine` 与 `line-clamp-*` / `maxLines` 相互替代，后写生效；后写 clamp 不保留 truncate 的最小宽度行为。显式属性按原生顺序先 `maxLines` 后 `singleLine`，`singleLine: false` 只跳过调用。跨基础层与状态层仍为原生 overlay：状态 clamp 可以覆盖基础层行数，但无法撤销基础层 SingleLine 的最小宽度标志。
- 零参数快捷属性的 `false` 表示不调用：`w-full` 配合 `fillWidth: false` 仍为 100% 宽；用 `width: 0` 等具体值覆盖。`center: false`、`bold: false` 同理。`relative` 与 `whitespace-normal` 可重置同次 tw 的标志，但无法撤销组件本身启用的标志。
- `relative`、`whitespace-normal`、所有 transform / origin / shadow / inset-shadow 工具类仅支持基础样式，不能加状态前缀。变换按原生方法顺序组合（rotate → scale → skew → translate），不按工具类书写顺序；同名双轴变换保留另一轴，显式变换/阴影属性覆盖对应工具类。
- 状态名若同时用于非对象组件属性，不能再声明同名状态工具类，例如 Chart 的 `hover: true` 与 `hover:bg-surface` 冲突。
- 未知工具类、错误类型、无效数值与不支持的状态组合会使本次 render 失败，错误包含节点类型、具体 token 或状态路径；不静默忽略。空字符串合法。
- 不支持响应式断点、`dark:`、`group/peer`、组合变体、important、CSS 变量、插件、`calc()`、`flex` / `flex-auto/initial/none`、auto 尺寸、logical `ps/pe/ms/me/start/end`、fixed/sticky/z-index、单边 border / 单角 rounded、`visible/hidden`、`overflow-auto/scroll/visible`、反向文字标志、渐变或动画工具类。复杂材质、渐变、动画、事件与业务数据继续用显式 props。

### 节点引用

`activeDescendant: "result"` 指定无障碍活动项，键盘焦点仍留在源元素上。`scrollWith: "body"` 让源元素跟随目标的横向滚动；用于固定表头时，应为源元素设置 `clip: true`。

这两个属性引用同一面板内显式声明的节点 `key`。目标可写在源节点之前或之后，但必须在同一次构建中实际生成，并且 key 在面板内唯一。不同父节点下可重复使用局部 key，但这样的 key 不能作为明确的引用目标；同级节点的 key 不可重复。缺失或有歧义的目标以及自身引用会被忽略并记录诊断。引用不能放入 `hover` 等状态样式。

```javascript
{
  type: "Column",
  children: [
    { type: "TextInput", props: { activeDescendant: "result", value: query } },
    { type: "Text", key: "result", props: { text: "建议结果" } }
  ]
}
```

### 驼峰到帕斯卡命名反射

在 JavaScript 对象中使用 `camelCase` 属性名，底层自动映射为原生组件的 PascalCase 方法：

- `fontSize: 14` → `.FontSize(14)`
- `letterSpacing: 1.5` → `.LetterSpacing(1.5)`（字间距，单位 DIP）
- `gap: 12` → `.Gap(12)`
- `padding: 16` → `.Padding(16)`
- `width: 280` → `.Width(280)`
- `bold: true` → `.Bold(true)`

### 尺寸与百分比

长度数值使用 DIP（原生逻辑像素），与 `tw` 任意长度中的 `px` 相同。百分比字符串使用对应的百分比布局：

```javascript
props: {
  width: "100%",  // 调用 WidthPercent(100)
  height: 200     // 调用 Height(200)
}
```

### 面板打开方式

看板可在设置中为菜单栏、三侧 Sidebar 与快捷键全部五个触发来源各自配置启用开关与打开方式（Flyout 或 Dialog，例如菜单栏打开 Flyout，快捷键与右侧 Sidebar 打开 Dialog）。XML 根属性 `<bar open-mode="flyout">` 或 `<bar open-mode="dialog">` 仅提供作者默认值。两种方式显示同一棵 `panel` 树；宿主 Dialog 模式独立于树内的 Modal、Popover 等组件。

Dialog 的遮罩覆盖当前显示器完整 Bounds，面板内容居中，不进入系统全屏 Space。遮罩采用约 12% 黑色的高透明遮罩（系统高对比度模式下为约 40% 黑色）；内容卡片带圆角与阴影。界面无“X”关闭按钮。`panel` 根节点的 `width` 和 `height` 以设备独立像素（DIP）定义内容请求尺寸，默认 320×480。左右边距各至少为屏幕宽度的 10%，上下边距各至少为屏幕高度的 10%，内容区域宽高最多为屏幕宽高的 80%，超大内容内部滚动；内容尺寸动态变化不会缩小遮罩窗口。点击内容外或按 Esc 可关闭，内容内点击不关闭；有内部浮层时 Esc 优先关闭该浮层。注意：Windows 与 Linux 桌面 GUI 实机验收仍待完成。

### 面板材质

通过 XML 根属性 `<bar material="glass">`（缺省）或 `<bar material="solid">` 选择宿主面板内容的材质，包括空白与加载、错误状态区域。Dialog 模式下，材质仅作用于居中的内容区域，遮罩保持独立外观。solid 使用不透明主题 Surface 和常规主题控件。glass 保留系统材质或 Glass 回退，控件使用透明派生主题。macOS 托盘的两种材质均保留原生 NSPopover 气泡箭头、圆角和阴影；材质由 Host 外壳管理，Worker 仅负责内部内容。macOS 14 及以上的 solid 外壳（含箭头）使用不透明主题 Surface；macOS 12/13 的 solid 正文不透明，箭头保留系统材质。其他平台保持既有浮窗样式。

保存源码并成功重载后，新材质会在下次打开面板时生效；重载失败保留当前运行定义和材质。Worker 节点更新不改变宿主材质，预览跟随当前运行定义。根节点、子节点和状态样式的局部 `material` 均按插件绘制，不控制宿主；所有节点 props 均不接受 `"solid"`。

### 玻璃材质

在根组件、子组件或状态样式中，通过 `material: "glass"` 启用 MyGO 官方 [Glass 插件](https://mygo.egoist.dev/docs/plugins/glass)，也可以显式配置：

```javascript
props: {
  material: { type: "glass", style: "regular", tint: "surface", interactive: false },
  radius: 12
}
```

`style` 支持 `"regular"`（默认）或 `"clear"`；`tint` 接受主题 token 或十六进制颜色，`interactive` 默认为 `false`。同一组件的基础 `material` 会覆盖 `background`，也可用于 `hover` 等状态样式。

Glass 采样同一窗口内已经绘制的内容，不能模糊桌面。看板面板优先保留系统 Vibrancy，不支持系统材质时使用带主题 tint 的 Glass 回退。自定义看板的根节点如果设置不透明背景，会遮住系统材质。

### 颜色系统

颜色属性支持以下取值：

- 十六进制颜色代码：`#RGB`、`#RRGGBB`、`#RRGGBBAA`（如 `"#3b82f6"`）。
- 语义化主题 Token：
  - `background`, `surface`, `surfaceHover`, `surfacePressed`
  - `border`
  - `text`, `textMuted`
  - `accent`, `accentHover`, `accentPressed`, `accentText`
  - `danger`, `warning`, `success`
  - `selection`, `focus`
  - `inverse`, `inverseText`

---

## 使用 `htm` 简化 UI 树编写

如果你希望用类似 JSX 的语法编写界面，可在 Worker 中通过 ESM 导入 [htm](https://github.com/developit/htm)：

```bar
<permissions>
  <network origin="https://esm.sh" />
</permissions>

<script>
let html;

export default {
  async scheduled() {
    if (!html) {
      const htm = await import("https://esm.sh/htm");
      const h = (type, props, ...children) => ({
        type,
        props: props || {},
        children: children.flat().filter(Boolean)
      });
      html = htm.default.bind(h);
    }
  },

  render(env) {
    return {
      tray: { title: "监控指标" },
      panel: html`
        <Column gap=${12} padding=${16} width=${300}>
          <Row justify="space-between" alignItems="center">
            <Text text="服务运行状态" bold=${true} fontSize=${14} />
            <Badge text="正常" color="success" />
          </Row>
          <Button
            text="立即刷新"
            onClick=${() => $metrix.invalidate()}
          />
        </Column>
      `
    };
  }
};
</script>
```

## 文档与导航

`RichText` 通过 `spans` 显示静态富文本；编辑文档使用 `RichTextEditor`。以下组件直接使用 MyGO 原生渲染器，不执行网页内容。

| 组件                          | 属性与回调                                                                                                                                                                                                                                                                                                                                                                          |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Markdown`、`MarkdownStream`  | `content`、`wrapCode`、`shareSelection`、`reveal:{unit,catchUp,fade}`、`onLink(url)`                                                                                                                                                                                                                                                                                                |
| `RichTextEditor`              | `value`、`format:"plain"\|"markdown"\|"html"`（默认 markdown）、`readOnly`（默认 true）、`noMarkdown`、`placeholder`、`label`、`onChange(serializedText)`、`onLink(url)`、`onLinkCommand()`、`onSelectionChange({anchor,caret})`                                                                                                                                                    |
| `DiffView`                    | `original`、`modified`、`language`、`editable`、`layout:"split"\|"unified"`、`wrap`、`fitContent`、`hunkActions`、`contextLines`、`expandUnchanged`、`lineNumbers`、`tabSize`、`expansionLines`、`indicators`、`noBackground`、`inline`、`annotations`；`onChange(modified)`、`onOriginalChange(original)`、`onLineSelection({side,start,end})`、`onGutterButton({side,start,end})` |
| `Router`                      | `path`（默认 `/`）、`routeTransition:"auto"\|"fade"\|"none"`、`routeAnimation`、`onChange(location)`；子节点为含 `path`、`title` 的 `Route`                                                                                                                                                                                                                                         |
| `Route`                       | 使用第一个匹配项的 children；路径必须为本地路径，不得带 scheme、host 或以 `//` 开头                                                                                                                                                                                                                                                                                                 |
| `BackButton`、`ForwardButton` | 操作所在 Router 的历史；在 Router 外禁用                                                                                                                                                                                                                                                                                                                                            |

Markdown reveal 的 unit 为 `word`、`character`、`sentence`、`line`、`paragraph` 或 `atOnce`，catchUp/fade 单位毫秒。图片遵循声明权限。每张图片最多 8 MiB、单边 4096 像素与总计 1600 万像素。无 loader 时显示 alt。编辑器受控回显保留 caret 与 undo，Router 回显保留历史。Diff annotations 为 `{key?,side:"original"|"modified",line,content:Node}`，行号从零起；支持 `contextLines:-1`。inline 接受 `wordAlt`（默认）、`word`、`wordLine`、`char`、`none`，indicators 接受 `bars`、`classic`、`none`。

### Editor：身份、增量与动作

同一 surface 和稳定 `key` 保留原生编辑状态。受控 `value` echo 保留选区、滚动和 undo；真正外部替换会换 Buffer，不回声 `onChange/onEdit`。移除重挂、切换 surface 或 Worker 会产生新的 `instanceId`。文本统一 LF；Editor 的选区、edit、completion、hover、diagnostics、inlays 偏移都是 **UTF-8 字节边界**，不是 JS 字符串下标。`value/diffBase` 最多 8 MiB，有效 UTF-8；Worker 导出时 JS 未配对 surrogate 规范化为 U+FFFD，合法 U+FFFD 允许，panel 层仅拒非法 UTF-8。

`onState({instanceId,version,stateRevision,byteLength,lineCount,selections:[{anchor,head}],cursor:{line,column},readOnly})` 首次及状态变化时握手。version 标识文档；stateRevision 还随选区、焦点与输入上下文变化。`onEdit({instanceId,baseVersion,version,edits:[{start,end,text}]})` 报告相对旧 snapshot 的真实增量；未订阅 `onChange` 可避免生成全文，增量比较最坏仍扫描全文，全替换也可能输出全文。

`action` 共有 `{instanceId,id,version}`，id 为递增正 safe integer，每实例首次尝试即消费，失败不重放；错误身份不消费当前实例 id。支持 `{kind:"edit",edits}`、`{kind:"select",stateRevision,selections}`、`{kind:"command",stateRevision,command}`；后两者必须匹配当前 stateRevision，command 使用原生 CommandNamed 的 lowerCamel 名称。编辑必须按旧 snapshot 排序且不重叠，最多 256 项、插入共 1 MiB、结果 8 MiB，整批可单步 undo；选区为 1–256 项。只读允许导航、选择、查找、复制、折叠和显示的受支持命令，拒绝编辑及 FindReplace。`onAction({instanceId,id,version,stateRevision,ok,error?})` 报告 `stale-instance/stale-version/stale-state/read-only/invalid-range`。非法结构由 ValidateView 定位到 props 路径。

`onCompletionRequest({instanceId,requestId,version,offset,prefix,explicit})` / `onHoverRequest({instanceId,requestId,version,offset})` 可异步回传 `completions:{instanceId,requestId,version,start,items}` / `hover:{instanceId,requestId,version,start,end,text}`。completion item 为 `{label,detail?,kind?,insert?,documentation?,filterText?}`；kind 支持 text/keyword/function/method/variable/field/type/module/constant/snippet。每类仅最新 pending 请求可应用一次，且文档、输入 generation、完整选区、焦点仍须匹配。输入、Escape、指针移动、失焦或移除 handler 取消；移除 handler 也关闭浮层，重复 render 不重开。

`diagnostics:{instanceId,version,items:[{start,end,severity,message,source?}]}`（severity 为 error/warning/info/hint）与 `inlays:{instanceId,version,items:[{offset,text}]}` 只接受当前身份和版本，移除 prop 或外部全文替换清标记；`diffBase` 为独立差异基线，移除即清除。diagnostics/inlays 各最多 1024 项，completion 最多 512 项，LSP 各文本字段最多 64 KiB，仍受 Worker 树预算限制。

以下为完整 `<script>` 模块内容；按钮在 `onState` 握手后请求一次可撤销的字节插入。普通 render 不会恢复初始文本；需要保存全文时再订阅 `onChange`。

```js
let state,
  action,
  nextId = 0;
export default {
  render() {
    return {
      panel: {
        type: "Column",
        children: [
          {
            type: "Editor",
            key: "code",
            props: {
              value: "hello\n",
              readOnly: false,
              ...(action ? { action } : {}),
              onState(s) {
                state = s;
              },
              onAction(result) {
                action = undefined;
              },
            },
          },
          {
            type: "Button",
            props: {
              text: "Insert",
              keepFocus: true,
              onClick() {
                if (!state) return;
                action = {
                  instanceId: state.instanceId,
                  version: state.version,
                  id: ++nextId,
                  kind: "edit",
                  edits: [{ start: 0, end: 0, text: "// " }],
                };
              },
            },
          },
        ],
      },
    };
  },
};
```

### RichTextEditor 与 TextBuffer

RichTextEditor 的 `onState({instanceId,version,stateRevision,length,anchor,caret,readOnly,formatting})` 使用 **rune** 长度与选区。formatting 提供 bold/italic/underline/strikethrough/code 的 `off|on|mixed`、link、heading（0–6）、list（none/bullets/numbers/tasks）、quote、codeBlock、paragraphsMixed、canUndo/canRedo。选区或 typing 格式变化也推进 stateRevision。先握手，再传 `action:{instanceId,id,version,stateRevision,...}`，支持 `kind:"format"` 加 command（bold/italic/underline/strikethrough/code/quote/codeBlock/undo/redo）、`kind:"heading",level`、`kind:"list",list`、`kind:"link",url`、`kind:"select",anchor,caret`。id 为一次性正 safe integer，失败也消费；`onAction` 使用 EditorActionResult，只读仅允许 select。工具栏普通 Button 可用 `keepFocus:true` 保持选区；例如依据最新 state 构造 `{instanceId:s.instanceId,version:s.version,stateRevision:s.stateRevision,id:++id,kind:"format",command:"bold"}`，不能复用旧状态。

受控 echo 保留文档、选区和 undo，外部内容或 format 变化替换文档。未订阅 onChange 时普通帧不序列化；echo 比较可序列化。要保存 underline 使用 HTML，Markdown 不保证保留 underline；原生 HTML 不执行网页。value 上限 8 MiB 有效 UTF-8，link 上限 64 KiB；非法选区拒绝，不 clamp。JS surrogate 的 U+FFFD 规范化规则同 Editor。

四种 TextBuffer 控件支持 `onState({instanceId,version,length,byteLength,lineCount})`、`onEdit({instanceId,baseVersion,version,edits})`、`action:{instanceId,id,version,edits}` 与 `onAction`；所有编辑坐标及 length 为 **rune**。握手与一次性 id 同上，不要求 stateRevision。增量为旧 snapshot 的单段替换，未订阅 onChange/onSubmit 可避免全文字符串；最坏比较扫描全文。整批有序不重叠 edits 最多 256 项、插入共 1 MiB、结果 8 MiB，逆序应用后报告一个 onEdit；只读拒绝。程序替换清 undo/IME，需要保留历史时使用 Editor。可编辑十万行文本，但不保证每次变更恒定耗时或帧率；外部 value 替换仅回报新 state。

## Base 控件与命名 Part

`ButtonBase`、`CheckboxBase`、`SwitchBase`、`ToggleBase`、`SliderBase`、`RadioBase`、`TextInputBase`、`TextAreaBase` 沿用对应控件的值和事件契约。ButtonBase 支持 children 与 `href`。`TextInputBuffer`、`TextAreaBuffer`、`TextInputBufferBase`、`TextAreaBufferBase` 通过 `value`、`onChange`、`onSubmit` 收发字符串，并在回显时保留原生 TextBuffer。

仅在匹配的 Base 内使用 `{type:"Part",props:{name:"..."},children:[...]}`：

| Base                | Part                                  |
| ------------------- | ------------------------------------- |
| `CollapsibleBase`   | `trigger`, `panel`                    |
| `ComboboxBase`      | `input`, `popup`, `item` (`value`)    |
| `SelectBase`        | `trigger`, `popup`, `item` (`value`)  |
| `TabsBase`          | `list`, `tab` (`index`)               |
| `SegmentedBase`     | `track`, `segment` (`index`)          |
| `DialogBase`        | `backdrop`, `panel`                   |
| `PopoverBase`       | `panel`                               |
| `ToastBase`         | `root`, `actionButton`, `closeButton` |
| `ToastViewportBase` | `viewport`, `toast`                   |

Collapsible/Dialog/Popover 使用 `open`、`onOpenChange(boolean)`；ComboboxBase 还支持字符串 `value`/`onChange`。SelectBase 报告 `onOpenChange`，展开由 trigger 管理。TabsBase/SegmentedBase 使用整数 `value`/`onChange(index)`。ToastBase 支持 `title`、`description`、`action`、`type`、`timeout`（秒）、`onAction`、`onClose`；关闭或超时后消失，移除再加入节点可重新弹出。ToastViewportBase 每个活跃 toast 仅渲染一次，其 `toast` Part 内可使用 ToastBase 的 Part。

## 结构化表格

Table 的 `items[].cells[]` 接受字符串或显式 Node，保留事件回调。`columnLayout:{order:string[],widths:Record<string,number>}` 与 `onColumnLayoutChange(layout)` 保存列顺序和宽度。列支持 `fixed`、`minWidth`、`maxWidth`、`width`、`align`、`sortable`。`selection` 为行 key 数组，`onSelectionChange(keys)` 按可见行顺序回传已选 key。`sortBy:[{column,descending?}]` 与 `onSortBy(orders)` 支持多列排序。`headerMenu` 接受标准菜单项，`onHeaderMenu(columnIndex)` 标识列头；既有 `sort`/`onSort` 保留。

## 材质与时间线

除 Glass 外，`material` 接受 `{type:"blur",radius,mask?}`（radius 为有限非负数，mask 为 LinearGradient）或 `{type:"scrollEdge",hard?,bottom?,background?}`；拒绝未知字段与类型。

`timeline` 接受非空 `tracks:[{property,from,to,duration,ease?,at?}]`，property 为 `x`、`y`、`scale`、`scaleX`、`scaleY`、`rotate`、`opacity`、`fade`。duration 与绝对 at 偏移以毫秒计，省略 at 时顺序衔接。选项包含 `run`（非负整数）、`repeat`（≥ -1 的整数）、`alternate`、`paused`、`reverse`、`delay`、`repeatDelay`，以及互斥的 `seek`（非负毫秒）或 `progress`（0–1）。所有轨道参与总时长计算，按 playhead 合并通道，包括平移和缩放。

## 本地与系统拖放

`drag` 提供同窗口 JSON 快照（不含 handler，最多 64 KiB），与 `dragData:{text?,html?,markdown?}` 互斥。后者仅接受字符串，导出单个 Copy 条目，总量最多 64 KiB。本地 `onDrop` 收到 `{kind:"local",value}`。

`dropFormats` 仅允许 `text/plain`、`text/html`、`text/markdown`。系统 `onDrop` 在全部读取成功后收到 `{kind:"data",items:[{types:string[],data:{[mime]:string}}],operation:"copy"}`：最多 64 items、总计 64 KiB UTF-8 文本，失败不部分交付。`onDragOver` 在变化时回传 `{over:boolean,kind?:"local"|"data",types?:string[]}`，离开为 `{over:false}`；仅有悬停回调也能观察本地拖入/移出。系统悬停检查排序后的白名单交集，不调用数据 provider。

## Canvas：受限 2D 绘制与连续输入

`Canvas` 使用原生 Painter，坐标与目标尺寸为 DIP，绘制裁剪到视口。稳定 `key` 保持输入与时钟；动态列表、条件替换和重排中的 Canvas 必须使用树内唯一的稳定 key，无 key 时仅按 Children/props 结构位置识别。props：`commands`、`onFrame`、`paused`（默认 false）、`fps`（1..60，默认60）、`stepMs`（1..250ms，默认1000/60）、`maxSteps`（整数1..8，默认5），以及 width/height/autoFocus 等现有布局属性。

```js
let x = 0;
export default {
  render() {
    return {
      panel: {
        type: "Canvas",
        key: "scene",
        props: {
          width: 320,
          height: 180,
          autoFocus: true,
          commands: [{ op: "rect", rect: { x, y: 20, width: 24, height: 24 }, color: "accent" }],
          onFrame(frame) {
            x = (x + frame.steps * 2) % 296;
          },
        },
      },
    };
  },
};
```

指令按 op 区分：rect（rect/color/radius）、line（x/y/x2/y2/width/color）、circle（x/y/radius/color）、path（path/color/width，width=0 填充）、text（x/y/text/size/color）、image（src/rect/可选srcRect/flipX/flipY）、clip（rect/commands）、transform（可选translate:[x,y]、scale:[x,y]、rotate弧度及commands）。rect统一为 `{x,y,width,height}`；path包含 `['M',x,y]`、`['L',x,y]`、`['Q',cx,cy,x,y]`、`['C',cx1,cy1,cx2,cy2,x,y]`、`['Z']`。transform依次组合平移、缩放、旋转。srcRect使用位图像素，目标rect使用DIP；SVG不支持区域裁剪和翻转。颜色接受十六进制和主题名称；图片复用Image资源缓存及权限加载，未加载时暂不显示。

每树上限8192指令、16层嵌套、65536路径点（含曲线控制点）。数值须有限且绝对值不超过1000000，scale每轴绝对值不超过4；尺寸/半径/线宽非负，字号为正。结构诊断与渲染使用同一decoder拒绝非法数据。精确类型由 `/v0/types` 导出 CanvasCommand、CanvasFrame、CanvasInput。

`onFrame({time,delta,stepMs,steps,alpha,droppedMs,width,height,input})` 提供单调毫秒时间、最大250ms的delta、固定步长次数和[0,1)插值比例；超出maxSteps的整步计入droppedMs。handler完成后宿主自动render，每Canvas最多一个invoke+render在途，且等待该版本或更新视图提交后再发帧，慢脚本产生背压。失败清held并设reset；执行前按目标从最新已提交视图解析 onFrame，原始输入参数保持不变；旧 Worker 实例、已移除或歧义目标拒绝派发并等待新视图。invoke 成功后即使 render 失败也不重放该帧输入。无完成型dispatcher的headless/静态预览仅绘图，不自动调用onFrame。

input包含keys:string[]、buttons:number[]、pointer:`{x,y}`、events、reset。聚焦后的keydown/keyup包含key、physicalKey、repeat、mods:`{alt,ctrl,meta,shift}`；未知逻辑键回退物理键名。pointerdown/move/up包含局部x/y、button、mods；scroll另含dx/dy/precise。原生pointer-down捕获窗外移动/释放；Tab与系统快捷键保持正常路由。事件最多256条，相邻move合并；溢出优先丢旧move，否则清held并reset。事件仅随帧交付。失焦、出视口、paused、隐藏和销毁清输入与累计时钟，失焦后暂停派帧直到重新聚焦，初始未设置 autoFocus 仍可启动动画；恢复首帧delta=0，已有在途帧仍须完成后才能再派帧；无onFrame时不默认获取键盘焦点。

### Canvas 多 pass Shader

`shaders:Record<string,string>` 声明 Kage 源码；`passes:[{id,shader,width?,height?,scale?,uniforms?,images?}]` 顺序生成中间纹理；最终 `commands` 使用 `{op:"shader",shader,rect,uniforms?,images?}`。shader 引用本 Canvas 的声明；pass id 唯一，仅能引用前序 pass，最终 command 可引用任意 pass，形成前序 DAG。images 最多四项，严格为 `{src:string}` 或 `{pass:string}`；外部纹理仅 bitmap，复用图片权限、加载与预览资源。

以下是完整 `<script>` 模块内容，两级 pass 后绘制到 Canvas；不需要外部资源权限。

```js
const shaders = {
  paint: `package main
var Tint vec4
func Fragment(dst vec4, pos vec2) vec4 { return Tint }`,
  copy: `package main
func Fragment(dst vec4, pos vec2) vec4 {
  return image0Sample(pos / rectSize() * image0Size())
}`,
};
export default {
  render() {
    return {
      panel: {
        type: "Canvas",
        key: "shader-scene",
        props: {
          width: 256,
          height: 128,
          shaders,
          passes: [
            {
              id: "base",
              shader: "paint",
              width: 128,
              height: 64,
              uniforms: { Tint: [0.2, 0.4, 0.8, 1] },
            },
            { id: "filtered", shader: "copy", scale: 0.5, images: [{ pass: "base" }] },
          ],
          commands: [
            {
              op: "shader",
              shader: "copy",
              rect: { x: 0, y: 0, width: 256, height: 128 },
              images: [{ pass: "filtered" }],
            },
          ],
        },
      },
    };
  },
};
```

显式 width/height 必须成对为 1–2048 的整数**物理像素**，且不能带 scale；否则目标为 `round(viewportDIP * pixelScale * scale)`，scale 默认 1、范围 `(0,2]`，viewport 使用祖先变换前布局。pass 坐标为物理像素，最终 draw.rect 为 Canvas 局部 **DIP**。零尺寸或超限拒绝整树 Shader。可逐帧调整分辨率，仍受预算限制。

uniforms 为 `Record<string,number|boolean|number[]>`，标量为单值，向量/矩阵/数组为完整展平数值数组；必须匹配声明名称、类型和形状，省略为零。数值须有限且可表示为 float32；int/uint 须为各自 32 位整数。仅允许 Fragment，禁止 ColorEffect/DistortionEffect/LayerEffect/backdrop。每源码最多 64 KiB、16000 AST 节点、64 层表达式、64 函数；展开数组共最多 4096 scalar，单数组也最多 4096。禁止递归、range、动态 loop；只允许常量起止、常量步长的规范计数循环（`<`/`<=` 递增或对应递减），每循环最多 1024 次，循环体不能改计数器。动态 count 可手动改为静态容量循环并用 `if i >= Count { break }`，同时在脚本限制 Count。最坏路径展开用户调用并按迭代计费，每源码函数静态 cost 合计最多 500000；这是复杂度门槛，不保证 GPU 耗时或编译器硬内存隔离。

整树（含 props 内嵌节点、重复源码声明）最多 8 shader、256 KiB 源码、8 pass、16 shader draw（含 pass）、声明和传入 uniform 各 4096 scalar。布局后分配前检查目标单边 2048、目标总计 8 × 1024² 像素、目标加最终 draw 覆盖 16 × 1024² 像素。覆盖保守计算 Canvas、祖先完整 world affine 及嵌套 command transform 的包围矩形，每个最终 draw 至少计 Canvas 变换后包围面积，动画变换也计入。Shader 仅支持有限 affine 2D，拒绝透视等非 affine；布局超限跳过整树 Shader 并显示提示，普通 2D 继续。

需要 GPU 加速；无 GPU 仍校验源码/类型，显示提示并保留普通 2D，不执行 pass 或 Shader.Draw，也无昂贵 CPU fallback。`onFrame` 的 `accelerated:boolean` 可用于选择内容，headless 预览不保证 GPU 图像。

跨次渲染保持 Canvas key 与 pass id 稳定。制作动画时，优先更新 uniform，保持 Shader 源码不变。

## Loading 与行列选择

`Loading` 提供原生加载动画，`variant` 默认 `arc`，可选：arc, atom, blocks, bouncingDots, cascade, circularDots, classic, classicV2, clock, comet, compass, dual, eclipse, flip, gather, leap, linearDots, loading, morph, orbit, pulse, radar, ring, ripple, slide, snake, swirl, trace, wave。`size` 是 DIP 高度（默认 20），`durationMs` 是一个动画周期的毫秒数；二者须为有限非负数，0 使用默认值。支持主题 `color`、`paused`、无障碍 `label` 与通用 Element 属性。`easing` 为 `linear|easeInOut|stacked`，`cap` 为 `round|flat`，`sweep` 为 `diagonal|rows|columns`，`rippleDirection` 为 `out|in`，`waveOrigin` 为 `center|bottom`。`direction` 仍控制布局方向；系统减少动态效果与隐藏窗口暂停由原生运行时处理。

List 与 Table 支持 `selection` 行 key 数组、`onSelectionChange(keys)`、`dragSelect` 和 `menuKeepsSelection`。List children 使用节点 key（缺省为索引），items 使用 item key；回调按当前行顺序返回。`dragSelect` 需要 `selection`；List 与 `onReorder` 同时配置时会诊断并保留 reorder、禁用拖选。

Table 另支持 `cellSelect`（需要 `selection`）、`cells:{cursor?:string,anchor?:string,all?:boolean}` 与 `onCellsChange(cells,columns)`；columns 是当前显示顺序的列 ID 数组。受控值只在 prop 改变时重新初始化。`columns[].rowHeader` 标识行表头列。`cellMenu` 使用标准菜单项，`onCellMenu(row,column)` 提供源 items/columns 索引；先展示当前已提交菜单，回调可更新后续状态。Outline/OutlineTable 不开放此单元格多选协议。
