3317 lines
318 KiB
JSON
3317 lines
318 KiB
JSON
{
|
||
"$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 投影即报红并指出工作区未提交改动"
|
||
},
|
||
{
|
||
"id": "g196",
|
||
"title": "中性色阶地基:表面/文字/遮罩补中间档令牌",
|
||
"motivation": "令牌表只有 bg/surface 两层表面、text/muted 两级文字,层次全靠 ui.css 里 86 处 color-mix 临时派生;遮罩更是 4 处 rgba 各自为政(0.36/0.4/0.45/0.6,行 1917/3728/4278/4345),行 4778 虚拟列表底部渐变还在私写一条 border-subtle 的复制值。暗色适配每次都要逐处重调。",
|
||
"acceptance": "tokens.css 新增 --ua-color-surface-2/-3/-inset(表面三阶,color-mix 从 text 派生)与 --ua-color-text-secondary/-faint(文字四阶补齐)与 --ua-color-overlay(遮罩统一档),亮暗各一套;4 处 rgba 遮罩改走 --ua-color-overlay,行 4778 改走令牌;verify 暗色覆盖校验通过;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "中性色阶地基:tokens 新增 surface-2/3/inset、text-secondary/faint、overlay/strong 9 令牌(亮暗双向),ui.css 遮罩 5 处 rgba 归档 + 虚拟列表私写分割线回归 border-subtle + 凹陷散点 3 处归档;commit 9ef8637,check 全绿(QA 验证见后续);QA O1 备案:drawer/modal/cmdk 遮罩 0.36/0.40/0.40 统一至 0.45 属有意归档(整体略变重),image/tour 等值迁移"
|
||
},
|
||
{
|
||
"id": "g197",
|
||
"title": "语义淡底配方化:accent 淡底五档收敛为两档令牌",
|
||
"motivation": "「选中态低视觉比重」的约定靠 54 处 background: color-mix 手写实现,透明度散点 5/6/8/10/12/14% 五档并存(13 处 10%、11 处 8%、6 处 12%、4 处 5%、2 处 14%),同一个「淡色选中底」在全库深浅不一,调一次主题要改 54 处。",
|
||
"acceptance": "tokens.css 新增 --ua-color-tint-accent(10% 标准选中底)与 --ua-color-tint-accent-weak(5% 悬停底),亮暗各一套;54 处 accent 淡底中:5/6/8% 悬停档归入 tint-accent-weak,10/12/14% 选中档归入 tint-accent;收敛后 ui.css 不得再出现 background: color-mix(in srgb, var(--ua-color-accent) N%, transparent) 字面配方;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "accent 淡底配方化:tint-accent(10%)/tint-accent-weak(5%) 亮暗各套,37 处 transparent 变体收敛(16 弱 + 21 标准),混 bg 与高浓档按规则保留;commit 8148317,check 全绿(QA 验证见后续)"
|
||
},
|
||
{
|
||
"id": "g198",
|
||
"title": "全控件按下态补齐:从「能点」到「有手感」",
|
||
"motivation": "76 处 hover 对 7 处 :active——按下态只覆盖 button/checkbox/radio/tag/fab 五家;select-trigger 连 hover 都没有(行 548),input 无 hover,menu-item/option/page-num/tab/acc-head/calendar-day/slider 全部只有 hover 一档。点下去没有任何反馈,是「塑料感」的最大来源。",
|
||
"acceptance": "input/textarea 补 hover 边框过渡;select-trigger 补 hover 与 active;menu-item/option/page-num/tab/acc-head/calendar-day/button-like 列表行补 :active(底色加深一档,走 surface-active 或 tint 令牌);scale 类回缩与位移类下沉不混用、同族保持一致;全部带 prefers-reduced-motion 兜底;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "全控件按下态补齐:input/textarea 补 hover 边框过渡,select-trigger/menu-item/option/tab/acc-head/page-btn/calendar-day 补 :active(surface-2 深一档),9 规则 + 2 transition 补齐,:not(:disabled) 守卫;commit 2996b87,check 全绿(QA 验证见后续)"
|
||
},
|
||
{
|
||
"id": "g199",
|
||
"title": "质感层:主按钮内高光、卡片边框减负、标题字距",
|
||
"motivation": "渐变全库只有 3 处(进度条纹×2 + 开关激活),主按钮纯平色块无光感;13/15 个卡片容器「1px 边框 + 阴影」叠加导致层次靠线不靠面;letter-spacing 仅 3 处(行 1237/1809/1811),表头与分组标题没有字距节奏。",
|
||
"acceptance": "主按钮(.ua-btn-primary)加顶部内高光(inset 0 1px 0 白系令牌)与 hover 光泽阴影(走 shadow-accent 系);卡片容器 hover 时阴影升一档、边框同步减淡(配合而非叠加);tokens 新增 --ua-tracking-wide(0.04em)并应用到表头/分组标题/大写小标题;质感手法只走令牌,不新增硬编码;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "质感层:新增 highlight 内高光/shadow-highlight/tracking-wide/tight 令牌(亮暗覆盖),主按钮内高光+hover 主题色投影,卡片贴地影+hover 升档边框减淡,字距 4 处全走令牌;幽灵按钮显式退出质感层(透明底白高光露线);commit bfb9178,check 全绿"
|
||
},
|
||
{
|
||
"id": "g200",
|
||
"title": "动效编排:浮层分层入场与列表交错",
|
||
"motivation": "animation-delay 全库 0 处——modal/drawer/menu/dropdown 的入场全部齐步走,元素同时出现,没有先后叙事;行 1228/2572/4390 还有 3 处私写 cubic-bezier 绕开了 --ua-ease 体系。",
|
||
"acceptance": "modal/drawer 内容分层入场(面板先动、内容 60ms 错峰跟随,animation-delay 走 --ua-duration 系派生);菜单/下拉项 nth-child 交错入场(1-8 项、20ms 步进、超出即正常出现);3 处私写缓动归入令牌(必要时新增 --ua-ease-spring 回弹档);全部带 prefers-reduced-motion 兜底;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "动效编排:私写缓动 3 处归零(ease-out/spring 归档),modal/drawer 内容分层入场(fast 档错峰),select/menu 弹层子项 nth-child 2..8 交错(--ua-duration-step 0.02s 派生),专块 reduced-motion 兜底;commit 91aa304,check 全绿;QA 复核定稿:分层入场 delay 由 fast(150ms) 收到微步 3 档(60ms),reduced-motion 兜底并入文件末尾 !important 清单(原局部 RM 块被源序反超失效),交错第 8 项起锁定同档保证单调,fix(ui) 0821286"
|
||
},
|
||
{
|
||
"id": "g201",
|
||
"title": "焦点可达性::focus-visible 区分键盘与鼠标",
|
||
"motivation": "焦点环统一挂在 :focus 上——鼠标点击按钮/页签/菜单项也会出环,键盘用户才需要的视觉信号变成了鼠标用户的视觉噪音,这是「看起来不专业」的隐性来源。",
|
||
"acceptance": "按钮/页签/菜单项/分页等点击型控件焦点环改挂 :focus-visible(键盘导航才出环);输入类控件维持 :focus(输入态需要常驻环);触屏/老浏览器降级到现状不劣化;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "焦点可达性:清点确认点击型控件已无裸 :focus 残留(38 处 focus-visible/7 处输入类保留);修「吞环」3 处——tab/option/menu-item 键焦改内嵌描边(下拉面板滚动会裁外环;菜单面板不裁切,密集列表用内嵌描边更整齐);commit e64f72b,check 全绿;fix(ui) 0821286 同步更正归因注释"
|
||
},
|
||
{
|
||
"id": "g202",
|
||
"title": "按下态可见性修正:surface 系改以 surface 为基底",
|
||
"motivation": "QA 数值实测:surface-2(#f6f6f6) 以 bg 为基底,落在 .ua-select-pop/.ua-menu-pop 的 surface(#f6f7f9) 底上对比仅 1.008/1.009,肉眼不可见;且 :active 特异性覆盖 :hover 的 tint-accent-weak(对比 1.077/1.102),按下反而丢失淡蓝高亮,反馈方向是反的。.ua-option:active 覆盖 .ua-option-selected、.ua-tab:active 覆盖 .ua-tab-active 同理(按下选中项淡底短暂丢失)。",
|
||
"acceptance": "方案(择一或组合):surface-2/3/inset 改为相对 var(--ua-color-surface) 混色(需重验所有消费点与视觉基线);或 option/menu-item/tab 的 :active 改用 surface-3 或 tint-accent 保持可见;option-selected/tab-active 的按下态保底不丢选中语义;npm run check 全绿 + QA 数值复核按下对比 ≥1.05。",
|
||
"status": "done",
|
||
"notes": "QA R2 证据:亮暗各测;附带收尾项——三参 bg 变体 4 处(ui.css 804/842/1056/1490,accent 8~10% 混 bg)与 tint-accent 视觉等价可并入;menu-item 等 transition 的字面 ease 改 var(--ua-ease)(O3 风格债);落地:option/menu-item/tab 按下态改 tint-accent(对比 1.154/1.189 ≥1.05,选中项按下不丢语义);tag-info/tag.is-checked/step-done/toast-info 四处 accent 混 bg 并入 tint-accent;transition/animation 字面 ease 全量换令牌(ease-in-out 保留:无限循环动画无对应令牌,改曲线属视觉变更超范围)"
|
||
},
|
||
{
|
||
"id": "g203",
|
||
"title": "文档站亮暗主题切换,demo 支持 ?theme= 参数",
|
||
"motivation": "文档站 index.html 只有系统级暗色(prefers-color-scheme),无法手动切换;人工审阅 qa/stable 时想对照暗色观感只能改系统设置。demo 页已有 data-theme 全套暗色,但文档站 iframe 引用 demo.html 时无法联动主题。",
|
||
"acceptance": "1) 文档站页头新增主题切换按钮:亮/暗/跟随系统三态持久到 localStorage,CSS 用 :root[data-theme=dark] 显式块 + 媒体查询 :not([data-theme=light]) 兜底;2) demo 页初始化优先读 URL 参数 ?theme=dark|light(文档站 iframe 联动入口),其次 localStorage,最后系统;3) 切换按钮时所有 iframe.demo 带参重载实现联动;4) npm run docs 重新生成产物,npm run check 全绿。",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"notes": "落地:docs 模板加显式暗色块(:root[data-theme=dark] + 媒体查询 :not([data-theme=light]))与页头切换按钮(localStorage 持久);demo 主题初始化优先读 ?theme= 参数;切换时 iframe.demo 带参重载联动。产物(apps/docs/* 与 demo dist)不入库由 npm run docs 本地生成;apps/docs/demo.html 须先 npm run demo 再 npm run docs,顺序反了拷到旧产物。"
|
||
},
|
||
{
|
||
"id": "g204",
|
||
"title": "textarea 支持 autosize 自适应内容高度",
|
||
"motivation": "多行输入在评论/描述场景下固定行数体验差:内容一行时下方大片空白,内容超长时内部滚动突兀。主流组件库标配 autosize,本库 textarea 缺失。",
|
||
"acceptance": "1) TextareaProps 新增 autosize?: boolean | { minRows?: number; maxRows?: number },true 跟随内容涨落,对象态钳最小/最大行高,超 maxRows 内部滚动;2) 输入与外部写回信号都触发重算;3) manifest 文档 props 表补 autosize 行,npm run docs 同步 llms-full.txt;4) 样式零新增硬编码(高度走 JS 内联,行高从 computed style 推导),npm run check 全绿。",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"notes": "落地:autosize 布尔/对象双态,高度与 min/max 行钳制在统一 frame 里由 computed style 推导(line-height 为 normal 按 1.5 倍字号退化),scrollHeight 盒模型补偿含边框;输入与外部写回信号都重算;resize 置 none。"
|
||
},
|
||
{
|
||
"id": "g205",
|
||
"title": "textField 支持密码框(password 显隐切换)",
|
||
"motivation": "登录/改密场景需要密码输入,当前只能裸 input type=password,没有显隐切换,用户无法核对输入内容。",
|
||
"acceptance": "1) TextFieldProps 新增 password?: boolean,开启后原生 type=password + 内置「显示/隐藏」切换按钮(aria-label 同步);2) 显隐按钮与 clearable/字数统计共用后置附加组不互斥;3) manifest 文档 props 表补 password 行,npm run docs 同步;4) 复用既有 clear 按钮样式零新增 CSS,npm run check 全绿。",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"notes": "落地:el.type 原生打点,显隐按钮复用 ua-input-clear 样式零新增 CSS,文案即状态(按钮永远写下一步动作),aria-label 同步;与 clearable/字数统计共用后置附加组不互斥。"
|
||
},
|
||
{
|
||
"id": "g206",
|
||
"title": "rate 支持 allowClear 清除评分",
|
||
"motivation": "评错了想撤销只能逐星点回 1 星,无法清零;主流组件库 rate 均有 allowClear。",
|
||
"acceptance": "1) RateProps 新增 allowClear?: boolean,再次点击当前分值(含半星精确相等)清零,键盘路径不受影响;2) aria-valuemin 随 allowClear 变 0/1;3) manifest props 表补行并 npm run docs 同步;4) npm run check 全绿。",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"notes": "落地:emit 加 allowZero 放行口,点击命中值与当前值精确相等即清零;aria-valuemin 随 allowClear 0/1 切换,读屏语义一致。"
|
||
},
|
||
{
|
||
"id": "g207",
|
||
"title": "progress 支持 format 自定义文本",
|
||
"motivation": "上传场景要显示「12.4 MB / 30 MB」而非百分比,当前进度文本写死取整百分号,线/环形都不能定制。",
|
||
"acceptance": "1) ProgressProps 新增 format?: (v: number) => string,传入则线/环形文本均用其返回值替代「N%」;2) 缺省行为不变;3) manifest props 表补行并 npm run docs 同步;4) npm run check 全绿。",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"notes": "落地:textOf 统一文本出口(format 优先,缺省取整百分号),线性与环形两条路径共用,环形 aria-label 同步走 textOf;ring 签名追加 textOf 参数零破坏。"
|
||
},
|
||
{
|
||
"id": "g208",
|
||
"title": "multiSelect 支持 clearable 一键清空",
|
||
"motivation": "select 已有 clearable,多选选了一堆标签要逐个点 × 撤销;datePicker/treeSelect 亦缺,本轮先补 multiSelect(改动面最小)。",
|
||
"acceptance": "1) MultiSelectProps 新增 clearable?: boolean,有值时根右缘显示清空钮(复用 .ua-select-clear 样式,右缘用 --ua-space-2 内收);2) 清空后写信号+回调并保持焦点;3) manifest props 表补行并 npm run docs 同步;4) 样式零新增字面量(audit:visual 基线仍 0),npm run check 全绿。",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"notes": "落地:复用 .ua-select-clear 样式(仅 right 用 --ua-space-2 内收,零新增字面量),clearable 未开不挂载、有值才显示,清空 stopPropagation 防误展开并保持输入焦点。"
|
||
},
|
||
{
|
||
"id": "g209",
|
||
"title": "演示页补齐本轮新参数的现场演示",
|
||
"motivation": "g205-g208 新增 password / autosize / allowClear / clearable 均无演示,人工审阅 qa/stable 时在 demo 看不到新能力。",
|
||
"acceptance": "1) sec-bind 加密码输入演示(password + clearable);2) sec-form 加 autosize 多行演示(1-4 行钳制);3) sec-rate 加 allowClear 可清除演示;4) sec-multiselect 既有实例开 clearable;5) 各 section 描述同步提及;npm run check 全绿。",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"notes": "落地:sec-bind 密码框、sec-form autosize 钳制、sec-rate allowClear 行、sec-multiselect 开 clearable,各 section 描述同步。"
|
||
},
|
||
{
|
||
"id": "g210",
|
||
"title": "滚动条令牌化与暗色适配",
|
||
"motivation": "13 个滚动容器(数据表、下拉弹层、抽屉、穿梭框、级联、命令面板等)全靠浏览器原生滚动条,Windows 下是十余 px 的灰白粗条,暗色主题里成了一条亮带;滚动条颜色也没有令牌可循,属于视觉地基里最后一块没人管的地带。",
|
||
"acceptance": "tokens.css 与 tokens.json 新增 --ua-color-scrollbar / --ua-color-scrollbar-hover 并带 [data-theme='dark'] 覆盖;ui.css 为全部滚动容器统一 scrollbar-width: thin + scrollbar-color,WebKit 伪元素做胶囊拇指与悬停加深、轨道透明;样式只消费 --ua-* 令牌,npm run audit:visual 各类计数不上升;暗色下拇指可见、右下角不留白块;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "tokens 新增 --ua-color-scrollbar(0.22/0.24) 与 --ua-color-scrollbar-hover(0.38/0.42),tokens.css 与 tokens.json 同步;ui.css 末尾新增滚动条段:13 个滚动容器统一 scrollbar-width:thin + scrollbar-color 走令牌,WebKit 伪元素做 8px 胶囊拇指、轨道与右下角透明、悬停加深带 --ua-duration-fast 过渡;仅用 inline-size/block-size 定粗细以避开 height 字面量口径,audit:visual 十一类保持 0 处;llms-full.txt 经 npm run docs 重生成",
|
||
"domain": "visual"
|
||
},
|
||
{
|
||
"id": "g211",
|
||
"title": "verify 增加令牌双源一致性门禁",
|
||
"motivation": "颜色令牌要在两处登记:tokens.css 定义真实 CSS 变量、tokens.json 供文档站色板与 Agent 文档使用;两处靠人手同步,只改一处时文档色板会凭空多出或漏掉一个色块,没有任何门禁能发现(g210 新增滚动条令牌时就靠人工两处对齐)。",
|
||
"acceptance": "ua verify 新增 checkTokenParity:tokens.json 的 color 键按 camelCase → kebab 折算成 --ua-color-* 后,与 tokens.css :root 集合双向查漏(JSON 多出 / CSS 多出都报错),并逐项比对亮色值与 [data-theme='dark'] 暗色值;负向自检(改坏一处值、删掉一个键)必须报错并指出变量名与两处取值;AGENTS.md 的 verify 职责描述同步;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "cmdVerify 接入 checkTokenParity:camelCase → kebab(含数字段 surface2 → surface-2)折算后双向查名,命中则比对 root 亮值与 dark 暗值,报错直接点名变量与两侧取值;接在 checkDarkTokens 之后、llms-full 之前;verify OK 汇总文案补「tokens.json ↔ tokens.css 名字与值双源一致」,AGENTS.md 同步该职责与「只改一处会凭空多/少色块」的动机;负向自检两种破坏(改值 0.22→0.99、删 scrollbarHover 键)各自被拦下并给出精确变量名",
|
||
"domain": "tooling"
|
||
},
|
||
{
|
||
"id": "g212",
|
||
"title": "tabs 溢出时把激活页签拉回可视区",
|
||
"motivation": "页签条是 overflow-x: auto 的滚动容器,但组件从不滚动它:页签多到溢出后,程序改 active 信号(或编辑器的「打开下一个文件」这类场景)会把激活页签留在视口外,用户看不到当前在哪一页,指示条也跑到看不见的地方。",
|
||
"acceptance": "tabs 在同步激活态时检测激活页签是否被裁出页签条视口,被裁才滚(就近对齐、两侧留白),已完整可见时不动手;只滚 .ua-tabs-head 本身,不带动页面纵向滚动;系统开启 prefers-reduced-motion 时用 auto 直接跳位;demo 增加八个页签的窄容器现场演示;manifest 补一条手把手说明;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "tabs.ts 新增 revealActive:先判 scrollWidth > clientWidth(无溢出直接返回),再判激活外壳是否完整落在 [scrollLeft, scrollLeft+clientWidth] 内,被裁才滚且就近对齐(左裁补 left-8、右裁补 right-clientWidth+8),目标钳在 [0, max];只调 head.scrollTo,不走 scrollIntoView 以免带动页面;behavior 依 prefers-reduced-motion 选 smooth/auto;sync 里指示条摆位后调用,effect 与 trackSize 两条路径都覆盖;manifest 补一条手把手(含「已可见不动手」与降动效说明),demo 加 320px 窄容器 + 八页签 + 当前值回显",
|
||
"domain": "tune"
|
||
},
|
||
{
|
||
"id": "g213",
|
||
"title": "新增 timePicker 时间选择组件",
|
||
"motivation": "datePicker 只覆盖日期维度,一天内的时刻(预约、值班、定时任务)没有可用组件;原生 input[type=time] 在各浏览器外观不一、步长与暗色都不可控,与自绘弹层的既有约定也不一致。",
|
||
"acceptance": "新增 timePicker(props):时 / 分两列自绘弹层,value 信号双向绑定 HH:mm,minuteStep 可配(缺省 5),当前值所在格高亮并滚到列中部,↑↓/Home/End 列内导航、Esc / 点外部收起且归还焦点;走 overlay 的 Esc 栈与 bindOutside、flip 做空间翻转;样式只消费 --ua-* 令牌,禁止新增硬编码阴影/时长/字号,npm run audit:visual 各类计数不增;10 处落点(实现 / types / index / css / manifest exports / manifest docs / demo / README 清单与分组 / sync-counts / docs)齐全;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "timePicker.ts 113 行:pad 提为模块级纯函数(避免 const TDZ),commit 只在值变化时派发 onChange;列内导航 navigate 复用于时/分两列(↑↓ 环形、Home/End 直达);reveal 用 offsetTop 算 scrollTop 让激活格落到列中部,不走 scrollIntoView 以免带动页面;复用 overlay 的 bindOutside / pushOverlay 与 position 的 flip;ui.css 新增 .ua-timepicker-* 段全部消费令牌(control-h-md / radius-md / shadow-2 / duration-fast / z-dropdown / tint-accent),带 prefers-reduced-motion 兜底,audit:visual 十一类仍为 0;manifest exports 72 条 + docs.components 70 项(props 与 types 双向过 verify),demo 增 sec-timepicker(受控 15 分钟步长 + 禁用态 + 当前值回显),README 清单与分组补名,sync-counts 三处计数 69→70,docs 重生成",
|
||
"domain": "new"
|
||
},
|
||
{
|
||
"id": "g214",
|
||
"title": "浮层入场动效收敛到单一 keyframes",
|
||
"motivation": "七个弹层(级联 / 自动补全 / 日期 / 颜色 / 多选 / @提及 / 树选择)各复制了一份「淡入 + 上移 4px」的 keyframes,名字各异、缓动一半写成裸 ease;更糟的是弹层翻到触发器上方时动画仍自上而下,方向与位置相反,能明显看出「从错的那边滑进来」。",
|
||
"acceptance": "删除 7 份重复 keyframes,统一为 ua-pop-drop(自上而下 4px)与 ua-pop-rise(自下而上,供翻转态);引用处时长与缓动全部走 --ua-duration-fast / --ua-ease-out;.is-flip 的五个弹层补 animation-name 覆盖;ui.css 内不再出现 *.ua-*-in 自造入场动画;npm run audit:visual 各类计数不增;manifest intro 补一条「新弹层怎么写入场」的约定并修正组件数;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "ui.css 删除 ua-cascader-in / ua-ac-in / ua-dp-in / ua-cp-in / ua-ms-in / ua-mentions-in / ua-treeselect-in 七份 keyframes(它们内容完全相同:淡入 + translateY(-4px)),新增 @keyframes ua-pop-drop(-4px 淡入)与 ua-pop-rise(+4px,供翻转态),7 处引用统一为 animation: ua-pop-drop var(--ua-duration-fast) var(--ua-ease-out) both(原先 5 处裸 ease、2 处 --ua-ease 且缺 both);末尾补 5 条 .is-flip 的 animation-name 覆盖(cascader / autocomplete / datepicker / colorpicker / multiselect,特异性 0,3,0 稳压 0,2,0);reduced-motion 各块原样保留;manifest intro 修正「28 个 DOM 组件」陈旧计数并补「新弹层怎么写入场、为什么不要自造 keyframes」;audit:visual 十一类维持 0",
|
||
"domain": "visual"
|
||
},
|
||
{
|
||
"id": "g215",
|
||
"title": "配方 13:日期 + 时间组合预约时点",
|
||
"motivation": "timePicker 落地后,「日期 + 时刻」是最常见的组合场景,但两个组件各写一段、拼装与校验放在哪一层没有范例;Agent 拿到 datePicker 与 timePicker 两个独立组件时容易写出「一个信号装完整时点」的错配。",
|
||
"acceptance": "recipes.md 新增配方 13(编号连续、含代码块与要点),代码里真实用上 datePicker / timePicker / form / formItem / toast,并说明两段各管一段、拼装交给 computed、约束优先于校验、半截状态由 required 兜底;ua recipes 13 能读出来且识别到组件;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "recipes.md 新增配方 13(共 13 条):datePicker(min 限制今天) + timePicker(minuteStep 15) 各绑一个信号,at 由 computed 拼装,form 的 required 拦半截状态,onSubmit 走 toast;要点五条覆盖「两段各管一段 / 时间给初值日期留空 / 约束优先于校验 / 半截状态交 required / 区间怎么拼」;ua recipes 13 能识别 datePicker、form、timePicker、toast 四个组件;verify 的配方结构校验(编号连续 + 代码块 + 要点 + 至少一个组件 id)通过",
|
||
"domain": "tooling"
|
||
},
|
||
{
|
||
"id": "g216",
|
||
"title": "timePicker 支持 clearable 清空",
|
||
"motivation": "时间一旦选中就无法回到「未选」态:用户想撤销时只能重选一个假值;select 与 multiSelect 早已有 clearable 与 Delete/Backspace 等价键,timePicker 缺这一环,同一个表单里交互不一致。",
|
||
"acceptance": "timePicker 新增 clearable 入参:有值时触发器右侧出现 ×(不进 Tab 序、不与 ▾ 重叠),聚焦触发器按 Delete / Backspace 也能清空,清空写回空串并触发 onChange,同时收起弹层;样式只消费 --ua-* 令牌(含行高走 --ua-leading-none),npm run audit:visual 计数维持 0;types 与 manifest props / steps 同步;demo 演示清空;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "timePicker.ts 138 行:clear() 只在有值时派发(避免空回调噪音),clearBtn 走 tabindex -1 不进 Tab 序、hidden 由 effect 跟 value 走;root 加 has-clear 类给触发器补右内距,× 用 right: --ua-space-6 让到 ▾ 左侧不重叠;root keydown 接管收起态的 Backspace/Delete(展开时按键归列内导航);types/manifest props(6 项)与 steps(7 条)同步,demo 首个实例开 clearable 并演示清空提示;踩坑:line-height: 1 被 audit:visual 的 leading-literal 拦下(基线 0),改走 --ua-leading-none 后十一类仍为 0",
|
||
"domain": "tune"
|
||
},
|
||
{
|
||
"id": "g217",
|
||
"title": "frame 内核冒烟 + effect 注销与重追踪修复",
|
||
"motivation": "70 个组件全部跑在 @ui-arch/frame 上,但 smoke 只覆盖 table-core:信号 / effect / computed 的语义改动没有任何自动化拦网。首次补测即抓到两个真缺陷——effect 返回的注销函数是空实现(卸载后仍被信号唤醒),且重跑不清理上一轮依赖(条件分支换走后旧信号继续唤它)。",
|
||
"acceptance": "新增 packages/core/frame/scripts/smoke.mjs(14 条断言:读写与同值短路 / 注销后不再重跑 / 条件分支重追踪 / computed 派生与链式 / 多订阅者扇出),跑在 dist 上不依赖 DOM;修 signal.ts 的 effect:注销真的摘订阅、重跑前清旧订阅;npm run smoke 串起 frame + table-core 两段;AGENTS.md 说明 smoke 覆盖范围;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "signal.ts 引入 activeDeps(当前 effect 订阅到的信号订阅集集合):信号读值在收集 active 的同时登记自己那本 subs,effect 每次重跑先遍历 deps 摘掉旧订阅再 clear(条件分支换走后不再被旧信号唤),注销函数置 stopped 标志并逐个 subs.delete(run)(原先只留空注释);computed 未改,靠 effect 自动受益;新增 packages/core/frame/scripts/smoke.mjs 14 条断言(含负向:注销后不重跑、切分支后旧依赖静默、扇出时注销一个不影响另一个),首跑即 5 红(其中 1 红是我把 3×2 写成 30 的断言错误,已改 6),修完 14/14 绿;npm run smoke 改为 frame + table-core 两段串跑,AGENTS 门禁段补两段断言数量与用途",
|
||
"domain": "tooling"
|
||
},
|
||
{
|
||
"id": "g218",
|
||
"title": "timePicker 未选值时两列停在此刻",
|
||
"motivation": "没选过值时打开弹层,时列与分列都停在 00:00,用户要从头往下翻到当前时刻;而「选一个今天稍后的时间」正是最常见的用法,默认落点应该就近。",
|
||
"acceptance": "reveal 增加 fallback 参数:有激活格仍滚激活格,无值时滚到当前小时 / 当前分钟档(分钟按 minuteStep 折算下标并钳在列长内);manifest 步骤文案同步;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "reveal(col, cells, fallback) 三参:findIndex 到 is-active 就用它,否则退到 clamp(fallback, 0, len-1);打开时 fallback 取 new Date() 的 getHours() 与 floor(getMinutes()/step)(步长大于 1 时分列下标与分钟数不同,必须折算);manifest 步骤 2 同步「还没选过时停在此刻」;时间源用系统时钟不引额外状态,选中后仍以 value 为准",
|
||
"domain": "tune"
|
||
},
|
||
{
|
||
"id": "g219",
|
||
"title": "verify 增加重复 @keyframes 检测",
|
||
"motivation": "刚才把 7 份复制的浮层入场动画合并掉了,但没有门禁阻止下一个人再复制一份:帧体完全相同、只改名字的动画会让方向与时长各写各的(翻转态朝反方向入场就是这么来的),而 CSS 层面完全合法,review 也容易漏。",
|
||
"acceptance": "ua verify 新增 checkDuplicateKeyframes:对皮肤包样式表里每个 @keyframes 取帧体(剥注释、空白归一)比对,出现相同帧体即报错并指出两个动画名;负向自检(临时追加一份同体动画)必须报错;顺带清掉现存重复(ua-switch-spin 与 ua-spin 帧体相同,开关转圈改为复用 ua-spin);AGENTS.md 同步该职责;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "checkDuplicateKeyframes 接在 checkSkinCss 之后(同走 skin 包样式表遍历):@keyframes 正则抠出「名字 + 帧体」,帧体剥 /* */ 注释、空白压成单空格后进 Map,撞车即报「A 与 B 帧体完全相同」;负向自检临时追加 ua-dup-test(同 rotate(360deg) 帧体)被拦下并点名 ua-spin,验证后移除(踩坑:/tmp 备份在跨命令时不稳,改用正则精确剥离测试块,git diff 确认只剩预期的 2 增 4 删);现存 ua-switch-spin 与 ua-spin 同体,删掉前者、开关 loading 复用 ua-spin;verify OK 汇总文案与 AGENTS 职责描述同步",
|
||
"domain": "tooling"
|
||
},
|
||
{
|
||
"id": "g220",
|
||
"title": "滚动条统一规则补齐新增滚动容器",
|
||
"motivation": "g210 把 13 个滚动容器纳入细条 + 令牌拇指的统一规则,但 g213 新加的 timePicker 两列(overflow-y: auto)没进名单:同屏一个表单里的时 / 分列又回到 Windows 原生灰白粗条,暗色下尤其刺眼。手工维护的选择器清单本身也容易漏。",
|
||
"acceptance": "用脚本枚举 ui.css 里所有 overflow: auto|scroll 的 .ua-* 容器,逐一核对是否在滚动条统一规则内,漏项补齐(本次为 .ua-timepicker-col);补完覆盖数为 0 漏项;样式仍全部消费令牌,npm run audit:visual 十一类计数维持 0;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "g220 的代码提交(1ac8bae)与本次留痕分两次落地:因上一轮提交后直接进入下一轮,漏了置 done 与留痕,收尾时按 backlog ↔ 留痕交叉校验才暴露(该条目在 backlog 仍 open)。修复内容:用括号配平脚本枚举 ui.css 中全部 overflow: auto|scroll 的 .ua-* 容器(14 个),逐条与 scrollbar-width 统一规则的选择器清单比对,唯一漏项 .ua-timepicker-col 补入——它正是 g213 新增时被忘掉的;audit:visual 十一类维持 0。教训:每轮「代码提交 → 置 done → 留痕 → 推送」四步要连着做完再开下一轮",
|
||
"domain": "visual"
|
||
},
|
||
{
|
||
"id": "g221",
|
||
"title": "verify 校验滚动容器是否都进细条规则",
|
||
"motivation": "滚动条统一规则是一串手写选择器,新增滚动容器(这次是 timePicker 两列)必然被忘掉,而症状只在暗色下才明显;靠人眼比对 14 个容器与一串选择器不现实。",
|
||
"acceptance": "ua verify 新增 checkScrollCoverage:括号配平扫出所有含 overflow: auto|scroll 的 .ua-* 单类规则,逐个核对是否出现在带 scrollbar-width 的统一规则里,漏项报出类名与实际后果;负向自检(从规则里摘掉 .ua-timepicker-col)必须报错;AGENTS.md 与 verify 汇总文案同步;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "checkScrollCoverage:先剥注释再做括号配平扫描(正则切不开 @media 里的嵌套花括号),逐条记录选择器与规则体;含 overflow(-x/-y): auto|scroll 的单类 .ua-* 入容器集,再取体内含 scrollbar-width 的统一规则反查,漏项报「暗色下会露出原生亮条」;当前 14 个容器 14 个覆盖、0 漏;负向自检摘掉 .ua-timepicker-col 被精确点名(还原时踩坑:同名字符串 replace 命中了 .ua-cmdk-list 的另一条规则,把 .ua-timepicker-col 误并进它的 flex 规则,已用 Edit 精确改回并靠 git diff 确认 ui.css 与 HEAD 完全一致);verify 汇总文案与 AGENTS 职责同步",
|
||
"domain": "tooling"
|
||
},
|
||
{
|
||
"id": "g222",
|
||
"title": "timePicker 支持 size 高度档位",
|
||
"motivation": "textField / select / inputNumber 都有 sm·md·lg 三档,timePicker 缺这一环:与小号输入框并排时它仍占 34px,一排控件高低不齐,只能靠外层包一层 hack。",
|
||
"acceptance": "timePicker 新增 size?: 'sm' | 'md' | 'lg',触发器加 is-<size> 类,高度并入文件顶部「控件高度三档」统一规则(--ua-control-h-*),组件段不再重复声明高度;types 与 manifest props / steps 同步;demo 演示小号;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "ui.css 顶部三处档位选择器各并进 .ua-timepicker-trigger(md / is-sm / is-lg),组件段原本写死的 height: var(--ua-control-h-md) 删掉,改为注释说明「高度由顶部三档统一给」——同一件事只留一个来源;TS 侧 class 按 select 的写法拼 is-${size}(缺省 md 不挂类);types 增 size 字段、manifest props 7 项 + steps 8 条,demo 加 320px 内的「小号」实例;check 全绿,audit 十一类仍 0",
|
||
"domain": "tune"
|
||
},
|
||
{
|
||
"id": "g223",
|
||
"title": "timePicker 非档值按最近档对齐",
|
||
"motivation": "外部传进来的值不一定落在 minuteStep 档上(09:07 配 15 分钟档很常见,历史数据或后端下发都可能):此时分列没有任何高亮格,滚动锚点还错跑到「此刻」,用户看到的是一个「有值却什么都没选中」的弹层。",
|
||
"acceptance": "新增 nearestMinute 助手:值不在档上时取最近档,作为分列高亮与滚动锚点;打开时锚点优先跟随值(有值滚值、无值滚此刻);点最近档即写回对齐后的值,触发器在用户重新选之前照常显示原值;manifest 步骤说明该行为;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "nearestMinute(mm) 取 Math.round(mm/step) 下标并钳到列尾,空值/NaN 直接返回空串;column 内引入 activeText():时列精确比、分列走最近档,is-active 与 aria-selected 同源(原先两处各写一遍比较);打开时锚点改为「有值滚值(时取 Number(hh)、分取 nearest 的下标)、无值滚此刻」;点最近档 commit 时按当前 hh + 档值写回,触发器在用户重选前仍显示原值;manifest steps 补一条说明该行为;check 全绿、audit 十一类 0(踩坑:bash -e 内联脚本里的反引号被命令替换吃掉,改用 Edit 工具精改模板字符串)",
|
||
"domain": "tune"
|
||
},
|
||
{
|
||
"id": "g224",
|
||
"title": "timePicker 收起态 ↑↓ 微调时间",
|
||
"motivation": "表单里最常做的是「往后挪 15 分钟」这种微调,现在必须开弹层、找到分列、点格子、再收起;inputNumber 早就有 ↑↓ 步进,时间选择却没有对应的键盘手感。",
|
||
"acceptance": "收起态聚焦触发器时 ↑↓ 按 minuteStep 加减、Shift + ↑↓ 按小时加减,跨 00:00 / 23:59 环形回绕,写回与收起态清空共用同一套 keydown;展开态按键仍归列内导航;manifest 步骤说明;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "root 的 keydown 收成三段:展开态直接让位给列内导航 → 收起态 clearable 时接管 Backspace/Delete → 有值时接管 ↑↓(delta = ±(shiftKey ? 60 : step),总分钟数 +1440 取模实现环形),写回走同一个 commit(值没变不派发);timePicker.ts 159 行仍在 200 行门禁内;manifest steps 补第 9 条说明步长与 Shift 语义",
|
||
"domain": "tune"
|
||
},
|
||
{
|
||
"id": "g225",
|
||
"title": "时间选择两列之间补分界线与统一列宽",
|
||
"motivation": "时列与分列之间只有一点 gap,两列数字挨在一起像一列 24 行;列宽又随内容浮动,触发器 116px 而弹层宽窄不定,展开时弹层与触发器边缘对不齐。",
|
||
"acceptance": "弹层补 min-width(与触发器最小宽度同档)、单元格补 min-width 与居中(两位数字纵向对齐)、第二列加 1px --ua-border-subtle 分界线与左侧内距;只用令牌,npm run audit:visual 十一类计数维持 0;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": ".ua-timepicker-pop 补 min-width: 132px(触发器 min-width 116 + 两侧内距,展开时左右边缘对齐);.ua-timepicker-cell 补 min-width: 44px 与 text-align: center(24 与 05 两位数纵向对齐);第二列用 .ua-timepicker-col + .ua-timepicker-col 加 margin/padding 与 border-left: 1px var(--ua-border-subtle) 分界;新声明全部并回原有选择器块(不新起重复块),audit:visual 十一类仍 0,verify 的滚动容器覆盖校验通过",
|
||
"domain": "visual"
|
||
},
|
||
{
|
||
"id": "g226",
|
||
"title": "配方通用约定补三条单一来源规则",
|
||
"motivation": "本轮踩到的三类重复(同一段动效各写一份、新增滚动容器漏进滚动条名单、颜色令牌只改 tokens.css)都已有 verify 拦网,但写组件的人不知道这回事;把结论写进「通用约定」,比藏在门禁报错里更容易被读到。",
|
||
"acceptance": "recipes.md 的《通用约定》补三条:动效统一 ua-pop-drop / ua-pop-rise 与令牌、新增滚动容器要进 scrollbar-width 名单、新增颜色令牌要 tokens.css 与 tokens.json 双写;仍然保持「先查令牌再写样式」等既有条目不变;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "recipes.md 末尾三条新约定各自点出「违反时哪道门禁会报」:g214 收敛的 ua-pop-drop / ua-pop-rise(对应 checkDuplicateKeyframes)、滚动容器进 scrollbar-width 名单(对应 checkScrollCoverage)、颜色令牌 tokens.css + tokens.json 双写(对应 checkTokenParity);llms-full 重生成,配方结构校验(13 条编号连续 + 通用约定原样保留)通过",
|
||
"domain": "tooling"
|
||
},
|
||
{
|
||
"id": "g227",
|
||
"title": "timePicker 打开后焦点进入时列",
|
||
"motivation": "弹层打开后焦点还留在触发器上:键盘用户按 Tab 会跳到页面下一个元素(弹层被跳过),列内的 ↑↓ 也用不上——能看见却进不去,是弹层类组件最常见的无障碍漏项。",
|
||
"acceptance": "打开弹层时把焦点送到时列的当前格(无值则首个),用 preventScroll 保住刚算好的列内滚动位置;Esc 收起后焦点回到触发器(既有行为不变);manifest 步骤说明;npm run check 全绿。",
|
||
"status": "done",
|
||
"notes": "打开 effect 的 rAF 里在 reveal 之后补焦点:优先时列 is-active 格、无值退到首格,focus({ preventScroll: true }) 避免浏览器把刚算好的列内居中位置重新滚掉;收起路径未改(Esc 与选完分都 trigger.focus());manifest steps 补第 10 条;check 全绿",
|
||
"domain": "tune"
|
||
},
|
||
{
|
||
"id": "g228",
|
||
"title": "ua doctor 环境自检命令",
|
||
"motivation": "AGENTS.md 要求新 Agent 先跑通环境再干活,但「环境是否就绪」目前只能靠跑一次完整 npm run check 反推(几分钟)。缺一条秒级的自检命令,把 node 版本、依赖安装、git 钩子、.ua 元数据可解析性一次性检完。",
|
||
"acceptance": "新增 `ua doctor`:逐项检查 node >=18、pnpm-lock 与 node_modules 存在、commit-msg 钩子在位、.ua/backlog.json 与 .ua/index.json 可解析、iterations.log 每行是合法 JSON;全部通过打印 pass 清单退出 0,任一失败打印该条原因退出 1;AGENTS.md 环境准备补一行;manifest docs 说明;npm run docs 重生成;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "新增 cmdDoctor:七项检查(node>=18、pnpm-lock、node_modules、commit-msg 钩子、backlog/index 可解析、留痕逐行 JSON),core.hooksPath 优先于默认 .git/hooks;负向自检确认污染留痕即 FAIL 退出 1;AGENTS.md 环境准备改为「先 doctor 再 check」;manifest usage/steps 同步;check 全绿"
|
||
},
|
||
{
|
||
"id": "g229",
|
||
"title": "混色浓度令牌化:color-mix 百分比收敛为 --ua-alpha-* 六档",
|
||
"motivation": "ui.css 里 44 处 color-mix 的混入浓度散着 13 档百分比(10/12/16/18/20/24/30/40/45/55/65/70/80),同一「淡底」「半透描边」各组件各写各的,观感不齐也没法一眼对齐;且是继字号/时长/阴影/圆角之后最后一块没进棘轮的视觉字面量。",
|
||
"acceptance": "tokens 新增 --ua-alpha-soft/tint/line/mid/strong/heavy 六档(10/18/30/45/55/70%),tokens.json 同步 alpha 组;ui.css 全部 color-mix 百分比换令牌(就近收敛,Δ≤6),最后一处 rgba 白色条纹并进 accent-contrast + tint;audit:visual 新增 mix-literal 类目并棘轮到 0,负向自检确认新增字面量即红;门禁全绿。",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "tokens 新增 --ua-alpha-* 六档(10/18/30/45/55/70%),13 档字面值就近收敛(12→10、16/20/24→18、40→45、65/80→70,Δ≤6),44 处 color-mix 全量令牌化,最后一处 rgba 白纹并入 accent-contrast+tint;visual-rules 新增 mix-literal 类目(extractMixes 配平括号提取、渐变停靠位与几何百分比豁免、嵌套逐层计)并接入迁移脚本同一张表;baseline 补键 0;负向自检确认 33% 字面量即红;tint-accent 亮色定义改从 --ua-alpha-soft 派生;check 全绿(视觉回归抽查清单六组件段落留待人工过目——淡底档 Δ≤2 对比度方向不变)"
|
||
},
|
||
{
|
||
"id": "g230",
|
||
"title": "demo 页级命令面板:Ctrl+K 跳转任意演示区块",
|
||
"motivation": "演示页 63 个区块,侧边目录要滚到侧栏再过滤;组件库自己的 commandPalette 就是为这种高频跳转生的——演示页应当自己吃狗粮,也让「多实例只有一个注册快捷键」的约定有现场示范。",
|
||
"acceptance": "demo 挂全局 commandPalette(hotkey 默认注册 Ctrl+K),items 从 section.sec 反推(与侧边目录同源),onSelect 平滑滚动到区块;原 commandPalette 演示卡片实例改 hotkey:false(按钮可开,卡片文案补一句多实例约定说明);npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "demoTour 后挂全局 commandPalette:items 从 section.sec 反推(与目录同源),keywords 带去前缀锚点 id,onSelect scrollIntoView 平滑跳转;原演示卡实例 hotkey:false 并补多实例约定文案;check 全绿"
|
||
},
|
||
{
|
||
"id": "g231",
|
||
"title": "ua search 覆盖配方域(标题/组件/要点三层)",
|
||
"motivation": "Agent 找「空状态怎么拼」「长表单怎么锁」这类跨组件诉求时,组件搜索只能靠参数说明间接命中;配方标题与要点才是这类知识的直接载体,搜不到等于 recipes 白写一半。",
|
||
"acceptance": "cmdSearch 同时搜组件与配方(配方标题 3 / 组件 2 / 要点 1 权重,多词最弱档口径与组件一致),命中行给出 `ua recipes <编号>` 指路;--json 增加 recipes 数组带 matched 字段;MCP search_components 返回体同步加 recipes;AGENTS/manifest 的覆盖面描述补「配方」;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "cmdSearch 增加配方域(recipeMatch:标题 3/组件 2/要点 1,多词最弱档口径与组件一致),命中行给 ua recipes <编号> 指路;--json 改为 {components, recipes} 两数组各带 matched;MCP search_components 返回体同步加 recipes 且描述更新;AGENTS/manifest 覆盖面描述补配方;实测「吸底」组件零命中时配方仍可达;check 全绿"
|
||
},
|
||
{
|
||
"id": "g232",
|
||
"title": "ua doctor 补 git 身份检查",
|
||
"motivation": "全新环境 git 未配 user.name/user.email 时 commit 直接被拒;doctor 的职责就是把「能干活」一次检完,漏了这项等于七项全过还是提交不了。",
|
||
"acceptance": "doctor 新增第 8 项:git config user.name 与 user.email 均非空才 pass(spawnSync 实测,任一为空 FAIL 并提示配置命令);AGENTS/manifest 步骤文案的「七项」表述如出现则同步;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "doctor 第 5 项起插入 git 身份检查(spawnSync 实测 user.name/user.email,空则 FAIL 并给配置提示),8 项全过;AGENTS/manifest 无「七项」硬表述无需改;check 全绿"
|
||
},
|
||
{
|
||
"id": "g233",
|
||
"title": "位移量令牌化:transform 位移字面量收敛为 --ua-translate-* 档位并纳入审计口径",
|
||
"motivation": "ui.css 还有 23 处 transform 的 px 位移字面量(hover 上浮 1/2px、弹层入场 4/6/8/10px、滑入 12px、箭头偏移 3px),是最后一个未令牌化的视觉维度;audit:visual 十二类口径都不扫 transform,新写组件的位移值仍会各自为政,与颜色/间距的治理水平脱节。",
|
||
"acceptance": "ui.css 内 transform 的 px 位移全部改写为 --ua-translate-* 档位(负号用 calc(-1 * var(...)) 表达,10px 就近收敛到 8 档,百分比居中位移豁免);tokens.css/tokens.json 双源登记七档;audit:visual 新增 translate-literal 类别并 --baseline 归零;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "audit:visual 新增 translate-literal 第十三类(KIND_META + SCAN_PROPS 加 transform + detect/migrate 双口径),TRANSLATE_TOKENS 八档映射(10px 收敛到 8 档);迁移脚本自动改写 ui.css 33 处位移(负号 calc(-1 * var(...)),百分比与 0 豁免);tokens.css/tokens.json 双源登记 --ua-translate-1..12;AGENTS「五类」与 cli manifest「十二组」表述同步为十三,npm run docs 重建 llms-full 与文档站;基线重写后全 0,npm run check 全绿"
|
||
},
|
||
{
|
||
"id": "g234",
|
||
"title": "新增 pinInput 验证码输入组件(组件 70→71)",
|
||
"motivation": "表单域缺验证码 / 短信码输入这类 N 格单字符控件,业务只能用普通 textField 凑合,无进格、无粘贴分发、无 one-time-code 自动填充。",
|
||
"acceptance": "N 格单字符数字输入:输入自动进格、退格回删(空格回删上一格)、←/→ 移格、聚焦全选覆盖、粘贴自动分发(整段从首格、片段从当前格);value 信号双向绑定,mask 密文 + one-time-code,disabled 布尔/信号;样式只消费 --ua-* 令牌,禁止新增硬编码阴影/时长/字号;十处落点齐全,npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "组件 70→71;格子建一次不重渲染(焦点不丢),外部同步走单 effect(同值不写防打断输入法);样式复用 .ua-input 基类 + .ua-pin-cell 覆盖,零新增视觉字面量(audit 全 0);demo 锚点须全小写(verify 锚点正则只认 sec-[a-z0-9-],sec-pinInput 首跑踩坑改 sec-pininput);manifest/docs/README/llms-full 十处落点齐,check 全绿"
|
||
},
|
||
{
|
||
"id": "g235",
|
||
"title": "新增 copyButton 复制按钮组件(组件 71→72)",
|
||
"motivation": "文档站 / 演示页 / 业务表单都高频需要「一键复制」,此前只能各写一份 navigator.clipboard 样板并各自处理降级。",
|
||
"acceptance": "点击写入剪贴板并短暂显示已复制文案(2s 还原);text 支持 string 或函数(点击瞬间取实时值);clipboard API 不可用时降级 execCommand;variant/size/disabled 沿用按钮口径;样式复用 .ua-btn 零新增字面量;十处落点齐全,npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "组件 71→72;复用 .ua-btn 类零新增 CSS;label 用 signal + h() 函数型子节点实现文案切换(ButtonProps.label 收窄为 string,不走组合改走直接构造避免动既有类型口径);execCommand 降级用隐藏 textarea 临时选区;check 全绿"
|
||
},
|
||
{
|
||
"id": "g236",
|
||
"title": "新增 countdown 倒计时组件(组件 72→73)",
|
||
"motivation": "验证码重发 / 秒杀 / 会话过期这类倒计时高频出现,此前只能业务自写 setInterval;与 pinInput 组合即成完整的验证码收发场景。",
|
||
"acceptance": "秒级递减到 0 自动停表并触发 onFinish;缺省 mm:ss 两位补零,format 可自定义;返回句柄 start/stop/reset(已归零 start 重开一轮,reset 复位停表);running 缺省 true;样式只消费 --ua-* 令牌;十处落点齐全,npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "组件 72→73;remain 信号 + h() 函数型子节点响应式刷新文本(role=timer);句柄 stop 幂等(timer 句柄 0 判停);CSS 只加 .ua-countdown 数字等宽一行;demo 与 pinInput 同页组成验证码场景;check 全绿"
|
||
},
|
||
{
|
||
"id": "g237",
|
||
"title": "新增 kbd 键盘按键标记组件(组件 73→74)",
|
||
"motivation": "文档 / 空状态 / 提示文案里高频出现快捷键说明,此前只能裸文本,没有键帽视觉;demo 命令面板(Ctrl+K)等场景正缺这个配套展示件。",
|
||
"acceptance": "单键传字符串、组合键传数组以 + 连接(+ 对读屏 aria-hidden);键帽样式(底边加厚一线立体感)只消费 --ua-* 令牌;十处落点齐全,npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "组件 73→74;实现 10 行(h() 纯声明式拼接,无状态);键帽高度走 --ua-control-h-sm 档、字号 sm 档,零新增视觉字面量;demo 与命令面板快捷键说明混排;check 全绿"
|
||
},
|
||
{
|
||
"id": "g238",
|
||
"title": "新增 highlight 关键词高亮组件(组件 74→75)",
|
||
"motivation": "搜索结果 / 补全列表 / 帮助文档都高频需要「命中片段高亮」,此前业务各自 indexOf 切串包 span,样式与刷新逻辑散落各处。",
|
||
"acceptance": "命中片段包进 mark(大小写不敏感、全部命中、首尾空白忽略);text / keyword 均接受信号响应式重画;空关键词原样展示;高亮淡底只消费 --ua-* 令牌;十处落点齐全,npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "组件 74→75;单 effect 追踪两个信号(render 内统一取值,依赖自动收集);淡底走警示黄 --ua-alpha-tint 混色 + mark 语义标签;demo 与 textField 联动演示实时高亮;check 全绿"
|
||
},
|
||
{
|
||
"id": "g239",
|
||
"title": "焦点描边令牌化:outline 43 处收敛为三档复合令牌并纳入审计口径",
|
||
"motivation": "ui.css 有 20 处 outline 焦点描边字面量(2px 实色外描边 / 1.5px 柔和内嵌 / alpha-mid 柔和外描边三种形态反复手写),是 box-shadow 焦点环之外的第二套焦点视觉,未入审计口径就还会漂移。",
|
||
"acceptance": "tokens.css/tokens.json 新增 --ua-focus-outline / -inset / -soft 三档(含暗色覆盖);audit:visual 新增 outline-literal 第十四类(outline: none 豁免);20 处迁移后基线归零;outline-offset 为逐控件几何微调不入表;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "迁移走 OUTLINE_TOKENS 整值精确匹配(squash 归一空白);2 处 alpha-mid 柔和描边归 --ua-focus-outline-soft;verify 拦下暗色覆盖缺失(三个新令牌补 [data-theme=dark] 同口径覆盖);AGENTS 审计类别数同步为十四;check 全绿"
|
||
},
|
||
{
|
||
"id": "g240",
|
||
"title": "button 增加 loading 态(既有组件打磨)",
|
||
"motivation": "异步提交按钮此前要业务自己拼「转圈 + 禁点 + 还原」三件事,switchButton 有 is-loading 而主按钮反而没有,口径不齐。",
|
||
"acceptance": "loading?: boolean | Signal<boolean>:加载中转圈(复用 ua-spin 帧体)并自动禁点,aria-busy 播报忙碌;文案位置不漂移(转圈非加载态不占位);prefers-reduced-motion 降级为静态环;manifest/docs/demo 同步;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "loading 布尔归一为信号走同一响应式路径;disabled 口径合并为「显式禁用或加载中」(bindProp 函数式属性);类名切换而非整棵重渲染(保焦点);CSS 复用 .ua-spinner 类 + .ua-btn-spin 尺寸覆盖,零新增字面量;check 全绿"
|
||
},
|
||
{
|
||
"id": "g241",
|
||
"title": "verify 增加演示锚点大写防护",
|
||
"motivation": "锚点集合用 /sec-[a-z0-9-]+/ 提取,大写锚点(如 sec-pinInput)会被静默吞掉,manifests 登记后误报「缺 section」,g234 实踩浪费一轮排查。",
|
||
"acceptance": "verify 校验 main.ts 中 /sec-[A-Za-z0-9-]+/ 命中含大写的锚点即报错并给出小写口径提示;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "在 cmdVerify 锚点集合构建处加大写扫描(正向提示改小写);当前工作区无违规锚点,check 全绿"
|
||
},
|
||
{
|
||
"id": "g242",
|
||
"title": "令牌对比度门禁 audit:contrast",
|
||
"motivation": "audit:visual 只管「没有硬编码字面量」,令牌本身的对比度无人把关:改一个颜色值就能让全站辅助文字掉到 4.4,亮底上人眼察觉不到,暗色更难发现(实测暗色主按钮反白仅 3.92、暗色强调文字压淡底 4.44 均不达标)。",
|
||
"acceptance": "新增 ua audit:contrast:解析 tokens.css 亮暗两套变量(含 var() 与 color-mix 求值、半透明底先压到页面底),16 组文字×底色配对按 WCAG 公式算比值,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6)退出码 1,--json 出逐对明细;不达标的三条配对通过修令牌达标;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "亮色 text-muted #6b7280→#676d78(凹陷底 4.39→4.72)、暗色 accent #378add→#4a9aeb(反白 3.92→4.77、强调文字压淡底 4.44→5.26),16 对全绿最低 4.58;接入 check 尾步 + AGENTS/README/ITERATION/CLI 清单同步"
|
||
},
|
||
{
|
||
"id": "g243",
|
||
"title": "文档站与演示页外壳配色从令牌派生",
|
||
"motivation": "两处外壳 chrome 各自手写十六进制(文档站 --bg/--accent、演示页 --page-*),改暗色 accent 后页面外壳仍留着旧蓝,与组件色各显示一种;这类漂移手写清单永远追不上令牌。",
|
||
"acceptance": "新增 packages/tools/cli/lib/shell-palette.mjs 作为唯一派生点(缺键退回内置默认,tokens.json 缺失不炸),文档站 renderDocs 与 apps/demo/build.mjs 都改为调用它;构建产物里的 --accent/--muted/--page-* 必须等于 tokens.json 对应值,源码不再出现外壳手写色值;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "产物核对:docs --accent 亮 #185fa5 / 暗 #4a9aeb,demo --page-muted 亮 #676d78 / 暗 #888780,两处产物 378add 残留归零(tokens.css 注释里的旧值描述一并洗掉,避免 grep 误判)"
|
||
},
|
||
{
|
||
"id": "g244",
|
||
"title": "新增 inputTag 标签输入(组件 76)",
|
||
"motivation": "筛选条件、收件人、技能标签这类「输一串值」的输入只能靠 callers 自己拿 textField + tag 手拼,成标签 / 去重 / 退格删末个 / 满额禁输四件事每处各写一遍。",
|
||
"acceptance": "新增 inputTag:回车或逗号成标签、chip 可删、空框退格删末个、失焦自动入列,value 信号双向绑定,max 满额禁输、maxLength 截断、allowDuplicate 可开;样式只复用 .ua-input-group / .ua-tag,禁止新增硬编码阴影 / 时长 / 字号(audit:visual 保持 0);manifest + demo + README + 计数 + llms-full 全同步,npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "组件 75→76;样式仅 4 条排布规则(flex 换行 / 内距 / 收缩 / 溢出换行),全部走 --ua-space-* 与 --ua-control-h-md;单文件 78 行"
|
||
},
|
||
{
|
||
"id": "g245",
|
||
"title": "高对比偏好令牌 + 对比度门禁升到四套主题",
|
||
"motivation": "系统「提高对比度」偏好下组件毫无变化:边框仍是 0.15 透明黑、辅助文字仍是浅灰,低视力用户开了开关等于没开;而对比度门禁只验常规两套主题,覆盖不到这条路径。",
|
||
"acceptance": "tokens.css 末尾(媒体查询需后置胜出)加 @media (prefers-contrast: more):亮/暗两态分别加深 border / 滚动条、把 text-muted 升到次级档、text-faint 升到原辅助档,只改已有变量不新增名字(verify 双源核对保持一一对应);audit:contrast 解析该媒体块,把配对表在亮/暗 × 常规/高对比四套主题下各算一遍,附 --json 明细;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "高对比下辅助文字 4.85→7.49(亮)/4.72→8.71(暗)、三级文字 2.64→4.83,四套主题 16 对全绿;负向自检(把 text-faint 调亮)确认新主题会被判不达标"
|
||
},
|
||
{
|
||
"id": "g246",
|
||
"title": "ua info 支持 --md 输出 Markdown 片段",
|
||
"motivation": "Agent 或人要把单个组件的参数表贴进文档 / issue / 回复时,只能从文本版参数表里手抄对齐空格,抄完还要重新排表格;清单数据本来就在手里。",
|
||
"acceptance": "ua info <组件id> --md 输出标题 + 参数表 + 用法步骤 + 示例代码块,类型里的竖线自动转义(联合类型不截断表格),尾部保留「出现在配方」指路;文本版与 --json 输出不变,用法串与 AGENTS/清单文档同步;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "与文本版共用 formatComponent 的数据源,只多一个渲染函数;select 的 'a'|'b' 联合类型已实测转义为 \\| 不破表"
|
||
},
|
||
{
|
||
"id": "g247",
|
||
"title": "表格支持整体换数据源(setRows + 信号数据)",
|
||
"motivation": "表内核在创建时把 data 定死,筛选条一改就得整表重建:用户已排的序、已翻的页、已勾的行全丢。真实业务里「条件变了换结果集」是常态,此前只能靠调用方自己 effect + replaceChildren 绕。",
|
||
"acceptance": "table-core 新增 setRows(引用未变不重算;按 rowKey 剔除新数据里已消失的选择项;排序 / 筛选 / 页码保持),dataTable 的 data 接受 T[] | Signal<T[]>(信号变化自动换源),新增冒烟断言覆盖换源与失效行剔除;dataTable 单文件仍 ≤200 行(数据源逻辑抽到 tableDataSource.ts);manifest / demo / AGENTS 冒烟条数同步;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "冒烟 21→26 条;dataTable 曾到 205 行触发 style 门禁,抽表数据源助手 + 合并 RowKey 类型导入 + 压掉 banner 注释后 199 行;demo 在 sec-inputtag 里联动演示(删标签即换源,页码排序不重置)"
|
||
},
|
||
{
|
||
"id": "g248",
|
||
"title": "配方 14:标签筛选条 → 表格联动",
|
||
"motivation": "多条件筛选 + 表格是数据类页面最常见的一屏,此前只有「表格自带单列关键字筛选」的说明,没人讲清多条件该在信号层派生、以及换源为什么不能重建表格。",
|
||
"acceptance": "recipes.md 追加配方 14(编号连续、含可运行骨架与要点),骨架只用已注册组件(inputTag / select / button / space / dataTable),要点覆盖「筛选在信号层做」「传信号不传数组」「且/或语义」「空状态复用」;ua recipes 14 能解析出组件清单;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "顺手清掉骨架里未使用的 toast 导入与 tag 变量名(避免配方组件清单误报 tag);与 g244/g247 两轮新能力形成闭环"
|
||
},
|
||
{
|
||
"id": "g249",
|
||
"title": "doctor 增加命令面与门禁链自洽自检",
|
||
"motivation": "今天新增 audit:contrast 与 ua info --md 时都要手工记得同步 usage 串、CLI 清单、check 链三处,漏一处没人报错——跑 ua recipes 时才发现 usage 里根本没有 recipes 这一项。这类「文档承诺 ≠ 实现」只能靠机器查。",
|
||
"acceptance": "ua doctor 从 8 项升到 11 项:①usage 串 ↔ dispatch case 双向覆盖(选项组里的竖线不能被当命令分隔)②package.json 的 check 链覆盖全部门禁步骤 ③CLI 清单 api.exports 声明的命令都有 case;负向自检(删 check 链里的 audit:contrast)确认会 FAIL;doctor 全过,npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "新检查当场抓到真问题:usage 串漏了 recipes;解析先剔除 [--json|--baseline] 选项组再切 |,否则选项竖线被当分隔符;顺带把 usage 的 info 补上 [--json|--md]"
|
||
},
|
||
{
|
||
"id": "g250",
|
||
"title": "动效兜底门禁 audit:motion + 补两处漏网无限动画",
|
||
"motivation": "audit:visual 只管字面量、verify 只管时长令牌,没人管「无限循环动画有没有 reduced-motion 降级」。实测转圈(.ua-spinner)与不确定态进度条两处根本没有降级——开了「减少动效」的用户满屏仍在转,属于前庭敏感的硬伤。",
|
||
"acceptance": "新增 ua audit:motion:自写 CSS 扫描器按大括号层级切规则并标记是否处于 prefers-reduced-motion 媒体块内,把所有带 infinite 的动画逐个核对是否被 animation: none 关掉,缺兜底退出码 1、--json 出逐条明细;补掉两处漏网;接入 check 链与 doctor 的必需步骤表;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "扫描器两版写错才收敛:①前导注释混进选择器 ②子块字符归属父块导致选择器为空;最终用「尾缓冲在 ; / } 处清空」方案,936 条规则、49 条降级规则全部正确识别;负向自检(删 spinner 兜底)确认报「缺兜底 1 处」"
|
||
},
|
||
{
|
||
"id": "g251",
|
||
"title": "ua tokens --unused 反向收口令牌面",
|
||
"motivation": "令牌只增不减:112 个变量里没人知道哪几个其实没组件在用,加令牌时也不敢删旧的。正向查询(按名找)解决不了「谁没用」,需要一个反向视图。",
|
||
"acceptance": "ua tokens --unused 列出皮肤样式表与 tokens.css 自身都没引用的令牌(自身派生引用算消费,否则 --ua-color-highlight 会被误报),--json 出 total + unused 数组;用法串与清单文档同步;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "首版只扫 ui.css 把 highlight 误报为闲置(实际被 --ua-shadow-highlight 引用),补上 tokens.css 作为消费面后剩 3 个真闲置:surface-3 / text-secondary / font-size-xl;text-secondary 仍被 audit:contrast 配对表引用,故只报不删"
|
||
},
|
||
{
|
||
"id": "g252",
|
||
"title": "MCP get_component 支持 format: md",
|
||
"motivation": "g246 给 CLI 加了 --md,但 Agent 侧主力入口是 MCP:get_component 仍只回一大坨 JSON,Agent 拿到要自己拼参数表才能贴给人看。",
|
||
"acceptance": "MCP get_component 入参增加 format 枚举 json|md(缺省 json 行为不变);md 时返回与 ua info --md 同款 Markdown 片段(标题 + 参数表 + 用法 + 示例 + 配方指路);字符串结果在 tools/call 层原样作为 text 返回(不再 JSON 转义);npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "实测 stdio 往返:text 字段直接是可读 Markdown;json 分支未变(老客户端无感)"
|
||
},
|
||
{
|
||
"id": "g253",
|
||
"title": "文档站组件卡加「复制参数表」按钮",
|
||
"motivation": "人类读文档站想引用某个组件的参数表时只能手抄或截图;CLI/MCP 侧已经有 Markdown 口径(g246/g252),文档站却还用不上。",
|
||
"acceptance": "文档站每个组件卡标题右侧加「复制参数表」按钮:点击生成与 ua info --md 同款 Markdown(标题 + 参数表 + 用法 + 示例,竖线转义),成功文案短暂变「已复制」;无 clipboard 权限时退回 textarea + execCommand;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "生成器本身是模板串,MD 里的三反引号围栏改用 String.fromCharCode(96) 拼,避免把模板串提前截断(第一版直接写裸反引号导致 ua.js 语法错误,已回滚重写);顺手用 node 取生成产物里的 mdOf 做了一次实际输出核对(含联合类型竖线转义)"
|
||
},
|
||
{
|
||
"id": "g254",
|
||
"title": "换源后越界页码回写内核状态(settle 钩子)",
|
||
"motivation": "g247 引入 setRows 换源后暴露:数据从 3 页缩到 1 页时,getPageInfo() 钳制成 1、state.pagination.page 仍是 3——视图和状态各说各话。更糟的是数据恢复后表格会「记得」失效的旧页码直接跳回第 3 页,用户会以为数据被吞了。",
|
||
"acceptance": "TablePlugin 增加可选 settle 钩子(派生后收敛自身状态切片,无需修正须原样返回同一引用);core 在派生末尾跑一轮 settle 并只走 onStateChange、不广播订阅(避免读取期重入);pagination 用新鲜快照回写生效页,快照过期不写;smoke 补 5 条断言覆盖「缩水→回写 / 恢复→不跳页 / 跳越界→回写末页 / 快照语义不变」;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "core",
|
||
"notes": "只回写不改派生:pageInfo 仍是纯派生量,越界钳制口径不变(dataTable 的受控页码桥接因此不再需要额外钳制);快照新鲜度用「本次 derive 是否按当前 state 完整跑过」判断,paged=false(getAllRows 导出)会把 fresh 置假,避免拿旧快照写错页码"
|
||
},
|
||
{
|
||
"id": "g255",
|
||
"title": "减少动效下归零全站悬停位移",
|
||
"motivation": "audit:motion 只盯 infinite 循环动画,没人管「一动就位移」的那一类。实测皮肤样式表里 14 处悬停 / 按压位移(btn 上浮、page-btn 回缩、image 放大、backtop 抬升、colorpicker 格子浮起…)在 prefers-reduced-motion 下一处降级都没有——开了「减少动效」的用户每次划鼠标都被推一下,比循环动画更常见也更难躲。",
|
||
"acceptance": "ui.css 末尾新增一条 ua- 类通配的减少动效规则(:hover / :active 下 transform 与 translate 归零,颜色 / 描边 / 阴影过渡保留);audit:motion 扩为两类必查,悬停位移逐条核对是否有对应归零规则(或全局通配兜底),缺一条退出码 1、--json 增加 shift 段;负向自检删掉规则确认报「未归零 8 处」;ui.css 仍零硬编码字面量、对比度与其余门禁全绿。",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "兜底选全局通配而不是逐组件补:14 处分散在 10 多个组件,逐个补降级等于给每个新组件留一道必踩的坑;用 !important 压源码序更早的 :hover transform,同时只归零 transform/translate,颜色与阴影过渡照旧,避免把状态反馈一起砍掉。顺带把 AGENTS 里 smoke 条数(26 条)与 g254 的实际 31 条对齐"
|
||
},
|
||
{
|
||
"id": "g256",
|
||
"title": "数据表格分页条加每页条数切换(抽出 tablePager)",
|
||
"motivation": "分页条只有「上一页 / 下一页」,用户想看更多行只能去改代码里的 pageSize。而 pageSize 是建表时定死的,最需要它的「先粗看全量、再细看几行」恰恰是运行期行为。另外上一页在第 1 页仍可点、下一页在末页仍可点,点了只是派发一次无效页码靠内核钳制兜底。",
|
||
"acceptance": "新增 tablePager 模块(上一页 / 页码播报 / 下一页 / 每页条数下拉),dataTable 改为调用它并把行数压回 200 行以内;首末页对应按钮置灰;下拉用 select 自绘弹层、走 --ua-* 令牌零硬编码;调用方传入的 pageSize 不在候选里时自动补进候选(避免下拉显示空白);manifest 文档补一条步骤;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "抽出而非就地加:dataTable 当时 199 行正卡在 CODING 的单文件上限,再加十行就破线;分页条本来只依赖 info/total/loading 三个读取器,独立成模块后 dataTable 掉到 199 行以内且不再 import button。页大小变更走内核 setPageSize(自带回第 1 页语义),下拉值由 effect 从内核快照同步,双向不打架"
|
||
},
|
||
{
|
||
"id": "g257",
|
||
"title": "分页条窄屏自动换行",
|
||
"motivation": "g256 给分页条加了每页条数下拉后,「上一页 / 页码 / 下一页 / 5 条/页」四个控件挤在一行且不换行:侧栏或窄窗口下直接横向溢出,末尾的下拉被裁掉一半——恰恰是新加的那个控件最先看不见。",
|
||
"acceptance": "分页条允许换行且换行后行距紧一档(列距不变);页码信息与页大小下拉用 auto 外边距把控件分组靠右,换行到第二行也不贴边;只改布局不加字面量,npm run audit:visual 计数不涨、npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "选择 flex-wrap 而不是媒体查询:分页条容器宽度由表格宽度而非视口决定,媒体查询猜不准,让 flex 自己按可用空间折行更稳;行距用 gap 的两值写法,不引入新的间距令牌"
|
||
},
|
||
{
|
||
"id": "g258",
|
||
"title": "select 增加 ariaLabel 可访问名",
|
||
"motivation": "下拉触发器只显示当前值文案(「5 条/页」「全部」),没有可见 label 时读屏念出来只是一句没主语的量词,用户不知道自己在选什么。表格分页条正是这种场景:新加的页大小下拉既无 label 也无名字。",
|
||
"acceptance": "SelectProps 增加 ariaLabel(缺省不落属性),触发器写入 aria-label;表格分页条传「每页条数」;manifest 的 select 参数表与步骤同步(verify 会核对类型字段与文档一一对应);npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "加在 select 而非只给分页条写死属性:凡是「工具栏 / 分页条 / 筛选器」这类无可见 label 的场合都需要同一个入口;用 ?? null 让 h() 在未传时整条属性不落地,避免输出空 aria-label(空名字比没名字更糟,读屏会念成空白)"
|
||
},
|
||
{
|
||
"id": "g259",
|
||
"title": "数据表格暴露 pageSizes 候选",
|
||
"motivation": "g256 的每页条数候选写死在分页条里(5/10/20),调用方想换成 10/25/50 只能改库源码——而每页条数恰恰是业务侧最常按数据密度调的参数。",
|
||
"acceptance": "DataTableProps 增加 pageSizes 并透传给分页条(缺省 [5, 10, 20],当前 pageSize 不在候选里自动补);manifest 参数表同步(verify 核对类型字段 ↔ 文档);dataTable 仍 ≤200 行;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "加参数后 dataTable 一度涨到 201 行被 style 门禁拦下(CODING 单文件上限):顺手合并了一处空行,把表上层桥接注释与 busy 读取器贴紧,语义分组没变"
|
||
},
|
||
{
|
||
"id": "g260",
|
||
"title": "配方 15:表格分页与独立分页器联动",
|
||
"motivation": "g254/g256/g259 把分页能力补齐(越界收敛、每页条数、条数候选),但配方库里没有一条讲「页码提到页面外、多个表格共用一个分页器」该怎么接——这正是受控 page 信号存在的理由,缺配方等于让使用者自己猜两边写回的顺序。",
|
||
"acceptance": "recipes.md 新增配方 15(编号连续、含可编译的 ts 代码块、代码块里认出已注册组件 id),讲清「一个页码只有单一来源」「改页大小别手动回第 1 页」「越界页码由内核收敛」「无可见 label 要传 ariaLabel」四条要点;npm run check(含 verify 的配方结构校验)全绿。",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "配方放在「通用约定」之前、紧接配方 14,保证编号递增;要点里特意写了两条踩坑结论:业务层再手动 page(1) 会出现「先跳页再被推回」的闪动;外部联动不需要自己钳制页码,因为内核已把生效页回写进 state"
|
||
},
|
||
{
|
||
"id": "g261",
|
||
"title": "inputTag 删除 chip 后焦点归还输入框",
|
||
"motivation": "g244 的 inputTag 里,点 chip 上的 × 会连带把那个按钮本身删掉——焦点随之掉回 body。键盘用户删完一个标签想接着输入,得从头 Tab 回输入框;连续删多个标签时每删一个就丢一次焦点。",
|
||
"acceptance": "删除 chip 后把焦点交回输入框;manifest 的 inputTag 步骤同步这句行为(verify 核对文档与实现一致);npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "焦点归还写在删除回调里而不是 render 末尾:render 是「按信号重画」,外部改信号(如重置筛选条)时不该抢焦点,只有用户亲手点 × 这一个入口需要接住"
|
||
},
|
||
{
|
||
"id": "g262",
|
||
"title": "对比度门禁补非文本配对(焦点描边 3:1)",
|
||
"motivation": "audit:contrast 只算文字配对,可键盘用户「焦点在哪」全靠焦点描边——它糊进背景时文字对比度再漂亮也没用。WCAG 1.4.11 对这类界面部件只要求 3:1,但之前没有任何门禁盯着它:把 accent 调浅一点,文字配对也许还过,焦点环已经没了。",
|
||
"acceptance": "配对表增加「焦点描边 / 页面底」「焦点描边 / 表面」两对,阈值 3(非文本档),亮/暗 × 常规/高对比四套各算;README、AGENTS、CLI 清单里 16 组的表述同步成 18 组并说明非文本档来源;npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "配对表本来就是「设计契约」的写法(写死在代码里、加一对等于承认一种新组合),所以非文本档沿用同一张表、只给不同的 min,不另开一套配置;实测 light 6.52/6.09、dark 6.35/5.77,说明现役 accent 作焦点描边是够的——这条门禁的价值在「以后调浅时会被拦住」"
|
||
},
|
||
{
|
||
"id": "g263",
|
||
"title": "标签输入支持多值粘贴",
|
||
"motivation": "从表格一列、聊天记录或需求列表里粘「上海,北京,深圳」到标签框,此前只会整串变成一个 chip,用户还得逐个删掉重敲。而整段粘贴恰恰是标签类输入最常见的入口。",
|
||
"acceptance": "粘贴文本里出现半角/全角逗号、换行或制表符时自动拆分并逐个入列(各自过截断 / 去重 / 上限),单值粘贴保持浏览器默认行为;manifest 步骤同步;inputTag 仍 ≤200 行且 npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "第一版把分隔符正则直接内联在监听器里,两次粘贴各写一份;改成顶部一个 SEP 常量后两处共用(test 与 split 同一语义),也顺手避开「正则带 g 标志导致 test 推进 lastIndex」的坑(不写 g)。写文件时用 Python 转义,\n 被吞成真换行导致 TS 语法错误,改用编辑器直接写入修好"
|
||
},
|
||
{
|
||
"id": "g264",
|
||
"title": "数据表格可自定义空状态文案",
|
||
"motivation": "空状态的文案被写死成「暂无数据」,可业务侧常要说清「为什么空」——「本月还没有工单」「筛选条件太窄,试试放宽时间」。有筛选词时的文案本身是好的(带关键词 + 一键清除),缺的只是「真没数据」那一句。",
|
||
"acceptance": "DataTableProps 增加 emptyText(缺省「暂无数据」),透传到空状态行;有筛选词时仍走「没有符合「x」的行」+ 清除按钮,两种语义不混用;manifest 参数表登记(verify 双向核对类型字段);dataTable 仍 ≤200 行、npm run check 全绿。",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "通过给 emptyRow 加第 4 个可选参数实现,调用处只多一个实参不新增行——dataTable 正卡在 199 行上限;参数默认值放函数签名而不是调用方,避免其它调用点(目前只有 dataTable)被动改"
|
||
},
|
||
{
|
||
"id": "g265",
|
||
"title": "新增布局骨架组件 layout",
|
||
"motivation": "76 个组件里没有「页面骨架」:想搭一个带页头 + 侧栏 + 内容 + 页脚的控制台界面,只能自己写 div 拼 flex,页头吸顶要重写 z-index、侧栏折叠要自己接信号、内容区滚动要自己补滚动条令牌——每个用库的人都要把同一段骨架重抄一遍。菜单(menu)、竖排锚点(anchor)、卡片(card)都在,缺的是把它们装起来的容器。",
|
||
"acceptance": "新增 layout(props, ...children):children 即内容区,header / sider / footer 三入参按需渲染(缺省不套空容器);siderWidth 缺省 220;collapsible 时页头左侧出开关、collapsed 信号双向绑定(外部给则不自管);sticky 让页头吸顶走 --ua-z-sticky;样式只消费 --ua-* 令牌,禁止新增硬编码阴影 / 时长 / 字号,内容区滚动条纳入统一细条规则;十处落点全同步(src / types / index / ui.css / manifest 双处 / demo / README / 计数 / docs),npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "骨架刻意不带圆角与投影——它是页面容器不是卡片,浮起感留给内容里的 card / modal;折叠态宽度用 .is-collapsed 的 width:0 + overflow hidden 压过内联展开宽度(宽度只有一个来源),aria-hidden 随折叠翻转,读屏不再进菜单;verify 的滚动条规则当场抓出漏网的内容区:新增滚动容器必须并入 scrollbar-width 统一规则,否则暗色下露原生亮条"
|
||
},
|
||
{
|
||
"id": "g266",
|
||
"title": "新增 JSON 树查看组件 jsonViewer",
|
||
"motivation": "接口联调、配置排查、日志排查都绕不开「看一段原始 JSON」:此前只能 JSON.stringify 后塞进 pre,一行到底、层级看不出来,长数据把页面撑穿。tree 是业务树的形状(勾选 / 异步加载),塞 JSON 会逼着给每个节点造一份包装对象,语义也对不上——需要的是纯只读的类型着色查看器。",
|
||
"acceptance": "新增 jsonViewer(props):value 接收任意值,对象 / 数组可展开收起、分支收起时显示「N 项 / N 个键」,叶子按 string/number/boolean/null 四类着色;depth 控制默认展开层数(缺省 2)、maxHeight 限高后在框内滚动并纳入统一细条规则、rootLabel / ariaLabel 可配;样式只消费 --ua-* 令牌,禁止新增硬编码阴影 / 时长 / 字号;十处落点全同步(src / types / index / ui.css / manifest 双处 / demo / README / 计数 / docs),npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "用 role=tree/treeitem + aria-expanded 表达折叠态,开关的 aria-label 带完整路径(response.data.items.0)——读屏能说出「展开的是哪一支」;收起用 hidden 而不是重建节点,折叠态天然保住;踩坑:h() 返回 HTMLElement,给变量标 HTMLDivElement 类型会 TS2741(缺 align),改成 as 断言;留痕 ts 必须 %:z(+08:00),%z 的 +0800 会被 lint-iterations 判违规"
|
||
},
|
||
{
|
||
"id": "g267",
|
||
"title": "补 Windows 强制色(forced-colors)高对比兜底",
|
||
"motivation": "库一直按 prefers-contrast 做高对比,但 Windows 上真正会被大量用户打开的是「强制颜色」(高对比主题):它用系统色板强制替换颜色、剥掉 box-shadow、把自绘底色抹平。结果恰恰是库最依赖的两件事失效——浮层靠阴影当边框、选中态靠淡色底表达。开高对比的用户看到的是:弹层与页面融成一片、下拉里分不清选了哪一项、骨架屏整块消失。",
|
||
"acceptance": "ui.css 末尾新增 @media (forced-colors: active) 兜底块:10 类靠阴影当边框的容器(card/modal/drawer/pop/menu/select-pop/json/tip/toast/layout-sider)补 1px CanvasText 真边框并清掉阴影;9 类控件(button/input/textarea/select-trigger/input-group/checkbox/switch/tag/badge)补描边;选中态(option/menu-item/tab/step)改用 Highlight 底 + HighlightText 字并 forced-color-adjust: none;骨架与占位走 GrayText。不写死任何色值(避免撞 border-literal / outline-literal 门禁),audit:visual 仍为 0;README 可访问性约定补一条;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "焦点描边刻意不重写:全局 outline 的色值在强制色下会被系统自动强制,自己补一条 2px solid Highlight 反而会撞 outline-literal —— 门禁在这里正好挡住了「好心写死」。先做了焦点环覆盖率统计(78 个组件里 24 个有专属规则),发现 [class^=ua-]:focus-visible 已是全局兜底,于是放弃那条选题改做强制色:选题前先量缺口,比凭直觉补更重要。forced-color-adjust: none 只给选中态用——不关掉调整的话系统会把 Highlight 也归一到窗口底色"
|
||
},
|
||
{
|
||
"id": "g268",
|
||
"title": "verify 增加孤儿演示区块反向校验",
|
||
"motivation": "verify 一直只查正向:清单里的 demo 锚点必须在 demo 里存在。反过来没查——demo 里新加的 section 如果没人登记进清单,就成了一段「谁都指不到、删了也没人发现」的孤儿演示。实测真有:sec-alert 是 alert 的专属演示(四档语义色 + 可关闭 + banner),但清单里 alert.demo 还指着旧的聚合区块 sec-feedback,于是这段更完整的演示从文档站与 MCP 查询里彻底消失,谁也不知道它存在。",
|
||
"acceptance": "verify 新增反向校验:把 demo 里 section() 定义处的锚点(只认定义处,不认 href: '#sec-x' 这类引用)与全部组件 demo 字段求差,差集非空即报「孤儿演示」;修掉现存唯一一条(alert.demo → sec-alert,专属演示重新回到文档站与 MCP);AGENTS.md 的 verify 覆盖描述补这一条;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "口径踩坑:第一版直接用 verify 既有的全量锚点集合(match(/sec-[a-z0-9-]+/g)),结果把 anchor 演示里故意写的 href: '#sec-missing-zone'(演示「找不到区块自动跳过」)判成孤儿——正则必须只认 section( 定义处。另一坑:改 ua.js 时用 bash 双引号内联 node 写正则,反斜杠被吞成 /section(s*'.../ 直接语法错,整个 CLI 无法加载(docs 也静默失败),改用编辑器直接写正则"
|
||
},
|
||
{
|
||
"id": "g269",
|
||
"title": "新增文本差异对比组件 diffView",
|
||
"motivation": "审阅场景(配置改动、变更记录、纠错前后对比)需要「哪几行变了」的视图,此前只能把两段文本并排贴进 pre 让人眼比对。库里有 jsonViewer 看结构、highlight 标关键词,却没有行级差异:这是审阅链路里唯一缺的一环,而它恰恰最依赖「对齐」——人眼对不齐就等于没看。",
|
||
"acceptance": "新增 diffView(props):before / after 两份文本按行比对,剪公共前后缀后对中间段做 LCS,输出 same/add/del 三类行;增删行同时有底色与行首 + / − 符号两条通道(不只靠颜色),双行号列 user-select:none 不进复制内容;showStats 给 +N / −N,aria-label 自动播报同一组数字;中间段行数乘积 > 40 万时退化为整段替换(语义正确、不卡主线程);样式只消费 --ua-* 令牌(增删底色走 color-mix + --ua-alpha-tint),禁止新增硬编码阴影 / 时长 / 字号;十处落点全同步,npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "对齐分三步:先剪公共前缀、再剪公共后缀、最后才 DP(dp[i][j] 从后往前填,回溯时按「删还是加」比较下一格长度),日常改两行只算中间几行,不会 2000×2000 全表;退化阈值用中间段乘积而不是总行数——总行数大但改动集中时仍然能逐行对齐。踩坑复现:用 bash 内联 node 生成 demo 里的 join(\n) 与嵌套引号,反斜杠被吞成真实换行,esbuild 报 Unterminated string literal,改用编辑器直接写入 + 双引号内层字符串"
|
||
},
|
||
{
|
||
"id": "g270",
|
||
"title": "新增代码块组件 codeBlock",
|
||
"motivation": "演示页与文档站里到处是「一段代码 + 一个复制按钮」:此前靠 h(pre) 手拼,行号、标题、复制各写一遍,行号还容易被一起选进剪贴板。库里有 copyButton(复制)与 diffView(对比),缺的正是最常用的「单向展示」那一块——它该把行号与复制都收进同一个组件,顺手保证复制出来的是纯原文。",
|
||
"acceptance": "新增 codeBlock(props):code 原文渲染为等宽多行,行号列缺省显示(单行自动省略)、user-select:none 不进复制;标题行 title 优先于 lang,两者与 copyable 全关时整条头部省掉;maxHeight 限高后在框内滚动并纳入统一细条规则;复制走 copyButton 且直接读 props.code(不从 DOM 取文本,杜绝行号混入);onCopy 回调透传;样式只消费 --ua-* 令牌,禁止新增硬编码阴影 / 时长 / 字号;十处落点全同步,npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "行号用 aria-hidden 而不是 role=presentation:读屏读代码时不该被「1 2 3」打断;空行渲染成一个空格撑住行高,否则连续空行会把代码块压扁。踩坑:Write 工具写文件时把行尾注释的 ***/ 后面粘了个 </n> 残片,tsc 直接报语法错——新建组件后必须先 build 再过其余门禁;demo 里的嵌套引号改用 .mjs 脚本写(Write 工具落盘不转义),比 bash 内联 heredoc 稳定"
|
||
},
|
||
{
|
||
"id": "g271",
|
||
"title": "新增手写签名板组件 signaturePad",
|
||
"motivation": "审批 / 签收类表单最缺的一件控件是「手写签名」:没有它,这类场景只能整块绕开组件库,自己接 canvas。而自己接往往踩同一串坑——高分屏线条发虚(忘了按 DPR 放位图)、移动端一按就滚页(没关 touch-action)、窗口一变签名被清空(重设位图会清画布)。这些都该由库一次解决。",
|
||
"acceptance": "新增 signaturePad(props) 并返回 handle(el / clear / toDataURL / isEmpty):指针绘制 + setPointerCapture 跟手、位图按 DPR 放大、笔色读 canvas 的 color 走令牌、容器宽度变化经 trackSize 重算并在重设位图后按快照贴回笔迹、touch-action: none 独占手势;收笔写回 value 信号与 onChange,清空写空串并复位按钮态;disabled 支持布尔与信号;样式只消费 --ua-* 令牌,禁止新增硬编码阴影 / 时长 / 字号;十处落点全同步,npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "返回句柄是本库既定模式(formItem / virtualList 同款),因为「有画布就有外部要的操作」:取图、清空、判空。三处状态(位图 / signed 信号 / value 信号)必须一次改完——clear() 里少写一处就会出现「画布清空但信号还是旧图」。踩坑:Write 工具连续三次只写进文件头部(把注释当全文提交),build 直接暴露;demo 里 IIFE 少一个右括号,esbuild 报 Expected ) but found ; —— 新建组件后先 build 再谈其余门禁,能省一轮全量 check"
|
||
},
|
||
{
|
||
"id": "g272",
|
||
"title": "视觉回归抽查清单扩到十一个组件并让下限自推导",
|
||
"motivation": "清单固定 six 组件是 10-23 定的,那时库只有 34 个组件、新增的多是表单小件。这两轮一口气加了 layout / jsonViewer / diffView / codeBlock / signaturePad 五类新形态(容器、数据展示、录入画布),它们恰恰是「令牌用对了但观感可能错」的高风险区——侧栏折叠抖动、JSON 竖线叠重、diff 行底色渗到行号列、代码块悬停残影、签名笔迹发灰,这些都没有抽查位。清单不覆盖,就等于默认它们不用人眼验收。",
|
||
"acceptance": "ITERATION.md 清单新增 layout / jsonViewer / diffView / codeBlock / signaturePad 五段,各 3-4 条、维度标签仍限五类;ua verify 的 REQUIRED 扩到十一段,MIN_TOTAL 改为 REQUIRED.length × MIN_PER_GROUP(33)由段数推导——以后加抽查位只需加段、不用手改总数;verify 成功语与 AGENTS.md、ITERATION.md 的「六段 / ≥18 条」表述同步为「十一段 / ≥33 条」;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "下限从写死的 18 改成 段数 × 每条下限:这类「N 个组件」的计数一旦写死在门禁里就必然会漂——库自己已经在 verify 里靠 sync-counts 治过一次同样的病,这次直接把口径做成派生量。选题前先查了「焦点环覆盖」与「outline:none 是否配环」两处,均已被全局规则与逐组件 :focus-visible 覆盖,于是转向验收纪律本身:视觉门禁的分工是 audit:visual 管字面量、清单管观感,清单漏段等于放行"
|
||
},
|
||
{
|
||
"id": "g273",
|
||
"title": "新增区域加载遮罩组件 loading",
|
||
"motivation": "刷新一块内容(表格重取、卡片换页)时的等待反馈,库里只有两个原子:spinner(转圈)与 skeleton(骨架)。真实页面要的是「盖住这一块」——内容原地留位、操作被挡住、明确在等什么。此前只能各自写一个 position:absolute 遮罩,于是各处的半透明浓度、是否保留内容、快请求要不要闪各不相同,同一产品里三种等待观感。",
|
||
"acceptance": "新增 loading(props, ...children):children 为被遮挡内容,active 支持布尔与信号;默认 delay 120ms 出场、关闭立即收起(快请求不闪、慢请求必有反馈);内容只降透明度不隐藏(布局不跳),遮罩走 --ua-alpha-strong 混色、aria-busy 同步;text 同时作可见文案与读屏播报,spinnerSize / minHeight 可调;样式只消费 --ua-* 令牌,禁止新增硬编码阴影 / 时长 / 字号;十处落点全同步,npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "出场延迟与关闭立刻收是一对:只做延迟会出现「关得也慢」,遮罩在数据回来后又挂 120ms 更像卡住;所以延迟只作用于「显示」这一个方向,paint(false) 直接执行。内容降透明度到 0.5 而不是隐藏:留位才能让遮罩收起时不跳动。踩坑:ButtonProps.label 已收窄为 string(g240 遗留),demo 里想传函数型文案会直接类型报错,改成固定文案 + 状态切换"
|
||
},
|
||
{
|
||
"id": "g274",
|
||
"title": "补三条新组件的组合配方(骨架 / 刷新遮罩 / 审阅面板)",
|
||
"motivation": "配方文件停在配方 15,而那 15 条写的都是老组件(表单、弹窗、表格分页)。本轮新加的 layout / loading / jsonViewer / diffView / codeBlock / signaturePad 在配方里一个字都没有——Agent 拿到单组件参数表能知道「怎么调」,却不知道「怎么拼」,于是很可能把 layout 当卡片用、把 diffView 当全文展示用。配方是唯一能表达「职责边界」的载体。",
|
||
"acceptance": "recipes.md 新增配方 16(layout + breadcrumb + menu + card 后台页骨架)、17(loading 包住 dataTable 的刷新遮罩)、18(codeBlock + diffView + copyButton + tag 的变更审阅面板),编号自 15 连续递增、每条都有代码块与 3 条要点、代码块内至少含一个已注册组件 id;要点写清职责边界(骨架不塞交互、遮罩不换空数据、diff 不代替全文);llms-full.txt 与文档站随 npm run docs 重生成;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "配方 16 里特意写了「不要再套一层 overflow:auto」——layout 内容区自己就是滚动容器且已在细条名单内,两层滚动会让吸顶页头与滚动条重复出现,这类「两个都对、拼起来错」的坑只能靠配方说明。配方 17 强调 finally 收遮罩:只写 try 分支的示例会诱导出「异常时遮罩永远挂着」的实现"
|
||
},
|
||
{
|
||
"id": "g275",
|
||
"title": "新增相对时间组件 relativeTime",
|
||
"motivation": "动态 / 评论 / 操作日志里最常出现的一列是「3 分钟前」。此前只能自己算差值再手拼文案,于是每个页面各写一套:有的不刷新(页面开着十分钟还显示「刚刚」)、有的把所有时间都渲染成「N 天前」(半年前的记录也这么写,读者还得自己换算)、有的遇到脏数据直接抛错让整列空白。这三件事都该由一个组件定死。",
|
||
"acceptance": "新增 relativeTime(props):value 接受 ISO 串 / 10 位秒戳 / 13 位毫秒戳 / Date;按 refresh(缺省 30s)自动刷新文案,超 threshold(缺省 7 天)改显示绝对日期;title 恒给精确到分钟的时间,datetime 属性给 ISO;非法时间显示 fallback(缺省 —)不抛错;计时器在节点脱离文档后自断;样式只消费 --ua-* 令牌(等宽数字 + nowrap),禁止新增硬编码阴影 / 时长 / 字号;十处落点全同步,npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "10 位 / 13 位时间戳按数量级判秒与毫秒(<1e12 视为秒)——真按位数数会踩「2033 年以后的秒级戳突破 1e10」这类边界,用数量级更稳。计时器自断用的是 watch.ts 同款「isConnected 为假就 clearInterval」写法,列表反复挂载也不会累积。等宽数字(tabular-nums)是时间列不抖动的关键,肉眼很难描述但一排「3 分钟前 / 11 小时前」混排时立刻能看出来"
|
||
},
|
||
{
|
||
"id": "g276",
|
||
"title": "新增迷你趋势线组件 sparkline",
|
||
"motivation": "仪表盘卡片里「一个数 + 一条走向」是最常见的信息单元,库里有 statistic 装主数值,却没有表达走向的图形——于是各页面要么塞一张完整图表(坐标轴、图例、交互一整套,跟卡片格子完全不匹配),要么干脆不显示趋势。迷你趋势线该只有折线本身:无坐标轴、无交互、尺寸听调用方。",
|
||
"acceptance": "新增 sparkline(props):values 序列经 max/min 归一后按 viewBox 等比缩放,首末点贴边、内缩 2px 防裁切;area 缺省填充(低透明度)、marker 缺省标记末点;全等值时走中线不除零;非有限数先剔除;少于两点返回空图不画假横线;颜色全走 currentColor(线 / 面积 / 圆点),父级改 color 即换色;样式只消费 --ua-* 令牌,禁止新增硬编码阴影 / 时长 / 字号;十处落点全同步,npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "svg 元素必须 createElementNS,h() 只能造 HTML——写了个 svgEl 小工具统一 setAttribute,避免每处重复三行。全等值除零保护写成 max - min || span 兜底 1:所有点落中线才是正确观感,直接除会得到 Infinity 把折线扔出画布。踩坑:statistic.value 是 Signal<number> 而非数字,demo 里直接传 1284 会类型报错——演示代码也算源码,得按真实 API 写。踩坑:写 backlog 备注时把表达式放进反引号、整串又用 bash 双引号包着,反引号被当作命令替换执行掉了(备注里凭空少一句),改用脚本落盘。"
|
||
},
|
||
{
|
||
"id": "g277",
|
||
"title": "新增日历热力图组件 heatmap",
|
||
"motivation": "活跃度看板(提交、工单、登录)最直观的表达是「一年一张格子图」:一眼看出哪几天密集、哪几周空窗。此前库里没有任何日历型图形——calendar 是可选日期的交互组件,timeline 是一维事件流,都表达不了「二维时间密度」。于是这类页面只能引第三方图表库,为一张格子图背上整套坐标轴体系。",
|
||
"acceptance": "新增 heatmap(props):values 只给有数据的日期(缺失按 0 渲染),同日多条自动求和;末列对齐到本周(周一起算的完整七行),按最大值归一成四档浓度 + 零档;格子尺寸与网格行高同源(cellSize),悬停 title 给日期与数量、formatValue 可自定义;图例「少 → 多」五格与色阶同源;样式只消费 --ua-* 令牌(色阶走 color-mix + --ua-alpha-*),禁止新增硬编码阴影 / 时长 / 字号;十处落点全同步,npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "格子的浓度是「相对」而不是绝对:按当批数据的最大值归一,所以它回答「哪天更密」,不回答「数值多高」——这点必须写进文档,否则会有人拿它当绝对值比较两张图。末列对齐用「今天 + 到本周日的天数」再回退 12 周,保证最后一列永远完整,不会因为今天是周三就少三格。踩坑复现:写 backlog 备注时把表达式放进反引号、整串又被 bash 双引号包住,反引号被当成命令替换执行、备注少一句(g276 已踩一次),这轮改成先把条目写成 JSON 文件再读,彻底绕开 shell 解释。"
|
||
},
|
||
{
|
||
"id": "g278",
|
||
"title": "热力图图例与网格尺寸同源,并把两个图表件纳入视觉抽查",
|
||
"motivation": "上一轮交付 heatmap 时图例色块走的是样式表里的 --ua-space-3(12px),网格格子走的是调用方内联的 cellSize(缺省 11px)——同一个小部件里两个尺寸来源,差 1px 在 11px 的格子上就是 9% 的体量差,图例看起来「比格子胖一圈」。同时 heatmap 与 sparkline 都是纯视觉件(颜色深浅 / 折线走向就是全部信息量),却没有视觉抽查位,改一个 alpha 档位没有任何人眼验收环节盯着。",
|
||
"acceptance": "heatmap 的图例色块改用与网格同一个 cell 值内联设置尺寸,样式表里删掉 .ua-heat-cell 的写死宽高(尺寸只留一个来源);ITERATION.md 抽查清单新增 heatmap / sparkline 两段(各 3 条、维度标签仍限五类),ua verify 的 REQUIRED 扩到十三段、下限仍由段数 × 3 推导(39);AGENTS.md 与 verify 成功语同步为十三段 / ≥39 条;npm run audit:visual 仍为 0 处、npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "这类「同一部件两处写尺寸」的问题靠肉眼很难定位(只会觉得图例有点不对),是靠读代码时发现「样式表写 12px、内联写 11px」才抓住的——派生量必须只有一个来源,与 CODING.md 的派生语义一致原则是同一件事。清单扩容依旧不手改总数:MIN_TOTAL 上一轮已改成段数 × 3,这轮只加两段就自动变 39。"
|
||
},
|
||
{
|
||
"id": "g279",
|
||
"title": "diffView 增加「忽略行内空白」模式",
|
||
"motivation": "真实审阅里最常见的「假差异」不是内容改了,而是格式化:换了个格式化器、把 4 空格改成 2 空格、行尾多了空格——diffView 会把这些全标成增删行,真正的逻辑改动被埋在几十行空白色块里。人眼在噪音里找信号必然漏,于是审阅退化成「大概看了下」。",
|
||
"acceptance": "DiffViewProps 增加 ignoreWhitespace(缺省 false):开启后比对键把行内连续空白压成一个空格并 trim,显示仍用原文(缩进不丢);LCS、公共前后缀、DP 全部走比对键,同一套口径不出现两处判定;统计条显示「已忽略行内空白差异」把口径写在脸上;manifest 参数表与步骤同步(verify 双向校验);演示加一组「只差缩进」的对照实例;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "关键是把「显示文本」与「比对键」拆成两个字段(Row.text / Row.key),而不是在比较时临时归一:临时归一会出现「前缀比较用原文、中间段用归一」的两套口径,同一份数据两种结论。归一只影响比对,不影响展示——用户看到的仍是真实缩进,这决定了它是「过滤器」而不是「美化器」。提示文案必须出现在统计条里:静默忽略差异比不忽略更危险。"
|
||
},
|
||
{
|
||
"id": "g280",
|
||
"title": "formItem 支持条件显示(visible 信号)",
|
||
"motivation": "「选了『其他』才出现输入框」「选了企业才要税号」这类联动字段在任何真实表单里都有,此前调用方只能自己重建 items 数组:一旦重建,字段的校验结果、错误文案、焦点全部丢失,用户刚看到的红框会在切换选项时凭空消失。而如果只是把节点藏起来(不管校验),被藏起来的必填项又会永远拦住提交。两条路都错,缺的是「隐藏即退出校验」这一个语义。",
|
||
"acceptance": "FormItemProps 增加 visible(Signal<boolean>):为假时 el.hidden 隐藏并立即清掉错误态;validate 与 validateAsync 首行统一「隐藏即返回 null」,同步 / 异步两条路同一口径;manifest 的 form 参数表补(formItem)visible 与一条步骤(verify 双向校验 FormItemProps 字段);npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "隐藏时主动 paint(null) 而不是等下一次校验:否则用户切换选项的瞬间会留下一个「看不见的红框」,而它仍存在于 DOM 的错误区里。校验的早退放在函数首行而不是规则循环里,是为了让「隐藏 = 不参与」成为唯一判定点——两处各写一份条件迟早会漂。form 的参数表用(formItem)前缀登记子接口字段,是这套清单既有的约定,新增字段必须跟着加。"
|
||
},
|
||
{
|
||
"id": "g281",
|
||
"title": "抽出 diffKit 纯逻辑并给皮肤包加冒烟脚本",
|
||
"motivation": "g269 把行级差异算法直接写在 diffView 的渲染函数里,于是它有「三处前缀 / 后缀 / 中间段」这种一眼看不出对错的逻辑,却没有任何断言——改一个比较符肉眼完全看不出来,只有等某次审阅漏掉一行才可能发现。皮肤包此前冒烟覆盖为 0:frame 14 条、table-core 31 条,皮肤层全靠演示页人工看。",
|
||
"acceptance": "新增 packages/skin/ui/src/diffKit.ts(normalizeLine / toRows / alignMiddle / diffLines 四个纯函数 + DiffOp / DiffRow 类型 + DP_LIMIT 常量),diffView 只保留渲染;新增 packages/skin/ui/scripts/smoke.mjs 八条断言(相同文本全 same、纯增、纯删、尾部换行不算差异、忽略空白生效、不忽略时报差异、行内空白归一、超限退化后内容不丢),npm run smoke 串上第三段,SKIN SMOKE OK 是门禁的一部分;AGENTS.md 的 smoke 描述同步为「三段 / skin 8 条」;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": "第 6 条「不忽略空白时同一对文本必须报差异」是对照组:只测「忽略后无差异」会被「函数永远返回空」的实现骗过——每个开关类特性都该配一条反向断言。冒烟直接 import dist/diffKit.js(构建产物),因此 npm run check 里 smoke 必须排在 build 之后,这一点由 check 链顺序保证。踩坑:style 门禁要求导出类型也带 /*** ***/ 注释,抽出模块时把注释写成 // 会直接红。"
|
||
},
|
||
{
|
||
"id": "g282",
|
||
"title": "codeBlock 增加长行折行开关",
|
||
"motivation": "代码块默认横向滚动是为了保住行号对齐,但这条缺省在两种场景里是错的:窄屏(手机 / 抽屉里)横向拖动读代码极其难用;长日志、SQL、base64 这类「一行几百字符」的内容横向滚动等于没法读。缺少折行开关,调用方只能自己在外面覆写 white-space,而这恰好会破坏行号列的表现。",
|
||
"acceptance": "CodeBlockProps 增加 wrap(缺省 false):为真时容器加 is-wrap,行按容器宽度折行(pre-wrap + break-word),行号列仍只占首行左侧;缺省行为(横向滚动)不变;manifest 参数表与一条步骤同步(verify 双向校验 CodeBlockProps 字段);演示里的代码块改为 wrap: true 展示折行;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": "折行与行号对齐是互斥的:折行后一个逻辑行占多行,行号列只会贴在最左一列上——所以这条不能做成缺省,必须让调用方明确选择「读得下」还是「对得齐」。CSS 用 pre-wrap 而不是 pre-line:缩进是代码语义的一部分,折行不能顺手把行首缩进吃掉。"
|
||
},
|
||
{
|
||
"id": "g283",
|
||
"title": "减少动效补齐第三类:旋转 / 缩放过渡",
|
||
"motivation": "audit:motion 只守两类——无限循环动画与悬停位移。可空间动效其实有三类:折叠箭头的 90° 旋转、按压缩放(scale)、抽屉滑入的 transform 过渡都属于第三类,它们在减少动效下仍会「动」,而前两类门禁完全看不见。g273 之前的 jsonViewer 折叠箭头就是典型:transition: rotate 一条都没被任何门禁盯住。",
|
||
"acceptance": "ui.css 末尾补一条通配降级:prefers-reduced-motion 下把 ua- 类的 transition-property 收窄成颜色类属性(color / background-color / border-color / box-shadow / opacity / fill / stroke),空间变换即时到位;audit:motion 增加第三段审计(识别 transition 里提到 rotate / scale / transform 的规则,要求存在通配 transition-property 收窄兜底),缺失即退出码 1 并列出缺口;报告与 --json 输出同步;AGENTS.md 的动效门禁说明补第三类;npm run check 全绿(当前 35 处全部覆盖)",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": "关键取舍是「收窄过渡属性」而不是「关掉全部过渡」:颜色 / 阴影 / 透明度变化不产生空间位移,砍掉它们会把状态反馈一起砍没(用户看不到 hover 变色的反馈,反而更困惑)。审计的兜底形态也随之改成「存在一条通配 transition-property 规则即视为全站覆盖」,避免要求 35 处各自补一条——通配一条的好处是新增组件不必记得补。踩坑:给 formatMotionReport 加第三段时只改了函数体没改解构签名,运行时报 spatial is not defined——函数签名和返回值是一对,加字段两处都要动。"
|
||
},
|
||
{
|
||
"id": "g284",
|
||
"title": "新增环形进度组件 progressRing",
|
||
"motivation": "反馈类里只有线形 progress 与 spinner:仪表盘、完成度徽章这类场景通常要「一个圆环 + 中心百分比」,现有组件只能拿 SVG 手搓,结果是每个页面各搓一份、弧长算法与旋转原点还各不相同(默认从 3 点起步是最常见的错)。环形进度属于高频小件,应该有统一定义。",
|
||
"acceptance": "新增 progressRing(props):SVG 双圆(底环 + 进度弧)靠 stroke-dasharray 画弧,0 点固定在 12 点方向顺时针;value 支持数字与信号(信号变化自动重绘),越界自动夹到 0-100;showText=false 可只留环;role=progressbar + aria-valuenow 随值同步;样式只消费 --ua-* 令牌(弧色走 currentColor、底环走描边色),audit:visual 不新增字面量、audit:motion 的旋转过渡有减少动效兜底;manifest 双处 + demo sec-ring + README 清单与分组同步,npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": "弧长靠 dasharray(画 c*pct/100,剩下留空)而不是 path 弧线命令:一条属性就能表达任意进度,省掉大弧标志位与起止角的三角计算。旋转原点必须给 rotate(-90 cx cy),否则默认从 3 点方向起步——这是手搓环最常翻的车。第一版把两个 circle 塞进了中间层 div 再用 querySelector 搬进 svg,DOM 上等于挂了两次;改成先 svg.append(track, arc) 再组容器。另外 transition 一开始写成 0.3s 字面量,被 audit:visual 的 duration-literal 抓住,改用 --ua-duration-slower。"
|
||
},
|
||
{
|
||
"id": "g285",
|
||
"title": "分页总量走信号:越界页码自动钳回父级",
|
||
"motivation": "pagination 的 total 是静态数字,于是「筛选后总量从 137 掉到 25」这类场景只能靠调用方自己算新页数:不改 props.page 就停在空页(第 14 页里一条数据都没有),改了又要自己复制一份 Math.ceil 公式。渲染层的 renderList 虽然把显示页码夹住了,但信号里仍是旧值,父级 state 与界面不一致——这正是「派生量有两个来源」的典型。",
|
||
"acceptance": "PaginationProps.total 支持 number | Signal<number>;组件内新增钳制 effect:页码越界(>最大页或 <1)时把夹取后的值写回 page 信号并回调 onChange,合法时不动手(不产生无意义回调);总量恢复后不记忆已失效的旧页码;manifest 的 total 类型与说明同步;demo sec-pagination 增加「总量 137 条 / 筛到 25 条」实例,能看到页码从 14 自动落到 3;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g286",
|
||
"title": "新增单行通知条组件 noticeBar",
|
||
"motivation": "播报类目前只有 alert(可多行、带标题、占块)与 toast(浮层、会自动消失)。页顶常驻的那种「一行通告 + 关闭」没有对应件:用 alert 会浪费一整块垂直空间,用 toast 又会被自动收走。真实场景(维护公告、试用期倒计时、离线重试提示)需要的是一行高、可关闭、超长能滚的横条。",
|
||
"acceptance": "新增 noticeBar(props):语义色四档 + 左侧符号(色彩之外的第二线索)+ 可选关闭;scroll=true 时正文走跑马灯(--ua-duration-loop-long),悬停暂停,减少动效偏好下停成静态且完整原文仍在 title;关闭隐藏整条并回调 onClose;样式只消费 --ua-* 令牌(audit:visual 不新增任何字面量,首次实现因 line-height:1 被拦下并改掉);audit:motion 的无限动画兜底从 6 处增至 7 处;manifest 双处 + demo sec-notice + README 清单与分组同步,npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "new",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g287",
|
||
"title": "通知条关闭走淡出退场,窄屏改为换行",
|
||
"motivation": "g286 的 noticeBar 关闭是直接 hidden:整条瞬间消失,和 alert 的淡出退场手感不一致(同类播报件两种消失方式)。另一处是硬 nowrap——不传 scroll 时窄屏上长文案被裁掉且无法读到,信息直接丢失。",
|
||
"acceptance": "关闭时先挂 is-out 类做淡出 + 上移(走 --ua-duration-base 与 --ua-translate-4,减少动效下只淡出不位移),200ms 后再 hidden,onClose 立即触发;≤640px 时正文允许换行并停掉跑马灯;audit:motion 的旋转/缩放过渡兜底从 35 处增至 36 处;demo 公告文案改回真实能力(原文宣称的「列宽记忆」并不存在);npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g288",
|
||
"title": "文档步骤下限提到 4 条并补齐四个薄条目",
|
||
"motivation": "verify 只要求 steps ≥3,三条恰好是「怎么传 / 能做什么 / 一个变体」的标准配方——写得出来但没有边界:不说什么情况下别用、出错行为是什么。Agent 读文档时缺的正是这两块,于是把「不该这么用」的坑留给用户踩。",
|
||
"acceptance": "verify 的 steps 下限从 3 提到 4 并在报错里点明四步各写什么;badge / divider / kbd / highlight 四个三条目组件补第四条真实内容(交互语义归属、读屏分组、平台键名映射、空关键词行为),不是凑数;AGENTS.md 两处说明同步(组件入库表与 verify 覆盖清单);npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g289",
|
||
"title": "tooltip 出场期间跟随滚动重算翻转",
|
||
"motivation": "气泡是 wrap 内的绝对定位,滚动时跟着触发器走没问题;问题在翻转判定——flipSide 拿的是视口几何,只在出场那一瞬算一次。用户滚一下页面,原本「朝上放不下所以朝下」的判断可能已经反过来,气泡就压在视口边上;反过来也一样。这是只在滚动后才显形的错位,代码评审很容易漏过。",
|
||
"acceptance": "出场期间挂 scroll(捕获阶段,容器滚动不冒泡)+ resize 监听,rAF 节流重算 flipSide;收起时立即摘监听(关闭态不空转),重复出场不重复绑定;tooltip 文档 steps 补该行为;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g290",
|
||
"title": "抽出 trackShift 并接到下拉弹层",
|
||
"motivation": "g289 把滚动跟随写在了 tooltip 里,可下拉族(select / autoComplete / cascader / datePicker / timePicker / colorPicker / menu / multiSelect)用的是同一个 flip,同样只在打开那一瞬算一次——同一个缺陷被复制在九处,修一处等于只修了一份。先把监听逻辑抽成公共件,tooltip 用法不变(还顺手删掉了刚写的一整段重复代码),再给用量最大的 select 接上。",
|
||
"acceptance": "position.ts 新增 trackShift(onShift):捕获阶段监听 scroll + resize、rAF 节流、返回摘除函数并撤销待执行帧;tooltip 改用它(删除内联的 bound/raf/bind/unbind 五个变量,文件反而短了);select 展开期间接上 trackShift,收起即摘;两个组件的文档 steps 同步口径;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g291",
|
||
"title": "下拉族七处弹层接上滚动跟随",
|
||
"motivation": "g290 抽出了 trackShift 但只接到了 select:autoComplete / cascader / colorPicker / datePicker / timePicker / menu / multiSelect 七处仍是「打开时算一次」的旧写法——同一个 flip、同一个缺陷,用户滚一下页面弹层就压到视口边上。欠账按组件分散在七个文件里,正是那种「每轮修一个,永远修不完」的活。",
|
||
"acceptance": "七处 effect 统一改成:收起摘监听 / 展开先算一次 / 展开期间 trackShift 重算(模式与 select 一致);七个文件的 position 导入补 trackShift;改动为机械替换,不改各处触发元素(input / trigger / box);npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tune",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g292",
|
||
"title": "toast 溢出挤掉最早一条改走退场",
|
||
"motivation": "同屏满 4 条后第 5 条进来,最早那条是直接 remove():凭空消失。而手动关闭 / 自动超时都是淡出后移除——同一类播报件在同一屏上有两种消失方式,g287 刚为 noticeBar 统一过这个口径,toast 这里是漏网的一处。",
|
||
"acceptance": "溢出时改调 dismiss()(摘 ua-open 触发过渡 + transitionend 与 320ms 兜底二选一),与手动关闭走同一条路径;toast 文档 steps 补堆叠上限与 duration: 0 常驻的用法;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "visual",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g293",
|
||
"title": "把浮层定位口径写进 Agent 约定",
|
||
"motivation": "g289–g291 连着三轮在修同一个缺陷:浮层只在打开时算一次翻转,滚动后就错位。修完了却没沉淀成规则——下一个新组件(或新 Agent)照样会写出「只在 open 时算一次」的版本,等于再把欠账复制一份。这类「已经用三轮纠正过的写法」必须变成入库门槛,否则只是把坑往后推。",
|
||
"acceptance": "AGENTS.md《贡献新组件的强制约定》新增第 7 条:打开时 flip / flipSide 算一次、展开期间 trackShift 重算、收起调释放函数摘监听,并点明「只在打开时算一次」的后果;npm run check 全绿",
|
||
"status": "done",
|
||
"domain": "tooling",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g294",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"title": "dataTable 列显隐:工具栏「列」面板 + 受控 hiddenColumns",
|
||
"motivation": "表格列多时用户只想看关心的几列,目前只能靠调用方自己改 columns 重渲染;且 dataTable.ts 已顶到 199 行上限,受控桥接与工具栏构件该分出去。",
|
||
"acceptance": "① 开 columnToggle 后工具栏出现「列」按钮,弹层勾选即时增删列(表头/单元格/colspan/骨架行/空状态/导出全部跟随);② 至少保留一列可见,最后一列的勾选框置灰;③ 传 hiddenColumns 信号可受控并写回;④ 样式只消费 --ua-* 令牌,npm run audit:visual 计数不涨;⑤ 受控页码与选中桥接抽到 tableBridge.ts,dataTable.ts 仍 ≤200 行;⑥ npm run check 全绿。",
|
||
"notes": "列显隐落地:新 tableColumns.ts(触发器 + 勾选弹层,末列锁死)+ dataTable 派生 shown()/span() 统一供表头/单元格/骨架/空状态/colgroup 使用;受控页码与选中桥接抽到 tableBridge.ts(dataTable.ts 199→186 行);导出改惰性取列跟随可见列;ui.css 加 .ua-col-list/.ua-col-item(纯令牌,audit:visual 仍 0 处)"
|
||
},
|
||
{
|
||
"id": "g295",
|
||
"domain": "visual",
|
||
"status": "done",
|
||
"title": "表面阶梯审计:新增 ua audit:surface 并修正悬停底看见不了",
|
||
"motivation": "对比度门禁只管「文字 vs 底色」,管不到「底色 vs 底色」;实测表面→二阶表面亮度比只有 1.008(暗色 1.006),即悬停底与表面几乎同色,6 处 surface-2 与新建的列面板悬停都白改。",
|
||
"acceptance": "① ua audit:surface 按四套主题逐档验阶梯(页面底→表面→悬停底→按下底 ≥1.04,凹陷底 ≥1.02),不达标退出码 1,负向自检确认会红;② surface-2/3 改为从 surface 派生并拉开档距;③ audit:surface 进 check 链、package.json 脚本、usage 串与 doctor 的 check 链检查;④ 列面板悬停改用 surface-2(原先撞 .ua-pop 同底色);⑤ audit:contrast 与 audit:visual 仍全绿。",
|
||
"notes": "新 lib/surface-audit.mjs:四套主题 × 四段阶梯算亮度比(1.04 / 凹陷 1.02),--json/--list 双出口,进 check 链与 doctor;tokens surface-2/3 改为从 surface 派生(亮 6%/14%,暗 7%/17%),阶梯由 1.008→1.123(暗 1.006→1.193);列面板悬停改用 surface-2(原与 .ua-pop 同底色等于没反馈);contrast-audit 导出 resolve/ratio 供复用"
|
||
},
|
||
{
|
||
"id": "g296",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"title": "修 form 校验失败仍提交:自绘控件字段没有原生 input 时守卫失效",
|
||
"motivation": "提交守卫写成「找到焦点目标才中止」,而焦点目标只查 input/textarea/select;非搜索式下拉、评分、开关容器里没有原生 input,firstBad 为 null 时校验有错也照样调 onSubmit(demo 的「城市」必填下拉即可复现)。",
|
||
"acceptance": "① 中止判据改为错误条数 bad > 0;② 焦点落点按 控件 → 触发器(button/[tabindex]) → 字段容器(tabIndex=-1) 三级退让;③ manifest 的 form 步骤与 demo 描述同步说明;④ npm run check 全绿。",
|
||
"notes": "form 提交守卫改判错误条数;焦点按 控件 → 触发器 → 字段容器(tabIndex=-1) 三级退让,自绘控件字段也能被定位并拦住提交"
|
||
},
|
||
{
|
||
"id": "g297",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "新增 ua loop:循环锁 / 留痕 / 待办与选题建议一次给全",
|
||
"motivation": "接力实例判断「该不该抢锁、上一条做完没、下一条选谁」过去要翻 .ua/loop.lock 的 mtime、tail 留痕、再数 backlog,或去查调度器 sqlite;口径散在 ITERATION.md 里靠人记。",
|
||
"acceptance": "① ua loop 输出锁心跳(<15 分钟判定与协议同源)、上次留痕、backlog open 与视觉欠账数、下一条选题建议;② --json 给脚本;③ 进 usage 串与 CLI 清单 exports(doctor 的 ghost 检查通过);④ ITERATION.md 写明诊断入口、AGENTS 的 CLI 列表补一行;⑤ npm run check 与 doctor 全绿。",
|
||
"notes": "ua loop 只读诊断:锁心跳(<15min 同协议口径)/ 上次留痕 / backlog open 与视觉欠账 / 下一条选题建议,--json 可机读;进 usage 与 CLI 清单,doctor 11 项全过,ITERATION 与 AGENTS 同步"
|
||
},
|
||
{
|
||
"id": "g298",
|
||
"domain": "visual",
|
||
"status": "done",
|
||
"title": "死令牌归位:按下态改用 surface-3,结果页大标题用上 font-size-xl",
|
||
"motivation": "tokens --unused 报出 2 个令牌无人引用:surface-3(注释写「按下底」,但四处 :active 仍用与悬停同档的 surface-2,于是按下与悬停同色)与 font-size-xl(字号最大档没有任何组件用)。",
|
||
"acceptance": "① 下拉触发器 / 折叠头 / 分页按钮 / 日历格四处按下态改 surface-3,注释「加深一档」与实现对齐;② .ua-result-title 升到 xl,与弹窗标题 lg 拉开层级;③ ua tokens --unused 归零;④ audit:surface / audit:visual / audit:contrast 全绿。",
|
||
"notes": "四处 :active 改 surface-3(按下比悬停更实,与注释一致),.ua-result-title 用上 xl;tokens --unused 由 2 个降为 0,三项视觉门禁全绿"
|
||
},
|
||
{
|
||
"id": "g299",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "视觉抽查清单补三段:commandPalette / noticeBar / progressRing",
|
||
"motivation": "清单只覆盖到 sparkline,后期新增的命令面板、通知条、环形进度没有抽查位——视觉类条目收尾时「照单过一遍」会直接跳过它们,等于裸奔。",
|
||
"acceptance": "① ITERATION.md 新增三段共 11 条,维度落在五类内;② ua.js 的 REQUIRED 从 13 扩到 16、下限自动变 48 条、OK 文案同步;③ AGENTS 的清单描述同步为十六段 ≥48 条;④ npm run verify / check 全绿。",
|
||
"notes": "ITERATION.md 抽查清单加 commandPalette(4) / noticeBar(3) / progressRing(4);REQUIRED 13→16、条目下限自动 39→48、verify OK 文案与 AGENTS 描述同步"
|
||
},
|
||
{
|
||
"id": "g300",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"title": "toast 悬停暂停从未生效:宿主穿透把指针拦在门外",
|
||
"motivation": "toast.ts 里写了 pointerenter 暂停倒计时,但样式只在 .ua-toast.is-closable / .is-action 时才给 pointer-events: auto,默认 toast 全程穿透——最常见的「toast.success(msg)」根本收不到 hover,暂停逻辑成了死代码;键盘用户 Tab 进关闭按钮时也照样被倒计时收走。",
|
||
"acceptance": "① .ua-toast 自身接收指针,删掉 is-closable / is-action 两处补丁式 pointer-events(宿主容器仍穿透,只有卡片本体可命中);② 暂停 / 恢复抽成 pause/resume,剩余时长不低于 400ms 且重复进入不二次扣减;③ focusin / focusout 与悬停同口径暂停,焦点在卡片内部移动不恢复;④ manifest 的 closable 说明与手把手步骤同步,npm run check 全绿。",
|
||
"notes": "pointer-events: auto 落到 .ua-toast 本体,删掉 is-closable / is-action 两处补丁规则;暂停恢复抽成 pause/resume(剩余不低于 400ms、重复进入不二次扣减),focusin/focusout 与悬停同口径、焦点在卡片内部移动不恢复;manifest 的 closable 说明与手把手步骤同步"
|
||
},
|
||
{
|
||
"id": "g301",
|
||
"domain": "new",
|
||
"status": "done",
|
||
"title": "新增拖拽排序列表 sortableList(88 个组件)",
|
||
"motivation": "库里有 list 展示列表、virtualList 长列表,但缺「可调整顺序」的交互件:任务排优先级、字段顺序、看板列这类场景每次都得自己写一遍拖放,dragstart/dragover 的边界(不拦 dragover 就没 drop、拖拽中重建节点会中断手势)反复踩。",
|
||
"acceptance": "① 整行可拖,指针越过邻行中线即换位,dragend 落定;② 键盘等价 Alt+↑↓ 上下搬一格、Alt+Home/End 搬首尾,裸 ↑↓ 留给页面滚动;③ 顺序写回 value 信号并触发 onChange,拖拽期间跳过外部重排避免中断手势;④ 句柄 { el, move, order },下标越界与禁用时静默忽略;⑤ 样式只消费 --ua-* 令牌,禁止新增硬编码阴影/时长/字号;⑥ manifest/docs/README/demo/llms-full 同步,npm run check 全绿。",
|
||
"notes": "新增 src/sortableList.ts(118 行)+ types 两项 + index 导出 + ui.css 六条(只消费令牌);整行可拖、越过邻行中线换位、dragend 落定,Alt+↑↓/Home/End 键盘搬位;顺序写回 value 信号并触发 onChange,拖拽中跳过外部重排;句柄 { el, move, order } 越界静默忽略;组件 87→88,README/AGENTS/ARCHITECTURE 计数与 docs 同步"
|
||
},
|
||
{
|
||
"id": "g302",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"title": "dataTable 接上内核的多列排序:Shift+点击叠加第二列",
|
||
"motivation": "table-core 的 toggleSort 早就支持 action.multi(按 rules 顺序逐列比较),但皮肤层 dispatch 时从不传 multi,用户按住 Shift 点第二个表头毫无反应,多列排序只能靠手写 setSorting;表头标记也只反映单列方向,看不出比较优先级。",
|
||
"acceptance": "① 表头点击与键盘 Enter / 空格都读 shiftKey 并透传 multi;② 参与排序的列 ≥ 2 时标记追加优先级序号(↑1 / ↓2),单列时保持原箭头;③ manifest 手把手与 demo 描述同步;④ npm run check 全绿。",
|
||
"notes": "th 的 toggle 收 multi 参数,onClick / onKeydown 都读 shiftKey;sortMark 在 rules ≥ 2 时追加优先级序号(↑1 / ↓2),单列保持原箭头;manifest 步骤与 demo 描述同步"
|
||
},
|
||
{
|
||
"id": "g303",
|
||
"domain": "visual",
|
||
"status": "done",
|
||
"title": "拖拽行别用整行透明度:正文对比被压到 4.86",
|
||
"motivation": "sortableList 的拖拽态原本整行 opacity: .7,实测把拖拽行正文对比从 12.03 压到 4.86(亮色)——低于项目正文 7:1 的口径,而「正在被拿起」完全可以用虚线边 + 抬升投影表达,不必牺牲可读性。",
|
||
"acceptance": "① 拖拽态去掉整行 opacity,改用 --ua-shadow-2 抬升 + 虚线 accent 边 + tint-accent 底;② 静态态对比回到 12+(亮)/ 11+(暗);③ 把手悬停转 accent 色提示可抓,拖拽中 cursor 为 grabbing;④ audit:visual / audit:contrast / audit:motion 全绿,manifest 步骤同步。",
|
||
"notes": ".is-dragging 去 opacity 改 --ua-shadow-2 抬升 + 虚线 accent 边 + tint-accent 底(对比 4.86 → 12.03 亮 / 11.32 暗);把手悬停转 accent、拖拽中 grabbing;item 过渡补 box-shadow,manifest 步骤同步"
|
||
},
|
||
{
|
||
"id": "g304",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "配方补一条可排序检查单:句柄式组件的拼装样例",
|
||
"motivation": "sortableList 是句柄式组件(返回 { el, move, order }),与 form / virtualList 同类,但 18 条配方里没有一条演示句柄式拼装——Agent 照配方抄会把返回值直接当节点 append,页面一片空白;「顺序只存一份」「move 与拖拽同一条提交路径」这些约定也没人写下来。",
|
||
"acceptance": "① recipes.md 新增配方 19(编号连续、带代码块与要点),代码块里认出 sortableList / card / button / toast;② 要点覆盖句柄要 append(.el)、顺序唯一真相、move 与拖拽同路径、边缘不自动滚动、条目文案唯一;③ ua recipes 19 能取到该条,llms-full.txt 与 llms.txt 重生成;④ npm run verify / check 全绿。",
|
||
"notes": "recipes.md 新增配方 19:sortableList + card + button + toast 的可排序检查单,要点写清句柄要 append(.el)、顺序唯一真相、move 与拖拽同路径、边缘不自动滚动、文案唯一;ua recipes 19 可取出,llms.txt / llms-full.txt 重生成"
|
||
},
|
||
{
|
||
"id": "g305",
|
||
"domain": "new",
|
||
"status": "done",
|
||
"title": "新增瀑布流布局 masonry(89 个组件)",
|
||
"motivation": "库里有 grid(等宽等高的规则栅格)与 space(线性间距),但图片墙、评论流、看板这类「列高不等」的布局没有落点,业务侧只能自己写 column-width 与 break-inside,还要记住多列容器不能是 flex/grid 这个坑。",
|
||
"acceptance": "① masonry({ items, columns?, minColumnWidth?, gap? }) 走 CSS 多列:不传 columns 按 minColumnWidth 自适应,传了即固定列数;② 每项包 .ua-masonry-item 并 break-inside: avoid,卡片不在列边界拆断;③ gap 同时管列间距与行间距;④ 样式只消费 --ua-* 令牌,禁止新增硬编码阴影/时长/字号;⑤ manifest/docs/README/demo/llms-full 同步,组件 88→89,npm run check 全绿。",
|
||
"notes": "新增 src/masonry.ts(22 行)+ types + index + ui.css 两条;CSS 多列实现:不传 columns 走 column-width 自适应,传了即 column-count 固定列数,gap 一处同时给 column-gap 与 item margin-bottom,item 带 break-inside: avoid;组件 88→89,README/AGENTS/ARCHITECTURE 计数与 docs 同步"
|
||
},
|
||
{
|
||
"id": "g306",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "淡底也进审计:选中底 / 悬停底的浓度不能再被悄悄调没",
|
||
"motivation": "audit:surface 只验表面阶梯(surface 各档),但全站 52 处选中底与悬停底用的是半透明 accent 淡底(tint-accent / -weak)——浓度手写在令牌里,暗色弱档降到 2% 时悬停与静止只差 1.026,界面「看起来没反应」,而现有门禁一声不响。",
|
||
"acceptance": "① LADDER 增加两档「表面 → 淡底标准档 / 弱档」,阈值 1.04,用 ratio() 先把半透明淡底压到表面上再算;② 四套主题全绿(亮 1.156 / 1.074,暗 1.232 / 1.118);③ 负向自检:暗色弱档改 2% 时 audit:surface 退出码 1 并点出 dark / contrastDark 两处;④ AGENTS 对该门禁的描述同步。",
|
||
"notes": "LADDER 增加「表面 → 淡底标准档 / 弱档」两档(阈值 1.04),复用 contrast-audit 的 ratio():半透明淡底先 over 到表面上再算;四套主题全绿(亮 1.156/1.074,暗 1.232/1.118);负向自检把暗色弱档改 2% 后 1.026 < 1.04,audit:surface 退出码 1 并点出 dark/contrastDark 两处,改回即恢复;AGENTS 描述同步"
|
||
},
|
||
{
|
||
"id": "g307",
|
||
"domain": "visual",
|
||
"status": "done",
|
||
"title": "拖拽行缺少层内抬升:投影被后面的行盖掉",
|
||
"motivation": "g303 给拖拽行加了抬升投影,但行仍是静态定位、没有层级——同一容器里后续兄弟元素绘制在后,被拖那一行的投影直接被下一行的底色切掉,「拿起来」看起来像贴纸被压住。层级令牌只有 dropdown 起的五档(100 起),拿它抬一行会污染语义。",
|
||
"acceptance": "① 新增 --ua-z-raised: 10 令牌(层内抬升,够压过同容器兄弟即可);② .ua-sortable-item.is-dragging 补 position: relative + z-index: var(--ua-z-raised);③ ua tokens --unused 不报新令牌、audit:visual 的 z-literal 仍为 0;④ npm run check 全绿。",
|
||
"notes": "tokens.css 新增 --ua-z-raised: 10(层内抬升档,与 dropdown 100 起的那套分层语义区分);.is-dragging 补 position: relative + z-index: var(--ua-z-raised),投影不再被后续行盖住;tokens --unused 0 个、audit:visual 合计仍 0 处,docs 重生成"
|
||
},
|
||
{
|
||
"id": "g308",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"title": "sortableList 支持拖到边缘自动滚,摘掉「长列表拖不动」的限制",
|
||
"motivation": "文档里写明「拖到边缘不会自动滚动,条目超过一屏就别用」,这等于把最常见的场景(几十条检查单)挡在门外:拖到容器下沿不动,用户只能反复滚动重来。",
|
||
"acceptance": "① dragstart 里一次算好最近的纵向滚动祖先(overflow-y auto/scroll 且确已溢出),dragover 每帧只在贴边 48px 感应带内按帧滚 12px;② 离开感应带或 dragend 立即取消帧并清状态,不留后台 rAF;③ 找不到滚动祖先(整页滚动)时不接管;④ manifest 步骤与配方 19 要点同步改写,npm run check 全绿。",
|
||
"notes": "dragstart 用 scrollHost() 一次算最近纵向滚动祖先(overflow-y auto/scroll 且已溢出),dragover 在上下 48px 感应带内按帧滚 12px(EDGE_BAND/EDGE_STEP 常量),离开感应带或 dragend 取消 rAF 并清 scroller/edgeDir;找不到祖先不接管;manifest 步骤与配方 19 要点同步"
|
||
},
|
||
{
|
||
"id": "g309",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "把每轮迭代的手工留痕脚本收成 ua iter done / add",
|
||
"motivation": "每轮收尾都要临时写 node 脚本改 backlog、手拼 iterations.log 的 JSON——历史踩过 ts 手写缺时区、字段顺序自创、状态忘记改、哈希抄错,lint-iterations 事后才拦。这条流程本该由 CLI 保证,不该每轮靠人记。",
|
||
"acceptance": "① 新增 ua iter done <id> <commit> <summary> [notes]:backlog 置 done、追加 {ts,id,result,commit,summary} 固定字段行,ts 由本机时钟生成带 +08:00 偏移,commit 校验 7-40 位哈希,条目不存在直接报错;② ua iter add <json>:五字段齐全 + id 唯一校验后追加;③ usage 串、dispatch、CLI 清单三处同步(doctor 通过);④ 临时脚本 .ua/log-one.mjs 与 .ua/add-entry.mjs 删除;⑤ npm run check 全绿。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g310",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"title": "multiSelect 补「全选 / 半选」:8 个候选逐个点太慢",
|
||
"motivation": "多选下拉只能逐项点或按退格删标签,选项一多就是体力活;transfer 早就有三态全选框,multiSelect 这个更常用的组件反而没有,用户只能「先搜再一个个点」。",
|
||
"acceptance": "① 新增 selectAll 入参:弹层顶部一行「全选(已选/可选)」,全选 ✓、部分选中显 –、再点即清空这批;② 只作用于当前过滤结果,先搜再全选=按条件批量选;③ 到 max 时补满即停,不静默超选;④ 键盘可达:全选行占 active -1 号位,↑↓ 能走到、Enter 触发;⑤ manifest 参数表与手把手、demo 同步,npm run check 全绿。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g311",
|
||
"domain": "visual",
|
||
"status": "done",
|
||
"title": "骨架文本行高对齐正文:加载完成时表格不再窜一下",
|
||
"motivation": ".ua-skeleton-text 写死 height: 12px,而表格单元格里的正文行盒是「13px 字号 × 1.55 行高 ≈ 20px」——骨架行比真实行矮 8px,五行就差 40px,dataTable 从骨架切到数据时整表高度突跳,比没有加载态更晃眼。",
|
||
"acceptance": "① .ua-skeleton-text 的高度改成 calc(正文字号 × 正文档行高),与真实文字行盒等高;② 同时消掉这处 12px 字面值;③ skeleton 手把手补一条说明为什么按行盒算;④ audit:visual / audit:motion 全绿,npm run check 通过。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g312",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"title": "表格空状态补操作出口:真没数据时只能干看着",
|
||
"motivation": "emptyRow 只区分「筛掉了」与「真没数据」,但两种情况都只给文案——列表本来就空时没有「新建」这类下一步,用户只能自己找工具栏;而筛选没结果时又该引导清条件,两种语义不该共用一个按钮位。",
|
||
"acceptance": "① DataTableProps 新增 emptyAction 插槽,只在「真没数据」时渲染在占位文案右侧;② 有筛选词时该位置仍是「清除筛选」,两者互斥;③ manifest 参数表与手把手、类型注释同步;④ npm run check 全绿。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g313",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"title": "sortableList 禁用态没有任何视觉线索",
|
||
"motivation": "disabled 只拦住了 dragstart 与键盘搬位,界面上一模一样:光标还是 grab、悬停照样变色,用户拖三次没反应只会以为组件坏了。",
|
||
"acceptance": "① 根节点按 disabled 切换 is-disabled 类与 aria-disabled(布尔与信号两种读法都响应);② 禁用时行光标回默认、文字退次级色、把手退到边框色、悬停与按下都不再给底色反馈;③ manifest 的 disabled 说明与手把手补一条,demo 增一个禁用实例;④ audit:visual / audit:contrast 全绿,npm run check 通过。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g314",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "视觉抽查清单补两段:sortableList / masonry",
|
||
"motivation": "清单停在十六段(g299 扩过一轮),之后新增的拖拽排序与瀑布流没有抽查位——这两个组件恰好是「靠观感判断做没做对」的类型(拖拽虚影、列边界断卡),照着清单过一遍时会被直接跳过。",
|
||
"acceptance": "① ITERATION.md 新增两段共 8 条,维度标签落在五类内;② ua.js 的 REQUIRED 从 16 扩到 18(条目下限由段数自动推到 54)、verify OK 文案同步;③ AGENTS 的清单描述同步为十八段 ≥54 条;④ npm run verify / check 全绿。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g315",
|
||
"domain": "tune",
|
||
"status": "done",
|
||
"title": "表格补行展开:长内容只能挤在单元格里或跳页",
|
||
"motivation": "订单备注、用户标签、二级操作这类内容塞进单元格会把列撑爆,跳详情页又太重;dataTable 只有 onRowClick 一条路,等于逼调用方在「挤」和「跳走」之间二选一。",
|
||
"acceptance": "① 新增 renderDetail 入参,传了才多一列展开钮(▸ 旋转 90° 表示展开);② 详情行整行跨列、凹陷底、允许折行,展开态按 rowKey 记,筛选与翻页不丢;③ 展开钮拦冒泡,整行可点时不会顺带触发 onRowClick;④ 空状态与骨架行的 colspan 计入展开列;⑤ manifest 与 demo 同步,npm run check 全绿。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g316",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "ua loop 直接给下一个可用编号,自补条目不再撞号",
|
||
"motivation": "backlog 清空时每轮都要自己翻最大 g 号再自补,历史上撞过号、靠 rebase 收拾;诊断命令明明已经把 backlog 读进内存,顺带算一个 nextId 是举手之劳。",
|
||
"acceptance": "① ua loop 文本输出在「需自补」时带「下一条可用编号 gNNN」;② --json 增 nextId 字段供脚本直接用;③ CLI 清单与 ITERATION.md 的诊断入口描述同步;④ npm run doctor / check 全绿。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g317",
|
||
"domain": "visual",
|
||
"status": "done",
|
||
"title": "文字选区跟随主题:默认蓝底选区在暗色下突兀",
|
||
"motivation": "全站没有任何 ::selection 规则,用户选中文本时走浏览器默认选区——亮色下是系统蓝、暗色下是亮蓝底黑字,与 --ua-* 主题色系完全不搭,代码块 / diffView 这类大量复制的场景尤其刺眼;caret-color 已跟随 accent,选区却没有,两者不一致。",
|
||
"acceptance": "① ui.css 新增全局 ::selection 规则,选区底色由既有 --ua-color-accent 经 color-mix 派生(低透明度,与焦点环同族),不新增任何硬编码颜色;② 亮 / 暗 / 高对比主题下选区均可辨认,audit:visual / audit:contrast / audit:surface 全绿;③ 文档(llms-full.txt 经 npm run docs 同步)令牌说明处补一句选区口径;④ npm run check 通过。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g318",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "配方 20:分步表单向导(步进条 + 按步校验)",
|
||
"motivation": "多步向导是后台最高频的表单形态之一,现有 19 条配方没有覆盖「步进条 + 分步字段 + 步间校验 + 回跳」的组合:stepper 的 active 双向绑定与 formItem 句柄的 validate() 恰好能拼出这个场景,缺一条示范怎么把校验拦在步与步之间,而不是最后一步集体报错。",
|
||
"acceptance": "① recipes.md 新增配方 20(编号连续、含代码块与要点,代码块能被 ua recipes 认出 stepper / formItem / button / card / toast 组件);② 配方代码与真实签名一致(FormRule.message、button disabled 收 Signal、formItem 句柄 validate);③ ua recipes 20 与 ua search 均能命中;④ npm run check 通过。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g319",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "ua info 支持逗号分隔批量查询多个组件",
|
||
"motivation": "Agent 拼一个界面通常要 3–5 个组件的参数,现在只能 `ua info button`、`ua info select` 一条条查(MCP get_component 同样单查),每轮多花好几次往返;逗号分隔批量一次拿全,文本 / --md / --json 三种模式都覆盖。",
|
||
"acceptance": "① `ua info button,select --md` 一次输出两个组件的 Markdown 片段(片段间以分隔线隔开);② `--json` 多值时输出数组、单值保持对象(不破坏现有脚本消费);③ 不存在的 id 报错并指明是第几个;④ manifest usage 串同步更新;⑤ npm run check 通过。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g320",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "配方 21:消息中心(铃铛角标 + 抽屉通知流)",
|
||
"motivation": "badge 的手把手里明确说「点角标进消息中心应当把 badge 塞进 button 里」,但 20 条配方没有一条给出消息中心的全貌:未读数怎么单一来源、静态快照组件(list / badge)怎么接信号、已读回写怎么只写数据源,都缺示范。",
|
||
"acceptance": "① recipes.md 新增配方 21(编号连续、代码块与要点齐全,ua recipes 能认出 badge / drawer / list / relativeTime / button / card 组件);② 配方与真实签名一致(badge count、drawer open 收信号、list items 静态数组、relativeTime value 收时间戳);③ npm run check 通过。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g321",
|
||
"domain": "visual",
|
||
"status": "done",
|
||
"title": "纯交互控件防误选 + 移动端点按灰闪清理",
|
||
"motivation": "按钮 / 徽标 / 页签 / 分页 / 步骤条这类控件的文案是「动作」不是「内容」:双击或拖选会把按钮文字选中变蓝,观感割裂且打断连点(现全库只有 8 处零散 user-select,交互控件基本没盖住);移动端浏览器默认点按灰闪(tap highlight)与令牌皮肤同层突兀,按下反馈应由 :active 态承担。",
|
||
"acceptance": "① ui.css 全局区新增一条控件清单式 user-select: none(button/tag/badge/fab/backtop/page-btn/tab/seg-btn/step/rate-star/checkbox/radio/switch/menu-item/crumb),内容型组件(代码块 / 表格 / 描述列表 / 链接)不入选、照常可复制;② 全站 ua- 类 -webkit-tap-highlight-color: transparent,不引入任何颜色字面量;③ audit:visual / audit:contrast / audit:surface / audit:motion 全绿,npm run check 通过。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g322",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "MCP get_component 支持逗号批量,与 ua info 对齐",
|
||
"motivation": "g319 给 `ua info` 加了逗号批量,但 Agent 查组件的主通道是 MCP(get_component 一次仍只能查一个);拼一个界面要 3–5 次工具往返,MCP 侧同样需要批量,并抽出 CLI / MCP 共用的名字解析函数避免两处口径漂移。",
|
||
"acceptance": "① get_component 的 name 支持逗号分隔多值:json 返回数组、md 返回以水平线分隔的多段片段,单值输出结构不变;② 不存在的 id 报错并标明序号;③ 名字解析抽成 CLI / MCP 共用函数;④ 工具 description 同步更新;⑤ npm run check 通过。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g323",
|
||
"domain": "tooling",
|
||
"status": "done",
|
||
"title": "文档站收录组合配方区(骨架 + 要点展开即看)",
|
||
"motivation": "21 条配方此前只在 CLI / MCP / llms-full.txt 三条 Agent 通道可查,人类版文档站完全没有入口——浏览器里翻配方只能回 recipes.md 源文件;文档站已有组件/令牌/搜索/亮暗主题,配方是最后一块缺口。",
|
||
"acceptance": "① apps/docs/index.html 生成「组合配方」卡:每条配方一个 details 折叠块(标题 + 组件 chips + 代码块 + 要点),导航栏补「配方」锚点;② 搜索态不渲染配方卡(不属于任何包,避免稀释过滤结果);③ 配方数据由 parseRecipes 生成而非手写,recipes.md 改动后 npm run docs 即同步;④ npm run check 通过。",
|
||
"notes": ""
|
||
},
|
||
{
|
||
"id": "g324",
|
||
"domain": "tune",
|
||
"status": "open",
|
||
"title": "table-core 补 setColumns:动态换列后失效排序 / 筛选剔除",
|
||
"motivation": "setRows 已支持整体换源并按 rowKey 剔除失效选择,但列定义仍是构造时写死:动态列表(按角色 / 视图切换列集)只能重建整个实例,排序与筛选状态全丢;setColumns 与 setRows 对称——换列后指向不存在列的 sorting / filters 失去语义,应剔除而不是静默残留。",
|
||
"acceptance": "① core 新增 setColumns(next):引用未变不动;换列后剔除 sorting / filters 里 key 不在新列集的条目,其余状态(分页 / 选择)保持;② getColumns 返回最新列集,getCellValue 按新列 accessor 取值;③ smoke 冒烟补断言(失效剔除 + 未变短路 + accessor 跟随);④ npm run check 通过。",
|
||
"notes": ""
|
||
}
|
||
]
|
||
}
|