@kerfjs/ui 5.0.0-beta.22 → 5.0.0-beta.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/README.md +12 -3
  2. package/ai/compile-time-contracts-v1.json +2 -1
  3. package/ai/component-catalog-v2-overrides.json +5 -12
  4. package/ai/component-catalog-v2.json +266 -32
  5. package/ai/component-catalog.json +293 -37
  6. package/ai/component-catalog.schema.json +1 -0
  7. package/ai/public-api-signatures-v1.md +29 -16
  8. package/ai/skill.md +4 -4
  9. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  10. package/analyzer/index.mjs +2 -8
  11. package/dist/app-tab.js +1 -1
  12. package/dist/browser/list.js +3 -0
  13. package/dist/catalog.d.ts +8 -4
  14. package/dist/catalog.js +19 -10
  15. package/dist/catalog.js.map +1 -1
  16. package/dist/{chunk-RYRIFZCT.js → chunk-7XRDUG4Y.js} +5 -5
  17. package/dist/{chunk-RYRIFZCT.js.map → chunk-7XRDUG4Y.js.map} +1 -1
  18. package/dist/{chunk-OHU6CI22.js → chunk-B3A3IVO5.js} +2 -2
  19. package/dist/{chunk-OHU6CI22.js.map → chunk-B3A3IVO5.js.map} +1 -1
  20. package/dist/{chunk-ELSJX4W7.js → chunk-FEAMR7S4.js} +2 -2
  21. package/dist/{chunk-ELSJX4W7.js.map → chunk-FEAMR7S4.js.map} +1 -1
  22. package/dist/{chunk-OZWU5ZPZ.js → chunk-IJBSZ4NX.js} +2 -2
  23. package/dist/{chunk-OZWU5ZPZ.js.map → chunk-IJBSZ4NX.js.map} +1 -1
  24. package/dist/chunk-J5BFYY7Q.js +35 -0
  25. package/dist/chunk-J5BFYY7Q.js.map +1 -0
  26. package/dist/{chunk-QQFJMRIB.js → chunk-KWWR5VMS.js} +2 -2
  27. package/dist/{chunk-QQFJMRIB.js.map → chunk-KWWR5VMS.js.map} +1 -1
  28. package/dist/{chunk-K57KRAOV.js → chunk-NWSEP5RT.js} +2 -2
  29. package/dist/{chunk-K57KRAOV.js.map → chunk-NWSEP5RT.js.map} +1 -1
  30. package/dist/{chunk-GP5DP5DL.js → chunk-OJWWHFH3.js} +39 -4
  31. package/dist/chunk-OJWWHFH3.js.map +1 -0
  32. package/dist/{chunk-DSNIYOZ3.js → chunk-PSKN344N.js} +2 -2
  33. package/dist/{chunk-DSNIYOZ3.js.map → chunk-PSKN344N.js.map} +1 -1
  34. package/dist/{chunk-W7YD6E4N.js → chunk-R7ZWUN64.js} +2 -2
  35. package/dist/{chunk-W7YD6E4N.js.map → chunk-R7ZWUN64.js.map} +1 -1
  36. package/dist/{chunk-PZQVSDLH.js → chunk-RKVQEH4J.js} +1 -1
  37. package/dist/chunk-RKVQEH4J.js.map +1 -0
  38. package/dist/{chunk-7L6EHUMV.js → chunk-SYDMYBPG.js} +3 -3
  39. package/dist/chunk-SYDMYBPG.js.map +1 -0
  40. package/dist/{chunk-W4CXQYSG.js → chunk-TB6DY7H5.js} +3 -1
  41. package/dist/chunk-TB6DY7H5.js.map +1 -0
  42. package/dist/{chunk-BBRXX6PF.js → chunk-UFZKLBCF.js} +2 -2
  43. package/dist/{chunk-BBRXX6PF.js.map → chunk-UFZKLBCF.js.map} +1 -1
  44. package/dist/{chunk-AASLYRGE.js → chunk-UKKSMZYT.js} +18 -7
  45. package/dist/chunk-UKKSMZYT.js.map +1 -0
  46. package/dist/{chunk-VLYV65LK.js → chunk-W5L2JSBY.js} +5 -3
  47. package/dist/chunk-W5L2JSBY.js.map +1 -0
  48. package/dist/{chunk-PIDTEQ3U.js → chunk-Y45PPBDV.js} +2 -2
  49. package/dist/{chunk-PIDTEQ3U.js.map → chunk-Y45PPBDV.js.map} +1 -1
  50. package/dist/divider-sides-267FA7sY.d.ts +4 -0
  51. package/dist/index.d.ts +3 -1
  52. package/dist/index.js +16 -15
  53. package/dist/list-action-row.js +1 -1
  54. package/dist/list-header.js +1 -1
  55. package/dist/list-item.js +1 -1
  56. package/dist/list.d.ts +19 -0
  57. package/dist/list.js +3 -0
  58. package/dist/list.js.map +1 -0
  59. package/dist/nav-stack.js +1 -1
  60. package/dist/panel-header.js +1 -1
  61. package/dist/segmented-control.js +1 -1
  62. package/dist/select.js +1 -1
  63. package/dist/skeleton.d.ts +1 -1
  64. package/dist/skeleton.js +1 -1
  65. package/dist/split-view.js +1 -1
  66. package/dist/state-banner.d.ts +4 -2
  67. package/dist/state-banner.js +1 -1
  68. package/dist/styles/catalog.css +8 -2
  69. package/dist/styles/list-item.css +6 -6
  70. package/dist/styles/list.css +43 -0
  71. package/dist/styles/state-banner.css +32 -1
  72. package/dist/styles/styles.css +1 -0
  73. package/dist/styles/sunken-panel.css +4 -0
  74. package/dist/styles/toolbar.css +38 -2
  75. package/dist/sunken-panel.d.ts +5 -2
  76. package/dist/sunken-panel.js +1 -1
  77. package/dist/toolbar-text.js +1 -1
  78. package/dist/toolbar.d.ts +5 -3
  79. package/dist/toolbar.js +1 -1
  80. package/dist/value-table.js +1 -1
  81. package/dist/wire-catalog.d.ts +4 -2
  82. package/dist/wire-catalog.js +95 -36
  83. package/dist/wire-catalog.js.map +1 -1
  84. package/dist/wire-tab-bars.js +1 -1
  85. package/dist/wire-token-search-fields.d.ts +1 -1
  86. package/dist/wire-token-search-fields.js +1 -1
  87. package/docs/accessibility.md +12 -3
  88. package/docs/catalog.md +17 -12
  89. package/docs/component-contract.md +14 -1
  90. package/docs/component-selection.md +18 -2
  91. package/docs/design/templates/empty-state/filtered-dark.svg +1 -1
  92. package/docs/design/templates/empty-state/filtered.svg +1 -1
  93. package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
  94. package/docs/design/templates/empty-state/with-action.svg +1 -1
  95. package/docs/design/templates/empty-state-dark.svg +1 -7
  96. package/docs/design/templates/empty-state.svg +1 -7
  97. package/docs/design/templates/list/compact-dark.svg +1 -0
  98. package/docs/design/templates/list/compact.svg +1 -0
  99. package/docs/design/templates/list/stack-dark.svg +1 -0
  100. package/docs/design/templates/list/stack.svg +1 -0
  101. package/docs/design/templates/list-action-row/default-dark.svg +1 -1
  102. package/docs/design/templates/list-action-row/default.svg +1 -1
  103. package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
  104. package/docs/design/templates/list-action-row/selected.svg +1 -1
  105. package/docs/design/templates/list-action-row-dark.svg +1 -7
  106. package/docs/design/templates/list-action-row.svg +1 -7
  107. package/docs/design/templates/list-dark.svg +1 -0
  108. package/docs/design/templates/list-header/action-dark.svg +1 -1
  109. package/docs/design/templates/list-header/action.svg +1 -1
  110. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -1
  111. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -1
  112. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -1
  113. package/docs/design/templates/list-header/toggle-expanded.svg +1 -1
  114. package/docs/design/templates/list-header-dark.svg +1 -11
  115. package/docs/design/templates/list-header.svg +1 -11
  116. package/docs/design/templates/list-item/default-dark.svg +1 -1
  117. package/docs/design/templates/list-item/default.svg +1 -1
  118. package/docs/design/templates/list-item/multiline-dark.svg +1 -1
  119. package/docs/design/templates/list-item/multiline.svg +1 -1
  120. package/docs/design/templates/list-item/selected-dark.svg +1 -1
  121. package/docs/design/templates/list-item/selected.svg +1 -1
  122. package/docs/design/templates/list-item/trailing-dark.svg +1 -1
  123. package/docs/design/templates/list-item/trailing.svg +1 -1
  124. package/docs/design/templates/list-item-dark.svg +1 -11
  125. package/docs/design/templates/list-item.svg +1 -11
  126. package/docs/design/templates/list.svg +1 -0
  127. package/docs/design/templates/panel-header/icon-actions-dark.svg +1 -1
  128. package/docs/design/templates/panel-header/icon-actions.svg +1 -1
  129. package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +1 -1
  130. package/docs/design/templates/panel-header/icon-summary-actions.svg +1 -1
  131. package/docs/design/templates/panel-header-dark.svg +1 -13
  132. package/docs/design/templates/panel-header.svg +1 -13
  133. package/docs/design/templates/segmented-control-dark.svg +1 -7
  134. package/docs/design/templates/segmented-control.svg +1 -7
  135. package/docs/design/templates/skeleton-dark.svg +1 -9
  136. package/docs/design/templates/skeleton.svg +1 -9
  137. package/docs/design/templates/state-banner/danger-dark.svg +1 -1
  138. package/docs/design/templates/state-banner/danger.svg +1 -1
  139. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  140. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  141. package/docs/design/templates/state-banner/info-dark.svg +1 -1
  142. package/docs/design/templates/state-banner/info.svg +1 -1
  143. package/docs/design/templates/state-banner/success-dark.svg +1 -1
  144. package/docs/design/templates/state-banner/success.svg +1 -1
  145. package/docs/design/templates/state-banner/warning-dark.svg +1 -1
  146. package/docs/design/templates/state-banner/warning.svg +1 -1
  147. package/docs/design/templates/state-banner-dark.svg +1 -11
  148. package/docs/design/templates/state-banner.svg +1 -11
  149. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -1
  150. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -1
  151. package/docs/design/templates/tab-bar-dark.svg +1 -5
  152. package/docs/design/templates/tab-bar.svg +1 -5
  153. package/docs/design/templates/token-search-field/empty-dark.svg +1 -1
  154. package/docs/design/templates/token-search-field/empty.svg +1 -1
  155. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -1
  156. package/docs/design/templates/token-search-field/tokens.svg +1 -1
  157. package/docs/design/templates/token-search-field-dark.svg +1 -7
  158. package/docs/design/templates/token-search-field.svg +1 -7
  159. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  160. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  161. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -1
  162. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -1
  163. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -1
  164. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -1
  165. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -1
  166. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -1
  167. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
  168. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
  169. package/docs/design/templates/toolbar-control-group-dark.svg +1 -11
  170. package/docs/design/templates/toolbar-control-group.svg +1 -11
  171. package/docs/design/templates/toolbar-dark.svg +1 -5
  172. package/docs/design/templates/toolbar-text-dark.svg +1 -9
  173. package/docs/design/templates/toolbar-text.svg +1 -9
  174. package/docs/design/templates/toolbar.svg +1 -5
  175. package/docs/design/templates/value-table/details-dark.svg +1 -1
  176. package/docs/design/templates/value-table/details.svg +1 -1
  177. package/docs/design/templates/value-table-dark.svg +1 -5
  178. package/docs/design/templates/value-table.svg +1 -5
  179. package/docs/design/templates.md +25 -14
  180. package/docs/sunken-panel.md +11 -1
  181. package/docs/type-contracts.md +6 -3
  182. package/docs/ui-analyzer.md +5 -0
  183. package/docs/ui-doctor.md +8 -1
  184. package/docs/ux-demo.md +2 -2
  185. package/docs/workbench.md +13 -0
  186. package/doctor/eslint-diagnostics.d.mts +8 -0
  187. package/doctor/eslint-diagnostics.mjs +7 -0
  188. package/doctor/index.mjs +11 -10
  189. package/llms.txt +15 -3
  190. package/package.json +9 -2
  191. package/traversal-exclusions.mjs +27 -0
  192. package/ux-demo/recipes/collapsible-sidebar.tsx +1 -1
  193. package/ux-demo/recipes/navigation-sidebar.tsx +1 -1
  194. package/dist/chunk-7L6EHUMV.js.map +0 -1
  195. package/dist/chunk-AASLYRGE.js.map +0 -1
  196. package/dist/chunk-GP5DP5DL.js.map +0 -1
  197. package/dist/chunk-PZQVSDLH.js.map +0 -1
  198. package/dist/chunk-VLYV65LK.js.map +0 -1
  199. package/dist/chunk-W4CXQYSG.js.map +0 -1
