14 KiB
设计规范
事实来源
- 状态:生效中
- 最近更新:2026-08-24
- 主要产品界面:
examples/下的可执行应用,以及用于检查任意已挂载应用的通用 ETAF 性能记录器/面板。 - 已审阅证据:ETAF 公开源码与用户指南、core/Data/Resource 测试、
../etaf-playground/DESIGN.md、Research Shelf 集成基准、Ebox 性能评估器与架构分析、TP surface report,以及原始 Ebox Flex 参考示例。
品牌
- 气质:精确、克制、现代、技术化,并且经过明确构图。
- 可信信号:稳定的几何结构、清晰的对比度、明确的状态所有权、只使用公开 API,以及可见的成功/错误/选择状态。
- 避免:裸 fixture 文本、低对比度浅色文字、装饰性 emoji、假浏览器外壳、无意义的大面积留白,以及与正文无法区分的控件。
产品目标
- 目标:通过小型可执行文件提供可复制的 ETAF core 最佳实践;证明已安装 Runtime 的真实交互路径;让视觉质量与 Flex 参考和 ETAF Showcase 保持一致;让每个公共操作的跨包延迟都可归因到具体嵌套阶段。
- 非目标:替代
etaf-playground、假装缺失的etaf-ui目录已经存在、引入第二套主题系统、在一个应用里展示全部公开符号,或把性能分析绑定到某个示例/应用包。 - 成功信号:每个示例只讲清一个所有权边界;加载文件时不产生应用副作用;通过公开事件响应;正确清理资源;在紧凑 GUI 窗口中不裁切。性能面板可记录任意挂载应用,保持操作返回值/错误,展示 inclusive 与 exclusive 阶段耗时,并保留有界历史。
用户与任务
- 主要用户:ETAF 应用作者和框架维护者。
- 用户任务:复制正确的状态/Data/Resource 模式、检查真实挂载应用、验证事件产生的可见结果,并定位哪个框架/包阶段承担了操作延迟。
- 主要环境:源码阅读、GUI Emacs 探索、自动化 ERT 和框架回归审查。
信息架构
- 主导航:不增加共享 launcher;每个文件拥有一个明确的 open 与 close 命令。
- 核心界面:保留式计数器、任务 Data Controller、Resource 健康状态,以及通用性能记录缓冲区。
- 内容层级:示例使用能力 eyebrow、应用标题与说明、主要状态区域、操作区、所有权规则页脚。性能面板先显示 operation 摘要,再显示带包/类别、inclusive、exclusive、状态和细节的嵌套阶段。
设计原则
- 原则一:一个示例只讲一个 lifecycle owner;避免用 mega-demo 隐藏状态与清理的归属。
- 原则二:写操作发生在 Event、Action、lifecycle、Data 或 Resource 边界;render 函数保持只读。
- 原则三:使用暖色、克制的语义色和一像素边框呈现结构,不增加设计系统依赖。
- 原则四:公共 operation 边界只插桩一次,并通过包无关的 stage 注册扩展覆盖面;示例只能作为验证负载,不能成为分析器概念。
- 取舍:独立示例采用固定的紧凑教学画布;完整 viewport 响应式应用继续由
etaf-playground展示。
性能架构:三个根原则
一、Host/Box 属性是视觉更新单位,Component 不是默认重绘单位
Component 是 setup、状态、Context、slot 和 lifecycle 的计算/所有权边界;它可能 产生一个或多个 Host/Box,也可能透明地只产生 Component、Range 或 fragment, 因此不能把 Component 数据结构直接等同为一个 Box。但一次已经定位到具体视觉 节点的属性变化,不应重新执行产生它的 Component。
Runtime 必须保留结构化属性 delta,而不是把所有响应式依赖压缩成 “Component dirty”:
source-id → semantic-host-id → backend-node-id → property → old/new → impact
更新层级按确定性从小到大选择:
paint:直接更新 Host/Box 的属性贡献,不执行 Component、不运行布局;content或局部 geometry:只重算对应 Box 及精确布局依赖闭包;- control flow、slot、列表拓扑或 lifecycle:执行拥有该结构的 Component;
- 来源不可信、外部 buffer 被改写或索引缺失:显式进入 root fallback。
Theme 是第一条垂直实现:Theme token 编译为 property binding;palette 变化只调度
绑定到颜色、背景和边框属性的 Host。只有 Light theme/Dark theme 等真实内容
变化继续调度它自己的 Component/Range。
二、权威依赖状态替代热路径上的重复 proof
初次布局应产出并持有权威状态:父链、分配 slot、containment boundary、 property impact、overflow、viewport/display revision 和布局 revision。之后的更新 根据 property schema 与依赖图直接计算 dirty closure,不从渲染后的 buffer 重新 扫描并猜测局部更新是否安全。
- paint delta 对布局没有依赖,必须是零 proof;
- 固定 slot 的内容变化只比较 O(1) revision/token;
- auto/intrinsic 内容变化沿已保存的布局依赖边更新,到 containment boundary 停止;
- 结构或 viewport 变化只失效实际受影响的证书/依赖边;
- 完整 span 扫描和保守 proof 只保留给不可信 candidate、外部 mutation、debug assertion 与 fallback,不属于正常交互热路径。
当前 retained allocation certificate 是迁移措施:它把重复 proof 改为一次建立、 后续 revision 校验;最终依赖图完整后,应删除被取代的扫描路径,而不是永久叠加 两套判断逻辑。
三、Paint 必须以 TP 有序属性贡献保留到 publication
Theme、Component 样式、交互状态、显式 inline 属性和 focus/selection 是不同的
paint contribution。Ebox 不应在进入 TP 之前把它们压平成每个 fragment 的最终
face,也不应在 palette 切换时重新调和所有 fragment。
这里的“层”指 TP properties surface/ledger 中的有序 property contribution,
不是 tp-layer.el 的 definition-time recipe。职责边界是:
- ETAF 保留
source → Host property contribution绑定和优先级来源; - Ebox 保留 Box/role 到稳定 TP range/object 的映射,只报告 layout/content delta;
- TP 合并同一 surface 内的有序属性贡献,拥有 baseline、冲突检测、原子发布、 回滚和 unmount restoration;
- palette 切换替换 Theme contribution,较高优先级的 Component/state/inline contribution 保持不变。
因此换主题的目标路径是:
Theme source changed
→ resolve changed Theme contributions
→ TP properties-only scoped publication
→ redisplay
它不得创建新 View、重新执行颜色消费者 Component、运行 Ebox layout、扫描祖先 slot,或遍历完整 fragment ledger。只有真实文本/结构变化走独立的 content/layout 事务;二者仍由同一个上层 ETAF operation 关联并原子提交。
预编译 Operation Program 与性能可组合性
预编译产物不能只保存 View 语法树或最终文本坐标。一个可以在运行时直接消费的 Operation Program 必须把下面五项作为同一个不可拆分契约:
- 稳定 Host/Box/Range 拓扑模板和动态 hole;
source/effect → OperationBatch的依赖路由;- 布局 slot、containment、overflow 与 revision 失效边;
- 已闭合的样式计算环境,包括 selector/继承/Theme contribution;
- TP object/ownership range/paint contribution 地址和原子回滚边界。
只保留其中一部分会把工作转移到下一层:只保留坐标仍需重新建立 style 与 TP object;只保留 View blueprint 仍需重新 lower、layout 和 paint;只保留最终 face 又会丢失 TP 分层。因此这种“局部预编译”不能宣称为 App 性能预编译。
运行时只允许提交精确操作批次:
OperationBatch = RangeReplace | HostPropertySet | InlineTextSet |
LayoutInvalidate | PaintContributionSet
每一层只消费属于自己的字段并保留其余地址。一次数据只 materialize 一次;同一
事务的全量遍历最多一次;各模块成本必须是 O(changed) 或有明确小常数上界。
如果增加一个模块后总耗时明显增加,说明接口丢失了上层中间产物,而不是架构
层数本身应有的代价。
当前 etaf-view-blueprint/0 只缓存静态 View 构造,是迁移产物。下一 ABI 只有在
真实 App 的 Range 操作无需重新进行整树 Ebox projection/render,并通过正式
Pagination 预算后,才能称为 Operation Program。
根源实现顺序与验收
- Theme 垂直切片:建立 Theme source→Host/property 索引和 TP contribution; Theme 颜色消费者的 Component render 次数为 0,内容消费者单独更新。
- 通用 property effect:把直接写在 Host 属性位置的响应式表达式编译为稳定 property binding;结构表达式继续归 Component/Range。
- 精确 incremental layout:Ebox 持有 property impact 与 containment 依赖图, 直接重算 dirty closure,移除正常路径的 span/ancestor proof。
- TP paint plane:将预合成 fragment face 迁移为有序 properties contribution, 删除 palette 切换时的全 fragment recomposition。
- 收尾:删除已被替代的兼容缓存和 proof;保守 root 路径继续保证任意 candidate、 rollback 和现有功能语义。
验收必须同时满足:GUI 可见完成计时而非只看 batch;Theme paint 操作没有 layout 阶段和全 fragment 重组;结构、slot、lifecycle、继承、优先级、冲突与 rollback 测试不退化;代表性 warmed 操作达到性能 evaluator 的场景预算。
视觉语言
- 颜色:暖色画布
#F8F5EE、纸张#FFFDF8、正文#252A2E、弱化正文#66706A、陶土色#F1D4C9、鼠尾草绿#DCEBDD、蓝色#D9EAF2与紫色#E7E2F1,始终搭配明确的深色文字。 - 字体:沿用 Emacs 当前等宽字体;粗体只用于标题、操作、状态和关键数值。
- 间距:一行纵向间隔、10–12 px 水平间隔、12–18 px 表面内边距。
- 形状与层级:方正的文本原生区块和一像素边框;不使用阴影或伪圆角。
- 动效:无;同步 commit 后几何结构必须稳定。
- 图像与图标:只使用文本和选择圆点等克制语义标记。
组件
- 复用能力:core Host、保留式 Component、ref、computed、Action、focusable Behavior、Data Controller、Resource 与 Runtime lifecycle callback。
- 新增或调整:示例专用 shell、metric、action、row、status 与 footer Component/Host;一个由通用 operation/stage 记录驱动的
tabulated-list-mode性能面板。 - 状态:ready/active 计数器、selected/unselected 任务、open/done 筛选、idle/loading/success/error Resource;记录器禁用/启用、操作成功/失败、空历史、有界历史,以及在被测热路径之外显式手动刷新。
- 所有权:每个示例拥有自己的小型静态配色和组合;ETAF/Ebox 拥有语义、布局和渲染。
可访问性
- 目标:高对比、可通过键盘寻址的文本 UI。
- 键盘与焦点:每个操作都具有稳定 ref、button role 和
etaf-focusableBehavior。 - 对比度:着色表面始终显式设置前景色;状态同时使用文字和颜色表达。
- 语义:保留文字标签和语义 role,不只用装饰表达含义。
- 动效:不使用动画或闪烁状态。
响应式行为
- 支持范围:正文宽度约 760 px 及以上的 GUI Emacs。
- 布局适配:示例使用 680–720 px 教学画布;操作组使用可换行 Flex;完整 viewport 响应式模式留在
etaf-playground。 - 触控/悬停:无;交互使用 Runtime event 边界。
交互状态
- Loading:明确显示 Resource/Data 状态,不伪造 fallback。
- Empty:有边界的解释行,不填充隐藏的占位数据。
- Error:高对比、持续可见的错误文案,直到下一次操作。
- Success:鼠尾草绿表面和明确状态文字。
- Disabled:移除 event/tab stop,不展示误导性的可用控件。
- 性能记录器禁用:说明启用命令,不伪造示例数据。
- 性能历史为空:显示简洁空状态,并说明哪些公共操作会生成记录。
- 操作失败:保留带 error 状态的 timing 记录,同时原始 condition 不变地继续传播。
- 离线/慢网络:同步 core 示例不适用。
内容语气
- 风格:直接、技术化、简洁、适合教学。
- 术语:统一使用 View、Component、Host、Runtime、Data Controller、Resource、Event、Action、Scope 与 public API。
- 文案规则:点明展示的所有权边界与可见操作效果,避免宣传式填充文字。
实现约束
- 技术边界:Emacs 29.1+、公开
etaffacade,以及通过 ETAF Host 下沉的公开 Ebox 属性。 - Token:直接复用既有暖色配色;不为三个示例增加 token 框架。
- 性能:只使用同步、有界数据;不使用 timer、后台工作或隐藏的重复 mount。
- 性能分析:记录必须按需启用、有界、包无关,并保持返回值/error;关闭后可完全移除。默认阶段采用粗粒度边界,第三方包可注册临时细节探针而不产生反向依赖。嵌套 inclusive/exclusive 统计必须成立。
- 兼容性:core 示例不得依赖
etaf-ui、etaf-sqlite、etaf-playground,也不得调用私有etaf--*/ebox--*API。 - 验证:
make check会编译示例并驱动已挂载的公开事件路径;GUI 验证只使用一个目标缓冲区窗口,并拒绝裁切或续行标记。
待解决问题
- 只有 ETAF 定义公开异步完成契约后才增加异步 Resource 示例;维护者;避免教授臆造 API。
- GUI redisplay 完成不属于 batch publication timing;在把它展示成同步框架阶段前,需要定义可移植的 Emacs redisplay marker;维护者;避免错误的端到端结论。