@qxuken/kui 0.1.0-alpha.43 → 0.1.0-alpha.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -21,6 +21,167 @@ listed under both (backlog F61, from the alpha.12 field reports: the list
21
21
  is what the release knows it broke, and a fix it did not think of as one
22
22
  was the first bare bump to break an app in five releases).
23
23
 
24
+ ## 0.1.0-alpha.44 (2026-10-08)
25
+
26
+ **What breaks.**
27
+
28
+ - A menu row wider than the window — a recent file's path, a long
29
+ `<select>` option — draws its label cut short with "…" in a menu as
30
+ wide as the window less 8 px a side (and never narrower than the
31
+ metric's menu width, 200 px), where the menu ran off the edge (under
32
+ Fixed, RG150).
33
+ - With a frame clock (every runner sets one), moving the pointer from an
34
+ open submenu's row to another row of its menu switches after 0.3 s of
35
+ rest there, not at once (under Fixed, RG150). A driver that sets no
36
+ clock switches at once, as before.
37
+ - A `<select>` option given `items` (or a C `KuiMenuItem` with
38
+ `submenu`) opens no submenu: the rows are dropped, with an
39
+ `unknown-prop` warning in Node and Lua (under Fixed, RG150).
40
+ - Windows: a `Blur` or `Tinted` window whose environment presents through
41
+ the window's handle (`WGPU_DX12_PRESENTATION_SYSTEM=hwnd`) or names no
42
+ D3D12 in `WGPU_BACKEND` draws the wallpaper kui reads and reports
43
+ `Tinted` (or `Opaque`), where it was translucent over black and
44
+ reported `Blur`; a transparent window whose `WGPU_BACKEND` lists D3D12
45
+ among others opens D3D12 alone (under Fixed, RG150).
46
+ - Windows and Linux: a `Blur` or `Tinted` window on a desktop with no
47
+ wallpaper kui can read (a solid colour, a file it cannot decode) reads
48
+ `Tinted` for its first frame or frames and `Opaque` once the loader's
49
+ thread has answered, where alpha.43 read the path on the event loop
50
+ and said `Opaque` from the first frame (under Fixed, RG150). A view
51
+ that branches on the backdrop sees it change once, early.
52
+ - An image or a fragment that declares `border` draws it, as a ring over
53
+ the content, where it drew none (under Fixed, RG152): an app that
54
+ kept the `border` and drew its own ring around the picture draws two.
55
+
56
+ ### Added
57
+
58
+ - `MenuItem::stray_keys`, `MenuItem::stray_option_keys` and
59
+ `MenuBar::stray_keys`: the keys of plain-data menu rows no row reads,
60
+ a submenu's rows' included, for a binding to warn about (backlog
61
+ RG150).
62
+ - `kui_wgpu::see_through_by_visual` (and `_with`, its pure form): whether
63
+ a Windows window can be seen through, by the environment wgpu reads —
64
+ the one answer the window's surface and the swapchain both take
65
+ (backlog RG150).
66
+
67
+ ### Fixed
68
+
69
+ - **A menu has a width ceiling** (backlog RG150, from alpha.43's pre-tag
70
+ pass). F127 made a menu as wide as its widest row, with nothing above
71
+ it: a long path or `<select>` option ran the panel and its
72
+ accelerators off a narrow window. A menu is now never wider than the
73
+ window less 8 px a side (a narrower ceiling the caller declared in px
74
+ stands; the metric's menu width is the floor), and a row's label is
75
+ bounded by what its accelerator leaves it and ends in "…".
76
+ - **A submenu survives the pointer passing over a row on its way in**
77
+ (backlog RG150). A diagonal path from a row to a lower row of its
78
+ submenu crosses the rows below it, and each one closed the submenu. A
79
+ row that would close one now waits 0.3 s of rest on the frame clock,
80
+ and moving into the submenu cancels it; the frames for the wait are
81
+ owed. And a submenu the keyboard closed opens again when the pointer
82
+ leaves the menu and comes back to its row.
83
+ - **A stray key inside a submenu warns** (backlog RG150). Only a select's
84
+ top-level options were checked, so `{ label, disabled: true }` inside a
85
+ submenu was silently an enabled row; Node's `openMenu` and `<menuBar>`
86
+ and Lua's `open_menu` and `menu_bar` now warn for every level.
87
+ - **The backdrop blur's scratch is the size of what it blurs** (backlog
88
+ RG150). Four surface-sized textures, cleared and stored by every pass,
89
+ were ~236 MB at 5K and three full-surface stores per blurred node, and
90
+ were kept by a window gone idle. The scratch is now the largest
91
+ region's, its passes load rather than clear, the composite rides in the
92
+ pass that follows, and the first frame without a blur drops it all:
93
+ 65.5 MB → 16.8 MB and ~230 → ~150 µs a frame at 2560×1600 for one
94
+ toolbar, the output bit-identical.
95
+ - **Windows: one answer for the window's surface and the swapchain**
96
+ (backlog RG150). `WGPU_BACKEND` decided the first and
97
+ `WGPU_DX12_PRESENTATION_SYSTEM` the second, so `WGPU_BACKEND=dx12` drew
98
+ translucency over black while the window reported `Blur`. Compiled and
99
+ read, not run.
100
+ - **Linux: the wallpaper is found off the event loop** (backlog RG150):
101
+ up to three `gsettings` runs at window creation on GNOME before the
102
+ window showed; and Wayland's blur managers are bound once per process,
103
+ not once per window and never released. A window with no wallpaper to
104
+ read is `Tinted` until the thread answers, within its first frames,
105
+ and `Opaque` from then on (see What breaks). Compiled and read, not
106
+ run; the Linux half built and smoked under WSLg's X11 in the pre-tag
107
+ pass.
108
+
109
+ - **A box that becomes a float keeps a float it held above it** (backlog
110
+ RG151, from berainder). The float stack kept last frame's floats in
111
+ their order and put a new one on top, so a box that kept its key while
112
+ turning into a float went over the float inside it: a debug build
113
+ panicked on the stack's own assertion, a release build painted and hit
114
+ the inner float under its parent. A float found below the one it is in
115
+ now moves to just above it.
116
+ - **`border` on an image draws** (backlog RG152, from berainder). The
117
+ border was painted with the background, under the picture, which
118
+ covered it; on an image or a fragment it is now a ring over the
119
+ content, as over a gradient. And an image with a `gradient` and a
120
+ `border` no longer carries an empty quad where the gradient's own
121
+ ring went (from the alpha.44 pre-tag pass).
122
+ - **The float stack keeps the order of two floats a box held, and
123
+ sorts a float moved into one** (backlog RG153, from the alpha.44
124
+ pre-tag pass). RG151's move put each held float just above the box in
125
+ turn, so of two — a name panel and a tip opened over it — the first
126
+ came out on top; and a float that moves into another float under a
127
+ key the app keeps (`open_key`, `leaf_key`) changed no rank, so the
128
+ steady path kept it under the float it is now in and the debug
129
+ assertion tripped by RG151's other road. A held float now waits for
130
+ the float it is in and goes just above it, in the order it had; and
131
+ the steady order is checked for nesting and rebuilt when it fails.
132
+
133
+ **What you can delete.**
134
+
135
+ - An app's own truncation of menu labels to keep a menu on screen
136
+ (RG150).
137
+ - A key of its own for a box on each side of becoming a float — the
138
+ card behind and the card on top — kept only to stop a float inside it
139
+ falling under it (RG151), or to keep two floats inside it in the order
140
+ they opened (RG153).
141
+ - A padded box around an image to draw its border (RG152).
142
+
143
+ ### Native verification
144
+
145
+ The by-hand round alpha.6 introduced (backlog R4), on 2026-10-08, over
146
+ RG150 from alpha.43's pre-tag pass and berainder's RG151 and RG152, with
147
+ alpha.44's pre-tag pass over them on the Windows machine and under WSLg:
148
+ the mechanical round on both, then three read-only reviews of the diff
149
+ since alpha.43 (the menus; the backdrop and the blur, read against
150
+ wgpu's and wayland-client's sources; the floats, the image border and
151
+ the docs), each claim probed. They filed RG153, built before the tag
152
+ (the float stack reversing two floats a box held, and keeping a float
153
+ moved into another under a kept key below it), and RG154, open; and the
154
+ round itself caught F127's width test failing on both platforms under
155
+ RG150's ceiling (its accelerator is a word each there, wider than the
156
+ test's window).
157
+
158
+ **Windows**, the pre-tag pass. fmt and clippy are clean; `nu
159
+ scripts/test.nu --node`: **2144 tests over 143 suites**, 0 failed. The C round passes (6 checks), and so do the **57 scenes**
160
+ through Rust, Lua, C, Node and Odin; the Odin binding's four steps with
161
+ CI's pinned `dev-2026-09`; Node's tests under
162
+ `KUI_CONFORMANCE_REQUIRED=1` (**218 of 219**, the one skip Windows'),
163
+ `npm run gen` with no diff, the examples' typecheck, the headless round
164
+ (37 drives) and the book's listing. The windowed round with Node's:
165
+ **53 examples on both bases**, every one clean on a first run; `counter`
166
+ and `host` opened by hand after `cbuild`. The bench guard against the
167
+ alpha.43 tag: **green**, the guarded rows −5.9% to +3.6%
168
+ (`frame_10k_rects_with_access_tree`; `frame_10k_rects` itself −5.9%),
169
+ the worst run-to-run spread on a guarded row 5.4%.
170
+
171
+ **Linux**, under WSLg (llvmpipe), the same commit with the pass's
172
+ fixes: fmt and clippy clean; `cargo test --workspace`: **1921 tests
173
+ over 142 suites**, 0 failed; the C round (5 checks) and the 57 scenes
174
+ through every adapter, the Odin binding's four steps, Node **219 of
175
+ 219** with the corpus required, gen clean, the typecheck, the headless
176
+ round. The windowed round under X11 with Node's: 53 examples on both
177
+ bases; eight windows of a first run four at a time died with
178
+ "X connection to :0 broken" — XWayland's, twice — and the four
179
+ examples drew every frame run again one at a time. Nothing opened a
180
+ Wayland window (Weston's decorations crash under winit here) and
181
+ nothing read a wallpaper: F126's Linux half is still compiled and read,
182
+ not met on KDE or GNOME. No Mac ran this round: the AX audit and the
183
+ macOS halves of RG150 are CI's check and the next Mac round's.
184
+
24
185
  ## 0.1.0-alpha.43 (2026-10-08)
25
186
 
26
187
  **What breaks.**
@@ -250,8 +411,6 @@ in the JSON a row already was.
250
411
  …" rows one per folder up to a cap (F128).
251
412
  - A toolbar's opaque fill over content that scrolls under it (F129).
252
413
  - Inline code drawn in the body face, or as a box beside the text (F130).
253
- - A toolbar's opaque fill over content that scrolls under it (F129).
254
- - Inline code drawn in the body face, or as a box beside the text (F130).
255
414
 
256
415
  ### Native verification
257
416
 
@@ -180,10 +180,17 @@ and none of which the tree order gives:
180
180
  declares it under a fresh key. This is the only "raise" and it is not a
181
181
  row; see *What was declined*.
182
182
 
183
- A nested float is above its enclosing float without a rule: it opened
184
- the same frame (appended after its parent, in tree order) or a later one
185
- (appended above). The key scheme means the reverse cannot happen, and a
186
- `debug_assert` says so where the stack is rebuilt.
183
+ A nested float is above its enclosing float, and almost always without a
184
+ rule: it opened the same frame (appended after its parent, in tree
185
+ order) or a later one (appended above), and a key derived from its
186
+ parent's cannot have been opened first. Two roads reach the reverse,
187
+ both under a key the app keeps across the change (backlog RG151 and
188
+ RG153, 2026-10-08): a box that becomes a float around a float it already
189
+ held — the box is new to the stack and the held float is not — and a
190
+ float that moves into another float under `open_key`, which changes no
191
+ rank. So the rebuild places each float after the float it is in, the
192
+ held ones in the order they had, and the steady order is checked for the
193
+ same and rebuilt when it fails. A `debug_assert` says the result holds.
187
194
 
188
195
  The steady state — the same float roots as last frame — is one
189
196
  comparison of two short key lists and costs nothing more; the first
package/encoder.js CHANGED
@@ -1026,7 +1026,9 @@ export function createEncoder(P) {
1026
1026
  for (const o of p.options) {
1027
1027
  const ok = (typeof o === 'string' && o.length > 0) || (o !== null && typeof o === 'object' && !Array.isArray(o));
1028
1028
  if (!ok) throw new Error('<select> options are non-empty strings or menu item objects { label, id, enabled }');
1029
- if (typeof o === 'object') for (const k in o) if (!MENU_ITEM_KEYS.has(k)) unknown.push([MENU_ITEM, k]);
1029
+ // An option is chosen, never opened: its `items` are dropped,
1030
+ // so they are reported as any key no option reads (RG150).
1031
+ if (typeof o === 'object') for (const k in o) if (!MENU_ITEM_KEYS.has(k) || k === 'items') unknown.push([MENU_ITEM, k]);
1030
1032
  }
1031
1033
  const current = p.current;
1032
1034
  if (current != null && (!Number.isInteger(current) || current < 0)) {
package/howto.md CHANGED
@@ -805,14 +805,20 @@ clicked, or Enter or the Right arrow is pressed; Left or Escape closes
805
805
  it, and Escape again closes the menu. It nests, in a context menu and in
806
806
  a menu bar, drawn or the platform's. A row inside is chosen like any
807
807
  row: one `{kind:"menu", role, item}` with its own `id`, on the node the
808
- menu is about. A host that shows menus itself reports one with
808
+ menu is about. Give the rows inside an `id`: a row without one posts its
809
+ label, and "Name" under "Sort by ▸" and "Name" under "Group by ▸" would
810
+ post the same `item`. A host that shows menus itself reports one with
809
811
  `Core::activate_menu_path(&[1, 0])` (Node `activateMenuPath`, C
810
812
  `kui_activate_menu_path` with a path of `size_t`s). In C a row's
811
813
  `submenu` / `submenu_count` nest the same `KuiMenuItem`s, and
812
814
  `KUI_MENU_ITEM_SUBMENU` in a row's flags says it has rows, read with
813
815
  `kui_menu_item_path`. An
814
816
  `accel` in the portable spelling (`"mod+shift+n"`) is drawn the
815
- platform's way, and the menu widens to its longest row.
817
+ platform's way, and the menu widens to its longest row, up to the window
818
+ less a margin (and never below the metric's menu width), where a longer
819
+ label ends in an ellipsis. On the frame
820
+ clock, an open submenu waits 0.3 s before giving way to a row the
821
+ pointer crosses, so a diagonal path into it does not close it.
816
822
 
817
823
  [ADR 0018](docs/adr/0018-a-menu-bar-the-app-declares.md) ·
818
824
  [`tests/submenu.rs`](../crates/kui-core/tests/submenu.rs)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qxuken/kui",
3
- "version": "0.1.0-alpha.43",
3
+ "version": "0.1.0-alpha.44",
4
4
  "description": "kui for Node: JSX views lowered into the kui IR, Elm-style messages as data",
5
5
  "license": "MIT",
6
6
  "repository": {
Binary file
Binary file
Binary file
package/props.md CHANGED
@@ -118,9 +118,9 @@ where they make sense); text props apply to `<text>` and `<edit>`.
118
118
  |---|---|---|---|---|---|
119
119
  | `color` | `color` | `KuiTextStyle.color` | `Text_Style.color` | color (`#hex` or `0xRRGGBBAA`), or a `"$color"` token | Text color; default foreground when omitted. |
120
120
  | `ellipsis` | `ellipsis` | `KuiTextStyle.ellipsis` | `Text_Style.ellipsis` | boolean | End the last line with an ellipsis when the text is cut off: a single line unless `maxLines` says otherwise. |
121
- | `family` | `family` | `KuiTextStyle.family` (`KUI_FONT_*`) | `Text_Style.family` | `sans` \\| `serif` \\| `mono` \\| a family name | Font family: `sans`, `serif` or `mono`, kui's own, or the name of an installed family or one loaded with `loadFontsDir` / `loadFontFile` — `"Berkeley Mono"` — drawn in its face in the frame that names it (ADR 0037). A name is matched as `addSystemFont` matches it and registered in the session on first sight, exactly as the font database spells it (`"menlo"` is not `"Menlo"`); the session's first registration of any font maps the installed font files once (~30 ms on a Mac, backlog DX24), which a family named in a view pays in that frame. `systemFonts()` lists the names there are. A name nothing matches shapes as sans and raises `unknown-family`. It and `font` set the same thing, so declare one. |
121
+ | `family` | `family` | `KuiTextStyle.family` (`KUI_FONT_*`); `KuiSpan.family` (with `KUI_SPAN_FAMILY`) | `Text_Style.family` | `sans` \\| `serif` \\| `mono` \\| a family name | Font family: `sans`, `serif` or `mono`, kui's own, or the name of an installed family or one loaded with `loadFontsDir` / `loadFontFile` — `"Berkeley Mono"` — drawn in its face in the frame that names it (ADR 0037). A name is matched as `addSystemFont` matches it and registered in the session on first sight, exactly as the font database spells it (`"menlo"` is not `"Menlo"`); the session's first registration of any font maps the installed font files once (~30 ms on a Mac, backlog DX24), which a family named in a view pays in that frame. `systemFonts()` lists the names there are. A name nothing matches shapes as sans and raises `unknown-family`. It and `font` set the same thing, so declare one. |
122
122
  | `features` | `features` | `KuiTextStyle.features` (a `KuiStr`, the same spelling) | `Text_Style.features` | string | OpenType features for the shaper, as `tag=value` pairs separated by spaces or commas — a bare `tag` is 1, `-tag` is 0: `"liga=0 calt=0"` keeps a coding font from joining `->` and `!=` (what a terminal built on runs needs to hold its grid), `"tnum"` lines figures up in a gutter, `"ss01"` picks a stylistic set. Unset, the font's own defaults apply. At most 8; part of what the text is shaped as, so two texts differing only here are shaped twice. |
123
- | `font` | `font` | `KuiTextStyle.font` (from `kui_font_add*`) | `Text_Style.font` | resource handle | A registered font handle (addFont / addSystemFont); overrides `family`. |
123
+ | `font` | `font` | `KuiTextStyle.font` (from `kui_font_add*`); `KuiSpan.font` | `Text_Style.font` | resource handle | A registered font handle (addFont / addSystemFont); overrides `family`. |
124
124
  | `lineHeight` | `line_height` | `KuiTextStyle.line_height` | `Text_Style.line_height` | number, or a `"$length"` token | Line height (logical px); default size * 1.35. |
125
125
  | `maxLines` | `max_lines` | `KuiTextStyle.max_lines` | `Text_Style.max_lines` | number, or a `"$length"` token | Lay out at most this many lines (0 = unlimited); with `ellipsis`, a line clamp. |
126
126
  | `strikethrough` | `strikethrough` | `KuiTextStyle.decoration` (`KUI_DECO_STRIKETHROUGH`); `KuiSpan.flags` (`KUI_SPAN_STRIKETHROUGH`) | `Text_Style.strikethrough` | boolean | A line through the text, where the face puts its strikeout. Paint only; on a `<span>` the span alone, per line. |
@@ -146,7 +146,7 @@ where they make sense); text props apply to `<text>` and `<edit>`.
146
146
  | `pad`, `padX`, `padY`, `padL`, `padR`, `padT`, `padB` | `pad = n` or `pad = { all=, x=, y=, l=, r=, t=, b= }` | `pad_l`, `pad_r`, `pad_t`, `pad_b` | `Spec.pad`: `kui.pad(16)`, `kui.pad(16, 8)`, or `{l = .., r = .., t = .., b = ..}` | Padding; a frontend reports the names it saw and `PadShorthand::resolve` turns them into four edges — an edge falls back to its axis, an axis to the all-round `pad`, and the specific one always wins. |
147
147
  | `rowCount` | `row_count` | `kui_row_count` | `Spec.row_count`, a `Maybe(u64)`, which calls `kui.row_count` on the node | How many `index`ed rows this node's virtual list has, built or not. `uniformList` / `uniform_list` / `widgets::uniform_list` and `widgets::list` declare it on their container; a list composed by hand says it beside `scrollY`. What it buys: Select All (Cmd/Ctrl-A, the menu's row) inside a `selectable` virtual list selects the *data*, rows `0..rowCount`, rather than the rows the frame built, and the copy is a `selectionrange` ask whose `to.byte` is past the last row's length when that row is not built — cut it to the row. Without it Select All is the built rows, which is all the core can see. |
148
148
  | `secureInput` (root box only) | `secure_input = true` (root table) | `kui_set_secure_input` | `kui.set_secure_input` | Declares that this frame wants the keyboard to this window kept from every other process while the window has it — macOS's Secure Keyboard Entry, what a terminal turns on at a password prompt (backlog F85). Frame state the way `alwaysOnTop` is, default false: declare it on every frame the prompt is up, and the frame that stops is what turns it off, so nothing has to remember to undo it. The runner owns the platform call and its balance: `EnableSecureEventInput` is process-wide and counted, and the runner holds one count while a window whose frame asked has the keyboard, giving it back when that window loses the keyboard, closes or stops asking, and at exit — Apple's rule, since while it is on no other process can read the keyboard at all (a launcher's hotkey, a text expander, an accessibility tool). Nothing on Windows or Linux, which have no such switch. A C host with its own loop reads the ask with `kui_secure_input_get` and makes the call itself. |
149
- | `size` (text) | `size` | `KuiTextStyle.size` | `Text_Style.size` | Font size in logical px; the text style is constructed from it, so declare it for the other style props to apply at that size. |
149
+ | `size` (text) | `size` | `KuiTextStyle.size`; `KuiSpan.size` | `Text_Style.size` | Font size in logical px; the text style is constructed from it, so declare it for the other style props to apply at that size. |
150
150
  | `title` (root box only) | `window_title` (root table) | `kui_window_title` | `kui.window_title` | Declares the window title for this frame; the driver diffs and applies. |
151
151
  | `tooltip="hint"` | `tooltip = "hint"` | `KuiSpec.tooltip` (`kui_tooltip` / `kui_tooltip_with` draw a hint that is not hover-gated) | `Spec.tooltip` (`kui.tooltip` / `kui.tooltip_with` draw a hint that is not hover-gated) | Floats a hint below the node while hovered. All three effects — hover tracking, the accessible description, and the float itself — come from `PropsOut::apply_tooltip`, so no frontend can implement two of them; a Rust view has all three in `NodeSpec::tooltip` (`NodeSpec::apply_tooltip` is the spec half, for a caller that floats the hint itself). The `description` row is that middle effect on its own, for a hint that is spoken and never drawn. On a box or a `fragment` the float is the node's last child; a leaf holds no children — a `line`, `polygon`, `path`, `cells` grid, `image` or `edit` — and its hint floats beside it instead, anchored to it, and lands below its box the same way, out of every clip and flipping above near the window's bottom (backlog RG113; `PropsOut::for_leaf`). A leaf draws its description, which is the hint unless a `description` applied after it overwrote the slot. A `line`, `polygon` or `path` is hovered by its shape, so its hint shows while the pointer is on the stroke or inside the outline, not anywhere in its box. |
152
152
  | `windows={[{ name, kind?, anchor?, width?, height?, activates? }]}` (root box only; `windows: (model) => [...]` in the loop config) | `windows = { { name=, kind=, anchor=, width=, height=, activates= } }` (root table) | `kui_window_declare` | `kui.window_declare` | Declares which windows exist this frame, by stable name (`docs/adr/0004-multi-window.md`). A window opens on the first frame any window's frame declares it — its config is read then and never again, since the user owns its geometry once it exists — and closes on the first frame none does. The driver drains the `Open` / `Close` that result, and the app sees `{kind:"window", phase, name, id}`. A window the user closed does not reopen while it is still declared: stop declaring it, then declare it again. `kind: "popup"` makes it a menu surface instead: borderless, off the taskbar, owned by the window that declared it and closed with it, placed in screen coordinates against `anchor` — the `{x, y, w, h}` an `onLayout` node reported — and non-activating unless `activates` says otherwise, so the field that opened it keeps the focus ring while the arrows walk the list. A press outside it or Escape raises `{kind:"dismiss", reason, name, id}` and closes nothing, exactly as a `modal` node's does: stop declaring the window. Reach for a popup only for the placements a float cannot make — a list taller than the window, a menu with nowhere in-window to go, a panel beside the app; everything else stays `fit` plus a `modal` float, which costs one tree instead of an OS surface. |
@@ -654,6 +654,6 @@ its generator (`nu scripts/odin.nu gen --check`).
654
654
  | `Core::audio_ended` | `kui_audio_ended` | `audio_ended` | `Ctx.audioEnded` | *none: 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* | The device reports a playback over. |
655
655
  | `Core::audio_truncated` | `kui_audio_truncated` | `audio_truncated` | `Ctx.audioTruncated` | *none: 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* | The device reports a stop that cut a playback short — a one-shot node's removal becomes `truncated-playback`. |
656
656
  | `Core::audio_refused` | `kui_audio_refused` | `audio_refused` | `Ctx.audioRefused` | *none: 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* | The device reports a play it would not take — a `refused` sound event and `playback-refused`. |
657
- | `Launcher::size` | `width` / `height` in the `KuiRunConfig` `kui_run_with` takes | `width` / `height` in the `Run_Config` `kui.run` takes | `width` / `height` in `WindowOptions` | *none: 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* | 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. |
657
+ | `Launcher::size` | `width` / `height` in the `KuiRunConfig` `kui_run_with` takes | `width` / `height` in the `Run_Config` `kui.run` takes | `width` / `height` in `WindowOptions` | *none: 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* | 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. |
658
658
  | `Launcher::icon` | `kui_set_icon` | `set_icon` | `icon` in `WindowOptions` | *none: 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* | 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. |
659
659
  | `App::teardown` | `kui_on_teardown` | the `teardown` procedure `kui.run` takes | `KuiWindow.onTeardown` | *none: 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* | 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. |