@qxuken/kui 0.1.0-alpha.41 → 0.1.0-alpha.42
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 +188 -0
- package/encoder.js +2 -1
- package/index.d.ts +2 -0
- package/index.js +6 -2
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/kui_node.node +0 -0
- package/prebuilds/linux-arm64/kui_node.node +0 -0
- package/prebuilds/linux-x64/kui_node.node +0 -0
- package/prebuilds/win32-x64/kui_node.node +0 -0
- package/props.md +380 -374
package/CHANGELOG.md
CHANGED
|
@@ -21,6 +21,194 @@ 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.42 (2026-10-07)
|
|
25
|
+
|
|
26
|
+
**What breaks.**
|
|
27
|
+
|
|
28
|
+
- A press after a dead key it does not combine with carries what the
|
|
29
|
+
platform typed in its `text`: German's `^ space` is a Space whose
|
|
30
|
+
`text` is `^`, and `^ z` a `z` whose `text` is `^z`, where they said `" "`
|
|
31
|
+
and `z` — in a key sink's payload and in what an editor inserts (under
|
|
32
|
+
Fixed, RG127). A keymap that binds Space by its `text` rather than its
|
|
33
|
+
`code` sees the accent there.
|
|
34
|
+
- Node: a whole number past 2^53 in a message comes back a `number`,
|
|
35
|
+
where it came back a `BigInt` (RG138).
|
|
36
|
+
- C: `kui_take_menu_action` with a NULL or too-small `out` leaves the
|
|
37
|
+
action queued, where it dropped it; `kui_take_warnings` keeps what does
|
|
38
|
+
not fit `cap` for the next call, where it dropped it; `kui_draw_data`'s
|
|
39
|
+
`fragments` and `textures` are NULL on a frame that draws none, where
|
|
40
|
+
they were a non-NULL pointer to nothing (RG133, RG134, RG131).
|
|
41
|
+
- A text style whose size or line height is under a pixel — 0, negative,
|
|
42
|
+
NaN — shapes at a pixel, where Node and Lua aborted or hung and Rust
|
|
43
|
+
panicked (RG136); a cell grid's rows are a pixel apart at an infinite
|
|
44
|
+
one, where they went to the layout's limit (RG144).
|
|
45
|
+
- Rust: `schema::Door`, `schema::CustomProp` and `schema::ElementDef` gain
|
|
46
|
+
`odin` (under Added, the Odin binding), so a struct literal of one needs
|
|
47
|
+
the field.
|
|
48
|
+
|
|
49
|
+
C stays at ABI 25 (kui.h gains no function and no field; four doors now
|
|
50
|
+
keep what it always promised) and the Node wire at v21 (no frame version).
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **An Odin binding, experimental** (`packages/odin`). `kui/c` mirrors
|
|
55
|
+
kui.h declaration by declaration, pinned to the C compiler's layout by
|
|
56
|
+
848 generated `#assert`s. `kui` is the typed layer, generated from
|
|
57
|
+
kui.h and the prop schema:
|
|
58
|
+
- `Spec` and `Text_Style`, one field per schema row;
|
|
59
|
+
- an enum or bit set per family of constants;
|
|
60
|
+
- a door per C function: out-params as results, arrays as slices,
|
|
61
|
+
messages as plain Odin values with `#[derive(Message)]`'s `kind`
|
|
62
|
+
rule.
|
|
63
|
+
|
|
64
|
+
The elements are written by hand: containers close at the end of their
|
|
65
|
+
`if` through `@(deferred_in)`. A C function the hand-written files do
|
|
66
|
+
not call gets a generated door, so the binding covers kui.h whole: 219
|
|
67
|
+
generated, 51 by hand, 2 skipped with their reason.
|
|
68
|
+
`examples/odin/tools/surface.odin` is `surface.c` through it, and calls
|
|
69
|
+
every door, and `examples/odin/tools/conformance.odin` rebuilds the
|
|
70
|
+
scene corpus through `Spec` and the doors, matching the reference report
|
|
71
|
+
byte for byte, as the Rust, Lua, C and Node adapters do.
|
|
72
|
+
`polyline` and `polygon` take points (`[][2]f32`), the count kui.h
|
|
73
|
+
means; a dash or a pivot is a fixed array in a `Maybe` (`[5]f32`,
|
|
74
|
+
`[2]f32`), so a short one cannot be written; and pixels are checked
|
|
75
|
+
against the size beside them.
|
|
76
|
+
|
|
77
|
+
It covers extensions both ways. An Odin host loads a plugin with
|
|
78
|
+
`ctx_add_extension` and `slot`. An Odin plugin is a shared library whose
|
|
79
|
+
seven `kui_ext_*` exports are one line each over `extension.odin`.
|
|
80
|
+
Built with `KUI_PLUGIN` on macOS and Linux, it links no kui and loads
|
|
81
|
+
into any host (on Windows it imports `kui_ffi.dll`, as a C plugin does):
|
|
82
|
+
`nu scripts/odin.nu slots` drives the Odin panel in the C and Rust
|
|
83
|
+
hosts and the C panel in the Odin host.
|
|
84
|
+
|
|
85
|
+
Run it with `nu scripts/odin.nu gen | test | slots | run counter`. It is
|
|
86
|
+
not published. CI's `check` installs a pinned Odin release (`ODIN_VERSION`)
|
|
87
|
+
and runs `gen --check`, `check`, `test` and `slots`, so a header or schema
|
|
88
|
+
change that was not regenerated goes red there. All four also run green
|
|
89
|
+
by hand on Windows x64 and Linux x64. On Windows the binding links
|
|
90
|
+
`kui_ffi.dll.lib`, odin.nu puts `kui_ffi.dll` beside the programs, and
|
|
91
|
+
`gen --check` reads a CRLF checkout as current.
|
|
92
|
+
- `scripts/pack-ffi.nu`: libkui_ffi for every platform kui ships, dynamic
|
|
93
|
+
and static, with `kui.h`, rustc's `native-static-libs` as `link.txt`, a
|
|
94
|
+
tarball each and `SHA256SUMS`. It is for a C or Odin host that links a
|
|
95
|
+
library instead of building the workspace. The machine's own platform
|
|
96
|
+
is built natively. Linux x64 and arm64 (glibc 2.28) and Windows x64 are
|
|
97
|
+
built in a pinned Docker image, with cargo-zigbuild at the release
|
|
98
|
+
workflow's pins and with cargo-xwin. Windows also asks for
|
|
99
|
+
`--accept-msvc-license`, for the CRT and SDK xwin downloads. macOS is
|
|
100
|
+
built on a Mac only. A Windows pack carries windows-targets' import
|
|
101
|
+
libraries (`windows.0.53.0.lib`, `windows.0.52.0.lib`), which its
|
|
102
|
+
`link.txt` names and no SDK has, so a static link finds them. Every
|
|
103
|
+
leg has been run, and a C host linked against each x64 pack alone,
|
|
104
|
+
dynamically and statically, and run; the arm64 pack was read, with no
|
|
105
|
+
arm64 machine to run it on. `kui.h` ships with LF line ends from any
|
|
106
|
+
checkout.
|
|
107
|
+
- `examples/rust/tools/schema-dump.rs`: the prop schema (props,
|
|
108
|
+
composites, elements, events, the verb table, the name lists) as JSON,
|
|
109
|
+
for a binding generated outside Rust.
|
|
110
|
+
- The verb table (`schema::DOORS`) and `docs/props.md` have an Odin
|
|
111
|
+
column beside C's, each spelling held by the Odin generator both ways;
|
|
112
|
+
Node's `protocol()` carries it (`Door.odin`).
|
|
113
|
+
|
|
114
|
+
### Fixed
|
|
115
|
+
|
|
116
|
+
- **A dead key types its accent before a key it does not combine with**
|
|
117
|
+
(backlog RG127, from the Windows and Linux round after alpha.41). With
|
|
118
|
+
German, `^ space` typed a space where it types `^`, `´ space` a space
|
|
119
|
+
where it types `´`, and `^ z` a `z` where it types `^z` — on Windows
|
|
120
|
+
always, and under X11 with `imeOff` on; on US-International, where `'`
|
|
121
|
+
and `"` are dead, a quote could not be typed at all. winit composed it
|
|
122
|
+
right: the runner read a press's text off the logical key, which is
|
|
123
|
+
the key the accent fell back to, and Space's insert was a space
|
|
124
|
+
whatever the press carried. A press's text is now what the platform
|
|
125
|
+
composed, the layout's character where it composed nothing, and Space
|
|
126
|
+
inserts its own. `^ e` was right and is unchanged; with the input
|
|
127
|
+
method on, X11 composes through XIM as before.
|
|
128
|
+
- The docs of F103 (alpha.41) said an animating Windows window behind
|
|
129
|
+
other windows skips its frames. It does not: DWM composes a covered
|
|
130
|
+
window, and it draws at the display's rate, 241 frames a second here,
|
|
131
|
+
as an uncovered one does; minimized, it asks for none (RG45). F103's
|
|
132
|
+
wait holds there only for an acquire that times out. `retry.rs` says so
|
|
133
|
+
now (backlog RG128).
|
|
134
|
+
- **Text is read aloud on Windows and Linux** (backlog RG129, from the
|
|
135
|
+
regression and smoke round after RG127). Every static text — a line of
|
|
136
|
+
text, a list row's content, a drawn title — reached UI Automation with
|
|
137
|
+
an empty Name and AT-SPI with an empty name: AccessKit names a label by
|
|
138
|
+
its value, and kui set only its label. macOS read it all along.
|
|
139
|
+
- **A disclosure or a select opens from Narrator** (RG130). UI
|
|
140
|
+
Automation gives a node with `expanded` its ExpandCollapse pattern in
|
|
141
|
+
place of Invoke, and its Expand and Collapse reached nothing. They are
|
|
142
|
+
the node's click.
|
|
143
|
+
- **A text size or line height of 0 no longer ends the process** (RG136).
|
|
144
|
+
`lineHeight: 0`, `size: 0`, or a size under 0.4 (whose line height
|
|
145
|
+
rounds to 0) aborted a Node or Lua process — cosmic-text asserts a line
|
|
146
|
+
height is not 0 — and a negative one spun its layout; on an `edit`, a
|
|
147
|
+
`cells` and in `measureText` too. Every buffer is shaped at a pixel at
|
|
148
|
+
least. C's door had always read them as unset.
|
|
149
|
+
- C: what kui.h says a door hands out lives as long as it says (RG131–
|
|
150
|
+
RG134). A second `kui_draw_data` in a frame freed the arrays the first
|
|
151
|
+
handed out; `kui_access_runs` freed the strings `kui_access_tree`
|
|
152
|
+
handed out; reading a menu row or asking for a copy freed a taken menu
|
|
153
|
+
action's text; and `kui_take_warnings` dropped what did not fit `cap`,
|
|
154
|
+
where kui.h says the rest wait.
|
|
155
|
+
- A custom editor whose `caret` or `selection_anchor` falls inside a
|
|
156
|
+
character has an access tree (RG135): the run was sliced there and
|
|
157
|
+
panicked, which under C emptied the tree every frame; the position is
|
|
158
|
+
the character's start.
|
|
159
|
+
- Node: `measureText` inside a `<devtoolsTab>` function child no longer
|
|
160
|
+
breaks the frame being encoded (RG137); a message, a `<select>` option
|
|
161
|
+
or a `menuBar` row holding a string cut through an emoji draws its lone
|
|
162
|
+
surrogate as U+FFFD rather than failing the frame, and `dir: null` is
|
|
163
|
+
absent like every other null prop (RG138, RG140).
|
|
164
|
+
|
|
165
|
+
### Native verification
|
|
166
|
+
|
|
167
|
+
The by-hand round alpha.6 introduced (backlog R4), on 2026-10-07, over
|
|
168
|
+
the rounds after the alpha.41 tag — the Odin binding and `pack-ffi.nu`,
|
|
169
|
+
RG127 and RG128 from the Windows and Linux round after alpha.41, and the
|
|
170
|
+
regression and smoke round after RG127 (RG129–RG138) — with alpha.42's
|
|
171
|
+
pre-tag pass over them on the Mac: the mechanical round, then four
|
|
172
|
+
read-only reviews of the diff since alpha.41 (the Odin binding; the C
|
|
173
|
+
doors and `pack-ffi.nu`; the runner's keys, the access bridge, the text
|
|
174
|
+
floor and Node; the docs), each claim probed. They filed RG140–RG144,
|
|
175
|
+
built before the tag (a `<select>` option or menu bar row cut through an
|
|
176
|
+
emoji failing the Node frame, `kui_draw_data` while a frame built losing
|
|
177
|
+
the finished frame's fragment draws, the Odin drains freeing the strings
|
|
178
|
+
they returned, Odin's popups, unchecked slices and acronym kinds, a cell
|
|
179
|
+
grid's infinite line height), and RG145, open.
|
|
180
|
+
|
|
181
|
+
**macOS**, the pre-tag pass. fmt and clippy are clean; `cargo test
|
|
182
|
+
--workspace --features kui-core/conformance`: **1856 tests over 139
|
|
183
|
+
suites**. The C round passes, and so do the **57 scenes**, the Odin
|
|
184
|
+
binding's `gen --check`, `check`, `test` and `slots`, Node's tests under
|
|
185
|
+
`KUI_CONFORMANCE_REQUIRED=1` (**215 of 215**), `npm run gen` with no
|
|
186
|
+
diff, the headless round and the book. The windowed round with Node's:
|
|
187
|
+
**124 windows**. The AX audit: **106/106**. RG127's dead keys, which the
|
|
188
|
+
Windows and Linux round could not reach on a Mac: twelve sequences typed
|
|
189
|
+
into the `edit` example through `CGEventPostToPid` and read back through
|
|
190
|
+
AX, each as AppKit composes it (`⌥e e` is `é`, `⌥e space` `´`, `⌥e z`
|
|
191
|
+
`´z`). The bench guard against the alpha.41 tag: **green**, the guarded
|
|
192
|
+
rows −1.2 to −0.3%, RG126's warm cell grid −0.4%.
|
|
193
|
+
|
|
194
|
+
**Windows 11 x64 (MSVC) and Linux x64 (WSL Ubuntu 24.04), rustc
|
|
195
|
+
1.99.0**, on the tree with the pre-tag fixes and `pack-ffi.nu`'s. fmt
|
|
196
|
+
and `cargo clippy --workspace --all-targets --features
|
|
197
|
+
kui-core/conformance -- -D warnings` are clean on both. `cargo test
|
|
198
|
+
--workspace --features kui-core/conformance`: **1857 tests over 140
|
|
199
|
+
suites** on Windows (5 ignored), **1853 over 139** on Linux (4
|
|
200
|
+
ignored), **0 failed**. `cargo audit --deny warnings` is clean over
|
|
201
|
+
`Cargo.lock`'s 444 crates. `pack-ffi.nu` ran every leg but macOS's: native
|
|
202
|
+
on both, and linux-x64, linux-arm64 and win32-x64 (cargo-xwin) in its
|
|
203
|
+
container. A C host linked against each x64 pack alone, dynamically and
|
|
204
|
+
statically, ran; the arm64 pack was read, not run. It found three things,
|
|
205
|
+
fixed before the tag (RG145): a CRLF `kui.h` from a CRLF checkout, the
|
|
206
|
+
closing table lost off a terminal, and a `docker` that could not be
|
|
207
|
+
spawned stopping the script. Earlier the same day, before the pre-tag
|
|
208
|
+
fixes, the Odin binding's four steps passed on both, and the regression
|
|
209
|
+
and smoke round after RG127 ran the windowed smoke and walked the
|
|
210
|
+
accessibility tree through UI Automation and AT-SPI.
|
|
211
|
+
|
|
24
212
|
## 0.1.0-alpha.41 (2026-10-07)
|
|
25
213
|
|
|
26
214
|
**What breaks.**
|
package/encoder.js
CHANGED
|
@@ -402,7 +402,8 @@ export function createEncoder(P) {
|
|
|
402
402
|
const np = fi++;
|
|
403
403
|
let n = 0;
|
|
404
404
|
// dir and size first: the decoder constructs spec/style from them.
|
|
405
|
-
|
|
405
|
+
// `!= null`: a null dir is absent, as every other null prop is.
|
|
406
|
+
if (p.dir != null && p.dir !== 'column') {
|
|
406
407
|
// `table` is a column whose rows' cells line up (ADR 0033).
|
|
407
408
|
const dir = p.dir === 'row' ? 1 : p.dir === 'table' ? 2 : undefined;
|
|
408
409
|
if (dir === undefined) throw new Error(`bad dir ${JSON.stringify(p.dir)} (row | column | table)`);
|
package/index.d.ts
CHANGED
|
@@ -1890,6 +1890,8 @@ export type DoorCell = { is: string } | { as: string } | { no: string };
|
|
|
1890
1890
|
export interface Door {
|
|
1891
1891
|
rust: string;
|
|
1892
1892
|
c: DoorCell;
|
|
1893
|
+
/** The Odin binding's (packages/odin), pinned by its generator. */
|
|
1894
|
+
odin: DoorCell;
|
|
1893
1895
|
node: DoorCell;
|
|
1894
1896
|
lua: DoorCell;
|
|
1895
1897
|
doc: string;
|
package/index.js
CHANGED
|
@@ -52,8 +52,12 @@ export function withEffects(model, ...effects) {
|
|
|
52
52
|
|
|
53
53
|
// A frame crosses the boundary one way: JS encodes the tree into one
|
|
54
54
|
// Float64Array + string table and the addon lowers it zero-copy. One encoder
|
|
55
|
-
// serves every context — its buffers are consumed synchronously.
|
|
55
|
+
// serves every context — its buffers are consumed synchronously. Measuring
|
|
56
|
+
// has one of its own: a `<devtoolsTab>`'s function child is called in the
|
|
57
|
+
// middle of an encode, and a `measureText` there reset the frame's buffers
|
|
58
|
+
// under it ("binary frame must start with the root op").
|
|
56
59
|
const encoder = createEncoder(native.protocol());
|
|
60
|
+
const measurer = createEncoder(native.protocol());
|
|
57
61
|
|
|
58
62
|
// A prop name the schema does not know has no wire id, so it never crosses:
|
|
59
63
|
// the encoder is the only side that can see it, and it reports what it
|
|
@@ -206,7 +210,7 @@ KuiWindow.prototype.setView = function setView(tree, window) {
|
|
|
206
210
|
// it draws. A style name the schema does not know is reported like a
|
|
207
211
|
// view's would be.
|
|
208
212
|
function measureText(content, style, maxWidth) {
|
|
209
|
-
const { stream, strings, unknown, unknownTokens } =
|
|
213
|
+
const { stream, strings, unknown, unknownTokens } = measurer.encodeText(content, style, this[TOKENS]);
|
|
210
214
|
const m = this.measureTextBinary(stream, strings, maxWidth);
|
|
211
215
|
reportUnknown(this, unknown, unknownTokens);
|
|
212
216
|
return m;
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|