Skip to main content

pathland_core/
constants.rs

1//! Protocol constants: categories, commands, component types, value types, flags.
2//!
3//! These constants are part of the wire protocol (see `spec/OPCODE.md`) and MUST
4//! NOT be renumbered once released. Component types were renumbered pre-release
5//! to the grouped ranges in `spec/PRIMITIVES.md` (wire version stays 1).
6
7/// Opcode categories (`Category` field, u8).
8pub mod category {
9    /// Tree mutations (guest → host): node create/delete/insert/remove/move.
10    pub const TREE: u8 = 0x01;
11    /// Parameters and design tokens (guest → host): the constraint properties
12    /// (spacing, padding, alignment, …) and content that native renderers use
13    /// to lay out and style native elements. The engine does NOT emit rects.
14    pub const PARAMETER: u8 = 0x02;
15    /// Raw input events (host → guest): pointer down/move/up, key down/up.
16    pub const EVENT: u8 = 0x03;
17    /// Control (both directions): reset, environment, custom data.
18    pub const META: u8 = 0x04;
19}
20
21/// Commands within the `TREE` category.
22pub mod tree {
23    /// `A=nodeId, B=componentType (u16, low)`
24    pub const CREATE_NODE: u8 = 0x01;
25    /// `A=nodeId`
26    pub const DELETE_NODE: u8 = 0x02;
27    /// `A=parentId, B=childId, C=index` (append is `C = u32::MAX`, see [`crate::APPEND`])
28    pub const INSERT_CHILD: u8 = 0x03;
29    /// `A=parentId, B=childId`
30    pub const REMOVE_CHILD: u8 = 0x04;
31    /// `A=parentId, B=childId, C=newIndex` (append is `C = u32::MAX`, see [`crate::APPEND`])
32    pub const MOVE_CHILD: u8 = 0x05;
33}
34
35/// Commands within the `PARAMETER` category.
36pub mod parameter {
37    /// `A=nodeId, B=(valueType << 16) | propertyId, C=value`
38    pub const SET_PROPERTY: u8 = 0x01;
39    /// `A=arenaRef (path), B=valueType (u8), C=value`
40    pub const SET_DESIGN_TOKEN: u8 = 0x02;
41    /// `A=nodeId, B=arenaRef (utf8)`
42    pub const SET_TEXT: u8 = 0x03;
43    /// **Draft.** `A=nodeId, B=days since epoch (I32), C=millis of day (U32)` —
44    /// set a node's date value (e.g. a `DATE_PICKER`); see
45    /// `spec/PRIMITIVES.md` / `spec/OPCODE.md`.
46    pub const SET_DATE: u8 = 0x04;
47    /// `A=nodeId, B=arenaRef (attributed payload)` — set a node's text plus
48    /// range-scoped style runs and links (see [`crate::AttributedString`] and
49    /// `spec/OPCODE.md`). The payload is binary; the text is its plain fallback.
50    pub const SET_ATTRIBUTED_TEXT: u8 = 0x05;
51}
52
53/// Commands within the `EVENT` category.
54///
55/// Events are **raw inputs** (host → guest), each carrying a host-resolved
56/// `targetId`. High-level interpretations (tap, drag, long-press) are built by
57/// the guest/app from these raw inputs and are NOT part of the protocol.
58pub mod event {
59    /// `A=targetId, B=x (f32), C=y (f32)` (Flags `POINTER_SECONDARY`)
60    pub const POINTER_DOWN: u8 = 0x01;
61    /// `A=targetId, B=x (f32), C=y (f32)` (Flags `HOVER_ENTER` / `HOVER_LEAVE`)
62    pub const POINTER_MOVE: u8 = 0x02;
63    /// `A=targetId, B=x (f32), C=y (f32)` (Flags `POINTER_SECONDARY`)
64    pub const POINTER_UP: u8 = 0x03;
65    /// `A=targetId, B=keyCode (u16, low), C=modifiers (u8, low)` (Flags `KEY_REPEAT`)
66    pub const KEY_DOWN: u8 = 0x04;
67    /// `A=targetId, B=keyCode (u16, low), C=modifiers (u8, low)`
68    pub const KEY_UP: u8 = 0x05;
69    /// `A=targetId, B=value (f32)` — a control's value changed (host → guest).
70    /// The renderer resolves the control's semantic value (e.g. a slider thumb
71    /// dragged to a point on its track); the guest/app writes it into its state.
72    pub const VALUE_CHANGED: u8 = 0x06;
73    /// `A=targetId, B=string offset` — a text field's value changed (host →
74    /// guest). The new text lives in the batch's string section, referenced by
75    /// the *relative* offset in `B` (the same convention as `PARAMETER::SET_TEXT`).
76    pub const TEXT_CHANGED: u8 = 0x07;
77    /// **Draft.** `A=targetId, B=focused (0/1)` — a node gained/lost focus.
78    pub const FOCUS_CHANGED: u8 = 0x08;
79    /// **Draft.** `A=targetId, B=editing (0/1)` — an editing session began/ended.
80    pub const EDITING_CHANGED: u8 = 0x09;
81    /// **Draft.** `A=targetId, B=0` — the user committed a field (onSubmit).
82    pub const SUBMIT: u8 = 0x0A;
83    /// **Draft.** `A=targetId, B=offsetX (f32), C=offsetY (f32)` — scroll offset.
84    pub const SCROLL: u8 = 0x0B;
85    /// **Draft.** `A=targetId, B=deltaX (f32), C=deltaY (f32)` — wheel/trackpad.
86    pub const WHEEL: u8 = 0x0C;
87    /// **Draft.** `A=targetId, B=days since epoch (I32), C=millis of day (U32)`
88    /// — a `DATE_PICKER`'s value changed (matches `PARAMETER::SET_DATE`).
89    pub const DATE_CHANGED: u8 = 0x0D;
90    /// **Draft.** Global navigation request (host → guest), never node-keyed.
91    /// With the `NAVIGATE_URL` flag, `B` is a string offset (event arena /
92    /// batch string section) to the destination URL; without it, the event means
93    /// "back one step" (native back affordance) and `B`/`C` are ignored.
94    pub const NAVIGATE: u8 = 0x0E;
95    /// **Draft.** A bound `MEDIA_CONTROLLER`'s playback state changed
96    /// (host → guest); `B` = 0/1. Reported only when `PLAYBACK_STATE` is bound.
97    pub const MEDIA_PLAY_STATE_CHANGED: u8 = 0x0F;
98    /// **Draft.** A bound controller's current position (host → guest); `B` =
99    /// seconds (f32). Reported while playing; an app-initiated seek is not echoed.
100    pub const MEDIA_TIME_UPDATED: u8 = 0x10;
101    /// **Draft.** A bound controller reached the end of its media (host → guest).
102    pub const MEDIA_ENDED: u8 = 0x11;
103    /// **Draft.** A bound controller's volume changed (host → guest); `B` =
104    /// volume (f32, 0..1).
105    pub const MEDIA_VOLUME_CHANGED: u8 = 0x12;
106    /// **Draft.** A `SIZE_THAT_FITS` slot's selected candidate index changed
107    /// (host → guest); `B` = index (f32). Reported once after the first measure,
108    /// then only when the derived fit index changes.
109    pub const FIT_CHANGED: u8 = 0x13;
110    /// **Draft.** A bound controller's media duration became known/changed
111    /// (host → guest); `B` = seconds (f32). Reported when the resource loads and
112    /// only when the value changes — never a stream.
113    pub const MEDIA_DURATION_CHANGED: u8 = 0x14;
114}
115
116/// Commands within the `META` category.
117pub mod meta {
118    /// Host must clear all rendered output.
119    pub const RESET: u8 = 0x01;
120    /// `A=fieldId (u16, low), B=field value` — a platform environment field
121    /// (host → guest); see [`crate::environment`]. An extensible field family:
122    /// each opcode sets one field (viewport, initial route, …).
123    pub const ENVIRONMENT: u8 = 0x02;
124    /// The host (renderer) requests a full snapshot of the current tree
125    /// (host → guest). `A/B/C = 0`. Used for reconnect/gap recovery.
126    pub const RESYNC: u8 = 0x03;
127    /// A transport-liveness heartbeat probe (guest → host). `A/B/C = 0`, no
128    /// payload. **Network batch transport only** (spec/OPCODE.md §Transport —
129    /// heartbeat): the shared-memory ring MUST NOT emit, expect, or relay it.
130    pub const PING: u8 = 0x04;
131    /// The reply to `META::PING` (host → guest). `A/B/C = 0`, no payload.
132    /// Network batch transport only (see [`PING`]).
133    pub const PONG: u8 = 0x05;
134    /// **Draft.** `A = mode` — renderer-wide color-scheme override
135    /// (guest → host): `0` = platform-derived (default), `1` = light,
136    /// `2` = dark. The renderer re-resolves every token-referencing property;
137    /// while forced, platform scheme changes are ignored. See
138    /// `spec/TOKENS.md` §Color Schemes.
139    pub const SET_COLOR_SCHEME: u8 = 0x06;
140    /// **Draft.** `A/B/C = 0` — clear every application design-token override
141    /// (base + `dark.*`) and re-resolve against the renderer defaults
142    /// (guest → host). Emit before a replacement theme's `SET_DESIGN_TOKEN`
143    /// opcodes in the same frame.
144    pub const RESET_THEME: u8 = 0x07;
145    /// **Draft.** `A = fieldId (u16, low), B = value` — presentation metadata
146    /// for the host window/surface that owns this stream (guest → host):
147    /// title, description, locale (and future fields — an extensible family
148    /// like [`ENVIRONMENT`]). Each field defines its own `B` encoding; today
149    /// every field is a `STRING` offset (the arena / batch string-section
150    /// convention). Renderer-global: survives `META::RESET`, and a full
151    /// snapshot re-emits it. Each renderer maps the fields onto its own chrome;
152    /// the browser mapping is `<title>` / `<meta name="description">` /
153    /// `<html lang>`, not the protocol definition.
154    pub const SET_WINDOW_METADATA: u8 = 0x08;
155}
156
157/// Window/surface metadata field ids for `META::SET_WINDOW_METADATA`
158/// (guest → host).
159///
160/// Each opcode sets one field; new fields are new ids, never new commands
161/// (spec/OPCODE.md §Window metadata). An empty string clears the field and the
162/// renderer falls back to its default.
163pub mod window_field {
164    /// The window/surface title (STRING in `B`).
165    pub const TITLE: u16 = 0x0001;
166    /// A human-readable description of the current content (STRING in `B`).
167    /// Renderers map it to their own affordance (browser meta description,
168    /// native accessible description, …).
169    pub const DESCRIPTION: u16 = 0x0002;
170    /// The content locale as a BCP 47 tag (STRING in `B`). The browser maps it
171    /// to `<html lang>`; native renderers may use it for accessibility/formatting.
172    pub const LOCALE: u16 = 0x0003;
173
174    /// Every field this protocol version defines.
175    pub const ALL: [u16; 3] = [TITLE, DESCRIPTION, LOCALE];
176}
177
178/// Values for `META::SET_COLOR_SCHEME`'s `A` field.
179pub mod color_scheme_mode {
180    /// Follow the platform's native light/dark signal (default).
181    pub const PLATFORM: u32 = 0;
182    /// Force the light scheme.
183    pub const LIGHT: u32 = 1;
184    /// Force the dark scheme.
185    pub const DARK: u32 = 2;
186}
187
188/// Environment field ids for `META::ENVIRONMENT` (host → guest).
189///
190/// Each opcode sets one platform environment field; new fields are new ids, never
191/// new commands (spec/OPCODE.md §Environment fields).
192pub mod environment {
193    /// Viewport width in logical points (f32 in `B`).
194    pub const VIEWPORT_WIDTH: u16 = 0x0001;
195    /// Viewport height in logical points (f32 in `B`).
196    pub const VIEWPORT_HEIGHT: u16 = 0x0002;
197    /// The initial route/deep-link path (STRING: `B` is a string-section/event-arena
198    /// offset — the `TEXT_CHANGED`/`NAVIGATE` dual convention).
199    pub const ROUTE: u16 = 0x0003;
200}
201
202/// Flag bits used across categories.
203pub mod flag {
204    /// `POINTER_DOWN` / `POINTER_UP`: secondary button (bit 0).
205    pub const POINTER_SECONDARY: u16 = 0x0001;
206    /// `POINTER_MOVE`: pointer is entering/hovering (bit 0).
207    pub const HOVER_ENTER: u16 = 0x0001;
208    /// `POINTER_MOVE`: pointer is leaving (bit 1).
209    pub const HOVER_LEAVE: u16 = 0x0002;
210    /// `KEY_DOWN`: key repeat (bit 1).
211    pub const KEY_REPEAT: u16 = 0x0002;
212    /// `NAVIGATE`: `B` is a URL string offset (bit 0). Without this flag the
213    /// event is a back request.
214    pub const NAVIGATE_URL: u16 = 0x0001;
215}
216
217/// Bits for the `EVENT_LISTENERS` semantic property (a u32 bitmask).
218///
219/// Each bit requests that a renderer attach native input recognition for the
220/// matching raw-input event and report it for that node — so any element (not
221/// just a button) can emit raw events. Bits are independent of the `EVENT`
222/// command ids; more event kinds can be added later.
223pub mod listener {
224    /// `POINTER_DOWN` events (bit 0).
225    pub const POINTER_DOWN: u32 = 1 << 0;
226    /// `POINTER_MOVE` events (bit 1).
227    pub const POINTER_MOVE: u32 = 1 << 1;
228    /// `POINTER_UP` events (bit 2).
229    pub const POINTER_UP: u32 = 1 << 2;
230    /// `KEY_DOWN` events (bit 3).
231    pub const KEY_DOWN: u32 = 1 << 3;
232    /// `KEY_UP` events (bit 4).
233    pub const KEY_UP: u32 = 1 << 4;
234    /// **Draft.** `FOCUS_CHANGED` events (bit 5).
235    pub const FOCUS: u32 = 1 << 5;
236    /// **Draft.** `EDITING_CHANGED` events (bit 6).
237    pub const EDITING: u32 = 1 << 6;
238    /// **Draft.** `SUBMIT` events (bit 7).
239    pub const SUBMIT: u32 = 1 << 7;
240    /// **Draft.** `SCROLL` events (bit 8).
241    pub const SCROLL: u32 = 1 << 8;
242    /// **Draft.** `WHEEL` events (bit 9).
243    pub const WHEEL: u32 = 1 << 9;
244
245    /// All pointer events (down + move + up).
246    pub const POINTER: u32 = POINTER_DOWN | POINTER_MOVE | POINTER_UP;
247}
248
249/// Bits for the `BORDER_EDGES` style property (a u32 bitmask).
250///
251/// Each bit selects one edge of a node's border. `LEADING`/`TRAILING` are
252/// direction-aware; a renderer resolves them to physical `left`/`right`
253/// according to its layout direction (LTR by default).
254pub mod border_edges {
255    /// Top edge (bit 0).
256    pub const TOP: u32 = 1 << 0;
257    /// Leading edge (bit 1) — physical left in LTR.
258    pub const LEADING: u32 = 1 << 1;
259    /// Bottom edge (bit 2).
260    pub const BOTTOM: u32 = 1 << 2;
261    /// Trailing edge (bit 3) — physical right in LTR.
262    pub const TRAILING: u32 = 1 << 3;
263
264    /// All four edges.
265    pub const ALL: u32 = TOP | LEADING | BOTTOM | TRAILING;
266}
267
268/// Value types for `PARAMETER` properties (u8, encoded in the high byte of `B`).
269pub mod value_type {
270    pub const U8: u8 = 0x01;
271    pub const U32: u8 = 0x02;
272    pub const I32: u8 = 0x03;
273    pub const F32: u8 = 0x04;
274    pub const STRING: u8 = 0x05;
275    pub const ENUM: u8 = 0x06;
276    pub const COLOR: u8 = 0x07;
277    pub const DESIGN_TOKEN: u8 = 0x08;
278    /// **Draft.** Arena-backed length-prefixed array: `[u32 count][element × count]`
279    /// in the string arena (each element holds the value-type's bit pattern —
280    /// today `F32`s, e.g. the `FIT_QUERY` threshold table).
281    pub const LIST: u8 = 0x09;
282}
283
284/// Component types (u16, encoded in the low half of `B` of `TREE::CREATE_NODE`).
285///
286/// IDs are grouped into three definitive ranges (see `spec/PRIMITIVES.md`):
287/// Primitive Drawing `0x01`–`0x0F`, Layout & Container `0x10`–`0x1F`, Semantic
288/// Control `0x20`–`0x2F`, plus the utility `COMMENT` at `0x7F`. Renumbered
289/// pre-release (wire version stays 1).
290pub mod component_type {
291    // ── Primitive Drawing & Visual Nodes (0x01–0x0F) ────────────────────────
292    /// Standard styled text display.
293    pub const TEXT: u16 = 0x01;
294    /// Raster, vector, or system icon asset.
295    pub const IMAGE: u16 = 0x02;
296    /// **Draft.** Solid color / layout-filling background view.
297    pub const COLOR: u16 = 0x03;
298    /// **Draft.** Vector geometry (`SHAPE_KIND` selects the shape).
299    pub const SHAPE: u16 = 0x04;
300    /// **Draft.** Axis-aligned separator line.
301    pub const DIVIDER: u16 = 0x05;
302    /// Flexible expanding layout filler.
303    pub const SPACER: u16 = 0x06;
304    /// **Draft.** Determinate progress or activity indicator.
305    pub const PROGRESS_VIEW: u16 = 0x07;
306    /// **Draft.** Range meter against a scale (read-only).
307    pub const GAUGE: u16 = 0x08;
308    // 0x09 freed (formerly AUDIO — audio now has no view; see MEDIA_CONTROLLER).
309    /// **Draft.** Video playback **surface** — renders a `MEDIA_CONTROLLER`'s
310    /// video output; it references the controller via `MEDIA_CONTROLLER_ID` and
311    /// carries no source of its own (spec/MEDIA.md).
312    pub const VIDEO: u16 = 0x0A;
313    /// **Draft.** Semantic icon (`ICON_NAME` selects a renderer-native symbol —
314    /// Adwaita on GTK, Lucide on the web; spec/ICONS.md).
315    pub const ICON: u16 = 0x0B;
316    // 0x0C–0x0F reserved (future drawing nodes).
317
318    // ── Layout & Container Primitives (0x10–0x1F) ───────────────────────────
319    /// Vertical flex stack.
320    pub const VSTACK: u16 = 0x10;
321    /// Horizontal flex stack.
322    pub const HSTACK: u16 = 0x11;
323    /// **Draft.** Depth-overlapping layer stack.
324    pub const ZSTACK: u16 = 0x12;
325    /// Static 2D matrix grid (eagerly rendered).
326    pub const GRID: u16 = 0x13;
327    /// Scrollable content container.
328    pub const SCROLLVIEW: u16 = 0x14;
329    /// **Draft.** Virtualized vertical grid.
330    pub const LAZY_VGRID: u16 = 0x15;
331    /// **Draft.** Virtualized horizontal grid.
332    pub const LAZY_HGRID: u16 = 0x16;
333    /// **Draft.** Fit slot (`SizeThatFits`): shows its single selected child —
334    /// only the selected candidate is ever transmitted. The renderer measures
335    /// its own allocated width against `FIT_QUERY` and reports `FIT_CHANGED`.
336    pub const SIZE_THAT_FITS: u16 = 0x17;
337    // 0x18–0x1A reserved (formerly composite views, removed).
338    /// **Draft.** Virtualized vertical stack.
339    pub const LAZY_VSTACK: u16 = 0x1B;
340    /// **Draft.** Virtualized horizontal stack.
341    pub const LAZY_HSTACK: u16 = 0x1C;
342    /// **Draft.** Explicit row grouping for a `GRID`: a grid child whose children
343    /// are one row's cells (the SwiftUI `GridRow` surface). Renders nothing
344    /// outside a `GRID`.
345    pub const GRID_ROW: u16 = 0x1D;
346    /// **Draft.** Proxy view (`spec/PROXY.md`): a transparent single-child
347    /// container whose child subtree is served by **another** Pathland server.
348    /// Renderers render it as a plain container; the proxying server remaps the
349    /// foreign subtree's node ids into a disjoint high range and routes its
350    /// events back to the origin server.
351    pub const PROXY_VIEW: u16 = 0x1E;
352    // 0x1F reserved (future layout nodes).
353
354    // ── Semantic Control Nodes (0x20–0x2F) ──────────────────────────────────
355    /// Action trigger control.
356    pub const BUTTON: u16 = 0x20;
357    /// Single-line text input (secure variant via `IS_SECURE`).
358    pub const TEXT_FIELD: u16 = 0x21;
359    /// **Draft.** Multi-line text editing area.
360    pub const TEXT_EDITOR: u16 = 0x22;
361    // 0x23 reserved.
362    /// Boolean control; the visual variant is `TOGGLE_STYLE`
363    /// (`Switch`/`Checkbox`/`Button`).
364    pub const TOGGLE: u16 = 0x24;
365    /// Continuous or stepped numeric range control.
366    pub const SLIDER: u16 = 0x25;
367    /// **Draft.** Discrete increment/decrement control.
368    pub const STEPPER: u16 = 0x26;
369    /// **Draft.** Date & time selection control (`PARAMETER::SET_DATE` value).
370    pub const DATE_PICKER: u16 = 0x27;
371    /// **Draft.** Selection control (options are child nodes).
372    pub const PICKER: u16 = 0x28;
373    /// **Draft.** Contextual action trigger + popover container.
374    pub const MENU: u16 = 0x29;
375    /// **Draft.** Native system color picker control.
376    pub const COLOR_PICKER: u16 = 0x2A;
377    // 0x2B–0x2F reserved (future semantic controls).
378
379    // ── Non-visual / Resource Nodes (0x30–0x3F) ─────────────────────────────
380    /// **Draft.** Media controller — a **non-visual** node that owns a media
381    /// resource (`MEDIA_SOURCE`) and its transport state (`PLAYBACK_STATE`,
382    /// `MEDIA_POSITION`, `MEDIA_VOLUME`, `MEDIA_MUTED`, `MEDIA_LOOP`,
383    /// `MEDIA_RATE`). A `VIDEO` surface references it by id
384    /// (`MEDIA_CONTROLLER_ID`); audio has no view and is controlled by the
385    /// application's own views. Layout-transparent (spec/MEDIA.md).
386    pub const MEDIA_CONTROLLER: u16 = 0x30;
387    // 0x31–0x3F reserved (future non-visual/resource nodes).
388
389    /// Opaque/debug node; renderers ignore it (no native element).
390    pub const COMMENT: u16 = 0x7F;
391}
392
393/// Property IDs (u16, encoded in the low half of `B` of `PARAMETER::SET_PROPERTY`).
394///
395/// IDs are specified in `spec/OPCODE.md` (carried forward from the historical protocol).
396pub mod property_id {
397    // Stack
398    pub const SPACING: u16 = 0x0001;
399    pub const ALIGNMENT: u16 = 0x0002;
400    // 0x0003 reserved (formerly JUSTIFICATION; main-axis distribution is
401    // expressed via a spacer element instead).
402    // 0x0004 reserved (formerly PADDING; now a styling modifier at 0x1011).
403    pub const CONTENT_MARGINS: u16 = 0x0005;
404    // Text
405    pub const TEXT: u16 = 0x000A;
406    pub const LINE_LIMIT: u16 = 0x000B;
407    pub const TEXT_ALIGNMENT: u16 = 0x000C;
408    pub const TRUNCATION_MODE: u16 = 0x000D;
409    /// **Draft.** `SHAPE` geometry kind (F32 enum): `Circle`=0, `Rectangle`=1,
410    /// `RoundedRectangle`=2, `Capsule`=3, `Ellipse`=4, `Path`=5.
411    pub const SHAPE_KIND: u16 = 0x0006;
412    // Layout modifiers (draft, 0x000E–0x001D)
413    /// **Draft.** Post-layout translation, x (F32). `.offset(x:y:)`.
414    pub const OFFSET_X: u16 = 0x000E;
415    /// **Draft.** Post-layout translation, y (F32).
416    pub const OFFSET_Y: u16 = 0x000F;
417    /// **Draft.** Absolute position within the parent, x (F32). `.position(x:y:)`.
418    pub const POSITION_X: u16 = 0x0010;
419    /// **Draft.** Absolute position within the parent, y (F32).
420    pub const POSITION_Y: u16 = 0x0011;
421    /// **Draft.** Minimum width (F32). `.frame(minWidth:...)`.
422    pub const MIN_WIDTH: u16 = 0x0012;
423    // 0x0013 was IDEAL_WIDTH: retired in the POC; do not reuse.
424    /// **Draft.** Maximum width (F32).
425    pub const MAX_WIDTH: u16 = 0x0014;
426    /// **Draft.** Minimum height (F32).
427    pub const MIN_HEIGHT: u16 = 0x0015;
428    // 0x0016 was IDEAL_HEIGHT: retired in the POC; do not reuse.
429    /// **Draft.** Maximum height (F32).
430    pub const MAX_HEIGHT: u16 = 0x0017;
431    /// **Draft.** Fixed-size hint, horizontal (U8 0/1). `.fixedSize()`.
432    pub const FIXED_SIZE_HORIZONTAL: u16 = 0x0018;
433    /// **Draft.** Fixed-size hint, vertical (U8 0/1).
434    pub const FIXED_SIZE_VERTICAL: u16 = 0x0019;
435    /// **Draft.** Layout priority (F32). `.layoutPriority(_:)`.
436    pub const LAYOUT_PRIORITY: u16 = 0x001A;
437    /// **Draft.** Aspect ratio (F32). `.aspectRatio(_:)`.
438    pub const ASPECT_RATIO: u16 = 0x001B;
439    /// **Draft.** Content mode (F32 enum): `Fit`=0, `Fill`=1.
440    pub const CONTENT_MODE: u16 = 0x001C;
441    /// **Draft.** Minimum scale factor (F32). `.minimumScaleFactor(_:)`.
442    pub const MINIMUM_SCALE_FACTOR: u16 = 0x001D;
443    /// **Draft.** Grid column count (F32, constructor property): a positive value
444    /// pins the count (equal `1fr` tracks); `FILL`/absent = auto-fit. Never a
445    /// pixel width — the grid's box comes from `WIDTH`/`HEIGHT`.
446    pub const GRID_COLUMNS: u16 = 0x001E;
447    /// **Draft.** Grid row count (F32, constructor property): the `LAZY_HGRID`
448    /// fixed track; positive = count, `FILL`/absent = auto-fit.
449    pub const GRID_ROWS: u16 = 0x001F;
450    /// **Draft.** Grid track spec (STRING, constructor property): a comma-
451    /// separated list of per-track sizes — `flex` | `fixed:<points>` |
452    /// `adaptive:<points>` — taking precedence over `GRID_COLUMNS`/`GRID_ROWS`
453    /// (the equal-`1fr` count is `N`×`flex` sugar). CSS-grid-native: renderers
454    /// without a native equivalent (GTK `GtkGrid`) fall back to natural sizing.
455    pub const GRID_TRACKS: u16 = 0x0020;
456    /// **Draft.** A `SCROLLVIEW`'s scroll axis (F32 enum): `Vertical`=0 (default
457    /// when absent), `Horizontal`=1. The view is layout-greedy on this axis; the
458    /// cross axis hugs its content (fill propagation applies).
459    pub const SCROLL_AXIS: u16 = 0x0021;
460    // Style (0x1000 range)
461    pub const BACKGROUND_COLOR: u16 = 0x1001;
462    /// **Draft.** Image source (STRING: a resource name, file path, or URL —
463    /// an **asset reference**, resolved and loaded by the renderer/host; never
464    /// embedded in the opcode stream).
465    pub const IMAGE_SOURCE: u16 = 0x1002;
466    // 0x1033, 0x1034 freed (formerly AUDIO_SOURCE / VIDEO_SOURCE).
467    /// **Draft.** Media playback state (U32 0=pause, 1=play) on a
468    /// `MEDIA_CONTROLLER`. When bound, the renderer reports
469    /// `MEDIA_PLAY_STATE_CHANGED`.
470    pub const PLAYBACK_STATE: u16 = 0x1035;
471    /// **Draft.** Media position in seconds (F32) on a `MEDIA_CONTROLLER`; a
472    /// change seeks. When bound, the renderer reports `MEDIA_TIME_UPDATED`.
473    pub const MEDIA_POSITION: u16 = 0x1036;
474    /// **Draft.** Media volume (F32 0..1) on a `MEDIA_CONTROLLER`. When bound,
475    /// the renderer reports `MEDIA_VOLUME_CHANGED`.
476    pub const MEDIA_VOLUME: u16 = 0x1037;
477    /// **Draft.** The referenced `MEDIA_CONTROLLER`'s node id (U32) on a `VIDEO`
478    /// surface — the binding that connects the surface to its playback.
479    pub const MEDIA_CONTROLLER_ID: u16 = 0x103A;
480    /// **Draft.** Media resource (STRING asset reference, see `IMAGE_SOURCE`) on
481    /// a `MEDIA_CONTROLLER`.
482    pub const MEDIA_SOURCE: u16 = 0x103B;
483    /// **Draft.** Mute (U8 0/1) on a `MEDIA_CONTROLLER`; independent of
484    /// `MEDIA_VOLUME`.
485    pub const MEDIA_MUTED: u16 = 0x103C;
486    /// **Draft.** Loop the resource on end (U8 0/1) on a `MEDIA_CONTROLLER`.
487    pub const MEDIA_LOOP: u16 = 0x103D;
488    /// **Draft.** Playback rate (F32, 1.0 = normal) on a `MEDIA_CONTROLLER`.
489    pub const MEDIA_RATE: u16 = 0x103E;
490    /// **Draft.** Semantic icon name (STRING, canonical; spec/ICONS.md) on an
491    /// `ICON` node. The renderer maps it to its native icon set and falls back
492    /// to a renderer-default visual for unknown names.
493    pub const ICON_NAME: u16 = 0x1038;
494    pub const BORDER_WIDTH: u16 = 0x1003;
495    pub const BORDER_COLOR: u16 = 0x1004;
496    pub const BORDER_RADIUS: u16 = 0x1005;
497    pub const PADDING_STYLE: u16 = 0x1006;
498    pub const FONT_SIZE: u16 = 0x1007;
499    pub const FONT_WEIGHT: u16 = 0x1008;
500    pub const FONT_FAMILY: u16 = 0x1009;
501    pub const COLOR: u16 = 0x100A;
502    pub const WIDTH: u16 = 0x100B;
503    pub const HEIGHT: u16 = 0x100C;
504    pub const OPACITY: u16 = 0x100D;
505    pub const VISIBLE: u16 = 0x100E;
506    pub const Z_INDEX: u16 = 0x100F;
507    pub const CLIPS_TO_BOUNDS: u16 = 0x1010;
508    pub const PADDING: u16 = 0x1011;
509    pub const PADDING_TOP: u16 = 0x1012;
510    pub const PADDING_RIGHT: u16 = 0x1013;
511    pub const PADDING_BOTTOM: u16 = 0x1014;
512    pub const PADDING_LEFT: u16 = 0x1015;
513    /// `BORDER_EDGES` — a u32 bitmask (see [`crate::border_edges`]) selecting
514    /// which edges of a node's border are drawn.
515    pub const BORDER_EDGES: u16 = 0x1016;
516    // Text-format modifiers (draft, 0x1017–0x1020)
517    /// **Draft.** Font style (F32 enum): `Normal`=0, `Italic`=1.
518    pub const FONT_STYLE: u16 = 0x1017;
519    /// **Draft.** Font design (F32 enum): `Default`=0, `Serif`=1, `Rounded`=2,
520    /// `Monospaced`=3.
521    pub const FONT_DESIGN: u16 = 0x1018;
522    /// **Draft.** Font width (F32, 0.5–1.5, 1.0 default).
523    pub const FONT_WIDTH: u16 = 0x1019;
524    /// **Draft.** Kerning (F32 points). `.kerning(_:)`.
525    pub const KERNING: u16 = 0x101A;
526    /// **Draft.** Tracking (F32 points). `.tracking(_:)`.
527    pub const TRACKING: u16 = 0x101B;
528    /// **Draft.** Baseline offset (F32 points). `.baselineOffset(_:)`.
529    pub const BASELINE_OFFSET: u16 = 0x101C;
530    /// **Draft.** Line spacing (F32 points). `.lineSpacing(_:)`.
531    pub const LINE_SPACING: u16 = 0x101D;
532    /// **Draft.** Text case (F32 enum): `None`=0, `Uppercase`=1, `Lowercase`=2.
533    pub const TEXT_CASE: u16 = 0x101E;
534    /// **Draft.** Underline (U8 0/1). `.underline()`.
535    pub const UNDERLINE: u16 = 0x101F;
536    /// **Draft.** Strikethrough (U8 0/1). `.strikethrough()`.
537    pub const STRIKETHROUGH: u16 = 0x1020;
538    // Effect modifiers (draft, 0x1021–0x102C)
539    /// **Draft.** Shadow color (COLOR). `.shadow(color:...)`.
540    pub const SHADOW_COLOR: u16 = 0x1021;
541    /// **Draft.** Shadow radius (F32).
542    pub const SHADOW_RADIUS: u16 = 0x1022;
543    /// **Draft.** Shadow x offset (F32).
544    pub const SHADOW_X: u16 = 0x1023;
545    /// **Draft.** Shadow y offset (F32).
546    pub const SHADOW_Y: u16 = 0x1024;
547    /// **Draft.** Blur radius (F32). `.blur(radius:)`.
548    pub const BLUR_RADIUS: u16 = 0x1025;
549    /// **Draft.** Saturation (F32 0–1). `.saturation(_:)`.
550    pub const SATURATION: u16 = 0x1026;
551    /// **Draft.** Contrast (F32 0–1). `.contrast(_:)`.
552    pub const CONTRAST: u16 = 0x1027;
553    /// **Draft.** Brightness (F32 −1–1). `.brightness(_:)`.
554    pub const BRIGHTNESS: u16 = 0x1028;
555    /// **Draft.** Grayscale (F32 0–1). `.grayscale(_:)`.
556    pub const GRAYSCALE: u16 = 0x1029;
557    /// **Draft.** Hue rotation (F32 degrees). `.hueRotation(_:)`.
558    pub const HUE_ROTATION: u16 = 0x102A;
559    /// **Draft.** Color multiply (COLOR). `.colorMultiply(_:)`.
560    pub const COLOR_MULTIPLY: u16 = 0x102B;
561    /// **Draft.** Color invert (U8 0/1). `.colorInvert()`.
562    pub const COLOR_INVERT: u16 = 0x102C;
563    // Transform (draft)
564    /// **Draft.** Rotation (F32 degrees, counter-clockwise). `.rotationEffect(_:)`.
565    pub const ROTATION_DEGREES: u16 = 0x102D;
566    /// **Draft.** Uniform scale (F32). `.scaleEffect(_:)`.
567    pub const SCALE: u16 = 0x102E;
568    /// **Draft.** Whether the element participates in hit testing (U8 0/1).
569    /// `.allowsHitTesting(_:)`.
570    pub const ALLOWS_HIT_TESTING: u16 = 0x102F;
571    /// **Draft.** Accent/tint color (COLOR). `.tint(_:)`.
572    pub const TINT: u16 = 0x1030;
573    /// **Draft.** Presentation hint for structural swaps (F32 enum code):
574    /// `None`=0, `PlatformDefault`=1, `Fade`=2, `Slide`=3, `Scale`=4 — emitted
575    /// by a `NavigationContainer`/`Conditional.when` slot; the renderer may
576    /// animate the swap and must render normally when it ignores the hint.
577    pub const TRANSITION: u16 = 0x1031;
578    /// **Draft.** A `SIZE_THAT_FITS` slot's candidate fit-query (LIST): the
579    /// candidates' `minWidth` thresholds, ascending. The renderer measures its
580    /// allocated width and reports the derived fit index (`FIT_CHANGED`).
581    pub const FIT_QUERY: u16 = 0x1039;
582    /// **Draft.** Predefined typography (ENUM code, see [`crate::text_style`]):
583    /// `.font(.title2)` picks a whole design-system typography; the heading
584    /// styles (LargeTitle…Headline) imply a heading element on a `TEXT`.
585    /// Raw font modifiers (`FONT_SIZE`/`FONT_WEIGHT`/`FONT_FAMILY`/…) override
586    /// the visual on top and never imply a heading.
587    pub const TEXT_STYLE: u16 = 0x1032;
588    // Semantic (0x2000 range)
589    pub const ROLE: u16 = 0x2001;
590    pub const STATE: u16 = 0x2002;
591    pub const ENABLED: u16 = 0x2003;
592    pub const SELECTED: u16 = 0x2004;
593    /// `EVENT_LISTENERS` — a u32 bitmask (see [`crate::listener`]) declaring
594    /// which raw-input events a node wants the renderer to report. This is the
595    /// declarative signal the application sets so a renderer attaches native
596    /// input recognition to any element — not just buttons.
597    pub const EVENT_LISTENERS: u16 = 0x2005;
598    /// The current value of a value-bearing control (e.g. a `SLIDER`).
599    pub const VALUE: u16 = 0x2006;
600    /// The inclusive minimum of a value-bearing control's range (e.g. a `SLIDER`).
601    pub const MIN_VALUE: u16 = 0x2007;
602    /// The inclusive maximum of a value-bearing control's range (e.g. a `SLIDER`).
603    pub const MAX_VALUE: u16 = 0x2008;
604    /// The caption label of a `TEXT_FIELD` (a `STRING` property).
605    pub const LABEL: u16 = 0x200A;
606    /// The placeholder text of a `TEXT_FIELD` (a `STRING` property).
607    pub const PROMPT: u16 = 0x200B;
608    /// **Draft.** Stepper increment per press (F32).
609    pub const STEP_VALUE: u16 = 0x2009;
610    /// **Draft.** Control size (F32 enum): `Small`=0, `Regular`=1, `Large`=2.
611    pub const CONTROL_SIZE: u16 = 0x200C;
612    /// **Draft.** Masked-input token for a `TEXT_FIELD` (U8 0/1).
613    pub const IS_SECURE: u16 = 0x200D;
614    /// **Draft.** Determinate progress fraction of a `PROGRESS_VIEW` (F32).
615    pub const PROGRESS: u16 = 0x200E;
616    /// **Draft.** Indeterminate (activity) token of a `PROGRESS_VIEW` (U8 0/1).
617    pub const IS_INDETERMINATE: u16 = 0x200F;
618    /// **Draft.** Selected child index of a `PICKER`/`MENU` (U32).
619    pub const SELECTION: u16 = 0x2010;
620    /// **Draft.** Current color of a `COLOR_PICKER` (COLOR).
621    pub const COLOR_VALUE: u16 = 0x2012;
622    /// **Draft.** `DATE_PICKER` presentation (F32 enum): `Date`=0, `Time`=1,
623    /// `DateAndTime`=2.
624    pub const DATE_PICKER_MODE: u16 = 0x2013;
625    /// **Draft.** `PICKER` presentation (F32 enum): `Menu`=0, `Segmented`=1,
626    /// `Wheel`=2, `RadioGroup`=3.
627    pub const PICKER_STYLE: u16 = 0x2014;
628    /// **Draft.** Bound callback id; gates event delivery for this node.
629    pub const ACTION_ID: u16 = 0x2016;
630    /// **Draft.** Two-way binding id (control value ↔ app state).
631    pub const BINDING_ID: u16 = 0x2017;
632    /// **Draft.** Visual style token for a `TOGGLE` (F32 enum code):
633    /// `Switch`=0, `Checkbox`=1, `Button`=2.
634    pub const TOGGLE_STYLE: u16 = 0x2018;
635    /// **Draft.** Current navigation path (STRING, arenaRef), emitted by a
636    /// `NavigationContainer` slot; drives web URL sync.
637    pub const ROUTE: u16 = 0x2019;
638    /// **Draft.** Navigation back-stack depth (U32: number of destinations in
639    /// the app's path, including the current one — `push` increments, `pop`
640    /// decrements, `replace` leaves it unchanged). Emitted by a
641    /// `NavigationContainer` slot so native navigation adapters can reconcile
642    /// their page stack by depth (push/pop/replace/reset) instead of route
643    /// tags alone. See `spec/DSL.md §4.5` and `spec/MODIFIERS.md §6`.
644    pub const NAV_DEPTH: u16 = 0x201A;
645    /// **Draft.** Navigation chrome mode (F32 enum code) on a
646    /// `NavigationContainer` slot: `PlatformDefault`=0 (the renderer supplies
647    /// the navigation chrome — the native container where one exists, a
648    /// renderer-drawn back affordance on DOM), `Custom`=1 (the developer owns
649    /// all navigation UI and the renderer adds none). A container emits this
650    /// once at mount (it never varies per destination). See `spec/DSL.md §4.5`
651    /// and `spec/MODIFIERS.md §6`.
652    pub const NAV_CHROME: u16 = 0x201B;
653}
654
655/// The protocol value type for a property id.
656///
657/// Color-valued properties (COLOR, BACKGROUND_COLOR, BORDER_COLOR) are emitted
658/// with the `COLOR` value type; `EVENT_LISTENERS` and `BORDER_EDGES` are raw
659/// `U32` bitmasks; `LABEL`/`PROMPT`/`FONT_FAMILY` are `STRING`; `LINE_LIMIT`,
660/// `ACTION_ID`, and `BINDING_ID` are `U32`; all other constraint/style
661/// properties are `F32` (enum-valued properties like `ALIGNMENT`/`TOGGLE_STYLE`
662/// carry their numeric code as an `F32`). See `spec/MODIFIERS.md` for the
663/// canonical mapping.
664pub fn value_type_for(prop: u16) -> u8 {
665    match prop {
666        property_id::COLOR
667        | property_id::BACKGROUND_COLOR
668        | property_id::BORDER_COLOR
669        | property_id::COLOR_VALUE
670        | property_id::COLOR_MULTIPLY
671        | property_id::SHADOW_COLOR
672        | property_id::TINT => value_type::COLOR,
673        property_id::EVENT_LISTENERS | property_id::BORDER_EDGES => value_type::U32,
674        property_id::SELECTED
675        | property_id::IS_SECURE
676        | property_id::IS_INDETERMINATE
677        | property_id::VISIBLE
678        | property_id::ENABLED
679        | property_id::CLIPS_TO_BOUNDS
680        | property_id::UNDERLINE
681        | property_id::STRIKETHROUGH
682        | property_id::COLOR_INVERT
683        | property_id::ALLOWS_HIT_TESTING
684        | property_id::FIXED_SIZE_HORIZONTAL
685        | property_id::FIXED_SIZE_VERTICAL
686        | property_id::MEDIA_MUTED
687        | property_id::MEDIA_LOOP => value_type::U8,
688        property_id::LABEL
689        | property_id::PROMPT
690        | property_id::FONT_FAMILY
691        | property_id::IMAGE_SOURCE
692        | property_id::GRID_TRACKS
693        | property_id::MEDIA_SOURCE
694        | property_id::ICON_NAME
695        | property_id::ROUTE => value_type::STRING,
696        property_id::LINE_LIMIT
697        | property_id::SELECTION
698        | property_id::ACTION_ID
699        | property_id::BINDING_ID
700        | property_id::PLAYBACK_STATE
701        | property_id::MEDIA_CONTROLLER_ID
702        | property_id::NAV_DEPTH => value_type::U32,
703        property_id::FIT_QUERY => value_type::LIST,
704        _ => value_type::F32,
705    }
706}
707
708/// Special size constants for `WIDTH` / `HEIGHT`.
709pub mod size {
710    /// Fill the available space (parent main-axis remainder).
711    pub const FILL: f32 = -1.0;
712    /// Hug content (size to the intrinsic content).
713    pub const HUG_CONTENT: f32 = -2.0;
714}
715
716/// Semantic-role codes for the `ROLE` semantic property (`0x2001`).
717///
718/// Roles are protocol-semantic (carried as an `F32` enum code; see
719/// `spec/OPCODE.md` §semantic properties). The catalog is **semantic structure
720/// only** — interactive/control roles (button, link, checkbox, …) are NOT roles:
721/// they are intrinsic to the control components (a `BUTTON` renders as
722/// `<button>`, a `TOGGLE` as `<input type="checkbox">`, a `MENU` as
723/// `role="menu"`), and a custom-looking button is expressed with `Button` +
724/// `ButtonStyle`. The web renderer maps each semantic role onto its native
725/// element. Values MUST NOT be renumbered once released (wire version stays 1).
726pub mod role {
727    /// No semantic role (the default).
728    pub const NONE: u8 = 0;
729    /// A heading (ARIA `heading`; the HTML renderer uses `<hN>`, the level
730    /// driven by a `TEXT_STYLE` typography when present, default `<h2>`).
731    pub const HEADER: u8 = 1;
732    /// A paragraph (HTML `<p>`).
733    pub const PARAGRAPH: u8 = 2;
734    /// A list container (the HTML renderer uses `<ul>`).
735    pub const LIST: u8 = 3;
736    /// An item within a list (HTML `<li>`).
737    pub const LIST_ITEM: u8 = 4;
738    /// A summarising/complementary region (HTML `<section>`).
739    pub const SUMMARY: u8 = 5;
740    /// Site banner (HTML `<header>`).
741    pub const BANNER: u8 = 6;
742    /// Site navigation (HTML `<nav>`).
743    pub const NAVIGATION: u8 = 7;
744    /// The page's main content (HTML `<main>`).
745    pub const MAIN: u8 = 8;
746    /// Site footer / content info (HTML `<footer>`).
747    pub const CONTENT_INFO: u8 = 9;
748    /// Complementary/aside content (HTML `<aside>`).
749    pub const COMPLEMENTARY: u8 = 10;
750    /// A self-contained article (HTML `<article>`).
751    pub const ARTICLE: u8 = 11;
752    /// A thematically grouped section (HTML `<section>`).
753    pub const SECTION: u8 = 12;
754    /// A search region (HTML `<search>`).
755    pub const SEARCH: u8 = 13;
756}
757
758/// Predefined typography codes for the `TEXT_STYLE` property (see
759/// `spec/MODIFIERS.md`). Each value selects a whole typography from the design
760/// system (font size/weight/design). The **heading** styles (LargeTitle through
761/// Headline) imply a heading element when applied to a `TEXT`: the renderer
762/// emits `<h1>`–`<h5>` (the level comes from the style — no `ROLE` needed). The
763/// non-heading styles render as plain `<span>` text. A raw font modifier
764/// (`FONT_SIZE`, `FONT_WEIGHT`, `FONT_FAMILY`, …) never implies a heading — it
765/// only overrides the typography's visual.
766pub mod text_style {
767    /// The largest title style (heading level 1).
768    pub const LARGE_TITLE: u8 = 0;
769    /// A primary title (heading level 2).
770    pub const TITLE: u8 = 1;
771    /// A secondary title (heading level 3).
772    pub const TITLE2: u8 = 2;
773    /// A tertiary title (heading level 4).
774    pub const TITLE3: u8 = 3;
775    /// A headline (heading level 5).
776    pub const HEADLINE: u8 = 4;
777    /// A sub-headline (NOT a heading — plain `<span>` text).
778    pub const SUBHEADLINE: u8 = 5;
779    /// Body text (the default).
780    pub const BODY: u8 = 6;
781    /// A callout (slightly larger than body).
782    pub const CALLOUT: u8 = 7;
783    /// Footnote text.
784    pub const FOOTNOTE: u8 = 8;
785    /// Caption text.
786    pub const CAPTION: u8 = 9;
787    /// A second, smaller caption.
788    pub const CAPTION2: u8 = 10;
789}
790
791/// Canonical icon names (`ICON_NAME` values; spec/ICONS.md).
792///
793/// The spec is the **vocabulary contract only**: these names are the protocol
794/// `ICON_NAME` surface every renderer understands. The canonical-name →
795/// native-glyph mapping is **renderer-owned** (Adwaita on GTK, Remix filled on
796/// the web, SF Symbols on Apple) and lives in each renderer — never here, never
797/// in the spec.
798pub mod icon {
799    pub const HOME: &str = "home";
800    pub const SETTINGS: &str = "settings";
801    pub const SEARCH: &str = "search";
802    pub const MENU: &str = "menu";
803    pub const CLOSE: &str = "close";
804    pub const CLOUD: &str = "cloud";
805    pub const CHEVRON_LEFT: &str = "chevron-left";
806    pub const CHEVRON_RIGHT: &str = "chevron-right";
807    pub const CHEVRON_UP: &str = "chevron-up";
808    pub const CHEVRON_DOWN: &str = "chevron-down";
809    pub const ARROW_LEFT: &str = "arrow-left";
810    pub const ARROW_RIGHT: &str = "arrow-right";
811    pub const ADD: &str = "add";
812    pub const REMOVE: &str = "remove";
813    pub const CHECK: &str = "check";
814    pub const EDIT: &str = "edit";
815    pub const DELETE: &str = "delete";
816    pub const SAVE: &str = "save";
817    pub const SHARE: &str = "share";
818    pub const DOWNLOAD: &str = "download";
819    pub const UPLOAD: &str = "upload";
820    pub const REFRESH: &str = "refresh";
821    pub const PLAY: &str = "play";
822    pub const PAUSE: &str = "pause";
823    pub const STOP: &str = "stop";
824    pub const SKIP_BACK: &str = "skip-back";
825    pub const SKIP_FORWARD: &str = "skip-forward";
826    pub const VOLUME: &str = "volume";
827    pub const VOLUME_MUTE: &str = "volume-mute";
828    pub const SHUFFLE: &str = "shuffle";
829    pub const REPEAT: &str = "repeat";
830    pub const INFO: &str = "info";
831    pub const WARNING: &str = "warning";
832    pub const ERROR: &str = "error";
833    pub const SUCCESS: &str = "success";
834    pub const HEART: &str = "heart";
835    pub const STAR: &str = "star";
836    pub const USER: &str = "user";
837    pub const USERS: &str = "users";
838    pub const LOCK: &str = "lock";
839    pub const LOGOUT: &str = "logout";
840    pub const BELL: &str = "bell";
841    pub const FOLDER: &str = "folder";
842    pub const FILE: &str = "file";
843    pub const IMAGE: &str = "image";
844    pub const MUSIC: &str = "music";
845    pub const GRID: &str = "grid";
846    pub const LIST: &str = "list";
847    pub const FILTER: &str = "filter";
848
849    /// Every canonical name, in deterministic (spec, spec/ICONS.md) order.
850    pub const NAMES: &[&str] = &[
851        HOME, SETTINGS, SEARCH, MENU, CLOSE, CLOUD, CHEVRON_LEFT, CHEVRON_RIGHT,
852        CHEVRON_UP, CHEVRON_DOWN, ARROW_LEFT, ARROW_RIGHT, ADD, REMOVE, CHECK,
853        EDIT, DELETE, SAVE, SHARE, DOWNLOAD, UPLOAD, REFRESH, PLAY, PAUSE, STOP,
854        SKIP_BACK, SKIP_FORWARD, VOLUME, VOLUME_MUTE, SHUFFLE, REPEAT, INFO,
855        WARNING, ERROR, SUCCESS, HEART, STAR, USER, USERS, LOCK, LOGOUT, BELL,
856        FOLDER, FILE, IMAGE, MUSIC, GRID, LIST, FILTER,
857    ];
858
859    /// Whether `name` is a canonical `ICON_NAME` in the spec vocabulary.
860    pub fn is_canonical(name: &str) -> bool {
861        NAMES.contains(&name)
862    }
863}
864
865/// Sentinel for append positions (`index = u32::MAX`).
866pub const APPEND: u32 = u32::MAX;