@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.
Files changed (60) hide show
  1. package/CHANGELOG.md +13739 -13313
  2. package/LICENSE +21 -21
  3. package/README.md +336 -336
  4. package/docs/adr/0001-accessibility-as-data.md +414 -414
  5. package/docs/adr/0002-keyboard-focus-as-data.md +452 -452
  6. package/docs/adr/0003-modal-surfaces.md +248 -248
  7. package/docs/adr/0004-multi-window.md +626 -626
  8. package/docs/adr/0005-the-paint-vocabulary.md +606 -601
  9. package/docs/adr/0006-c-abi-versioning.md +257 -257
  10. package/docs/adr/0007-composite-keyboard-patterns.md +350 -350
  11. package/docs/adr/0008-live-regions-and-announcements.md +379 -379
  12. package/docs/adr/0009-press-drag-release-into-a-popup.md +318 -318
  13. package/docs/adr/0010-a-segment-primitive.md +456 -410
  14. package/docs/adr/0011-keys-bubble-to-the-enclosing-sink.md +258 -258
  15. package/docs/adr/0012-the-exit-budget.md +411 -411
  16. package/docs/adr/0013-effects-as-data.md +197 -197
  17. package/docs/adr/0014-slots-an-extension-fills-in-place.md +411 -411
  18. package/docs/adr/0015-a-fragment-element-and-the-painter-it-is-not.md +559 -559
  19. package/docs/adr/0016-caching-against-the-last-frame.md +351 -351
  20. package/docs/adr/0017-selection-as-a-scope.md +696 -696
  21. package/docs/adr/0018-a-menu-bar-the-app-declares.md +264 -264
  22. package/docs/adr/0019-a-theme-derived-from-appearance-and-accent.md +382 -382
  23. package/docs/adr/0020-the-surface-the-schema-does-not-cover.md +351 -351
  24. package/docs/adr/0021-one-subject-per-example.md +729 -729
  25. package/docs/adr/0022-focus-regions.md +205 -205
  26. package/docs/adr/0023-layers-stack-in-the-order-they-open.md +376 -376
  27. package/docs/adr/0024-the-devtools-are-the-cores.md +425 -425
  28. package/docs/adr/0025-the-image-is-the-canvas.md +507 -507
  29. package/docs/adr/0026-hit-testing-by-shape.md +197 -197
  30. package/docs/adr/0027-tokens-beside-the-theme.md +521 -521
  31. package/docs/adr/0028-derived-tokens.md +572 -572
  32. package/docs/adr/0029-a-selection-follows-the-pointer-past-the-edge.md +543 -543
  33. package/docs/adr/0030-the-standard-menus-the-runner-keeps.md +292 -292
  34. package/docs/adr/0031-a-drop-zone-is-a-row-and-the-files-are-an-event.md +409 -409
  35. package/docs/adr/0032-a-devtools-tab-mounts-a-slot.md +480 -480
  36. package/docs/adr/0033-a-table-is-a-column-whose-cells-align.md +377 -377
  37. package/docs/adr/0034-stock-controls-over-the-roles.md +192 -192
  38. package/docs/adr/0035-a-rounded-background-is-joined-by-meeting.md +134 -134
  39. package/docs/adr/0036-an-event-handler-gets-its-window.md +142 -142
  40. package/docs/adr/0037-a-family-is-named.md +134 -134
  41. package/docs/adr/0038-a-scroll-gesture-latches-its-target.md +188 -188
  42. package/docs/adr/0039-a-tutorial-is-a-sequence.md +139 -139
  43. package/docs/adr/0040-a-path-is-a-mask-in-the-atlas.md +470 -0
  44. package/docs/adr/0041-a-mask-turns-about-its-centre.md +337 -0
  45. package/docs/adr/0042-a-gradient-is-an-image-the-core-paints.md +455 -0
  46. package/encoder.js +1340 -1282
  47. package/howto.md +1857 -1774
  48. package/index.d.ts +5192 -5162
  49. package/index.js +1414 -1414
  50. package/jsx-dev-runtime.js +1 -1
  51. package/jsx-runtime.d.ts +1007 -905
  52. package/jsx-runtime.js +45 -45
  53. package/native.cjs +118 -118
  54. package/package.json +51 -51
  55. package/prebuilds/darwin-arm64/kui_node.node +0 -0
  56. package/prebuilds/darwin-x64/kui_node.node +0 -0
  57. package/prebuilds/linux-arm64/kui_node.node +0 -0
  58. package/prebuilds/linux-x64/kui_node.node +0 -0
  59. package/prebuilds/win32-x64/kui_node.node +0 -0
  60. 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.