@kerfjs/ui 5.0.0-beta.67 → 5.0.0-beta.69
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/README.md +3 -0
- package/ai/application-ui-profile-sync.cjs +20 -1
- package/ai/application-ui-profile.d.ts +4 -0
- package/ai/application-ui-profile.mjs +27 -0
- package/ai/application-ui-profile.schema.json +10 -0
- package/ai/component-catalog-extension.schema.json +12 -0
- package/ai/component-catalog.json +14 -4
- package/ai/component-catalog.schema.json +14 -0
- package/ai/component-composition-rules.json +2 -2
- package/ai/component-composition.d.ts +2 -0
- package/ai/component-composition.json +2 -2
- package/ai/component-composition.schema.json +24 -0
- package/ai/components/loading-spinner.md +1 -0
- package/ai/components/tab-bar.md +5 -4
- package/ai/components/toolbar-control-group.md +2 -2
- package/ai/public-api-signatures-v1.md +34 -14
- package/ai/skill.md +18 -3
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/analyzer/index.d.mts +1 -0
- package/analyzer/index.mjs +108 -12
- package/dist/catalog.js +4 -4
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-P4UXSYIJ.js → chunk-2ZOLX3IE.js} +2 -2
- package/dist/{chunk-P4UXSYIJ.js.map → chunk-2ZOLX3IE.js.map} +1 -1
- package/dist/{chunk-E36EVCKG.js → chunk-47CKKLBE.js} +2 -2
- package/dist/{chunk-E36EVCKG.js.map → chunk-47CKKLBE.js.map} +1 -1
- package/dist/{chunk-JCV4GXWB.js → chunk-6DFDTWGG.js} +100 -61
- package/dist/chunk-6DFDTWGG.js.map +1 -0
- package/dist/{chunk-IAOAZUOO.js → chunk-7OKX3EF6.js} +2 -2
- package/dist/{chunk-IAOAZUOO.js.map → chunk-7OKX3EF6.js.map} +1 -1
- package/dist/{chunk-C74LITXE.js → chunk-B3XWUX3A.js} +2 -2
- package/dist/{chunk-C74LITXE.js.map → chunk-B3XWUX3A.js.map} +1 -1
- package/dist/{chunk-F7OCOBTG.js → chunk-CRVDF4A6.js} +4 -1
- package/dist/chunk-CRVDF4A6.js.map +1 -0
- package/dist/{chunk-TGM424PI.js → chunk-H7IODXGY.js} +3 -3
- package/dist/{chunk-TGM424PI.js.map → chunk-H7IODXGY.js.map} +1 -1
- package/dist/chunk-KVBRMEPM.js +44 -0
- package/dist/chunk-KVBRMEPM.js.map +1 -0
- package/dist/{chunk-EN4LDR5Y.js → chunk-NC4CFOID.js} +2 -2
- package/dist/{chunk-EN4LDR5Y.js.map → chunk-NC4CFOID.js.map} +1 -1
- package/dist/{chunk-5SYE4CCT.js → chunk-PQZPVBP4.js} +25 -18
- package/dist/chunk-PQZPVBP4.js.map +1 -0
- package/dist/{chunk-NXWE5S57.js → chunk-QJ5VFCW3.js} +12 -3
- package/dist/chunk-QJ5VFCW3.js.map +1 -0
- package/dist/{chunk-RKTTF3L4.js → chunk-X3TPECWG.js} +1 -1
- package/dist/chunk-X3TPECWG.js.map +1 -0
- package/dist/{chunk-VY6YRCTV.js → chunk-X3X4KRDK.js} +2 -2
- package/dist/{chunk-VY6YRCTV.js.map → chunk-X3X4KRDK.js.map} +1 -1
- package/dist/collapsible-panel.js +2 -2
- package/dist/empty-state.js +2 -1
- package/dist/index.js +10 -10
- package/dist/list-action-row.js +1 -1
- package/dist/list-item.js +1 -1
- package/dist/loading-spinner.d.ts +7 -2
- package/dist/loading-spinner.js +2 -1
- package/dist/nav-stack.js +1 -1
- package/dist/select.d.ts +4 -2
- package/dist/select.js +1 -1
- package/dist/split-view.js +1 -1
- package/dist/styles/app-tab.css +29 -0
- package/dist/styles/loading-spinner.css +4 -0
- package/dist/styles/select.css +79 -0
- package/dist/styles/tab-bar.css +12 -8
- package/dist/styles/token-search-field.css +28 -10
- package/dist/styles/toolbar-control-group.css +96 -3
- package/dist/styles/webawesome.css +17 -0
- package/dist/tab-bar.d.ts +2 -0
- package/dist/tab-bar.js +1 -1
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-control-group.d.ts +13 -5
- package/dist/toolbar-control-group.js +2 -1
- package/dist/wire-catalog.js +1 -1
- package/dist/wire-scroll-dividers.d.ts +7 -4
- package/dist/wire-scroll-dividers.js +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.js +1 -1
- package/docs/accessibility.md +14 -0
- package/docs/apple-hig.md +1 -0
- package/docs/catalog.md +3 -0
- package/docs/component-contract.md +65 -10
- package/docs/component-selection.md +24 -6
- package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
- package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
- package/docs/design/templates/toolbar-dark.svg +1 -1
- package/docs/design/templates/toolbar.svg +1 -1
- package/docs/examples/component-catalog-extension.json +1 -0
- package/docs/layout.md +6 -0
- package/docs/ui-analyzer.md +12 -0
- package/docs/ui-doctor.md +15 -3
- package/docs/webawesome-theme.md +7 -0
- package/doctor/cli.mjs +5 -0
- package/doctor/config.schema.json +2 -0
- package/doctor/index.d.mts +3 -0
- package/doctor/index.mjs +27 -1
- package/llms.txt +15 -5
- package/package.json +1 -1
- package/dist/chunk-5SYE4CCT.js.map +0 -1
- package/dist/chunk-F7OCOBTG.js.map +0 -1
- package/dist/chunk-JCV4GXWB.js.map +0 -1
- package/dist/chunk-NXWE5S57.js.map +0 -1
- package/dist/chunk-RKTTF3L4.js.map +0 -1
- package/dist/chunk-ZLWQZUGF.js +0 -27
- package/dist/chunk-ZLWQZUGF.js.map +0 -1
|
@@ -92,6 +92,16 @@ the `data-*` attributes it actually writes. First-party entries always carry
|
|
|
92
92
|
the list, empty when their helpers write none; it is optional in extension
|
|
93
93
|
catalogs.
|
|
94
94
|
|
|
95
|
+
An extension catalog may add `wiring.sources` entries with `export` and
|
|
96
|
+
`source` for each application-owned helper. The source path is relative to its
|
|
97
|
+
package root; `ui-wiring` resolves helper imports through this file, including
|
|
98
|
+
relative re-export barrels. Every named export must also appear in
|
|
99
|
+
`wiring.helpers`.
|
|
100
|
+
|
|
101
|
+
The selection catalog's `wiringProviders` lists public helpers that install
|
|
102
|
+
other wiring helpers. The `ui-wiring` rule accepts a provider call for each
|
|
103
|
+
helper it declares; `wireCatalog` provides `wireScrollDividers`.
|
|
104
|
+
|
|
95
105
|
The generator starts with permissive defaults. An `any` mode records that the
|
|
96
106
|
catalog has no defensible prohibition; it does not claim every composition is
|
|
97
107
|
recommended. Objective rules live in
|
|
@@ -154,7 +164,7 @@ check in `.kerf-ui-profile.json` conforming to
|
|
|
154
164
|
[`application-ui-profile.schema.json`](../ai/application-ui-profile.schema.json).
|
|
155
165
|
Keep package-qualified catalog locations and recurring-concept preferences,
|
|
156
166
|
allowed color schemes and density, public semantic-token overrides,
|
|
157
|
-
layout/responsive conventions, and narrow rule exceptions there. Product
|
|
167
|
+
layout/responsive conventions, application wiring entry paths, and narrow rule exceptions there. Product
|
|
158
168
|
records, copy, permissions, user preferences, and transport state do not belong
|
|
159
169
|
in this policy file.
|
|
160
170
|
|
|
@@ -175,6 +185,12 @@ Discovery and precedence are deterministic:
|
|
|
175
185
|
Later scalar and object-map values win. Catalogs merge by package; preferences
|
|
176
186
|
and token overrides merge by key; exceptions merge by stable id. Lists such as
|
|
177
187
|
allowed themes/densities replace the earlier list rather than accumulating.
|
|
188
|
+
`wiring.entries` similarly names workspace-relative application entry modules;
|
|
189
|
+
later profile layers replace the entry list. The `ui-wiring` ESLint rule checks
|
|
190
|
+
application-scoped helper calls and registration imports once at each entry
|
|
191
|
+
that reaches a rendering module, while a composition entry with
|
|
192
|
+
`wiring.scope: "module"` keeps a local obligation. See the
|
|
193
|
+
[`ui-wiring` rule](../../eslint-plugin/docs/rules/ui-wiring.md).
|
|
178
194
|
Every resolved field retains its source file. The shipped
|
|
179
195
|
`application-ui-profile.mjs` API implements discovery, merge, loading, and
|
|
180
196
|
validation and reports actionable originating file + JSON-path diagnostics for stale
|
|
@@ -246,6 +262,8 @@ claim Kerf delivery paths for them.
|
|
|
246
262
|
Extension entries may add `uses` for direct composition dependencies and
|
|
247
263
|
repository-relative `source` and `demoSource` files. Resolve each dependency
|
|
248
264
|
against the combined Kerf and consumer catalogs; do not duplicate a Kerf id.
|
|
265
|
+
For Doctor's opt-in component ownership mode, add `styleSources` with the CSS
|
|
266
|
+
paths each entry owns, relative to the consumer package root.
|
|
249
267
|
|
|
250
268
|
- Components own semantic markup, stable anatomy, documented variants, ARIA projection, and package CSS hooks.
|
|
251
269
|
- Applications own signals/stores, product copy, domain-state mapping, persistence, routing, permissions, and transport.
|
|
@@ -500,20 +518,49 @@ a plain `<button>` — the group styles `> button` fully, and it keeps the group
|
|
|
500
518
|
free of a Web Awesome dependency and shadow DOM. Use `ToolbarActionLink` when
|
|
501
519
|
the action must retain native anchor navigation; the group owns its geometry,
|
|
502
520
|
hover, and focus treatment. Set `overflow="scroll"` when a row of controls must
|
|
503
|
-
stay inside the available toolbar width.
|
|
521
|
+
stay inside the available toolbar width. A disabled native button keeps the
|
|
522
|
+
group's geometry, dims to half opacity, uses the `not-allowed` cursor, and
|
|
523
|
+
paints neither its own nor a lone group's hover surface.
|
|
524
|
+
Set `visibility="compact-only"` for a
|
|
504
525
|
group that replaces wider toolbar controls below the Toolbar's compact container
|
|
505
|
-
breakpoint; do not hide the group with an application class.
|
|
506
|
-
`
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
526
|
+
breakpoint; do not hide the group with an application class.
|
|
527
|
+
Use `sizing="grow"` on an expanded search group in a wrapping zone: it grows from
|
|
528
|
+
`growBasis` (19rem by default) into available row space, then takes a whole
|
|
529
|
+
row at 480px container width or less. The collapsed search stays icon-sized.
|
|
530
|
+
Use `sizing="fill"` for a group, including a non-search segmented control,
|
|
531
|
+
that spans its wrapping row. Set `visibility="hide-collapsed-tiny"` on a
|
|
532
|
+
collapsible group to omit its closed icon at 224px or less; expanded content
|
|
533
|
+
remains visible. These sizes use the enclosing Toolbar's container width.
|
|
534
|
+
In a stacked Toolbar, set `placement="end"` on a collapsed group that should
|
|
535
|
+
sit at the trailing edge of its flex row. An expanded search with
|
|
536
|
+
`sizing="fill"` takes a whole row and enters from the row above; reduced-motion
|
|
537
|
+
preferences remove that entrance animation.
|
|
538
|
+
Use `visibility="yield-to-expanded-sibling"` on trailing groups that should
|
|
539
|
+
leave a 480px-or-narrower toolbar while a sibling trailing group is expanded.
|
|
540
|
+
They return when it collapses or the toolbar widens; leading and center zones
|
|
541
|
+
remain visible.
|
|
542
|
+
Reach for `wa-button` only when you need a Web Awesome feature; a popup menu is a `PopupMenu`, which renders its own
|
|
543
|
+
trigger. By default a group stays on one row: each icon sits beside its label,
|
|
544
|
+
and the enclosing `Toolbar`'s responsive policy relocates whole groups. Set
|
|
545
|
+
`overflow="wrap"` only for independent links that should move as whole actions
|
|
546
|
+
to another row within the group; use `overflow="scroll"` for a bounded one-row
|
|
547
|
+
action strip. A wrapping group centers in the toolbar band on one row and
|
|
548
|
+
starts at its top when it grows to multiple rows. For a compact mixed-content
|
|
549
|
+
group, set `nestedDropdown`; a
|
|
512
550
|
text-and-caret trigger grows to its intrinsic width while an icon-only trigger
|
|
513
551
|
can remain square. A compact icon `<button>` is a 32px circle, so a selected
|
|
514
552
|
compact segment never becomes an oval. An icon-only `Select` joins a group as
|
|
515
553
|
one more segment with its siblings' inset, radius, hover, and focus ring; only a
|
|
516
|
-
lone Select grows the group to its icon-and-caret pill.
|
|
554
|
+
lone Select grows the group to its icon-and-caret pill.
|
|
555
|
+
|
|
556
|
+
For a rail view heading, use `Select presentation="title"`: its intrinsic
|
|
557
|
+
36px borderless trigger has a bold large label flush with its leading edge and
|
|
558
|
+
a caret beside that label. Set `focusRingInset` when a NavStack or panel clips
|
|
559
|
+
outside painting; the Select keeps its focus outline 3px inside the trigger
|
|
560
|
+
and drops it while the listbox is open. Both settings belong to Select, not an
|
|
561
|
+
application `::part()` override.
|
|
562
|
+
|
|
563
|
+
Set `menuInset` to configure the dropdown menu surface
|
|
517
564
|
without application `::part(menu)` CSS; the default `standard` keeps each menu's
|
|
518
565
|
own inset (8px for a raw `wa-dropdown`, the Select listbox's 4px for a
|
|
519
566
|
`PopupMenu`), while `compact` and `list-zero` apply to every nested menu. A
|
|
@@ -649,6 +696,8 @@ the only shrinking, horizontally scrolling region; `leading`, `trailing`, and
|
|
|
649
696
|
trailing action belongs immediately after the final tab and use `end` for a
|
|
650
697
|
separate action pinned to the far edge. Applications own the action content,
|
|
651
698
|
but must not restyle TabBar descendants to recreate that allocation.
|
|
699
|
+
When a leading AppTab is pinned, the leading scroll divider appears at that
|
|
700
|
+
tab's trailing edge while peer tabs scroll beneath it, including in RTL.
|
|
652
701
|
|
|
653
702
|
CSS, the generated wrappers that make it
|
|
654
703
|
reachable, and the registration module are the package's only declared side
|
|
@@ -670,6 +719,12 @@ line of its own and centers in its slot. It is a
|
|
|
670
719
|
placeholder, not progress: use `LoadingSpinner` for known busy activity, and never
|
|
671
720
|
animate the skeleton.
|
|
672
721
|
|
|
722
|
+
`LoadingSpinner` accepts the same `size` steps as `LucideIcon`: `xs`, `s`, `m`,
|
|
723
|
+
`l`, `xl` (12/16/20/24/32px at a 16px root) or a positive finite pixel
|
|
724
|
+
number converted to rem. Omit `size` to keep its inherited 1em box. A supplied
|
|
725
|
+
size is exposed as `data-size`; callers configure the spinner through this prop
|
|
726
|
+
instead of sizing its SVG from outside.
|
|
727
|
+
|
|
673
728
|
A placeholder is loading, not unavailable, so it keeps the `disabled` attribute (its
|
|
674
729
|
controls stay inert and out of the keyboard order) but never shows disabled chrome.
|
|
675
730
|
`skeleton.css`, which every component that renders a skeleton loads, neutralizes generic
|
|
@@ -171,7 +171,7 @@ beside a `ContentItem` value, and connect its id to the preview group's
|
|
|
171
171
|
| Panel, dialog, or page heading — `Toolbar` + `ToolbarText` | A panel, dialog, or page needs an extra-large title, optional icon, grouped trailing actions, and optional supporting copy. | Do not add a private heading wrapper or bespoke layout. The toolbar supplies structure, not modal behavior; use an application overlay or Web Awesome `wa-dialog` for that behavior. In a narrow peripheral rail or drawer, where xlarge would truncate at the panel's minimum size, use the default `ToolbarText` size as pane identity. | Connect title/supporting-copy ids to the host, group controls, and delegate actions. **For a page or view title set `headingLevel` (usually `1`)** on `ToolbarText`; omit it for a dialog title referenced by `aria-labelledby`. | Open state, focus lifecycle, dismissal, controls, ids, copy, and heading level. | `@kerfjs/ui/toolbar` + `@kerfjs/ui/toolbar-text` | [Toolbar headings](./accessibility.md#toolbar-headings) |
|
|
172
172
|
| Dialog or popup geometry — `DialogSurface`, `PopupSurface` | Recurring Web Awesome dialogs need standard sizes/presentations/body/footer insets, or dropdown menus need list-compatible inset ownership. | Do not use these wrappers as generic layout or to replace native dialog/dropdown behavior. Avoid consumer `::part()` overrides and do not stack a dialog body inset around list-owned child geometry. | Wrap one `wa-dialog` or `wa-dropdown`; choose the typed geometry while retaining native labeling, open state, focus, dismissal, and menu semantics. Compose a dialog body as a `List` by default, use `bodyInset="none"` when its children own list geometry, and wrap bare prose in `ListInsetText`. | State, focus lifecycle, dismissal, labels, content, and outer placement. | `@kerfjs/ui/surface-scaffold` | [Surface scaffolds](./surface-scaffold.md) |
|
|
173
173
|
| Key/value facts — `ValueTable`, `ValueTableRow` | Read-only labels and values form a semantic definition list, optionally with a leading icon. | Do not use it for editable form fields or a row/column data grid; use native form or table semantics. | Compose typed `ValueTableRow` entries; pass `icon` when a 24px leading icon adds useful context. | Values, formatting, icon meaning, and empty/loading policy. | `@kerfjs/ui/value-table` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
174
|
-
| Indeterminate activity — `LoadingSpinner` | A Kerf surface needs compact, labeled or decorative indeterminate progress. | Do not use it for known progress; use Web Awesome `wa-progress-bar` or `wa-progress-ring`. Direct Web Awesome UI may use `wa-spinner`; do not mix spinner systems within one surface. |
|
|
174
|
+
| Indeterminate activity — `LoadingSpinner` | A Kerf surface needs compact, labeled or decorative indeterminate progress. | Do not use it for known progress; use Web Awesome `wa-progress-bar` or `wa-progress-ring`. Direct Web Awesome UI may use `wa-spinner`; do not mix spinner systems within one surface. | Pass a label when the spinner conveys status; use size for a named icon step or positive pixel size. | Loading lifecycle and adjacent status copy. | `@kerfjs/ui/loading-spinner` | [Accessibility](./accessibility.md#shared-rules) |
|
|
175
175
|
| Loading placeholder — `Skeleton` + a component's `placeholder` prop | A value or a whole component is still loading and should hold its space as a subtle, unanimated block, keeping the layout stable — an inspector or detail view rendering its real chrome with per-record values absent. | Do not use it for known progress (use `LoadingSpinner`), do not animate it, and do not hand-rebuild a component's empty state — set `placeholder` on the component instead. Prefer it over `wa-skeleton`, which the pure-Kerf primitives avoid to stay Web-Awesome-free. | Set `placeholder` on a value-bearing component (`Select`, `ListHeader`, `ListItem`, `ValueTableRow`, `SegmentedControl`, `StateBanner`, `AppTab`, `ToolbarText`, `ListActionRow`) to render skeletons in its value slots with interactivity disabled; use the standalone `Skeleton` for a custom slot. | Loading lifecycle, which slots are unknown, and announcing the loading region. | `@kerfjs/ui/skeleton` | [Accessibility](./accessibility.md#shared-rules) |
|
|
176
176
|
| Command menu — `PopupMenu` | A short list of commands sits behind one trigger, such as a toolbar sort or More menu, optionally grouped under headings and dividers with item icons. | Do not use it to choose a persistent value; use `Select`. Do not hand-write `wa-dropdown`/`wa-dropdown-item` markup for an action menu. | Import `@kerfjs/ui/popup-menu/register` once. Place it in a `single` `ToolbarControlGroup` with `nestedDropdown` and a `menuInset`, or wrap it in `PopupSurface` outside a toolbar. Handle item `action`s by delegation. | Commands, enabled state, and reactions to opening or closing. | `@kerfjs/ui/popup-menu` | [Toolbar composition](#toolbar-composition) |
|
|
177
177
|
| Value selection — `Select` | A controlled form, toolbar, or compact navigation value comes from a moderate or long choice list, possibly grouped, icon-bearing, icon-only when closed, or truncating. Set `multiple` with an array `value` when the person may choose any number of them; add `selectedPresentation="icon-only"` and a fixed `triggerIcon` for a toolbar filter menu. | Do not use it for commands; use `PopupMenu`. Do not use it for a small visible choice set; use `SegmentedControl`. Prefer it over direct `wa-select`, `wa-option`, or value-like `wa-dropdown`/`wa-dropdown-item` composition. | Import `@kerfjs/ui/select/register` once for registration and current-request animation ownership; listen for standard input/change events. Do not add consumer popup timing or positioning repairs. Use typed presentation props rather than consumer `::part()` overrides; let the parent own placement. | Controlled value, validation, choices, domain mapping, and outer placement. | `@kerfjs/ui/select` | [Web Awesome integration](../README.md#web-awesome-theme) |
|
|
@@ -268,10 +268,17 @@ instead of the default ellipsis. A `TabBar` owns its
|
|
|
268
268
|
scrolling strip; its public `--kui-tab-bar-strip-*` tokens configure strip
|
|
269
269
|
height, spacing, border, radius, background, and scroll inset without styling
|
|
270
270
|
its internal classes. `--kui-tab-bar-trailing-flex` controls the trailing zone.
|
|
271
|
+
For a distinct application action, use `end` to pin it at the far edge; use
|
|
272
|
+
`trailingPlacement="adjacent"` to keep a tab-local `trailing` action beside the
|
|
273
|
+
strip. Each zone accepts one `ToolbarControlGroup` or one standalone
|
|
274
|
+
`wa-button` for a primary action with its own chrome. Avoid wrappers and
|
|
275
|
+
application flex overrides.
|
|
271
276
|
Set `pinned` on the first `AppTab` when that tab must remain visible as peers
|
|
272
277
|
scroll. It stays inside the tablist and keyboard order. Set the public
|
|
273
278
|
`--kui-app-tab-pinned-background` token if the surrounding surface differs from
|
|
274
|
-
the default; the pinned tab must cover peers as they scroll beneath it.
|
|
279
|
+
the default; the pinned tab must cover peers as they scroll beneath it. Its
|
|
280
|
+
background also covers the strip's inline padding and border so scrolled labels cannot
|
|
281
|
+
paint beside the pinned edge, including in right-to-left strips. Call
|
|
275
282
|
`wireTabBars` for keyboard selection and reorder behavior and its WebKit RTL
|
|
276
283
|
scroll correction.
|
|
277
284
|
|
|
@@ -343,6 +350,11 @@ search content before saving.
|
|
|
343
350
|
The form-field corners and required marker use the same Web Awesome
|
|
344
351
|
form-control radius, marker color, and offset tokens as a neighboring
|
|
345
352
|
`wa-input`.
|
|
353
|
+
The four public color tokens (`--kui-token-search-background`,
|
|
354
|
+
`--kui-token-search-border`, `--kui-token-search-token-background`, and
|
|
355
|
+
`--kui-token-search-token-foreground`) inherit from an app ancestor or the
|
|
356
|
+
field's root. `className` marks that root, including the outer wrapper in
|
|
357
|
+
`form-field` presentation, so one app class can configure either form.
|
|
346
358
|
`TokenSearchRule.suggest(input, state)` receives the unfinished value and current committed tokens, so a rule can omit values already selected. Existing one-argument suggestion callbacks continue to work.
|
|
347
359
|
For a sibling date picker or other app helper, call `model.commit(value)` while a `name:` prefix is active. It parses and commits that value for the active rule even when it is absent from suggestions, replacing the unfinished prefix text. Invalid values and calls without an active prefix leave the query unchanged; `choose(value)` remains restricted to suggested tokens.
|
|
348
360
|
To apply a saved search or restore a query programmatically, call `model.replace({ query, tokens })`. It publishes parsed state and rebuilds the DOM-owned editor text even when no chip changed. For a field without a model, change the field's `revision` prop when setting `query` yourself.
|
|
@@ -354,6 +366,9 @@ passive content such as a shortcut hint.
|
|
|
354
366
|
Set `fill` on a collapsible `TokenSearchField` in a stretched Toolbar center to
|
|
355
367
|
use the available width when expanded; the field and its search group retain
|
|
356
368
|
their compact widths when collapsed.
|
|
369
|
+
When the expanded toolbar field has focus, its inset surface uses
|
|
370
|
+
`--kui-token-search-background` or the default surface color if that token is
|
|
371
|
+
unset.
|
|
357
372
|
|
|
358
373
|
## Toolbar composition
|
|
359
374
|
|
|
@@ -375,10 +390,13 @@ copy below as app-owned content. Size the title to its track: a narrow
|
|
|
375
390
|
peripheral rail or drawer (a navigator, inspector, or console beside a work
|
|
376
391
|
area that carries the extra-large title, often with its own close control in
|
|
377
392
|
the header) labels itself with the default size, because extra-large truncates
|
|
378
|
-
there — "Inspector" becomes "Ins…" in a 160px rail.
|
|
379
|
-
`ToolbarControlGroup`: an icon sits beside its label on one row and the
|
|
380
|
-
sizes to its content, so a row that does not fit relocates whole groups
|
|
381
|
-
through the toolbar's `responsive` policy
|
|
393
|
+
there — "Inspector" becomes "Ins…" in a 160px rail. By default, nothing wraps
|
|
394
|
+
inside a `ToolbarControlGroup`: an icon sits beside its label on one row and the
|
|
395
|
+
group sizes to its content, so a row that does not fit relocates whole groups
|
|
396
|
+
through the toolbar's `responsive` policy. For independent action links that
|
|
397
|
+
must all remain visible, set `overflow="wrap"`: whole links move to another
|
|
398
|
+
row inside the group without breaking an icon from its label. Use
|
|
399
|
+
`overflow="scroll"` when a single-row action strip is intentional. A
|
|
382
400
|
toolbar draws no divider by default: pinned over or under scrolling content,
|
|
383
401
|
it gets one from the scroll state instead — the `Pane` it heads draws the line
|
|
384
402
|
only while content is scrolled beneath it, once `wireScrollDividers` is wired
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 104" width="640" height="104"><defs><clipPath id="cp0"><rect x="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 104" width="640" height="104"><defs><clipPath id="cp0"><rect x="24" y="24" width="460" height="52"/></clipPath><clipPath id="ov1"><rect x="24" y="24" width="460" height="52" rx="0"/></clipPath><clipPath id="cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#cp0)"/><g clip-path="url(#ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp2)"><g clip-path="url(#ov3)"><g clip-path="url(#cp4)"><g clip-path="url(#ct5)"><text x="38" y="55.41" font-family=""ui-sans-serif", "-apple-system", "system-ui", "Segoe UI", sans-serif" font-size="16" font-weight="650" fill="rgb(174,174,178)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(58,58,60)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp6)"><g color="rgb(174, 174, 178)"><clipPath id="svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 12z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 19z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 5h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 12h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 19h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp8)"><g color="rgb(174, 174, 178)"><clipPath id="svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp10)"><g color="rgb(174, 174, 178)"><clipPath id="svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 104" width="640" height="104"><defs><clipPath id="cp0"><rect x="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 104" width="640" height="104"><defs><clipPath id="cp0"><rect x="24" y="24" width="460" height="52"/></clipPath><clipPath id="ov1"><rect x="24" y="24" width="460" height="52" rx="0"/></clipPath><clipPath id="cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#cp0)"/><g clip-path="url(#ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp2)"><g clip-path="url(#ov3)"><g clip-path="url(#cp4)"><g clip-path="url(#ct5)"><text x="38" y="55.41" font-family=""ui-sans-serif", "-apple-system", "system-ui", "Segoe UI", sans-serif" font-size="16" font-weight="650" fill="rgb(110,110,115)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(229,229,234)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp6)"><g color="rgb(110, 110, 115)"><clipPath id="svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 12z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 19z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 5h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 12h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 19h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp8)"><g color="rgb(110, 110, 115)"><clipPath id="svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp10)"><g color="rgb(110, 110, 115)"><clipPath id="svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 158" width="672" height="158"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="104"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="104" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="158" rx="0" fill="rgb(28,28,30)"/><text x="16" y="28" font-family="system-ui, "-apple-system", sans-serif" font-size="12" font-weight="600" fill="rgb(174,174,178)">Title with a trailing group</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="104"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 158" width="672" height="158"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="104"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="104" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="158" rx="0" fill="rgb(28,28,30)"/><text x="16" y="28" font-family="system-ui, "-apple-system", sans-serif" font-size="12" font-weight="600" fill="rgb(174,174,178)">Title with a trailing group</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="104"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="24" y="24" width="460" height="52"/></clipPath><clipPath id="svgimg3ov1"><rect x="24" y="24" width="460" height="52" rx="0"/></clipPath><clipPath id="svgimg3cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="svgimg3ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="svgimg3cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"/><g clip-path="url(#svgimg3ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp2)"><g clip-path="url(#svgimg3ov3)"><g clip-path="url(#svgimg3cp4)"><g clip-path="url(#svgimg3ct5)"><text x="38" y="55.41" font-family=""ui-sans-serif", "-apple-system", "system-ui", "Segoe UI", sans-serif" font-size="16" font-weight="650" fill="rgb(174,174,178)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(58,58,60)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp6)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 12z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 19z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 5h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 12h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 19h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp8)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp10)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g></g></g></g></g></svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 158" width="672" height="158"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="104"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="104" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="158" rx="0" fill="rgb(255,255,255)"/><text x="16" y="28" font-family="system-ui, "-apple-system", sans-serif" font-size="12" font-weight="600" fill="rgb(110,110,115)">Title with a trailing group</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="104"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 158" width="672" height="158"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="104"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="104" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="158" rx="0" fill="rgb(255,255,255)"/><text x="16" y="28" font-family="system-ui, "-apple-system", sans-serif" font-size="12" font-weight="600" fill="rgb(110,110,115)">Title with a trailing group</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="104"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="24" y="24" width="460" height="52"/></clipPath><clipPath id="svgimg3ov1"><rect x="24" y="24" width="460" height="52" rx="0"/></clipPath><clipPath id="svgimg3cp2"><rect x="30" y="30" width="88.1" height="40"/></clipPath><clipPath id="svgimg3ov3"><rect x="30" y="30" width="88.1" height="40" rx="0"/></clipPath><clipPath id="svgimg3cp4"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3ct5"><rect x="38" y="40.4" width="72.1" height="19.2"/></clipPath><clipPath id="svgimg3cp6"><rect x="502" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp8"><rect x="542" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp10"><rect x="582" y="42" width="16" height="16"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"/><g clip-path="url(#svgimg3ov1)"><rect x="28.5" y="28.5" width="91" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp2)"><g clip-path="url(#svgimg3ov3)"><g clip-path="url(#svgimg3cp4)"><g clip-path="url(#svgimg3ct5)"><text x="38" y="55.41" font-family=""ui-sans-serif", "-apple-system", "system-ui", "Segoe UI", sans-serif" font-size="16" font-weight="650" fill="rgb(110,110,115)">Northstar</text></g></g></g></g></g><rect x="488" y="28" width="124" height="44" rx="22" fill="rgb(229,229,234)"/><rect x="488.5" y="28.5" width="123" height="43" rx="21.5" fill="none" stroke="rgb(209,209,214)" stroke-width="1"/><rect x="490.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp6)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic7"><rect x="502" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic7)"><g transform="translate(502 42)scale(.66667)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 12z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 19z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 5h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 12h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 19h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="530.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp8)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic9"><rect x="542" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic9)"><g transform="translate(542 42)scale(.66667)"><rect width="18" height="18" x="3" y="3" rx="2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M9 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M15 3v18" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><rect x="570.5" y="30.5" width="39" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp10)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic11"><rect x="582" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic11)"><g transform="translate(582 42)scale(.66667)"><path d="M9.7 4.1a2.3 2.3 0 0 1 4.6 0 2.3 2.3 0 0 0 3.3 2 2.3 2.3 0 0 1 2.4 4 2.3 2.3 0 0 0 0 3.8 2.3 2.3 0 0 1-2.4 4 2.3 2.3 0 0 0-3.3 2 2.3 2.3 0 0 1-4.6 0 2.3 2.3 0 0 0-3.3-2A2.3 2.3 0 0 1 4 14a2.3 2.3 0 0 0 0-3.8 2.3 2.3 0 0 1 2.3-4 2.3 2.3 0 0 0 3.4-2" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><circle cx="12" cy="12" r="3" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g></g></g></g></g></svg>
|
package/docs/layout.md
CHANGED
|
@@ -111,6 +111,12 @@ scrolls under its pinned header; with a header it draws the line under that
|
|
|
111
111
|
header itself, so the stack's chrome never doubles it. It follows re-renders: panes that appear later are paired, and an
|
|
112
112
|
attribute a morph drops returns before paint.
|
|
113
113
|
|
|
114
|
+
Multiple calls in one document share one attribute writer, including calls on
|
|
115
|
+
overlapping roots. Each call contributes its own root and optional named
|
|
116
|
+
targets; its disposer removes only that registration. When the last disposer
|
|
117
|
+
runs, the writer removes the attributes it owns. An app that calls `wireCatalog`
|
|
118
|
+
for a root already has scroll dividers there and does not need another call.
|
|
119
|
+
|
|
114
120
|
The wiring only reports state; every component draws its own line. It writes
|
|
115
121
|
`data-scroll-overflow` (the edges with content hidden beyond them, in canonical
|
|
116
122
|
`t`/`r`/`b`/`l` order) on each scroller and `data-scroll-divider` (the sides to
|
package/docs/ui-analyzer.md
CHANGED
|
@@ -144,6 +144,10 @@ which element a selector styles.
|
|
|
144
144
|
imported component's JSX (`<Toolbar className="header" />`) is the subject
|
|
145
145
|
of a rule in a stylesheet that file imports, so the application restyles the
|
|
146
146
|
component through it. The evidence names the stylesheet, line, and selector.
|
|
147
|
+
For cataloged application components, the import may be direct and relative
|
|
148
|
+
or use an exact or wildcard TypeScript `paths` alias from the nearest
|
|
149
|
+
`tsconfig.json` inside the analyzed root (including inherited options).
|
|
150
|
+
Unresolved aliases remain unclassified; the analyzer does not execute code.
|
|
147
151
|
|
|
148
152
|
Every message names the configuration to use (the component's typed props,
|
|
149
153
|
public tokens, and variants from its catalog entry) and ends with the same
|
|
@@ -188,6 +192,14 @@ findings. `eslint-plugin-kerfjs`'s `ui-component-ownership` rule reports
|
|
|
188
192
|
`KUI-L020` and `KUI-L021` in JavaScript and TypeScript (style strings, style
|
|
189
193
|
objects, and `style.setProperty()`), where this analyzer reads only CSS.
|
|
190
194
|
|
|
195
|
+
Pass `ownership: 'component'` to `analyzeUiProject` to enforce the boundary
|
|
196
|
+
between components in one package. In that mode each catalog entry owns only
|
|
197
|
+
its declared `styleSources` for CSS and its selection catalog `source` module
|
|
198
|
+
for JSX; sibling files are foreign. Paths are relative to that entry's package
|
|
199
|
+
root. Declare `styleSources` on the selection catalog extension entry or
|
|
200
|
+
composition entry. Entries without those paths own no files in component mode,
|
|
201
|
+
so add them before adopting it.
|
|
202
|
+
|
|
191
203
|
### Retired: `KUI-L005` (forced component dimension)
|
|
192
204
|
|
|
193
205
|
`KUI-L005` used to report, as a review finding, any `width`/`height`/`min-*`/
|
package/docs/ui-doctor.md
CHANGED
|
@@ -7,6 +7,7 @@ npx kerf-ui-doctor --full
|
|
|
7
7
|
npx kerf-ui-doctor --changed
|
|
8
8
|
npx kerf-ui-doctor --package @acme/admin --changed
|
|
9
9
|
npx kerf-ui-doctor --full --browser-url http://127.0.0.1:4173
|
|
10
|
+
npx kerf-ui-doctor --full --fail-on warning
|
|
10
11
|
```
|
|
11
12
|
|
|
12
13
|
The default terminal output is short and repair-oriented. `--format json` emits the schema-version-1 report; `--output report.json` writes the same representation. The package exports its contract from `@kerfjs/ui/doctor`, its configuration schema from `@kerfjs/ui/doctor/config.schema.json`, and its report schema from `@kerfjs/ui/doctor/report.schema.json`.
|
|
@@ -39,6 +40,15 @@ root (`KUI-L022`); the ESLint stage's `ui-component-ownership` rule reports
|
|
|
39
40
|
component's configuration, and its message routes a need with no prop to a
|
|
40
41
|
component-gap report instead of an override (see
|
|
41
42
|
[the analyzer rule](./ui-analyzer.md#component-ownership-kui-l019kui-l022)).
|
|
43
|
+
The analyzer resolves a cataloged application component imported through an
|
|
44
|
+
exact or wildcard TypeScript `paths` alias from the nearest consumer tsconfig,
|
|
45
|
+
so the same root hook-class check applies in those files.
|
|
46
|
+
Set `"ownership": "component"` in `.kerf-ui-doctor.json` to enforce the same
|
|
47
|
+
boundary between a consumer package's own cataloged components. The default is
|
|
48
|
+
`"package"`. Each local catalog entry then owns only its declared
|
|
49
|
+
`styleSources` (relative to its package root) and its selection catalog
|
|
50
|
+
`source` module. The component mode reports sibling CSS and hook-class
|
|
51
|
+
restyles while still allowing a component's own stylesheet and module.
|
|
42
52
|
|
|
43
53
|
The browser evaluator is different: it runs the application and is disabled by default. It only runs when configuration supplies `browser.url` or the command receives `--browser-url`. Start and authorize the target application separately.
|
|
44
54
|
|
|
@@ -66,8 +76,10 @@ Place `.kerf-ui-doctor.json` at the workspace root:
|
|
|
66
76
|
"$schema": "./node_modules/@kerfjs/ui/doctor/config.schema.json",
|
|
67
77
|
"schemaVersion": 1,
|
|
68
78
|
"mode": "full",
|
|
79
|
+
"ownership": "component",
|
|
69
80
|
"stages": { "browser": false },
|
|
70
81
|
"cache": true,
|
|
82
|
+
"failOn": "warning",
|
|
71
83
|
"suppressions": [
|
|
72
84
|
{
|
|
73
85
|
"id": "legacy-toolbar",
|
|
@@ -91,14 +103,14 @@ Reports replace the workspace's absolute path with `<repo-root>` and express in-
|
|
|
91
103
|
|
|
92
104
|
Every diagnostic has a stable `id`, `severity`, `stage`, message, and—when applicable—an exact source location with JSON path or DOM context/selector. Analyzer evidence, catalog facts, documentation links, and safe next actions are preserved when the source tool provides them. Identical findings merge with their source stages; same identifiers with conflicting severities remain separate and add `KUI-D003`.
|
|
93
105
|
|
|
94
|
-
Exit codes are deterministic
|
|
106
|
+
Exit codes are deterministic. `failOn` defaults to `error`; `--fail-on error|review|warning` overrides the config for a run. `review` fails on errors or review findings; `warning` also fails on warnings. Suppressed diagnostics never count. The effective threshold is part of the cache key.
|
|
95
107
|
|
|
96
|
-
- `0`: no active
|
|
108
|
+
- `0`: no active diagnostics at or above the selected threshold;
|
|
97
109
|
- `1`: repairable application findings;
|
|
98
110
|
- `2`: malformed configuration, unavailable required tooling, or a failed stage;
|
|
99
111
|
- `130`: cancellation.
|
|
100
112
|
|
|
101
|
-
Warnings and review findings remain visible
|
|
113
|
+
Warnings and review findings remain visible at every threshold. A cached report keeps its original exit code and labels previously run stages `cached`.
|
|
102
114
|
|
|
103
115
|
## Monorepos and repair loops
|
|
104
116
|
|
package/docs/webawesome-theme.md
CHANGED
|
@@ -86,6 +86,13 @@ exposes `label` instead of `form-control-label`; the theme gives it the same
|
|
|
86
86
|
uppercase 12px/650 treatment. Inline control labels (Checkbox, Switch) keep
|
|
87
87
|
their natural sentence case.
|
|
88
88
|
|
|
89
|
+
On coarse touch devices, the editable part of small and extra-small Web Awesome
|
|
90
|
+
Input, Number Input, Time Input, Textarea, and Select has a 16px minimum text
|
|
91
|
+
size. Small field chrome keeps its compact height and desktop typography stays
|
|
92
|
+
small. This prevents Safari from magnifying the page just to focus text; it does
|
|
93
|
+
not constrain a user's own pinch zoom. App-owned native fields need the same
|
|
94
|
+
editable-text audit.
|
|
95
|
+
|
|
89
96
|
Known Date's field captions and bordered text-like field hints use the same 9px
|
|
90
97
|
inline inset, keeping secondary text aligned with the value rather than the
|
|
91
98
|
field's outer border. This covers Input, Known Date, Number Input, OTP Input,
|
package/doctor/cli.mjs
CHANGED
|
@@ -54,6 +54,7 @@ Options:
|
|
|
54
54
|
--browser <list> Comma-separated chromium,firefox,webkit
|
|
55
55
|
--eslint <preset> recommended-ui or strict-ui
|
|
56
56
|
--format <format> text or json (default: text)
|
|
57
|
+
--fail-on <severity> error, review, or warning (default: error)
|
|
57
58
|
--output <path> Also write the merged report
|
|
58
59
|
--no-cache Disable the content-addressed cache
|
|
59
60
|
|
|
@@ -68,6 +69,7 @@ Exit codes: 0 clean, 1 findings, 2 configuration/tool failure, 130 cancelled.`);
|
|
|
68
69
|
const browserList = take('--browser');
|
|
69
70
|
const eslintConfig = take('--eslint') ?? 'recommended-ui';
|
|
70
71
|
const format = take('--format') ?? 'text';
|
|
72
|
+
const failOn = take('--fail-on');
|
|
71
73
|
const output = take('--output');
|
|
72
74
|
const full = args.includes('--full');
|
|
73
75
|
const changed = args.includes('--changed');
|
|
@@ -80,6 +82,8 @@ Exit codes: 0 clean, 1 findings, 2 configuration/tool failure, 130 cancelled.`);
|
|
|
80
82
|
throw new Error('--full and --changed are mutually exclusive.');
|
|
81
83
|
if (!['text', 'json'].includes(format))
|
|
82
84
|
throw new Error('--format must be text or json.');
|
|
85
|
+
if (failOn && !['error', 'review', 'warning'].includes(failOn))
|
|
86
|
+
throw new Error('--fail-on must be error, review, or warning.');
|
|
83
87
|
if (!['recommended-ui', 'strict-ui'].includes(eslintConfig))
|
|
84
88
|
throw new Error('--eslint must be recommended-ui or strict-ui.');
|
|
85
89
|
const abort = new globalThis.AbortController();
|
|
@@ -95,6 +99,7 @@ Exit codes: 0 clean, 1 findings, 2 configuration/tool failure, 130 cancelled.`);
|
|
|
95
99
|
cache: !noCache,
|
|
96
100
|
signal: abort.signal,
|
|
97
101
|
eslintConfig,
|
|
102
|
+
failOn,
|
|
98
103
|
browser: url
|
|
99
104
|
? { url, browsers: browserList?.split(',').map((item) => item.trim()) }
|
|
100
105
|
: undefined,
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"schemaVersion": { "const": 1 },
|
|
10
10
|
"mode": { "enum": ["full", "changed"] },
|
|
11
11
|
"package": { "type": "string", "minLength": 1 },
|
|
12
|
+
"ownership": { "enum": ["package", "component"] },
|
|
12
13
|
"stages": {
|
|
13
14
|
"type": "object",
|
|
14
15
|
"properties": {
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"additionalProperties": false
|
|
38
39
|
},
|
|
39
40
|
"cache": { "type": "boolean" },
|
|
41
|
+
"failOn": { "enum": ["error", "review", "warning"] },
|
|
40
42
|
"suppressions": {
|
|
41
43
|
"type": "array",
|
|
42
44
|
"items": {
|