From 2973dd3021249b39fa29bf487b5225fd21147b0a Mon Sep 17 00:00:00 2001 From: Kinneyzhang Date: Wed, 26 Aug 2026 18:21:43 +0800 Subject: [PATCH] docs: define layered property schemas and projections --- docs/proposals/module-boundaries.en.md | 388 ++++++++++++++++++++++++- docs/proposals/module-boundaries.zh.md | 350 +++++++++++++++++++++- 2 files changed, 724 insertions(+), 14 deletions(-) diff --git a/docs/proposals/module-boundaries.en.md b/docs/proposals/module-boundaries.en.md index f959fb9..252ef84 100644 --- a/docs/proposals/module-boundaries.en.md +++ b/docs/proposals/module-boundaries.en.md @@ -10,10 +10,56 @@ completion criteria. Implementation order belongs in a separate delivery plan; historical names and compatibility policy do not shape the target architecture. User mental cost is a hard constraint of this proposal: one capability has one canonical -name and one recommended spelling. Internal algorithms, backend nodes, wrappers, and -compatibility aliases must not become peer public concepts. Ordinary applications need -to learn only Text, Box, and Component; Fragment, layout details, and lower-level -packages enter through progressive disclosure. +identity, canonical name, and recommended spelling. A small set of aliases may exist +only as parse-time input sugar and must not become peer public concepts. Internal +algorithms, backend nodes, wrappers, and compatibility aliases must not become peer +public concepts. Ordinary applications need to learn only Text, Box, and Component; +Fragment, layout details, and lower-level packages enter through progressive disclosure. + +## 0. Design Decision Summary + +Ordinary ETAF users learn only Text, Box, Component, and these structural forms: + +```elisp +"plain text" +(text PROPS "styled text") +(box PROPS CHILD...) +(row PROPS CHILD...) +(column PROPS CHILD...) +(flex PROPS CHILD...) +(grid PROPS CHILD...) +``` + +`row/column/flex/grid` are only layout forms of Box. User properties fall into metadata, +semantics/events, visual style, and parent participation. They use semantically aligned +standard CSS names, a small set of explicit ETAF/Ebox-specific properties, and a small +set of parse-time aliases. Users do not interact with the Ebox, TP, or Emacs backend. + +Standalone Ebox users access the same visual forms as inert data syntax through +`ebox-build`, but have no Component, Fragment, expr, slot, or lifecycle. + +Framework integrators use only typed Text/Box constructors, LayoutConfig, and the render +and commit boundaries. They do not reconstruct DSL, rerun cascade, or generate Emacs +text properties. + +Internal execution has only four layers: + +```text +Author API + → Canonical Model + → Render Plan (Ebox geometry + TP paint + ETAF semantics) + → Emacs Adapter commit +``` + +Core ownership remains unchanged: ETAF Runtime owns identity/lifecycle/reactivity; ECSS +owns selectors/cascade/computed style; Ebox owns measurement/geometry; TP owns paint +priority/transactions; only the final adapter owns Emacs text properties such as +`face`, `display`, and `keymap`. + +Ordinary user documentation does not mention NormalizedView, PropertyDefinition, +ProjectionPlan, node IDs, RangeAnchor, or TP slots. The framework integration reference +describes typed constructors; only internal architecture sections describe facts, plans, +patches, and journals. ## 1. Final Model @@ -393,9 +439,10 @@ Rules: - Flex/Grid participation is stored on the child Box, but a change dirties only the corresponding parent layout owner for geometry; - ECSS cascade output must retain the impact set above; -- the ETAF/Ebox candidate normalization pipeline parses each property fact once, then - passes it by owner/impact to Runtime metadata, ECSS, Ebox geometry, and the appropriate - TP adapter; owners do not reparse the original plist; +- ETAF/Ebox candidate normalization parses the author plist once and produces canonical + declarations/metadata facts; ECSS computes the computed facts once; projection then + passes them by owner/impact to Runtime, Ebox, and TP, and no owner reparses the original + plist; - a multi-impact fact such as border or font may project to both geometry and paint fields, but its computed value materializes only once and paint-only state is not duplicated in the Ebox node; @@ -423,6 +470,294 @@ failure preserves the previous generation. Unlike browsers, which commonly treat inapplicable CSS property as having no effect, this framework does not silently ignore invalid combinations. +### 3.1 Canonical Property Composition of Author Forms + +The following compositions are the normative public schemas. Shorthands expand +immediately upon entering the canonical registry and do not create a second set of owner +or impact rules. + +```text +SharedSourceMetadata = :key :class :id +ETAFOnlyMetadata = :ref :role :aria-* :on-* :use +ETAFSourceMetadata = SharedSourceMetadata + ETAFOnlyMetadata +EboxSourceMetadata = SharedSourceMetadata + +TextStyle = + :font-family :font-size :font-weight :font-style + :color :background-color + :text-decoration-line :text-decoration-color :text-decoration-style + :wrap-mode (word | grapheme | none) + +BoxFrame = + :outer + :width/:min-width/:max-width + :height/:min-height/:max-height + :box-sizing + margin/padding shorthands and logical/physical longhands + border shorthands and width/style/color longhands + :overflow :visibility + inherited standard text style source such as font/color + non-inherited surface source such as :background-color + +NormalConfig = :text-align + +RowConfig = :item-gap :cross-align +ColumnConfig = :item-gap :cross-align + +FlexConfig = + :flex-direction :flex-wrap :flex-flow + :justify-content :align-items :align-content + :gap :row-gap :column-gap + +GridConfig = + :grid-template-columns :grid-template-rows + :grid-auto-columns :grid-auto-rows :grid-auto-flow + :justify-items :align-items :justify-content :align-content + :gap :row-gap :column-gap + +FlexParticipation = + :order :flex :flex-grow :flex-shrink :flex-basis :align-self + +GridParticipation = + :order :grid-column :grid-row :align-self :justify-self +``` + +The six visual forms are produced only through composition: + +```text +text = SurfaceSourceMetadata + TextStyle + one String +box = SurfaceSourceMetadata + BoxFrame + NormalConfig + ParentParticipation + children +row = SurfaceSourceMetadata + BoxFrame + RowConfig + ParentParticipation + children +column = SurfaceSourceMetadata + BoxFrame + ColumnConfig + ParentParticipation + children +flex = SurfaceSourceMetadata + BoxFrame + FlexConfig + ParentParticipation + children +grid = SurfaceSourceMetadata + BoxFrame + GridConfig + ParentParticipation + children +``` + +`SurfaceSourceMetadata` resolves to ETAFSourceMetadata on an ETAF surface and to +EboxSourceMetadata on a standalone Ebox surface. `:item-gap` and `:cross-align` are Ebox +Row/Column extensions, not CSS `gap`/`align-items`: the former inserts fixed spacing +along the main axis; the latter accepts `start|center|end|stretch` and controls the cross +axis. Flex/Grid `:gap` retains standard CSS shorthand semantics and expands to both +row-gap and column-gap. + +The final direct parent selects `ParentParticipation` as Default, +FlexParticipation, or GridParticipation. A candidate cannot carry mutually exclusive +Flex-only and Grid-only fields simultaneously. Text accepts neither BoxFrame, +LayoutConfig, nor Participation; wrap it in an explicit Box when those capabilities are +required. The Ebox DSL does not accept ETAFOnlyMetadata. + +### 3.2 Naming Layers and Backend Projection + +Names in the author layer belong to four namespaces: CSS, ETAF, Ebox, and the optional +Emacs adapter. A standard CSS name is adopted only when its applicable objects, value +grammar, defaults/inheritance, impact, and observable behavior match CSS or form an +explicit subset. A capability that is conceptually similar but semantically different +retains a precise ETAF/Ebox name; familiarity does not justify an incorrect standard +name. + +The target public API classifies historical and backend names as follows: + +```text +:bgcolor → exact alias of :background-color +:font-height → removed from core; optional Emacs adapter source only +:font-slant → exact alias of :font-style with identical value grammar +:border-top-p → retained Ebox boolean shorthand for top width/style +:grid-column-span → Ebox positive-integer shorthand for :grid-column (span N) +:grid-row-span → Ebox positive-integer shorthand for :grid-row (span N) +:face → not a core canonical property +:display → private Ebox/Emacs lowering field +:content → private legacy runtime storage field +``` + +`:wrap-mode` is not equivalent to CSS `white-space`, `overflow-wrap`, `word-break`, or +`line-break`. It therefore remains the sole public name in the Ebox namespace, with its +value restricted to `word`, `grapheme`, or `none`. If complete, semantically equivalent +CSS properties are implemented in the future, one explicit migration must replace it; +the two naming systems must not be retained indefinitely. + +Standard CSS shorthands have exactly one expansion rule, including `:margin`, +`:padding`, `:border`, `:gap`, `:flex`, `:flex-flow`, `:grid-column`, and `:grid-row`. +The canonical registry uses namespaced IDs such as `ebox/font-size` and +`ebox/background-color`; authors write only keywords and never see internal IDs. + +Every definition records its naming domain and relationship to standards explicitly: + +```text +PropertyDefinition { + authorName + canonicalId + aliases + namespace: css | etaf | ebox | emacs-adapter + standardReference + semanticSubset + acceptedContexts + valueGrammar + initial + inherits + impactSet + owner + projections +} +``` + +An alias must have exactly the same contexts, value grammar, owner, impact, and +projections as its canonical property, and it cannot transform the value. A shorthand +performs one-to-many expansion; an adapter source resolves a composite backend value. +An alias resolves to canonicalId immediately at the parse entry point, so computed +style, caches, diff, Ebox, TP, and Rust never see it. PropertyDefinition registration +rejects canonical/alias collisions. Writing both the canonical name and an alias—or +repeating an alias—in the same declaration is an error rather than an implicit +last-wins rule. The source spelling may be retained only in diagnostic provenance. + +Property references list aliases under the canonical property, for example: + +```text +background-color + Canonical: :background-color + Aliases: :bgcolor +``` + +They do not list `bgcolor` as a second capability. In the first phase, aliases are +registered by the owning package's schema; process-global user aliases are not exposed. +An upper-level package that needs an alias must scope it to its own schema and must not +pollute the global Ebox visual-property domain. + +`:border-top-p` is not an alias but a retained Ebox boolean shorthand: `t` expands to +`:border-top-width 1` + `:border-top-style solid`, while `nil` expands to width 0 + style +none. Color comes independently from `:border-top-color`, `:border-color`, or the default +foreground. Use standard `:border-top` or its longhands when full control is required. +Mixing `:border-top-p` with its width/style outputs in the same declaration is an error; +different cascade sources still override one another normally through ECSS. + +`:grid-column-span` and `:grid-row-span` are likewise not aliases. They accept only a +positive integer and respectively expand to the corresponding Grid shorthand's `span N` +value; mixing either with `:grid-column` or `:grid-row` in the same declaration is an +error. `:font-slant` is an exact alias only when its value grammar is identical to +`:font-style`. The Emacs face-height semantics of `:font-height` must not masquerade as +CSS `font-size`. + +The Emacs text property names `face`, `display`, `keymap`, `mouse-face`, `help-echo`, +`invisible`, `line-prefix`, `wrap-prefix`, and sticky properties belong only to the final +adapter. Ebox core, ETAF Runtime, ECSS, and Rust neither produce nor consume these names +directly. Reuse of a named Emacs face, when needed, is available only through an +optional adapter's explicit `:emacs-face` source input. That input immediately resolves +to standard font/paint facts and computes their impact; the raw face symbol does not +enter the canonical IR. + +A typical projection is: + +```text +:font-weight bold + → ebox/font-weight, impact {geometry, paint} + → Ebox Text measurement weight + + TP face-weight contribution + → Emacs adapter emits face :weight + +:width 120 + → ebox/width, impact {geometry} + → Ebox GeometryPatch + → Emacs adapter may emit display space width +``` + +### 3.3 Unified Property Facts and Projection Layer + +The unified layer is not a direct `author keyword → Emacs text property` dictionary. +One property may affect both measurement and paint; several properties may merge into +one Emacs `face`; and a `display` produced by layout may have no direct author property. +The internal stages must therefore distinguish: + +```text +CanonicalDeclaration { + propertyId + specifiedValue + sourceSpelling + sourceProvenance +} + +SelectorSubjectFact { + classList + id + semanticSourceHandle +} + +RuntimeMetadataFact { + propertyId + value + sourceHandle +} + +ComputedPropertyFact { + propertyId + computedValue + winningProvenance + impactSet + sourceHandle +} + +ProjectionPlan { + metadataFacts + measurementFacts + geometryFacts + paintFacts +} +``` + +Normalization parses author input once and simultaneously produces immutable +CanonicalDeclaration, SelectorSubjectFact, and RuntimeMetadataFact artifacts. +`:class/:id` do not enter CSS declarations; SelectorSubjectFact constructs the ECSS subject +directly before cascade. Selector and semantic projection share the same parsed source +value for `:id` rather than rereading the plist independently. ECSS cascade/inheritance +receives SelectorSubjectFact + declarations and produces immutable +ComputedPropertyFact artifacts. `:key`, `:ref`, `:on-*`, and similar properties do not +enter ECSS and appear only in RuntimeMetadataFact. “Materialize once” means that each +stage produces its own immutable artifact once, not that one object mutates across +stages. + +A pure property projector consumes computed-style facts and metadata facts and generates +ProjectionPlan according to the PropertyDefinition owner/impact/projections fields. +Every consumer references the same fact rather than reparsing or copying its computed +value. The TP paint projector is the only module that converts paintFacts into +ContributionPlan. The ETAF semantic projector converts metadataFacts into +Runtime/SemanticPlan. + +A registry is not one process-global universal table. Each owning package supplies an +immutable SchemaRegistry conforming to the same schema protocol; a surface composes a +SurfaceSchemaSet: + +```text +ETAF SurfaceSchemaSet = ETAF registry + Ebox registry +Ebox SurfaceSchemaSet = Ebox registry +ETAF + Emacs adapter = ETAF registry + Ebox registry + adapter registry +``` + +Each canonicalId has exactly one owning definition. Canonical/alias collisions are +checked when composing the SchemaSet. This avoids both reverse dependencies and +duplicate sources of truth. + +Logical modules remain small and complete: + +- package property schema: a package-scoped PropertyDefinition registry; +- SurfaceSchemaSet: conflict-free composition of package registries; +- property normalizer: author names, shorthands, and value grammar; +- property projector: computed/metadata facts → backend-neutral ProjectionPlan; +- Ebox layout projector: measurement/geometry facts → LayoutPlan; +- TP paint projector: paint facts → ContributionPlan; +- ETAF semantic projector: metadata facts → Runtime/SemanticPlan; +- Emacs adapter: GeometryPatch + PaintPatch + SemanticPlan → text-property operations. + +Consequently, an Ebox canonical node stores no paint-only field; TP stores no geometry; +the Emacs adapter does not reinterpret CSS; and Rust receives only canonical facts/plans, +not `face`, `display`, or an Elisp plist. Debugging and performance tools must expose the +complete chain, for example: + +```text +:font-weight +→ ebox/font-weight +→ {geometry, paint} +→ measurement/font-weight + paint/font-weight +→ face :weight +``` + ## 4. Module Owners ### 4.1 ETAF Core @@ -563,6 +898,27 @@ The Playground `.etaf` manifest belongs to the Playground. If a general compiler in the future, it must lower to the same View/Component contract and must not create a second Runtime. +### 4.6 Public Surfaces Layered by Consumer + +- Ordinary ETAF users use only `etaf-view`, Component, Text/Box forms, ETAF metadata/ + events, canonical CSS-aligned properties, explicitly listed ETAF/Ebox extensions such + as `:outer` and `:wrap-mode`, and parse-time aliases; they do not call Ebox, TP, or the + Emacs adapter. +- Standalone Ebox users use inert data forms with the same visual structural vocabulary + through `ebox-build`; they do not own Component, slot, expr, or lifecycle. +- Framework integrators use `ebox-text-create`, `ebox-box-create`, typed LayoutConfig, + RangeAnchor, and the TP contribution contract; they do not reconstruct DSL, pass + shorthands, or run a second cascade. +- The optional Emacs adapter is an explicit opt-in, independent schema namespace. Only + a surface that loads the adapter accepts `:emacs-face`; otherwise normalization + reports the unknown namespace/property precisely. +- `ebox--*`, `etaf--*`, `tp--*`, `:ebox-type`, `:node-id`, `:region-id`, Emacs text + properties, and runtime storage fields are all private. + +The user guide describes only the first layer; the Ebox DSL guide describes the second; +the integration reference describes the third; only architecture documentation +describes canonical IR, identity owners, impact projection, and the commit protocol. + ## 5. Dependency Direction ```text @@ -624,6 +980,21 @@ scans already completed by Rust. The final gates must cover: - structural equivalence of Text/Box/Fragment; +- rejection of canonical/alias collisions when package registries compose a + SurfaceSchemaSet; +- exact aliases disappear after normalization, while each shorthand expands exactly + once and conflicts with its owned outputs in the same declaration; +- the optional adapter namespace is accepted only by explicitly opted-in surfaces and + reports a precise error when the adapter is not loaded; +- CanonicalDeclaration, SelectorSubjectFact, RuntimeMetadataFact, and + ComputedPropertyFact remain immutable across stages, and the selector subject is + produced exactly once before cascade; +- ProjectionPlan contains only paintFacts, and only the TP projector may produce + ContributionPlan; +- Row/Column accept only the `:item-gap/:cross-align` extensions rather than borrowing + the different semantics of CSS `:gap/:align-items`; +- a multi-impact computed value such as geometry + paint materializes once and is + referenced by both paths; - strings normalize only to Text, and both author DSLs reject `box :content`; - Text is always inline and has exactly one string payload; nested, empty, or multiple payloads are errors; @@ -677,6 +1048,7 @@ Non-goals: - making Flex/Grid item an author node, visual wrapper, or independent identity; - allowing applications to inject raw Ebox Nodes into View; - making layout modes Components or a second set of Runtime nodes; -- retaining old Host aliases or optional shorthand spellings in Core; +- retaining old Host/layout-form aliases in Core; property aliases follow only the + unified mechanism in Section 3.2; - allowing Playground, UI, or database packages to own Core protocols; - adding App precompilation artifacts before real compile cost and runtime benefit exist. diff --git a/docs/proposals/module-boundaries.zh.md b/docs/proposals/module-boundaries.zh.md index 5a6c6f8..5a3888a 100644 --- a/docs/proposals/module-boundaries.zh.md +++ b/docs/proposals/module-boundaries.zh.md @@ -7,10 +7,53 @@ 本文只定义目标模型、模块 owner、依赖方向和完成条件。实现顺序属于单独的 实施计划;历史名称和兼容策略不参与目标架构设计。 -用户心智成本是本提案的硬约束:同一能力只有一个规范名称和一种推荐写法;内部 -算法、后端节点、wrapper 和兼容别名不能作为并列公共概念。普通应用只需学习 +用户心智成本是本提案的硬约束:同一能力只有一个 canonical identity、规范名称和 +推荐写法;少量 alias 只能是 parse-time 输入糖,不能成为并列公共概念。内部算法、 +后端节点、wrapper 和兼容别名不能作为并列公共概念。普通应用只需学习 Text、Box、Component;Fragment、布局细节和底层包通过渐进披露进入。 +## 0. 设计决策摘要 + +普通 ETAF 用户只学习 Text、Box、Component,以及下面的结构 form: + +```elisp +"plain text" +(text PROPS "styled text") +(box PROPS CHILD...) +(row PROPS CHILD...) +(column PROPS CHILD...) +(flex PROPS CHILD...) +(grid PROPS CHILD...) +``` + +`row/column/flex/grid` 都只是 Box 的布局形式。用户属性分为 metadata、语义/事件、 +视觉样式和 parent participation;采用语义一致的标准 CSS 名称、少量明确的 +ETAF/Ebox 个性属性和少量 parse-time alias。用户不接触 Ebox/TP/Emacs backend。 + +独立 Ebox 用户通过 `ebox-build` 使用相同视觉 form 的 inert data syntax,但没有 +Component、Fragment、expr、slot 或 lifecycle。 + +框架集成者只使用 typed Text/Box constructors、LayoutConfig、render 和 commit +边界;不重新拼 DSL,不重新执行 cascade,不生成 Emacs text properties。 + +内部执行只有四层: + +```text +Author API + → Canonical Model + → Render Plan (Ebox geometry + TP paint + ETAF semantics) + → Emacs Adapter commit +``` + +核心 owner 不变:ETAF Runtime 拥有 identity/lifecycle/reactivity;ECSS 拥有 +selector/cascade/computed style;Ebox 拥有 measurement/geometry;TP 拥有 paint +priority/transaction;最终 adapter 才拥有 `face`、`display`、`keymap` 等 Emacs +text properties。 + +普通用户文档不出现 NormalizedView、PropertyDefinition、ProjectionPlan、node ID、 +RangeAnchor 或 TP slot。框架 integration reference 才描述 typed constructors; +只有内部架构章节描述 facts、plans、patches 和 journals。 + ## 1. 最终模型 ETAF/Ebox 的表示链按职责分层: @@ -358,9 +401,9 @@ ETAF 也不提供 `spacer` 类型。没有子节点的 Box 就是空 Box。 - `:outer`、Layout variant 属于 structure + geometry; - Flex/Grid participation 存储在 child Box,但变化时只使对应父布局 owner geometry dirty; - ECSS cascade 输出必须保留上述 impact set; -- ETAF/Ebox candidate normalization pipeline 只解析一次 property fact,再按 owner/impact - 交给 Runtime metadata、ECSS、Ebox geometry 和对应 TP adapter;各 owner 不重新解析 - 原 plist; +- ETAF/Ebox candidate normalization 只解析一次 author plist 并产生 canonical + declarations/metadata facts;ECSS 只计算一次 computed facts;projection 再按 + owner/impact 交给 Runtime、Ebox 和 TP,各 owner 不重新解析原 plist; - border/font 等 multi-impact fact 可以同时投影 geometry 与 paint 字段,但 computed value 只 materialize 一次,不在 Ebox node 中复制 paint-only 状态; - TP 只消费已经解析的 paint contribution,不判断布局合法性; @@ -381,6 +424,274 @@ ETAF 也不提供 `spacer` 类型。没有子节点的 Box 就是空 Box。 Layout 和 child participation 时只验证最终 candidate,任何失败保留上一代。与 浏览器对不适用 CSS 属性常采取 no-effect 不同,本框架不静默忽略无效组合。 +### 3.1 作者 form 的规范属性组合 + +下面的组合是规范性公共 schema;shorthand 在进入 canonical registry 时立即展开, +不产生第二套 owner 或 impact 规则。 + +```text +SharedSourceMetadata = :key :class :id +ETAFOnlyMetadata = :ref :role :aria-* :on-* :use +ETAFSourceMetadata = SharedSourceMetadata + ETAFOnlyMetadata +EboxSourceMetadata = SharedSourceMetadata + +TextStyle = + :font-family :font-size :font-weight :font-style + :color :background-color + :text-decoration-line :text-decoration-color :text-decoration-style + :wrap-mode (word | grapheme | none) + +BoxFrame = + :outer + :width/:min-width/:max-width + :height/:min-height/:max-height + :box-sizing + margin/padding shorthands and logical/physical longhands + border shorthands and width/style/color longhands + :overflow :visibility + inherited standard text style source such as font/color + non-inherited surface source such as :background-color + +NormalConfig = :text-align + +RowConfig = :item-gap :cross-align +ColumnConfig = :item-gap :cross-align + +FlexConfig = + :flex-direction :flex-wrap :flex-flow + :justify-content :align-items :align-content + :gap :row-gap :column-gap + +GridConfig = + :grid-template-columns :grid-template-rows + :grid-auto-columns :grid-auto-rows :grid-auto-flow + :justify-items :align-items :justify-content :align-content + :gap :row-gap :column-gap + +FlexParticipation = + :order :flex :flex-grow :flex-shrink :flex-basis :align-self + +GridParticipation = + :order :grid-column :grid-row :align-self :justify-self +``` + +六个视觉 form 只通过组合产生: + +```text +text = SurfaceSourceMetadata + TextStyle + one String +box = SurfaceSourceMetadata + BoxFrame + NormalConfig + ParentParticipation + children +row = SurfaceSourceMetadata + BoxFrame + RowConfig + ParentParticipation + children +column = SurfaceSourceMetadata + BoxFrame + ColumnConfig + ParentParticipation + children +flex = SurfaceSourceMetadata + BoxFrame + FlexConfig + ParentParticipation + children +grid = SurfaceSourceMetadata + BoxFrame + GridConfig + ParentParticipation + children +``` + +`SurfaceSourceMetadata` 在 ETAF surface 取 ETAFSourceMetadata,在独立 Ebox surface +取 EboxSourceMetadata。`:item-gap` 与 `:cross-align` 是 Ebox Row/Column extension, +不是 CSS `gap`/`align-items`;前者沿主轴插入固定间距,后者值为 +`start|center|end|stretch` 并控制 cross axis。Flex/Grid 的 `:gap` 保持标准 CSS +shorthand 语义,同时展开 row-gap 和 column-gap。 + +`ParentParticipation` 由最终直接父级选择为 Default、FlexParticipation 或 +GridParticipation;同一个 candidate 不能同时携带互斥的 Flex-only 与 Grid-only +字段。Text 不接受 BoxFrame、LayoutConfig 或 Participation;需要这些能力时显式包 +Box。Ebox DSL 不接受 ETAFOnlyMetadata。 + +### 3.2 命名层与后端投影 + +作者层名称分为 CSS、ETAF、Ebox 和 optional Emacs-adapter 四个 namespace。只有 +适用对象、值 grammar、默认/继承、impact 和可观察行为与 CSS 一致或是明确子集时, +才采用标准 CSS 名称;概念相近但语义不同的能力保留精确的 ETAF/Ebox 名称,不能 +为了熟悉感使用错误的标准名称。 + +目标公共 API 对历史/后端名称分类处理: + +```text +:bgcolor → exact alias of :background-color +:font-height → removed from core; optional Emacs adapter source only +:font-slant → exact alias of :font-style with identical value grammar +:border-top-p → retained Ebox boolean shorthand for top width/style +:grid-column-span → Ebox positive-integer shorthand for :grid-column (span N) +:grid-row-span → Ebox positive-integer shorthand for :grid-row (span N) +:face → not a core canonical property +:display → private Ebox/Emacs lowering field +:content → private legacy runtime storage field +``` + +`:wrap-mode` 不与 CSS `white-space`、`overflow-wrap`、`word-break` 或 `line-break` +等价,因此保留为 Ebox namespace 的唯一公共名称,值限定为 `word`、`grapheme`、 +`none`。如果未来实现完整且语义一致的 CSS 属性,应以一次明确迁移替换它,而不是 +长期同时保留两套名称。 + +标准 CSS shorthand 只有一套展开规则,例如 `:margin`、`:padding`、`:border`、 +`:gap`、`:flex`、`:flex-flow`、`:grid-column` 和 `:grid-row`。canonical registry 使用 +namespaced ID(例如 `ebox/font-size`、`ebox/background-color`);作者只写 keyword, +不会看到内部 ID。 + +每个定义显式记录命名领域与标准关系: + +```text +PropertyDefinition { + authorName + canonicalId + aliases + namespace: css | etaf | ebox | emacs-adapter + standardReference + semanticSubset + acceptedContexts + valueGrammar + initial + inherits + impactSet + owner + projections +} +``` + +alias 必须与 canonical property 的 context、value grammar、owner、impact 和 projection +完全相同,且不能转换 value;需要一对多展开的是 shorthand,需要解析后端复合值的 +是 adapter source。alias 在 parse 入口立即解析为 canonicalId,computed style、 +cache、diff、Ebox、TP 和 Rust 都不再看到它。PropertyDefinition 注册时拒绝 +canonical/alias collision;同一声明同时写 canonical 与 alias(或重复 alias)直接 +报错,不采用隐式 last-wins。source spelling 只可保存在 diagnostics provenance。 + +属性参考把 alias 列在 canonical property 下,例如: + +```text +background-color + Canonical: :background-color + Aliases: :bgcolor +``` + +不把 `bgcolor` 单独列成第二个功能。第一阶段 alias 由 owning package 的 schema +注册,不开放 process-global 用户 alias;上层包若需要 alias,必须限制在自己的 +schema scope,不能污染全局 Ebox visual property domain。 + +`:border-top-p` 不是 alias,而是保留的 Ebox boolean shorthand:`t` 展开为 +`:border-top-width 1` + `:border-top-style solid`,`nil` 展开为 width 0 + style none; +颜色由 `:border-top-color`/`:border-color`/默认前景提供。需要完整控制时使用标准 +`:border-top` 或 longhands。同一声明混写 `:border-top-p` 与其 width/style 输出必须 +报错;不同 cascade source 仍按 ECSS 正常覆盖。 + +`:grid-column-span`/`:grid-row-span` 同样不是 alias;它们只接受正整数并分别展开 +为对应 Grid shorthand 的 `span N` value,与 `:grid-column`/`:grid-row` 在同一声明 +混写时报错。`:font-slant` 只有在值 grammar 与 `:font-style` 完全相同时才作为 exact +alias;`:font-height` 的 Emacs face-height 语义不冒充 CSS `font-size`。 + +Emacs text property 名称 `face`、`display`、`keymap`、`mouse-face`、`help-echo`、 +`invisible`、`line-prefix`、`wrap-prefix` 和 sticky properties 只属于最终 adapter。 +Ebox core、ETAF Runtime、ECSS 和 Rust 都不直接生成或消费这些名字。若需要复用 +named Emacs face,只能由可选 adapter 提供显式 `:emacs-face` source input,立即 +解析为标准 font/paint facts 并计算 impact;原始 face symbol 不进入 canonical IR。 + +一次典型投影是: + +```text +:font-weight bold + → ebox/font-weight, impact {geometry, paint} + → Ebox Text measurement weight + + TP face-weight contribution + → Emacs adapter emits face :weight + +:width 120 + → ebox/width, impact {geometry} + → Ebox GeometryPatch + → Emacs adapter may emit display space width +``` + +### 3.3 统一属性事实与投影层 + +统一层不是 `author keyword → Emacs text property` 的直接字典。一项属性可能同时 +影响测量与 paint,多项属性也可能合并为一个 Emacs `face`;布局结果产生的 +`display` 又可能没有直接作者属性。内部阶段必须区分: + +```text +CanonicalDeclaration { + propertyId + specifiedValue + sourceSpelling + sourceProvenance +} + +SelectorSubjectFact { + classes + id + sourceHandle + sourceProvenance +} + +RuntimeMetadataFact { + propertyId + value + sourceHandle +} + +ComputedPropertyFact { + propertyId + computedValue + winningProvenance + impactSet + sourceHandle +} + +ProjectionPlan { + metadataFacts + measurementFacts + geometryFacts + paintFacts +} +``` + +normalization 对 author input 只解析一次,并同时产生 immutable +CanonicalDeclaration、SelectorSubjectFact 和 RuntimeMetadataFact。`:class/:id` 不 +进入 CSS declarations;SelectorSubjectFact 在 cascade 前直接构造 ECSS subject。 +`:id` 的同一个 parsed source value 由 selector 与 semantic projection 共享,不各自 +重读 plist。ECSS cascade/inheritance 接收 SelectorSubjectFact + declarations,产生 +immutable ComputedPropertyFact。`:key`、`:ref`、`:on-*` 等不进入 ECSS,只进入 +RuntimeMetadataFact。这里的“一次 materialize”指每个阶段只生成一次自己的 +immutable artifact,不是让同一对象跨阶段变异。 + +纯 property projector 在 cascade 后消费 computed style facts 与 metadata facts,按 +PropertyDefinition 的 owner/impact/projections 生成 ProjectionPlan,各 consumer +引用同一 fact,不重新解析或复制 computed value。TP paint projector 是唯一把 +paintFacts 转为 ContributionPlan 的模块;ETAF semantic projector 把 metadataFacts +转为 Runtime/SemanticPlan。 + +Registry 不是 process-global 万能表。每个 owning package 提供 immutable +SchemaRegistry,并遵守同一个 schema protocol;一个 surface 组合成 SurfaceSchemaSet: + +```text +ETAF SurfaceSchemaSet = ETAF registry + Ebox registry +Ebox SurfaceSchemaSet = Ebox registry +ETAF + Emacs adapter = ETAF registry + Ebox registry + adapter registry +``` + +每个 canonicalId 只有一个 owning definition;canonical/alias collision 在 +SchemaSet composition 时检查。这样既没有反向依赖,也没有重复事实源。 + +逻辑模块保持小而完整: + +- package property schema:package-scoped PropertyDefinition registry; +- SurfaceSchemaSet:无冲突组合各 package registry; +- property normalizer:author name、shorthand、value grammar; +- property projector:computed/metadata facts → backend-neutral ProjectionPlan; +- Ebox layout projector:measurement/geometry facts → LayoutPlan; +- TP paint projector:paint facts → ContributionPlan; +- ETAF semantic projector:metadata facts → Runtime/SemanticPlan; +- Emacs adapter:GeometryPatch + PaintPatch + SemanticPlan → text-property operations。 + +因此 Ebox canonical node 不保存 paint-only 字段,TP 不保存 geometry,Emacs adapter +不重新解释 CSS,Rust 只接收 canonical facts/plans 而不接收 `face`、`display` 或 +Elisp plist。调试与性能工具必须能显示完整链路,例如: + +```text +:font-weight +→ ebox/font-weight +→ {geometry, paint} +→ measurement/font-weight + paint/font-weight +→ face :weight +``` + ## 4. 模块 owner ### 4.1 ETAF Core @@ -508,6 +819,24 @@ Ebox surface 与 TP/Emacs 提交对齐的唯一机制。 Playground 的 `.etaf` manifest 属于 Playground。未来通用 compiler 若存在,必须 降低到同一套 View/Component 契约,不得创建第二套 Runtime。 +### 4.6 按使用者分层的公共面 + +- 普通 ETAF 用户只使用 `etaf-view`、Component、Text/Box form、ETAF metadata/ + events、canonical CSS-aligned properties,以及明确列出的 ETAF/Ebox extensions + (如 `:outer`、`:wrap-mode`)和 parse-time aliases;不调用 Ebox/TP/Emacs adapter。 +- 独立 Ebox 用户通过 `ebox-build` 使用同一视觉结构词汇的 inert data form;不拥有 + Component、slot、expr 或 lifecycle。 +- 框架集成者使用 `ebox-text-create`、`ebox-box-create`、typed LayoutConfig、 + RangeAnchor 和 TP contribution contract;不重新拼 DSL、不传 shorthand、不运行 + 第二次 cascade。 +- optional Emacs adapter 是显式 opt-in 的独立 schema namespace;只有加载 adapter + 的 surface 才接受 `:emacs-face`,否则 normalization 精确报未知 namespace/property。 +- `ebox--*`、`etaf--*`、`tp--*`、`:ebox-type`、`:node-id`、`:region-id`、Emacs + text properties 和 runtime storage fields 全部私有。 + +用户指南只描述第一层;Ebox DSL 指南描述第二层;integration reference 描述第三层; +架构文档才描述 canonical IR、identity owner、impact projection 和 commit protocol。 + ## 5. 依赖方向 ```text @@ -563,6 +892,15 @@ cascade、布局或文字扫描。 最终门禁必须覆盖: - Text/Box/Fragment 结构等价; +- package registries 组合 SurfaceSchemaSet 时拒绝 canonical/alias collision; +- exact alias 在 normalization 后消失,shorthand 唯一展开且与 owned outputs 混写报错; +- optional adapter namespace 只有显式 opt-in surface 接受,未加载时精确报错; +- CanonicalDeclaration、SelectorSubjectFact、RuntimeMetadataFact 和 + ComputedPropertyFact 分阶段 immutable,selector subject 在 cascade 前只生成一次; +- ProjectionPlan 只含 paintFacts,只有 TP projector 能产生 ContributionPlan; +- Row/Column 只接受 `:item-gap/:cross-align` extension,不借用 CSS + `:gap/:align-items` 的不同语义; +- geometry+paint 等 multi-impact computed value 只 materialize 一次并被两路引用; - 字符串只规范化为 Text,两个作者 DSL 都拒绝 `box :content`; - Text 固定 inline 且恰好一个字符串 payload;嵌套/空/多 payload 均报错; - ETAF/Ebox 五个 Box form 的 canonical shape、defaults 和错误跨接口一致; @@ -603,6 +941,6 @@ typed Layout 尚未实现,目标公共 View 也不保留 `raw-ebox`。 - 不把 Flex/Grid item 做成作者节点、视觉 wrapper 或独立 identity; - 不允许应用把原始 Ebox Node 注入 View; - 不把布局模式做成 Component 或第二套 Runtime 节点; -- 不在 Core 中保留旧 Host alias 或可选短写法; +- 不在 Core 中保留旧 Host/layout-form alias;property alias 只服从 3.2 的统一机制; - 不让 Playground、UI 或数据库包拥有 Core 协议; - 不在没有真实编译成本和运行时收益前增加 App 预编译产物。