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