@qxuken/kui 0.1.0-alpha.35 → 0.1.0-alpha.37
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 +13739 -13313
- package/LICENSE +21 -21
- package/README.md +336 -336
- package/docs/adr/0001-accessibility-as-data.md +414 -414
- package/docs/adr/0002-keyboard-focus-as-data.md +452 -452
- package/docs/adr/0003-modal-surfaces.md +248 -248
- package/docs/adr/0004-multi-window.md +626 -626
- package/docs/adr/0005-the-paint-vocabulary.md +606 -601
- package/docs/adr/0006-c-abi-versioning.md +257 -257
- package/docs/adr/0007-composite-keyboard-patterns.md +350 -350
- package/docs/adr/0008-live-regions-and-announcements.md +379 -379
- package/docs/adr/0009-press-drag-release-into-a-popup.md +318 -318
- package/docs/adr/0010-a-segment-primitive.md +456 -410
- package/docs/adr/0011-keys-bubble-to-the-enclosing-sink.md +258 -258
- package/docs/adr/0012-the-exit-budget.md +411 -411
- package/docs/adr/0013-effects-as-data.md +197 -197
- package/docs/adr/0014-slots-an-extension-fills-in-place.md +411 -411
- package/docs/adr/0015-a-fragment-element-and-the-painter-it-is-not.md +559 -559
- package/docs/adr/0016-caching-against-the-last-frame.md +351 -351
- package/docs/adr/0017-selection-as-a-scope.md +696 -696
- package/docs/adr/0018-a-menu-bar-the-app-declares.md +264 -264
- package/docs/adr/0019-a-theme-derived-from-appearance-and-accent.md +382 -382
- package/docs/adr/0020-the-surface-the-schema-does-not-cover.md +351 -351
- package/docs/adr/0021-one-subject-per-example.md +729 -729
- package/docs/adr/0022-focus-regions.md +205 -205
- package/docs/adr/0023-layers-stack-in-the-order-they-open.md +376 -376
- package/docs/adr/0024-the-devtools-are-the-cores.md +425 -425
- package/docs/adr/0025-the-image-is-the-canvas.md +507 -507
- package/docs/adr/0026-hit-testing-by-shape.md +197 -197
- package/docs/adr/0027-tokens-beside-the-theme.md +521 -521
- package/docs/adr/0028-derived-tokens.md +572 -572
- package/docs/adr/0029-a-selection-follows-the-pointer-past-the-edge.md +543 -543
- package/docs/adr/0030-the-standard-menus-the-runner-keeps.md +292 -292
- package/docs/adr/0031-a-drop-zone-is-a-row-and-the-files-are-an-event.md +409 -409
- package/docs/adr/0032-a-devtools-tab-mounts-a-slot.md +480 -480
- package/docs/adr/0033-a-table-is-a-column-whose-cells-align.md +377 -377
- package/docs/adr/0034-stock-controls-over-the-roles.md +192 -192
- package/docs/adr/0035-a-rounded-background-is-joined-by-meeting.md +134 -134
- package/docs/adr/0036-an-event-handler-gets-its-window.md +142 -142
- package/docs/adr/0037-a-family-is-named.md +134 -134
- package/docs/adr/0038-a-scroll-gesture-latches-its-target.md +188 -188
- package/docs/adr/0039-a-tutorial-is-a-sequence.md +139 -139
- package/docs/adr/0040-a-path-is-a-mask-in-the-atlas.md +470 -0
- package/docs/adr/0041-a-mask-turns-about-its-centre.md +337 -0
- package/docs/adr/0042-a-gradient-is-an-image-the-core-paints.md +455 -0
- package/encoder.js +1340 -1282
- package/howto.md +1857 -1774
- package/index.d.ts +5192 -5162
- package/index.js +1414 -1414
- package/jsx-dev-runtime.js +1 -1
- package/jsx-runtime.d.ts +1007 -905
- package/jsx-runtime.js +45 -45
- package/native.cjs +118 -118
- package/package.json +51 -51
- package/prebuilds/darwin-arm64/kui_node.node +0 -0
- package/prebuilds/darwin-x64/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 +643 -636
|
@@ -1,134 +1,134 @@
|
|
|
1
|
-
---
|
|
2
|
-
status: accepted
|
|
3
|
-
date: 2026-09-27
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# A family is named where the text declares it
|
|
7
|
-
|
|
8
|
-
> **Accepted and built 2026-09-27**, the day it was proposed (backlog
|
|
9
|
-
> DX17, from the DX sweep of kawoosh's views). The user chose frame v18
|
|
10
|
-
> for decision 4. What the building settled: the parser resolves the
|
|
11
|
-
> name. `NameRefs::family`, over a token lookup that now carries the
|
|
12
|
-
> session, registers it through `Session::register_family`, the body
|
|
13
|
-
> `add_system_font` always had, moved where a parse holding the core's
|
|
14
|
-
> lookup can reach it. So Lua and Node resolve it in the one step they
|
|
15
|
-
> already resolve a `$token` in. A miss is remembered beside the token
|
|
16
|
-
> misses and raised by the same code. The schema gained `Kind::Family`
|
|
17
|
-
> and `Parsed::Family`. C keeps the index and the handle door (decision
|
|
18
|
-
> 5), and its parity sample is the stock family at index 1. `font` and
|
|
19
|
-
> `family` write the same field, so the last one declared wins, and the
|
|
20
|
-
> row's doc says so rather than "the handle wins".
|
|
21
|
-
|
|
22
|
-
`family` takes `sans`, `serif` or `mono`. Any other face is drawn with a
|
|
23
|
-
handle: the host registers the family by name (`Core::add_system_font`,
|
|
24
|
-
`addSystemFont`, `kui_font_add_system`) and hands the `FontId` down, and
|
|
25
|
-
the text names it as `font`. A Lua view cannot register anything (ADR
|
|
26
|
-
0014: registering is the host's), so a script that shows an installed
|
|
27
|
-
face needs its host to register it and pass it in. kawoosh built that
|
|
28
|
-
door, `kawoosh.fonts.face(NAME)`. Its first cut registered a family
|
|
29
|
-
when a card first asked for it, at the next frame, so every card the
|
|
30
|
-
fonts pane scrolled to drew one frame in kui's mono and then in its own
|
|
31
|
-
face. The fix it shipped registers all 613 installed families at once,
|
|
32
|
-
10 ms once in a debug build (`kawoosh/src/fonts.rs:7-12`,
|
|
33
|
-
`docs/design/fonts.md:48-60`).
|
|
34
|
-
|
|
35
|
-
## Context
|
|
36
|
-
|
|
37
|
-
- **Registering a family by name loads nothing.** The font database
|
|
38
|
-
scans every installed face's metadata when it starts, which is how
|
|
39
|
-
`system_fonts` (F97) lists them without opening a file. So
|
|
40
|
-
`add_system_font` is a query against that scan and an idempotent
|
|
41
|
-
registry entry. The face's bytes are memory-mapped and shaped only
|
|
42
|
-
when a text first uses it, as for any face. Resolving a name in the
|
|
43
|
-
frame that declares it costs a lookup. The frame of mono in kawoosh
|
|
44
|
-
came from its host deferring the registration a frame, not from kui.
|
|
45
|
-
(Since DX24 the session's first registration of any font also maps
|
|
46
|
-
the installed files once, ~30 ms on a Mac, so the first frame to name
|
|
47
|
-
a family pays that; noted by the alpha.22 regression pass.)
|
|
48
|
-
- **`TextStyle` is `Copy`,** and views rely on that everywhere (a
|
|
49
|
-
closure capturing a style, `style` passed by value). A name cannot
|
|
50
|
-
live in it. A handle can, which is why `FontFamily::Custom(FontId)`
|
|
51
|
-
exists.
|
|
52
|
-
- **Every binding lowers a style where the core is at hand.** Lua's
|
|
53
|
-
parser runs inside `view` with the `Ui`. Node's frame is lowered by
|
|
54
|
-
the addon (`binary.rs`), with the window's `Core`. C builds a
|
|
55
|
-
`KuiTextStyle` itself and has `kui_font_add_system`. Rust has the
|
|
56
|
-
`Ui`.
|
|
57
|
-
- **The weights follow a family, not a handle.** RG59 reweighs text
|
|
58
|
-
already shaped when a face of its family is loaded, and F100
|
|
59
|
-
synthesizes bold for a family with none. Both key on the family. A
|
|
60
|
-
named family resolves to the handle `add_system_font` returns, so
|
|
61
|
-
both keep working.
|
|
62
|
-
|
|
63
|
-
## Decisions
|
|
64
|
-
|
|
65
|
-
1. **`family` takes an installed family's name.** `family = "Berkeley
|
|
66
|
-
Mono"` in Lua, `family="Berkeley Mono"` in JSX. The stock names
|
|
67
|
-
`sans`, `serif` and `mono`, lowercase, stay the stock families. Any
|
|
68
|
-
other string is a family name, matched as `add_system_font` matches
|
|
69
|
-
it: installed, or loaded with `load_fonts_dir` / `load_font_file`.
|
|
70
|
-
2. **The name is resolved where the style is lowered, to the handle.**
|
|
71
|
-
The binding's lowering calls `Core::add_system_font(name)` and sets
|
|
72
|
-
`FontFamily::Custom(id)`, so the text shapes in its face in the frame
|
|
73
|
-
that declares it. `TextStyle` stays `Copy`, and nothing after
|
|
74
|
-
lowering knows a name was involved. The registry's answer is
|
|
75
|
-
idempotent. A name lowered every frame costs a lookup, and a
|
|
76
|
-
name→handle map in the session makes it constant if a profile asks.
|
|
77
|
-
3. **A name nothing matches is a warning, and sans.** `unknown-family`,
|
|
78
|
-
keyed by the name, so a typo in a view drawn every frame is one line.
|
|
79
|
-
The text shapes as sans, as a stale handle does today.
|
|
80
|
-
4. **Node carries the name on the wire.** The frame's `family` slot is
|
|
81
|
-
an index into `schema::FAMILIES`. A name needs the string table the
|
|
82
|
-
frame already has for text. So `family` becomes an index for the
|
|
83
|
-
stock three and a string for anything else, and the frame is v18.
|
|
84
|
-
The encoder and addon ship together, so this breaks only a stale
|
|
85
|
-
prebuilt addon, which refuses the stream by its version. The other
|
|
86
|
-
route is below, with why it is second.
|
|
87
|
-
5. **Rust and C gain nothing new but a pass-through.** Both already hold
|
|
88
|
-
the door. `Ui::system_font(name) -> Option<FontId>` is added so a
|
|
89
|
-
Rust view needs no `core()` to reach it. C keeps
|
|
90
|
-
`kui_font_add_system` and `KuiTextStyle.font`.
|
|
91
|
-
6. **The prop's type says it.** The schema row's kind becomes a family
|
|
92
|
-
kind, the three stock names or a string. Lua's meta types it
|
|
93
|
-
`"sans"|"serif"|"mono"|string`, and TS `'sans' | 'serif' | 'mono' |
|
|
94
|
-
(string & {})`, so an editor still offers the three.
|
|
95
|
-
|
|
96
|
-
## Considered options
|
|
97
|
-
|
|
98
|
-
- **Leave it to the host** (today). It works for Rust, C and Node, which
|
|
99
|
-
can call the door. For Lua it means every host that shows a face
|
|
100
|
-
builds kawoosh's `fonts.face` and learns about its flicker.
|
|
101
|
-
- **A Lua `env.add_system_font` door.** It would let a script register.
|
|
102
|
-
But registering is the host's (ADR 0014), and a handle is a thing to
|
|
103
|
-
keep and pass around. A family by name has nothing to own or free,
|
|
104
|
-
like `family = "mono"`, so it can be a declaration, not a verb.
|
|
105
|
-
- **Node resolves in JS**, with `ctx.addSystemFont(name)` from the
|
|
106
|
-
encoder, cached per name, emitting the `font` handle the wire already
|
|
107
|
-
has. No frame version, but the encoder would need a context. Today
|
|
108
|
-
there is one encoder per module, built from `protocol()` alone
|
|
109
|
-
(`index.js:56`) and shared by every window and every test. A string on
|
|
110
|
-
the wire keeps it that way.
|
|
111
|
-
- **Load asynchronously and swap** (CSS's `font-display: swap`): draw a
|
|
112
|
-
fallback, register off the frame, reshape when ready. It answers a
|
|
113
|
-
cost that is not there (decision 2's context). The swap is the flicker
|
|
114
|
-
kawoosh removed.
|
|
115
|
-
- **A root `fonts = { … }` preload.** A second place to name a family,
|
|
116
|
-
for the same lookup the prop makes.
|
|
117
|
-
|
|
118
|
-
## Consequences
|
|
119
|
-
|
|
120
|
-
- A Lua view draws an installed face by naming it. kawoosh's
|
|
121
|
-
`kawoosh.fonts.face` and its 613-family registration go, and the fonts
|
|
122
|
-
pane's cards say `family = name`.
|
|
123
|
-
- The Node frame is v18: one slot's encoding, under What breaks.
|
|
124
|
-
`schema::FAMILIES` and C's `KUI_FONT_*` are unchanged.
|
|
125
|
-
- One new warning, `unknown-family`, in `diag::WARNINGS`, the TS union
|
|
126
|
-
and the Lua docs.
|
|
127
|
-
- A family's handles and weights come from one registry. A family named
|
|
128
|
-
in a view and one registered by the host get the same handle,
|
|
129
|
-
pinned by a test.
|
|
130
|
-
- Built with a core test that a named family shapes in its face in the
|
|
131
|
-
frame that names it (no frame of sans), the warning for a name
|
|
132
|
-
nothing matches, a Lua and a Node scene naming a family the corpus
|
|
133
|
-
loads from its fixture font, and the reweigh (RG59) still following a
|
|
134
|
-
named family's new face.
|
|
1
|
+
---
|
|
2
|
+
status: accepted
|
|
3
|
+
date: 2026-09-27
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# A family is named where the text declares it
|
|
7
|
+
|
|
8
|
+
> **Accepted and built 2026-09-27**, the day it was proposed (backlog
|
|
9
|
+
> DX17, from the DX sweep of kawoosh's views). The user chose frame v18
|
|
10
|
+
> for decision 4. What the building settled: the parser resolves the
|
|
11
|
+
> name. `NameRefs::family`, over a token lookup that now carries the
|
|
12
|
+
> session, registers it through `Session::register_family`, the body
|
|
13
|
+
> `add_system_font` always had, moved where a parse holding the core's
|
|
14
|
+
> lookup can reach it. So Lua and Node resolve it in the one step they
|
|
15
|
+
> already resolve a `$token` in. A miss is remembered beside the token
|
|
16
|
+
> misses and raised by the same code. The schema gained `Kind::Family`
|
|
17
|
+
> and `Parsed::Family`. C keeps the index and the handle door (decision
|
|
18
|
+
> 5), and its parity sample is the stock family at index 1. `font` and
|
|
19
|
+
> `family` write the same field, so the last one declared wins, and the
|
|
20
|
+
> row's doc says so rather than "the handle wins".
|
|
21
|
+
|
|
22
|
+
`family` takes `sans`, `serif` or `mono`. Any other face is drawn with a
|
|
23
|
+
handle: the host registers the family by name (`Core::add_system_font`,
|
|
24
|
+
`addSystemFont`, `kui_font_add_system`) and hands the `FontId` down, and
|
|
25
|
+
the text names it as `font`. A Lua view cannot register anything (ADR
|
|
26
|
+
0014: registering is the host's), so a script that shows an installed
|
|
27
|
+
face needs its host to register it and pass it in. kawoosh built that
|
|
28
|
+
door, `kawoosh.fonts.face(NAME)`. Its first cut registered a family
|
|
29
|
+
when a card first asked for it, at the next frame, so every card the
|
|
30
|
+
fonts pane scrolled to drew one frame in kui's mono and then in its own
|
|
31
|
+
face. The fix it shipped registers all 613 installed families at once,
|
|
32
|
+
10 ms once in a debug build (`kawoosh/src/fonts.rs:7-12`,
|
|
33
|
+
`docs/design/fonts.md:48-60`).
|
|
34
|
+
|
|
35
|
+
## Context
|
|
36
|
+
|
|
37
|
+
- **Registering a family by name loads nothing.** The font database
|
|
38
|
+
scans every installed face's metadata when it starts, which is how
|
|
39
|
+
`system_fonts` (F97) lists them without opening a file. So
|
|
40
|
+
`add_system_font` is a query against that scan and an idempotent
|
|
41
|
+
registry entry. The face's bytes are memory-mapped and shaped only
|
|
42
|
+
when a text first uses it, as for any face. Resolving a name in the
|
|
43
|
+
frame that declares it costs a lookup. The frame of mono in kawoosh
|
|
44
|
+
came from its host deferring the registration a frame, not from kui.
|
|
45
|
+
(Since DX24 the session's first registration of any font also maps
|
|
46
|
+
the installed files once, ~30 ms on a Mac, so the first frame to name
|
|
47
|
+
a family pays that; noted by the alpha.22 regression pass.)
|
|
48
|
+
- **`TextStyle` is `Copy`,** and views rely on that everywhere (a
|
|
49
|
+
closure capturing a style, `style` passed by value). A name cannot
|
|
50
|
+
live in it. A handle can, which is why `FontFamily::Custom(FontId)`
|
|
51
|
+
exists.
|
|
52
|
+
- **Every binding lowers a style where the core is at hand.** Lua's
|
|
53
|
+
parser runs inside `view` with the `Ui`. Node's frame is lowered by
|
|
54
|
+
the addon (`binary.rs`), with the window's `Core`. C builds a
|
|
55
|
+
`KuiTextStyle` itself and has `kui_font_add_system`. Rust has the
|
|
56
|
+
`Ui`.
|
|
57
|
+
- **The weights follow a family, not a handle.** RG59 reweighs text
|
|
58
|
+
already shaped when a face of its family is loaded, and F100
|
|
59
|
+
synthesizes bold for a family with none. Both key on the family. A
|
|
60
|
+
named family resolves to the handle `add_system_font` returns, so
|
|
61
|
+
both keep working.
|
|
62
|
+
|
|
63
|
+
## Decisions
|
|
64
|
+
|
|
65
|
+
1. **`family` takes an installed family's name.** `family = "Berkeley
|
|
66
|
+
Mono"` in Lua, `family="Berkeley Mono"` in JSX. The stock names
|
|
67
|
+
`sans`, `serif` and `mono`, lowercase, stay the stock families. Any
|
|
68
|
+
other string is a family name, matched as `add_system_font` matches
|
|
69
|
+
it: installed, or loaded with `load_fonts_dir` / `load_font_file`.
|
|
70
|
+
2. **The name is resolved where the style is lowered, to the handle.**
|
|
71
|
+
The binding's lowering calls `Core::add_system_font(name)` and sets
|
|
72
|
+
`FontFamily::Custom(id)`, so the text shapes in its face in the frame
|
|
73
|
+
that declares it. `TextStyle` stays `Copy`, and nothing after
|
|
74
|
+
lowering knows a name was involved. The registry's answer is
|
|
75
|
+
idempotent. A name lowered every frame costs a lookup, and a
|
|
76
|
+
name→handle map in the session makes it constant if a profile asks.
|
|
77
|
+
3. **A name nothing matches is a warning, and sans.** `unknown-family`,
|
|
78
|
+
keyed by the name, so a typo in a view drawn every frame is one line.
|
|
79
|
+
The text shapes as sans, as a stale handle does today.
|
|
80
|
+
4. **Node carries the name on the wire.** The frame's `family` slot is
|
|
81
|
+
an index into `schema::FAMILIES`. A name needs the string table the
|
|
82
|
+
frame already has for text. So `family` becomes an index for the
|
|
83
|
+
stock three and a string for anything else, and the frame is v18.
|
|
84
|
+
The encoder and addon ship together, so this breaks only a stale
|
|
85
|
+
prebuilt addon, which refuses the stream by its version. The other
|
|
86
|
+
route is below, with why it is second.
|
|
87
|
+
5. **Rust and C gain nothing new but a pass-through.** Both already hold
|
|
88
|
+
the door. `Ui::system_font(name) -> Option<FontId>` is added so a
|
|
89
|
+
Rust view needs no `core()` to reach it. C keeps
|
|
90
|
+
`kui_font_add_system` and `KuiTextStyle.font`.
|
|
91
|
+
6. **The prop's type says it.** The schema row's kind becomes a family
|
|
92
|
+
kind, the three stock names or a string. Lua's meta types it
|
|
93
|
+
`"sans"|"serif"|"mono"|string`, and TS `'sans' | 'serif' | 'mono' |
|
|
94
|
+
(string & {})`, so an editor still offers the three.
|
|
95
|
+
|
|
96
|
+
## Considered options
|
|
97
|
+
|
|
98
|
+
- **Leave it to the host** (today). It works for Rust, C and Node, which
|
|
99
|
+
can call the door. For Lua it means every host that shows a face
|
|
100
|
+
builds kawoosh's `fonts.face` and learns about its flicker.
|
|
101
|
+
- **A Lua `env.add_system_font` door.** It would let a script register.
|
|
102
|
+
But registering is the host's (ADR 0014), and a handle is a thing to
|
|
103
|
+
keep and pass around. A family by name has nothing to own or free,
|
|
104
|
+
like `family = "mono"`, so it can be a declaration, not a verb.
|
|
105
|
+
- **Node resolves in JS**, with `ctx.addSystemFont(name)` from the
|
|
106
|
+
encoder, cached per name, emitting the `font` handle the wire already
|
|
107
|
+
has. No frame version, but the encoder would need a context. Today
|
|
108
|
+
there is one encoder per module, built from `protocol()` alone
|
|
109
|
+
(`index.js:56`) and shared by every window and every test. A string on
|
|
110
|
+
the wire keeps it that way.
|
|
111
|
+
- **Load asynchronously and swap** (CSS's `font-display: swap`): draw a
|
|
112
|
+
fallback, register off the frame, reshape when ready. It answers a
|
|
113
|
+
cost that is not there (decision 2's context). The swap is the flicker
|
|
114
|
+
kawoosh removed.
|
|
115
|
+
- **A root `fonts = { … }` preload.** A second place to name a family,
|
|
116
|
+
for the same lookup the prop makes.
|
|
117
|
+
|
|
118
|
+
## Consequences
|
|
119
|
+
|
|
120
|
+
- A Lua view draws an installed face by naming it. kawoosh's
|
|
121
|
+
`kawoosh.fonts.face` and its 613-family registration go, and the fonts
|
|
122
|
+
pane's cards say `family = name`.
|
|
123
|
+
- The Node frame is v18: one slot's encoding, under What breaks.
|
|
124
|
+
`schema::FAMILIES` and C's `KUI_FONT_*` are unchanged.
|
|
125
|
+
- One new warning, `unknown-family`, in `diag::WARNINGS`, the TS union
|
|
126
|
+
and the Lua docs.
|
|
127
|
+
- A family's handles and weights come from one registry. A family named
|
|
128
|
+
in a view and one registered by the host get the same handle,
|
|
129
|
+
pinned by a test.
|
|
130
|
+
- Built with a core test that a named family shapes in its face in the
|
|
131
|
+
frame that names it (no frame of sans), the warning for a name
|
|
132
|
+
nothing matches, a Lua and a Node scene naming a family the corpus
|
|
133
|
+
loads from its fixture font, and the reweigh (RG59) still following a
|
|
134
|
+
named family's new face.
|