# 套件 API —— `dsh/canvas` 画布文件**只能** import 这一个模块说明符: ~~~tsx import { Stack, Row, Grid, Divider, CollapsibleSection, H1, H2, Text, Code, Card, CardHeader, CardBody, Callout, Stat, Table, BarChart, TodoList, Progress, KeyValue, Timeline, Button, Pill, useState, useEffect, useMemo, useCallback, useRef, useCanvasState, useCanvasOverlay, useCanvasAction, useHostTheme, useCanvasResource, } from "dsh/canvas"; ~~~ 不要 `import React from "react"`,也不要任何其它模块——它们会被 `E_PARSE_IMPORT` 拒绝。 ## 设计约定(先读这五条) 1. **只认语义 `tone`,不认颜色**:不要写颜色值、不要写 class、不要写像素间距。颜色与间距由宿主解析,主题切换自动跟随。 2. **受控组件**:`active` / `open` 由调用方给。唯一例外是 `CollapsibleSection` 的 `defaultOpen`。 3. **`tone` 与 `size` 是两条独立的轴**:`tone` 管颜色,`size` 管字号档位。 4. **只加不减**:套件不会删导出或改签名。若某天必须改,会先给 `W_DEPRECATED` 渲染期信号(**不是编译诊断**)并保留至少两个 minor 版本。 5. **字号只从 harness 排版刻度取**:不要在画布里写 `fontSize` / 像素值 / `calc(base * 1.6)`。要新的档位就先改套件,并在「排版标准」里登记。 ## 通用类型 ~~~ts type Tone = "neutral" | "info" | "success" | "warning" | "danger"; type Status = "pending" | "in_progress" | "blocked" | "completed" | "cancelled"; ~~~ | tone | 语义 | 典型用途 | |---|---|---| | `"neutral"` | 中性 / 未知 | "无参考基线"、归档 | | `"info"` | 信息 | "ours" 系列、一般提示 | | `"success"` | 通过 | 门禁绿、已完成 | | `"warning"` | 需注意 | 偏差、进行中、超阈值 | | `"danger"` | 失败 / 回归 | 门禁红、编译错误 | `Text` 的 `tone` 额外接受 `"primary" | "secondary" | "tertiary"` 三个便利别名(映射到中性色的三个弱化档)。 --- ## 排版标准 画布活在右栏,右栏的正文基准是 **`--dsh-content-font-size-secondary`(13px)**,不是聊天区的 14px。所有字号都取 harness 排版 token 的**长写形式**(`--dsw-font-*-font-size` / `-line-height`):简写 token(如 `--dsw-font-xs-13` = `13px/20px family`)作为 `font-size` 是无效值,会静默退回继承字号——这正是「有大有小」的成因。规则由套件内部执行,画布只选档: | 用法 | 组件 / prop | token | 实际 | |---|---|---|---| | 正文(默认) | `Text` | `--dsh-content-font-size-secondary` | 13 / 20 | | 小字 | `Text size="small"` | `--dsw-font-xxs-12` | 12 / 18 | | 注脚 | `Text size="caption"` | `--dsw-font-xxxs-11` | 11 / 14 | | 大字 | `Text size="large"` | `--dsw-font-s-14` | 14 / 22 | | 一级标题 | `H1` | `--dsw-font-l-20` | 20 / 28 · 500 | | 二级标题 | `H2` | `--dsw-font-base-strong-16` | 16 / 24 · 500 | | 指标数字 | `Stat` | `--dsw-font-l-20` | 20 / 28 · 600 | | 等宽 / 行内代码 | `Code` | `--dsw-font-markdown-code` + `--ds-font-family-code` | 12 / 19 | | 表格 | `Table` | 正文档 13 / 20,表头字重 500 | — | **`Code` 的字族是 `--ds-font-family-code`**。主题里**没有** `--dsw-font-mono`,裸 `var()` 是无效字族,会让代码块退回 UI 字体(已修的 bug,不要再写那个名字)。 不要在画布里写 `style={{ fontSize: ... }}` 或 `calc(...)` 覆盖字号:绕开刻度就会重新变成「有大有小」。 --- ## 布局 ### `Stack` 纵向 flex。画布根节点通常就是它。 | prop | 类型 | 默认 | 说明 | |---|---|---|---| | `gap` | `number` | `12` | 子元素间距(宿主间距单位) | | `align` | `"start" \| "center" \| "end" \| "stretch"` | — | 交叉轴对齐 | | `children` | `ReactNode` | — | — | ~~~tsx

Title

