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