kerfjs 5.0.0-beta.50 → 5.0.0-beta.51
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 +456 -4
- package/README.md +42 -4
- package/ai/manifest.json +1 -1
- package/dist/{chunk-JRKE6XEK.js → chunk-3GSWKMBZ.js} +39 -29
- package/dist/chunk-3GSWKMBZ.js.map +1 -0
- package/dist/{chunk-DLY444MN.js → chunk-H3TTM7PQ.js} +32 -22
- package/dist/chunk-H3TTM7PQ.js.map +1 -0
- package/dist/html.js +1 -1
- package/dist/index.js +2 -2
- package/dist/jsx-runtime.js +1 -1
- package/dist/list.d.ts +4 -1
- package/dist/list.js +68 -13
- package/dist/list.js.map +1 -1
- package/dist/overlay.d.ts +13 -3
- package/dist/overlay.js +358 -262
- package/dist/overlay.js.map +1 -1
- package/dist/remount.js +1 -1
- package/dist/router.js +1 -1
- package/dist/scope.js +1 -1
- package/llms.txt +3 -0
- package/package.json +4 -4
- package/dist/chunk-DLY444MN.js.map +0 -1
- package/dist/chunk-JRKE6XEK.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,467 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
- Added `wa-dialog.hide-actions` support to `@kerfjs/ui/webawesome.css`, hiding
|
|
4
|
-
Web Awesome's directly exported `header-actions` shadow part for dialogs that
|
|
5
|
-
provide their own dismissal affordance.
|
|
6
|
-
|
|
7
3
|
All notable changes to **kerf** are documented in this file.
|
|
8
4
|
|
|
9
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
10
6
|
|
|
11
7
|
## [Unreleased]
|
|
12
8
|
|
|
9
|
+
- `@kerfjs/ui/catalog`'s application-sized (`height: "app"`) example frame
|
|
10
|
+
no longer loses taps in Safari/WebKit. The frame became the containing block
|
|
11
|
+
for screen-fixed chrome through `contain: layout`. Combined with its clip,
|
|
12
|
+
that made WebKit mis-hit-test any transformed element inside an open compact
|
|
13
|
+
overlay. Every pressed `ListItem` scales, so taps on an overlay rail's items
|
|
14
|
+
went to their section instead. The frame now uses an identity transform,
|
|
15
|
+
which docks the same chrome without the bug and renders identically.
|
|
16
|
+
- **Breaking (`@kerfjs/ui/catalog`):** `CatalogExample`'s viewport drops
|
|
17
|
+
`height: "fill"` and `fillChildren`. The catalog lays examples out at their
|
|
18
|
+
content height, so neither could give a specimen a definite height, and a
|
|
19
|
+
child sized with `height: 100%` (a horizontal `ResizableRegion`, say)
|
|
20
|
+
collapsed to its content. Use a fixed `height` (`short` through `app`) when
|
|
21
|
+
a specimen must fill its frame. `minHeight` remains, documented as a minimum
|
|
22
|
+
rather than a fill.
|
|
23
|
+
- `create-kerf-component`'s `kerf.components.json` now accepts an optional
|
|
24
|
+
`composition.wiring.stateAttributes` declaration (the wiring-owned state
|
|
25
|
+
attributes `@kerfjs/ui`'s catalog now carries), so third-party
|
|
26
|
+
component authors can list the `data-*` attributes their wiring helpers write
|
|
27
|
+
(`{ name, on, helper, meaning }`). `kerf-component-catalog` passes them
|
|
28
|
+
through to the generated `component-catalog-v2.json` and reports a non-`data-*`
|
|
29
|
+
name, a duplicate name, an empty `on` or `meaning`, or a helper missing from
|
|
30
|
+
`wiring.helpers` at its exact path.
|
|
31
|
+
- `@kerfjs/ui`'s component catalog now declares **wiring-owned state
|
|
32
|
+
attributes**. A new optional `wiring.stateAttributes` in
|
|
33
|
+
`component-catalog-v2` (and per wiring item in v1) lists each `data-*`
|
|
34
|
+
attribute a wiring helper writes at runtime, such as `data-handle-inset` and
|
|
35
|
+
`data-resizing` from `wireResizableRegions`. For each one it records the
|
|
36
|
+
element, the helper, and the meaning, so AI consumers and tools can tell
|
|
37
|
+
them apart from attributes the app writes. Every first-party wiring helper
|
|
38
|
+
is declared. `check:catalog` fails when a helper's declarations differ from
|
|
39
|
+
what its source writes.
|
|
40
|
+
- `@kerfjs/ui`'s `wireSidebar` no longer leaves focus inside a panel it just
|
|
41
|
+
collapsed. When the collapse was triggered from a toggle inside the panel
|
|
42
|
+
(the documented placement), focus now moves to that panel's toggle outside
|
|
43
|
+
it, including one the app renders only once the panel is collapsed. The
|
|
44
|
+
collapsible-sidebar recipe now shows one control per action: each panel's
|
|
45
|
+
own collapse toggle while it is open, and the main header's expand toggle
|
|
46
|
+
only while it is collapsed. Wide widths no longer show two rail toggles.
|
|
47
|
+
- `@kerfjs/ui`'s `wireSidebar` keeps a compact overlay intact when the app
|
|
48
|
+
re-renders the wired root without changing a panel. Previously, selecting a
|
|
49
|
+
destination in an open compact rail morphed the root, which removed the
|
|
50
|
+
overlay attributes and the backdrop, and the "overlay" rail fell back into
|
|
51
|
+
the layout. The wire now re-applies its host attributes and backdrop after
|
|
52
|
+
any such re-render, and the backdrop carries `data-morph-preserve`.
|
|
53
|
+
- `@kerfjs/ui`'s `wireSidebar` now opens a compact overlay only on a user
|
|
54
|
+
action. Wiring on a compact device, or crossing from a wide device class to
|
|
55
|
+
a compact one, collapses every panel. The page is no longer covered by a
|
|
56
|
+
backdrop and focus trap until dismissed. Crossing back to a wide class, or
|
|
57
|
+
disposing the wire, restores each panel's remembered inline state.
|
|
58
|
+
Persistence now stores only that inline choice, never an overlay's
|
|
59
|
+
open/closed state. A new optional `WireSidebarPanel.inlineCollapsed`
|
|
60
|
+
declares the inline default when an app seeds its signal from the device
|
|
61
|
+
class, which avoids an open first frame on a compact device. The
|
|
62
|
+
collapsible-sidebar recipe no longer opens its rail as a blocking overlay
|
|
63
|
+
on load at compact widths.
|
|
64
|
+
- `@kerfjs/ui`'s `ResizableRegion` handle keeps its hit target and focus ring
|
|
65
|
+
whole at a clipping edge. When a parent clamps the region to its own edge
|
|
66
|
+
(the catalog's 390px stage, an app shell with a `max-width` container),
|
|
67
|
+
`wireResizableRegions` marks the region `data-handle-inset` and the handle
|
|
68
|
+
moves inside it instead of overhanging the edge by 10px, where the frame cut
|
|
69
|
+
its focus ring in half. The ring is now drawn inside the handle's box
|
|
70
|
+
(`outline-offset: -3px`), so it is also no longer shaved where the region's
|
|
71
|
+
ends meet a clipping edge.
|
|
72
|
+
- `@kerfjs/ui`'s `wireResizableRegions` keeps a clamped separator's
|
|
73
|
+
`aria-valuenow`/`aria-valuemax` current at rest. Previously, after a size was
|
|
74
|
+
saved at a wider width, narrowing the parent or any unrelated re-render left
|
|
75
|
+
the handle reporting the unclamped rendered size until it was next focused or
|
|
76
|
+
resized. A `ResizeObserver` on each region and its parent and a
|
|
77
|
+
`MutationObserver` for re-renders now re-clamp the report; the disposer
|
|
78
|
+
disconnects both.
|
|
79
|
+
- The `@kerfjs/ui` catalog's `height: "app"` example frame is now the
|
|
80
|
+
containing block for screen-fixed chrome, so a compact `CollapsiblePanel`
|
|
81
|
+
overlay, its backdrop, and restore controls dock to the frame's edges. The
|
|
82
|
+
collapsible-sidebar recipe's compact rail was fixed to the page's left edge,
|
|
83
|
+
24px outside the inset frame, so its leading edge looked cut off at 390px.
|
|
84
|
+
- The `CollapsiblePanel` compact-overlay backdrop now covers its whole
|
|
85
|
+
containing block. As a `<button>`, it did not stretch between its insets and
|
|
86
|
+
scrimmed only a 35px strip.
|
|
87
|
+
- `@kerfjs/ui`'s `wireResizableRegions` now bounds a resize to the space the
|
|
88
|
+
region's parent actually leaves. When a parent clamps the track (a
|
|
89
|
+
`max-width` stage, a narrow container), pointer and keyboard resizing stop at
|
|
90
|
+
the visible size, and the separator reports that size in `aria-valuenow` and
|
|
91
|
+
its visible maximum in `aria-valuemax`. Previously a region resized to 420
|
|
92
|
+
inside a 340px stage announced 420 and needed several key presses before the
|
|
93
|
+
separator moved. Focusing the handle reports a stale larger committed size as
|
|
94
|
+
the shown size without committing it.
|
|
95
|
+
- The UI evaluator's `KUI-B050` target-size rule now accepts a 44×36 target
|
|
96
|
+
inside compact density (`[data-density="compact"]`), matching the 36px
|
|
97
|
+
compact row height; standard density still requires 44×44, and the width
|
|
98
|
+
floor is unchanged.
|
|
99
|
+
- Fixed `kerf-ui-analyze` and the `eslint-plugin-kerfjs` UI rules treating
|
|
100
|
+
every name in a catalog entry's `publicExports` as that entry's component,
|
|
101
|
+
so a helper such as `uiColor` or `flex` called with an object whose keys
|
|
102
|
+
matched a contract path (`uiColor({ choices: [...] })`) was checked as a
|
|
103
|
+
`Select` or `List` call. Only component exports, whose names start with an
|
|
104
|
+
uppercase letter as JSX component tags do, now resolve to an entry; the
|
|
105
|
+
`@kerfjs/ui` catalog check fails if a cataloged `SafeHtml` render function
|
|
106
|
+
is lowercase or any other runtime export is uppercase.
|
|
107
|
+
- The `@kerfjs/ui` component catalog's `ToolbarText` entry now lists every
|
|
108
|
+
token its `xlarge` size reads (`--kui-color-text`, `--kui-font-l`,
|
|
109
|
+
`--kui-font-2xl`) in `publicTokens`; previously it named only the tokens of
|
|
110
|
+
the smaller sizes.
|
|
111
|
+
- The `@kerfjs/ui` browser evaluator's target-size check (`KUI-B050`) now
|
|
112
|
+
measures the pointer-reachable area instead of only the border box: an
|
|
113
|
+
undersized control is hit-tested outward from its center, so a transparent
|
|
114
|
+
`::before`/`::after` hit layer (such as the `ListHeader` action's) counts,
|
|
115
|
+
while `pointer-events: none` decoration, clipping, and overlapping
|
|
116
|
+
neighbors do not. Diagnostic evidence now carries the measured size plus
|
|
117
|
+
the border `box`.
|
|
118
|
+
- Fixed the `eslint-plugin-kerfjs` `ui-css-values` messages (`KUI-L013`,
|
|
119
|
+
`KUI-L014`, `KUI-L017`) listing suggested helpers with unbalanced backticks
|
|
120
|
+
(``use `uiColor() or `foregroundColorVar().``); each helper is now wrapped
|
|
121
|
+
as `` `uiColor()` ``.
|
|
122
|
+
- Fixed `kerf-ui-analyze` skipping the CSS-value contracts of `@kerfjs/ui`
|
|
123
|
+
`Select` and `Skeleton`. Its component lookup was keyed by display name, so
|
|
124
|
+
the same-named Web Awesome catalog entries (`wa-select`, `wa-skeleton`) —
|
|
125
|
+
which carry no CSS-value props — shadowed them, and a wrong-grammar helper
|
|
126
|
+
such as `colorVar()` for `SelectChoice.color` went unreported although the
|
|
127
|
+
ESLint rule caught it. Entries now resolve by `package:id`, using the
|
|
128
|
+
selection catalog's public exports and import subpaths.
|
|
129
|
+
- Fixed `@kerfjs/ui` monospace text rendering in the generic face (Courier on
|
|
130
|
+
macOS Chromium) whenever Web Awesome was loaded: `--kui-font-mono` deferred to
|
|
131
|
+
Web Awesome's `ui-monospace, monospace`, which Chromium does not resolve, and
|
|
132
|
+
the trailing generic `monospace` kept Kerf's named fallbacks from ever
|
|
133
|
+
applying. `--kui-font-mono` is now Kerf's own stack (`ui-monospace,
|
|
134
|
+
SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace`),
|
|
135
|
+
and the Web Awesome theme points `--wa-font-family-code` at it so Kerf `Text
|
|
136
|
+
font="monospace"` and Web Awesome code text match.
|
|
137
|
+
- The `@kerfjs/ui` Web Awesome theme now gives default-size single-line
|
|
138
|
+
controls Kerf's 44px control height, so a plain `wa-input` or `wa-button`
|
|
139
|
+
sits flush beside a Kerf `Select` instead of about 9px shorter. It sets
|
|
140
|
+
`--wa-form-control-height` to `2.75em` (44px at the default size) on Button,
|
|
141
|
+
Color Picker, Input, Known Date, Number Input, Radio Group, Select, Textarea,
|
|
142
|
+
and Time Input; `size="large"` scales proportionally, `size="small"` keeps Web
|
|
143
|
+
Awesome's compact height, and buttons inside a `ToolbarControlGroup` keep the
|
|
144
|
+
group's 40px (32px compact) height. Tags, copy buttons, and toasts are
|
|
145
|
+
unchanged.
|
|
146
|
+
- Fixed an `@kerfjs/ui` `Select` menu drawing two separators when a choice with
|
|
147
|
+
`separatorBefore` opened a separated group: the group's border plus a
|
|
148
|
+
`wa-divider` left an empty band between the group title and its first
|
|
149
|
+
choice. `separatorBefore` now only divides a choice from the previous choice
|
|
150
|
+
in the same list; a group boundary is already the single separator.
|
|
151
|
+
- Fixed `@kerfjs/ui` compact `ToolbarControlGroup` end highlights not being
|
|
152
|
+
concentric with the group: controls sat 2px from the sides but 1px from the
|
|
153
|
+
top and bottom. A compact group now has no padding, so its 32px controls sit
|
|
154
|
+
on a 1px inset on every side with a radius of the group radius minus 1px
|
|
155
|
+
(pill and rounded). In compact mixed groups the raised selection still paints
|
|
156
|
+
over the border with the group's radius, neighboring controls still abut, and
|
|
157
|
+
the trailing dropdown trigger keeps the 1px end inset instead of running onto
|
|
158
|
+
the border. Compact groups are 2px narrower as a result.
|
|
159
|
+
- Added `Toolbar.safeAreaEdges` to `@kerfjs/ui` so an app bar or bottom bar
|
|
160
|
+
that sits at a screen edge, outside any `Pane` header or footer, can claim
|
|
161
|
+
those edges for device safe areas. A toolbar there used to take only the
|
|
162
|
+
inline edges an owner handed it, so the app-shell recipe's top bar never
|
|
163
|
+
cleared the status area. Each claimed side pads by the routed
|
|
164
|
+
`--kui-edge-inset-*` context, or the full device inset when nothing routes
|
|
165
|
+
it, while the bar's surface and divider still paint through; the minimum
|
|
166
|
+
height grows with the claimed block insets so a shrinking flex column keeps a
|
|
167
|
+
whole control row clear. A claim inside a `Pane` header or footer adds
|
|
168
|
+
nothing, since the pane already owns those edges. The app-shell recipe's app
|
|
169
|
+
bar now claims the top edge and both sides.
|
|
170
|
+
- Fixed `@kerfjs/ui` slide-motion `ResizableRegion` content overflowing its
|
|
171
|
+
separator when a parent clamps the region. The expanded content kept the
|
|
172
|
+
committed size even when a `max-width` or narrow container held the track
|
|
173
|
+
smaller, so in the catalog at 390px a region resized to 420px showed text
|
|
174
|
+
running past its handle. Expanded content now follows the region's actual
|
|
175
|
+
track on both axes; the fixed expanded size still applies while collapsing, so
|
|
176
|
+
the collapse keeps reading as a slide.
|
|
177
|
+
- Fixed `@kerfjs/ui` `ResizableRegion` panes stopping at their content's
|
|
178
|
+
height. The region's content now spans the whole region, like its separator,
|
|
179
|
+
and a lone child (normally a `Pane`) fills it, so the app-shell recipe's
|
|
180
|
+
navigation and inspector panes reach the bottom edge and scroll long content
|
|
181
|
+
inside their own content slot instead of ending partway down the region.
|
|
182
|
+
Several children still stack at their natural height.
|
|
183
|
+
- Fixed `@kerfjs/ui` `Toolbar responsive="center-priority"` leaving an
|
|
184
|
+
expanded center control at roughly half the row on narrow toolbars. The
|
|
185
|
+
policy set the toolbar's own grid template inside a container query on the
|
|
186
|
+
toolbar itself, which can never match, so the center kept only the first
|
|
187
|
+
column track once the leading and trailing zones left. The dead rule is gone
|
|
188
|
+
and the expanded center now spans every column track, filling the toolbar's
|
|
189
|
+
content width at phone widths.
|
|
190
|
+
- Fixed a nested `@kerfjs/ui` `List`, `Row`, or `Grid` inheriting its
|
|
191
|
+
ancestor's `gap` and `flex`. Those props travel through private custom
|
|
192
|
+
properties, which inherit, so a nested `List` without its own `gap` took the
|
|
193
|
+
enclosing `List`'s gap (a heading unit inside a form got a 24px gap between
|
|
194
|
+
its title and supporting line) and any nested instance without `flex` took the
|
|
195
|
+
ancestor's flex growth. Every instance now resets its private values, so an
|
|
196
|
+
omitted prop means that component's own default. The workspace-header recipe's
|
|
197
|
+
breadcrumb now sits directly above its title and the List demo's section rows
|
|
198
|
+
sit flush under their header, as intended.
|
|
199
|
+
- Fixed `@kerfjs/ui` slide-motion `ResizableRegion` content staying at its
|
|
200
|
+
previous width during a live resize until the app re-rendered the committed
|
|
201
|
+
size. `wireResizableRegions` now applies the live size to the sliding content
|
|
202
|
+
as well as the track, for pointer drags and key presses, so the content fills
|
|
203
|
+
the region (including a safe-area edge extent) throughout the drag and a later
|
|
204
|
+
collapse still slides it out at the resized width.
|
|
205
|
+
- Fixed the `@kerfjs/ui` `ListHeader` action's pointer target being only its
|
|
206
|
+
fitted 36px square although the guidance promises 44px. A transparent hit
|
|
207
|
+
layer now extends the target to at least 44 × 44 around the square without
|
|
208
|
+
moving layout or enlarging the painted hover surface. Compact headers are
|
|
209
|
+
36px tall like the rest of the compact List family, so their action target is
|
|
210
|
+
44px wide and clamped to the header height, never reaching into a neighboring
|
|
211
|
+
row.
|
|
212
|
+
- Fixed re-renders closing open Web Awesome popups: the morph (and the keyed-list
|
|
213
|
+
attribute fast path) no longer removes a live `open` attribute from custom
|
|
214
|
+
elements (`wa-select`, `wa-dropdown`, `wa-dialog`, …) when the template omits
|
|
215
|
+
it — the same user-agent-owned treatment `<details>` / `<dialog>` already
|
|
216
|
+
had. A template can still set `open`; closing stays imperative.
|
|
217
|
+
- The `@kerfjs/ui` composer-form recipe's supporting line now sits directly
|
|
218
|
+
under its toolbar title instead of 24px lower, because a nested `List` no
|
|
219
|
+
longer inherits the form's section gap (see the nested `List`/`Row`/`Grid`
|
|
220
|
+
fix above). The guidance now
|
|
221
|
+
also states that `ToolbarText` below `xlarge` is intentionally quiet identity
|
|
222
|
+
text with no tone option. A normal-color subsection title is `Text` with an
|
|
223
|
+
`h2`–`h6` variant, or `ListHeader` for a list or menu section.
|
|
224
|
+
- Fixed `@kerfjs/ui` `ToolbarText size="small"` labels rendering in a
|
|
225
|
+
monospace fallback face in Chromium. The small size used the code-font token,
|
|
226
|
+
which resolves through Web Awesome's `ui-monospace, monospace` stack; Chromium
|
|
227
|
+
does not recognize `ui-monospace`, so labels such as a pane's "Workspace" or
|
|
228
|
+
"Ready" fell back to the generic monospace font. Every `ToolbarText` size now
|
|
229
|
+
keeps the inherited UI sans stack.
|
|
230
|
+
- Fixed `@kerfjs/ui` `ListInsetControl` not stretching its control as
|
|
231
|
+
documented. Its `justify-content: stretch` is a no-op on a flex row, so a
|
|
232
|
+
`Select`, `wa-input`, or `wa-button` inside stayed at its natural width. Each
|
|
233
|
+
direct child now grows to fill the inset row (several children share it with
|
|
234
|
+
the 8px gap) and may shrink below its intrinsic width instead of overflowing.
|
|
235
|
+
- `@kerfjs/ui` `List`, `Row`, and `Grid` can now be the layout root of a
|
|
236
|
+
fixed-height parent. New `fill` takes the parent's full height (use `flex`
|
|
237
|
+
inside a flex layout as before), and new `rootAttributes` carries safe
|
|
238
|
+
`data-*` metadata while the component's structural attributes stay
|
|
239
|
+
protected. The app-shell and collapsible-sidebar recipes now use a filling
|
|
240
|
+
List or Row root instead of a frame `Pane` whose scroll owner never scrolled;
|
|
241
|
+
the collapsible sidebar's rail and drawer now reach the real frame edges, so
|
|
242
|
+
they paint through safe areas as the layout model intends. The UX catalog
|
|
243
|
+
adds an application-sized `height: "app"` (592px) specimen frame, and both
|
|
244
|
+
recipes use it instead of the cramped 360px `tall` frame.
|
|
245
|
+
- Fixed a `@kerfjs/ui` stacked `Toolbar` clipping its last trailing action at
|
|
246
|
+
phone widths (the compact-toolbar recipe's "More" button at 390px). A
|
|
247
|
+
toolbar's trailing zone now wraps whole control groups onto another
|
|
248
|
+
trailing-aligned row under every `responsive` policy, and a
|
|
249
|
+
`responsive="stack"` toolbar wraps its stacked center and trailing groups the
|
|
250
|
+
same way, so no action is ever clipped or shrunk. Stacked zones also span the
|
|
251
|
+
full toolbar width; they previously lost 16px to empty column tracks. New:
|
|
252
|
+
`responsive="wrap"` keeps a heading on one row while everything fits and
|
|
253
|
+
otherwise moves the trailing zone below a whole title instead of truncating
|
|
254
|
+
it. The loading-inspector recipe uses it, so its narrow heading shows the
|
|
255
|
+
whole record title instead of an ellipsis.
|
|
256
|
+
- `@kerfjs/ui` layouts and `Pane` now handle device safe areas as one model.
|
|
257
|
+
Pane and panel backgrounds, separators, and dividers paint through unsafe
|
|
258
|
+
areas; content is padded only on the edges a region actually reaches, and
|
|
259
|
+
block insets become scroll padding so content can scroll under the status bar
|
|
260
|
+
or home indicator but always scroll clear. `Workbench` rails and drawer,
|
|
261
|
+
`CollapsiblePanel`, and the `SplitView` list grow by the inset they dock to.
|
|
262
|
+
A center pane picks up the inset when an adjacent rail collapses, and
|
|
263
|
+
interior or nested edges are never double-inset. A header or footer `Toolbar`
|
|
264
|
+
keeps its dividers edge to edge. New: `Pane.safeAreaEdges` (default all
|
|
265
|
+
sides; `[]` opts out), the `--kui-safe-area-*` device-inset tokens (default
|
|
266
|
+
`env(safe-area-inset-*)`), the inherited `--kui-edge-inset-*` edge context for
|
|
267
|
+
app-owned layouts, and `--kui-resizable-region-edge-extent`. Nothing changes
|
|
268
|
+
on devices or pages without safe-area insets.
|
|
269
|
+
- Fixed a `@kerfjs/ui` `ListHeader` action sitting about 4px outside the
|
|
270
|
+
trailing toolbar action of the Pane header above it. Its fitted action now
|
|
271
|
+
centers in the same 44px slot a toolbar control occupies at the 8px inline
|
|
272
|
+
margin, so trailing icon actions share one axis. The action's size, 18px
|
|
273
|
+
visual, and hover square are unchanged.
|
|
274
|
+
- **Breaking (`@kerfjs/ui`, 5.0 beta):** `SelectChoice.color` now accepts only
|
|
275
|
+
a foreground color, the new `CssForegroundColor` brand from
|
|
276
|
+
`@kerfjs/ui/css-values`. `uiColor()` returns it for foreground token names
|
|
277
|
+
(the new `UiForegroundColorName`: the `*-on-*` roles, `text`, `text-quiet`,
|
|
278
|
+
`text-link`, and the `*-text` aliases), and the new
|
|
279
|
+
`foregroundColorVar('--app-icon-color', fallback?)` returns it for an
|
|
280
|
+
application-owned property. The `--kui-color-success` / `warning` / `danger`
|
|
281
|
+
/ `pop` / `accent` compatibility aliases are quiet fills that read like
|
|
282
|
+
foreground colors; colored with one, an icon rendered at about 1.2:1
|
|
283
|
+
contrast. `uiColor('success')` and the other fill, border, and surface tokens
|
|
284
|
+
still return a plain `CssColor` and no longer type-check as an icon color,
|
|
285
|
+
and neither does a plain `colorVar()` value. Migrate
|
|
286
|
+
`color: uiColor('success')` to `uiColor('success-on-quiet')` and
|
|
287
|
+
`color: colorVar('--app-color')` to `foregroundColorVar('--app-color')`;
|
|
288
|
+
`uiColor('…-on-…')` calls keep compiling unchanged. The catalog's CSS-value
|
|
289
|
+
contract for `choices[].color` lists `uiColor` and `foregroundColorVar`, so
|
|
290
|
+
`eslint-plugin-kerfjs` also reports `colorVar()` there (`KUI-L014`), and
|
|
291
|
+
the source and packed consumer type contracts pin the rejected fills.
|
|
292
|
+
|
|
293
|
+
- Fixed an `@kerfjs/ui` icon-only `Select` beside other controls in one
|
|
294
|
+
`ToolbarControlGroup`. A compact Select no longer eats 1px of the group's
|
|
295
|
+
4px gap on each side (the lone-trigger padding overlap now applies only when
|
|
296
|
+
it is the group's only child), a compact rounded Select uses its siblings'
|
|
297
|
+
item radius instead of the lone-slot radius, and the Select now shows the
|
|
298
|
+
same hover pill as its sibling buttons (a lone Select lifts the whole group,
|
|
299
|
+
like a popup-menu dropdown). In a group with per-control focus rings
|
|
300
|
+
(`focusRing="control"`, the default) the Select paints the same ring as its
|
|
301
|
+
siblings even when `focusRingOwner="group"` was set, which previously left it
|
|
302
|
+
with no visible focus. A compact icon-only `<button>` is now a 32px circle,
|
|
303
|
+
so a selected compact segment is no longer a 34x32 oval. The
|
|
304
|
+
ToolbarControlGroup catalog adds a "Select beside actions" example.
|
|
305
|
+
|
|
306
|
+
- Changed the `@kerfjs/ui` icon-only `Select`
|
|
307
|
+
(`selectedPresentation="icon-only"`) to keep its disclosure caret. The
|
|
308
|
+
selected icon and caret now form one pill with the same geometry as a
|
|
309
|
+
`ToolbarControlGroup` popup-menu `wa-dropdown` trigger: 8px inline padding on
|
|
310
|
+
both sides, 12px from the icon to the caret, the same caret glyph size, and a
|
|
311
|
+
40px default or 32px compact control height, so the icon sits left of center
|
|
312
|
+
to balance the caret. In a single `ToolbarControlGroup` the group now sizes to
|
|
313
|
+
the trigger (a 70x44 default or 68x34 compact pill, wider than it is tall)
|
|
314
|
+
with an even inset on every side and a concentric radius, for pill and
|
|
315
|
+
rounded shapes, so the group-owned outline or halo ring follows the pill while
|
|
316
|
+
focused and while the listbox is open. Before, a default-size trigger was a
|
|
317
|
+
32px square left-aligned in its 44px group, and the caret was hidden. The
|
|
318
|
+
Select catalog adds a default-size "Toolbar icon" example beside the compact
|
|
319
|
+
one.
|
|
320
|
+
|
|
321
|
+
- Fixed the `@kerfjs/ui` icon-only `Select` focus ring in a
|
|
322
|
+
`ToolbarControlGroup` not matching the control: the compact trigger grew its
|
|
323
|
+
group into a 36x34 oval and its disclosure caret overflowed the trailing edge.
|
|
324
|
+
The group-owned ring now stays concentric with the trigger while focused and
|
|
325
|
+
while the listbox is open. The Select demo's "Quiet" option icon also used the success fill alias
|
|
326
|
+
as its color and was nearly invisible; it now uses `success-on-quiet`, and the
|
|
327
|
+
`SelectChoice.color` guidance names foreground tokens.
|
|
328
|
+
|
|
329
|
+
- `@kerfjs/ui` UX catalog: the ten production recipes now compose Kerf
|
|
330
|
+
components, layouts, and Web Awesome surfaces through their public
|
|
331
|
+
configuration alone, with no route stylesheets, inline styles, or
|
|
332
|
+
styling-only classes. The list-detail dialog is rebuilt from `DialogSurface`,
|
|
333
|
+
the dialog's own labeled header and close control, `SplitView` (a full-screen
|
|
334
|
+
list-to-detail drill-down on compact devices), aligned `ListHeader`s, a
|
|
335
|
+
`ValueTable`, and footer actions. The desktop application shell switches to one
|
|
336
|
+
pane at a time below desktop sizes through `deviceClass()`. Page and pane
|
|
337
|
+
headings stack instead of hiding their titles at narrow widths, and supporting
|
|
338
|
+
copy, content-item text, and loading skeletons sit on the shared 17px text
|
|
339
|
+
inset. The catalog frames each recipe in a `CatalogExample` viewport that the
|
|
340
|
+
recipe declares through an exported `presentation`, and recipe ownership notes
|
|
341
|
+
render as that example's note. `npm run check:recipes` now fails on a recipe
|
|
342
|
+
stylesheet, an inline style, a class outside the published layout and Web
|
|
343
|
+
Awesome vocabulary, a non-public stylesheet import, or recipe-specific rules in
|
|
344
|
+
the catalog shell stylesheet.
|
|
345
|
+
- Fixed `@kerfjs/ui/webawesome.css` `wa-dialog` titles given as a plain-string
|
|
346
|
+
`label` sitting at the bare surface inset; they now inset by one bordered
|
|
347
|
+
item step (`--kui-layout-item-padding` + 1px) so the header label lines up
|
|
348
|
+
with body text and icons. Slotted label elements are unaffected.
|
|
349
|
+
- Fixed icon + label `wa-button`s inside `ToolbarControlGroup` stacking the
|
|
350
|
+
icon above the label; their label part now lays out as a single nowrap row,
|
|
351
|
+
matching native mixed buttons.
|
|
352
|
+
- `prompt()` / `form()` now reject with a `TypeError` when a validator returns
|
|
353
|
+
a promise (validators are synchronous), instead of resolving the value and
|
|
354
|
+
leaving an unhandled rejection. `prompt()` also re-reads its input on every
|
|
355
|
+
OK, so a reactive `render` that replaces the input no longer submits the
|
|
356
|
+
detached original's stale value, and a removed input rejects the promise.
|
|
357
|
+
- A local `npm run check` run with `KERF_SKIP_PACKAGE_GATES=1` no longer
|
|
358
|
+
records a verified tree, so the pre-push hook cannot skip the sibling-package
|
|
359
|
+
gates that run left out.
|
|
360
|
+
- `kerfjs/overlay`: a `prompt()` `validate` or `form()` `field.validate` that
|
|
361
|
+
throws now closes the dialog (full teardown, focus restored) and rejects the
|
|
362
|
+
returned promise with the original error. Previously the error escaped the
|
|
363
|
+
OK click or Enter handler, and the dialog stayed open with an OK button that
|
|
364
|
+
could never succeed. Validators are synchronous; this is the error channel
|
|
365
|
+
for a validator bug.
|
|
366
|
+
- `kerfjs/overlay`: when a `form()` field input disappears after the dialog
|
|
367
|
+
opens (for example a reactive `render` re-render drops it), OK and Enter now
|
|
368
|
+
close the dialog and reject the promise with an error naming the missing
|
|
369
|
+
field. Previously `form()` resolved `null`, which
|
|
370
|
+
reads as a Cancel, and threw from the click handler.
|
|
371
|
+
- `@kerfjs/ui`: a multiline `ListItem` whose label fits on one line now
|
|
372
|
+
centers its icon on that line. The row's 44px minimum used to stretch the
|
|
373
|
+
grid track, which centered the label but left the start-aligned icon about
|
|
374
|
+
1.4px above it.
|
|
375
|
+
- `@kerfjs/ui`: a `ToolbarControlGroup` with `focusRing="outline"` or
|
|
376
|
+
`"halo"` keeps its group focus ring while a `Select` inside it has its
|
|
377
|
+
listbox open. Chromium and WebKit did not carry `:focus-within` from the
|
|
378
|
+
focused option to the group, so the ring disappeared.
|
|
379
|
+
- UX catalog: the foundation palette's Brand swatch uses the brand on-fill
|
|
380
|
+
color again, the Spacer demo gives its flexible spacer room to grow again,
|
|
381
|
+
and the TokenSearchField adoption demo again wires its chip keyboard and
|
|
382
|
+
`onEdit` readout. The catalog demo rework had broken all three.
|
|
383
|
+
- Documented and pinned how `tooltip()` surfaces a failed show from its delay
|
|
384
|
+
timer: the half-built tooltip is rolled back, the original error escapes the
|
|
385
|
+
timer for the host to report (a window `error` event in browsers, matching
|
|
386
|
+
kerf's other deferred callbacks), and the tooltip stays armed so the next
|
|
387
|
+
hover or focus tries again.
|
|
388
|
+
- Fixed the dialog helpers (`confirm` / `prompt` / `form` / `choice`) leaving
|
|
389
|
+
wiring behind when a step after the overlay opened failed. Their post-open
|
|
390
|
+
wiring (required-slot checks, the click-table `delegate()`, the Enter-key
|
|
391
|
+
listener) is now part of construction: a failure removes the listeners
|
|
392
|
+
already wired, closes the overlay (including a `native` `<dialog>`), restores
|
|
393
|
+
focus, and rethrows the original error, so no pending promise is left
|
|
394
|
+
attached to an on-screen dialog.
|
|
395
|
+
- Added `ticket:timing summary --all`, a read-only cross-ticket timing report
|
|
396
|
+
(median/p90 per phase, gate, and check step) that counts one push shared by
|
|
397
|
+
a batch of tickets once and detects and excludes fan-out backfill, such as
|
|
398
|
+
the first timed push that attached one check interval to 374 historical
|
|
399
|
+
tickets.
|
|
400
|
+
- Ticket timing now records per-step durations of the root `npm run check`
|
|
401
|
+
chain (printed at the end of every run), interrupted gate attempts, active
|
|
402
|
+
time through `ticket:timing claim`/`release` wrappers around `hotsheet-cli`,
|
|
403
|
+
and CI/publication intervals imported idempotently from GitHub Actions with
|
|
404
|
+
`ticket:timing import-ci`.
|
|
405
|
+
- The pre-push hook no longer reruns `npm run check` when a local run already
|
|
406
|
+
passed on the exact clean tree being pushed; the skip is recorded in ticket
|
|
407
|
+
timing, and any dirty worktree, different tree, Node.js change,
|
|
408
|
+
failed/interrupted rerun, or `KERF_FORCE_CHECK=1` runs the full gate.
|
|
409
|
+
- The pre-push hook no longer fans its timing record out to every ticket in a
|
|
410
|
+
push that names more than 25 of them (a first push of a long history, a
|
|
411
|
+
force-push). It prints a notice and records only the explicit
|
|
412
|
+
`KERF_TICKET_TIMING_TICKETS` list, or nothing, so it can no longer write the
|
|
413
|
+
backfill shape the cross-ticket summary has to exclude.
|
|
414
|
+
- The pre-push hook's "already verified" skip now also requires the same
|
|
415
|
+
platform, architecture, and installed dependencies as the passing run: a
|
|
416
|
+
fingerprint of every `package-lock.json` and `node_modules/.package-lock.json`
|
|
417
|
+
across the root, `ui/`, `eslint-plugin/`, `create-kerf-component/`, and
|
|
418
|
+
`site/`. A reinstall, a lockfile change, or a missing `node_modules` now runs
|
|
419
|
+
the full gate.
|
|
420
|
+
- `ticket:timing record` accepts `--run-id <n>`, and `import-ci` no longer
|
|
421
|
+
imports a GitHub run a second time when it was already recorded by hand:
|
|
422
|
+
an interval with that run id, or one with no run id for the same phase and
|
|
423
|
+
gate whose span overlaps the run's (within 60 seconds), counts as imported.
|
|
424
|
+
- `bindList()` now rolls back when its first render throws: rows that pass
|
|
425
|
+
already created are disposed (content mounts and element-mode `dispose`
|
|
426
|
+
callbacks) and removed before the original error is rethrown, instead of
|
|
427
|
+
leaking live row effects with no handle to release them.
|
|
428
|
+
- `bindList()` virtualization now validates its dimensions: heights (fixed,
|
|
429
|
+
estimated, callback-returned, and `setHeight` reports) must be finite and
|
|
430
|
+
non-negative, a fixed window-mode `rowHeight` must be positive, and
|
|
431
|
+
`overscan` / `minRows` must be non-negative integers. Invalid values throw a
|
|
432
|
+
descriptive error instead of producing blank windows or a corrupted scroll
|
|
433
|
+
anchor.
|
|
434
|
+
- `bindList()` now rejects a source that carries the `arraySignal` brand but
|
|
435
|
+
has no patch queue with a descriptive error at bind time, instead of failing
|
|
436
|
+
with a generic `TypeError` on its first render.
|
|
437
|
+
- Added `wa-dialog.hide-actions` support to `@kerfjs/ui/webawesome.css`, hiding
|
|
438
|
+
Web Awesome's directly exported `header-actions` shadow part for dialogs that
|
|
439
|
+
provide their own dismissal affordance.
|
|
440
|
+
- Fixed `mount()` leaving an element permanently "already mounted" when its
|
|
441
|
+
first render throws. The first render is now transactional: the original
|
|
442
|
+
error is rethrown, every binding effect and the dev listener observer
|
|
443
|
+
acquired so far are released, the element's pre-mount child nodes are
|
|
444
|
+
restored, and a retry on the same element succeeds.
|
|
445
|
+
- Fixed `overlay()` construction leaking a live wrapper, mount, dismissal
|
|
446
|
+
listeners, and concurrent-overlay stack entry when setup failed partway. A
|
|
447
|
+
malformed `initialFocus` selector now throws a descriptive `overlay()` error
|
|
448
|
+
before anything is touched, and a throwing first render or `native`
|
|
449
|
+
`showModal()` / `showPopover()` call rolls back everything already installed
|
|
450
|
+
(restoring focus) before rethrowing. `popover()` and `tooltip()` roll back the
|
|
451
|
+
same way when positioning fails; the dialog helpers inherit the guarantee.
|
|
452
|
+
- Added an opt-in `@kerfjs/ui/document.css` baseline for border-box sizing,
|
|
453
|
+
token-backed body and link presentation, and a `.kui-app-root` full-height
|
|
454
|
+
chain for application shells.
|
|
455
|
+
- Added an inline `Text` span variant for secondary copy inside rows, labels,
|
|
456
|
+
and table cells, preserving tone, size, and font roles without box geometry.
|
|
457
|
+
- Reworked every focused UX catalog demo to dogfood Kerf components, Web
|
|
458
|
+
Awesome surfaces, and semantic HTML without route-local stylesheets, inline
|
|
459
|
+
styles, styling-only classes, or shared icon/button render helpers. Added
|
|
460
|
+
catalog-owned specimen viewport constraints and a nonwaivable conformance
|
|
461
|
+
gate that prevents those styling shortcuts from returning.
|
|
462
|
+
- Fixed `ToolbarControlGroup` dropdown triggers that were sized to fixed 48px/66px boxes,
|
|
463
|
+
which clipped popup icons and let compact mixed-control carets overflow in
|
|
464
|
+
Safari while distorting longer catalog controls.
|
|
13
465
|
- Added a `TabBar.end` action zone for layouts that need a tab-local adjacent
|
|
14
466
|
`trailing` action and a separate far-edge action; the tab strip now owns the
|
|
15
467
|
shrink/scroll geometry while all action regions remain visible.
|
package/README.md
CHANGED
|
@@ -31,14 +31,17 @@ const app = document.getElementById('app')!;
|
|
|
31
31
|
mount(app, () => (
|
|
32
32
|
<div>
|
|
33
33
|
<button data-action="inc">+</button>
|
|
34
|
-
<span>{count
|
|
34
|
+
<span>{count}</span>
|
|
35
35
|
</div>
|
|
36
36
|
));
|
|
37
37
|
|
|
38
38
|
delegate(app, 'click', '[data-action="inc"]', () => count.value++);
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
That's it. Your JSX renders to HTML strings,
|
|
41
|
+
That's it. Your JSX renders to HTML strings, and handing the signal itself to
|
|
42
|
+
the text hole binds that one node directly. Structural reads still re-run the
|
|
43
|
+
render, where kerf's native diff applies the minimum DOM mutations needed to
|
|
44
|
+
make the live tree match.
|
|
42
45
|
|
|
43
46
|
Here's the whole development loop — write a component, run the dev server, click around, edit, watch the browser pick it up:
|
|
44
47
|
|
|
@@ -81,7 +84,9 @@ Write plain `.tsx` and build with your existing esbuild / Vite / tsup — no ext
|
|
|
81
84
|
|
|
82
85
|
4. **Focus, selection, and listeners survive re-renders — even mid-list.** The reconciler morphs instead of rebuilding, so caret position, IME composition, scroll, and delegated listeners survive every update; keyed rows are patched in place rather than recreated.
|
|
83
86
|
|
|
84
|
-
5. **
|
|
87
|
+
5. **A first-party UI layer when you want one.** `@kerfjs/ui` adds accessible components, responsive application layouts, typed CSS values, and explicit wiring helpers without changing kerf's stateless component model. It is separately installable and tree-shakeable, so the core stays the core. ([tour →](#first-party-ui-separately-installable))
|
|
88
|
+
|
|
89
|
+
6. **Safe by default.** Text and attributes are HTML-escaped automatically, URL attributes are scheme-screened (`javascript:` dropped), and inline `on*` handlers are rejected outright — so untrusted data stays inert. `raw()` is the explicit, auditable opt-out.
|
|
85
90
|
|
|
86
91
|
**Plus, nothing you don't ask for:** JSX typed against the HTML standard (not React's props) · a ~18-export API with no hooks, lifecycle, or per-instance state · **nine** tree-shakeable companion subpaths (`router`, `list`, `overlay`, `async`, …) that stay out of the core until imported · an [ESLint plugin](https://brianwestphal.github.io/kerf/docs/eslint-plugin/) + opt-in dev warnings + `create-kerf-component` scaffold · plain TS/JSX/ESM that drops into esbuild / Vite / tsup — or **no** build at all via the `html` tagged template.
|
|
87
92
|
|
|
@@ -97,7 +102,7 @@ Write plain `.tsx` and build with your existing esbuild / Vite / tsup — no ext
|
|
|
97
102
|
### When to reach for something else
|
|
98
103
|
|
|
99
104
|
- Need a full ecosystem (router + forms + data + SSR streaming) → **Next.js / Remix / SolidStart**.
|
|
100
|
-
- Building a deeply
|
|
105
|
+
- Building a deeply componentized design-system app → **React / Solid / Svelte**.
|
|
101
106
|
- Need React Native / cross-platform mobile → **React** (Kerf + Tauri/Electron also covers many of these cases).
|
|
102
107
|
- Building a static site → **Astro** (we use it for _this_ project's site).
|
|
103
108
|
- Already invested in a framework where switching cost outweighs the bundle size gain.
|
|
@@ -278,6 +283,38 @@ mount(app, () => <div><nav>{/* <a href> links, auto-intercepted */}</nav>{router
|
|
|
278
283
|
|
|
279
284
|
Each subpath adds nothing to the main barrel until it's imported. See [`docs/8-api-reference.md`](./docs/8-api-reference.md) for the full list (`list`, `router`, `overlay`, `scope`, `async`, `timing`, `remount`, `attach`, `actions`).
|
|
280
285
|
|
|
286
|
+
### First-party UI, separately installable
|
|
287
|
+
|
|
288
|
+
`@kerfjs/ui` is the optional component layer for applications that want a
|
|
289
|
+
coherent interface vocabulary without switching frameworks. Components return
|
|
290
|
+
Kerf `SafeHtml`; the application still owns state and wires behavior explicitly.
|
|
291
|
+
Component subpaths pull in only their reachable CSS, while responsive app
|
|
292
|
+
layouts, typed CSS-value builders, Web Awesome adapters, and the checked
|
|
293
|
+
component catalog stay opt-in.
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npm install kerfjs @kerfjs/ui
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
import { mount } from "kerfjs";
|
|
301
|
+
import { ListItem } from "@kerfjs/ui/list-item";
|
|
302
|
+
import { Toolbar } from "@kerfjs/ui/toolbar";
|
|
303
|
+
import { ToolbarText } from "@kerfjs/ui/toolbar-text";
|
|
304
|
+
|
|
305
|
+
const root = document.getElementById("app")!;
|
|
306
|
+
|
|
307
|
+
mount(root, () => (
|
|
308
|
+
<>
|
|
309
|
+
<Toolbar label="Files" leading={<ToolbarText text="Workspace" />} />
|
|
310
|
+
<ListItem action="open-file" label="src/main.ts" selected />
|
|
311
|
+
</>
|
|
312
|
+
));
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Start with the [component selection matrix](./ui/docs/component-selection.md),
|
|
316
|
+
or read the complete [`@kerfjs/ui` package guide](./ui/README.md).
|
|
317
|
+
|
|
281
318
|
## Optional tooling
|
|
282
319
|
|
|
283
320
|
Install and JSX setup are in [Quick start](#quick-start) above. These companion packages are opt-in.
|
|
@@ -314,6 +351,7 @@ See [`docs/13-component-packages.md`](./docs/13-component-packages.md) for the f
|
|
|
314
351
|
- **Docs:** [`docs/`](./docs/) — overview · reactivity · stores · render · events · jsx · svg · [API reference](./docs/8-api-reference.md)
|
|
315
352
|
- **Migrating:** [coming from another framework?](https://brianwestphal.github.io/kerf/migrating/) — side-by-side TodoMVC translations + per-framework gotchas
|
|
316
353
|
- **AI guide:** [`docs/ai/usage-guide.md`](./docs/ai/usage-guide.md) — reference for AI tools fetching kerf docs (linked from `llms.txt`)
|
|
354
|
+
- **UI package:** [`@kerfjs/ui`](./ui/README.md) — accessible components, responsive app layouts, typed CSS values, Web Awesome adapters, and checked AI-facing component metadata
|
|
317
355
|
- **ESLint plugin:** [brianwestphal.github.io/kerf/docs/eslint-plugin/](https://brianwestphal.github.io/kerf/docs/eslint-plugin/) — `eslint-plugin-kerfjs`; eight rules (four hard-rule errors + four warns: `require-delegate-disposer`, `prefer-attr-selector`, `no-raw-with-dynamic-arg`, `ai-assistant-configs`) at edit time (source: [`eslint-plugin/`](./eslint-plugin/))
|
|
318
356
|
- **Component scaffold:** `npm create kerf-component@latest <dir>` — `create-kerf-component`; generates a publishable component package with packaging rules plus deterministic, drift-checked AI metadata pre-wired (source: [`create-kerf-component/`](./create-kerf-component/))
|
|
319
357
|
- **Demo:** [live demo](https://brianwestphal.github.io/kerf/demo/) — nine sections exercising every primitive (counter, store-backed cart, focus survival, keyed list, morph-skip, SVG render, Tier-2 capture, `arraySignal` patches, fine-grained signal bindings)
|
package/ai/manifest.json
CHANGED