zenith_core/ast/node/container.rs
1//! Container node structs that own child `Node`s: frame, group, and the table
2//! family (table + column/row/cell).
3
4use std::collections::BTreeMap;
5
6use crate::ast::Span;
7use crate::ast::value::{Dimension, PropertyValue};
8
9use super::common::{Node, UnknownProperty};
10
11/// A `frame` node — a container that CLIPS its children to its rectangular
12/// bounds and renders them in source order (first child = bottom of z-order).
13///
14/// Unlike `group`, a frame has **required** geometry (x, y, w, h): these four
15/// dimensions define the clip rectangle. Children are rendered at their
16/// **absolute** page coordinates — frame does NOT translate children (dx/dy
17/// are unchanged). The frame only clips; it has no fill of its own in v0.
18///
19/// Opacity cascades (multiplies) into all descendant node alphas, exactly as
20/// in `GroupNode`.
21#[derive(Debug, Clone, PartialEq)]
22pub struct FrameNode {
23 pub id: String,
24 pub name: Option<String>,
25 pub role: Option<String>,
26 /// Required: clip-rectangle left edge in page coordinates.
27 pub x: Option<PropertyValue>,
28 /// Required: clip-rectangle top edge in page coordinates.
29 pub y: Option<PropertyValue>,
30 /// Required: clip-rectangle width.
31 pub w: Option<PropertyValue>,
32 /// Required: clip-rectangle height.
33 pub h: Option<PropertyValue>,
34 /// Layout algorithm hint ("absolute"/"flow"/"grid"). `"flow"` activates a
35 /// vertical-stack flow layout (uniform `padding` inset + `gap` between
36 /// children, resolved from the frame's style); `"grid"` tiles children
37 /// row-major into a `columns × rows` grid inside the padded content box with
38 /// uniform `gap` gutters; any other value (including `None` and `"absolute"`)
39 /// keeps the clip-only absolute-positioning model.
40 pub layout: Option<String>,
41 /// Grid column count for `layout="grid"` (ignored otherwise). When the frame
42 /// uses grid layout, children tile row-major into `columns` columns; absent →
43 /// treated as 1 column. KDL: `columns=2`.
44 pub columns: Option<u32>,
45 /// Grid row count for `layout="grid"` (ignored otherwise). Absent → derived as
46 /// `ceil(child_count / columns)` so the grid grows to fit its children. KDL:
47 /// `rows=3`.
48 pub rows: Option<u32>,
49 /// Opacity that cascades (multiplies) into all descendant node alphas.
50 pub opacity: Option<f64>,
51 /// When `Some(false)` the entire subtree (including the clip) is excluded
52 /// from the render.
53 pub visible: Option<bool>,
54 pub locked: Option<bool>,
55 /// Rotation in degrees, applied at render about the node's center (the
56 /// subtree rotates with it; see the compile-site notes for clip limitations).
57 pub rotate: Option<Dimension>,
58 /// Compositing blend mode: `"normal"` (default) or one of the 11 separable
59 /// blends. `None`/`"normal"` render source-over (byte-identical).
60 pub blend_mode: Option<String>,
61 /// Gaussian blur radius applied to the node's own rendered ink (sigma in
62 /// the declared unit, resolved to pixels at compile time). `None` / 0 →
63 /// no blur (byte-identical to having no attribute).
64 pub blur: Option<Dimension>,
65 pub style: Option<String>,
66 /// Child nodes in source order.
67 pub children: Vec<Node>,
68 /// Page-relative placement anchor (one of the nine named positions, e.g.
69 /// `"bottom-right"`). When present and recognized, the compile step derives
70 /// the node's x and/or y from the page and node dimensions. An explicitly-
71 /// authored x or y always wins.
72 pub anchor: Option<String>,
73 /// Optional safe-zone reference for the anchor. See [`RectNode::anchor_zone`](super::RectNode::anchor_zone).
74 pub anchor_zone: Option<String>,
75 /// Optional sibling node id for sibling-relative anchor positioning.
76 /// See [`RectNode::anchor_sibling`](super::RectNode::anchor_sibling).
77 pub anchor_sibling: Option<String>,
78 /// Adjacent-placement edge relative to `anchor-sibling`: `above`/`below`/`before`/`after`.
79 /// See [`RectNode::anchor_edge`](super::RectNode::anchor_edge).
80 pub anchor_edge: Option<String>,
81 /// Gap (px) between this node and its `anchor-sibling` edge when `anchor-edge` is set.
82 /// See [`RectNode::anchor_gap`](super::RectNode::anchor_gap).
83 pub anchor_gap: Option<Dimension>,
84 /// Parent-relative anchor toggle. See [`RectNode::anchor_parent`](super::RectNode::anchor_parent).
85 pub anchor_parent: Option<bool>,
86 /// Source declaration span, when available.
87 pub source_span: Option<Span>,
88 /// Unknown properties preserved for forward-compat.
89 pub unknown_props: BTreeMap<String, UnknownProperty>,
90}
91
92/// A named text-safe rectangle declared on a [`GroupNode`] as a
93/// `protected-region` child.
94///
95/// Declared as `protected-region id="…" x=(px)N y=(px)N w=(px)N h=(px)N`
96/// (with an optional `label`). Non-rendering metadata: it is not emitted to
97/// the scene and carries no visual properties of its own. Consumers (e.g.
98/// external layout tools) may consult it to avoid placing text over UI chrome
99/// or other reserved areas.
100#[derive(Debug, Clone, PartialEq)]
101pub struct ProtectedRegion {
102 pub id: String,
103 pub x: Dimension,
104 pub y: Dimension,
105 pub w: Dimension,
106 pub h: Dimension,
107 pub label: Option<String>,
108 pub source_span: Option<Span>,
109}
110
111/// A `group` node — a container that holds child nodes and renders them in
112/// source order (first child = bottom of z-order).
113///
114/// Groups introduce recursive nesting: a group can contain any mix of leaf
115/// nodes and further groups. The group itself emits no scene command; it
116/// only propagates a render context (opacity cascade + translation offset)
117/// to its descendants.
118#[derive(Debug, Clone, PartialEq)]
119pub struct GroupNode {
120 pub id: String,
121 pub name: Option<String>,
122 pub role: Option<String>,
123 /// Advisory x-translation offset applied to the subtree (default 0).
124 pub x: Option<PropertyValue>,
125 /// Advisory y-translation offset applied to the subtree (default 0).
126 pub y: Option<PropertyValue>,
127 /// Advisory bounding width — NOT used to scale children.
128 pub w: Option<PropertyValue>,
129 /// Advisory bounding height — NOT used to scale children.
130 pub h: Option<PropertyValue>,
131 /// Opacity that cascades (multiplies) into all descendant node alphas.
132 pub opacity: Option<f64>,
133 /// When `Some(false)` the entire subtree is excluded from the render.
134 pub visible: Option<bool>,
135 pub locked: Option<bool>,
136 /// Rotation in degrees, applied at render about the node's center (the
137 /// subtree rotates with it; see the compile-site notes for clip limitations).
138 pub rotate: Option<Dimension>,
139 /// Compositing blend mode: `"normal"` (default) or one of the 11 separable
140 /// blends. `None`/`"normal"` render source-over (byte-identical).
141 pub blend_mode: Option<String>,
142 /// Gaussian blur radius applied to the node's own rendered ink (sigma in
143 /// the declared unit, resolved to pixels at compile time). `None` / 0 →
144 /// no blur (byte-identical to having no attribute).
145 pub blur: Option<Dimension>,
146 pub style: Option<String>,
147 /// Advisory semantic layer role for external tooling (e.g. `"background"`,
148 /// `"overlay"`). Non-rendering; distinct from the structural `role` field.
149 /// Open-ended string; no value is invalid.
150 pub semantic_role: Option<String>,
151 /// Advisory visual prominence hint in the range `0.0..=1.0`. Non-rendering;
152 /// values outside this range produce a validation warning.
153 pub intensity: Option<f64>,
154 /// Advisory z-ordering hint for external tooling. Non-rendering; all integer
155 /// values are valid.
156 pub layer_priority: Option<i64>,
157 /// Child nodes in source order.
158 pub children: Vec<Node>,
159 /// Advisory text-safe rectangles declared as `protected-region` children.
160 /// Non-rendering metadata; defaults to empty (byte-identical output when absent).
161 pub protected_regions: Vec<ProtectedRegion>,
162 /// Ids of parameters that external tooling is permitted to tweak, declared
163 /// as `editable-param id="…"` children. Non-rendering metadata; defaults to
164 /// empty (byte-identical output when absent).
165 pub editable_param_ids: Vec<String>,
166 /// Page-relative placement anchor (one of the nine named positions, e.g.
167 /// `"bottom-right"`). When present and recognized, the compile step derives
168 /// the node's x and/or y from the page and node dimensions. An explicitly-
169 /// authored x or y always wins.
170 pub anchor: Option<String>,
171 /// Optional safe-zone reference for the anchor. See [`RectNode::anchor_zone`](super::RectNode::anchor_zone).
172 pub anchor_zone: Option<String>,
173 /// Optional sibling node id for sibling-relative anchor positioning.
174 /// See [`RectNode::anchor_sibling`](super::RectNode::anchor_sibling).
175 pub anchor_sibling: Option<String>,
176 /// Adjacent-placement edge relative to `anchor-sibling`: `above`/`below`/`before`/`after`.
177 /// See [`RectNode::anchor_edge`](super::RectNode::anchor_edge).
178 pub anchor_edge: Option<String>,
179 /// Gap (px) between this node and its `anchor-sibling` edge when `anchor-edge` is set.
180 /// See [`RectNode::anchor_gap`](super::RectNode::anchor_gap).
181 pub anchor_gap: Option<Dimension>,
182 /// Parent-relative anchor toggle. See [`RectNode::anchor_parent`](super::RectNode::anchor_parent).
183 pub anchor_parent: Option<bool>,
184 /// Source declaration span, when available.
185 pub source_span: Option<Span>,
186 /// Unknown properties preserved for forward-compat.
187 pub unknown_props: BTreeMap<String, UnknownProperty>,
188}
189
190/// A single column declaration in a [`TableNode`] (a `column` child).
191///
192/// `width` absent means an AUTO column: its width is content-based (the natural
193/// width its cells demand), scaled to fit the table's leftover width.
194#[derive(Debug, Clone, PartialEq)]
195pub struct TableColumn {
196 /// Explicit column width; `None` = auto (content-based width).
197 pub width: Option<Dimension>,
198 /// Source declaration span, when available.
199 pub source_span: Option<Span>,
200 /// Unknown properties preserved for forward-compat.
201 pub unknown_props: BTreeMap<String, UnknownProperty>,
202}
203
204/// A single cell in a [`TableRow`] (a `cell` child).
205///
206/// A cell holds ordinary child nodes (text/rect/image/…) — the same node model
207/// used by `frame`/`group` children — and may span multiple columns/rows via
208/// `colspan`/`rowspan` (HTML-table cell flow).
209#[derive(Debug, Clone, PartialEq)]
210pub struct TableCell {
211 /// Number of columns this cell spans (default 1).
212 pub colspan: u32,
213 /// Number of rows this cell spans (default 1).
214 pub rowspan: u32,
215 /// Cell content — ordinary nodes in source order.
216 pub children: Vec<Node>,
217 /// Per-cell background fill override (token-required color).
218 pub fill: Option<PropertyValue>,
219 /// Per-cell border color override (token-required color).
220 pub border: Option<PropertyValue>,
221 /// Per-cell border width override (token/dimension).
222 pub border_width: Option<PropertyValue>,
223 /// Per-cell horizontal alignment override (`start`/`center`/`end`).
224 pub h_align: Option<String>,
225 /// Per-cell vertical alignment override (`top`/`middle`/`bottom`).
226 pub v_align: Option<String>,
227 /// Source declaration span, when available.
228 pub source_span: Option<Span>,
229 /// Unknown properties preserved for forward-compat.
230 pub unknown_props: BTreeMap<String, UnknownProperty>,
231}
232
233/// A single row in a [`TableNode`] (a `row` child), holding cells left→right.
234#[derive(Debug, Clone, PartialEq)]
235pub struct TableRow {
236 /// Cells in source order (left→right).
237 pub cells: Vec<TableCell>,
238 /// Source declaration span, when available.
239 pub source_span: Option<Span>,
240 /// Unknown properties preserved for forward-compat.
241 pub unknown_props: BTreeMap<String, UnknownProperty>,
242}
243
244/// A `table` node — a grid container of `column`/`row`/`cell` children.
245///
246/// Renders tables with explicit, proportional, or content-based (auto) column
247/// widths; separate or collapsed borders; styled header rows; and multi-page
248/// flow when a table is taller than its page.
249#[derive(Debug, Clone, PartialEq)]
250pub struct TableNode {
251 pub id: String,
252 pub name: Option<String>,
253 pub role: Option<String>,
254 /// Required: table box left edge in page coordinates.
255 pub x: Option<PropertyValue>,
256 /// Required: table box top edge in page coordinates.
257 pub y: Option<PropertyValue>,
258 /// Required: table box width.
259 pub w: Option<PropertyValue>,
260 /// Required: table box height.
261 pub h: Option<PropertyValue>,
262 /// Column declarations, order = left→right.
263 pub columns: Vec<TableColumn>,
264 /// Row declarations, order = top→bottom.
265 pub rows: Vec<TableRow>,
266 /// First N rows are headers: styled via `header_fill`/`header_style` and
267 /// repeated atop each page slice in multi-page flow.
268 pub header_rows: Option<u32>,
269 /// Multi-page flow id. Tables sharing a `flows` id form ONE logical table:
270 /// the FIRST member (page-order, then source-order) is the SOURCE carrying
271 /// all rows + columns; continuation members declare the same id with empty
272 /// rows and receive the body-row slice that fits their box, with header rows
273 /// repeated. Mirrors the text-node `chain` field. `None` = standalone table.
274 pub flows: Option<String>,
275 /// Uniform gutter between cells in px (token or literal).
276 pub gap: Option<PropertyValue>,
277 /// Inset inside each cell in px (token or literal).
278 pub cell_padding: Option<PropertyValue>,
279 /// Border model: `"separate"` (default) or `"collapse"`; both are rendered.
280 pub border_collapse: Option<String>,
281 /// Default cell background (token-required color).
282 pub fill: Option<PropertyValue>,
283 /// Default cell border color (token-required color).
284 pub border: Option<PropertyValue>,
285 /// Default border width (token/dimension).
286 pub border_width: Option<PropertyValue>,
287 /// Header-row background override, applied to header cells (precedence:
288 /// cell.fill > header_fill > table.fill).
289 pub header_fill: Option<PropertyValue>,
290 /// Header text style ref, applied to header-row cell text.
291 pub header_style: Option<String>,
292 /// Default horizontal alignment (`start`(default)/`center`/`end`).
293 pub h_align: Option<String>,
294 /// Default vertical alignment (`top`(default)/`middle`/`bottom`).
295 pub v_align: Option<String>,
296 pub style: Option<String>,
297 pub opacity: Option<f64>,
298 pub visible: Option<bool>,
299 pub locked: Option<bool>,
300 /// Rotation — parsed and preserved but not yet applied at render for tables.
301 pub rotate: Option<Dimension>,
302 /// Page-relative placement anchor (one of the nine named positions, e.g.
303 /// `"bottom-right"`). When present and recognized, the compile step derives
304 /// the node's x and/or y from the page and node dimensions. An explicitly-
305 /// authored x or y always wins.
306 pub anchor: Option<String>,
307 /// Optional safe-zone reference for the anchor. See [`RectNode::anchor_zone`](super::RectNode::anchor_zone).
308 pub anchor_zone: Option<String>,
309 /// Optional sibling node id for sibling-relative anchor positioning.
310 /// See [`RectNode::anchor_sibling`](super::RectNode::anchor_sibling).
311 pub anchor_sibling: Option<String>,
312 /// Adjacent-placement edge relative to `anchor-sibling`: `above`/`below`/`before`/`after`.
313 /// See [`RectNode::anchor_edge`](super::RectNode::anchor_edge).
314 pub anchor_edge: Option<String>,
315 /// Gap (px) between this node and its `anchor-sibling` edge when `anchor-edge` is set.
316 /// See [`RectNode::anchor_gap`](super::RectNode::anchor_gap).
317 pub anchor_gap: Option<Dimension>,
318 /// Parent-relative anchor toggle. See [`RectNode::anchor_parent`](super::RectNode::anchor_parent).
319 pub anchor_parent: Option<bool>,
320 /// Source declaration span, when available.
321 pub source_span: Option<Span>,
322 /// Unknown properties preserved for forward-compat.
323 pub unknown_props: BTreeMap<String, UnknownProperty>,
324}