Subtitle
~~~ ### `Row` 横向 flex。 | prop | 类型 | 默认 | 说明 | |---|---|---|---| | `gap` | `number` | `8` | 间距 | | `wrap` | `boolean` | — | 允许换行(按钮条、Pill 条建议开) | | `align` | 同 `Stack` | — | 交叉轴对齐 | | `justify` | `"start" \| "center" \| "end" \| "space-between"` | — | 主轴对齐 | ~~~tsx Open 12 All 40 ~~~ ### `Grid` 等宽或自定义列网格。 | prop | 类型 | 默认 | 说明 | |---|---|---|---| | `columns` | `number \| string` | `2` | 数字 = 等宽列数;字符串直接作为 CSS `grid-template-columns`(支持 `"minmax(0, 1.1fr) minmax(0, 0.9fr)"`) | | `gap` | `number` | `16` | 间距 | | `align` | 同 `Stack` | — | 交叉轴对齐 | > **并排看内容量。** 卡片里只有「一个数字 + 一句标签」这种短内容——例如顶部的 Stat 概览——就并排:`columns="repeat(auto-fit, minmax(120px, 1fr))"` 会让窄栏自动折成 2 列。承载句子、字段列表或表格的内容(待办 / 详情 / 明细 / 时间线)一律纵向:右栏宽度可变、默认偏窄,把长内容分栏必然挤碎。 ~~~tsx {/* 概览指标:三列等宽 */} {/* 左右主从:列表 + 详情 */} ... ... ~~~ ### `Divider` 1px 分隔线,无 props。 ~~~tsx ~~~ ### `CollapsibleSection` 折叠区。用来把"历史 / 归档 / 说明"收起来,是保持画布小的主要手段。 | prop | 类型 | 默认 | 说明 | |---|---|---|---| | `title` | `string` | 必填 | 折叠区标题 | | `count` | `number` | — | 渲染成标题后的尾随徽标 | | `trailing` | `ReactNode` | — | 标题行右端(放说明文字或 Pill) | | `defaultOpen` | `boolean` | `false` | 非受控初值;这是套件里唯一带内部状态的 prop | | `children` | `ReactNode` | — | 折叠内容 | ~~~tsx 历史不进默认视图} > 见 specs 归档。 ~~~ --- ## 排版 ### `H1` / `H2` 两级标题,props 只有 `children`。 ~~~tsx

STEP to OBJ remaining gaps

Face count vs OCCT

~~~ ### `Text` 正文。 | prop | 类型 | 默认 | 说明 | |---|---|---|---| | `size` | `"small" \| "md" \| "large"` | `"md"` | 字号档 | | `weight` | `"normal" \| "semibold"` | `"normal"` | 字重档 | | `tone` | `Tone \| "primary" \| "secondary" \| "tertiary"` | — | 颜色 | | `style` | `CSSProperties` | — | 逃生口;只在确实需要时用 | | `children` | `ReactNode` | — | — | ~~~tsx 正文。 强调一行 来源与脚注 Hits: Shape ~~~ ### `Code` 等宽文本,用于文件路径、命令、标识符。props 只有 `children`。 ~~~tsx crates/occt-topo/src/brep_surface.rs:306 ~~~ --- ## 容器 ### `Card` / `CardHeader` / `CardBody` 卡片。`CardHeader` 是标题行(`trailing` 右对齐),`CardBody` 是内容区(带内边距)。三者按 `Card > CardHeader|CardBody` 嵌套。 | 组件 | props | |---|---| | `Card` | `children` | | `CardHeader` | `trailing?: ReactNode`;`children` | | `CardBody` | `children` | ~~~tsx active}> T-323 未移植的极值搜索窗口 OCCT: ShapeAnalysis_Surface.cxx:1340-1352 ~~~ ### `Callout` 提示块。宿主也用它渲染编译错误卡(`tone="danger"`)。 | prop | 类型 | 默认 | 说明 | |---|---|---|---| | `tone` | `Tone` | `"info"` | 语义色 | | `title` | `string` | — | 加粗标题行 | | `children` | `ReactNode` | — | 正文 | ~~~tsx 管线是共享的。补缺失的分支,保住既有基线。 ~~~ ### `KeyValue` 对齐的「标签 / 值」字段表。详情面板字段超过 3 条时用它:标签落在同一列,值对齐成一条线,比一串 `Text` 更容易扫读。 | prop | 类型 | 默认 | 说明 | |---|---|---|---| | `items` | `{ label: string; value: ReactNode; tone?: Tone }[]` | 必填 | 每行的标签与值;`tone` 只影响值的颜色 | | `columns` | `number` | `1` | 分栏数;字段多且面板宽时可以给 2 | | `dense` | `boolean` | `false` | 紧凑行距 | ~~~tsx {t.evidence} }, ]} /> ~~~ --- ## 数据 ### `Stat` 单个指标块:大号 `value` + 小号 `label`。 | prop | 类型 | 默认 | 说明 | |---|---|---|---| | `value` | `string \| number` | 必填 | 主数值 | | `label` | `string` | 必填 | 说明文字 | | `tone` | `Tone` | — | 语义色 | | `hint` | `string` | — | 悬浮说明 | ~~~tsx ~~~ ### `Table` 明细表。画布的主力组件。 | prop | 类型 | 默认 | 说明 | |---|---|---|---| | `headers` | `ReactNode[]` | 必填 | 表头 | | `rows` | `ReactNode[][]` | 必填 | 每行的单元格(可以是任意节点,例如放 `