Files
UI/.ua/backlog.json
T
2026-10-26 17:58:19 +08:00

2163 lines
172 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"$schema": "迭代点子库,status: open=待做 / done=已完成;按数组顺序取第一条 open 执行",
"items": [
{
"id": "b01",
"title": "新增 tooltip 组件",
"motivation": "皮肤包 16 组件缺少悬浮提示,表单与工具栏场景常用;可复用自绘弹层与微动画既有模式",
"acceptance": "packages/skin/ui 新增 tooltip.ts:content/message 简单 API,hover/focus 触发,延迟约 150ms 出场,带出入场微动画与 prefers-reduced-motion 兜底,只消费 --ua-* 令牌;导出并注册进 ua.manifest.json,文档站 props 表与 llms-full.txt 同步更新;npm run check 全绿",
"status": "done",
"notes": "API 定为 tooltip(props, trigger) 包裹式;delay 默认 150ms,Esc 立即收起,气泡 pointer-events:none 防抖,aria-describedby + role=tooltip 保证读屏可读;demo 新增 sec-tooltip 区块。顺带修复 ua style 对 https:// 的误判(先剥离字面量再定位 //)",
"domain": "new"
},
{
"id": "b02",
"title": "新增 toast 消息提示组件",
"motivation": "alert 是常驻块级元素,缺少轻量转瞬即逝的全局反馈;操作成功/失败场景刚需",
"acceptance": "新增 toast.ts:toast.success/error/warning/info 四个工厂函数,入参 {message, duration?},默认 3s 自动消失,右上角堆叠,语义色复用四档 variant,出入场微动画 + prefers-reduced-motion 兜底;导出、manifest、文档、llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "对外暴露 toast.info/success/warning/error(message, duration?) 四工厂 + showToast(props) 完整入参版;全局堆叠容器懒创建,role=status 无障碍;退场用 transitionend 移除并带 320ms 兜底(覆盖 reduced-motion 下 transition 不触发的情况);样式复用 alert 语义色 color-mix 模式,reduced-motion 列表已补 .ua-toast;demo 新增 sec-toast 区块",
"domain": "new"
},
{
"id": "b03",
"title": "新增 skeleton 骨架屏组件",
"motivation": "异步加载占位是数据表格与卡片的标配,配合 dataTable 空状态形成完整加载体验",
"acceptance": "新增 skeleton.ts:skeleton({variant: 'text'|'rect'|'circle', rows?}),呼吸/扫光微动画 + prefers-reduced-motion 静态化,只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt 同步;demo 页加一节演示;npm run check 全绿",
"status": "done",
"notes": "skeleton({variant, rows}):text(多行,末行缩短)/ rect / circle 三形状,扫光动画(::after 位移 + 渐变),reduced-motion 下静态灰块(动画列表已补 .ua-skeleton 与 ::after);demo 新增 sec-skeleton 卡片组合演示",
"domain": "new"
},
{
"id": "b04",
"title": "新增独立 pagination 分页器组件并与 dataTable 演示联动",
"motivation": "table-core 已有 pagination 插件,但皮肤包没有独立分页器;dataTable 演示也未展示分页联动",
"acceptance": "新增 pagination.ts:受控 API {page, pageSize, total, onChange},页码过多时折叠省略号,键盘可达,禁用态完整;demo 页 dataTable 一节增加分页联动演示;导出、manifest、文档、llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "pagination(props):page 信号双向绑定(受控),≤7 页全展开、更多时首尾 + 当前页 ±1 折叠省略号(renderList 重建),原生 button 键盘可达,aria-current / aria-label 完整,首末页禁用 ‹ ›;dataTable 新增可选 page 信号桥接(双向同步 + 越界钳制,防无效派发);demo sec-table 新增 pageSize 3 × 24 行的联动卡片",
"domain": "new"
},
{
"id": "b05",
"title": "文档站演示区由 manifest 驱动自动生成",
"motivation": "目前 apps/docs/index.html 手写 props 表与演示,每加组件都要人肉同步;由 manifest 单一来源驱动可消除重复维护",
"acceptance": "ua CLI 新增 docs-gen 逻辑:读取各组件 ua.manifest.json 中的 components 参数表,自动生成 props 表 HTML 片段注入文档站模板;生成结果与现有手写版信息量对齐(props 名/类型/默认值/说明);npm run check 全绿且文档站可正常打开",
"status": "done",
"notes": "核查确认 props 表/示例/步骤/演示 iframe 早已由 ua docs 从 manifest docs.components 单一来源驱动;真正的缺口是信息量不含默认值——本轮 CLI propsTable 新增「默认值」列(props 条目可选 df 字段,缺省渲染 —),llms-full.txt 参数行同步追加「默认 x」标注;16 处可机读默认值从描述文案提取进 df;check 全绿 + 文档站结构完整性校验通过",
"domain": "tooling"
},
{
"id": "c01",
"title": "button / checkbox / switch 补 disabled 禁用态",
"motivation": "三个基础交互组件缺禁用能力,表单只读/权限控制场景刚需",
"acceptance": "ButtonProps / CheckboxProps / SwitchProps 新增可选 disabled(boolean | Signal<boolean>):静态布尔与信号绑定都支持,禁用时事件不触发、置灰 + not-allowed 光标;demo 表单区加静态禁用与信号联动示例;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "零新增辅助代码:复用 frame h() 对 false/undefined 移除属性、Signal 函数走响应式绑定的既有语义,disabled 透传即可;CSS 补通用禁用态(opacity 0.45 + not-allowed,开关轨道同级置灰);demo 用同一 locked 信号绑定 switch 的 checked 与 checkbox 的 disabled 展示联动",
"domain": "tune"
},
{
"id": "c02",
"title": "textField / textarea 补 disabled 禁用态",
"motivation": "输入类组件与 c01 的交互组件对齐,表单只读场景闭环",
"acceptance": "TextFieldProps / TextareaProps 新增可选 disabled(boolean | Signal<boolean>),禁用时不响应输入、置灰 + not-allowed 光标;demo 禁用示例区补一个信号绑定输入框;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "与 c01 同模式:disabled 透传 h() 即得布尔/信号双支持;禁用态 CSS 追加 .ua-input / .ua-textarea;demo 复用 locked 信号联动开关与输入框",
"domain": "tune"
},
{
"id": "d01",
"title": "新增下拉菜单 menu 组件",
"motivation": "工具栏/行操作场景缺省可折叠的动作集合,select 只能选值不能派发动作;可复用自绘弹层与微动画既有模式",
"acceptance": "packages/skin/ui 新增 menu.ts:触发器 + 自绘弹层,items 支持 disabled / danger / divider,点条目 / 点外部 / Esc 关闭,↑↓ 环形导航(禁用项跳过),Enter/Space 选中,Esc 收起后焦点回触发器;只消费 --ua-* 令牌并带 prefers-reduced-motion 兜底;导出 + manifest + 文档 + llms-full.txt + demo 同步;npm run check 全绿",
"status": "done",
"notes": "API menu({ label, items, variant, align, onSelect });MenuItem = { value, label, disabled?, danger?, divider? };弹层 ua-open 同时挂在根与弹层(根管箭头旋转、弹层管出入场),危险条目取 danger 色;键盘 ↑↓ 环形导航 + Esc 回焦触发器;demo 新增 sec-menu 双示例(含禁用/危险/分隔线与右对齐)",
"domain": "new"
},
{
"id": "d02",
"title": "select / radioGroup 补 disabled,修复 select 箭头旋转失效",
"motivation": "c01/c02 已覆盖 button/checkbox/switch/输入类,选择类仍缺禁用;同时发现 select 的 .ua-select.ua-open 选择器永不命中(ua-open 只挂在弹层),展开时箭头不旋转",
"acceptance": "SelectProps / RadioGroupProps 新增可选 disabled(boolean | Signal<boolean>);禁用时 select 触发器置灰且不展开弹层,radioGroup 整组置灰不可选;select 箭头在展开时旋转 180°;demo 加信号禁用与静态禁用示例;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "disabled 仍走 h() 透传(原生 button/input 拦截交互)零辅助代码;select 根节点补 class 函数加 ua-open,箭头旋转选择器命中;CSS 通用禁用态列表并入 .ua-select-trigger 与 .ua-radio,另加 :has() 让单选行整行弱化、禁用触发器 hover 不改边框色",
"domain": "tune"
},
{
"id": "d03",
"title": "新增折叠面板 accordion 组件",
"motivation": "长表单/设置页需要分区收起,皮肤包无折叠容器;grid 行高过渡可零测量实现展开动画",
"acceptance": "新增 accordion.ts:items(value/title/content/disabled)+ 单开(手风琴)与 multiple 多开,active 信号可选受控,标题为原生 button(aria-expanded/controls + region 关联),内容区 0fr→1fr 过渡且 reduced-motion 静态化;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "contents 走 appendChild 既有语义,字符串/DOM/响应式函数通吃;未传 active 时内部 signal<string[]> 持有展开集合,受控模式只认 active 单开;CSS 用 grid-template-rows 0fr→1fr 过渡免测量,reduced-motion 列表补 ua-acc-*;demo 新增 sec-accordion(多开 + 禁用面板)",
"domain": "new"
},
{
"id": "d04",
"title": "dataTable 接加载态(骨架行 + 交互禁用)",
"motivation": "异步取数时表格直接闪空状态,缺少 loading 反馈;skeleton 已就位但未与表格打通",
"acceptance": "DataTableProps 新增 loading?: boolean | Signal<boolean>;为真时表体渲染骨架行(行数随 pageSize,列数含选择列)、筛选输入与上下页按钮禁用、容器 aria-busy=true;demo 加开关演示;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "busy() 读取器兼顾布尔与信号(信号在 renderList 内调用可被依赖收集);骨架行数量 Math.min(pageSize,6) 防过长;加载态优先于数据判断,避免闪出旧行;disabled 仍走 props 透传,零新增辅助组件",
"domain": "tune"
},
{
"id": "d05",
"title": "新增面包屑 breadcrumb 组件",
"motivation": "多层级页面的位置指示缺失;顺带补齐导航类组件的语义结构(nav/ol/li + aria-current)",
"acceptance": "新增 breadcrumb.ts:nav + ol 语义结构,末项自动为当前页(不可点、aria-current=page),条目有 href 渲染 <a>、无 href 渲染 button 走 onNavigate,分隔符可配;只消费 --ua-* 令牌;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "API breadcrumb({ items: {label, href?}[], separator?, onNavigate? });分隔符 aria-hidden;reduced-motion 列表补 .ua-crumb-link;demo 新增 sec-breadcrumb('›' 分隔 + toast 反馈跳转);首轮 style 门禁拦下多行 /*** 注释(检查器只认紧邻单行块),已改单行",
"domain": "new"
},
{
"id": "d06",
"title": "门禁新增 ua verify:清单 ↔ 文档 ↔ 演示锚点一致性校验",
"motivation": "新增组件靠人工同步 manifest/docs/demo,易漏(本轮已出现 badge/alert/spinner 手把手只有 2 步);把不变量机器化,防回归",
"acceptance": "CLI 新增 verify 子命令:docs.components 的 id 唯一且 api.exports 有 `${id}(` 或以 `${id}.` 开头条目、props 条目必备 n/t/d、steps ≥3、example 必填、demo 锚点必须存在于 apps/demo/main.ts;纳入 npm run check 链条;AGENTS.md 同步;npm run check 全绿",
"status": "done",
"notes": "锚点校验改看演示源码而非打包产物(demo.html 是 esbuild 产物,锚点在运行时生成);顺带补齐 badge/alert/spinner 的第 3 条手把手(说明与 button/skeleton 的分工);check 链变为 build→style→typecheck→smoke→index→verify",
"domain": "new"
},
{
"id": "d07",
"title": "新增可交互气泡卡片 popover 组件",
"motivation": "tooltip 只做纯展示(pointer-events:none),筛选面板/快捷操作需要可点击的浮层;与 tooltip 形成展示/交互分工",
"acceptance": "新增 popover.ts:popover(props, trigger) 包裹式,默认点击开合,trigger:'hover' 走延迟出场,点外部 / Esc 收起(Esc 回焦触发器),content 支持任意 DOM 且内部点击不误关,onOpenChange 回调;aria-haspopup/aria-expanded 正确;只消费 --ua-* 令牌 + reduced-motion 兜底;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "触发器 click 内 stopPropagation,避免刚开就被 document 的外部点击关掉;aria-expanded 用 effect 写回(setAttribute 不吃函数值);弹层点击已阻止冒泡;demo 新增 sec-popover(含 checkbox+button 的筛选面板与 hover 触发示例)",
"domain": "new"
},
{
"id": "d08",
"title": "演示页加侧边目录与滚动高亮",
"motivation": "演示页已 14 个区块,长页滚动找区块靠手滑;目录同时给文档站 iframe 外的直接访问提供导航",
"acceptance": "demo 页改为 shell 双栏布局(左 sticky 目录 + 右内容),目录项挂载后从 .sec 自动反推(不手写清单),IntersectionObserver 高亮当前区块,窄屏(≤820px)隐藏目录,reduced-motion 下关平滑滚动;npm run check 全绿、demo 可正常构建",
"status": "done",
"notes": "目录由 buildToc 在 mount 后扫描 section.sec 生成(id + h2 文案),新增区块自动入目录无需维护;rootMargin -10%/-80% 让高亮更跟手;.page 去掉 max-width 由 .shell 统一控宽 1160",
"domain": "tune"
},
{
"id": "d09",
"title": "新增滑块 slider 组件",
"motivation": "设置类场景(音量/阈值/预算)需要范围输入,皮肤包只有文本类输入;原生 range 自带键盘与触屏,接入成本低",
"acceptance": "新增 slider.ts:value 信号双向绑定(拖动写回 + 外部赋值同步),min/max/step 可配,disabled 支持布尔或信号,showValue 可关,onChange 回调;样式只消费 --ua-* 令牌(accent-color)且带聚焦光环与禁用态;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "组件不持有数值,一律委托调用方信号;effect 内等值不重写 input.value,避免拖动过程被打断;轨道配色用 accent-color 令牌化,无需自绘 thumb;demo 新增 sec-slider(音量 step 5 + 与 locked 信号联动的禁用滑块)",
"domain": "new"
},
{
"id": "d10",
"title": "toast 补手动关闭与同屏堆叠上限",
"motivation": "自动消失前用户无法提前关掉(如已看清错误),且连续触发会无限堆叠淹没页面",
"acceptance": "ToastProps 新增 closable(显示 × 立即退场);同屏最多 4 条,超出挤掉最早一条;退场逻辑抽为内部 dismiss() 供自动/手动共用;is-closable 才接收指针事件,其余保持 pointer-events:none;manifest/文档/llms-full.txt/demo 同步;npm run check 全绿",
"status": "done",
"notes": "dismiss() 统一 transitionend + 320ms 双保险;超限用 firstElementChild.remove() 立即腾位(不走动画,保证硬上限);demo sec-toast 增加「可关闭」按钮演示 showToast({closable:true})",
"domain": "tune"
},
{
"id": "d11",
"title": "modal 补 size 档位与 footer 操作区",
"motivation": "弹窗内容宽窄差异大(确认框 vs 表单),底部操作按钮只能塞进 body 导致分割线语义缺失",
"acceptance": "ModalProps 新增 size?: 'sm'|'md'|'lg'(340/420/640)与 footer?: unknown;footer 渲染右对齐操作区带上分割线;缺省行为与旧版一致;manifest/文档/llms-full.txt/demo 同步;npm run check 全绿",
"status": "done",
"notes": "宽度档位用 .ua-modal-sm/lg 覆盖基准 md,基础宽 min(420px,90vw) 不动;footer 仅在传入时渲染(null 分支不进 DOM);demo 的弹窗改为 size:'lg' + 取消/确定 footer 按钮",
"domain": "tune"
},
{
"id": "d12",
"title": "新增可关闭标签 tag 组件",
"motivation": "badge 纯展示无法移除,筛选条件/已选关键字需要带 × 的标签;补齐 badge 的交互版",
"acceptance": "新增 tag.ts:label + variant 五档 + closable/onClose;closable 时渲染带 aria-label 的 × 按钮;只消费 --ua-* 令牌;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "组件无状态:onClose 只通知,节点由调用方按信号重渲染移除(与 form/formItem 返回句柄、dataTable 委托内核的哲学一致);demo 用 signal<string[]> + renderList 演示真实删除链路,另有四档语义色静态展示",
"domain": "new"
},
{
"id": "d13",
"title": "dataTable 列定义支持对齐与单元格格式化",
"motivation": "皮肤层只能 String() 原始值,数字列无法右对齐、年龄/金额无法加单位;内核不关心展示,字段应留在皮肤层",
"acceptance": "新增皮肤层 TableColumn<T> extends ColumnDef<T>,追加 align?: left|center|right 与 format?: (value,row)=>string;DataTableProps.columns 改用它;表头与单元格共用对齐类;format 走 textContent 天然免注入;demo 表格演示年龄右对齐+单位、城市居中;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "严格分层:内核 ColumnDef 不动,展示字段只在 skin 的 TableColumn 上扩展(extends 而非改核心);对齐类同时作用于 th(取代原 class 函数,合并 ua-sortable);demo 首表展示 align:right + format(v=>`${v} 岁`)、城市居中",
"domain": "tune"
},
{
"id": "d14",
"title": "新增抽屉 drawer 组件",
"motivation": "详情查看/编辑需要不打断主视图的侧滑容器,modal 是强打断确认;移动端尤其常用",
"acceptance": "新增 drawer.ts:open 信号驱动,placement 左右、width 可调(窄屏 92vw 上限)、footer 操作区、遮罩 / × / Esc 关闭、打开聚焦首个可交互元素;只消费 --ua-* 令牌 + reduced-motion 兜底;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "遮罩开合用 opacity+visibility(与 tooltip/popover 同模式),面板靠 transform:translateX 滑入;width 走 inline style(调用方数据驱动的表现值不进 CSS);demo 新增 sec-drawer(订单详情 + footer 关闭 + tag/badge 组合)",
"domain": "new"
},
{
"id": "d15",
"title": "CLI 包补 ua.manifest.json 并修正文档里的过期组件数",
"motivation": "工具链自身不在注册表里,Agent 无法通过 MCP/llms 发现 ua 的 6 个子命令;AGENTS.md 与 ARCHITECTURE.md 仍写 17 个组件(实际 27)",
"acceptance": "packages/tools/cli/ua.manifest.json(type: tooling)描述 index/list/info/docs/style/verify/mcp 七个子命令与约定,索引包数变 5;AGENTS.md 与 ARCHITECTURE.md 的组件数改成 27;llms.txt / llms-full.txt 重新生成且 CLI 段落可读;npm run check 全绿",
"status": "done",
"notes": "tooling 类型在 ORDER 表外,排序自然落到末尾(??9 兜底),无需改 CLI 排序逻辑;CLI 清单 docs.components 留空,故不参与 verify 的组件级校验;顺带确认 docs 生成对无组件包不报错",
"domain": "tooling"
},
{
"id": "d16",
"title": "dataTable 落地内核已定义的列宽 width",
"motivation": "table-core 的 ColumnDef 声明了 width?: number | 'flex',但皮肤层从未消费,列宽完全靠浏览器猜;契约与实现脱节",
"acceptance": "dataTable 渲染 colgroup:数字 width 落成固定像素 <col>,'flex' 或不设交给自适应,选择列固定 44px;demo 表格演示 name 160 / age 100 / city flex;manifest 列定义说明与手把手同步;npm run check 全绿",
"status": "done",
"notes": "用 colgroup 而非逐 th 设宽,保证表头与表体一致且不入侵单元格渲染;未开 table-layout:fixed(nowrap 内容会被裁),宽高仍由内容兜底;pinned 固定列仍未消费(多列 sticky 偏移需测量),留作后续条目",
"domain": "tune"
},
{
"id": "d17",
"title": "表单错误态覆盖包裹式控件并修正校验时机事件",
"motivation": "① 现有 blur 不冒泡,checkbox/select/radioGroup 这类包裹式控件失焦永远不校验;② ua-invalid 只加在控件根,自绘 select 的边框在内部触发器上,视觉上无错误提示;③ 缺 aria-invalid,读屏感知不到",
"acceptance": "formItem 用 focusout 取代 blur、补 change(select/checkbox 变更即校验);错误态同时落到控件根与内部原生控件,并同步 aria-invalid;CSS 覆盖 .ua-invalid 后代 .ua-input/.ua-textarea/.ua-select-trigger/.ua-radio/.ua-slider-input;demo 表单加必填 select 演示;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "marked() 统一收集控件根 + 内部 input/textarea/select/.ua-select-trigger 两处落点;.ua-invalid 后代选择器让包裹式控件描红,无需组件各自改签名(低侵入);demo formlab 新增城市必填 select,提交结果显示城市",
"domain": "tooling"
},
{
"id": "d18",
"title": "把「皮肤包禁止硬编码颜色」做成 ua verify 门禁",
"motivation": "AGENTS.md 写着皮肤禁硬编码颜色,但一直靠自觉;ui.css 实际残留 5 处 #fff(按钮/徽标/开关滑块),暗色下对比也不理想",
"acceptance": "checkSkinCss():skin-dom 类型包里所有 .css 出现 #hex 字面量即报错并指出文件行号;5 处 #fff 改为 var(--ua-color-bg)(信号色底上的反色,暗色自动翻转);负向测试(临时加 .ua-tmp{color:#fff})能拦下,移除后回归全绿;AGENTS.md 门禁说明同步;npm run check 全绿",
"status": "done",
"notes": "rgba(...) 未禁:仅用于阴影与遮罩透明度叠加,不承载品牌色,禁掉会逼出更难读的写法(已在函数注释写明边界);实现上先随 python 写文件把正则转义搞坏两次,改用编辑工具直写并做负向验证确认门禁真实生效",
"domain": "tooling"
},
{
"id": "d19",
"title": "dataTable 选中态受控(selected 信号)",
"motivation": "选中集只在内核里,外部拿不到也无法预设,批量操作/清空选择只能靠内部工具栏;与 page 受控桥接形成对称能力",
"acceptance": "DataTableProps 新增 selected?: Signal<RowKey[]>,与内核选择双向同步(内容比较避免来回派发,外部数组拷贝保护内核);demo 首表传 picked 信号,展示已选计数与「清空选择」按钮;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "复用 page 桥接的同一套思路:两个 effect 各管一向,sameKeys 内容比较收敛;内部写回拷贝 [...internal],防外部改动内核数组;demo 需 import type { RowKey } 才能对齐 Signal 的逆变",
"domain": "tune"
},
{
"id": "d20",
"title": "progress 支持不确定态(未知时长加载)",
"motivation": "请求时长未知时只能硬编造一个假百分比,或用 spinner 但失去条状形态;不确定态是进度条的标准形态",
"acceptance": "ProgressProps.value 改为可选;缺省时渲染不确定态(35% 填充块 1.2s 往复)、不显示百分比、role=progressbar + aria-label=进行中;定量态补 aria-valuemin/max/now;reduced-motion 静态化;demo 加不确定态示例;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "以「不传 value」作为不确定态的开关,不新增布尔参数(少一个可矛盾的状态);定量态的 aria-valuenow 用函数属性随信号更新;reduced-motion 下靠既有 .ua-progress-fill 规则把动画归零,退化为静态 35% 条",
"domain": "tune"
},
{
"id": "d21",
"title": "无障碍收尾:× 按钮 aria-label、spinner/skeleton 语义、tablist 标签",
"motivation": "审计发现 modal 的 × 按钮无 aria-label(读屏只念「×」)、spinner 无 role=status(不播报加载)、skeleton 未 aria-hidden(占位文本被读出来)、tablist 缺 aria-label 与 aria-disabled",
"acceptance": "modal 关闭按钮补 type/aria-label、spinner 补 role=status + aria-label、skeleton 全形态 aria-hidden、tabs 的 tablist 补 aria-label 且禁用页签标 aria-disabled;manifest conventions 记录无障碍基线;npm run check 全绿",
"status": "done",
"notes": "纯属性级修复,不动结构与样式;tabIndex 漫游(仅激活页签可 Tab)此前已实现,本次只补语义;分页器审计后无需改动(已有 nav aria-label、页码 aria-current、‹› aria-label)",
"domain": "tune"
},
{
"id": "d22",
"title": "新增头像 avatar 组件",
"motivation": "用户列表/评论区需要头像位,缺 component 只能手拼 img + 圆角;首字占位是缺图时的标准兜底",
"acceptance": "新增 avatar.ts:name 必填(alt / title / 首字来源),src 可选(有则图片、无则首字占位),size 可调,variant 四档语义底色;只消费 --ua-* 令牌;导出 / manifest / 文档 / llms-full.txt / demo 同步;npm run check 全绿",
"status": "done",
"notes": "首字占位对中文取首字、英文大写,空名兜底 ?;flex:none 保证不被挤压;首次跑 verify 被自身新门禁拦下(声明 sec-avatar 但 demo 缺区块),补独立区块后通过——门禁有效性再次得到验证",
"domain": "new"
},
{
"id": "d23",
"title": "alert 补标题行与可关闭,并补 role=alert",
"motivation": "常驻提示无法消除(错误已处理完仍占版面),且只有单层正文放不下「结论 + 详情」;role=alert 缺失导致读屏不播报",
"acceptance": "AlertProps 新增 title? / closable? / onClose?;关闭时先退场(opacity+上移,200ms)再从 DOM 移除;容器 role=alert;只消费 --ua-* 令牌;demo 新增 sec-alert 展示无标题与可关闭带标题两种;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "与 toast 的分工保持:alert 常驻在页面流里、toast 全局浮动;关闭后由调用方自行决定是否重新渲染;body/title/message 三层结构让 flex 布局能把 × 对齐到首行;对现有 alert({message}) 调用完全兼容",
"domain": "tune"
},
{
"id": "d24",
"title": "重写 README 并把组件清单纳入 verify 防漂移",
"motivation": "README 只有标题一行,新人/Agent 从仓库首页拿不到任何信息;而文档里的组件数此前已经漂移过(17 vs 27)",
"acceptance": "README 覆盖定位 / 快速开始 / 28 组件清单 / 目录表 / 三道门禁 / Agent 接入 / 分支流水线;新增 <!-- ua:components --> 标记区块,ua verify 校验区块 id 集合与全部清单 docs.components 完全一致(缺/多都报);负向测试(删 avatar)能拦下;AGENTS.md 同步;npm run check 全绿",
"status": "done",
"notes": "不做自动写入(避免工具偷偷改文档),只做一致性校验:新增组件时 verify 报「README 组件区块缺少 xxx」,由人补一行;标记区块用 · 分隔便于人读与机器切分",
"domain": "tune"
},
{
"id": "d25",
"title": "menu 条目支持快捷键提示",
"motivation": "菜单里展示快捷键是桌面级体验的标配;缺字段时调用方只能把快捷键拼进 label,文案与对齐都失控",
"acceptance": "MenuItem 新增 shortcut?: string;条目改为 flex 两端对齐,label 单行省略,右侧 kbd 灰字提示(不绑定按键);demo 文件操作菜单加 F2 / ⌘D;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "明确边界:快捷键只做展示,实际按键仍需调用方全局监听(组件不做全局键盘陷阱);两端对齐让长文案截断而快捷键始终可见;kbd 用 inherit 字体避免等宽字跳脱整体排版",
"domain": "tune"
},
{
"id": "d26",
"title": "select 键盘导航:↑↓ 环形移动、Home/End 跳转、Esc 回焦触发器",
"status": "done",
"domain": "tune"
},
{
"id": "d27",
"title": "modal 焦点陷阱:Tab/Shift+Tab 回绕、role=dialog/aria-modal、关闭归还焦点",
"status": "done",
"domain": "tune"
},
{
"id": "d28",
"title": "drawer 焦点陷阱:Tab/Shift+Tab 回绕、关闭归还焦点(对齐 modal)",
"status": "done",
"domain": "tune"
},
{
"id": "d29",
"title": "menu 补 Home/End 跳转,与 select 键盘导航对称",
"status": "done",
"domain": "tune"
},
{
"id": "e01",
"title": "新增 inputNumber 数字输入组件",
"motivation": "数量、份数等离散数值输入是表单常见场景,纯 textField 缺少步进语义;加减按钮 + 键盘调节可复用既有 button 与信号双向绑定模式",
"acceptance": "packages/skin/ui 新增 inputNumber.ts:−/+ 按钮 + 输入框三段式,value 信号双向绑定,min/max/step 超界自动夹取,键盘 ↑↓ 调节,非法输入回滚,disabled 支持布尔或信号;只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt 同步;demo 加一节演示;npm run check 全绿",
"status": "done",
"notes": "inputNumber(props):−/+ 复用 ghost button(自带微动画与 disabled 透传),输入框 inputmode=decimal,change 时 parseFloat 解析、非法回滚当前值;↑↓ 步进并 preventDefault;clamp 统一夹取 min/max(缺省 ±Infinity);aria-label 三件套 + role=group;CSS 步进按钮收窄、tabular-nums;demo 新增 sec-inputnumber(与禁用态实例共用 qty 信号);manifest/docs/llms-full/README 同步为 29 组件",
"domain": "new"
},
{
"id": "e02",
"title": "新增 stepper 步骤条组件",
"motivation": "下单、注册等多步流程需要进度指示与回跳,现有导航类组件(tabs/breadcrumb/pagination)都不覆盖分步语义;纯展示 + 可选点击回跳,改动范围可控",
"acceptance": "packages/skin/ui 新增 stepper.ts:stepper({steps, active, clickable?, onChange?}),done/current/upcoming 三态视觉,clickable 时已完成步骤可点回跳并写回 active 信号,aria-current=step;只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt、README 清单同步;demo 加一节演示;npm run check 全绿",
"status": "done",
"notes": "stepper(props):active 信号变化整条重渲染(effect 首跑赋值 + replaceWith,条目少开销可忽略);done 显示 ✓、current 光环聚焦、upcoming 弱化,aria-current=step;clickable 时已完成步骤渲染为 button 回跳写回信号;连接线用 .ua-step + .ua-step::before 横向短线;demo 新增 sec-stepper(上一步/下一步按钮 computed 禁用联动);README 同步 30 组件",
"domain": "new"
},
{
"id": "e03",
"title": "新增 rate 评分组件",
"motivation": "评价、打分场景常用,现有组件无覆盖;星标交互可完整复用信号双向绑定与微动画审美约定,单文件可控",
"acceptance": "packages/skin/ui 新增 rate.ts:rate({value, max?, disabled?, onChange?}),max 缺省 5,hover 预览移出还原,键盘 ←→/↑↓ 调节,aria 星标语义;只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt、README 清单同步;demo 加一节演示;npm run check 全绿",
"status": "done",
"notes": "rate(props):hover 预览用组件内局部 hover 信号(容器 mouseleave 归零),shown = hover || value 决定点亮数;value/hover 变化整条重渲染(与 stepper 同款 effect + replaceWith 模式);键盘 →/↑ 加分 ←/↓ 减分并 clamp [1,max];role=slider + aria-valuenow;选中色取 --ua-color-warning 金黄;demo 新增 sec-rate(可交互 + 只读两实例共用 score 信号);README 同步 31 组件",
"domain": "new"
},
{
"id": "e04",
"title": "新增 empty 空状态组件",
"motivation": "列表、表格、搜索无结果时需要统一的占位提示,dataTable 内置空态样式但独立场景(卡片、面板、筛选结果)没有可复用组件",
"acceptance": "packages/skin/ui 新增 empty.ts:empty({title?, description?, action?}),居中排版,action 为可选 DOM 操作区(常见放按钮);只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt、README 清单同步;demo 加一节演示;npm run check 全绿",
"status": "done",
"notes": "empty(props):纯静态组件(无信号),role=status 由读屏播报;装饰符号 ⌀ 弱化为边框色且 aria-hidden;min-height 120px 撑留白(令牌只到 space-4);demo 新增 sec-empty(信号切换有数据/无结果对比,内容用响应式函数渲染);README 同步 32 组件",
"domain": "new"
},
{
"id": "e05",
"title": "新增 divider 分隔线组件",
"motivation": "表单分组、卡片内区块、工具栏之间需要统一分隔语义,目前只有 menu 内部私有分隔线,独立场景无组件可用",
"acceptance": "packages/skin/ui 新增 divider.ts:divider({label?, vertical?, dashed?}),水平/垂直两种方向,可带居中文案标签;只消费 --ua-* 令牌;导出、manifest、文档、llms-full.txt、README 清单同步;demo 加一节演示;npm run check 全绿",
"status": "done",
"notes": "divider(props):水平线用 ::before/::after 双线夹 label 居中文案;vertical 单线(伪元素清空,dashed 走 border-left);dashed 水平线 border-top 替代背景色;role=separator + aria-label;demo 新增 sec-divider(文案线/虚线/工具栏竖线三种用法);README 同步 33 组件",
"domain": "new"
},
{
"id": "f01",
"title": "组件数漂移治理:AGENTS/ARCHITECTURE 数字纳入 verify 门禁",
"motivation": "组件数第三次漂移(AGENTS.md 与 ARCHITECTURE.md 仍写 27,实际 33),ua verify 只锁 README 区块锁不住散文数字",
"acceptance": "ua verify 新增规则:扫描 AGENTS.md 与 ARCHITECTURE.md 中的「NN 个组件」模式数字,与 manifest docs.components 长度比对不一致即报错;当前两处 27 改为 33;npm run check 全绿",
"status": "done",
"notes": "来源:2026-10-23 第二次冷启动审计应修 #1",
"domain": "tooling"
},
{
"id": "f02",
"title": "check 链描述同步:AGENTS.md 与 ITERATION.md 补 verify 第六步",
"motivation": "AGENTS.md:10 与 ITERATION.md:31 写 5 步,package.json 实为 6 步(含 verify),文档与实际门禁不一致",
"acceptance": "两处描述改为 build → style → typecheck → smoke → index → verify 六步;npm run check 全绿",
"status": "done",
"notes": "来源:审计应修 #2",
"domain": "tune"
},
{
"id": "f03",
"title": "新增 ua lint-iterations 门禁:留痕格式机器校验",
"motivation": "iterations.log 41 行中 36 行违规(time 字段混用、UTC 时间戳、缺 commit),靠人工反复修补,规范缺机器校验",
"acceptance": "ua 新增 lint-iterations 子命令:校验每行 JSON 字段恰为 {ts,id,result,commit,summary}、ts 为本地 +08:00 ISO 且可用 Date 解析、commit 为 7-40 位哈希或 aborted 时允许为空、result 枚举 done/aborted;并入 npm run check;历史 36 条违规行按规范修复(ts 以对应 git 提交的 author date 本地时区回填,commit 补哈希);npm run check 全绿",
"status": "done",
"notes": "来源:审计应修 #3;历史行修复用 git log --format=%cI 按条目 id 关联提交时间回填",
"domain": "new"
},
{
"id": "f04",
"title": "commit-lint 收严:chore/docs 等强制 scope,杜绝无 scope 放行",
"motivation": "COMMITTING.md 规定 <type>(<scope>) 格式,但 chore:、docs: 无 scope 被 lint 放行,历史还有 17 条 iter: 缺 scope",
"acceptance": "commit-lint 规则收严为:除 Merge 外所有 type 必须带 scope(scope 允许 cli/skin/core/backlog/留痕等枚举或条目 id);反向用例 chore: xxx / docs: xxx / iter: xxx 必须被拒;正例全部通过;npm run check 全绿",
"status": "done",
"notes": "来源:审计应修 #4;历史 iter 缺 scope 的提交不回改(已发布历史不动),只管增量",
"domain": "tune"
},
{
"id": "f05",
"title": "ua index 幂等化:generatedAt 仅内容变化时更新",
"motivation": "ua index 每次重写 .ua/index.json 的 generatedAt,npm run check 必然弄脏工作区,与「提交前 check」工作流冲突",
"acceptance": "index 命令改为:若除 generatedAt 外内容与现有文件一致则不写入(或 generatedAt 改为内容哈希派生);连续两次 npm run check 后 git status 必须干净",
"status": "done",
"notes": "来源:审计应修 #5",
"domain": "tune"
},
{
"id": "f06",
"title": "pnpm-lock.yaml 入库 + 目录树补全",
"motivation": "lockfile 缺失导致依赖树不可复现;ARCHITECTURE.md 目录树缺 scripts/、COMMITTING.md、ITERATION.md、.ua/ 清单等条目,新代理接手有盲区",
"acceptance": "pnpm-lock.yaml 提交入库;ARCHITECTURE.md 目录树补 scripts/(gitea.mjs/gitea-api.mjs 一句话说明)、COMMITTING.md、ITERATION.md、.ua/backlog.json、.ua/iterations.log、pnpm-workspace.yaml;npm run check 全绿",
"status": "done",
"notes": "来源:审计应修 #6 + 建议 #7",
"domain": "tune"
},
{
"id": "g01",
"title": "ua docs 幂等化:llms-full.txt 时间戳不弄脏工作区",
"motivation": "llms-full.txt 头部含 generatedAt,每次 ua docs 重写必然弄脏工作区,与 index 幂等化(f05)同类问题",
"acceptance": "内容与现有文件仅时间戳之差时不写入;npm run docs 连跑两次 git status 干净",
"status": "done",
"notes": "自补:延续 f05 方案",
"domain": "tune"
},
{
"id": "g02",
"title": "新增 image 图片组件(缩略图 + 点击预览)",
"motivation": "皮肤包缺常规媒体组件,img 缩略图 + 遮罩预览是常规表单/详情页刚需",
"acceptance": "image({src, alt, width?}) 返回缩略图,点击开遮罩大图(Esc/点击遮罩关闭,reduced-motion 兜底);manifest/docs/llms-full/README/demo 同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充方向",
"domain": "new"
},
{
"id": "g03",
"title": "textField/textarea/inputNumber 补 prefix/suffix 附加内容槽",
"motivation": "搜索框放大镜、金额单位等前后缀是输入组件高频需求,目前只能外部拼装",
"acceptance": "三个输入组件新增 prefix/suffix(string|HTMLElement),渲染为输入框内侧前后附加块;manifest 参数表与 llms-full 同步;npm run check 全绿",
"status": "done",
"notes": "自补:可用性细节方向",
"domain": "tune"
},
{
"id": "g04",
"title": "dataTable 筛选框用 prefix 放搜索图标",
"motivation": "g03 落地了 prefix 附加块,dataTable 工具栏筛选框是最佳实战落点,同时验证包装不影响事件冒泡",
"acceptance": "dataTable 筛选输入框带 🔍 prefix;筛选功能回归正常(smoke 不变绿转红);npm run check 全绿",
"status": "done",
"notes": "自补:g03 实战验证",
"domain": "tune"
},
{
"id": "g05",
"title": "ua verify 增加 llms-full.txt 生成同步校验",
"motivation": "llms-full.txt 是手工触发 ua docs 生成的产物,改 manifest 后忘记跑 docs 会产生文档漂移,目前无门禁",
"acceptance": "verify 内联重渲染 Agent 文档(时间戳归一化)与根目录 llms-full.txt 比对,不一致即报错提示 npm run docs;npm run check 全绿",
"status": "done",
"notes": "自补:工具链防漂移方向",
"domain": "tooling"
},
{
"id": "g06",
"title": "组件数门禁扩展 README 计数文本",
"motivation": "f01 的计数漂移校验只覆盖 AGENTS/ARCHITECTURE,README 的「N 个 DOM 组件」文本仍可漂移",
"acceptance": "checkCountDocs 扫描列表加入 README.md,漂移即 verify 失败;npm run check 全绿",
"status": "done",
"notes": "自补:f01 收尾",
"domain": "tooling"
},
{
"id": "g07",
"title": "新增 timeline 时间线组件",
"motivation": "皮肤包缺少按时间顺序展示事件流的组件,日志 / 审批流 / 版本记录场景常用;只需轴线 + 语义色节点,可复用既有令牌与微动画模式",
"acceptance": "新增 packages/skin/ui/src/timeline.ts:items(title/content/time/variant) + mode(left|right) + pending 尾部节点,节点色只走 --ua-* 令牌,入场淡入带 prefers-reduced-motion 兜底;导出 + manifest props 表与 steps + demo sec-timeline + README 与文档同步;npm run check 全绿",
"status": "done",
"notes": "API 定为 items(title/content/time/variant) + mode(left|right) + pending 尾部虚节点;节点语义色走 --ua-* 令牌,0.18s 入场淡入 + 虚线脉冲动画,prefers-reduced-motion 全部关停;demo 新增 sec-timeline(按钮切换 pending);组件数 34→35 三处计数与 README 清单同步",
"domain": "new"
},
{
"id": "g08",
"title": "新增 segmented 分段控制器组件",
"motivation": "tabs 是页签语义(下划线 + 面板),视图 / 单位切换需要紧凑的胶囊分段控件;与 radioGroup 的差别是不承载表单语义",
"acceptance": "新增 src/segmented.ts:options / value(信号双向) / size(sm|md) / disabled,滑块指示条随选中平移,←→ 键盘导航 + roving tabindex,选中态低视觉比重;导出 + manifest + demo sec-segmented + 文档同步;npm run check 全绿",
"status": "done",
"notes": "API 为 options + value(必传信号) + size + disabled(布尔|信号) + onChange;滑块几何由 getBoundingClientRect 实测(减去容器 clientLeft 边框),挂载后 rAF 二次测量;←→ 环形导航 + 漫游 tabindex;整组禁用用 is-disabled 类 + 逐按钮 disabled;demo sec-segmented 用两个实例共用 range 信号演示联动",
"domain": "new"
},
{
"id": "g09",
"title": "新增 descriptions 描述列表组件",
"motivation": "详情页与抽屉里高频需要 label:value 两列布局,目前只能借用 form 或手写 DOM;需要列数自适应与 bordered 两种形态",
"acceptance": "新增 src/descriptions.ts:items(label/value/span) + column(1-4,窄屏自动降列) + bordered + size;label 走 text-muted 令牌,值支持任意 DOM;导出 + manifest + demo sec-descriptions + 文档同步;npm run check 全绿",
"status": "done",
"notes": "API 为 items(label/value/span) + column(1-4) + bordered + size + title;列数通过 CSS 变量 --ua-desc-col 注入,样式表在 ≤640px 覆盖为单列;span 在 min(span, column) 处截断防越界;标题为可选外层 wrap;demo sec-descriptions 展示纯网格(含 tag 值与 span 占两列)与 bordered 小尺寸两种形态",
"domain": "new"
},
{
"id": "g10",
"title": "新增 statistic 统计数值组件",
"motivation": "看板需要大号数值 + 单位 + 环比趋势,现在只能手写;数值变化时的滚动动画能强化数据反馈",
"acceptance": "新增 src/statistic.ts:title / value(Signal<number>) / precision / prefix / suffix / trend(up|down),值变化用 rAF 从旧值滚到新值(约 400ms),prefers-reduced-motion 直接落终值,涨红跌绿;导出 + manifest + demo sec-statistic + 文档同步;npm run check 全绿",
"status": "done",
"notes": "API 为 title + value(信号) + precision + prefix + suffix + trend;滚动用 rAF 逐帧改文本(400ms easeOutCubic),新目标先 cancelAnimationFrame 打断,收尾强制对齐终值防浮点残差;matchMedia prefers-reduced-motion 直落终值;trend 按 A 股涨红跌绿;demo sec-statistic 用随机涨跌按钮演示双向滚动",
"domain": "new"
},
{
"id": "g11",
"title": "浮层定位空间不足时自动翻转",
"motivation": "popover / tooltip / select / menu 固定按 placement 定位,视口底部会溢出被裁切,缺少统一 flip 判定",
"acceptance": "新增 src/position.ts:flip(trigger, panel, preferred) 依据 getBoundingClientRect 与视口高度决定是否上下翻转,四个浮层组件接入;导出 + 文档说明 + demo 覆盖说明;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g12",
"title": "verify 增加 backlog 与 iterations.log 交叉校验",
"motivation": "lint-iterations 只校验行格式,无法发现『条目置 done 但无留痕』或『留痕指向不存在的条目』",
"acceptance": "ua.js 新增交叉校验并挂进 cmdVerify:每条 done 条目必须在 iterations.log 有 result=done 记录,每条日志 id 必须在 backlog 存在;人为注入违规时 verify 退出码 1;npm run check 全绿",
"status": "done",
"notes": "cmdVerify 增加 checkIterations:done 条目必须有 done 留痕、留痕 id 必须存在于 backlog;行格式仍归 lint-iterations;实测注入幽灵 id zz99 后 verify 报 1 violation 退出码 1,还原后复绿;AGENTS.md 门禁说明同步补一句",
"domain": "tooling"
},
{
"id": "g13",
"title": "新增 ua changelog 生成 CHANGELOG.md",
"motivation": "迭代提交已有规范前缀,但没有面向人的变更汇总,qa/stable 的提交史对使用者不可读",
"acceptance": "ua.js 新增 cmdChangelog:解析 git log 按 iter / feat / fix / chore 分组生成 CHANGELOG.md,带 --check 幂等模式(无变化不写盘、退出码 0);挂进 package.json scripts;npm run check 全绿",
"status": "done",
"notes": "cmdChangelog 解析 git log -n 400(%h\\t%s),按 iter/feat/fix/docs/chore|refactor|test|build|ci 五组 + 其他兜底分组渲染,输出不含时间戳故可幂等比对;--check 只校验不写盘、无变化 skip 不写;package.json 挂 changelog 与 changelog:check 两个脚本;不进 check 链(生成内容依赖提交史,提交后天然落后一步)",
"domain": "new"
},
{
"id": "g14",
"title": "演示站侧边目录支持关键字过滤",
"motivation": "组件增至 35 个后侧边目录变长,定位区块只能靠滚动",
"acceptance": "apps/demo/main.ts 的 buildToc 前置搜索框,输入按标题子串过滤目录链接(无匹配给空态提示),Esc 清空;不破坏 IntersectionObserver 高亮;npm run check 全绿",
"status": "done",
"notes": "buildToc 顶部加 type=search 输入框,input 事件按标题子串小写匹配过滤(hidden 属性 + .toc-link[hidden] 覆盖 display:block),无匹配显示 .toc-empty 空态,Esc 清空后恢复全量;IntersectionObserver 高亮逻辑未改动;样式写在 apps/demo/build.mjs 模板内",
"domain": "tune"
},
{
"id": "g15",
"title": "toast 悬停暂停自动关闭",
"motivation": "toast 到点即消失,用户来不及读完长文案;悬停暂停是主流反馈组件的常规可用性细节",
"acceptance": "showToast 悬停时暂停倒计时、移出后按剩余时长继续;duration 语义不变;manifest 与 demo 说明同步;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g16",
"title": "image 加载失败占位",
"motivation": "图片地址失效时显示破图图标,观感差且无提示;需要统一的失败占位兜底",
"acceptance": "img 加载失败自动替换为虚线占位块,fallback 可自定义文案;样式只消费 --ua-* 令牌;manifest/demo 同步;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g17",
"title": "select 键入字母快跳选项",
"motivation": "选项多时纯方向键逐个移动太慢,主流下拉都支持键入首字母循环跳转",
"acceptance": "select 展开时键入可打印字符循环聚焦 label 前缀匹配项(不区分大小写),1 秒无输入重置缓冲;既有 ↑↓/Home/End 行为不变;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g18",
"title": "textField 可清空按钮",
"motivation": "长文本输入后逐字删除太慢,主流输入框都提供 clearable 一键清空",
"acceptance": "clearable 开启后有值时显示清空按钮,点击清空并同步信号与 onInput;空值自动隐藏;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g19",
"title": "textarea 字数统计",
"motivation": "多行输入常配字数上限,用户需要 n/limit 实时反馈而不是提交后才报错",
"acceptance": "maxCount 传入后 textarea 设原生 maxlength 并在下方附加块显示实时计数,与外部写回同步;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g20",
"title": "inputNumber 长按连发步进",
"motivation": "大步长调节时反复点按钮太累,主流数字输入都支持长按持续步进",
"acceptance": "按住 −/+ 按钮 400ms 后每 100ms 连续步进,松开或移出即停;单击行为不变;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g21",
"title": "pagination 跳页输入框",
"motivation": "页数多时逐页点击太慢,主流分页器都提供直接跳页入口",
"acceptance": "jumpable 开启且总页数 > 1 时末尾显示页码输入 + 总页数,回车跳转并钳制合法范围;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g22",
"title": "menu 键入字母快跳菜单项",
"motivation": "菜单项多时方向键逐个移动太慢,与 select 的键入快跳对齐保持组件族一致性",
"acceptance": "menu 展开时键入可打印字符循环聚焦 label 前缀匹配项(忽略大小写),1s 无输入重置;既有导航不变;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g23",
"title": "tabs 键盘 Home/End 跳转",
"motivation": "页签多时方向键逐个移动太慢,与 select/menu 的 Home/End 行为对齐",
"acceptance": "tabs 聚焦时 Home 跳首个可用页签、End 跳末个可用页签并激活;方向键行为不变;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节;Home/End 直达首末启用页签,方向键环形逻辑不变",
"domain": "tune"
},
{
"id": "g24",
"title": "新增 list 列表组件",
"motivation": "皮肤包缺通用列表容器,审批/消息/搜索结果等场景只能手写 div 拼装,样式与令牌脱节",
"acceptance": "list({ items, header, footer, bordered, emptyText, onItemClick }) 渲染条目 avatar/title/description/extra;空数组回落 empty;组件数 38→39 三处计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;items 为空回落 empty,onItemClick 让条目可点击",
"domain": "new"
},
{
"id": "g25",
"title": "令牌查询能力:ua tokens + MCP get_tokens",
"motivation": "Agent 与人都得翻 tokens.json 才知道有哪些 --ua-* 变量,容易凭记忆写错名;llms-full 里还把 textMuted 当成变量名(实际是 text-muted)",
"acceptance": "ua tokens 列出全部令牌(颜色带暗色值)、--json 输出数组;MCP 新增 get_tokens 工具;llms-full 令牌名统一 kebab-case;npm run check 全绿",
"status": "done",
"notes": "自补:工具链改进;CLI 与 MCP 共用 loadTokenList 单一来源",
"domain": "tooling"
},
{
"id": "g26",
"title": "verify 增加令牌引用校验",
"motivation": "皮肤样式表写错令牌名(如 --ua-color-textt)不会报错、静默失效;#hex 门禁只管硬编码,管不到拼错变量",
"acceptance": "var(--ua-*) 必须是 tokens.css 已定义或组件自设变量,否则 verify 失败;ua tokens / llms-full 令牌清单统一取 tokens.css(补全字号与复合间距);npm run check 全绿",
"status": "done",
"notes": "自补:门禁改进;负向测试 var(--ua-color-textt) 已被拦截",
"domain": "tooling"
},
{
"id": "g27",
"title": "弹层 Esc 栈:多弹层只关最上层",
"motivation": "modal 与 drawer 各自往 document 挂 keydown,叠加打开时一次 Esc 会把所有弹层全关;实例销毁也不摘监听",
"acceptance": "modal/drawer 共用 overlay.ts 的 Esc 栈,后开先关;栈空自动摘全局监听;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节;去掉两处各自挂的 document keydown",
"domain": "tune"
},
{
"id": "g28",
"title": "Esc 逐层关闭:浮层阻止冒泡",
"motivation": "弹窗内的 select/menu/popover 展开时按 Esc,会先收下拉再冒泡到 document 的 Esc 栈,导致弹窗一起被关——一次 Esc 关两层",
"acceptance": "menu/select/popover/tooltip/image 的 Esc 处理各自 stopPropagation,只关本层;演示页在弹窗内放下拉可复现逐层关闭;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节;演示页弹窗内加下拉可复现",
"domain": "tune"
},
{
"id": "g29",
"title": "新增 carousel 轮播组件",
"motivation": "组件库缺轮播,首页 banner/图片墙只能外部引库,样式与令牌体系脱节",
"acceptance": "carousel({ items, index, autoplay, height, label, onChange }) 横排位移 + 指示点/箭头/←→ 键切换 + 悬停暂停 + 环形回绕;组件数 39→40 三处计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;index 信号可外部受控,内部信号兜底",
"domain": "new"
},
{
"id": "g30",
"title": "check 链并入 demo 构建 + 产物锚点校验",
"motivation": "npm run check 不重建 apps/demo/dist/demo.html,改了演示页门禁照样全绿,文档站 iframe 拿到的是旧产物;verify 只查 main.ts 源码锚点,查不到产物缺块",
"acceptance": "check 链为 build → demo → style → typecheck → smoke → index → verify → lint-iterations;verify 校验产物含全部 sec-* 锚点(负向删除锚点即失败);文档四处链路描述同步;npm run check 全绿",
"status": "done",
"notes": "自补:门禁改进;文档四处链路描述同步",
"domain": "tooling"
},
{
"id": "g31",
"title": "新增 tree 树形组件",
"motivation": "文件树/组织架构/权限勾选等场景缺组件,只能手写嵌套 DOM,展开态与键盘可达性都要重造",
"acceptance": "tree({ nodes, expanded, selected, defaultExpandAll, onSelect }) 递归渲染 + 箭头展开收起 + 点选 + Enter/→/← 键盘 + 禁用不响应;组件数 40→41 三处计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;selected/expanded 信号均可外部受控",
"domain": "new"
},
{
"id": "g32",
"title": "tree 补 ↑↓/Home/End 行间导航",
"motivation": "tree 只支持 Enter/→/←,无法用键盘在节点间移动焦点,键盘用户仍得靠鼠标找行",
"acceptance": "↑↓ 在当前可见节点间移动、Home/End 到首末行,只改焦点不改选中,收起分支的子节点不参与遍历;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节;NodeList 需 Array.from 展开(downlevel 迭代)",
"domain": "tune"
},
{
"id": "g33",
"title": "新增 upload 文件选择组件",
"motivation": "表单里选附件只能裸写 input[type=file],样式脱节、无拖拽、无已选清单,也没有统一的 onChange 出口",
"acceptance": "upload({ accept, multiple, hint, disabled, onChange }) 支持点击/拖拽/键盘选择、清单逐个移除、同名同大小去重、disabled 全禁;组件数 41→42 三处计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;不内置上传请求,onChange 回传副本",
"domain": "new"
},
{
"id": "g34",
"title": "ua info 支持组件 id 查询",
"motivation": "ua info 只认包名,Agent 按组件名查(如 ua info list / MCP get_component(list))直接 not found,只能翻整包清单;输出还是原始 JSON,抄参数得自己解析",
"acceptance": "ua info <组件id> 输出 props 表 + 步骤 + 示例,ua info <包名> 出摘要且 --json 保留原始清单;MCP get_component 支持组件 id(返回 package + component);npm run check 全绿",
"status": "done",
"notes": "自补:Agent 查询体验;--json 保留原始清单",
"domain": "tune"
},
{
"id": "g35",
"title": "新增 notification 通知卡片",
"motivation": "toast 是右上角轻量瞬时提示,装不下「标题 + 描述 + 操作按钮 + 需读完」的通知场景,业务侧只能自己写浮层",
"acceptance": "notification({ title, description, variant, duration, closable, action }) 右下角堆叠(与 toast 分区)、超出 3 条挤掉最早、返回 { el, close }、duration 0 常驻;组件数 42→43 三处计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;与 toast 分区避免视觉冲突",
"domain": "new"
},
{
"id": "g36",
"title": "新增 result 结果页组件",
"motivation": "提交成功/校验失败/权限不足这类终态页每次手写图标+标题+按钮,语义色和留白都不统一",
"acceptance": "result({ status, title, description, extra }) 四档语义色 + 居中终态布局 + role=\"status\";组件数 43→44 三处计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充",
"domain": "new"
},
{
"id": "g37",
"title": "llms-full 增加组合配方章节",
"motivation": "清单只能描述单组件参数,Agent 拼多组件(表单+校验+提交反馈、表格+分页+批量、危险操作确认)时没有权威范例,容易各写一套",
"acceptance": "packages/tools/cli/recipes.md 作为唯一来源,ua docs 时追加到 llms-full.txt 末尾;缺少文件时不报错;npm run check 全绿",
"status": "done",
"notes": "自补:Agent 文档;配方文件缺失时静默跳过",
"domain": "tooling"
},
{
"id": "g38",
"title": "select 支持 searchable 搜索",
"motivation": "选项超过十个时只能靠键入首字母快跳,看不到筛掉的候选;业务常自己再包一层搜索框,交互割裂",
"acceptance": "select({ searchable: true }) 弹层顶部带搜索框,打开即聚焦、实时包含匹配、无命中显示「无匹配选项」,↑↓/Home/End 只在可见项间移动;README 分组行补齐新组件;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件可用性细节",
"domain": "tune"
},
{
"id": "g39",
"title": "统一键盘焦点可见性 + 可访问性约定入文档",
"motivation": "多数组件为自绘样式写了 outline:none,只有 14 处补了 focus-visible 光环,漏掉的组件键盘 Tab 过去完全看不出焦点在哪",
"acceptance": "ui.css 顶部统一 [class^=ua-]:focus-visible 描边兜底(不引入硬编码色);README 增「可访问性约定」章节;npm run check 全绿",
"status": "done",
"notes": "自补:a11y;避免组件各自 outline:none 丢失焦点指示",
"domain": "tooling"
},
{
"id": "g40",
"title": "修表单双提交:button 显式 type + Enter 提交",
"motivation": "button() 不写 type,放进 <form> 里默认是 submit:点提交会先跑 click 回调再触发原生 submit(onSubmit 触发两次),点「重置」还会顺带提交表单",
"acceptance": "ButtonProps 增 type(缺省 button);form 内提交按钮用 submit、重置用 button;单行输入框 Enter 等同提交且 textarea 不受影响;文档同步;npm run check 全绿",
"status": "done",
"notes": "自补:缺陷修复;textarea 保留换行语义",
"domain": "tune"
},
{
"id": "g41",
"title": "verify 校验 README 分组行",
"motivation": "README「分组:」行是第二份组件清单,新增组件只改 ua:components 区块时分组行会静默过期(本轮之前已漏 6 个组件)",
"acceptance": "verify 解析分组行并比对清单集合,漏写/多写均报错(formItem 作为别名放行);负向删除 tree 即失败;npm run check 全绿",
"status": "done",
"notes": "自补:门禁改进;把手工维护的第二份清单纳入自动校验",
"domain": "tooling"
},
{
"id": "g42",
"title": "pagination 支持每页条数切换",
"motivation": "分页器只能固定 pageSize,列表页要改条数得业务另做下拉并自算总页数;且每次翻页都在重建跳页输入框",
"acceptance": "pagination({ pageSizes, onPageSizeChange }) 渲染条数切换器、切换后回第 1 页并按新条数重算总页数;跳页区只创建一次;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;新增 sec-pagination 演示区块",
"domain": "tune"
},
{
"id": "g43",
"title": "select/menu/popover/image 的 Esc 接入统一弹层栈",
"motivation": "四个弹层组件各自监听 document 的 Esc:多层叠加(如 popover 内再开 image 预览)时一次 Esc 全部关闭,与 modal/drawer 的 pushOverlay 栈行为不一致",
"acceptance": "四个组件展开期入 Esc 栈、收起即释放;多层叠加时 Esc 后进先出逐层关闭;焦点归还触发器;npm run check 全绿",
"status": "done",
"notes": "自补:一致性修复;复用 overlay.ts 的 pushOverlay 栈",
"domain": "tune"
},
{
"id": "g44",
"title": "accordion 标题行方向键导航",
"motivation": "标题是原生 button 只有 Enter/Space,行间移动要靠 Tab 逐个跳,与 tabs/tree 的 ↑↓/Home/End 键盘约定不一致",
"acceptance": "↑↓ 在标题行间环形移动、Home/End 首尾直达,均跳过禁用行;焦点不在标题行不拦截;npm run check 全绿",
"status": "done",
"notes": "自补:可达性对齐;复用环形回绕取模模式",
"domain": "tune"
},
{
"id": "g45",
"title": "modal/drawer/image 打开时锁定背景滚动",
"motivation": "模态弹层开着时背景页面仍可滚动,长页面里滚动会带着遮罩底下内容跑;非模态弹层(select/menu/popover/tooltip)不受影响",
"acceptance": "overlay.ts 新增 lockScroll 引用计数:嵌套叠加只由最后一层还原 body;modal/drawer/image 预览接入,关闭即解锁;npm run check 全绿",
"status": "done",
"notes": "自补:缺陷修复;引用计数避免多层同开时提前解锁",
"domain": "tune"
},
{
"id": "g46",
"title": "新增 transfer 穿梭框组件",
"motivation": "组件库缺常规的双栏搬运组件:批量选择/授权/字段启用这类场景,调用方目前只能用两个 list + 手写搬运逻辑,勾选态与全选三态都要重复实现",
"acceptance": "新增 transfer(两栏由 dataSource 按 targetKeys 派生、逐栏搜索、全选含半选、禁用条目不参与、信号双向绑定);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;归属只存 key,搬运只改 targetKeys,不删条目",
"domain": "new"
},
{
"id": "g47",
"title": "新增 cascader 级联选择组件",
"motivation": "省市区、组织层级这类逐级下钻的选择场景目前只能靠多级 select 拼接,调用方得自己维护中间态与列联动",
"acceptance": "新增 cascader(多列联动、悬停下钻、点击提交、changeOnSelect、全键盘、Esc 入栈);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;浏览路径与已提交路径分离,避免悬停污染受控值",
"domain": "new"
},
{
"id": "g48",
"title": "新增 splitter 可拖拽分栏容器",
"motivation": "仪表盘/IDE 式布局需要可调整比例的分栏,库内没有任何布局基础件,调用方只能自己写拖拽与边界夹逼",
"acceptance": "新增 splitter(指针拖拽、键盘微调、双击复位、min 夹逼、sizes 信号受控、横纵两向);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:布局基础件;两栏之和恒定,只改相邻一对",
"domain": "new"
},
{
"id": "g49",
"title": "新增 autoComplete 自动补全组件",
"motivation": "搜索式输入(组件名、城市、标签)目前只能用 select 的 searchable 近似,无法做到「边打边联想 + Enter 取首项」的输入法式体验",
"acceptance": "新增 autoComplete(输入即过滤、↑↓ 环形高亮、Enter 选中、Esc 入栈、options 函数式远程联想、空态提示);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;mousedown+preventDefault 保住输入框焦点",
"domain": "new"
},
{
"id": "g50",
"title": "新增 datePicker 日期选择组件",
"motivation": "表单体系里日期只能填文本,缺一个受控的单月网格选择器;min/max 约束与键盘逐日导航是排期/账期场景的刚需",
"acceptance": "新增 datePicker(单月网格、翻月、今天、min/max 闭区间、←→↑↓/PageUp/PageDown/Enter、Esc 入栈、value 信号);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:表单组件补缺;跨月自动翻页,漫游 tabindex 只有焦点日可 Tab 进入",
"domain": "new"
},
{
"id": "g51",
"title": "新增 backTop 返回顶部按钮",
"motivation": "长文档/长表格页面缺少回顶入口,用户只能拖滚动条;进度环还能给出「还剩多少没看」的定位感",
"acceptance": "新增 backTop(阈值浮现、滚动进度环、平滑回顶且尊重 reduced-motion、支持内滚容器 target);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;进度环用 conic-gradient 内联写角度,不引入新令牌",
"domain": "new"
},
{
"id": "g52",
"title": "新增 colorPicker 颜色选择组件",
"motivation": "主题定制、标签配色等场景只能填十六进制文本框;缺省色板应直接消费 --ua-* 令牌,才能天然支持暗色切换",
"acceptance": "新增 colorPicker(预设色板 + 原生取色器 + 手输 CSS 颜色串、值信号、Esc 入栈、触发器实时预览);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;预设缺省为令牌色而非硬编码 hex",
"domain": "new"
},
{
"id": "g53",
"title": "新增 tour 引导漫游组件",
"motivation": "新用户首次进入复杂页面缺少分步指引;库内已有 overlay/position 能力,聚光挖洞式引导是最自然的组合产物",
"acceptance": "新增 tour(返回 {el,start,close} 句柄、按选择器聚光、卡片自动选方位、滚动重算、末步完成/跳过区分回调、Esc 入栈、焦点归还);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;聚光用超大 box-shadow 挖洞,省掉独立遮罩层",
"domain": "new"
},
{
"id": "g54",
"title": "新增 watermark 水印组件",
"motivation": "内部系统的防截图外传诉求没有落地件;水印颜色必须能跟随暗色主题,所以要从 --ua-* 令牌解析出实色再喂给 canvas",
"acceptance": "新增 watermark(props, ...children)(canvas 平铺单元、令牌色解析、多行/旋转/间距可配、覆盖层 pointer-events:none);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;canvas 不认 CSS 变量,先 getComputedStyle 解析",
"domain": "new"
},
{
"id": "g55",
"title": "新增 checkboxGroup 多选组组件",
"motivation": "权限勾选、标签多选这类场景只有单个 checkbox,调用方要自己拼数组、自己算全选与半选、自己处理「最多选 N 项」",
"acceptance": "新增 checkboxGroup(数组信号、min/max 禁用边界、checkAll 半选、纵横向、整组禁用);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:表单组件补缺;边界用禁用表达,不抛错不静默回滚",
"domain": "new"
},
{
"id": "g56",
"title": "ua style 增加单文件 200 行门禁",
"motivation": "CODING.md 写明「单文件尽量 < 200 行」,但一直是人工约定;随着组件数涨到 54,靠人盯不住,应把这条规范变成可执行门禁",
"acceptance": "cmdStyle 增加 sizeCheckFile:只约束分层包 src 内实现文件、types.ts 与 index.ts 豁免;正向全绿、负向(造 210 行文件)退出码 1;AGENTS/CODING/README 同步该门禁说明;npm run check 全绿",
"status": "done",
"notes": "自补:工具链改进;只约束包内实现文件,cli 自身与 demo 单文件不受限",
"domain": "tooling"
},
{
"id": "g57",
"title": "form 校验支持异步规则(validator 返回 Promise)",
"motivation": "邀请码占用、用户名唯一性这类校验必须打后端;原来 validator 只接受同步返回值,调用方只能绕过 form 自己写提交拦截",
"acceptance": "FormRule.validator 允许返回 Promise;formItem 返回 validateAsync 并按轮次丢弃过期结论;form 提交逐项 await、期间禁用提交按钮、有错聚焦首错;同步 validate 行为不变;demo 增异步字段;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件增强;同步路径不被 Promise 拖慢,异步路径防乱序覆盖",
"domain": "tooling"
},
{
"id": "g58",
"title": "新增 affix 固钉组件",
"motivation": "文档站目录、工具栏、筛选条都需要「滚过就贴顶」;直接用 position:sticky 常受父级 overflow 影响,需要占位 + fixed 的可靠实现",
"acceptance": "新增 affix(props, ...children)(阈值固定、外层等高占位不塌陷、宽度横向随容器自适应、支持内滚容器 target);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;固定态只在状态切换时重写样式,滚动中仅刷新宽度",
"domain": "new"
},
{
"id": "g59",
"title": "verify 检测 README 组件清单重复项",
"motivation": "README 区块与分组行原来用 Set 去重后比对,同一组件写两遍也能通过;重复项会让「组件数」与分组导航对不上,属于纯文档漂移",
"acceptance": "checkReadme 保留原始顺序并检测重复:区块重复、分组行重复(整词计数,避开 formItem 命中 form)都报错;正负向验证通过;npm run check 全绿",
"status": "done",
"notes": "自补:工具链改进;分组行按整词边界计数,formItem 不被 form 误伤",
"domain": "tune"
},
{
"id": "g60",
"title": "新增 multiSelect 多选下拉组件",
"motivation": "select 只支持单选,标签多选场景只能退回 checkboxGroup(占空间)或自拼 popover;需要一个「标签式回显 + 可搜索 + 连续多选」的标准控件",
"acceptance": "新增 multiSelect(标签回显与删除、搜索过滤、点选不收起、max 禁用边界、↑↓/Enter/Esc、受控信号);manifest/docs/demo/README/计数同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充;输入框常驻不重建,避免重绘丢焦点",
"domain": "new"
},
{
"id": "g61",
"title": "tree 支持勾选多选与父子级联",
"motivation": "权限树、菜单授权这类场景必须多选;原来只有单选 selected,调用方只能自己拿 DOM 拼一套勾选逻辑",
"acceptance": "TreeProps 增加 multi/checked/onCheck;勾父选子、子全选回勾父、部分勾选半选、禁用项不参与级联;勾选不触发选中,多选下 Enter/空格切换勾选;manifest/docs/demo 同步;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件增强;key→节点/父节点索引在建树时一次建立",
"domain": "tune"
},
{
"id": "g62",
"title": "verify 校验 demo 实际调用每个组件",
"motivation": "原来只校验 section 锚点存在,理论上可以保留锚点却删掉组件调用——文档声称「有演示」而实际没渲染",
"acceptance": "verify 对每个 docs.components 条目要求 apps/demo/main.ts 出现 id( 或 id.(兼容 toast.info 这类命名空间调用);正负向验证通过;AGENTS 同步;npm run check 全绿",
"status": "done",
"notes": "自补:门禁改进;当前 56 个组件全部通过该规则",
"domain": "tooling"
},
{
"id": "g63",
"title": "card 支持标题操作区、底部操作区与悬停态",
"motivation": "card 只有 title + children,业务里最常要的「标题右侧操作 / 底部主次按钮 / 可点击卡片」都得在外部再包一层",
"acceptance": "CardProps 增加 extra / footer / bordered / hoverable;标题行改 flex 头、标题下边距移到头部(视觉不变);demo/manifest 同步;npm run check 全绿",
"status": "done",
"notes": "自补:既有组件增强;不改内部结构,只在有 title/extra 时才输出头部",
"domain": "tune"
},
{
"id": "g64",
"title": "刷新 CHANGELOG 并说明刷新时机",
"motivation": "CHANGELOG 停在 g14,之后 40+ 条迭代都没进日志;同时没人说明它为何不在 check 链里(它包含提交自身,天然自指)",
"acceptance": "npm run changelog 重新生成到最新;AGENTS 增加「变更日志」段说明生成方式与「不进 check」的原因;README 脚本清单补 changelog;npm run check 全绿",
"status": "done",
"notes": "自补:文档完善;changelog:check 只能用于发版前确认漂移,不能作为提交门禁",
"domain": "tune"
},
{
"id": "g65",
"title": "verify 校验文档 props 落到实现,并补齐漏记字段",
"motivation": "文档 props 表与实现参数是两处手写,改了参数名忘改文档时 check 全绿但 Agent 读到的是幽灵字段;反向也有漏记(image.fallback、textField.clearable、textarea.maxCount、pagination.jumpable 都没进文档)",
"acceptance": "ua verify 新增「每个文档 props 名必须在组件实现源码里出现」校验(按同名文件优先、export function 兜底定位实现);补齐四处漏记字段;负向造幽灵字段能命中报错;npm run check 全绿",
"status": "done",
"notes": "自补:门禁增强;说明性条目(...children、带括号注记)按非标识符跳过,避免误报",
"domain": "tooling"
},
{
"id": "g66",
"title": "verify 反向校验入参接口字段均已登记文档",
"motivation": "上一轮只挡了「文档有、实现无」的幽灵字段;反方向(types.ts 加了字段但文档没写)仍会漏,Agent 读文档就会少一个可用参数",
"acceptance": "ua verify 解析 src/types.ts 的 <组件>Props 字段并与文档 props 表双向比对;清单支持 propsType 覆盖接口名、false 表示无入参;补齐 form.onReset 漏记;负向注入 ghostField 能命中报错;npm run check 全绿",
"status": "done",
"notes": "自补:门禁增强;接口名默认取 PascalCase(组件 id)+Props,switchButton/dataTable 用 propsType 覆盖",
"domain": "tooling"
},
{
"id": "g67",
"title": "tag 支持 checkable 筛选片",
"motivation": "筛选场景只能用 closable 标签表达「已选」,表达不了「未选可点」;且删掉再重建节点会丢焦点",
"acceptance": "TagProps 增加 checkable/checked/onChange;渲染为 button + aria-pressed,effect 同步 class 不整块重渲染;选中态低视觉比重(accent 10% 底 + 55% 边框),hover 上浮、按压回缩、focus-visible 光环并带 reduced-motion 兜底;文档与演示补齐;npm run check 全绿",
"status": "done",
"notes": "自补:组件增强;checkable 与 closable 互斥(同时传以 checkable 为准),checked 不传则内部自持",
"domain": "tune"
},
{
"id": "g68",
"title": "alert 增加语义图标与通栏形态",
"motivation": "提示条只有文字,语义强度全靠颜色(色弱不可辨);顶部通知也做不出通栏效果,得靠调用方套壳改样式",
"acceptance": "AlertProps 增加 banner/showIcon/icon;图标纯装饰 aria-hidden 不干扰读屏播报;is-banner 去圆角、只留下边框、通栏 padding;文档 props 与手把手补齐;npm run check 全绿",
"status": "done",
"notes": "自补:组件增强;图标用字符而非外部图标集,避免引入依赖,颜色走 currentColor 跟随语义档",
"domain": "tune"
},
{
"id": "g69",
"title": "修正单击排序的升降序循环与 getPageInfo 陈旧快照",
"motivation": "补冒烟时发现两处真 bug:非 multi 的 toggleSort 每轮先清空规则,导致同一列永远停在 asc(点了变不回去);getPageInfo 只在 derive 里赋值,没先取可见行时返回陈旧/默认快照",
"acceptance": "toggleSort 保留本列既有方向再循环 asc → desc → 取消(multi 保留全部列);getPageInfo 内部先跑一次带 memo 的派生;冒烟补三条不变量(快照即时性、selection 跨筛选保留、三次点击回无序);npm run check 全绿",
"status": "done",
"notes": "自补:核心正确性;state.pagination.page 保留调用方意图、仅视图钳制,这条既有语义未改",
"domain": "tune"
},
{
"id": "g70",
"title": "冒烟补多列排序、页大小与 reset 重置断言",
"motivation": "冒烟只覆盖单列排序与越界钳制;multi 排序的规则优先级、setPageSize 回第 1 页、reset 回默认态都没有回归网,改动 patchState 时容易静默破功",
"acceptance": "smoke 新增三条不变量:multi 双列规则生成且按规则序生效(city 升序 + 同城 age 升序)、setPageSize 回第 1 页、reset 清空排序筛选并把分页回 1/20;npm run check 全绿",
"status": "done",
"notes": "自补:回归网;断言数 9 → 16",
"domain": "tune"
},
{
"id": "g71",
"title": "数据表表头补 aria-sort 三态播报",
"motivation": "排序态只有 ↑ / ↓ / ↕ 视觉箭头,读屏用户听不出当前按哪列排、什么方向;这是可排序表最基本的一条 a11y 约束",
"acceptance": "可排序 th 用 effect 同步 aria-sort(ascending / descending / none),与箭头同源;文档手把手补一条;npm run check 全绿",
"status": "done",
"notes": "自补:无障碍;非 sortable 列不写 aria-sort,避免读屏误报",
"domain": "tune"
},
{
"id": "g72",
"title": "文档站搜索框与结果区补无障碍语义",
"motivation": "生成的文档站搜索框没有可访问名(读屏只报「编辑框」),搜索后也没有结果数播报,键盘/读屏用户拿不到反馈",
"acceptance": "input 改 type=search + aria-label;nav 加 aria-label;新增 sr-only 的 role=status 区域,render 时播报「匹配 N 个包」与「无匹配结果」;npm run check 全绿",
"status": "done",
"notes": "自补:无障碍(文档站产物本身)",
"domain": "tooling"
},
{
"id": "g73",
"title": "表格分页区补导航语义与页码播报",
"motivation": "底栏只是一排按钮,读屏不知道那是分页;页码/行数变化也没有播报,筛选后用户不知道结果变了",
"acceptance": "分页容器加 role=navigation + aria-label;页码行数文本改 role=status aria-live=polite;npm run check 全绿",
"status": "done",
"notes": "自补:无障碍;live region 只在文本变化时播报,翻页/筛选都会触发",
"domain": "tune"
},
{
"id": "g74",
"title": "弹窗与抽屉补 aria-labelledby 标题关联",
"motivation": "role=dialog 但没挂可访问名,读屏只会念「对话框」,用户不知道是哪个弹窗;标题元素本身也没有 id,无法被引用",
"acceptance": "modal/drawer 用模块级序号生成唯一标题 id 并挂 aria-labelledby,多实例不撞号;npm run check 全绿",
"status": "done",
"notes": "自补:无障碍;drawer 的 aria-hidden 与 open 同步逻辑未动",
"domain": "tune"
},
{
"id": "g75",
"title": "评分组件键盘可达性与焦点保持",
"motivation": "role=slider 却不可聚焦(div 无 tabindex),keydown 只挂在首帧节点上且整体重渲染会替换节点——方向键实际按一下就失效,读屏也听不到键盘操作",
"acceptance": "整组 tabindex=0 可聚焦,keydown 每轮渲染重挂,重渲染后焦点归还并继续连按;补 focus-visible 光环;npm run check 全绿",
"status": "done",
"notes": "自补:无障碍;重渲染策略保持不变(星标少,开销可忽略)",
"domain": "tune"
},
{
"id": "g76",
"title": "文字头像补 role=img 使名称可播报",
"motivation": "无 src 的文字头像只是带 aria-label 的 span,普通元素上的 aria-label 不保证被读屏当名称读出来,等于没有可访问名",
"acceptance": "文字头像容器补 role=img,与有 src 时的 img alt 语义对齐;npm run check 全绿",
"status": "done",
"notes": "自补:无障碍;title 仍保留供鼠标悬浮提示",
"domain": "tune"
},
{
"id": "g77",
"title": "刷新 CHANGELOG 至最新提交",
"motivation": "g64 之后又落了 12 次提交,changelog:check 已经报漂移;CHANGELOG 停在旧版本等于给读者过期信息",
"acceptance": "npm run changelog 重新生成(226 提交),changelog:check 不再报漂移;npm run check 全绿",
"status": "done",
"notes": "自补:发版前刷新步骤,按 g64 约定的方式执行",
"domain": "tune"
},
{
"id": "g78",
"title": "新增 space 间距容器组件",
"motivation": "库里 56 个组件都在各自实现里写 margin/gap,「一排按钮统一间距」「工具栏两端撑开」这类布局诉求没有承载物,演示页与业务方只能重复拼 style",
"acceptance": "新增 space(props, ...children):direction / size(sm|md|lg 走令牌,数字按像素)/ align / justify / wrap / block;manifest 参数表 + 手把手步骤 + api.exports 登记;demo 有 sec-space 区块真实调用;README 计数与分组同步;npm run check 全绿",
"status": "done",
"notes": "自补:布局基础件缺失;间距一律走 --ua-space-* 令牌,几何用内联 style 不进皮肤样式表",
"domain": "new"
},
{
"id": "g79",
"title": "数据表可排序表头补键盘可达与列作用域",
"motivation": "表头只绑了 onClick,键盘用户聚焦不到、也按不出来——README 声称「关键交互都有键盘等价」,表格排序是唯一漏网的;th 还缺 scope=col,读屏无法把列标题与数据格关联",
"acceptance": "可排序 th 进 Tab 序(tabindex=0,不可排序的不抢焦点),Enter/空格 触发与点击相同的 toggleSort(空格不滚页),补 scope=col;悬停/聚焦视觉沿用既有 ua-sortable 与全局 focus-visible;dataTable 仍 ≤ 200 行;npm run check 全绿",
"notes": "自补:无障碍;排序入口收敛为单个 toggle 闭包,鼠标与键盘同源,不改核心签名",
"status": "done",
"domain": "tune"
},
{
"id": "g80",
"title": "通知悬停/聚焦暂停自动关闭计时",
"motivation": "duration 一设定就是定时器到点即退场,鼠标悬停正在读的卡片照样消失(WCAG 2.2.1「计时可调」);通知恰恰是长篇描述,读到一半被收走比不自动关更烦",
"acceptance": "pointerenter / focusin 暂停并扣减已流逝时间,pointerleave / focusout 按剩余毫秒续计;duration 为 0 时行为不变;关闭仍先清计时器;文档与演示描述同步;npm run check 全绿",
"notes": "自补:无障碍;不改签名,remaining 时间在组件内折算",
"status": "done",
"domain": "tune"
},
{
"id": "g81",
"title": "树形组件改用 roving tabindex",
"motivation": "每行都写死 tabindex=0,键盘用户从树里穿过去要按 N 次 Tab(20 个节点就是 20 次),违反 WAI-ARIA 树模式「整棵树一个 Tab 停点、行间用方向键」的要求",
"acceptance": "只有当前行 tabindex=0 其余 -1;焦点落到哪行停点就跟到哪行(focusin 同步),下次 Tab 直接回到该行;↑↓/Home/End 移动后停点随焦点迁移;初始停点为首个未禁用根节点;tree ≤ 200 行;npm run check 全绿",
"notes": "自补:无障碍;keyOf 抽出复用,去掉 keydown 里的内联查找",
"status": "done",
"domain": "tune"
},
{
"id": "g82",
"title": "文件选择补类型/体积/数量校验与拒收提示",
"motivation": "accept 只设到原生 input 上,拖拽进来的文件完全绕过校验;也没有体积与数量上限,超限只能靠调用方在 onChange 里事后过滤,用户看不到为什么文件没进清单(静默丢弃)",
"acceptance": "新增 maxSize(字节)/ maxCount(多选上限)两个可选参数;accept 支持 .ext / type/subtype / type/* 且对拖拽同样生效;被拒文件不进清单并在 role=status 提示行写明原因(类型不符 / 超上限 / 数量已满);不改onChange 语义;文档与演示同步;npm run check 全绿",
"notes": "自补:可用性;校验入口收敛到 add(),点选与拖拽共用同一条路径",
"status": "done",
"domain": "tooling"
},
{
"id": "g83",
"title": "开关改用 switch 语义并补可访问名兜底",
"motivation": "开关底层是 input type=checkbox,读屏一律播报成「复选框」,与视觉上的开关语义不符;不留 label 时更是完全没有可访问名(只有一个匿名复选框)",
"acceptance": "input 补 role=switch(选中态沿用原生 checked 映射);新增可选 ariaLabel,缺省取 label,纯图标开关也有可访问名;文档 props 与演示(无文案开关实例)同步;npm run check 全绿",
"notes": "自补:无障碍;沿用原生 checkbox 的 Space 切换行为,不重造键盘交互",
"status": "done",
"domain": "tune"
},
{
"id": "g84",
"title": "数据表定宽列支持拖拽调宽",
"motivation": "列宽只能在定义时写死,长内容把列撑开就再也调不回去;表格库缺这个基本操作,用户只能改代码重发版",
"acceptance": "width 为数字的列在表头右缘出现手柄:拖动实时改对应 <col> 宽度(下限 56px,拖出表头仍跟手),聚焦手柄后 ← → 每次 ±8px,双击复位到声明宽度;拖拽/双击不触发排序;拖拽期间全局禁选文本;样式走令牌且有 reduced-motion 兜底;dataTable ≤ 200 行;npm run check 全绿",
"notes": "自补:可用性;拖拽与键盘共用 apply 单一写宽入口,逻辑拆到 columnResize.ts 免撑大 dataTable",
"status": "done",
"domain": "tune"
},
{
"id": "g85",
"title": "进度条补可访问名与语义色",
"motivation": "定量态只有 aria-valuenow,没有名称——读屏只念「50%」,不知道是什么在 50%;且除 accent/success 外没有失败告警色,同步失败也只能显示成一片蓝",
"acceptance": "新增 label(可访问名,不确定态缺省「进行中」兜底)与 variant(default/success/warning/danger);语义色规则置于 .ua-done 之后同特异性胜出,满格不回退 success;样式只消费 --ua-* 令牌;文档 props/steps 与演示同步;npm run check 全绿",
"notes": "自补:无障碍 + 视觉语义;CSS 层用特异性解决 variant 与满格 success 的冲突,不加 JS 分支",
"status": "done",
"domain": "tune"
},
{
"id": "g86",
"title": "弹层外部点击监听改为按需挂载",
"motivation": "8 个弹层(select / multiSelect / cascader / datePicker / colorPicker / autoComplete / menu / popover)在构造时就在 document 上挂一个永不移除的 click 监听:组件从页面移除后监听仍常驻(SPA 里实例越多越积压),且各写一份重复逻辑;popover 里还残留一个空的 keydown 监听",
"acceptance": "overlay 新增 bindOutside(root, isOpen, close):仅展开期间挂全局 click、收起即摘,内部点击直接返回;8 个弹层全部改用它并删掉各自的重复实现;popover 删掉空 keydown,点弹层内部不再误关;npm run check 全绿",
"notes": "自补:稳健性 + 去重;展开动作本身也是 click,靠 root.contains 卫语句避免刚开就被关",
"status": "done",
"domain": "tune"
},
{
"id": "g87",
"title": "轮播补焦点暂停与轮播语义",
"motivation": "自动播放只在 pointerenter 暂停——键盘用户没有 hover,Tab 进轮播后幻灯片照样自己换页,正在读的内容被抽走(WCAG 2.2.2);区域与幻灯片也只有笼统的 aria-label,读屏不知道这是轮播的第几张",
"acceptance": "焦点进入(focusin)与移出(focusout)同样暂停自动播放,两者都不在才前进;区域补 aria-roledescription=轮播,每张补 aria-roledescription=幻灯片 且标签为「第 x 张,共 n 张」;演示描述同步;npm run check 全绿",
"notes": "自补:无障碍;hover 与 focus 分开计数,避免一个 pointerleave 就误解除暂停",
"status": "done",
"domain": "tune"
},
{
"id": "g88",
"title": "数据表支持导出 CSV",
"motivation": "表格能筛能排却导不出来,用户只能截图或手抄;导出还必须导「筛选+排序后的全量行」而不是当前页,但内核只有 getVisibleRows()(已分页裁剪),UI 层拿不到全量派生行",
"acceptance": "内核 getVisibleRows(paged=false) 跳过 pagination 阶段并新增 getAllRows(),派生上下文带 paged 标记(缺省分页),分页快照不受影响;dataTable 新增 exportable / exportName,工具栏按钮导出筛选+排序后的全量行、单元格走 format;CSV 带 BOM + CRLF、逗号引号换行正确转义;冒烟补 4 条不变量;npm run check 全绿",
"notes": "自补:功能缺口;核心只加可选参数与一个实例方法,不动既有签名",
"status": "done",
"domain": "tune"
},
{
"id": "g89",
"title": "演示产物启用保守压缩",
"motivation": "demo.html 单文件 278 KB,其中 JS 近 190 KB 全是未压缩的空白与冗余括号;演示页是给别人点开看的产物,体积直接影响加载体验,而全量 minify 会把标识符改短、不利于现场排查",
"acceptance": "esbuild 开启 minifyWhitespace + minifySyntax(保留标识符),产物仍可读;demo.html 由 278 KB 降到 205 KB(JS 190 KB → 120 KB);产物语法可被 node --check 通过,sec-space / ua-col-grip 等新增内容仍在;npm run check 全绿",
"notes": "自补:构建改进;刻意不用全量 minify,平衡体积与可读性",
"status": "done",
"domain": "tune"
},
{
"id": "g90",
"title": "刷新 CHANGELOG 至最新提交",
"motivation": "g89 之后又累积了十余次迭代提交,changelog:check 已报漂移;CHANGELOG 停在旧版本等于给读者过期信息",
"acceptance": "npm run changelog 重新生成(253 提交),changelog:check 不再报漂移(提交本条后按既有约定会再差一条,属正常);npm run check 全绿",
"notes": "自补:发版前刷新步骤,按 g64/g77 的既定方式执行",
"status": "done",
"domain": "tune"
},
{
"id": "g91",
"title": "表单字段补辅助说明 hint",
"motivation": "formItem 只有 label 与错误位,没有「格式提示」的位置:调用方只能把说明塞进 placeholder 或标签里,一报错提示就被替换得无处可看,用户不知道正确格式是什么",
"acceptance": "FormItemProps 新增可选 hint,渲染在控件下方;校验失败时 hint 让位(hidden)只留错误文案,校验通过自动恢复;样式走令牌;文档参数表(formItem 前缀)与演示同步;npm run check 全绿",
"notes": "自补:表单可用性;不改 formItem 返回句柄与 form 聚合逻辑",
"status": "done",
"domain": "tune"
},
{
"id": "g92",
"title": "徽标支持数字角标与纯红点",
"motivation": "badge 只能塞一段文字,做「未读 5 条」「99+」「有更新的小红点」时得自己拼 span + 手写样式,红色点还会硬编码颜色绕过令牌",
"acceptance": "BadgeProps 新增 count / max / dot,label 改为可选;count 超过 max(缺省 99)显示 N+,负数归零;dot 渲染 8px 圆点并以 role=img + label 作可访问名;新增样式只消费 --ua-* 令牌;文档与演示(三种形态)同步;npm run check 全绿",
"notes": "自补:组件能力补齐;保持纯展示、无内部状态",
"status": "done",
"domain": "tune"
},
{
"id": "g93",
"title": "树形组件支持按标签搜索",
"motivation": "节点一多(如目录树、组织架构)就只能靠展开逐层找;tree 只有展开/收起与方向键,没有任何过滤入口,几十个节点等于不可用",
"acceptance": "TreeProps 新增 searchable:顶部搜索框按标签包含匹配(大小写不敏感),命中节点连同父链保留、命中枝临时强制展开,不命中整枝隐藏且不参与 ↑↓ 遍历(visibleRows 改判 [hidden]);搜索框放在 role=tree 之外避免语义混淆;文档与演示同步;tree ≤ 200 行;npm run check 全绿",
"notes": "自补:可用性;搜索只改视图显隐,不回写 expanded 信号,退出搜索即恢复用户原有展开态",
"status": "done",
"domain": "tune"
},
{
"id": "g94",
"title": "补充数据表联动导出配方",
"motivation": "本轮新增的受控 page 联动、列宽拖拽、CSV 导出三个能力只有单组件参数表,没有「怎么拼在一起」的指导;配方文件停在第 4 条,Agent 与新人拼表格联动时仍要自己试",
"acceptance": "recipes.md 新增「配方 5:数据表 + 独立分页器 + 导出」,覆盖同一 page 信号双绑、exportable 导全量行与当页的区别、拖拽只对数字列宽生效、rowKey 记账;npm run docs 后 llms-full.txt 出现该配方;npm run check 全绿",
"notes": "自补:文档完善;只写用法不碰实现,配方随 docs 注入 llms-full",
"status": "done",
"domain": "tooling"
},
{
"id": "g95",
"title": "统计数值支持倒计时模式",
"motivation": "活动剩余时间这类需求只能调用方自己 setInterval 改信号再塞进 statistic,还得自己管停表;组件本身只认一个数值信号,秒级刷新没有落脚点",
"acceptance": "StatisticProps 新增 countdown(目标时间戳毫秒),传了即每秒显示 DD 天 HH:MM:SS(不足一天省天数),value 转为可选;到点或组件被移除后自动 clearInterval,不留常驻定时器;倒计时不走滚动动画;文档与演示同步;npm run check 全绿",
"notes": "自补:功能补齐;停表条件放在 tick 内自检 isConnected,无需额外卸载钩子",
"status": "done",
"domain": "tune"
},
{
"id": "g96",
"title": "抽屉支持上下滑出方位",
"motivation": "drawer 的 placement 只有 left/right,移动端最常见的「底部弹层」只能拿 modal 或者自己写 fixed 容器凑,既丢了焦点陷阱与 Esc 栈,样式也不统一",
"acceptance": "placement 扩到 'left' | 'right' | 'top' | 'bottom';上下滑出时 width 语义变为高度、横向自动铺满(max-width 放开),入场位移走 translateY,四条方位共用同一套遮罩/焦点陷阱/Esc 栈;文档与演示(底部抽屉实例)同步;npm run check 全绿",
"notes": "自补:组件能力补齐;只加 CSS 分支与一个三元表达尺寸,逻辑零改动",
"status": "done",
"domain": "tune"
},
{
"id": "g97",
"title": "本轮收尾刷新变更日志",
"motivation": "g90 之后又落了 19 次迭代提交(space 组件、列宽拖拽、CSV 导出、树搜索、抽屉四向等),CHANGELOG 已明显落后",
"acceptance": "npm run changelog 重新生成(267 提交),changelog:check 一致(提交本条后按约定再差一条);npm run check 全绿",
"notes": "自补:发版前刷新步骤,收尾统一执行",
"status": "done",
"domain": "tune"
},
{
"id": "g98",
"title": "弹窗与抽屉补遮罩关闭开关",
"motivation": "modal/drawer 点遮罩必关,表单类弹窗填到一半误触就丢内容;调用方没有任何开关能保留 × 与 Esc 的同时禁掉遮罩关闭",
"acceptance": "ModalProps / DrawerProps 新增 closeOnOverlay(缺省 true);传 false 时点遮罩不关闭,× 与 Esc 行为不变;文档参数表与演示(防误关弹窗实例)同步;npm run check 全绿",
"notes": "自补:可用性;遮罩点击处理改成显式卫语句,语义比一长串 && 更清楚",
"status": "done",
"domain": "tune"
},
{
"id": "g99",
"title": "加载指示器补可定制播报文案",
"motivation": "spinner 的 aria-label 写死「加载中」,页面里同时有三处加载时读屏念三次一样的话,用户不知道在等什么;组件没有出口让调用方说明加载对象",
"acceptance": "SpinnerProps 新增可选 label,缺省仍是「加载中」(不传即旧行为);role=status 保持不变;文档参数与手把手步骤、演示(带 label 的实例)同步;npm run check 全绿",
"notes": "自补:无障碍;纯透传一个可选参数,行为向后兼容",
"status": "done",
"domain": "tune"
},
{
"id": "g100",
"title": "收尾刷新变更日志",
"motivation": "g97 之后又落了 6 次提交(抽屉四向、遮罩关闭开关、加载播报等),changelog:check 已报漂移",
"acceptance": "npm run changelog 重新生成(273 提交),changelog:check 一致;npm run check 全绿",
"notes": "自补:发版前刷新步骤;本条为本次唤起第 22 轮收尾",
"status": "done",
"domain": "tune"
},
{
"id": "g101",
"title": "新增锚点导航组件 anchor",
"motivation": "文档页、设置页、长表单都需要「侧边跳转 + 滚动高亮」,现在库里没有任何可复用件:demo 的侧栏目录 (buildToc) 是手写 IntersectionObserver + 类名切换,偏移量、hash 回写、禁用项、减少动效各写各的,调用方要复制一遍才用得上",
"acceptance": "新增 anchor(props)(packages/skin/ui/src/anchor.ts,< 200 行):items/href/title/disabled、offset 偏移量、container 内滚容器、onChange 回调;滚动按目标位置高亮并同步 aria-current,点击平滑定位 + replaceState 回写 hash,prefers-reduced-motion 下瞬移;types/manifest/index/ui.css/demo/README/AGENTS/ARCHITECTURE 同步,npm run check 全绿",
"notes": "自补:组件补充;样式全走令牌,选出态维持低视觉比重(accent 10% 底色 + 左侧 2px 指示条)",
"status": "done",
"domain": "new"
},
{
"id": "g102",
"title": "焦点陷阱下沉为共享助手,图片预览接上",
"motivation": "modal 与 drawer 各自复制了一份 25 行的 Tab 焦点陷阱(focusables + keydown 环形回绕),改一处漏一处;image 的全屏预览层是 role=dialog aria-modal,却完全没有焦点陷阱也没把焦点移进去,键盘用户 Tab 一下就穿到背后的页面",
"acceptance": "overlay.ts 新增 trapFocus(box, isOpen):Tab / Shift+Tab 首尾环形,有无可用焦点与焦点在容器外的两种情况都拦回;modal / drawer 删掉本地实现改为调用(行为不变);image 预览层补 tabindex=-1 + 打开即聚焦 + 全程锁焦点;npm run check 全绿",
"notes": "自补:一致性与无障碍;陷阱实现单一来源,后续新增模态层直接复用",
"status": "done",
"domain": "tune"
},
{
"id": "g103",
"title": "表格空状态区分「没数据」与「被筛掉」",
"motivation": "dataTable 不论有没有筛选词,0 行时一律显示「暂无匹配数据」:数据本身就空的时候提示「匹配」让人以为筛选还在生效,而筛选筛空的时候又不给任何出口,用户只能手动删掉输入框里的字才发现原来有数据",
"acceptance": "有筛选词时显示「没有符合「关键词」的行」并给「清除筛选」按钮(一次性清空关键字信号与内核筛选、回第 1 页);无筛选词时显示「暂无数据」且不出现按钮;关键字用受控信号持有,与筛选框同源;每行文件仍 ≤ 200 行;npm run check 全绿",
"notes": "自补:可用性;空状态行抽到 tableEmpty.ts,dataTable 顺手把 api 类型别名下沉到 types.ts 才没破 200 行",
"status": "done",
"domain": "tune"
},
{
"id": "g104",
"title": "留痕校验回查提交是否真实存在",
"motivation": "iterations.log 目前只校验字段格式:commit 随便写个哈希、或复制粘贴时抄错一位,门禁照样全绿。留痕是「这条迭代真的落过」的唯一凭证,凭证本身不可验证等于没校验",
"acceptance": "ua lint-iterations 额外做双向核对:每行 commit 必须能在 git 历史里按前缀命中,且该提交主题以 iter(<id>) 开头(兼容早期无 scope 的 iter: <id>);伪造 / 抄错的哈希报违规并退出码 1;非 git 环境(打包分发)自动跳过该维度;ITERATION.md 留痕章节写明该校验;npm run check 全绿",
"notes": "自补:门禁工具链;一次 git log 建索引,150 行留痕不额外起进程",
"status": "done",
"domain": "tooling"
},
{
"id": "g105",
"title": "标签页支持可关闭页签",
"motivation": "多开详情页 / 编辑器类界面需要「关掉这个页签」,tabs 只有 label + disabled,调用方得自己在页签头外面挂按钮,关闭按钮的 hover 显现、指示条宽度、键盘等价全得重写一遍",
"acceptance": "TabItem 新增 closable(缺省不可关),渲染 × 关闭按钮(aria-label=关闭 <label>),点击或页签聚焦后按 Delete 触发 onClose(value);指示条改为量外壳宽度,可关闭页签的高亮能盖住 ×;触摸设备(hover: none)常显 ×;表单化入参不变(closable 在项上),文档参数表/手把手/示例与演示(三张可关订单页签,关完显示空态)同步;npm run check 全绿",
"notes": "自补:组件可用性;关闭只回调不改数据,符合受控约定",
"status": "done",
"domain": "tune"
},
{
"id": "g106",
"title": "滚动监听改为卸载自清理",
"motivation": "affix / backTop / anchor / tour 四个组件都是「挂 window 或容器的 scroll+resize 监听,从不摘」:单页应用里每次进页面重建组件就多一根监听,旧闭包连同旧 DOM 一起被 window 拽住,切十次页面就十份空转的 sync 在滚动时跑",
"acceptance": "新增 watch.ts 的 watchViewport(root, host, handler):滚动 / 缩放时若 root 已脱离文档就一次性摘掉两个监听并停止回调,否则转交 handler 并首帧同步一次;四个组件改用该助手,行为与原来一致(首帧同步保留);npm run check 全绿",
"notes": "自补:一致性与泄漏治理;一处实现四处复用,后续新增长期监听组件照抄",
"status": "done",
"domain": "tune"
},
{
"id": "g107",
"title": "新增二次确认气泡 popconfirm",
"motivation": "删除 / 发布这类危险操作只有两条路:直接执行(误点就没了)或弹 modal(打断视线、还要再找一次按钮)。中间档位「就地确认」缺失,而 popover 的 open 状态是组件私有的,调用方连编程收起都做不到,没法自己拼",
"acceptance": "PopoverProps 新增受控 open 信号(不传行为不变);新增 popconfirm(props, trigger):title/description/confirmText/cancelText/variant/placement/onConfirm/onCancel/open,确定先收起再回调,取消 / 点外部 / Esc 走 onCancel,展开即聚焦「取消」;组件 58→59,manifest/docs/README/AGENTS/ARCHITECTURE/demo 同步;npm run check 全绿",
"notes": "自补:组件补充;复用 popover + button,不重复实现定位与 Esc 栈",
"status": "done",
"domain": "new"
},
{
"id": "g108",
"title": "表单提交失败给汇总条与回调",
"motivation": "提交失败时只把焦点扔到第一处错误:视觉上用户得自己数还有几处红框,读屏用户更是只听到某个输入框的 aria-invalid,完全不知道总共有几项要改;调用方也拿不到「这次失败了几项」做埋点或顶部提示",
"acceptance": "form 提交时统计不通字段数,顶部渲染 role=alert 的汇总条「有 N 项需要修改,已定位到第一处」(通过则隐藏);FormProps 新增 onError(count) 与 errorSummary(缺省 true,传 false 不插节点);文档参数表与演示说明同步;npm run check 全绿",
"notes": "自补:无障碍与可用性;汇总条与逐项红框同源,均由一次提交的校验结果推导",
"status": "done",
"domain": "tune"
},
{
"id": "g109",
"title": "提示气泡支持触摸屏点按",
"motivation": "tooltip 只挂 pointerenter / pointerleave:触摸屏上按下会触发 enter(延迟 150ms 出),抬手立刻触发 leave 收起 —— 等于气泡刚出来就被手指带走,用户根本没机会读;移动端等于该组件不可用",
"acceptance": "触摸指针(pointerType=touch)不走 hover 分支,改为按下即出场、抬手后延迟 1.2s 收起;鼠标与键盘路径行为不变;re-enter 时清掉上一轮的延迟收起计时;文档手把手与 api 说明补触摸语义;npm run check 全绿",
"notes": "自补:可用性;只在原文件加两个分支与一个计时器,未改签名",
"status": "done",
"domain": "tune"
},
{
"id": "g110",
"title": "下拉选择支持清空",
"motivation": "筛选项一旦选中就回不到「未选」:城市、负责人这类可选筛选的下拉没有清空出口,用户只能刷新页面或换一个值。textField 早有 clearable,select 一直没有对应能力",
"acceptance": "SelectProps 新增 clearable(缺省 false):有值时触发器右缘出现 ×,点它把值写成空串并回调 onChange('');收起态按 Delete / Backspace 等价清空;按钮不进 Tab 序、无值自动隐藏;文档参数表 / 手把手 / api 说明与演示(城市下拉开启清空)同步;npm run check 全绿",
"notes": "自补:可用性;复用既有 value 信号与 onChange,不新增状态",
"status": "done",
"domain": "tune"
},
{
"id": "g111",
"title": "面包屑支持超长路径折叠",
"motivation": "七层路径(首页/控制台/订单管理/华东区/2026 Q4/待发货/订单 #1024)在窄容器里会把末项当前页挤到换行甚至省略号之后,而当前页恰恰是最该看清的;breadcrumb 没有任何收窄手段",
"acceptance": "BreadcrumbProps 新增 maxItems:超出时渲染「首项 + … + 末 Math.max(2, maxItems-1) 项」,折叠按钮 aria-label 报出被折叠条数、点一下原地展开全部;展开后不再折叠,末项始终是当前页;文档参数表 / 手把手 / 演示(七层路径 maxItems=4)同步;npm run check 全绿",
"notes": "自补:可用性;渲染走 effect + replaceChildren,不重建整个 nav",
"status": "done",
"domain": "tune"
},
{
"id": "g112",
"title": "消息提示支持撤销类操作按钮",
"motivation": "「已删除 3 条记录」这种提示最常见的下一步就是撤销,现在只能干看着它 3 秒后消失,要撤销得自己去列表里找回来;toast 只有 closable 一个交互出口",
"acceptance": "ToastProps 新增 actionLabel 与 onAction:传了文案才渲染操作按钮(accent 色、hover 下划线、focus-visible 有环),点后先收起再回调;带操作按钮的 toast 接收指针事件(原来只有 closable 才接收),悬停暂停逻辑不变;文档参数表 / 手把手 / api 说明与演示(「带撤销」按钮,5s)同步;npm run check 全绿",
"notes": "自补:可用性;沿用现有 dismiss,不新增公开 API",
"status": "done",
"domain": "tune"
},
{
"id": "g113",
"title": "吸顶容器支持贴底固定",
"motivation": "底部操作条(保存 / 取消 / 合计金额)是长表单与详情页的刚需,affix 只会贴顶:调用方得自己监听滚动改 position,或者干脆让操作条跟着内容滚走,用户翻到表单底部才发现按钮在屏幕外",
"acceptance": "AffixProps 新增 position(top 缺省 / bottom):贴底时占位容器底边越过「视口底 - offset」才固定,内联边距写 bottom 并清空 top,取消固定时两边都还原;贴顶行为不变;文档参数表 / 手把手 / api 说明同步;npm run check 全绿",
"notes": "自补:组件可用性;只在 sync 里加一条判定与一个样式分支",
"status": "done",
"domain": "tune"
},
{
"id": "g114",
"title": "收尾刷新变更日志",
"motivation": "g100 之后又落了 26 次提交(锚点导航、焦点陷阱下沉、留痕真实性校验、可关闭页签、二次确认气泡、表单汇总条、下拉清空、面包屑折叠、toast 撤销、贴底固钉等),changelog:check 已报漂移",
"acceptance": "npm run changelog 重新生成(301 提交),changelog:check 与提交史一致;npm run check 全绿",
"notes": "自补:发版前刷新步骤;本条为本次唤起收尾轮",
"status": "done",
"domain": "tune"
},
{
"id": "g115",
"title": "新增月历组件 calendar",
"motivation": "排期表、值班表、日程页需要「整月网格一直看得见」,datePicker 只能弹层选一天且收起来就没了;调用方要自己算月初留白、当月天数、今天与选中的标记,还要补键盘漫游,一写就是一百多行重复逻辑",
"acceptance": "新增 calendar(props)(< 200 行):value/month 信号、marker 每日标记(文案同时进 aria-label)、onSelect / onMonthChange;月初留白与天数计算抽到 dateKit.ts,datePicker 改为共用同一份(消除两处日期工具);组件 59→60,types/index/css/manifest/docs/README/AGENTS/ARCHITECTURE/demo 同步;npm run check 全绿",
"notes": "自补:组件补充 + 派生语义单一来源(日期工具下沉 dateKit)",
"status": "done",
"domain": "new"
},
{
"id": "g116",
"title": "抽屉支持非模态模式",
"motivation": "「一边看列表一边改右侧详情」是后台最常见的交互,但 drawer 只有模态一档:打开就盖一层遮罩、锁住背景滚动、把焦点困在面板里,主视图彻底不可用;调用方只能拿 fixed 的 div 自己拼一个假抽屉",
"acceptance": "DrawerProps 新增 modal(缺省 true):false 时遮罩透明且 pointer-events: none、面板自身可交互并带阴影、不锁背景滚动、不启用 Tab 焦点陷阱、aria-modal=false、点外部不算关闭(× 与 Esc 仍可用);文档参数表 / 手把手与演示(非模态检查器)同步;npm run check 全绿",
"notes": "自补:组件可用性;沿用现有遮罩节点加一个 is-plain 分支,不新增 DOM 结构",
"status": "done",
"domain": "tune"
},
{
"id": "g117",
"title": "指示条与滑块跟随尺寸重算",
"motivation": "tabs 的滑动指示条与 segmented 的滑块都只在「激活项变化」和挂载后一帧各量一次:窗口缩放、侧栏展开收起、字体调大之后,指示条宽度就停在旧值上,明显错位;这类「布局变了但没重算」的问题用户只会觉得组件坏了一半",
"acceptance": "watch.ts 新增 trackSize(root, handler):用 ResizeObserver 观察根节点尺寸,变化即回调,节点脱离文档时自查并断开(无 ResizeObserver 的环境回落成 window resize);tabs / segmented 接入,指示条与滑块在容器变宽变窄后保持对齐;npm run check 全绿",
"notes": "自补:正确性;与 watchViewport 同一处收口,后续布局敏感组件照抄",
"status": "done",
"domain": "tune"
},
{
"id": "g118",
"title": "收尾再刷变更日志",
"motivation": "g114 之后又落了 4 次提交(新增 calendar 月历、抽屉非模态、trackSize 尺寸重算等),changelog:check 再次报漂移",
"acceptance": "npm run changelog 重新生成(309 提交),与提交史一致;npm run check 全绿",
"notes": "自补:发版前刷新步骤;本次唤起收尾轮",
"status": "done",
"domain": "tune"
},
{
"id": "g119",
"title": "空状态支持自定义图标",
"motivation": "empty 的图标位写死成 ⌀,业务方几乎都会换成自己的插图(搜索无结果用放大镜、空订单用纸箱);现在只能在外面再包一层插画 div,既影响居中节奏又和组件内置图标同屏",
"acceptance": "EmptyProps 新增 icon(unknown):字符串或图标 DOM 直接进图标位,缺省仍是 ⌀;图标位保持 aria-hidden,语义由 title / description 承担;文档参数表 / 手把手与演示(搜索无结果换成 🔍)同步;npm run check 全绿",
"notes": "自补:可用性;一个可选参数透传,行为向后兼容",
"status": "done",
"domain": "tune"
},
{
"id": "g120",
"title": "AGENTS.md 补新增组件落点清单",
"motivation": "本轮 20 次迭代里,每加一个组件都要现推一遍「要改哪些文件」:types / index / css / manifest 两处 / demo / README 三处 / AGENTS / ARCHITECTURE / docs,漏一处就靠 verify 报错来提醒,返工成本高;接手的 Agent 也没有一份照单打勾的清单",
"acceptance": "AGENTS.md「贡献新组件」章节补一张 10 行的落点表(每个落点写清具体动作与门禁原因),并说明改既有组件入参同样要走 manifest / docs 三步;npm run check 全绿",
"notes": "自补:Agent 文档完善;把本轮踩到的同步点固化成清单",
"status": "done",
"domain": "new"
},
{
"id": "g121",
"title": "textField 补 maxCount 字数统计",
"motivation": "textarea 早有 maxCount(原生上限 + 实时 n/limit),textField 却没有:昵称、标题、单行备注这类短字段同样需要限长,调用方只能自己拼计数节点贴在输入框外,样式与位置各写各的",
"acceptance": "TextFieldProps 新增 maxCount(缺省不渲染):传了即设原生 maxlength 拦截超限输入,输入框内侧附加组显示实时 n/上限(数字等宽不抖);与 clearable 共存时同组并排;文档参数表 / api 说明 / 手把手与 llms-full.txt 同步;npm run check 全绿",
"notes": "自补:输入类一致性;计数与清空共用后置附加组,零新增布局容器;首轮 verify 曾报 llms-full 漂移,重跑 npm run docs 后恢复(docs 需在 index 之后跑)",
"status": "done",
"domain": "tune"
},
{
"id": "g122",
"title": "评分组件支持 allowHalf 半星",
"motivation": "评分只有整数档:商品 4.5 星、服务 3.5 分这类半档表达不了,调用方只能取整或自己换别的组件;点星也只能选整颗,左半区白白浪费",
"acceptance": "RateProps 新增 allowHalf(缺省整数):点击星标左半区记 0.5 分、右半区整颗,键盘步进同为 0.5(值按 0.5 量化夹取),悬停位置实时预览半格填充(左半亮右半灭走渐变裁字),value 信号可写入 x.5;文档参数表 / api 说明 / 手把手与 llms-full.txt 同步;npm run check 全绿",
"notes": "自补:组件能力补齐;命中值与键盘步进共用 0.5 量化,不改信号语义",
"status": "done",
"domain": "tune"
},
{
"id": "g123",
"title": "收尾刷新变更日志",
"motivation": "g118 之后又落了 10 次提交(textField 字数统计、rate 半星等),changelog:check 已报漂移",
"acceptance": "npm run changelog 重新生成(319 提交),changelog:check 一致(提交本条后按约定再差一条,属正常);npm run check 全绿",
"notes": "自补:发版前刷新步骤;本次唤起收尾轮",
"status": "done",
"domain": "tune"
},
{
"id": "g124",
"title": "checkbox 补 indeterminate 半选态",
"motivation": "全选/子项联动只能表达「全勾」与「全不勾」:部分选中时全选框没有任何视觉区分,dataTable 内部的全选三态也是包私有实现,独立 checkbox 场景(树形勾选、批量列选择)用不上",
"acceptance": "CheckboxProps 新增 indeterminate(布尔或信号):同步到原生 input.indeterminate 属性,accent-color 自动渲染横杠,读屏播报 mixed;不改变 checked 值与点击语义(点一下即回全选);文档参数表 / api 说明 / 手把手与 llms-full.txt 同步;npm run check 全绿",
"notes": "自补:组件能力补齐;零新增 CSS(accent-color 原生覆盖 :indeterminate)",
"status": "done",
"domain": "tune"
},
{
"id": "g125",
"title": "开关补 loading 加载态",
"motivation": "开关切换常伴随后端写入(改配置、提交设置):请求在途时用户可以连续拨动,最后一次点击覆盖前面的状态,业务方只能自己在 onChange 里加标志位手动锁",
"acceptance": "SwitchProps 新增 loading(布尔或信号):加载中锁定交互(disabled 同值语义)、滑块换成转圈(reduced-motion 静态化为实心环)、input 挂 aria-busy;与 disabled 同时生效且不双写竞争;文档参数表 / api 说明 / 手把手与 llms-full.txt 同步;npm run check 全绿",
"notes": "自补:组件能力补齐;loading 接管时 disabled 不再走 h() 绑定,单一 effect 写入避免竞争",
"status": "done",
"domain": "tune"
},
{
"id": "g126",
"title": "数字输入补 precision 小数位",
"motivation": "金额、比例、权重这类小数场景:step 0.1 步进会累积出 0.30000000000000004 这样的浮点尾差直接写进信号,显示位数也无法约束,调用方只能自己在 onChange 里 toFixed 再写回",
"acceptance": "InputNumberProps 新增 precision(缺省不处理):写回与显示都按 toFixed(p) 归一(写回量化 + 输入框定长显示),步进浮点尾差消除;min/max 夹取语义不变;文档参数表 / api 说明 / 手把手与 llms-full.txt 同步;npm run check 全绿",
"notes": "自补:组件能力补齐;量化收敛在 emit/sync 单点,不改夹取与连发逻辑",
"status": "done",
"domain": "tune"
},
{
"id": "g127",
"title": "补充长表单编辑页组合配方",
"motivation": "affix 贴底、form 汇总条、hint、maxCount、saving 三处锁定这些能力都是本轮及此前几轮陆续落地的,但「长表单编辑页怎么拼」没有一篇端到端指导,Agent 与新人仍要自己试错组合",
"acceptance": "recipes.md 新增「配方 6:长表单编辑页(吸底操作条 + 提交锁定)」:affix position bottom 固定操作条、form 聚合校验与汇总条、同一 saving 信号锁按钮/开关/请求出口、maxCount 与 rules max 同源约束;npm run docs 后 llms-full.txt 出现该配方;npm run check 全绿",
"notes": "自补:文档完善;只写用法不碰实现,配方随 docs 注入 llms-full",
"status": "done",
"domain": "tooling"
},
{
"id": "g128",
"title": "收尾刷新变更日志",
"motivation": "g123 之后又落了 10 次提交(rate 半星、checkbox 半选、开关 loading、precision、配方 6 等),changelog:check 已报漂移",
"acceptance": "npm run changelog 重新生成(329 提交),changelog:check 一致(提交本条后按约定再差一条,属正常);npm run check 全绿",
"notes": "自补:发版前刷新步骤;本次唤起收尾轮,顺带提交 g127 漏带的 llms-full.txt",
"status": "done",
"domain": "tune"
},
{
"id": "g129",
"title": "新增排版文本组件 typography",
"motivation": "文档类页面(详情、说明、条款)目前只能手搓 h/p + 内联样式,标题层级、语义色、多行省略、复制都没有统一入口",
"acceptance": "typography 支持 variant(title/text/paragraph) + level + type + 装饰位(strong/underline/delete/mark/code) + align + ellipsis 多行截断 + copyable;标题出语义 h1-h5;样式只消费 --ua-*;manifest/docs/README/demo/llms-full 同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充方向",
"domain": "new"
},
{
"id": "g130",
"title": "新增栅格容器 grid",
"motivation": "页面骨架只能靠 space 与手写 flex,24 栅格、列间距、响应式断点缺失,示例页布局各写一套",
"acceptance": "grid 支持 columns 固定列数与 min 走 auto-fit 自适应列宽、gutter 统一列间距、align 交叉轴对齐、dense 紧凑填充;窄屏自动降为单列;样式只消费 --ua-*;manifest/docs/README/demo/llms-full 同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充方向(原设想 row/col 24 栅格,实施改为单容器 grid —— auto-fit 已覆盖响应式,跨列需求交给子项自身宽度,少一层抽象)",
"domain": "new"
},
{
"id": "g131",
"title": "新增悬浮操作按钮 floatButton",
"motivation": "backTop 只能回顶,页面里「新建 / 帮助 / 客服」这类常驻悬浮入口没有统一件,各处自己 fixed 定位",
"acceptance": "floatButton 固定右下角,支持 icon/label/tooltip/badge/点击回调与 position 偏移,键盘可达;样式只消费 --ua-*;文档与演示同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充方向",
"domain": "new"
},
{
"id": "g132",
"title": "@提及输入补 mentions 组件",
"motivation": "评论 / 协作场景要 @ 选人,目前只能 textarea + 自己接 autoComplete,光标定位与插入逻辑重复实现",
"acceptance": "mentions 在 @ 触发后按光标位置弹候选,↑↓/Enter/Tab 选择、Esc 关闭,选中插入文本并回调 onSelect;受控 value 信号;样式只消费 --ua-*;文档与演示同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充方向",
"domain": "tune"
},
{
"id": "g133",
"title": "树形选择补 treeSelect 组件",
"motivation": "tree 与 select 都在,但「可搜索的层级下拉选择」缺位,级联选择 cascader 不适配任意深度懒加载场景",
"acceptance": "treeSelect 点击开弹层、支持搜索过滤、单选/多选、父子联动、清空,弹层沿用 overlay 的 Esc 栈与外部点击收起;样式只消费 --ua-*;文档与演示同步;npm run check 全绿",
"status": "done",
"notes": "自补:组件补充方向",
"domain": "tune"
},
{
"id": "g134",
"title": "组件计数自动同步 ua sync-counts",
"motivation": "新增组件要手改 README 标题 / README 包说明 / AGENTS / ARCHITECTURE 四处数字,本轮四次新增每次都靠人工替换;且 README 标题「共 N 个」此前不在 verify 正则里,漏改不报错",
"acceptance": "新增 ua sync-counts(幂等,无变化不写盘)把三份文档的「N 个组件 / N 个 DOM 组件 / 共 N 个」统一改写为清单实际数量;verify 的计数正则覆盖 README 标题形态并提示修法;npm run check 全绿",
"notes": "自补:构建/门禁工具链改进(实测把 README 标题改成 61 后 verify 能报行号,sync-counts 一键修回)",
"status": "done",
"domain": "tune"
},
{
"id": "g135",
"title": "表单支持多列布局 columns",
"motivation": "字段多的编辑页只能一列往下堆,长表单要滚很久;页面里已有 grid,form 却没用上,调用方只能自己拆成多个 form(校验被割裂)",
"acceptance": "form 新增 columns(1/2/3),>1 时字段走 grid 铺列、窄屏自动单列、按钮区始终整行;单向依赖(form 复用 grid);manifest props/steps 与 demo 同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节",
"status": "done",
"domain": "tune"
},
{
"id": "g136",
"title": "新增链接组件 link",
"motivation": "页面里的 a 标签各写各的样式:站外链接普遍漏 rel=noopener、没有 ↗ 提示,读屏也不知道开了新窗口;禁用态要么当纯文本要么还能点",
"acceptance": "link 支持 href/label/target/external/muted/disabled/onClick,站外自动补 ↗ 与 rel 并播报新窗口,禁用去 href 保留 aria-disabled;样式只消费 --ua-*;文档与演示同步;npm run check 全绿",
"notes": "自补:组件补充方向",
"status": "done",
"domain": "new"
},
{
"id": "g137",
"title": "演示页补暗色主题开关",
"motivation": "tokens 里有 [data-theme=dark] 整套令牌,但演示页从未切换过:暗色令牌长期无人验证,页面自有 --page-* 变量也只跟系统偏好、不跟显式主题",
"acceptance": "演示页头部提供暗色主题开关(写 html[data-theme],localStorage 记忆,初值跟随系统);--page-* 变量响应 data-theme,媒体查询只在未显式指定时生效;npm run check 全绿",
"notes": "自补:演示页完善",
"status": "done",
"domain": "tune"
},
{
"id": "g138",
"title": "日期选择支持区间模式",
"motivation": "筛选条件几乎都要「起止日期」,datePicker 只有单日:调用方只能并排放两个选择器,用户看不出选了什么区间,也拿不到排序保证",
"acceptance": "datePicker 新增 range/end/onRangeChange:首点定起点不收弹层、次点定终点并排序,区间内部有底色;文档 props/steps 与演示同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节",
"status": "done",
"domain": "tune"
},
{
"id": "g139",
"title": "提示气泡支持左右方位",
"motivation": "tooltip 只有上下两向:竖排工具栏、贴右边的内容只能硬翻,left/right 全靠调用方自己补样式;position.ts 的 flip 也只认上下",
"acceptance": "position 新增 flipSide(四向判定,水平按宽度算左右空间),tooltip placement 支持 left/right 并复用自动翻转;CSS 四向定位 + 出场位移分离;文档 props/steps 与演示同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节",
"status": "done",
"domain": "tune"
},
{
"id": "g140",
"title": "表格支持整行点击",
"motivation": "「点行进详情」是表格最常用交互,dataTable 只能把跳转塞进某一列的按钮:点击区域小、键盘不可达,行本身对读屏也只是文本",
"acceptance": "dataTable 新增 onRowClick:传了则该行 tabindex=0 并响应点击 / Enter / 空格,未传时行为与现在完全一致(不进 Tab 序列);文档 props/steps 与演示同步;单文件仍 ≤200 行;npm run check 全绿",
"notes": "自补:既有组件可用性细节(行数恰好压到 200 行上限,靠合并注释行达标)",
"status": "done",
"domain": "tune"
},
{
"id": "g141",
"title": "通知支持四角停靠",
"motivation": "notification 只能堆在右下角,与抽屉 / 悬浮按钮抢位置;页面里已有 toast 占右上,通知没有别处可去",
"acceptance": "notification 新增 placement(四个角),每角独立容器与 3 条上限,顶部角落新通知贴边出现、旧的往下让;文档 props/steps 与演示同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节",
"status": "done",
"domain": "tune"
},
{
"id": "g142",
"title": "间距容器支持分隔符 split",
"motivation": "「A · B · C」这类一行多段信息(筛选摘要、面包屑式路径)只能手写 span 分隔符再逐个调 margin,gap 与分隔符混用后间距不均,读屏还会念出「·」",
"acceptance": "space 新增 split:仅在 2 个以上子元素间插入分隔符,分隔符 aria-hidden、弱化色、不可选中;文档与演示同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节",
"status": "done",
"domain": "tune"
},
{
"id": "g143",
"title": "页签栏支持右侧操作区 extra",
"motivation": "「新建 / 设置」这类页签级操作只能塞进页签数组或浮在页签栏外,垂直不对齐、分隔线断掉;调用方还得自己包一层 flex",
"acceptance": "tabs 新增 extra:与页签同一行、共用一条分隔线,不传时 DOM 结构不变(纯增量);文档 props/steps 与演示同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节",
"status": "done",
"domain": "tune"
},
{
"id": "g144",
"title": "新增头像组组件 avatarGroup",
"motivation": "协作列表 / 评论头部要展示参与人,并排 avatar 占横向空间且没有「还有 N 人」的表达;调用方各自用 margin 叠放,切口与层级都不一致",
"acceptance": "avatarGroup 支持 items(name/src/variant)、max 折叠为 +N、size 与 avatar 对齐;整组 title 列全部姓名、+N 有可访问名;样式只消费 --ua-*;文档与演示同步;npm run check 全绿",
"notes": "自补:组件补充方向",
"status": "done",
"domain": "new"
},
{
"id": "g145",
"title": "滑块支持刻度标注 marks",
"motivation": "0-100 的滑块看不出档位含义,用户只能看右侧数字猜;调用方要在轨道下标 0/50/100 得自己量宽度算百分比",
"acceptance": "slider 新增 marks(value/label),按 min-max 换算百分比绝对定位、随宽度自适应;轨道包一层不影响右侧数值;刻度 aria-hidden;文档与演示同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节",
"status": "done",
"domain": "tune"
},
{
"id": "g146",
"title": "进度条支持环形形态",
"motivation": "看板与上传卡片普遍要环形进度,progress 只有线性条:调用方只能自带 SVG 并对齐语义色与百分比文字",
"acceptance": "progress 新增 type=line|circle,环形走 createElementNS 的 SVG 双圆 + dashoffset,中间显示百分比、aria-label 拼名称与数值,variant 换弧色;linear 行为不变;文档与演示同步;npm run check 全绿",
"notes": "自补:既有组件可用性细节(坑:h() 用 createElement 造不出可渲染 SVG,必须 createElementNS)",
"status": "done",
"domain": "tune"
},
{
"id": "g147",
"title": "补三条组合配方(筛选栏/评论/详情页)",
"motivation": "本轮新增的 treeSelect、datePicker range、mentions、avatarGroup、typography、grid 只有参数表,Agent 拿到组件却不知道该怎么拼;recipes.md 还停在 6 条",
"acceptance": "recipes.md 补配方 7-9(筛选栏、评论输入、详情页排版),只消费对外 API,随 npm run docs 进入 llms-full.txt;npm run check 全绿",
"notes": "自补:Agent 文档完善",
"status": "done",
"domain": "tooling"
},
{
"id": "g148",
"title": "MCP 暴露组合配方检索(list_recipes / get_recipe)",
"motivation": "recipes.md 已有 9 条跨组件配方,但只写在文档里:Agent 通过 MCP 只能查单组件参数,拿不到「怎么拼」,容易各自造轮子或漏掉约定。",
"acceptance": "ua mcp 新增 list_recipes(id/title/components)与 get_recipe(骨架代码 + 要点 + 用到的组件)两个工具;配方解析复用 recipes.md 单一来源;verify 增加配方结构校验(编号连续、每条有代码块与要点、有通用约定段);AGENTS/README/CLI 清单文档同步。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g149",
"title": "CLI 补 ua search(关键词找组件 + 拼错给相近 id)",
"motivation": "组件已达 67 个,只有 ua list 全量平铺与 ua info 精确查询:不记得 id 时只能肉眼扫列表,拼错则只回一句 not found。",
"acceptance": "ua search <关键词> 按 id/标题/props 名/步骤匹配并排序输出,支持 --json;ua info 与 search 未命中时给出编辑距离 ≤3 的相近 id;usage 与 CLI 清单 steps、README、AGENTS 同步。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g150",
"title": "ua info 反查组件出现在哪些配方",
"motivation": "配方与组件是两套入口:查完 props 往往还要自己判断「这组件平时和谁一起用」,而 recipes.md 里已有答案却没接上。",
"acceptance": "ua info <组件id> 末尾列出包含该组件的配方 id 与标题(无命中不追加空行);--json 输出增加 recipes 字段;MCP get_component 组件级返回同步带 recipes。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g151",
"title": "verify 校验 MCP 工具清单三处一致",
"motivation": "MCP 工具从 4 个扩到 6 个时,README/AGENTS/CLI 清单靠手改;漏一处就会让 Agent 读到过期的工具名,而 verify 当时不管这块。",
"acceptance": "MCP_TOOL_NAMES 为单一来源,cmdMcp 启动时自证注册表与之一致;verify 检查 README/AGENTS/CLI 清单逐个写出工具名,缺失即报错并指出漏了哪个(已负向实测)。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g152",
"title": "ua list 增加组件全览模式",
"motivation": "ua list 只列 5 个包,67 个组件的 id 只能靠 ua search 猜关键词去找;没关键词时缺少「一次扫全量」的入口。",
"acceptance": "ua list 每行带上组件数;ua list --components 按包分组列出 id / 标题 / 演示锚点并给出总数与后续命令提示,原有无参输出保持可用。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g153",
"title": "ua search 支持多关键词与脚本退出码",
"motivation": "单一关键词只能粗筛:想找「日期区间」时搜日期会连 calendar/timeline 一起返回;且未命中仍退 0,脚本里判断不了。",
"acceptance": "ua search a b 取各词命中的最弱档做 AND 过滤仍保持 id>标题>props>步骤 的排序;未命中输出相近建议并 exit 1;无参数打印用法。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g154",
"title": "MCP search_components 补组件级命中",
"motivation": "原实现只在包级字段(name/description/tags/api.exports)里搜,命中后只返回包:Agent 搜「日期」得到 @ui-arch/ui,还得再猜组件 id。",
"acceptance": "返回 { packages, components } 两级结构,components 按 id/标题/props 名命中并带 package 字段;空 query 与无命中都抛明确错误;工具描述同步。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g155",
"title": "llms.txt 补配方目录 + 修正 MCP 工具清单描述",
"motivation": "Agent 常只读 llms.txt 速览,但速览里完全没有配方入口;同时 cli 包导出描述里只写了 3 个 MCP 工具,与实际的 6 个不符。",
"acceptance": "ua index 在 llms.txt 末尾输出配方目录(id / 标题 / 要点数)并指明三种取法;cli 清单的 mcp 导出描述与 MCP_TOOL_NAMES 一致。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g156",
"title": "ua tokens 支持按名字过滤",
"motivation": "21 个令牌全表打印后再肉眼找某一个,写样式时反复查多行;缺令牌名拼错也不报错。",
"acceptance": "ua tokens <子串> 只列匹配项并在末尾标注匹配词,--json 同步过滤;无匹配时给出总数并以 1 退出。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g157",
"title": "补配方 10:暗色主题切换",
"motivation": "令牌层早就支持 [data-theme=dark],但配方里没有任何一条说清「怎么切、初值怎么定、原生控件为何仍是白底」,实践只能靠翻令牌表猜。",
"acceptance": "recipes.md 新增配方 10(切 data-theme + 同步 color-scheme + 初值先读 localStorage),verify 的配方结构校验通过,llms-full 与 MCP get_recipe 10 可取到骨架。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g158",
"title": "日期选择支持快捷区间预设",
"motivation": "仪表盘类界面里「最近 7 天 / 最近 30 天」是高频刚需,现在调用方只能在弹层外自己摆按钮再算日期区间,算完还得手写 value/end 两个信号,重复且易错。",
"acceptance": "datePicker 新增 presets 入参(仅区间模式渲染):每项支持 days 相对今天或 from/to 绝对日期,点击即写入 value/end、按 min/max 钳制并收起;演示页区间示例带三档预设;manifest props 表与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g159",
"title": "多选下拉支持标签折叠",
"motivation": "multiSelect 的 max 放开后标签会把输入区挤到换行,窄栏(筛选栏 / 表单侧栏)里选中五六个就撑破布局,调用方只能自己截断数组、却因此拿不到真实值。",
"acceptance": "multiSelect 新增 maxTagCount:前 N 枚标签照常渲染与删除,其余折成虚线「+N」(title 给全量、点击展开弹层,含 aria-label);演示页加一枚预置 3 项的折叠实例;manifest props 表与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g160",
"title": "滑块支持双滑块区间",
"motivation": "价格 / 数值区间筛选是电商与报表的基本控件,此前只能拼两个单值 slider,视觉上两条独立轨道、还得自己保证下限不超过上限,调用方到处写同样的 clamp。",
"acceptance": "slider 新增 range / end / onRangeChange:两条原生 range 叠轨、只有拇指可命中,强调色画出已选段,越界自动压平保证 low ≤ high,右侧显示「低 ~ 高」;单值路径不变;manifest props 表与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g161",
"title": "通知支持同 key 原地更新",
"motivation": "异步任务(上传/导出/同步)要把一条通知从 0% 刷到 100%,此前只能再调一次 notification,结果右上角堆出三条互不相干的卡,MAX_STACK=3 还会把别的通知挤掉。",
"acceptance": "notification 新增 key 入参:同 key 二次调用原地改标题 / 描述 / 语义色并返回同一句柄;句柄新增 update(partial),close 时释放 key 登记;演示页加三拍进度演示;manifest props 表与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g162",
"title": "门禁补暗色令牌齐全校验",
"motivation": "暗色主题此前只靠人眼看:新增一个 --ua-color-* 忘了在 [data-theme='dark'] 里给值,暗色下就静默沿用亮色(如白底深字条突然发白),没有任何机制拦。",
"acceptance": "verify 新增两项校验:每个 --ua-color-* 必须在暗色块有覆盖值;暗色块里的变量必须先在 :root 定义过(防改名漏改);ua tokens 对缺暗色的颜色令牌追加「缺暗色」标注;AGENTS.md 门禁说明同步;npm run check 全绿,且临时抽掉一条暗色值时 verify 确实报错。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g163",
"title": "留痕校验补编号与时间线约束",
"motivation": "上一实例曾把新条目误插到旧条目后面,把一条 done 写成两条,还出现过手写时间戳——这些都是「结构错但内容看起来对」的漂移,verify 当时完全看不出来。",
"acceptance": "verify 新增:backlog id 不重复、g 编号严格递增、同一 id 只能有一条 done 留痕、留痕 ts 必须带 +08:00、时间线不回退、不落在未来(5 分钟容差);AGENTS.md 与 verify 成功文案同步;负向注入(复制条目 + 手写无偏移时间戳)四类错误全部报出,复原后全绿。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g164",
"title": "描述列表支持竖排与标题操作区",
"motivation": "详情页右侧窄栏里,字段名横排会把值挤成两三行断字;标题行想放个「刷新 / 更新时间」也没位置——只能在外面另拼一个 flex 头。",
"acceptance": "descriptions 新增 layout(vertical 把字段名挪到值上方,bordered 时自动去掉标签分隔线与底纹)与 extra(标题行右侧内容);演示页加竖排 + 操作区实例;manifest props 表与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g165",
"title": "步骤条支持竖向排列",
"motivation": "审批流 / 物流轨迹在侧栏里只能横排,三步以上就被挤成两行、连接线错位;调用方只能退回自己拼时间线,丢了 done/current 三态语义。",
"acceptance": "stepper 新增 direction:vertical 时圆点成一列、连接线走圆点下方、末步不画线、已完成段用主色;演示页加独立信号的竖向实例;manifest props 表与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g166",
"title": "下拉选项支持分组标题",
"motivation": "技术栈 / 部门 / 城市这类长候选集只能平铺,用户得一行行扫;分组要么拆成多个 select,要么在 label 里塞「【前端】Vue」这类假标题。",
"acceptance": "OptionItem 与 CheckOption 新增 group:select 在组首插灰色小标题(段间分隔线)、整组过滤无命中时标题隐藏;multiSelect 候选同步支持且小标题不占键盘下标;演示页加分组下拉实例;manifest 的 options 描述与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g167",
"title": "弹窗支持内置页脚与异步确认",
"motivation": "「取消 + 确定」这两个按钮几乎每个确认框都要手拼一遍,异步提交时还得自己管「处理中」禁用与成功后收窗,各处写法不一。",
"acceptance": "modal 新增 confirmText / cancelText / onConfirm:不传 footer 时渲染内置按钮组(footer 仍优先),onConfirm 返回 Promise 时按钮禁用并显示「处理中…」,成功自动收窗、失败保持打开;演示页把确认弹窗改成异步确认;manifest props 表与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g168",
"title": "补配方 11:数据看板筛选区",
"motivation": "本轮补的区间滑块、日期预设、分组下拉都是筛选区零件,但配方里没有一条把它们拼起来;Agent 拿到零件也不知道该用 computed 派生查询而不是各存一份状态。",
"acceptance": "recipes.md 新增配方 11(grid 三列放 slider/datePicker/select + dataTable),要点写清「信号单一来源」「区间控件自带 clamp」「group/presets 的取舍」;verify 配方结构校验通过;llms-full.txt 与 MCP get_recipe 可取到。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g169",
"title": "表单字段支持跨列",
"motivation": "columns: 2 的表单里,多行备注 / 地址这类字段只能各占一格,看上去被压成半宽;想整宽只能把 form 拆成两个再手拼,校验聚合也就断了。",
"acceptance": "formItem 新增 span:字段在栅格里占 N 列;grid 窄屏单列时用 !important 忽略跨列,避免撑出横向滚动;演示页两列表单加整宽备注;manifest 的 formItem 参数表与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g170",
"title": "分页器支持极简模式与单页隐藏",
"motivation": "表格工具栏里放完整页码会占掉半行;只有一页时又留一个灰掉的「1」占位,两种场景都只能靠调用方自己条件渲染。",
"acceptance": "pagination 新增 simple(只留前后箭头 + 「当前 / 总页」文字,带 aria-live)与 hideOnSinglePage(单页整块 hidden);演示页各加一例;manifest props 表与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g171",
"title": "刷新 CHANGELOG 与配方目录",
"motivation": "CHANGELOG 由提交史生成、不进 check(它包含每个提交自身,提交后必然差一条),只能作为发版前刷新;本轮 14 次迭代后已落后 30 余条,llms.txt 的配方目录也漏了配方 11。",
"acceptance": "npm run changelog 重生成到当前提交史(400 提交);llms.txt 补配方 11 目录行并在此后随 docs 归档;CHANGELOG 内容与 git log 一致(npm run changelog:check 通过)。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g172",
"title": "列表命令支持 JSON 输出",
"motivation": "ua info 早就有 --json,但 ua list 只能打印文本,脚本与 Agent 想拿「包 + 组件 id」清单只能抓 stdout 再正则,容易随文案变化失效。",
"acceptance": "ua list --json 输出 {name, version, type, description, components[]} 数组,--components 仍逐组件展开;CLI 清单 exports 与 AGENTS.md 的用法行同步;npm run check 全绿(含 lint-iterations)。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g173",
"title": "上传图片显示缩略图",
"motivation": "选完图片只看到文件名,用户没法确认选的是哪张——上传头像 / 凭证照这类场景必须点开本地文件核对,体验很别扭。",
"acceptance": "upload 对 image/* 文件渲染 28px 缩略图(object-fit: cover),对象 URL 按 File 缓存、文件离场即 revoke;非图片保持文件名 + 体积;manifest 步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g174",
"title": "时间线支持双侧交替",
"motivation": "里程碑 / 项目历程这类内容用单侧时间线时,长正文会把右侧挤成一条窄柱;做左右交替得自己包两层栅格,pending 虚节点还容易错位。",
"acceptance": "timeline 的 mode 增加 alternate:轴线居中、条目按奇偶分列左右、圆点贴中线、窄屏自动退回单侧(pending 虚节点参与交替计数);演示页加里程碑实例;manifest 参数类型与步骤、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g175",
"title": "确认气泡支持异步确认",
"motivation": "删除 / 归档的行内操作基本都要打后端,但 popconfirm 的 onConfirm 是同步签名:点确定立刻收起,请求失败时气泡已经没了,用户只能看到一条报错、得重新点开。",
"acceptance": "popconfirm 的 onConfirm 返回 Promise 时:气泡保持展开、确认按钮变「处理中…」并禁用,resolve 后收起、reject 后复原可重试;类型放宽为 () => unknown(普通回调仍先收起再执行),modal 的 onConfirm 同步放宽;演示页加异步归档实例;manifest、llms-full 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g176",
"title": "ua search 覆盖参数说明并标出命中字段",
"motivation": "Agent 找组件时给的往往是需求词(如「处理中」「省略号」「归档」),这些词只出现在参数说明或步骤里;原实现只拼参数名,说明文字搜不到,明明有组件却回 no match,Agent 只能改用 list 全量翻。",
"acceptance": "search 按 id / 标题 / 参数名 / 参数说明 / 步骤五层打分排序,命中非 id 标题时行尾标出命中字段(如「← 参数说明含『处理中』」),--json 附带 matched;MCP search_components 复用同一字段表,工具描述与 AGENTS.md、tools manifest 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tune"
},
{
"id": "g177",
"title": "补 ua recipes 子命令,配方不再只能靠 MCP 取",
"motivation": "组合配方(跨组件拼装骨架)原本只有 MCP 的 list_recipes / get_recipe 能读,没注册 MCP 或纯 CLI 场景下 Agent 只能翻 recipes.md 原文;另外 CLI 与 MCP 各写了一份「配方里用到哪些组件」的抽取逻辑,口径容易漂移。",
"acceptance": "ua recipes 列出编号/标题/用到的组件,ua recipes <编号> 打印骨架代码与要点,--json 给脚本用;MCP 复用同一抽取函数;AGENTS.md 与 packages/tools/cli/ua.manifest.json 同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g178",
"title": "verify 兜住「认不出组件」的配方",
"motivation": "配方骨架里的组件名如果拼错(如 cascader 写成 cascaderSelect),结构校验全都过——代码块在、要点在、编号连续,但 ua recipes 会把组件列成空表,Agent 拿到的是一份「没有依赖」的骨架,排查成本高。",
"acceptance": "ua verify 对每条配方检查代码块+正文是否至少认出一个已注册组件 id,认不出直接报错并指名配方编号与标题;负向自检(临时插一条用未注册组件名的配方)确认会拦;AGENTS.md 门禁说明同步;npm run check 全绿。",
"notes": "",
"status": "done",
"domain": "tooling"
},
{
"id": "g179",
"title": "弹层阴影语义化:剩余字面阴影归入 elevation 四档",
"motivation": "阴影已收敛出四档令牌,但仍有主题色投影(color-mix text/accent)与状态色环以字面值散落在弹层、气泡、选中态里,同名层级在不同组件用不同数值,观感不齐且改不动。",
"acceptance": "ui.css 中 box-shadow 的 px 字面值全部归入 --ua-shadow-1..4 或语义色环(无主题色投影字面值);`ua audit:visual --json` 的 shadow-literal 计数相对 .ua/visual-baseline.json 至少减少 8 处并写入新基线;在 packages/skin/ui/ua.manifest.json 的 docs.intro 写明「下拉=shadow-2 / 浮层=shadow-3 / 弹窗=shadow-4 / 通知=shadow-4」对照表;npm run check 全绿。",
"status": "done",
"notes": "主题色投影新增 shadow-accent / shadow-accent-strong 两档令牌(含暗色),文本系投影按「下拉=2 / 浮层=3 / 最高层=4」归档;shadow-literal 17→9(减 8 处),新基线 27 处已写入;manifest intro 补阴影对照表",
"domain": "visual"
},
{
"id": "g180",
"title": "内距令牌化:padding / gap 字面值降到基线以下",
"motivation": "间距只有 4/8/12/16 四档令牌,实际却大量写 6px/10px 这类中间值,同一类容器内距不统一,改密度只能全仓替换。",
"acceptance": "packages/design/tokens/tokens.css 补齐内距档位(新增 6px/10px 两档或把它们归并到既有档并在注释说明),tokens.json 同步;visual-rules 增加 space-literal 类(padding/gap/margin 的 px 字面值),迁移脚本同步规则并幂等;该类计数写入新基线且比首次统计少 30 处以上;npm run check 全绿。",
"status": "done",
"notes": "新增半步内距档 space-05/15/25/35(2/6/10/14px,tokens.css+json 同步);visual-rules 新增 space-literal 第六类(迁移脚本与审计共用同一张表、幂等);首次统计 160 处,迁移 121 处后余 13 处,新基线 40 处",
"domain": "visual"
},
{
"id": "g181",
"title": "表单控件高度统一接入 control-h 三档",
"motivation": "控件高度令牌 --ua-control-h-sm/md/lg 已就位,但 button/textField/select/inputNumber 仍各自写死高度,同一行并排时高度不一致,也挡住「略放大提高可点面积」的落地。",
"acceptance": "ui.css 中 .ua-btn / .ua-input / .ua-select / .ua-input-number 的高度声明全部引用 var(--ua-control-h-*);visual-rules 增加 control-h-literal 类且计数为 0;apps/demo/main.ts 中存在一行并排的三档高度演示(sec 锚点内真实调用四个组件);npm run check 全绿。",
"status": "done",
"notes": "四个控件载体(ua-btn/ua-input/ua-select-trigger/ua-input-number-field)统一 height 走 control-h 三档;button/textField/select/inputNumber 新增 size 入参(sm/md/lg,is-sm/is-lg 类);步进器按钮 height:auto 豁免;control-h-literal 第七类规则(28/34/40px 保留给控件档位)计数 0;28px×5 与 40px×2 字面值归档位令牌;demo sec-inputnumber 加四组件三档并排行",
"domain": "visual"
},
{
"id": "g182",
"title": "暗色主题阴影与焦点环适配",
"motivation": "暗色块只覆盖颜色,弹层阴影沿用亮色主题的低透明度黑投影,暗底上几乎看不出层级;焦点环在暗色下对比也不足。",
"acceptance": "[data-theme='dark'] 覆盖 --ua-shadow-1..4(更高黑度)与 --ua-focus-ring 两个槽位;ua verify 增加一条校验:暗色块缺 shadow / focus 覆盖即报错;ua tokens 输出的暗色列能看到这 6 个令牌的暗色值;npm run check 全绿。",
"status": "done",
"notes": "暗色块补 --ua-focus-ring / --ua-focus-ring-inset(透明度上调补对比),shadow-1..4 与 accent 两档人类上游已备;verify 暗色齐全校验扩展到 --ua-shadow-* / --ua-focus-* 前缀(负向自检删除 shadow-1 暗色覆盖已被拦);ua tokens 暗色列可见全部 6 个令牌的暗色值;tokens.json focus 组补 light/dark 结构",
"domain": "visual"
},
{
"id": "g183",
"title": "字重与行高统一到三档令牌",
"motivation": "标题、正文、辅助文字的字重与行高全靠各组件手写,同一层级的标题在不同弹窗里字重不同,多行文本行距忽松忽紧。",
"acceptance": "visual-rules 增加 weight-literal 与 leading-literal 两类(font-weight / line-height 的字面值),迁移脚本把字面值改为 --ua-weight-* / --ua-leading-*;两类计数归零并写入新基线;标题/正文/辅助三档在注释里写明归属;npm run check 全绿。",
"status": "done",
"notes": "visual-rules 新增 weight-literal / leading-literal 两类(第八、九类);字重映射 400/500/600/700 → normal/medium/semibold/bold(补 bold 档),行高 unitless 就近收敛到 none/tight/normal(补 none 档,px 行高按图形对齐豁免);两类 36+36 处全部归零并写新基线 40 处;tokens.css 注释标明标题/正文/辅助三档归属",
"domain": "visual"
},
{
"id": "g184",
"title": "层级令牌落地:z-index 字面值归零",
"motivation": "--ua-z-dropdown/sticky/overlay/modal/toast 五档已定义但无人使用,实际仍是各组件写 100/1000/9999,弹层互相盖住的问题只能靠试数值解决。",
"acceptance": "visual-rules 增加 z-literal 类;迁移脚本把 ui.css 中全部 z-index 字面值替换为 var(--ua-z-*);该类计数为 0 并写入新基线;ua tokens 能看到五档层级;npm run check 全绿。",
"status": "done",
"notes": "z-literal 第十类规则(组件内 z:1/2 微排序豁免,同 1px 边框惯例);按选择器把 20 处全局浮层字面值归入五档:下拉/气泡类 20/30/40→dropdown,fab→dropdown,toast/notice 宿主→toast,affix/backtop→sticky,tour/模态抽屉遮罩→overlay,image 遮罩→modal;迁移脚本对下拉类 20/30/40 提供幂等自动迁移;计数归零并写新基线 40 处",
"domain": "visual"
},
{
"id": "g185",
"title": "边框细节统一到 subtle 与基础色",
"motivation": "分割线、输入框、卡片边框混用基础边框色与更深的黑边,1px 线在不同组件上粗细观感不一,密集表格里分割线过重。",
"acceptance": "分割线改用 --ua-border-subtle、输入框与卡片边框用 --ua-color-border,ui.css 中不再出现硬编码黑边(rgba(0,0,0,…) 作为边框值);visual-rules 增加 border-literal 类且计数为 0;表格与输入框在 demo 中肉眼无粗细跳变;npm run check 全绿。",
"status": "done",
"notes": "border-literal 第十一类规则(border/border-color 色值字面量,纯黑 rgba 边可幂等迁移到基础边框色);19 处 1px 分割线统一改走 --ua-border-subtle(表格行线、卡片页脚、弹窗/抽屉页头页脚、列表项、选项卡底边、折叠面板与步骤条连接线、分隔线组件),组件轮廓仍用 --ua-color-border;border-literal 计数 0 并写新基线 40 处;顺带修正 manifest 里「五类/十组」陈旧表述",
"domain": "visual"
},
{
"id": "g186",
"title": "视觉回归抽查清单:六个代表组件的可勾选验收项",
"motivation": "视觉类改动只能靠人眼抽查,每次凭记忆看一遍,漏看是常态;把六个高频组件的观感要点固化成清单,后续视觉条目可直接复用。",
"acceptance": "在 ITERATION.md 或 packages/skin/ui/ua.manifest.json 增加《视觉回归抽查清单》,button / textField / select / modal / dataTable / toast 六个组件各 ≥3 条可勾选要点(合计 ≥18 条);清单条目必须覆盖边框、阴影、过渡、焦点环、暗色五项;ua verify 校验清单存在且 18 条要点齐全;npm run check 全绿。",
"status": "done",
"notes": "清单落在 ITERATION.md《视觉回归抽查清单》:六个代表组件共 26 条可勾选要点(button 5 / textField 4 / select 4 / modal 5 / dataTable 4 / toast 4),每条带【边框/阴影/过渡/焦点环/暗色】维度标签;ua verify 新增 checkVisualChecklist(章节存在 + 六段齐全 + 每段 ≥3 + 合计 ≥18 + 五维度全覆盖 + 维度标签合法性),负向自检确认删段/降标签/条目不足都会报错;AGENTS.md 与 cli manifest 同步该职责",
"domain": "visual"
},
{
"id": "g187",
"title": "新增虚拟列表组件 virtualList",
"motivation": "长列表(万级流水、日志、消息流)用 list 全量渲染必然卡死,DOM 数量随数据线性增长是性能悬崖;组件库有 list 但没有虚拟化,调用方只能各写各的滚动窗口逻辑。(自补选题;g179–g186 已被人工视觉域条目占用,本条顺延取号)",
"acceptance": "新增 virtualList(固定行高、只渲染可视窗口 + overscan 缓冲条、rAF 合帧滚动重建、scrollTo 句柄越界钳制),10 处落点齐全(实现/类型/导出/样式/manifest/demo/README/计数/文档),万行 demo 常驻 DOM 只有窗口行数;npm run check 全绿。",
"notes": "自补:组件补充;rebase 到人工新推的视觉令牌/审计门禁上游后门禁全绿",
"status": "done"
},
{
"id": "g188",
"title": "新增命令面板组件 commandPalette(⌘K 唤起)",
"motivation": "68 个组件里缺「全局命令入口」这类高频交互:菜单要逐级点、autoComplete 只能选值不能执行动作,Agent 与键盘用户都没有一个「输入即缩小、回车就执行」的入口。",
"acceptance": "新增 commandPalette:open 信号驱动显隐、items 支持 group/hint/keywords 三层过滤、↑↓ 环形选择 + Home/End + 回车执行、Esc 走弹层栈只关最上层、开关均归还焦点、⌘K/Ctrl+K 可开关;样式只消费 --ua-* 令牌,禁止新增硬编码阴影/时长/字号;demo 新增可交互区块;npm run check 全绿且 audit:visual 计数不涨。",
"status": "done",
"notes": "新增 packages/skin/ui/src/commandPalette.ts(148 行)+ types 三接口 + ui.css 13 条规则(全令牌,含 prefers-reduced-motion 兜底);过滤看 label/hint/keywords 三层,高亮只切 class 不重建列表(aria-activedescendant 供读屏);Esc 复用 overlay 栈、滚动锁复用 lockScroll;demo 新增 sec-cmdk(按钮 + ⌘K 双入口);README 清单与「浮层」分组、ARCHITECTURE/AGENTS 组件数随 sync-counts 同步到 69;audit:visual 仍 40 处"
},
{
"id": "g189",
"title": "补配方 12:命令面板(⌘K 全局操作入口)",
"motivation": "commandPalette 刚入库,但「id 分发 / keywords 兜拼音 / onSelect 里开二次确认 / 多实例 hotkey 冲突」这些坑只有实现者知道;配方是 Agent 与新人复用组件的入口,缺一条就会各写一遍。",
"acceptance": "packages/tools/cli/recipes.md 增加配方 12(编号连续、含代码块与要点),代码块至少认出一个已注册组件 id;ua recipes 12 能取到骨架与要点;npm run check 全绿。",
"status": "done",
"notes": "配方 12「命令面板(⌘K 全局操作入口)」:id 做分发键、label 只显示;keywords 塞拼音别名;同组连续才合并;二次确认交给 modal/popconfirm 而非面板内叠层;一页只留一个 hotkey;items 快照需重建。ua recipes 12 识别 commandPalette / modal / popconfirm / toast 四组件"
},
{
"id": "g190",
"title": "字号字面值归零:补展示档与相对缩放档",
"motivation": "五档主轴只覆盖到 18px,剩下 5 处 22/26/34px 与大号字符图标、代码片段 0.92em、行内图标 0.85em 无令牌可用,只能写字面值;字号门禁因此长期停在 5 处,谁也不敢动。",
"acceptance": "tokens 增加 --ua-font-size-3xl/4xl/5xl(22/26/34px 展示档)与 --ua-font-size-scale-md/sm(0.92em/0.85em 相对缩放档),visual-rules 映射表同步;迁移后 font-size-literal 计数为 0 并写入新基线;ua tokens 能看到五个新令牌;npm run check 全绿。",
"status": "done",
"notes": "字号令牌从 6 档扩到 11 档:3xl 22px(评分星等字符图形)/ 4xl 26px(统计主数值)/ 5xl 34px(空状态大图标)/ scale-md 0.92em(代码片段)/ scale-sm 0.85em(行内小图标,随文字缩放不建绝对档);迁移脚本按表幂等替换 5 处,font-size-literal 5→0,总计数 40→35 已写基线"
},
{
"id": "g191",
"title": "焦点环变体归零:补语义色环 / 光晕 / 选中环四档",
"motivation": "焦令牌只有强调色两档,评分星(警告色)、校验失败(错误色外扩环)、时间线脉冲(5px 4% 光晕)、色板选中(实色 2px)四处只能自绘,audit 的 focus-ring-variant 长期停在 4 处,也意味着语义色焦点在暗底上没有统一的可调点。",
"acceptance": "tokens 增加 --ua-focus-ring-warning / -danger / -pulse / -selected 四档(亮暗各一套,暗色块必须有覆盖),tokens.json 同步;ui.css 四处自绘环改走令牌;focus-ring-variant 计数为 0,总计数 35→27 并写入新基线;npm run check 全绿。",
"status": "done",
"notes": "四档语义环:warning 25%(评分星)/ danger 18%(校验失败外扩)/ pulse 5px 4%(时间线呼吸光晕,暗色加倍 8%)/ selected 实色 2px(色板选中);暗色块四个都给了覆盖,verify 暗色齐全校验通过;替换后 focus-ring-variant 4→0,同时这 4 条不再计入 shadow-literal,总计数 35→27"
},
{
"id": "g192",
"title": "动效时长字面值归零:补更慢档与循环四档",
"motivation": "三档交互时长上限 0.25s,转圈 0.7/0.8s、扫光 1.4s、脉冲 1.6s、条纹 0.6s、进度条 0.35s 全部无处可归,只能写字面值;duration-literal 一直卡在 9 处,加载动画的节奏也没法统一调。",
"acceptance": "tokens 增加 --ua-duration-slower(0.35s 交互)与 --ua-duration-loop-fast/mid/slow/long(0.6/0.8/1.2/1.6s 循环),tokens.json 同步;visual-rules 增加循环档映射(仅在 animation + infinite 时启用)并扩展交互档到四档;duration-literal 计数为 0,总计数 27→18 并写新基线;npm run check 全绿。",
"status": "done",
"notes": "交互时长三档→四档(新增 slower 0.35s,收 0.3/0.32/0.35 三处大跨度形变);循环四档 0.6/0.8/1.2/1.6s,0.7 与 0.8 并到中档、1.2 与 1.4 并到慢档;迁移脚本按「animation + infinite」判定走循环表,避免把交互过渡误归循环档;9 处全部归零,总计数 27→18"
},
{
"id": "g193",
"title": "阴影字面值归零:补描边族与断口令牌",
"motivation": "shadow-literal 剩下 5 处都不是投影,而是「用 box-shadow 画线」:粘性表头底部细线、日历今天的内框、色板/头像组的底色断口。它们没有令牌可用,只能写字面值,也让投影类目混进了非投影值。",
"acceptance": "tokens 增加描边族 --ua-shadow-hairline / inset-border / inset-accent 与断口 --ua-shadow-gap(亮暗各一套,暗色块必须有覆盖),tokens.json 同步;ui.css 五处改走令牌;shadow-literal 计数为 0,总计数 18→13 并写新基线;npm run check 全绿。",
"status": "done",
"notes": "四档描边族:hairline(inset 0 -1px 0 border-subtle,用阴影画线不占高度)/ inset-border(1px 细内框)/ inset-accent(强调内框标今天)/ gap(0 0 0 2px bg 底色断口,头像组与角标各一处);暗色四档全部覆盖;shadow-literal 5→0,总计数 18→13,剩空间类 13 处"
},
{
"id": "g194",
"title": "审计 json 输出每类命中明细(行号 / 属性 / 值 / 提示)",
"motivation": "ua audit:visual --json 只给计数,排查「哪一行踩了规则」必须临时手写 node -e 调 scanSource —— 本轮迭代里这件事就重复了三次;Agent 与 CI 都拿不到可定位信息,棘轮报红时仍要人工全表搜索。",
"acceptance": "audit:visual --json 在 counts / baseline 之外增加 hits:每个类目一组 { line, prop, value, hint },空类目给空数组;人读模式与退出码不变;cli manifest 的 api 描述同步;npm run check 全绿。",
"status": "done",
"notes": "--json 增加 hits(11 个类目齐全,空类目为空数组),每项含 line / prop / value / hint;实测 space-literal 13 条可定位到具体行与值;人读输出与棘轮退出码未改动,manifest 描述同步后 docs 已重生成"
},
{
"id": "g195",
"title": "内距字面值归零:视觉基线清零(11 类全 0)",
"motivation": "space-literal 剩 13 处,全部是「梯子里没有的零碎值」:3/5/7px 微调、13px 连接线对齐、22px 树缩进、28px 空态留白。它们让基线永远停在 13,也让「视觉零漂移」这个目标永远达不成。",
"acceptance": "tokens 补 --ua-space-6(24px 大面积留白)与 --ua-space-indent(22px 层级缩进);13 处按就近收敛或换用新令牌;space-literal 归零、总计数 13→0 并写入零基线;audit:visual 在零基线下能拦住任何新增字面值(负向自检);npm run check 全绿。",
"status": "done",
"notes": "13 处全部归位:3px 间隙→space-1(4)、5px 微距→space-15(6)、7px 页签→space-15(6)、13px 连接线→space-35(14,与圆点中心对齐)、28px 空态留白→新增 space-6(24)、22px 树缩进→新增 space-indent(22);合计 0 处,基线写为全 0(11 类),负向自检插一条 rgba 投影即报红并指出工作区未提交改动"
}
]
}