@@ -10,7 +10,7 @@ component picture without running the app.
10
10
  ## Coverage
11
11
 
12
12
  Templated components (each with light + dark variants): `PanelHeader`,
13
- `ToolbarControlGroup`, `Toolbar`, `ToolbarText`, `ListItem`, `ListHeader`,
13
+ `ToolbarControlGroup`, `Toolbar`, `ToolbarText`, `List`, `ListItem`, `ListHeader`,
14
14
  `ListActionRow`, `ValueTable`, `StateBanner`, `EmptyState`, `Skeleton`,
15
15
  `SegmentedControl`, `TabBar`/`AppTab`, and `TokenSearchField`.
16
16
 
@@ -27,7 +27,7 @@ catalog and the app-layouts docs cover those.
27
27
 
28
28
  ```
29
29
  docs/design/templates/
30
- panel-header.svg ← library: every variant, inlined (light)
30
+ panel-header.svg ← library: every variant, composed (light)
31
31
  panel-header-dark.svg ← library: dark theme
32
32
  panel-header/
33
33
  icon-summary-actions.svg ← one self-contained variant (light)
@@ -50,13 +50,12 @@ docs/design/templates/
50
50
  kerf UI design font stack is system-based, so the picture matches; a viewer
51
51
  without those fonts falls back to its own.
52
52
  - The **library** files (`<component>.svg` light, `<component>-dark.svg` dark) lay
53
- the variants out with captions and embed an inline **copy** of each variant as a
54
- positioned nested `<svg>`. Each is fully self-contained and renders everywhere —
55
- a browser, an `<img>`, GitHub, or a static rasterizer. (External
56
- `<use href="…#id">` and `<image href="…">` references render blank in many SVG
57
- viewers, so the library inlines copies instead. Each copy's local ids and
58
- domotion font-family names are namespaced so the inlined variants don't collide
59
- in the one document; the individual variant files stay individually reusable.)
53
+ the variants out with captions. The generator writes a temporary HTML page whose
54
+ `<img>` elements reference the individual variant SVGs, then captures that page
55
+ with domotion and `--flatten-nested-svg`. The final library is self-contained,
56
+ contains positioned groups rather than nested `<svg>` elements, and remains
57
+ reliable in design tools such as Sketch. The individual variant files stay
58
+ independently reusable.
60
59
 
61
60
  ## Building
62
61
 
@@ -67,6 +66,9 @@ npm run design-templates:build
67
66
  This renders each component's real `SafeHtml` (with its CSS subpaths + `foundation.css`
68
67
  tokens and representative sample data) into a standalone page and captures it with
69
68
  [`domotion-svg`](https://github.com/brianwestphal/domotion)'s `domotion capture`.
69
+ The capture command uses `--flatten-nested-svg`, which converts safely flattenable
70
+ inline SVGs (including component icons) to groups so design tools such as Sketch do
71
+ not have to interpret nested `<svg>` elements.
70
72
  `domotion` must be reachable: it resolves `DOMOTION_BIN`, then a local
71
73
  `node_modules/.bin/domotion`, then a sibling `../domotion` checkout.
72
74
 
@@ -79,7 +81,9 @@ component can render with or without an icon, include both.
79
81
  `npm run check:design-templates` (part of `npm run check`) is an offline gate
80
82
  that verifies every component + variant in the manifest has its committed output —
81
83
  a light and dark SVG per variant plus the two per-component library files — and
82
- that no stray template files linger for a removed component. It does **not**
84
+ that no stray template files linger for a removed component. It also rejects any
85
+ variant or library capture that still contains nested `<svg>` elements, guarding
86
+ the Sketch compatibility contract. It does **not**
83
87
  re-render (that needs domotion + a browser), so it catches a manifest entry whose
84
88
  templates were never generated or a half-regenerated set.
85
89
 
@@ -109,11 +113,18 @@ starting point and:
109
113
  tokens and `layout.css`), and any of your own component CSS.
110
114
  3. Enumerate the presentation combinations in the per-component manifest.
111
115
  4. Capture each variant with `domotion capture <page.html> --selector <css>
