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