Skip to main content

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}