112
- --text-mode system-font -o <variant>.svg`, once per theme with
116
+ --text-mode system-font --flatten-nested-svg -o <variant>.svg`, once per theme with
113
117
  `--color-scheme light` / `--color-scheme dark` (if your components theme with
114
- `light-dark()`), then write a light and a dark library file that each embed an
115
- inline copy of every variant (namespacing each copy's ids/font-family names so
116
- they don't collide) rather than referencing them, so they render everywhere.
118
+ `light-dark()`).
119
+ 5. Build a light and dark HTML library page that references the generated variants
120
+ with `<img src="./component/variant.svg">`, and capture each page with the same
121
+ domotion options, including `--flatten-nested-svg`. Let domotion own embedding,
122
+ id isolation, and flattening instead of hand-assembling SVG markup.
117
123
 
118
124
  Maintaining these next to the components — and reviewing the captured SVGs on
119
125
  every component change — keeps the design source of truth honest.
126
+
127
+ ListItem templates retain the shared 18px leading icon and 44px row target;
128
+ regenerate both themes and library sheets when that geometry changes. The browser
129
+ pane/sidebar geometry checks cover the resulting 43px icon-label offset from the
130
+ section edge (17px content inset + 18px icon + 8px gap).
@@ -14,6 +14,15 @@ import { SunkenPanel } from '@kerfjs/ui/sunken-panel';
14
14
  </SunkenPanel>;
15
15
  ```
16
16
 
17
+ The default `shape="rounded"` uses the shared rounded-rectangle radius. Choose
18
+ `shape="square"` for a flush or edge-to-edge area that needs `border-radius: 0`:
19
+
20
+ ```tsx
21
+ <SunkenPanel shape="square">
22
+ <Workspace />
23
+ </SunkenPanel>
24
+ ```
25
+
17
26
  ## Ownership
18
27
 
19
28
  The root owns its lowered background, 8px padding, and 8px vertical gap.
@@ -40,4 +49,5 @@ Override the public properties at the composition boundary:
40
49
  - `--kui-sunken-panel-radius`
41
50
 
42
51
  The public root class is `.kui-sunken-panel`. Prefer the properties above over
43
- styling descendants.
52
+ styling descendants. The `square` shape deliberately overrides the radius
53
+ property with zero; use `rounded` when customizing the radius token.
@@ -23,7 +23,7 @@ public-signature artifact.
23
23
  | `KUI-T008` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code. |
24
24
  | `KUI-T009` | A literal `TabScaffold.active` id belongs to its literal `tabs`. |
25
25
  | `KUI-T010` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback. |
26
- | `KUI-T011` | Finite public variants have named exported union types, including tab activation, banner urgency, and all ToolbarControlGroup presentation axes. |
26
+ | `KUI-T011` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all ToolbarControlGroup presentation axes. |
27
27
 
28
28
  Literal identity inference is intentionally additive for dynamic applications.
29
29
  When choices or ids arrive at runtime and the state signal is correctly typed as
@@ -48,7 +48,7 @@ ordinary source/type gates:
48
48
  `SegmentedControl`, `TokenSearchField`, `PanelHeader`, and `ValueTable`;
49
49
  - finite variants exported or represented as literal props on `DisclosureArrow`,
50
50
  `FloatingToolbar`, `Pane`, `ResizableRegion`, `SegmentedControl`, `StateBanner`,
51
- `ToolbarText`, `CollapsiblePanel`, `Catalog`, and the wire helpers;
51
+ `SunkenPanel`, `ToolbarText`, `CollapsiblePanel`, `Catalog`, and the wire helpers;
52
52
  - required controlled-state callbacks on `wireResizableRegions`, `wireTabBars`,
53
53
  and `wireTabScaffold`;
54
54
  - mutually exclusive count/badge metadata on `ListHeader`; and
@@ -58,7 +58,7 @@ ordinary source/type gates:
58
58
 
59
59
  The remaining components (`LucideIcon`, `DisclosureArrow`, `Toolbar`,
60
60
  `ToolbarControlGroup`, `ListInsetControl`, `ListInsetText`, `LoadingSpinner`,
61
- `Skeleton`, `SunkenPanel`, `EmptyState`, `StateBanner`, `ValueTableRow`, and the
61
+ `Skeleton`, `EmptyState`, `StateBanner`, `ValueTableRow`, and the
62
62
  pure helpers) have independent props with no further conditional invalid state
63
63
  that TypeScript can truthfully remove. Optional labels on decorative icons and
64
64
  unnamed non-landmark surfaces are intentional variants, not missing contracts.
@@ -94,6 +94,9 @@ move errors into casts without making the integration safer.
94
94
  wanted.
95
95
  - Remove orphaned `summaryId`, or render the matching `summary`; add
96
96
  `wrap: true` anywhere `ToolbarText.maxLines` is used.
97
+ - Replace `Toolbar({ divider: false })` with `dividerSides: ''`; the default
98
+ remains a bottom divider, while canonical combinations such as `tr` and
99
+ `trbl` select more physical edges.
97
100
 
98
101
  JavaScript runtime behavior remains defensive for previously emitted calls, but
99
102
  new TypeScript builds report these invalid or ignored combinations.
@@ -28,6 +28,11 @@ its reachable project-local styles into the report, including quoted or
28
28
  unquoted `url()` imports; external package styles remain outside the consumer
29
29
  boundary.
30
30
 
31
+ Recursive discovery excludes generated and tool-owned trees, including any
32
+ nested `.claude/worktrees` checkout. Those checkouts are separate repositories,
33
+ not application source, and cannot contribute files, diagnostics, profile
34
+ policy, or analysis inputs to the containing application.
35
+
31
36
  ## Diagnostics and exit behavior
32
37
 
33
38
  | Rule | Kind | Meaning |
package/docs/ui-doctor.md CHANGED
@@ -15,7 +15,7 @@ The default terminal output is short and repair-oriented. `--format json` emits
15
15
 
16
16
  The catalog stage discovers the package-default application UI profile and workspace/root-to-leaf directory layers. It validates referenced selection/composition catalogs and, when a workspace declares `package.json#kerfComponentCatalog`, invokes the installed `create-kerf-component` catalog checker to validate metadata, source exports, schema conformance, and generated-output drift. This checker reads source text; it does not import application modules.
