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