Skip to main content

kui_core/schema/
doors.rs

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