17
17
 
18
- TypeScript uses the compiler API with `noEmit`. ESLint loads the installed `eslint-plugin-kerfjs` `recommended-ui` preset (`--eslint strict-ui` opts into advisory rules as errors). Stable KUI identifiers come from the packaged `application-ui-diagnostic-ids-v1.json` registry; the doctor also reads installed ESLint message metadata for semantic conflict detection instead of duplicating rule definitions. The analyzer calls the public `@kerfjs/ui/analyzer` contract. None of these stages executes generated application code.
18
+ TypeScript uses the compiler API with `noEmit`. ESLint loads the installed `eslint-plugin-kerfjs` `recommended-ui` preset (`--eslint strict-ui` opts into advisory rules as errors). This is deliberately an isolated Kerf lint pass rather than the consumer's complete ESLint configuration. Inline directives for consumer-owned plugins, such as `@typescript-eslint`, therefore do not produce false "rule definition not found" diagnostics; the consumer's normal ESLint command remains authoritative for those rules. Unknown `kerfjs/*` directives still fail the doctor pass. Stable KUI identifiers come from the packaged `application-ui-diagnostic-ids-v1.json` registry; the doctor also reads installed ESLint message metadata for semantic conflict detection instead of duplicating rule definitions. The analyzer calls the public `@kerfjs/ui/analyzer` contract. None of these stages executes generated application code.
19
19
 
20
20
  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.
21
21
 
@@ -25,6 +25,13 @@ An unavailable or failed stage does not prevent independent stages from reportin
25
25
 
26
26
  `--full` is the default and analyzes the selected package. `--changed` reads tracked and untracked paths from Git unless one or more `--path` values are supplied. An empty changed set is a configuration error instead of a false-clean success. With `--package`, workspace-relative Git paths are converted to package-relative paths before TypeScript, ESLint, and analyzer selection; paths outside the selected package are ignored.
27
27
 
28
+ Full traversal treats generated/tool-owned directories (`dist`, `coverage`,
29
+ `node_modules`, `.git`, `.kerf-cache`, `kerf-ui-evidence`, and nested
30
+ `.claude/worktrees` checkouts) as outside application source. Their files are
31
+ excluded consistently from TypeScript, the isolated ESLint pass, analyzer
32
+ discovery, and cache inputs, even when one lives below another application
33
+ directory.
34
+
28
35
  TypeScript constructs the selected package's program so compiler options retain their real meaning, while its root inputs are narrowed to changed source files. Use `--full` for release gates.
29
36
 
30
37
  ## Configuration and suppressions
package/docs/ux-demo.md CHANGED
@@ -10,7 +10,7 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
10
10
  - The `ListActionRow` route demonstrates sibling primary/trailing native controls, independent disabled states, controlled selection, first-line leading-icon alignment for wrapped labels, and an application-owned native popover relationship without nesting controls. The `ListItem` route demonstrates the same multiline leading-icon anchor alongside its single-control navigation contract.
11
11
  - The `AppTab` route demonstrates a runtime-filtered domain metadata slot and a decorative replacement close icon without changing TabBar selection, close, reorder, or focus ownership. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
12
12
  - Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, feedback, and theme scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component.
