文档 / 原生 UI 组件
原生 UI 组件
Metrix.Bar .bar v0 原生 UI 组件库、属性反射、布局与事件回调完整参考。
概述
在 .bar v0 中,Worker 的 render(env) 钩子直接返回 JSON 树形结构来描述界面:
{
type: "Column",
props: { gap: 8, padding: 12 },
children: [
{ type: "Text", props: { text: "你好", bold: true } },
{ type: "Button", props: { text: "点我", onClick: () => { ... } } }
]
}每个 type 对应由 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 让你可以在面板中直接显示远程图片或内联图标:
{
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 组件可在面板中直接渲染折线图、面积图、柱状图与饼图:
{
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归一化):javascriptimport { 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 | 列表或菜单项处于高亮选择态时 |
{
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:javascriptkeyframes: { 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):回调参数携带点击次数与按键修饰符:javascriptonClick: (e) => { // e = { clicks: 1, mods: { meta: true, ctrl: false, alt: false, shift: false } } }; - 键盘监听 (
onKeyDown):组件绑定onKeyDown后会自动成为可聚焦元素(可配合autoFocus: true使用):javascriptonKeyDown: (e) => { // e = { key: "Enter", mods: { meta: false, ... } } }; - 局部快捷键 (
shortcuts):在任意容器或组件上绑定快捷键字典。当该元素自身或其焦点子树内的任意子元素获得焦点时生效(可配合autoFocus: true绑定面板级快捷键):javascriptshortcuts: { "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 命名的独立原生样式语法,支持范围以此处为准。
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 尺寸、logicalps/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 等状态样式。
{
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 相同。百分比字符串使用对应的百分比布局:
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 插件,也可以显式配置:
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,surfacePressedbordertext,textMutedaccent,accentHover,accentPressed,accentTextdanger,warning,successselection,focusinverse,inverseText
使用 htm 简化 UI 树编写
如果你希望用类似 JSX 的语法编写界面,可在 Worker 中通过 ESM 导入 htm:
<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。
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 等现有布局属性。
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;不需要外部资源权限。
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 不开放此单元格多选协议。