Skip to main content

kui_core/schema/
doors.rs

1//! The verb table: one row per call an app or host makes (register a
2//! resource, move focus, open a menu, size a window) with its spelling in
3//! Rust, C, Odin, Node and Lua, or the reason a binding has none.
4//!
5//! A Rust caller uses the methods on `Ui`, `Core` and friends directly;
6//! this table exists so the bindings stay in step with them and with each
7//! other. Each binding's test checks its names against [`DOORS`], so a
8//! verb added to one binding is a row here with its other cells, or a red
9//! test. A [`Cell::No`] says once why a binding lacks a verb instead of
10//! every reader re-deriving it: Lua is a guest with a view-time env,
11//! Node's `Ctx` is a driver and its `KuiWindow` refuses input, C is both,
12//! and Odin's doors are C's. The Odin column is checked by the Odin
13//! binding's generator (packages/odin/gen), which reads this table through
14//! `examples/rust/tools/schema-dump.rs`.
15//!
16//! ```rust
17//! use kui_core::schema::{Cell, DOORS};
18//!
19//! let open_menu = DOORS.iter().find(|d| d.rust == "Ui::open_menu").expect("a verb");
20//! assert!(matches!(open_menu.c, Cell::Is(_)));
21//! ```
22
23/// One binding's cell.
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum Cell {
26    /// The binding has the verb under this name: a `kui_*` function in C;
27    /// a procedure of package `kui` in Odin; a method in Node, on both
28    /// classes unless prefixed `Ctx.` or `KuiWindow.`; a function on `env`
29    /// in Lua. Checked by the binding's test.
30    Is(&'static str),
31    /// The binding has the same thing in another form — a prop, a
32    /// reading of `env`, a callback, a constructor option — and the text
33    /// says which.
34    As(&'static str),
35    /// The binding does not have it, and this is why.
36    No(&'static str),
37}
38
39/// One verb.
40pub struct Door {
41    /// The Rust spelling: `Ui::x` (view-time), `Core::x` (a host between
42    /// frames), `SharedResources::x` (the session's registry, reached as
43    /// `core.resources`), `Tokens::x` (a table), `Launcher::x` (the runner).
44    pub rust: &'static str,
45    pub c: Cell,
46    /// A procedure in the Odin binding's package `kui` (packages/odin),
47    /// checked by its generator. Odin's doors are kui.h's, so its column
48    /// follows C's: the same function under its name without `kui_`, the
49    /// Odin spelling of what C has another way, and `No` where C has none.
50    pub odin: Cell,
51    pub node: Cell,
52    pub lua: Cell,
53    pub doc: &'static str,
54}
55
56use Cell::{As, Is, No};
57
58/// The reason most of Lua's column is `No`: a script's env is a *reading*
59/// the host hands it for one `view`, not a handle on the host.
60/// It declares a tree and answers events; what it registers, drives,
61/// times or reads back is the host's.
62pub const GUEST: &str = "a script is a guest in the host's frame (ADR 0014): its env is the view's reading, and registering, driving, pacing and reading back are the host's";
63
64/// The reason for Lua's `No` on the resource rows.
65const NO_HANDLE: &str = "a script owns no handle: the host registers and the script names the id it was given (`image { id = }`, `font = id`, `audio { src = id }`)";
66
67/// The reason for Odin's `No`: every Odin door is a kui.h function, so
68/// what C has no door for, Odin has none for either - for C's reason, in
69/// C's cell.
70const ODIN_IS_C: &str =
71    "Odin's doors are kui.h's (packages/odin): C has none, for the reason in C's cell";
72
73/// The reason for Node's `No` on the renderer rows (not done
74/// here).
75const NEVER_PAINTS: &str = "a Node host never paints: the renderer behind `KuiWindow` is the runner's, and a headless `Ctx` has none";
76
77pub const DOORS: &[Door] = &[
78    // -- Resources ---------------------------------------------------------
79    Door {
80        rust: "SharedResources::add_image",
81        c: Is("kui_image_add"),
82        odin: Is("image_add"),
83        node: Is("addImage"),
84        lua: No(NO_HANDLE),
85        doc: "Registers RGBA pixels and mints an id for `<image src>`.",
86    },
87    Door {
88        rust: "Core::update_image",
89        c: Is("kui_image_update"),
90        odin: Is("image_update"),
91        node: Is("updateImage"),
92        lua: No(NO_HANDLE),
93        doc: "Replaces the pixels behind a live id, keeping the id (ADR 0025).",
94    },
95    Door {
96        rust: "Core::remove_image",
97        c: Is("kui_image_remove"),
98        odin: Is("image_remove"),
99        node: Is("removeImage"),
100        lua: No(NO_HANDLE),
101        doc: "Drops an image; every window's atlas lets it go (backlog AR8).",
102    },
103    Door {
104        rust: "Core::image_pixels",
105        c: Is("kui_image_pixels"),
106        odin: Is("image_pixels"),
107        node: No(NEVER_PAINTS),
108        lua: No(NO_HANDLE),
109        doc: "The pixels behind a handle, for a renderer meeting a texture quad.",
110    },
111    Door {
112        rust: "Core::parse_path",
113        c: Is("kui_path_parse"),
114        odin: Is("path_parse"),
115        node: As("`d` on `<path>` is the string; the addon hands it to this parser"),
116        lua: As("`d` on `path { }` is the string; the host hands it to this parser"),
117        doc: "SVG path data to the flat op form a `path` draws (ADR 0040): one parser, so every binding draws the same shape.",
118    },
119    Door {
120        rust: "Core::add_fragment",
121        c: Is("kui_fragment_add"),
122        odin: Is("fragment_add"),
123        node: Is("addFragment"),
124        lua: No(NO_HANDLE),
125        doc: "Registers a WGSL function and mints an id for `<fragment src>` (ADR 0015).",
126    },
127    Door {
128        rust: "Core::remove_fragment",
129        c: Is("kui_fragment_remove"),
130        odin: Is("fragment_remove"),
131        node: Is("removeFragment"),
132        lua: No(NO_HANDLE),
133        doc: "Drops a fragment; the renderer drops its pipelines (backlog AR8).",
134    },
135    Door {
136        rust: "Core::fragment_module_source",
137        c: Is("kui_fragment_source"),
138        odin: Is("fragment_source"),
139        node: No(NEVER_PAINTS),
140        lua: No(NO_HANDLE),
141        doc: "The whole WGSL module behind a handle, which is what a renderer compiles.",
142    },
143    Door {
144        rust: "Core::add_font_data",
145        c: Is("kui_font_add"),
146        odin: Is("font_add"),
147        node: Is("addFont"),
148        lua: No(NO_HANDLE),
149        doc: "Registers a font's bytes and mints an id for `font`.",
150    },
151    Door {
152        rust: "Core::add_system_font",
153        c: Is("kui_font_add_system"),
154        odin: Is("font_add_system"),
155        node: Is("addSystemFont"),
156        lua: No(NO_HANDLE),
157        doc: "Registers an installed family by name.",
158    },
159    Door {
160        rust: "Core::load_font_file",
161        c: Is("kui_font_load_file"),
162        odin: Is("font_load_file"),
163        node: Is("loadFontFile"),
164        lua: No(NO_HANDLE),
165        doc: "Registers a font file by path.",
166    },
167    Door {
168        rust: "Core::load_fonts_dir",
169        c: Is("kui_font_load_dir"),
170        odin: Is("font_load_dir"),
171        node: Is("loadFontsDir"),
172        lua: No(NO_HANDLE),
173        doc: "Registers every font file in a directory.",
174    },
175    Door {
176        rust: "Core::reload_system_fonts",
177        c: Is("kui_font_reload_system"),
178        odin: Is("font_reload_system"),
179        node: Is("reloadSystemFonts"),
180        lua: No(GUEST),
181        doc: "Scans the system's fonts again, so a font installed while the app runs is found (the scan is otherwise once a process); returns how many faces came and went.",
182    },
183    Door {
184        rust: "Core::set_fallback_fonts",
185        c: Is("kui_font_set_fallback"),
186        odin: Is("font_set_fallback"),
187        node: Is("setFallbackFonts"),
188        lua: No(NO_HANDLE),
189        doc: "The fonts asked, in order, for a character the text's own family lacks, before the platform's fallback list (backlog F121).",
190    },
191    Door {
192        rust: "Core::fallback_fonts",
193        c: No("the list is the one the host set"),
194        odin: No(ODIN_IS_C),
195        node: No("the list is the one the host set"),
196        lua: No(NO_HANDLE),
197        doc: "The families `set_fallback_fonts` named, in order.",
198    },
199    Door {
200        rust: "Core::remove_font",
201        c: Is("kui_font_remove"),
202        odin: Is("font_remove"),
203        node: Is("removeFont"),
204        lua: No(NO_HANDLE),
205        doc: "Drops a font.",
206    },
207    Door {
208        rust: "Core::system_font_families",
209        c: Is("kui_font_families"),
210        odin: Is("font_families"),
211        node: Is("systemFontFamilies"),
212        lua: No(NO_HANDLE),
213        doc: "The installed family names `add_system_font` accepts.",
214    },
215    Door {
216        rust: "Core::system_fonts",
217        c: Is("kui_system_fonts"),
218        odin: Is("system_fonts"),
219        node: Is("systemFonts"),
220        lua: No(NO_HANDLE),
221        doc: "The same families, each with what the font database read off its faces: `monospaced` (every face fixed-pitch), `weights`, `italic` (backlog F97) — a font picker's monospaced-first list without a file loaded or a glyph shaped.",
222    },
223    Door {
224        rust: "Core::add_sound",
225        c: Is("kui_sound_add"),
226        odin: Is("sound_add"),
227        node: Is("addSound"),
228        lua: No(NO_HANDLE),
229        doc: "Registers a sound's bytes and mints an id for `<audio src>`, `clickSound` and `play`.",
230    },
231    Door {
232        rust: "Core::remove_sound",
233        c: Is("kui_sound_remove"),
234        odin: Is("sound_remove"),
235        node: Is("removeSound"),
236        lua: No(NO_HANDLE),
237        doc: "Drops a sound.",
238    },
239    Door {
240        rust: "Core::set_text_cache_budget",
241        c: Is("kui_set_text_cache_budget"),
242        odin: Is("set_text_cache_budget"),
243        node: Is("setTextCacheBudget"),
244        lua: No(GUEST),
245        doc: "The shaped-text cache's byte budget (backlog C16).",
246    },
247    Door {
248        rust: "Core::text_cache_bytes",
249        c: Is("kui_text_cache_bytes"),
250        odin: Is("text_cache_bytes"),
251        node: Is("textCacheBytes"),
252        lua: No(GUEST),
253        doc: "What the shaped-text cache holds.",
254    },
255    // -- Audio -------------------------------------------------------------
256    Door {
257        rust: "Ui::play",
258        c: Is("kui_play"),
259        odin: Is("play"),
260        node: Is("play"),
261        lua: No(
262            "a script owns no sound handle, and its env is the view's: a playback started there would start again every frame — `audio { src = id }` is the declarative form, and what a script has",
263        ),
264        doc: "Starts a playback of a registered sound, outside any node; answers the playback id.",
265    },
266    Door {
267        rust: "Core::stop",
268        c: Is("kui_stop"),
269        odin: Is("stop"),
270        node: Is("stop"),
271        lua: No("as `play`: a script declares `audio { }` and stops it by not declaring it"),
272        doc: "Stops a playback, with an optional fade.",
273    },
274    Door {
275        rust: "Core::set_volume",
276        c: Is("kui_set_volume"),
277        odin: Is("set_volume"),
278        node: Is("setVolume"),
279        lua: As("`audio { volume = }` applies live"),
280        doc: "A playback's volume, with an optional tween.",
281    },
282    Door {
283        rust: "Core::pause",
284        c: Is("kui_pause"),
285        odin: Is("pause"),
286        node: Is("pause"),
287        lua: As("`audio { paused = true }` applies live"),
288        doc: "Pauses a playback.",
289    },
290    Door {
291        rust: "Core::resume",
292        c: Is("kui_resume"),
293        odin: Is("resume"),
294        node: Is("resume"),
295        lua: As("`audio { paused = false }`"),
296        doc: "Resumes a paused playback.",
297    },
298    Door {
299        rust: "Core::set_master_volume",
300        c: Is("kui_set_master_volume"),
301        odin: Is("set_master_volume"),
302        node: Is("setMasterVolume"),
303        lua: No(GUEST),
304        doc: "The device's master volume, with an optional tween.",
305    },
306    // -- Assistive ---------------------------------------------------------
307    Door {
308        rust: "Ui::announce",
309        c: Is("kui_announce"),
310        odin: Is("announce"),
311        node: Is("announce"),
312        lua: Is("announce"),
313        doc: "Says something once with no node behind it (ADR 0001).",
314    },
315    Door {
316        rust: "Core::take_announcements",
317        c: Is("kui_take_announcements"),
318        odin: Is("take_announcements"),
319        node: Is("Ctx.announcements"),
320        lua: No(GUEST),
321        doc: "Drains what was announced, for a host bridging assistive technology; a `KuiWindow`'s bridge is the runner's.",
322    },
323    Door {
324        rust: "Core::access_tree",
325        c: Is("kui_access_tree"),
326        odin: Is("access_tree"),
327        node: Is("accessTree"),
328        lua: No(GUEST),
329        doc: "The access tree of the last finished frame (ADR 0001).",
330    },
331    // -- Focus -------------------------------------------------------------
332    Door {
333        rust: "Ui::focus",
334        c: Is("kui_focus"),
335        odin: Is("focus"),
336        node: Is("focus"),
337        lua: Is("set_focus"),
338        doc: "Moves focus to a node now; an app's move stands over a modal's restore (backlog AR17). `keyFocus` is the declarative, edge-triggered form.",
339    },
340    Door {
341        rust: "Ui::blur",
342        c: As("`kui_focus(ctx, 0)`"),
343        odin: As("`focus(ui, 0)`"),
344        node: Is("blur"),
345        lua: Is("blur"),
346        doc: "Drops focus.",
347    },
348    Door {
349        rust: "Ui::focus_next",
350        c: Is("kui_focus_next"),
351        odin: Is("focus_next"),
352        node: Is("focusNext"),
353        lua: Is("focus_next"),
354        doc: "Steps the Tab ring forward (ADR 0002).",
355    },
356    Door {
357        rust: "Ui::focus_prev",
358        c: As("`kui_focus_next(ctx, false)`"),
359        odin: As("`focus_next(ui, false)`"),
360        node: Is("focusPrev"),
361        lua: Is("focus_prev"),
362        doc: "Steps the Tab ring backward.",
363    },
364    Door {
365        rust: "Ui::focus_region",
366        c: Is("kui_focus_region"),
367        odin: Is("focus_region"),
368        node: Is("focusRegion"),
369        lua: Is("focus_region"),
370        doc: "Enters a `focusRegion`'s ring, or leaves it for the main one (ADR 0022).",
371    },
372    Door {
373        rust: "Ui::region",
374        c: Is("kui_region"),
375        odin: Is("region"),
376        node: Is("region"),
377        lua: As("`env.region`, a reading"),
378        doc: "The region in effect.",
379    },
380    Door {
381        rust: "Ui::focused",
382        c: Is("kui_focused"),
383        odin: Is("focused"),
384        node: Is("focused"),
385        lua: As("`env.focus`, a reading"),
386        doc: "The focused node's key.",
387    },
388    Door {
389        rust: "Ui::is_focused",
390        c: Is("kui_is_focused"),
391        odin: Is("is_focused"),
392        node: Is("isFocused"),
393        lua: Is("is_focused"),
394        doc: "Whether a node has focus.",
395    },
396    Door {
397        rust: "Ui::focus_visible",
398        c: Is("kui_focus_visible"),
399        odin: Is("focus_visible"),
400        node: Is("focusVisible"),
401        lua: As("`env.focus_visible`, a reading"),
402        doc: "Whether focus came from the keyboard and the ring should show.",
403    },
404    Door {
405        rust: "Ui::key_of",
406        c: Is("kui_key_of"),
407        odin: Is("key_of"),
408        node: Is("keyOf"),
409        lua: As("every query and verb takes the label itself (`key_query`)"),
410        doc: "The key a label names this frame.",
411    },
412    Door {
413        rust: "Core::label_of",
414        c: No(
415            "the label is the app's own word for the node, and every door names a node by it or by the key an event carried; the one reader is the devtools' inspector, in the core",
416        ),
417        odin: No(ODIN_IS_C),
418        node: No("the same reason as C's"),
419        lua: No("the same reason as C's"),
420        doc: "The label a key was opened under.",
421    },
422    Door {
423        rust: "Ui::caret_visible",
424        c: Is("kui_caret_visible"),
425        odin: Is("caret_visible"),
426        node: Is("caretVisible"),
427        lua: As("`env.caret_visible`, a reading"),
428        doc: "The blink phase a custom editor draws its caret on.",
429    },
430    Door {
431        rust: "Core::set_caret_visible",
432        c: Is("kui_set_caret_visible"),
433        odin: Is("set_caret_visible"),
434        node: Is("setCaretVisible"),
435        lua: No(GUEST),
436        doc: "The host's blink clock writes the phase.",
437    },
438    Door {
439        rust: "Core::has_caret",
440        c: Is("kui_has_caret"),
441        odin: Is("has_caret"),
442        node: Is("hasCaret"),
443        lua: No(GUEST),
444        doc: "Whether anything focused draws a caret to blink — a `caretSolid` line's is not one — which arms a host's blink clock.",
445    },
446    Door {
447        rust: "Core::caret_stamp",
448        c: Is("kui_caret_stamp"),
449        odin: Is("caret_stamp"),
450        node: As(
451            "the loop in `index.js` runs the blink from `nextDeadlineMs`; a headless `Ctx` never blinks",
452        ),
453        lua: No(GUEST),
454        doc: "Changes when the caret moves or focus does, which re-arms the clock solid.",
455    },
456    // -- Queries -----------------------------------------------------------
457    Door {
458        rust: "Ui::is_hovered",
459        c: Is("kui_is_hovered"),
460        odin: Is("is_hovered"),
461        node: Is("isHovered"),
462        lua: Is("is_hovered"),
463        doc: "Whether the pointer is over a node.",
464    },
465    Door {
466        rust: "Ui::is_pressed",
467        c: Is("kui_is_pressed"),
468        odin: Is("is_pressed"),
469        node: Is("isPressed"),
470        lua: Is("is_pressed"),
471        doc: "Whether a press started on a node and the pointer is still over it.",
472    },
473    Door {
474        rust: "Ui::is_drop_target",
475        c: Is("kui_is_drop_target"),
476        odin: Is("is_drop_target"),
477        node: Is("isDropTarget"),
478        lua: Is("is_drop_target"),
479        doc: "Whether files dragged in from the OS are over a node (ADR 0031) — for drop-dependent layout; the colour is `drop_bg`.",
480    },
481    Door {
482        rust: "Core::drop_target",
483        c: Is("kui_drop_target"),
484        odin: Is("drop_target"),
485        node: Is("dropTarget"),
486        lua: Is("drop_target"),
487        doc: "The drop zone the dragged files are over, if any — what a driver answers the OS with, and what a test reads to say a zone was found (ADR 0031, decision 5).",
488    },
489    Door {
490        rust: "Ui::is_group_hovered",
491        c: As(
492            "`hoverBg` / `pressedBg` on a `hoverGroup` member paint it; the reader is what the Rust widgets ask when they paint by hand",
493        ),
494        odin: As("`hover_bg` / `pressed_bg` on a `hover_group` member paint it"),
495        node: As("the same form as C's"),
496        lua: As("the same form as C's"),
497        doc: "Whether any member of a hover group is hovered (`is_group_pressed` the same for a press).",
498    },
499    Door {
500        rust: "Core::cursor",
501        c: No("the pointer's position is the driver's own fact — it injected it"),
502        odin: No(ODIN_IS_C),
503        node: No("the same reason as C's"),
504        lua: No("the same reason as C's, one step removed"),
505        doc: "Where the pointer is, in logical viewport px.",
506    },
507    Door {
508        rust: "Core::cursor_shape",
509        c: Is("kui_cursor_shape"),
510        odin: Is("cursor_shape"),
511        node: Is("cursorShape"),
512        lua: No(GUEST),
513        doc: "The pointer shape the frame asks for, which the host sets on its window.",
514    },
515    Door {
516        rust: "Ui::layout_of",
517        c: Is("kui_layout_of"),
518        odin: Is("layout_of"),
519        node: Is("layoutOf"),
520        lua: Is("layout_of"),
521        doc: "Where layout put a node last frame (backlog C26).",
522    },
523    Door {
524        rust: "Ui::scroll_offset",
525        c: Is("kui_scroll_offset"),
526        odin: Is("scroll_offset"),
527        node: Is("scrollOffset"),
528        lua: Is("scroll_offset"),
529        doc: "A scrolling node's offset.",
530    },
531    Door {
532        rust: "Ui::scroll_geometry",
533        c: Is("kui_scroll_geometry"),
534        odin: Is("scroll_geometry"),
535        node: Is("scrollGeometry"),
536        lua: Is("scroll_geometry"),
537        doc: "A scrolling node's viewport and content sizes.",
538    },
539    Door {
540        rust: "Ui::set_scroll",
541        c: Is("kui_set_scroll"),
542        odin: Is("set_scroll"),
543        node: Is("setScroll"),
544        lua: Is("set_scroll"),
545        doc: "Scrolls a node to an offset.",
546    },
547    Door {
548        rust: "Ui::shift_scroll",
549        c: Is("kui_shift_scroll"),
550        odin: Is("shift_scroll"),
551        node: Is("shiftScroll"),
552        lua: Is("shift_scroll"),
553        doc: "Moves a node's scroll by content that moved under it, with no ease: a variable-height list's anchor (backlog C46).",
554    },
555    Door {
556        rust: "Ui::reveal",
557        c: Is("kui_reveal"),
558        odin: Is("reveal"),
559        node: Is("reveal"),
560        lua: Is("reveal"),
561        doc: "Scrolls whatever encloses a node until it is in view.",
562    },
563    Door {
564        rust: "Ui::text_hit",
565        c: Is("kui_text_hit"),
566        odin: Is("text_hit"),
567        node: Is("textHit"),
568        lua: Is("text_hit"),
569        doc: "The byte and line under a point in a node's text (backlog C18).",
570    },
571    Door {
572        rust: "Ui::caret_rect",
573        c: Is("kui_caret_rect"),
574        odin: Is("caret_rect"),
575        node: Is("caretRect"),
576        lua: Is("caret_rect"),
577        doc: "The caret rect for a byte offset in a node's text.",
578    },
579    Door {
580        rust: "Core::ime_rect",
581        c: Is("kui_ime_rect"),
582        odin: Is("ime_rect"),
583        node: Is("imeRect"),
584        lua: No(GUEST),
585        doc: "Where the OS candidate window goes, which the host hands to the platform (backlog C17).",
586    },
587    Door {
588        rust: "Ui::measure_text",
589        c: Is("kui_measure_text"),
590        odin: Is("measure_text"),
591        node: Is("measureText"),
592        lua: Is("measure_text"),
593        doc: "Shapes text in a style at a width and answers its size and line count.",
594    },
595    Door {
596        rust: "Ui::measure_rich_text",
597        c: Is("kui_measure_rich_text"),
598        odin: Is("measure_rich_text"),
599        node: As("`measureText` takes spans too"),
600        lua: As("`measure_text` takes spans too"),
601        doc: "The same for spans, shaped as one paragraph.",
602    },
603    Door {
604        rust: "Ui::edit_text",
605        c: Is("kui_edit_text"),
606        odin: Is("edit_text"),
607        node: Is("editText"),
608        lua: Is("edit_text"),
609        doc: "An editor's text, by key or by label.",
610    },
611    Door {
612        rust: "Ui::set_edit_text",
613        c: Is("kui_edit_set_text"),
614        odin: Is("edit_set_text"),
615        node: Is("setEditText"),
616        lua: Is("set_edit_text"),
617        doc: "Replaces an editor's text, caret at the end.",
618    },
619    Door {
620        rust: "Ui::set_edit_text_by_label",
621        c: Is("kui_edit_set_text_label"),
622        odin: Is("edit_set_text_label"),
623        node: As("`setEditText` takes the label too"),
624        lua: As("`set_edit_text` takes the label too"),
625        doc: "The same by the label an editor's `key` declares, which reaches one the frame is about to declare (backlog AR26).",
626    },
627    Door {
628        rust: "Core::animating",
629        c: Is("kui_animating"),
630        odin: Is("animating"),
631        node: Is("animating"),
632        lua: No(GUEST),
633        doc: "Whether the last frame left a transition mid-flight, so the host draws another without waiting for input.",
634    },
635    Door {
636        rust: "Core::owed",
637        c: Is("kui_owed"),
638        odin: Is("owed"),
639        node: Is("owed"),
640        lua: No(GUEST),
641        doc: "The same by kind — a finite transition, a keyframe cycle, a departing ghost, a requested frame, an autoscroll — so a test can wait for the transitions to run out under a cycle that never ends; Node's loop has `quiet()` for that wait (backlog F64).",
642    },
643    Door {
644        rust: "Core::set_frame_trace",
645        c: Is("kui_set_frame_trace"),
646        odin: Is("set_frame_trace"),
647        node: Is("setFrameTrace"),
648        lua: No(GUEST),
649        doc: "Turns on the trace of why frames run: who holds an owed frame, and whether a frame changed what is drawn (backlog F111).",
650    },
651    Door {
652        rust: "Core::frame_cause",
653        c: Is("kui_frame_cause"),
654        odin: Is("frame_cause"),
655        node: Is("frameCause"),
656        lua: No(GUEST),
657        doc: "Why the frame being built runs: the input it answers by kind, what the driver noted, and `owed` after a frame that owed one (backlog F111).",
658    },
659    Door {
660        rust: "Core::begin_frame_cause",
661        c: No(
662            "a C host's view runs between `kui_frame_begin` and `kui_frame_finish`, inside the frame it builds, so it reads that frame already",
663        ),
664        odin: No(ODIN_IS_C),
665        node: Is("Ctx.beginFrameCause"),
666        lua: No(GUEST),
667        doc: "Starts the next frame's record ahead of its `begin_frame`, for a driver whose view runs before the frame it is for — Node's loop — so `frame_cause` and `owed_by` read from the view answer that frame (backlog RG81).",
668    },
669    Door {
670        rust: "Core::note_frame_cause",
671        c: Is("kui_note_frame_cause"),
672        odin: Is("note_frame_cause"),
673        node: No(
674            "the drivers that note a reason are kui-native's, which a `KuiWindow` runs on; a `Ctx` driven by hand has nothing but the input the core already records",
675        ),
676        lua: No(GUEST),
677        doc: "A driver adds a reason the core cannot see — a wake, a resize, a blink, a retry — to the next frame's (backlog F111).",
678    },
679    Door {
680        rust: "Core::owed_by",
681        c: No(
682            "lists of named holders are strings the library would own across calls, an [out-array] struct and an ABI bump for a reading that is a debugging aid; a C host reads the kinds from `kui_owed`",
683        ),
684        odin: No(ODIN_IS_C),
685        node: Is("owedBy"),
686        lua: No(GUEST),
687        doc: "Who holds the frame the last one left owed: `owed` with the nodes, slots and calling lines named (backlog F111).",
688    },
689    Door {
690        rust: "Core::frame_unchanged",
691        c: Is("kui_frame_unchanged"),
692        odin: Is("frame_unchanged"),
693        node: Is("frameUnchanged"),
694        lua: No(GUEST),
695        doc: "Whether the last finished frame drew exactly what the one before drew, traced (backlog F111).",
696    },
697    Door {
698        rust: "Ui::request_frame",
699        c: As("`animate` on a node, and `kui_animating` for the driver to read"),
700        odin: As("`animate` on a node, and `animating` for the driver to read"),
701        node: As("the same form as C's"),
702        lua: As("the same form as C's"),
703        doc: "Asks for a frame after this one; the driver paces off `animating()`.",
704    },
705    Door {
706        rust: "Ui::modifiers",
707        c: No(
708            "the held modifiers ride on every key and pointer event's `mods`; the reader is what the stock editor's Shift-drag asks, inside the core",
709        ),
710        odin: No(ODIN_IS_C),
711        node: No("the same reason as C's"),
712        lua: No("the same reason as C's"),
713        doc: "The modifier keys held now.",
714    },
715    // -- Selection ---------------------------------------------------------
716    Door {
717        rust: "Ui::selection_text",
718        c: Is("kui_selection_text"),
719        odin: Is("selection_text"),
720        node: Is("selectionText"),
721        lua: Is("selection_text"),
722        doc: "The window's selected text — a scope's, a grid's or the focused editor's.",
723    },
724    Door {
725        rust: "Ui::selection_html",
726        c: Is("kui_selection_html"),
727        odin: Is("selection_html"),
728        node: Is("selectionHtml"),
729        lua: Is("selection_html"),
730        doc: "The same with the formatting the text declared.",
731    },
732    Door {
733        rust: "Ui::selection_ends",
734        c: Is("kui_selection_ends"),
735        odin: Is("selection_ends"),
736        node: Is("selectionEnds"),
737        lua: Is("selection_ends"),
738        doc: "A text selection's anchor and focus as row indices and bytes (ADR 0029).",
739    },
740    Door {
741        rust: "Ui::cell_selection",
742        c: Is("kui_cell_selection"),
743        odin: Is("cell_selection"),
744        node: Is("cellSelection"),
745        lua: Is("cell_selection"),
746        doc: "A `cells` grid's selection: its ends as absolute lines and columns, and whether it is a block (ADR 0017 §4).",
747    },
748    Door {
749        rust: "Ui::select_all_in",
750        c: Is("kui_select_all_in"),
751        odin: Is("select_all_in"),
752        node: Is("selectAllIn"),
753        lua: Is("select_all_in"),
754        doc: "Select All, scoped to a `selectable` node or a grid.",
755    },
756    Door {
757        rust: "Ui::clear_selection",
758        c: Is("kui_clear_selection"),
759        odin: Is("clear_selection"),
760        node: Is("clearSelection"),
761        lua: Is("clear_selection"),
762        doc: "Drops the window's selection.",
763    },
764    Door {
765        rust: "Ui::request_copy",
766        c: Is("kui_request_copy"),
767        odin: Is("request_copy"),
768        node: Is("requestCopy"),
769        lua: Is("request_copy"),
770        doc: "Asks for the selection as a copy, which may come back as a `selectionrange` question.",
771    },
772    Door {
773        rust: "Ui::answer_selection_range",
774        c: Is("kui_answer_selection_range"),
775        odin: Is("answer_selection_range"),
776        node: Is("answerSelectionRange"),
777        lua: Is("answer_selection_range"),
778        doc: "The app's answer to that question.",
779    },
780    Door {
781        rust: "Ui::set_clipboard",
782        c: Is("kui_set_clipboard"),
783        odin: Is("set_clipboard"),
784        node: Is("setClipboard"),
785        lua: Is("set_clipboard"),
786        doc: "A key sink's own Ctrl-C: posts a clipboard action for the host (backlog C33).",
787    },
788    Door {
789        rust: "Ui::set_clipboard_secret",
790        c: Is("kui_set_clipboard_secret"),
791        odin: Is("set_clipboard_secret"),
792        node: Is("setClipboardSecret"),
793        lua: Is("set_clipboard_secret"),
794        doc: "Posts a secret for the clipboard, which the host writes marked concealed and transient the way a password manager does, so no clipboard manager shows or keeps it (backlog F84).",
795    },
796    Door {
797        rust: "Ui::request_paste",
798        c: Is("kui_request_paste"),
799        odin: Is("request_paste"),
800        node: Is("requestPaste"),
801        lua: Is("request_paste"),
802        doc: "A key sink's own Ctrl-V: the clipboard comes back as a commit, marked `concealed` / `transient` when the pasteboard said so (backlog F84). One ask at a time — a second while one is unanswered is dropped.",
803    },
804    Door {
805        rust: "Ui::awaiting_paste",
806        c: Is("kui_awaiting_paste"),
807        odin: Is("awaiting_paste"),
808        node: Is("awaitingPaste"),
809        lua: Is("awaiting_paste"),
810        doc: "Whether a paste asked for is still unanswered (backlog AR34).",
811    },
812    Door {
813        rust: "Ui::request_files",
814        c: Is("kui_request_files"),
815        odin: Is("request_files"),
816        node: Is("requestFiles"),
817        lua: Is("request_files"),
818        doc: "Asks for the platform's Open, Save or folder dialog; the answer is a `files` event to whoever asked. One at a time — a second while one is out is dropped (backlog C51).",
819    },
820    Door {
821        rust: "Ui::awaiting_files",
822        c: Is("kui_awaiting_files"),
823        odin: Is("awaiting_files"),
824        node: Is("awaitingFiles"),
825        lua: Is("awaiting_files"),
826        doc: "Whether a file dialog asked for is still unanswered.",
827    },
828    Door {
829        rust: "Core::take_file_requests",
830        c: As("`kui_take_file_request`, then `kui_file_request_filter` per filter"),
831        odin: As("`take_file_request`, then `file_request_filter` per filter"),
832        node: Is("takeFileRequests"),
833        lua: No(GUEST),
834        doc: "Drains the dialog asked for, for a host that shows it itself; the runner does. The answer goes back as input (`Ctx.answerFiles`, `kui_input_files`).",
835    },
836    Door {
837        rust: "Core::set_lookup_available",
838        c: Is("kui_set_lookup_available"),
839        odin: Is("set_lookup_available"),
840        node: Is("setLookupAvailable"),
841        lua: No(GUEST),
842        doc: "Whether the host can show the platform's definition panel, which decides whether Look Up is offered.",
843    },
844    // -- Menus -------------------------------------------------------------
845    Door {
846        rust: "Ui::open_menu",
847        c: Is("kui_open_menu"),
848        odin: Is("open_menu"),
849        node: Is("openMenu"),
850        lua: Is("open_menu"),
851        doc: "Opens a context menu on a node at a point.",
852    },
853    Door {
854        rust: "Ui::close_menu",
855        c: Is("kui_close_menu"),
856        odin: Is("close_menu"),
857        node: Is("closeMenu"),
858        lua: Is("close_menu"),
859        doc: "Closes it.",
860    },
861    Door {
862        rust: "Core::take_menu_actions",
863        c: Is("kui_take_menu_action"),
864        odin: Is("take_menu_action"),
865        node: Is("takeMenuActions"),
866        lua: As(
867            "a chosen row comes back as a `menu` event on the node; the clipboard actions are the host's",
868        ),
869        doc: "Drains what a menu (or a chord, or the standard bar) asked of the host: a clipboard write, a paste, a Look Up.",
870    },
871    Door {
872        rust: "Core::menu",
873        c: As(
874            "`kui_menu_item_count` / `kui_menu_item`, one row at a time, and a submenu's by path with `kui_menu_submenu_count` / `kui_menu_item_path`",
875        ),
876        odin: As(
877            "`menu_item_count` / `menu_item`, one row at a time, and a submenu's by path with `menu_submenu_count` / `menu_item_path`",
878        ),
879        node: Is("menu"),
880        lua: No(GUEST),
881        doc: "The open menu, for a host showing it natively.",
882    },
883    Door {
884        rust: "Core::set_native_menus",
885        c: Is("kui_set_native_menus"),
886        odin: Is("set_native_menus"),
887        node: Is("setNativeMenus"),
888        lua: No(GUEST),
889        doc: "Whether the host shows menus itself; the core then draws none.",
890    },
891    Door {
892        rust: "Core::activate_menu_item",
893        c: Is("kui_activate_menu_item"),
894        odin: Is("activate_menu_item"),
895        node: Is("activateMenuItem"),
896        lua: No(GUEST),
897        doc: "Reports that the host's own menu chose a row; a row that cannot be chosen (disabled, a separator) is refused and the menu stays open.",
898    },
899    Door {
900        rust: "Core::activate_menu_path",
901        c: Is("kui_activate_menu_path"),
902        odin: Is("activate_menu_path"),
903        node: Is("activateMenuPath"),
904        lua: No(GUEST),
905        doc: "`activate_menu_item` for a row inside a submenu, by its path through the rows' submenus (backlog F128); a row that opens a submenu is refused.",
906    },
907    Door {
908        rust: "Core::menu_bar",
909        c: As(
910            "`kui_menu_bar_menu_count` / `kui_menu_bar_menu` / `kui_menu_bar_item`, one row at a time, and a submenu's by path with `kui_menu_bar_submenu_count` / `kui_menu_bar_item_path`",
911        ),
912        odin: As(
913            "`menu_bar_menu_count` / `menu_bar_menu` / `menu_bar_item`, one row at a time, and a submenu's by path with `menu_bar_submenu_count` / `menu_bar_item_path`",
914        ),
915        node: Is("menuBar"),
916        lua: No(GUEST),
917        doc: "The declared menu bar, for a host handing it to the OS.",
918    },
919    Door {
920        rust: "Core::set_native_menu_bar",
921        c: Is("kui_set_native_menu_bar"),
922        odin: Is("set_native_menu_bar"),
923        node: Is("setNativeMenuBar"),
924        lua: No(GUEST),
925        doc: "Whether the host owns the bar; the core then draws no strip.",
926    },
927    Door {
928        rust: "Core::activate_menu_bar_item",
929        c: Is("kui_activate_menu_bar_item"),
930        odin: Is("activate_menu_bar_item"),
931        node: Is("activateMenuBarItem"),
932        lua: No(GUEST),
933        doc: "Reports that the OS bar chose a row.",
934    },
935    Door {
936        rust: "Core::activate_menu_bar_path",
937        c: Is("kui_activate_menu_bar_path"),
938        odin: Is("activate_menu_bar_path"),
939        node: Is("activateMenuBarPath"),
940        lua: No(GUEST),
941        doc: "`activate_menu_bar_item` for a row inside a submenu, by its path (backlog F128).",
942    },
943    // -- Windows -----------------------------------------------------------
944    Door {
945        rust: "Ui::window",
946        c: Is("kui_window_declare"),
947        odin: Is("window_declare"),
948        node: As("the root's `windows` prop"),
949        lua: As("the root's `windows` field"),
950        doc: "Declares that a named window exists this frame (ADR 0003 step 3).",
951    },
952    Door {
953        rust: "Core::windows",
954        c: As("the ids arrive on `KUI_CMD_OPEN`; a host keeps the list it opened"),
955        odin: As(
956            "the ids arrive on `take_window_command`'s `.Open`; a host keeps the list it opened",
957        ),
958        node: Is("windows"),
959        lua: No(GUEST),
960        doc: "The names of the windows open now.",
961    },
962    Door {
963        rust: "Ui::window_name",
964        c: Is("kui_ctx_window_name"),
965        odin: Is("ctx_window_name"),
966        node: Is("windowName"),
967        lua: As("`env.window.name`, a reading"),
968        doc: "The name of the window this context draws.",
969    },
970    Door {
971        rust: "Ui::set_window_size",
972        c: Is("kui_set_window_size"),
973        odin: Is("set_window_size"),
974        node: Is("setWindowSize"),
975        lua: Is("set_window_size"),
976        doc: "Asks the driver to resize a window.",
977    },
978    Door {
979        rust: "Ui::focus_window",
980        c: Is("kui_focus_window"),
981        odin: Is("focus_window"),
982        node: Is("focusWindow"),
983        lua: Is("focus_window"),
984        doc: "Asks the driver to bring a window to the front.",
985    },
986    Door {
987        rust: "Ui::window_title",
988        c: Is("kui_window_title"),
989        odin: Is("window_title"),
990        node: As("the root's `title` prop"),
991        lua: As("the root's `title` field"),
992        doc: "Declares the window's title this frame.",
993    },
994    Door {
995        rust: "Ui::always_on_top",
996        c: Is("kui_set_always_on_top"),
997        odin: Is("set_always_on_top"),
998        node: As("the root's `alwaysOnTop` prop"),
999        lua: As("the root's `always_on_top` field"),
1000        doc: "Declares that the window sits above every other app's this frame (backlog C30).",
1001    },
1002    Door {
1003        rust: "Ui::secure_input",
1004        c: Is("kui_set_secure_input"),
1005        odin: Is("set_secure_input"),
1006        node: As("the root's `secureInput` prop"),
1007        lua: As("the root's `secure_input` field"),
1008        doc: "Declares that this frame wants secure keyboard entry while the window has the keyboard — a password prompt (backlog F85).",
1009    },
1010    Door {
1011        rust: "Ui::option_as_alt",
1012        c: Is("kui_set_option_as_alt"),
1013        odin: Is("set_option_as_alt"),
1014        node: As("the root's `optionAsAlt` prop"),
1015        lua: As("the root's `option_as_alt` field"),
1016        doc: "Declares which Option keys act as Alt in this window on macOS, so a dead key like ⌥u arrives as `<A-u>` (backlog F113).",
1017    },
1018    Door {
1019        rust: "Ui::ime_off",
1020        c: Is("kui_set_ime_off"),
1021        odin: Is("set_ime_off"),
1022        node: As("the root's `imeOff` prop"),
1023        lua: As("the root's `ime_off` field"),
1024        doc: "Declares that this window takes the keyboard as keys, with the input method off — no composition, and on a Mac no dead keys and no press-and-hold, so a held letter repeats (backlog F125).",
1025    },
1026    Door {
1027        rust: "Ui::window_command",
1028        c: As(
1029            "the chrome roles (`KuiSpec.window_role`) are the door; the verb is what `widgets::window_buttons` lowers to",
1030        ),
1031        odin: As("`window` on a node (`Spec.window`, the chrome roles) is the door"),
1032        node: As(
1033            "`KuiWindow.close()` for the one command the runner takes from outside a frame; the rest are `windowRole`",
1034        ),
1035        lua: As("`window_role`"),
1036        doc: "Minimize, toggle-maximize, start-drag, close — what a chrome node asks for on a press.",
1037    },
1038    Door {
1039        rust: "Core::window_title",
1040        c: Is("kui_window_title_get"),
1041        odin: Is("window_title_get"),
1042        node: Is("Ctx.windowTitle"),
1043        lua: No(GUEST),
1044        doc: "What the frame declared, for a driver applying it; a `KuiWindow` applies its own.",
1045    },
1046    Door {
1047        rust: "Core::always_on_top",
1048        c: Is("kui_always_on_top_get"),
1049        odin: Is("always_on_top_get"),
1050        node: Is("Ctx.alwaysOnTop"),
1051        lua: As("`env.window.always_on_top`, a reading"),
1052        doc: "The same for the level.",
1053    },
1054    Door {
1055        rust: "Core::secure_input",
1056        c: Is("kui_secure_input_get"),
1057        odin: Is("secure_input_get"),
1058        node: Is("Ctx.secureInput"),
1059        lua: No(GUEST),
1060        doc: "The same for the secure-input ask: what a driver with its own loop reads to make the platform call; the runner makes it for a `KuiWindow` and `kui_run`.",
1061    },
1062    Door {
1063        rust: "Core::option_as_alt",
1064        c: Is("kui_option_as_alt_get"),
1065        odin: Is("option_as_alt_get"),
1066        node: Is("Ctx.optionAsAlt"),
1067        lua: No(GUEST),
1068        doc: "The same for the Option-as-Alt ask: what a driver with its own loop reads to apply it to its window; the runner applies it for a `KuiWindow` and `kui_run`.",
1069    },
1070    Door {
1071        rust: "Core::ime_off",
1072        c: Is("kui_ime_off_get"),
1073        odin: Is("ime_off_get"),
1074        node: Is("Ctx.imeOff"),
1075        lua: No(GUEST),
1076        doc: "The same for the input-method ask: what a driver with its own loop reads to apply it to its window; the runner applies it for a `KuiWindow` and `kui_run`.",
1077    },
1078    Door {
1079        rust: "Core::take_window_commands",
1080        c: Is("kui_take_window_command"),
1081        odin: Is("take_window_command"),
1082        node: Is("Ctx.windowCommands"),
1083        lua: No(GUEST),
1084        doc: "Drains what the frame asked of the driver: open, close, resize, focus, redraw.",
1085    },
1086    Door {
1087        rust: "Core::window_closed",
1088        c: Is("kui_window_closed"),
1089        odin: Is("window_closed"),
1090        node: Is("Ctx.windowClosed"),
1091        lua: No(GUEST),
1092        doc: "The driver reports a window gone.",
1093    },
1094    Door {
1095        rust: "Core::dismiss_window",
1096        c: Is("kui_window_dismissed"),
1097        odin: Is("window_dismissed"),
1098        node: Is("Ctx.windowDismissed"),
1099        lua: No(GUEST),
1100        doc: "The driver reports a popup dismissed, with why (ADR 0003 step 4).",
1101    },
1102    // -- Theme, metrics, tokens --------------------------------------------
1103    Door {
1104        rust: "Core::set_theme",
1105        c: Is("kui_theme_set"),
1106        odin: Is("theme_set"),
1107        node: Is("setTheme"),
1108        lua: No("read-only: the palette is the host's (ADR 0019)"),
1109        doc: "Pins a whole palette.",
1110    },
1111    Door {
1112        rust: "Core::set_accent",
1113        c: Is("kui_theme_set_accent"),
1114        odin: Is("theme_set_accent"),
1115        node: Is("setAccent"),
1116        lua: No("as `set_theme`"),
1117        doc: "Pins an accent and keeps the OS's base.",
1118    },
1119    Door {
1120        rust: "Ui::theme",
1121        c: Is("kui_theme"),
1122        odin: Is("theme"),
1123        node: Is("theme"),
1124        lua: As("`env.theme`, a reading"),
1125        doc: "The palette in effect (`THEME_ROLES`).",
1126    },
1127    Door {
1128        rust: "Core::set_metrics",
1129        c: Is("kui_metrics_set"),
1130        odin: Is("metrics_set"),
1131        node: Is("setMetrics"),
1132        lua: No("as `set_theme` (backlog T2)"),
1133        doc: "Pins the stock widgets' sizes.",
1134    },
1135    Door {
1136        rust: "Ui::metrics",
1137        c: Is("kui_metrics"),
1138        odin: Is("metrics"),
1139        node: Is("metrics"),
1140        lua: As("`env.metrics`, a reading"),
1141        doc: "The sizes in effect (`METRIC_ROLES`).",
1142    },
1143    Door {
1144        rust: "Ui::set_tokens",
1145        c: Is("kui_tokens_set"),
1146        odin: Is("tokens_set"),
1147        node: Is("setTokens"),
1148        lua: Is("set_tokens"),
1149        doc: "Declares the origin's colour and length tokens (ADR 0027).",
1150    },
1151    Door {
1152        rust: "Tokens::derive",
1153        c: Is("kui_tokens_derive"),
1154        odin: Is("tokens_derive"),
1155        node: As("a colour with `from` in `setTokens`"),
1156        lua: As("a colour with `from` in `set_tokens`"),
1157        doc: "Adds derived colours to the declared ones (ADR 0028).",
1158    },
1159    Door {
1160        rust: "Ui::tokens",
1161        c: As("`kui_token_color` / `kui_token_length`, one name at a time"),
1162        odin: As("`token_color` / `token_length`, one name at a time"),
1163        node: Is("tokens"),
1164        lua: As("`env.tokens`, a reading"),
1165        doc: "The tokens in effect, resolved for the appearance.",
1166    },
1167    Door {
1168        rust: "Core::tokens_declared",
1169        c: No(
1170            "a plugin declares in every `kui_ext_view` and pays the parse; a reader that lets it skip the second is one line, once a plugin asks for it",
1171        ),
1172        odin: No(ODIN_IS_C),
1173        node: No("an app declares once, before its loop"),
1174        lua: No("the `tokens` global is declared once, at load"),
1175        doc: "Whether an origin declared tokens.",
1176    },
1177    // -- Diagnostics and devtools ------------------------------------------
1178    Door {
1179        rust: "Core::set_diagnostics",
1180        c: Is("kui_set_diagnostics"),
1181        odin: Is("set_diagnostics"),
1182        node: Is("setDiagnostics"),
1183        lua: No(GUEST),
1184        doc: "Turns the per-frame checks on.",
1185    },
1186    Door {
1187        rust: "Core::take_warnings",
1188        c: Is("kui_take_warnings"),
1189        odin: Is("take_warnings"),
1190        node: Is("warnings"),
1191        lua: No("the host drains and the Lua runner prints"),
1192        doc: "Drains the warnings raised since the last call.",
1193    },
1194    Door {
1195        rust: "Core::warnings_raised",
1196        c: No(
1197            "the C smoke round drains `kui_take_warnings` after each frame; a non-draining reader waits for a C harness that needs one",
1198        ),
1199        odin: No(ODIN_IS_C),
1200        node: Is("warningsRaised"),
1201        lua: No(GUEST),
1202        doc: "The warnings raised so far, undrained, which is what an example's self-check reads (ADR 0021).",
1203    },
1204    Door {
1205        rust: "Core::set_devtools",
1206        c: Is("kui_set_devtools"),
1207        odin: Is("set_devtools"),
1208        node: Is("setDevtools"),
1209        lua: No(GUEST),
1210        doc: "Turns the devtools panel on.",
1211    },
1212    Door {
1213        rust: "Core::devtools",
1214        c: Is("kui_devtools"),
1215        odin: Is("devtools"),
1216        node: Is("devtools"),
1217        lua: No(GUEST),
1218        doc: "Whether it is on.",
1219    },
1220    Door {
1221        rust: "Core::set_devtools_dock",
1222        c: Is("kui_set_devtools_dock"),
1223        odin: Is("set_devtools_dock"),
1224        node: Is("setDevtoolsDock"),
1225        lua: No(GUEST),
1226        doc: "Where it sits.",
1227    },
1228    Door {
1229        rust: "Core::devtools_dock",
1230        c: Is("kui_devtools_dock"),
1231        odin: Is("devtools_dock"),
1232        node: Is("devtoolsDock"),
1233        lua: No(GUEST),
1234        doc: "Where it sits, read back.",
1235    },
1236    Door {
1237        rust: "Core::host_rect",
1238        c: Is("kui_host_rect"),
1239        odin: Is("host_rect"),
1240        node: Is("hostArea"),
1241        lua: No(GUEST),
1242        doc: "Where the frame laid the host out in the window, logical px: the viewport with its origin, which is what tells the app's quads from the dock's (backlog F92).",
1243    },
1244    Door {
1245        rust: "Core::set_devtools_theme",
1246        c: Is("kui_set_devtools_theme"),
1247        odin: Is("set_devtools_theme"),
1248        node: Is("setDevtoolsTheme"),
1249        lua: No(GUEST),
1250        doc: "Seeds the panel's theme override.",
1251    },
1252    Door {
1253        rust: "Core::set_devtools_key",
1254        c: Is("kui_set_devtools_key"),
1255        odin: Is("set_devtools_key"),
1256        node: Is("setDevtoolsKey"),
1257        lua: No(GUEST),
1258        doc: "Respells the chord that moves the keyboard into the panel (`Ctrl+Shift+I` by default).",
1259    },
1260    Door {
1261        rust: "Core::devtools_key",
1262        c: Is("kui_devtools_key"),
1263        odin: Is("devtools_key"),
1264        node: Is("devtoolsKey"),
1265        lua: No(GUEST),
1266        doc: "That chord, read back in its portable spelling.",
1267    },
1268    Door {
1269        rust: "Ui::devtools_tab",
1270        c: Is("kui_devtools_tab"),
1271        odin: Is("devtools_tab"),
1272        node: As("`<devtoolsTab name label slot/>`"),
1273        lua: As("`devtools_tab { name=, label=, slot= }`"),
1274        doc: "Declares a devtools tab an extension fills through the slot named (ADR 0032).",
1275    },
1276    Door {
1277        rust: "Ui::devtools_tab_with",
1278        c: Is("kui_devtools_tab_open"),
1279        odin: Is("devtools_tab_open"),
1280        node: As(
1281            "`<devtoolsTab name label>{() => …}</devtoolsTab>`, the function child called only while the tab is on show",
1282        ),
1283        lua: As(
1284            "`devtools_tab { name=, label=, view = function(env) … end }`, called only while the tab is on show",
1285        ),
1286        doc: "Declares a devtools tab the host draws itself, and draws it only while it is on show.",
1287    },
1288    Door {
1289        rust: "Core::devtools_shown_tab",
1290        c: No(
1291            "a C host's open answers whether the tab is on show (`kui_devtools_tab_open`); nothing encodes ahead of the core there",
1292        ),
1293        odin: No(ODIN_IS_C),
1294        node: Is("devtoolsShownTab"),
1295        lua: No("the runner's converter reads it for the script (ADR 0032, decision 3)"),
1296        doc: "The declared devtools tab on show, which a data binding reads once a frame to call the tab's function.",
1297    },
1298    Door {
1299        rust: "Core::devtools_selected",
1300        c: Is("kui_devtools_selected"),
1301        odin: Is("devtools_selected"),
1302        node: Is("devtoolsSelected"),
1303        lua: No(GUEST),
1304        doc: "The node the panel's tree tab has selected (ADR 0032, decision 4).",
1305    },
1306    Door {
1307        rust: "Core::devtools_hovered",
1308        c: Is("kui_devtools_hovered"),
1309        odin: Is("devtools_hovered"),
1310        node: Is("devtoolsHovered"),
1311        lua: No(GUEST),
1312        doc: "The tree row under the pointer.",
1313    },
1314    Door {
1315        rust: "Core::devtools_picked",
1316        c: Is("kui_devtools_picked"),
1317        odin: Is("devtools_picked"),
1318        node: Is("devtoolsPicked"),
1319        lua: No(GUEST),
1320        doc: "The node the picker is over.",
1321    },
1322    Door {
1323        rust: "Core::set_devtools_pick",
1324        c: Is("kui_set_devtools_pick"),
1325        odin: Is("set_devtools_pick"),
1326        node: Is("setDevtoolsPick"),
1327        lua: No(GUEST),
1328        doc: "Raises the panel's picker from outside it, or puts it away; raised from a declared tab, the pick lands in `devtools_selected` and the tab stays up.",
1329    },
1330    Door {
1331        rust: "Core::devtools_picking",
1332        c: Is("kui_devtools_picking"),
1333        odin: Is("devtools_picking"),
1334        node: Is("devtoolsPicking"),
1335        lua: No(GUEST),
1336        doc: "Whether the picker is up.",
1337    },
1338    Door {
1339        rust: "Core::set_devtools_selected",
1340        c: Is("kui_set_devtools_selected"),
1341        odin: Is("set_devtools_selected"),
1342        node: Is("setDevtoolsSelected"),
1343        lua: No(GUEST),
1344        doc: "Selects and reveals a node in the tree tab from outside the panel.",
1345    },
1346    Door {
1347        rust: "Core::set_devtools_tab",
1348        c: Is("kui_set_devtools_tab"),
1349        odin: Is("set_devtools_tab"),
1350        node: Is("setDevtoolsTab"),
1351        lua: No(GUEST),
1352        doc: "Shows the panel's tab named — one of its own, in any case, or a declared one, as declared — from outside the panel, as the strip's click does; a hidden panel comes back docked.",
1353    },
1354    Door {
1355        rust: "Core::devtools_current_tab",
1356        c: Is("kui_devtools_current_tab"),
1357        odin: Is("devtools_current_tab"),
1358        node: Is("devtoolsCurrentTab"),
1359        lua: No(GUEST),
1360        doc: "The tab the panel is on, by name.",
1361    },
1362    Door {
1363        rust: "Core::set_devtools_legend",
1364        c: Is("kui_set_devtools_legend"),
1365        odin: Is("set_devtools_legend"),
1366        node: Is("setDevtoolsLegend"),
1367        lua: No(GUEST),
1368        doc: "The key legend the panel's facts tab shows.",
1369    },
1370    Door {
1371        rust: "Core::set_inspect",
1372        c: Is("kui_set_inspect"),
1373        odin: Is("set_inspect"),
1374        node: Is("setInspect"),
1375        lua: No(GUEST),
1376        doc: "Turns the per-frame node snapshot behind `nodes` on.",
1377    },
1378    Door {
1379        rust: "Core::nodes",
1380        c: Is("kui_nodes"),
1381        odin: Is("nodes"),
1382        node: Is("nodes"),
1383        lua: No(GUEST),
1384        doc: "The last frame's nodes with what layout and the declarations made of them — a tree view's and an inspector's data.",
1385    },
1386    // -- Extensions --------------------------------------------------------
1387    Door {
1388        rust: "Ui::add_extension",
1389        c: Is("kui_ctx_add_extension"),
1390        odin: Is("ctx_add_extension"),
1391        node: Is("Ctx.addExtension"),
1392        lua: Is("add_extension"),
1393        doc: "Loads a plugin under a namespace; a `KuiWindow` takes its list at construction (`extensions`).",
1394    },
1395    Door {
1396        rust: "Launcher::extensions",
1397        c: As("`kui_ctx_extension_count` / `kui_ctx_extension_namespace`, one at a time"),
1398        odin: As("`ctx_extension_count` / `ctx_extension_namespace`, one at a time"),
1399        node: Is("Ctx.extensionNamespaces"),
1400        lua: Is("extension_namespaces"),
1401        doc: "The namespaces loaded.",
1402    },
1403    // -- The driver's half: what a host does to run a core ----------------
1404    // Lua has none of these, for the one reason `GUEST` states; Node's are
1405    // on `Ctx` alone because a `KuiWindow`'s driver is the runner.
1406    Door {
1407        rust: "Core::frame",
1408        c: As("`kui_frame_begin` … `kui_frame_finish`"),
1409        odin: As("`frame_begin` … `frame_finish`, or `kui.frame` around a view"),
1410        node: Is("Ctx.frame"),
1411        lua: No(GUEST),
1412        doc: "Runs one frame: the view, layout, the draw list; `KuiWindow.setView` is the windowed form, the runner calling it.",
1413    },
1414    Door {
1415        rust: "Core::output",
1416        c: Is("kui_draw_data"),
1417        odin: Is("draw_data"),
1418        node: Is("quads"),
1419        lua: No(GUEST),
1420        doc: "The draw list: quads, clips, fragment and texture draws (`clips`, `fragmentDraws`, `textureDraws` beside `quads` in Node) and the frame's stats.",
1421    },
1422    Door {
1423        rust: "Core::take_pending_events",
1424        c: Is("kui_poll_event"),
1425        odin: Is("poll_event"),
1426        node: Is("pollEvents"),
1427        lua: As("`on_event(ev)`, pushed after each frame"),
1428        doc: "What the frame and the input since produced, for `update`.",
1429    },
1430    Door {
1431        rust: "Core::set_time",
1432        c: Is("kui_set_time"),
1433        odin: Is("set_time"),
1434        node: Is("Ctx.setTime"),
1435        lua: No(GUEST),
1436        doc: "The clock the tweens read; a window's runner sets it from the display.",
1437    },
1438    Door {
1439        rust: "Core::env",
1440        c: As("`kui_env_set` and its four siblings, `ENV_FIELDS`' C column"),
1441        odin: As("`env_set` and its four siblings"),
1442        node: Is("Ctx.setEnv"),
1443        lua: No(GUEST),
1444        doc: "The host facts written in (the `env` field); a `KuiWindow`'s runner writes its own.",
1445    },
1446    Door {
1447        rust: "Ui::env",
1448        c: No("C is the host, so it writes the facts and has no reading (`ENV_FIELDS`)"),
1449        odin: No(ODIN_IS_C),
1450        node: Is("env"),
1451        lua: As("`env`, the view's argument"),
1452        doc: "The facts read back, `ENV_FIELDS` row for row.",
1453    },
1454    Door {
1455        rust: "Core::handle_input",
1456        c: As("`kui_input_cursor` … `kui_input_access`, one per `InputEvent`"),
1457        odin: As(
1458            "`input_cursor` … `input_access`, one per input event; `click` and `press` for a drive",
1459        ),
1460        node: As(
1461            "`Ctx.cursor` … `Ctx.access`, one per `InputEvent`; a `KuiWindow` refuses injection",
1462        ),
1463        lua: No(GUEST),
1464        doc: "Pointer, wheel, key, text, IME, assistive and OS file-drag input; a wheel gesture's latching is `scroll_gesture` (`kui_input_scroll_gesture`, `Ctx.scrollGesture`, backlog F107); `press` / `release` are a click by label (`kui_input_press`, `Ctx.press`); the file drag is `drag_files` / `drop_files` / `drag_cancel` (ADR 0031); a file dialog's answer is `answer_files` (`kui_input_files`, `Ctx.answerFiles`, backlog C51); the documents the OS asked the app to open are `InputEvent::Open` (`kui_input_open`, `Ctx.openDocuments`, backlog F124).",
1465    },
1466    Door {
1467        rust: "Core::modifiers",
1468        c: Is("kui_input_modifiers"),
1469        odin: Is("input_modifiers"),
1470        node: Is("Ctx.modifiers"),
1471        lua: No(GUEST),
1472        doc: "The modifier state, reported on its own when the OS does (backlog AR22).",
1473    },
1474    Door {
1475        rust: "Core::release_held_keys",
1476        c: Is("kui_release_held_keys"),
1477        odin: Is("release_held_keys"),
1478        node: As(
1479            "`Ctx.setEnv({focused: false})` releases, as losing the keyboard does for every driver (ADR 0020)",
1480        ),
1481        lua: No(GUEST),
1482        doc: "Lets go of every key the focused sink holds.",
1483    },
1484    Door {
1485        rust: "Core::set_subpixel_text",
1486        c: Is("kui_set_subpixel_text"),
1487        odin: Is("set_subpixel_text"),
1488        node: No(NEVER_PAINTS),
1489        lua: No(GUEST),
1490        doc: "LCD subpixel coverage for outline glyphs, for a renderer that blends per channel.",
1491    },
1492    Door {
1493        rust: "Core::take_audio_commands",
1494        c: Is("kui_take_audio_commands"),
1495        odin: Is("take_audio_commands"),
1496        node: Is("Ctx.audioCommands"),
1497        lua: No(GUEST),
1498        doc: "Drains what the frame asked of the audio device; a `KuiWindow`'s device is the runner's.",
1499    },
1500    Door {
1501        rust: "Core::audio_ended",
1502        c: Is("kui_audio_ended"),
1503        odin: Is("audio_ended"),
1504        node: Is("Ctx.audioEnded"),
1505        lua: No(GUEST),
1506        doc: "The device reports a playback over.",
1507    },
1508    Door {
1509        rust: "Core::audio_truncated",
1510        c: Is("kui_audio_truncated"),
1511        odin: Is("audio_truncated"),
1512        node: Is("Ctx.audioTruncated"),
1513        lua: No(GUEST),
1514        doc: "The device reports a stop that cut a playback short — a one-shot node's removal becomes `truncated-playback`.",
1515    },
1516    Door {
1517        rust: "Core::audio_refused",
1518        c: Is("kui_audio_refused"),
1519        odin: Is("audio_refused"),
1520        node: Is("Ctx.audioRefused"),
1521        lua: No(GUEST),
1522        doc: "The device reports a play it would not take — a `refused` sound event and `playback-refused`.",
1523    },
1524    // -- The runner's options (`Launcher` in Rust, `WindowOptions` in Node,
1525    // `kui_run_with` in C). One row for the set, since they are one
1526    // decision: what a window opens as.
1527    Door {
1528        rust: "Launcher::size",
1529        c: As("`width` / `height` in the `KuiRunConfig` `kui_run_with` takes"),
1530        odin: As("`width` / `height` in the `Run_Config` `kui.run` takes"),
1531        node: As("`width` / `height` in `WindowOptions`"),
1532        lua: No(GUEST),
1533        doc: "The window's opening size; `min_size` / `max_size` / `chrome` / `text_aa` / `diagnostics` / `frame_latency` are the rest of the set, and each binding's form carries them all (`min_w`, `chrome`, `text_aa`, `diagnostics`, `frame_latency` in C; `minWidth`, `chrome`, `textAa`, `diagnostics`, `frameLatency` in Node). `Launcher::devtools` and `Launcher::core` are the two the others reach another way: `kui_set_devtools` / `setDevtools` on the context, and the context handed to `kui_run_with` *is* the core.",
1534    },
1535    Door {
1536        rust: "Launcher::icon",
1537        c: Is("kui_set_icon"),
1538        odin: Is("set_icon"),
1539        node: As("`icon` in `WindowOptions`"),
1540        lua: No(GUEST),
1541        doc: "The icon every window of the app is created with — RGBA pixels and their size — shown by Windows in the title bar, Alt-Tab and the taskbar and by X11's window manager; macOS (the bundle's `.icns`) and Wayland (the `.desktop` file's) have no window icon (backlog F86). `Launcher::icon_resource` is the Windows executable's own icon resource, which wins there — C's `resource` argument, Node's `icon.resource`. C's is a free function called before `kui_run`, for `kui_on_teardown`'s reason.",
1542    },
1543    Door {
1544        rust: "App::teardown",
1545        c: Is("kui_on_teardown"),
1546        odin: As("the `teardown` procedure `kui.run` takes"),
1547        node: Is("KuiWindow.onTeardown"),
1548        lua: No(GUEST),
1549        doc: "The window going for good — its close button, Quit from the menu or the dock, a close command on it, a pumped runner ended — heard once, before `run` returns or the process exits, with nothing drawing: the place to keep what the app would lose with the window (backlog F74, the other two hosts under RG1). On macOS a Quit ends the process from inside the loop, so this is the only thing an app runs on ⌘Q — nothing after `run`, `kui_run` or `await runWindowed(...)` does, not even `process.on('exit')`. C's is a free function called before `kui_run`, with the run's `user`, since `kui_run`'s app is three arguments and not a struct. Node's is the window's door, called from inside the pump that saw the window go; `runWindowed` registers its config's `teardown(model)` there, and `createApp`'s `app.teardown()` runs the same one for a headless drive.",
1550    },
1551];
1552
1553#[cfg(test)]
1554mod tests {
1555    use super::*;
1556
1557    /// A row's Rust spelling is a `pub fn` in the file its prefix names —
1558    /// `Ui::` in `ui.rs`, `Core::` under `runtime/`, `SharedResources::`
1559    /// in `session.rs`, `Tokens::` in `tokens.rs`, `Launcher::` in the
1560    /// `kui-native` crate, and `App::` a method of that crate's `App` trait (a
1561    /// trait's `fn` is public without the word) — so a renamed or
1562    /// removed verb is a red row and not a stale one, which is the pin
1563    /// Rust's column can have without reflection.
1564    #[test]
1565    fn every_rust_spelling_is_a_public_fn() {
1566        let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
1567        let read = |p: std::path::PathBuf| std::fs::read_to_string(&p).unwrap_or_default();
1568        let mut runtime = read(root.join("runtime.rs"));
1569        for dir in ["runtime", "runtime/devtools"] {
1570            for entry in std::fs::read_dir(root.join(dir)).unwrap() {
1571                let p = entry.unwrap().path();
1572                if p.extension().is_some_and(|e| e == "rs") {
1573                    runtime.push_str(&read(p));
1574                }
1575            }
1576        }
1577        let ui = read(root.join("ui.rs"));
1578        let session = read(root.join("session.rs"));
1579        let tokens = read(root.join("tokens.rs"));
1580        let launcher = read(root.join("../../kui-native/src/lib.rs"));
1581        // The `App` trait's body: a method of it is a callback the app
1582        // writes, spelled `fn name(` and public by being the trait's.
1583        let app_trait = launcher
1584            .split_once("pub trait App {")
1585            .map(|(_, rest)| rest.split_once("\n}\n").map_or(rest, |(body, _)| body))
1586            .unwrap_or_default();
1587        for d in DOORS {
1588            let (ty, name) = d.rust.split_once("::").expect(d.rust);
1589            let src = match ty {
1590                "Ui" => &ui,
1591                "Core" => &runtime,
1592                "SharedResources" => &session,
1593                "Tokens" => &tokens,
1594                "Launcher" => &launcher,
1595                "App" => app_trait,
1596                other => panic!("{}: {other} is not a prefix the table knows", d.rust),
1597            };
1598            let (public, any) = (
1599                format!("{}fn {name}(", if ty == "App" { "" } else { "pub " }),
1600                format!("fn {name}("),
1601            );
1602            assert!(
1603                src.contains(&public) || (ty == "Core" && name == "env"),
1604                "{}: no `{public}` in {ty}'s sources{}",
1605                d.rust,
1606                if src.contains(&any) {
1607                    " (a private fn is)"
1608                } else {
1609                    ""
1610                }
1611            );
1612        }
1613    }
1614
1615    /// The table is one row per verb, and a `No` says why in a sentence
1616    /// rather than in a word — the reasons are what ADR 0020 said a table
1617    /// would be made of, and the point of building one.
1618    #[test]
1619    fn rows_are_unique_and_every_no_has_a_reason() {
1620        let mut seen = std::collections::BTreeSet::new();
1621        for d in DOORS {
1622            assert!(seen.insert(d.rust), "{} is two rows", d.rust);
1623            assert!(!d.doc.is_empty(), "{} has no doc", d.rust);
1624            for (binding, cell) in [("C", d.c), ("Node", d.node), ("Lua", d.lua)] {
1625                match cell {
1626                    Is(name) => assert!(
1627                        !name.is_empty() && !name.contains(' '),
1628                        "{} in {binding}: {name:?} is not a name",
1629                        d.rust
1630                    ),
1631                    As(how) | No(how) => assert!(
1632                        how.len() >= 12,
1633                        "{} in {binding}: {how:?} is not a reason",
1634                        d.rust
1635                    ),
1636                }
1637            }
1638        }
1639    }
1640
1641    /// The C column's spellings are the header's: `kui_` and snake case.
1642    /// Node's are camelCase, optionally under one of the two classes;
1643    /// Lua's snake case. A cell in the wrong column's spelling is a pasted
1644    /// row.
1645    #[test]
1646    fn cells_are_spelled_in_their_bindings_case() {
1647        let snake = |s: &str| {
1648            s.bytes()
1649                .all(|b| b.is_ascii_lowercase() || b == b'_' || b.is_ascii_digit())
1650        };
1651        for d in DOORS {
1652            if let Is(c) = d.c {
1653                assert!(
1654                    c.starts_with("kui_") && snake(c),
1655                    "{}: C cell {c:?}",
1656                    d.rust
1657                );
1658            }
1659            if let Is(n) = d.node {
1660                let n = n
1661                    .strip_prefix("Ctx.")
1662                    .or_else(|| n.strip_prefix("KuiWindow."))
1663                    .unwrap_or(n);
1664                assert!(
1665                    !n.contains('_') && n.starts_with(|c: char| c.is_ascii_lowercase()),
1666                    "{}: Node cell {n:?}",
1667                    d.rust
1668                );
1669            }
1670            if let Is(l) = d.lua {
1671                assert!(snake(l), "{}: Lua cell {l:?}", d.rust);
1672            }
1673        }
1674    }
1675}