ebox/DESIGN.md

102 lines
7.2 KiB
Markdown

# Ebox design
Ebox is the low-level spatial rendering engine. Its job is to turn a declarative node tree into measured layout fragments and a generic TP surface plan. It is deliberately smaller than an application framework.
## Design principles
- One source tree, one style normalizer, one layout pipeline, one buffer owner.
- A public node is data; rendering and mutation happen only at explicit entry points.
- Stable identity comes from node identity and explicit keys, never from visible text or selector strings.
- Layout changes are measured before publication; a failed candidate preserves the previous buffer state.
- The native module is an optional accelerator with an exact Elisp fallback.
- New abstractions must remove duplication or make an existing public workflow simpler.
- Public property schema, ECSS metadata, and used engine projection are derived from one property definition record.
- Cross-package framework integration is an additive versioned provider; consumer selection and fallback policy remain outside Ebox.
## Pipeline
```text
node tree
-> normalized style
-> measured formatting context
-> layout fragments and snapshot
-> render context
-> pure TP surface plan
```
The owners are `ebox-child-range.el` for immutable child-sequence and key indexes, `ebox-tree.el` for Ebox identity and traversal, `ebox-style.el` for style semantics, `ebox-measure.el` for display-sensitive measurement, `ebox-layout.el`/`ebox-flex.el`/`ebox-grid.el` for geometry, `ebox-fragment.el` for fragment facts, `ebox-render-context.el` for the candidate/materialization input port, `ebox-patch-plan.el` for pure operation-antichain artifacts, `ebox-surface.el` for pure TP plan projection and retained publication, and `ebox-incremental.el` for dirty classification and live-fact adaptation. `ebox-layout.el` never loads or calls the surface/TP layer; the facade wires surface operations into its validated render-context port. The patch planner never reads a buffer or surface; its incremental adapter supplies immutable parent facts and tentative operations. `ebox-buffer-backend.el` only builds and reshapes propertized render strings; all public live buffer publication routes through the TP surface boundary.
## Public boundary
Use the functions and properties in `docs/user/ebox-api-reference.en.md`; the facade inventory is `ebox-public-api`. Do not call `ebox--*` names from an application or a sibling package. Higher-level Component, reactive, behavior, data, and control concepts belong to ETAF.
ECSS and TP are required package dependencies, even when an author does not
write selectors or stylesheets. EKP is a separate optional typesetting
dependency: selecting `:wrap-mode kp` requires its public paragraph layout API.
An unavailable or incompatible EKP is an error, not permission to substitute a
different wrapping algorithm. Its accelerator is independent of Ebox's Rust
reflow module.
`ebox-display-buffer` renders before delegating window placement to native
`display-buffer`. It accepts the caller's display action, returns the buffer,
and does not choose an exclusive window layout or select the result. A buffer
owns one mounted layout and viewport; independently sized views use distinct
buffers rather than competing layouts over the same text.
## Layout scope
Ebox supports explicit `px`, `%`, `vw`, `vh`, `ch`, and `lh` dimensions, padding, margins, borders, colors, overflow, wrapping, row/column/flex formatting, and Grid tracks including fixed, fractional, implicit, gap, placement, span, and alignment behavior. The public typography subset is `:font-family`, CSS-reference-pixel `:font-size`, `:font-weight`, and `:font-style`; `:font-slant` is only an exact parse-time alias of `:font-style`. Arbitrary Emacs faces and text-property plists belong to the final adapter and are not Ebox author inputs. Four explicit native capabilities are supported separately: `:help-echo`, `:pointer`, `:hover-style`, and `:keymap`. CSS compatibility is intentionally partial: absolute positioning, z-index, shadows, border radius, full browser typography, and browser-level bidi are outside this package.
The text-cluster model covers common combining marks, variation selectors,
emoji modifiers, ZWJ sequences, and regional-indicator pairs. It is deliberately
smaller than complete Unicode grapheme segmentation; neither character count
nor this limited segmentation promises arbitrary font-shaping equivalence.
Optional interaction navigation moves point between currently rendered keymap
owners. It reads the committed surface, respects narrowing and invisibility,
and installs no key bindings, focus registry, or application state. Ownership
of actions and state remains with the caller.
Horizontal `:overflow hidden` is enforced on the composed content before the
owner's padding and border are added. It selects a measured prefix of complete
supported text clusters and fills the remaining pixels with space. Fixed
display spaces may shrink; glyphs and images are not partially raster-cropped.
Clipped child properties do not survive on neutral filler. The algorithm adapts
the prefix-and-filler approach from
[s-pixel](https://github.com/Kinneyzhang/s-pixel/tree/a2a0d6ae6b3bd71c1a70084d0ea37f56463e5fe0)
under GPL-3.0-or-later, with Ebox's cluster/property model and no `s` or
`s-pixel` dependency. It does not add stacking or overlap composition.
## Size composition
`ebox-size.el` owns pure length validation, dependency discovery, and resolution
to fractional pixels, plus a pure cumulative quantization helper used at the
display boundaries chosen by layout. Its context contains measured viewport axes, the
property's percentage reference, zero-glyph advance, and line height. It does
not read windows or nodes, evaluate arbitrary Lisp, choose property defaults,
or publish buffers. This keeps unit arithmetic reusable across normal, Flex,
Grid, spacing, and border geometry.
The author format is `(unit number)` and the composable functions are `calc`,
`min`, `max`, and `clamp`. Property schemas own allowed units, keywords, and
range rules. The [shared unit-constraint table](docs/user/ebox-user-guide.en.md#size-unit-constraints)
is the author contract: inline geometry, block geometry, and border paint have
distinct unit domains. Validation traverses every function branch and track
operand; Flex basis uses its parent main axis. Invalid unit combinations fail
during construction, before layout or display.
Layout supplies containing-block and font measurements and resolves
intrinsic/automatic sizes. `fit-content` is a bare keyword only. Removed bare
lengths, singleton pixel lists, viewport keywords, and `contain` have no
compatibility execution path.
Missing reference measurements defer resolution rather than becoming zero.
Intermediate arithmetic keeps fractions until the formatting/display boundary.
The buffer backend materializes block geometry in whole lines, matching the
restricted block-unit contract. An empty Box contributes zero automatic
content height; explicit height, padding, or borders supply any reserved extent.
## Verification rule
Every behavior change needs a focused ERT test and a fresh `make check`. Changes to native source also require `make native-rust-tests`; changes to docs or the active file set require `make docs-contract-tests`.