13
- - `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, recommendation, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Non-supported Web Awesome recommendations project to a visible `Discouraged` sidebar tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<browser-subpath>.tsx` from their canonical `delivery.browserImport`.
13
+ - `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, recommendation, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Non-supported Web Awesome recommendations project to a visible `Discouraged` sidebar tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<public-subpath>.tsx` from their canonical `delivery.browserImport` or opt-in `delivery.moduleImport`.
14
14
  - Every detail footer groups compact `Demo source` and `Guidance` actions; first-party component details also expose `Component source`. Web Awesome details say `Integration guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
15
15
  - The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. Catalog entries can supply short reusable trailing tags; the Web Awesome projection uses them to label exactly the 15 discouraged choices. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
16
16
  - The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with the subtitle in its own aligned row like `PanelHeader`; the current-view heading is also a toolbar. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The checkerboard belongs to the complete scrollable preview region rather than a nested card. A lightly separated footer keeps live status above compact source, guidance, and relationship groups. The focused `ResizableRegion` specimen stretches its surface through the available stage height and reports committed width in that shared status footer; narrow layouts retain local horizontal access when the pane is resized wider than the stage, and the split remains readable at 200% root scaling. Geometry coverage checks wide, intermediate, narrow, and 200% zoom layouts.
@@ -21,7 +21,7 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
21
21
  - The `DisclosureArrow` route renders the production 18px root-scaled default without a catalog-only size override. Its two native buttons independently control the default right-to-down chevron and a recognizable right-facing replacement arrow with left-to-up custom directions; both use shortest-path rotation and their names stay stable while `aria-expanded` communicates state. Browser coverage verifies the custom clockwise path, pointer and native keyboard activation, focus, narrow and 200%-zoom stacking with contained labels, consumer sizing, and the independent `.5` Web Awesome glyph scale retained by Kerf `Select`.
22
22
  - A detail with relationships renders one grouped footer selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
23
23
  - Demo-only CSS supplies only the catalog shell and stages; component appearance stays in package CSS. Decorative chrome and Web Awesome controls share the same semantic system palette instead of carrying a separate demo identity.
24
- - Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; ListItem and ListHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; PanelHeader demonstrates its plain top-toolbar identity, extra-large title, optional subtitle row, and bordered brand-filled icon group; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
24
+ - Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; ListItem and ListHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; PanelHeader demonstrates its plain top-toolbar identity, extra-large title, optional subtitle row, and bordered brand-filled icon group; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone with tone-tinted badges plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
25
25
  - Recipe ownership notes are hidden by default. A page-toolbar toggle reveals them as floating warning-toned notes with a folded corner, keeping instructional copy visually distinct and removable from the specimen flow.
26
26
  - Settings exercise light/dark, increased contrast, and reduced motion without changing component code. The theme action starts from the operating system's effective color scheme, always names the appearance it will switch to, and installs an explicit light or dark override when activated; contrast and reduced motion expose their independent preferences through `aria-pressed`.
27
27
  - The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
package/docs/workbench.md CHANGED
@@ -49,6 +49,19 @@ Each `WorkbenchPanel` takes `content`, an optional `collapsed`, an optional
49
49
  `--kui-workbench-rail-width` 280px, `--kui-workbench-drawer-height` 220px), and
50
50
  an optional `label`.
51
51
 
52
+ ## Public styling boundary
53
+
54
+ Import `@kerfjs/ui/workbench.css` after the component subpath. Applications may
55
+ set `--kui-workbench-rail-width` and `--kui-workbench-drawer-height` on a
56
+ Workbench instance. The supported composition classes are `.kui-workbench`,
57
+ `.kui-workbench__rail`, `.kui-workbench__rail--left`,
58
+ `.kui-workbench__rail--right`, `.kui-workbench__center`,
59
+ `.kui-workbench__main`, `.kui-workbench__drawer`, and
60
+ `.kui-workbench__panel-content`; these exact hooks are cataloged for tools that
61
+ must classify public application selectors. Prefer the component props and two
62
+ size tokens before selecting internal anatomy, and do not target its data
63
+ attributes or descendant tags as styling contracts.
64
+
52
65
  ## How the collapse animates
53
66
 
54
67
  Collapsing snaps the panel's flex track to zero in a single reflow (so the work
@@ -0,0 +1,8 @@
1
+ export interface EslintDiagnosticLike {
2
+ ruleId?: string | null;
3
+ message: string;
4
+ }
5
+
6
+ export function isForeignRuleDefinitionDiagnostic(
7
+ item: EslintDiagnosticLike,
8
+ ): boolean;
@@ -0,0 +1,7 @@
1
+ export function isForeignRuleDefinitionDiagnostic(item) {
2
+ return (
3
+ typeof item.ruleId === 'string' &&
4
+ !item.ruleId.startsWith('kerfjs/') &&
5
+ item.message === `Definition for rule '${item.ruleId}' was not found.`
6
+ );
7
+ }
package/doctor/index.mjs CHANGED
@@ -23,6 +23,11 @@ import { createRequire } from 'node:module';
23
23
  import { loadApplicationUiProfile } from '../ai/application-ui-profile.mjs';
24
24
  import { analyzeUiProject, UI_ANALYSIS_RULES } from '../analyzer/index.mjs';
25
25
  import { evaluateUi, UI_EVALUATION_RULES } from '../evaluator/index.mjs';
26
+ import { isForeignRuleDefinitionDiagnostic } from './eslint-diagnostics.mjs';
27
+ import {
28
+ isUiTraversalExcluded,
29
+ UI_TRAVERSAL_ESLINT_IGNORES,
30
+ } from '../traversal-exclusions.mjs';
26
31
 
27
32
  export const UI_DOCTOR_SCHEMA_VERSION = 1;
28
33
  export const UI_DOCTOR_EXIT = Object.freeze({
@@ -54,14 +59,6 @@ const sourceExtensions = new Set([
54
59
  '.cts',
55
60
  '.css',
56
61
  ]);
57
- const ignoredDirectories = new Set([
58
- '.git',
59
- '.kerf-cache',
60
- 'coverage',
61
- 'dist',
62
- 'kerf-ui-evidence',
63
- 'node_modules',
64
- ]);
65
62
  const defaultStages = Object.freeze({
66
63
  catalog: true,
67
64
  typescript: true,
@@ -361,6 +358,7 @@ export async function resolveUiDoctorPackage(root, selector) {
361
358
  async function collectInputs(root, paths) {
362
359
  const result = [];
363
360
  const visit = async (path) => {
361
+ if (isUiTraversalExcluded(root, path)) return;
364
362
  let details;
365
363
  try {
366
364
  details = await stat(path);
@@ -381,7 +379,6 @@ async function collectInputs(root, paths) {
381
379
  return;
382
380
  }
383
381
  for (const entry of await readdir(path, { withFileTypes: true })) {
384
- if (entry.isDirectory() && ignoredDirectories.has(entry.name)) continue;
385
382
  await visit(resolve(path, entry.name));
386
383
  }
387
384
  };
@@ -777,6 +774,9 @@ async function runTypeScript({ packageRoot, paths }) {
777
774
  { ...ts.default.sys, onUnRecoverableConfigFileDiagnostic: () => {} },
778
775
  );
779
776
  if (!parsed) throw new Error('TypeScript could not parse tsconfig.json.');
777
+ parsed.fileNames = parsed.fileNames.filter(
778
+ (file) => !isUiTraversalExcluded(packageRoot, file),
779
+ );
780
780
  if (paths?.length) {
781
781
  const selected = new Set(paths.map((path) => resolve(packageRoot, path)));
782
782
  parsed.fileNames = parsed.fileNames.filter((file) => selected.has(file));
@@ -850,7 +850,7 @@ async function runEslint({
850
850
  },
851
851
  },
852
852
  };
853
- const configs = [base];
853
+ const configs = [{ ignores: UI_TRAVERSAL_ESLINT_IGNORES }, base];
854
854
  if (usesTypeScript) {
855
855
  const parserModule = await importFrom(
856
856
  packageRoot,
@@ -879,6 +879,7 @@ async function runEslint({
879
879
  const diagnostics = [];
880
880
  for (const result of results)
881
881
  for (const item of result.messages) {
882
+ if (isForeignRuleDefinitionDiagnostic(item)) continue;
882
883
  const code = item.message.match(/\bKUI-[A-Z]\d{3}\b/)?.[0];
883
884
  diagnostics.push(
884
885
  normalizedDiagnostic({
package/llms.txt CHANGED
@@ -52,7 +52,9 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
52
52
  6. **Before calling an integration complete**, run
53
53
  [`kerf-ui-doctor`](./docs/ui-doctor.md). Its portable JSON report merges
54
54
  profile/catalog, TypeScript, Kerf ESLint, and static analyzer diagnostics;
55
- repair source-located errors until exit 0. Use full mode for release gates.
55
+ repair source-located errors until exit 0. Use full mode for release gates;
56
+ generated `dist`/`coverage`, dependency, cache, evidence, and nested-worktree
57
+ directories are excluded consistently from every static stage.
56
58
  [`kerf-ui-analyze`](./docs/ui-analyzer.md) remains available when only static
57
59
  layout diagnostics are wanted; its review findings are a bounded human/AI
58
60
  review queue rather than an objective aesthetic score.
@@ -76,6 +78,10 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
76
78
  own the geometry (8px inline margin + 1px border + 8px padding). Do not add a
77
79
  container padding on top of a content-item's own margin — pick one owner. Use
78
80
  `.kui-content` for 24px major vertical rhythm.
81
+ - **Use `List` for vertical layout.** It supplies a stretch-aligned column plus
82
+ optional standard/custom `gap`, `flex`, vertical scrolling, and canonical
83
+ `dividerSides` (`t`/`r`/`b`/`l` order). It adds no list semantics; children own
84
+ those, and only one bounded region should own scrolling.
79
85
  - **Keep Web Awesome on the shared inset tiers.** `webawesome.css` uses
80
86
  `--kui-wa-control-inset` (8px controls/items) and
81
87
  `--kui-wa-surface-margin` / `--kui-wa-surface-inset` (8px around/inside
@@ -127,9 +133,11 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
127
133
  navigation stacks, tab selection and order, pane sizes, and `collapsed`/`expanded`
128
134
  disclosure flags in the app — they are data or persisted layout, and the app reads
129
135
  them to render. The `wire…` helpers own the _ephemeral_ parts: `wireNavStack` the
130
- push/pop animation, `wireTabBars` the overflow autoscroll and drag preview,
136
+ push/pop animation, `wireTabBars` the overflow autoscroll, drag preview, and
137
+ replacement focus after controlled automatic tab activation,
131
138
  `wireResizableRegions` the live drag preview; they report committed changes via
132
- callbacks. The one richer case is `wireTokenSearchFields`, which manages the
139
+ callbacks. Managed TokenSearchField clear also preserves open state and replacement
140
+ editor focus for continued typing. The one richer case is `wireTokenSearchFields`, which manages the
133
141
  collapsible field's expand/collapse/focus by default (on, per-behavior opt-out) and
134
142
  exposes the `expanded` signal on its handle — bind it in render or hand in your own
135
143
  via `collapsible.signals`. Do not hand-roll open/collapse/focus that helper
@@ -150,6 +158,10 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
150
158
  markup. Use the standalone `Skeleton` for a custom slot. Prefer it over `wa-skeleton`
151
159
  (the pure-Kerf primitives avoid Web Awesome), and never animate it — use
152
160
  `LoadingSpinner` for known busy activity instead.
161
+ - **Use a StateBanner badge only for compact supporting status.** Pass the
162
+ optional string `badge` for a terse count or status beside the title. It
163
+ inherits the banner tone and is announced with the banner, so keep its text
164
+ meaningful and never rely on color alone.
153
165
 
154
166
  ## Public exports
155
167
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerfjs/ui",
3
- "version": "5.0.0-beta.22",
3
+ "version": "5.0.0-beta.24",
4
4
  "description": "Accessible, composable UI primitives for kerf applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -74,6 +74,11 @@
74
74
  "browser": "./dist/browser/toolbar-text.js",
75
75
  "import": "./dist/toolbar-text.js"
76
76
  },
77
+ "./list": {
78
+ "types": "./dist/list.d.ts",
79
+ "browser": "./dist/browser/list.js",
80
+ "import": "./dist/list.js"
81
+ },
77
82
  "./list-action-row": {
78
83
  "types": "./dist/list-action-row.d.ts",
79
84
  "browser": "./dist/browser/list-action-row.js",
@@ -267,6 +272,7 @@
267
272
  "./toolbar-control-group.css": "./dist/styles/toolbar-control-group.css",
268
273
  "./floating-toolbar.css": "./dist/styles/floating-toolbar.css",
269
274
  "./toolbar-text.css": "./dist/styles/toolbar-text.css",
275
+ "./list.css": "./dist/styles/list.css",
270
276
  "./list-action-row.css": "./dist/styles/list-action-row.css",
271
277
  "./list-item.css": "./dist/styles/list-item.css",
272
278
  "./list-header.css": "./dist/styles/list-header.css",
@@ -301,6 +307,7 @@
301
307
  "analyzer",
302
308
  "doctor",
303
309
  "evaluator",
310
+ "traversal-exclusions.mjs",
304
311
  "docs",
305
312
  "ux-demo/recipes",
306
313
  "llms.txt",
@@ -376,7 +383,7 @@
376
383
  "@typescript-eslint/eslint-plugin": "^8.65.0",
377
384
  "@typescript-eslint/parser": "^8.65.0",
378
385
  "@vitest/coverage-v8": "4.1.11",
379
- "domotion-svg": "^0.29.0",
386
+ "domotion-svg": "^0.30.1",
380
387
  "esbuild": "^0.25.0",
381
388
  "eslint": "^10.8.0",
382
389
  "eslint-plugin-simple-import-sort": "^12.1.1",
@@ -0,0 +1,27 @@
1
+ import { relative, sep } from 'node:path';
2
+
3
+ export const UI_TRAVERSAL_IGNORED_DIRECTORY_NAMES = Object.freeze([
4
+ '.git',
5
+ '.kerf-cache',
6
+ 'coverage',
7
+ 'dist',
8
+ 'kerf-ui-evidence',
9
+ 'node_modules',
10
+ ]);
11
+ const ignoredDirectoryNames = new Set(UI_TRAVERSAL_IGNORED_DIRECTORY_NAMES);
12
+
13
+ export const UI_TRAVERSAL_ESLINT_IGNORES = Object.freeze([
14
+ ...UI_TRAVERSAL_IGNORED_DIRECTORY_NAMES.map((name) => `**/${name}/**`),
15
+ '**/.claude/worktrees/**',
16
+ ]);
17
+
18
+ export function isUiTraversalExcluded(root, candidate) {
19
+ const segments = relative(root, candidate).split(sep);
20
+ return (
21
+ segments.some((segment) => ignoredDirectoryNames.has(segment)) ||
22
+ segments.some(
23
+ (segment, index) =>
24
+ segment === '.claude' && segments[index + 1] === 'worktrees',
25
+ )
26
+ );
27
+ }
@@ -160,7 +160,7 @@ export const createRecipe: RecipeFactory = (announce) => {
160
160
  >
161
161
  <Toolbar
162
162
  label="Activity"
163
- divider
163
+ dividerSides="b"
164
164
  leading={
165
165
  <ToolbarControlGroup appearance="borderless" single>
166
166
  <ToolbarText text="Activity" size="small" />
@@ -92,7 +92,7 @@ export const createRecipe: RecipeFactory = (announce) => {
92
92
  <footer class="recipe-sidebar__footer kui-pane__footer">
93
93
  <Toolbar
94
94
  label="Sidebar actions"
95
- divider={false}
95
+ dividerSides=""
96
96
  leading={
97
97
  <ToolbarControlGroup appearance="borderless" single>
98
98
  <button
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/toolbar.tsx"],"names":[],"mappings":";;;AAWO,SAAS,OAAA,CAAQ;AAAA,EACtB,OAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA,GAAU,IAAA;AAAA,EACV,SAAA,GAAY;AACd,CAAA,EAAiB;AACf,EAAA,uBACE,IAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,YAAA,EAAe,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACvC,gBAAA,EAAe,SAAA;AAAA,MACf,cAAA,EAAc,OAAO,OAAO,CAAA;AAAA,MAC5B,iBAAA,EAAiB,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,MACvC,YAAA,EAAY,KAAA;AAAA,MAEZ,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,sBAAA,EAAwB,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,wBAC3C,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,qBAAA,EAAuB,QAAA,EAAA,MAAA,EAAO,CAAA;AAAA,wBACzC,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,uBAAA,EAAyB,QAAA,EAAA,QAAA,EAAS;AAAA;AAAA;AAAA,GAC/C;AAEJ","file":"chunk-7L6EHUMV.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nexport interface ToolbarProps {\n leading?: SafeHtml;\n center?: SafeHtml;\n trailing?: SafeHtml;\n label?: string;\n divider?: boolean;\n className?: string;\n}\n\nexport function Toolbar({\n leading,\n center,\n trailing,\n label,\n divider = true,\n className = '',\n}: ToolbarProps) {\n return (\n <header\n class={`kui-toolbar ${className}`.trim()}\n data-component=\"toolbar\"\n data-divider={String(divider)}\n data-has-center={String(Boolean(center))}\n aria-label={label}\n >\n <div class=\"kui-toolbar__leading\">{leading}</div>\n <div class=\"kui-toolbar__center\">{center}</div>\n <div class=\"kui-toolbar__trailing\">{trailing}</div>\n </header>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/wire-tab-bars.ts"],"names":[],"mappings":"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","file":"chunk-AASLYRGE.js","sourcesContent":["import type { TabActivation } from './tab-bar.js';\n\nexport type TabReorderSource = 'pointer' | 'keyboard';\nexport type TabDropPosition = 'before' | 'after';\n\nexport interface TabReorder {\n barId: string;\n sourceId: string;\n targetId: string;\n position: TabDropPosition;\n source: TabReorderSource;\n}\n\nexport type { TabActivation } from './tab-bar.js';\n\nexport interface WireTabBarsOptions {\n onReorder: (change: TabReorder) => void;\n /**\n * How arrow / Home / End keys activate tabs (default `'automatic'`):\n * - `'automatic'` moves roving focus **and** selects the focused tab (clicks it).\n * - `'manual'` moves roving focus only; the user selects with Enter / Space / click\n * (the ARIA Tabs manual-activation pattern). Use this when activation is a heavy or\n * side-effecting action (e.g. a tab that loads a project) so arrowing through the\n * strip doesn't trigger it on every tab.\n *\n * A per-bar `data-tab-activation=\"manual\" | \"automatic\"` attribute (see the `TabBar`\n * `activation` prop) overrides this option for that strip.\n */\n activation?: TabActivation;\n}\n\ntype TabRoot = HTMLElement;\ntype TabBarRoot = HTMLElement;\n\nconst AUTO_SCROLL_EDGE_PX = 56;\nconst AUTO_SCROLL_MIN_PX_PER_SECOND = 180;\nconst AUTO_SCROLL_MAX_PX_PER_SECOND = 900;\n\nexport function reorderTabs<T>(\n items: readonly T[],\n getId: (item: T) => string,\n sourceId: string,\n targetId: string,\n position: TabDropPosition,\n): T[] {\n if (sourceId === targetId) return [...items];\n const source = items.find((item) => getId(item) === sourceId);\n if (!source || !items.some((item) => getId(item) === targetId))\n return [...items];\n const remaining = items.filter((item) => getId(item) !== sourceId);\n const targetIndex = remaining.findIndex((item) => getId(item) === targetId);\n remaining.splice(targetIndex + (position === 'after' ? 1 : 0), 0, source);\n return remaining;\n}\n\nfunction tabRoot(target: EventTarget | null): TabRoot | undefined {\n return target instanceof Element\n ? (target.closest<TabRoot>('[data-component=\"app-tab\"]') ?? undefined)\n : undefined;\n}\n\nfunction tabBar(tab: TabRoot): TabBarRoot | undefined {\n return tab.closest<TabBarRoot>('[data-component=\"tab-bar\"]') ?? undefined;\n}\n\nfunction tabId(tab: TabRoot): string | undefined {\n return tab.dataset.tabId || undefined;\n}\n\nfunction barId(bar: TabBarRoot): string | undefined {\n return bar.dataset.tabBarId || undefined;\n}\n\nfunction positionFor(event: DragEvent, tab: TabRoot): TabDropPosition {\n const bounds = tab.getBoundingClientRect();\n return event.clientX < bounds.left + bounds.width / 2 ? 'before' : 'after';\n}\n\nfunction tabsIn(bar: TabBarRoot): HTMLButtonElement[] {\n return [\n ...bar.querySelectorAll<HTMLButtonElement>(\n '[data-kui-tab-list] [role=\"tab\"]',\n ),\n ];\n}\n\nfunction reveal(tab: HTMLElement | null | undefined): void {\n tab?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n}\n\n/** Wire reordering and keyboard navigation while leaving controlled state in the application. */\nexport function wireTabBars(\n root: HTMLElement | Document,\n { onReorder, activation = 'automatic' }: WireTabBarsOptions,\n): () => void {\n const ownerDocument = (\n root.nodeType === 9 ? (root as Document) : root.ownerDocument\n )!;\n const view = ownerDocument.defaultView!;\n let autoScroll:\n | {\n strip: HTMLElement;\n velocity: number;\n frame: number | undefined;\n previousTime: number | undefined;\n }\n | undefined;\n const stopAutoScroll = () => {\n if (!autoScroll) return;\n if (autoScroll.frame !== undefined)\n view.cancelAnimationFrame(autoScroll.frame);\n delete autoScroll.strip.dataset.tabAutoscroll;\n autoScroll = undefined;\n };\n const runAutoScroll = (time: number) => {\n const current = autoScroll;\n if (!current) return;\n current.frame = undefined;\n const elapsed =\n current.previousTime === undefined\n ? 1000 / 60\n : Math.min(40, Math.max(1, time - current.previousTime));\n current.previousTime = time;\n const maximum = Math.max(\n 0,\n current.strip.scrollWidth - current.strip.clientWidth,\n );\n const previous = current.strip.scrollLeft;\n current.strip.scrollLeft = Math.max(\n 0,\n Math.min(maximum, previous + (current.velocity * elapsed) / 1000),\n );\n if (current.strip.scrollLeft === previous) {\n stopAutoScroll();\n return;\n }\n current.frame = view.requestAnimationFrame(runAutoScroll);\n };\n const updateAutoScroll = (event: DragEvent, strip: HTMLElement) => {\n if (strip.scrollWidth <= strip.clientWidth) {\n stopAutoScroll();\n return;\n }\n const bounds = strip.getBoundingClientRect();\n const edge = Math.min(AUTO_SCROLL_EDGE_PX, bounds.width / 3);\n const startDistance = event.clientX - bounds.left;\n const endDistance = bounds.right - event.clientX;\n let direction = 0;\n let strength = 0;\n if (startDistance >= 0 && startDistance < edge) {\n direction = -1;\n strength = 1 - startDistance / edge;\n } else if (endDistance >= 0 && endDistance < edge) {\n direction = 1;\n strength = 1 - endDistance / edge;\n }\n if (direction === 0) {\n stopAutoScroll();\n return;\n }\n const maximum = Math.max(0, strip.scrollWidth - strip.clientWidth);\n if (\n (direction < 0 && strip.scrollLeft <= 0) ||\n (direction > 0 && strip.scrollLeft >= maximum)\n ) {\n stopAutoScroll();\n return;\n }\n const speed =\n AUTO_SCROLL_MIN_PX_PER_SECOND +\n (AUTO_SCROLL_MAX_PX_PER_SECOND - AUTO_SCROLL_MIN_PX_PER_SECOND) *\n strength *\n strength;\n if (autoScroll?.strip !== strip) stopAutoScroll();\n autoScroll ??= {\n strip,\n velocity: 0,\n frame: undefined,\n previousTime: undefined,\n };\n autoScroll.velocity = direction * speed;\n strip.dataset.tabAutoscroll = direction < 0 ? 'start' : 'end';\n autoScroll.frame ??= view.requestAnimationFrame(runAutoScroll);\n };\n let dragged: { barId: string; tabId: string } | undefined;\n const clearDropPositions = () =>\n root\n .querySelectorAll<HTMLElement>('[data-tab-drop-position]')\n .forEach((tab) => delete tab.dataset.tabDropPosition);\n const clear = () => {\n stopAutoScroll();\n dragged = undefined;\n root\n .querySelectorAll<HTMLElement>(\n '[data-tab-dragging], [data-tab-drop-position]',\n )\n .forEach((tab) => {\n delete tab.dataset.tabDragging;\n delete tab.dataset.tabDropPosition;\n });\n };\n const afterControlledRender = (sourceBarId: string, sourceTabId: string) =>\n globalThis.queueMicrotask(() => {\n const tab = [\n ...root.querySelectorAll<TabRoot>('[data-component=\"app-tab\"]'),\n ].find((candidate) => {\n const candidateBar = tabBar(candidate);\n return (\n tabId(candidate) === sourceTabId &&\n candidateBar !== undefined &&\n barId(candidateBar) === sourceBarId\n );\n });\n const button = tab?.querySelector<HTMLButtonElement>('[role=\"tab\"]');\n button?.focus();\n reveal(button);\n });\n\n const onDragStart = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar = tab && tabBar(tab);\n const sourceId = tab && tabId(tab);\n const sourceBarId = bar && barId(bar);\n if (\n !tab ||\n tab.getAttribute('draggable') !== 'true' ||\n !sourceId ||\n !sourceBarId\n )\n return;\n dragged = { barId: sourceBarId, tabId: sourceId };\n tab.dataset.tabDragging = 'true';\n if (dragEvent.dataTransfer) {\n dragEvent.dataTransfer.effectAllowed = 'move';\n dragEvent.dataTransfer.setData(\n 'application/x-kerf-tab',\n `${sourceBarId}:${sourceId}`,\n );\n }\n };\n const onDragOver = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar =\n event.target instanceof Element\n ? (event.target.closest<TabBarRoot>('[data-component=\"tab-bar\"]') ??\n undefined)\n : undefined;\n const targetId = tab && tabId(tab);\n const targetBarId = bar && barId(bar);\n if (!dragged || !bar || targetBarId !== dragged.barId) {\n stopAutoScroll();\n clearDropPositions();\n return;\n }\n dragEvent.preventDefault();\n const strip = bar.querySelector<HTMLElement>('[data-kui-tab-list]');\n if (strip) updateAutoScroll(dragEvent, strip);\n else stopAutoScroll();\n clearDropPositions();\n if (!tab || !targetId || targetId === dragged.tabId) return;\n tab.dataset.tabDropPosition = positionFor(dragEvent, tab);\n if (dragEvent.dataTransfer) dragEvent.dataTransfer.dropEffect = 'move';\n };\n const onDrop = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar = tab && tabBar(tab);\n const targetId = tab && tabId(tab);\n const targetBarId = bar && barId(bar);\n if (\n !dragged ||\n !tab ||\n !targetId ||\n targetId === dragged.tabId ||\n targetBarId !== dragged.barId\n ) {\n clear();\n return;\n }\n dragEvent.preventDefault();\n const change: TabReorder = {\n barId: dragged.barId,\n sourceId: dragged.tabId,\n targetId,\n position: positionFor(dragEvent, tab),\n source: 'pointer',\n };\n clear();\n onReorder(change);\n afterControlledRender(change.barId, change.sourceId);\n };\n const onKeyDown = (event: Event) => {\n const keyboardEvent = event as KeyboardEvent;\n const button =\n event.target instanceof Element\n ? event.target.closest<HTMLButtonElement>('[role=\"tab\"]')\n : null;\n const tab = button && tabRoot(button);\n const bar = tab && tabBar(tab);\n if (!button || !tab || !bar) return;\n if (keyboardEvent.key === 'Delete' || keyboardEvent.key === 'Backspace') {\n const close = tab.querySelector<HTMLButtonElement>('.kui-app-tab__close');\n if (!close) return;\n keyboardEvent.preventDefault();\n close.click();\n return;\n }\n const tabs = tabsIn(bar);\n const current = tabs.indexOf(button);\n if (current < 0) return;\n if (\n keyboardEvent.altKey &&\n keyboardEvent.shiftKey &&\n (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowRight')\n ) {\n const target =\n tabs[current + (keyboardEvent.key === 'ArrowLeft' ? -1 : 1)];\n const sourceId = tabId(tab);\n const targetRoot = target && tabRoot(target);\n const targetId = targetRoot && tabId(targetRoot);\n const sourceBarId = barId(bar);\n if (\n !target ||\n !sourceId ||\n !targetId ||\n !sourceBarId ||\n tab.getAttribute('draggable') !== 'true'\n )\n return;\n keyboardEvent.preventDefault();\n onReorder({\n barId: sourceBarId,\n sourceId,\n targetId,\n position: keyboardEvent.key === 'ArrowLeft' ? 'before' : 'after',\n source: 'keyboard',\n });\n afterControlledRender(sourceBarId, sourceId);\n return;\n }\n let next: number | undefined;\n if (keyboardEvent.key === 'ArrowRight') next = (current + 1) % tabs.length;\n else if (keyboardEvent.key === 'ArrowLeft')\n next = (current - 1 + tabs.length) % tabs.length;\n else if (keyboardEvent.key === 'Home') next = 0;\n else if (keyboardEvent.key === 'End') next = tabs.length - 1;\n if (next === undefined) return;\n keyboardEvent.preventDefault();\n // A per-bar data-tab-activation attribute overrides the wireTabBars option.\n const perBar = bar.dataset.tabActivation;\n const mode: TabActivation =\n perBar === 'manual' || perBar === 'automatic' ? perBar : activation;\n tabs[next]?.focus();\n // Manual activation moves roving focus only; the user selects with Enter / Space\n // (native on the tab <button>) or click. Automatic also selects the focused tab.\n if (mode === 'automatic') tabs[next]?.click();\n reveal(tabs[next]);\n };\n const onFocusIn = (event: Event) => {\n const tab =\n event.target instanceof HTMLElement &&\n event.target.matches('[role=\"tab\"]')\n ? event.target\n : undefined;\n reveal(tab);\n };\n\n root.addEventListener('dragstart', onDragStart);\n root.addEventListener('dragover', onDragOver);\n root.addEventListener('drop', onDrop);\n root.addEventListener('dragend', clear);\n root.addEventListener('keydown', onKeyDown);\n root.addEventListener('focusin', onFocusIn);\n root\n .querySelectorAll<HTMLElement>(\n '[data-kui-tab-list] [role=\"tab\"][aria-selected=\"true\"]',\n )\n .forEach(reveal);\n\n return () => {\n clear();\n root.removeEventListener('dragstart', onDragStart);\n root.removeEventListener('dragover', onDragOver);\n root.removeEventListener('drop', onDrop);\n root.removeEventListener('dragend', clear);\n root.removeEventListener('keydown', onKeyDown);\n root.removeEventListener('focusin', onFocusIn);\n };\n}\n"]}