@kerfjs/ui 5.0.0-beta.56 → 5.0.0-beta.57

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 (282) hide show
  1. package/README.md +63 -32
  2. package/ai/application-ui-diagnostic-ids-v1.json +9 -1
  3. package/ai/application-ui-profile-sync.cjs +1 -1
  4. package/ai/application-ui-profile.defaults.json +63 -8
  5. package/ai/application-ui-profile.mjs +1 -1
  6. package/ai/component-catalog.json +208 -32
  7. package/ai/component-catalog.schema.json +2 -0
  8. package/ai/component-class-owners.cjs +83 -0
  9. package/ai/component-composition-extension.d.ts +7 -0
  10. package/ai/component-composition-extension.schema.json +7 -0
  11. package/ai/{component-catalog-v2-overrides.json → component-composition-rules.json} +2 -1
  12. package/ai/{component-catalog-v2.d.ts → component-composition.d.ts} +9 -5
  13. package/ai/{component-catalog-v2.json → component-composition.json} +155 -24
  14. package/ai/{component-catalog-v2.schema.json → component-composition.schema.json} +21 -5
  15. package/ai/components/README.md +170 -0
  16. package/ai/components/application-tabs.md +65 -0
  17. package/ai/components/badge.md +72 -0
  18. package/ai/components/collapsible-panel.md +102 -0
  19. package/ai/components/content-item.md +90 -0
  20. package/ai/components/disclosure-arrow.md +77 -0
  21. package/ai/components/document-baseline.md +75 -0
  22. package/ai/components/empty-state.md +80 -0
  23. package/ai/components/feedback.md +71 -0
  24. package/ai/components/floating-toolbar.md +80 -0
  25. package/ai/components/foundation.md +65 -0
  26. package/ai/components/grid.md +93 -0
  27. package/ai/components/headers.md +63 -0
  28. package/ai/components/layout.md +75 -0
  29. package/ai/components/list-action-row.md +91 -0
  30. package/ai/components/list-header.md +89 -0
  31. package/ai/components/list-inset-control.md +74 -0
  32. package/ai/components/list-inset-text.md +72 -0
  33. package/ai/components/list-item.md +88 -0
  34. package/ai/components/list.md +103 -0
  35. package/ai/components/loading-spinner.md +72 -0
  36. package/ai/components/lucide-icon.md +66 -0
  37. package/ai/components/nav-stack.md +103 -0
  38. package/ai/components/pane.md +100 -0
  39. package/ai/components/popup-menu.md +82 -0
  40. package/ai/components/recipe-app-shell.md +57 -0
  41. package/ai/components/recipe-collapsible-sidebar.md +57 -0
  42. package/ai/components/recipe-compact-toolbar.md +57 -0
  43. package/ai/components/recipe-composer-form.md +57 -0
  44. package/ai/components/recipe-list-detail-dialog.md +57 -0
  45. package/ai/components/recipe-list-workspace-states.md +57 -0
  46. package/ai/components/recipe-loading-inspector.md +57 -0
  47. package/ai/components/recipe-navigation-sidebar.md +57 -0
  48. package/ai/components/recipe-navigation-stack.md +57 -0
  49. package/ai/components/recipe-workspace-header.md +57 -0
  50. package/ai/components/resize.md +101 -0
  51. package/ai/components/row.md +96 -0
  52. package/ai/components/segmented-control.md +82 -0
  53. package/ai/components/select.md +107 -0
  54. package/ai/components/skeleton.md +84 -0
  55. package/ai/components/spacer.md +86 -0
  56. package/ai/components/split-view.md +102 -0
  57. package/ai/components/state-banner.md +92 -0
  58. package/ai/components/sunken-panel.md +79 -0
  59. package/ai/components/surface-scaffold.md +78 -0
  60. package/ai/components/tab-bar.md +112 -0
  61. package/ai/components/tab-scaffold.md +94 -0
  62. package/ai/components/tabs.md +90 -0
  63. package/ai/components/text.md +82 -0
  64. package/ai/components/token-search-field.md +101 -0
  65. package/ai/components/toolbar-control-group.md +100 -0
  66. package/ai/components/toolbar-text.md +76 -0
  67. package/ai/components/toolbar.md +95 -0
  68. package/ai/components/value-table.md +72 -0
  69. package/ai/components/wa-accordion-item.md +55 -0
  70. package/ai/components/wa-accordion.md +65 -0
  71. package/ai/components/wa-animated-image.md +55 -0
  72. package/ai/components/wa-animation.md +55 -0
  73. package/ai/components/wa-avatar.md +55 -0
  74. package/ai/components/wa-badge.md +55 -0
  75. package/ai/components/wa-breadcrumb-item.md +53 -0
  76. package/ai/components/wa-breadcrumb.md +55 -0
  77. package/ai/components/wa-button-group.md +59 -0
  78. package/ai/components/wa-button.md +55 -0
  79. package/ai/components/wa-callout.md +58 -0
  80. package/ai/components/wa-card.md +59 -0
  81. package/ai/components/wa-carousel-item.md +53 -0
  82. package/ai/components/wa-carousel.md +55 -0
  83. package/ai/components/wa-checkbox-group.md +55 -0
  84. package/ai/components/wa-checkbox.md +55 -0
  85. package/ai/components/wa-color-picker.md +55 -0
  86. package/ai/components/wa-comparison.md +55 -0
  87. package/ai/components/wa-copy-button.md +55 -0
  88. package/ai/components/wa-details.md +61 -0
  89. package/ai/components/wa-dialog.md +59 -0
  90. package/ai/components/wa-divider.md +53 -0
  91. package/ai/components/wa-drawer.md +55 -0
  92. package/ai/components/wa-dropdown-item.md +60 -0
  93. package/ai/components/wa-dropdown.md +60 -0
  94. package/ai/components/wa-format-bytes.md +53 -0
  95. package/ai/components/wa-format-date.md +53 -0
  96. package/ai/components/wa-format-number.md +53 -0
  97. package/ai/components/wa-icon.md +58 -0
  98. package/ai/components/wa-include.md +53 -0
  99. package/ai/components/wa-input.md +59 -0
  100. package/ai/components/wa-intersection-observer.md +53 -0
  101. package/ai/components/wa-known-date.md +53 -0
  102. package/ai/components/wa-markdown.md +53 -0
  103. package/ai/components/wa-mutation-observer.md +53 -0
  104. package/ai/components/wa-number-input.md +55 -0
  105. package/ai/components/wa-option.md +59 -0
  106. package/ai/components/wa-otp-input.md +53 -0
  107. package/ai/components/wa-page.md +53 -0
  108. package/ai/components/wa-pagination.md +55 -0
  109. package/ai/components/wa-popover.md +55 -0
  110. package/ai/components/wa-popup.md +53 -0
  111. package/ai/components/wa-progress-bar.md +53 -0
  112. package/ai/components/wa-progress-ring.md +53 -0
  113. package/ai/components/wa-qr-code.md +53 -0
  114. package/ai/components/wa-radio-group.md +59 -0
  115. package/ai/components/wa-radio.md +55 -0
  116. package/ai/components/wa-random-content.md +53 -0
  117. package/ai/components/wa-rating.md +55 -0
  118. package/ai/components/wa-relative-time.md +53 -0
  119. package/ai/components/wa-resize-observer.md +53 -0
  120. package/ai/components/wa-scroller.md +53 -0
  121. package/ai/components/wa-select.md +63 -0
  122. package/ai/components/wa-skeleton.md +53 -0
  123. package/ai/components/wa-slider.md +55 -0
  124. package/ai/components/wa-spinner.md +58 -0
  125. package/ai/components/wa-split-panel.md +58 -0
  126. package/ai/components/wa-switch.md +53 -0
  127. package/ai/components/wa-tab-group.md +69 -0
  128. package/ai/components/wa-tab-panel.md +60 -0
  129. package/ai/components/wa-tab.md +59 -0
  130. package/ai/components/wa-tag.md +59 -0
  131. package/ai/components/wa-textarea.md +53 -0
  132. package/ai/components/wa-time-input.md +55 -0
  133. package/ai/components/wa-toast-item.md +60 -0
  134. package/ai/components/wa-toast.md +60 -0
  135. package/ai/components/wa-tooltip.md +55 -0
  136. package/ai/components/wa-tree-item.md +55 -0
  137. package/ai/components/wa-tree.md +55 -0
  138. package/ai/components/wa-zoomable-frame.md +55 -0
  139. package/ai/components/workbench.md +114 -0
  140. package/ai/public-api-signatures-v1.md +161 -32
  141. package/ai/skill.md +100 -48
  142. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  143. package/analyzer/component-ownership.d.mts +56 -0
  144. package/analyzer/component-ownership.mjs +184 -0
  145. package/analyzer/index.d.mts +3 -0
  146. package/analyzer/index.mjs +274 -20
  147. package/analyzer/selectors.d.mts +21 -0
  148. package/analyzer/selectors.mjs +212 -0
  149. package/dist/browser/catalog.js +25 -0
  150. package/dist/browser/collapsible-panel.js +7 -0
  151. package/dist/browser/nav-stack.js +8 -0
  152. package/dist/browser/split-view.js +10 -0
  153. package/dist/browser/tab-scaffold.js +4 -0
  154. package/dist/browser/workbench.js +10 -0
  155. package/dist/catalog-resources.d.ts +2 -2
  156. package/dist/catalog.d.ts +3 -3
  157. package/dist/catalog.js +7 -6
  158. package/dist/catalog.js.map +1 -1
  159. package/dist/{chunk-PK4N5QTO.js → chunk-553ASUK6.js} +2 -3
  160. package/dist/chunk-553ASUK6.js.map +1 -0
  161. package/dist/{chunk-G33XC2NB.js → chunk-6YFHCLHG.js} +2 -2
  162. package/dist/chunk-6YFHCLHG.js.map +1 -0
  163. package/dist/{chunk-T4UZTNZ4.js → chunk-EVYKTVBQ.js} +2 -11
  164. package/dist/chunk-EVYKTVBQ.js.map +1 -0
  165. package/dist/{chunk-AK67F6QN.js → chunk-EZVNC4J6.js} +4 -1
  166. package/dist/chunk-EZVNC4J6.js.map +1 -0
  167. package/dist/{chunk-MA6Z6EIM.js → chunk-F4QMLOXJ.js} +3 -2
  168. package/dist/chunk-F4QMLOXJ.js.map +1 -0
  169. package/dist/{chunk-QQOTQTDD.js → chunk-G2RKFVRB.js} +13 -10
  170. package/dist/chunk-G2RKFVRB.js.map +1 -0
  171. package/dist/chunk-JCV4GXWB.js +249 -0
  172. package/dist/chunk-JCV4GXWB.js.map +1 -0
  173. package/dist/chunk-K47P7QZC.js +17 -0
  174. package/dist/chunk-K47P7QZC.js.map +1 -0
  175. package/dist/{chunk-MOBRFZH3.js → chunk-OUK6QRDR.js} +2 -12
  176. package/dist/chunk-OUK6QRDR.js.map +1 -0
  177. package/dist/{chunk-O3EXIBEZ.js → chunk-PWXNJS2B.js} +7 -6
  178. package/dist/chunk-PWXNJS2B.js.map +1 -0
  179. package/dist/{chunk-VNPQDCQZ.js → chunk-QOWG4ZCL.js} +3 -2
  180. package/dist/chunk-QOWG4ZCL.js.map +1 -0
  181. package/dist/{chunk-V65EOWNF.js → chunk-XCO6YCYM.js} +5 -4
  182. package/dist/chunk-XCO6YCYM.js.map +1 -0
  183. package/dist/{chunk-K6UNVKDK.js → chunk-XDKAYSVD.js} +3 -1
  184. package/dist/chunk-XDKAYSVD.js.map +1 -0
  185. package/dist/collapsible-panel.d.ts +3 -3
  186. package/dist/collapsible-panel.js +2 -2
  187. package/dist/collapsible-panel.js.map +1 -1
  188. package/dist/index.d.ts +2 -1
  189. package/dist/index.js +10 -8
  190. package/dist/list-action-row.js +2 -1
  191. package/dist/list-header.js +1 -1
  192. package/dist/list-item.js +2 -1
  193. package/dist/nav-stack.d.ts +13 -1
  194. package/dist/nav-stack.js +1 -1
  195. package/dist/pane.d.ts +20 -3
  196. package/dist/pane.js +1 -1
  197. package/dist/{panel-toolbar-C8sF-YZC.d.ts → panel-toolbar-BN4x0rth.d.ts} +3 -2
  198. package/dist/select.js +1 -1
  199. package/dist/split-view.d.ts +3 -3
  200. package/dist/split-view.js +3 -2
  201. package/dist/split-view.js.map +1 -1
  202. package/dist/state-banner.js +1 -1
  203. package/dist/styles/app-tab.css +31 -0
  204. package/dist/styles/badge.css +19 -11
  205. package/dist/styles/collapsible-panel.css +13 -9
  206. package/dist/styles/floating-toolbar.css +25 -6
  207. package/dist/styles/list-header.css +0 -12
  208. package/dist/styles/list.css +4 -4
  209. package/dist/styles/nav-stack.css +46 -19
  210. package/dist/styles/pane.css +50 -0
  211. package/dist/styles/popup-menu.css +151 -0
  212. package/dist/styles/resizable-region.css +22 -10
  213. package/dist/styles/select.css +28 -14
  214. package/dist/styles/skeleton.css +30 -3
  215. package/dist/styles/split-view.css +10 -7
  216. package/dist/styles/state-banner.css +0 -19
  217. package/dist/styles/surface-scaffold.css +8 -3
  218. package/dist/styles/tab-bar.css +79 -28
  219. package/dist/styles/tab-scaffold.css +25 -1
  220. package/dist/styles/text.css +16 -0
  221. package/dist/styles/toolbar-control-group.css +121 -66
  222. package/dist/styles/toolbar-text.css +21 -0
  223. package/dist/styles/toolbar.css +8 -32
  224. package/dist/styles/workbench.css +18 -18
  225. package/dist/tab-scaffold.d.ts +11 -1
  226. package/dist/tab-scaffold.js +2 -0
  227. package/dist/tab-scaffold.js.map +1 -1
  228. package/dist/toolbar-text.d.ts +7 -1
  229. package/dist/toolbar-text.js +1 -1
  230. package/dist/toolbar.d.ts +8 -1
  231. package/dist/toolbar.js +1 -1
  232. package/dist/{types-DXAGogOS.d.ts → types-BuHELdXR.d.ts} +1 -1
  233. package/dist/wire-catalog.d.ts +3 -1
  234. package/dist/wire-catalog.js +2 -0
  235. package/dist/wire-catalog.js.map +1 -1
  236. package/dist/wire-scroll-dividers.d.ts +69 -0
  237. package/dist/wire-scroll-dividers.js +3 -0
  238. package/dist/wire-scroll-dividers.js.map +1 -0
  239. package/dist/workbench.d.ts +13 -12
  240. package/dist/workbench.js +1 -1
  241. package/docs/app-layouts.md +9 -1
  242. package/docs/catalog.md +10 -5
  243. package/docs/collapsible-panel.md +10 -6
  244. package/docs/component-contract.md +147 -21
  245. package/docs/component-selection.md +18 -6
  246. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -1
  247. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -1
  248. package/docs/design/templates/tab-bar-dark.svg +1 -1
  249. package/docs/design/templates/tab-bar.svg +1 -1
  250. package/docs/design-philosophy.md +30 -3
  251. package/docs/examples/application-ui-profile.json +4 -4
  252. package/docs/examples/{component-catalog-extension-v2.json → component-composition-extension.json} +3 -3
  253. package/docs/layout.md +82 -3
  254. package/docs/nav-stack.md +33 -3
  255. package/docs/split-view.md +10 -5
  256. package/docs/tab-scaffold.md +23 -1
  257. package/docs/type-contracts.md +4 -3
  258. package/docs/ui-analyzer.md +99 -5
  259. package/docs/ui-doctor.md +10 -0
  260. package/docs/workbench.md +19 -9
  261. package/doctor/index.mjs +6 -1
  262. package/llms.txt +85 -20
  263. package/package.json +17 -5
  264. package/ux-demo/recipes/collapsible-sidebar.tsx +0 -1
  265. package/ux-demo/recipes/composer-form.tsx +0 -1
  266. package/ux-demo/recipes/list-detail-dialog.tsx +0 -2
  267. package/ux-demo/recipes/list-workspace-states.tsx +0 -1
  268. package/ux-demo/recipes/loading-inspector.tsx +0 -1
  269. package/ux-demo/recipes/workspace-header.tsx +0 -1
  270. package/ai/component-catalog-extension-v2.d.ts +0 -7
  271. package/ai/component-catalog-extension-v2.schema.json +0 -7
  272. package/dist/chunk-AK67F6QN.js.map +0 -1
  273. package/dist/chunk-G33XC2NB.js.map +0 -1
  274. package/dist/chunk-K6UNVKDK.js.map +0 -1
  275. package/dist/chunk-MA6Z6EIM.js.map +0 -1
  276. package/dist/chunk-MOBRFZH3.js.map +0 -1
  277. package/dist/chunk-O3EXIBEZ.js.map +0 -1
  278. package/dist/chunk-PK4N5QTO.js.map +0 -1
  279. package/dist/chunk-QQOTQTDD.js.map +0 -1
  280. package/dist/chunk-T4UZTNZ4.js.map +0 -1
  281. package/dist/chunk-V65EOWNF.js.map +0 -1
  282. package/dist/chunk-VNPQDCQZ.js.map +0 -1
@@ -1,4 +1,4 @@
1
- export { ToolbarText } from './chunk-K6UNVKDK.js';
1
+ export { ToolbarText } from './chunk-XDKAYSVD.js';
2
2
 
3
3
 
4
4
 
package/dist/toolbar.d.ts CHANGED
@@ -8,7 +8,14 @@ interface ToolbarProps {
8
8
  center?: KerfUiContent;
9
9
  trailing?: KerfUiContent;
10
10
  label?: string;
11
- /** Physical divider edges in canonical top/right/bottom/left order. Defaults to bottom. */
11
+ /**
12
+ * Physical divider edges in canonical top/right/bottom/left order, drawn
13
+ * always. Defaults to none: a toolbar pinned over or under scrolling content
14
+ * gets its divider from the scroll state instead — a `Pane` draws the line
15
+ * between its chrome and content while content is scrolled away beneath it,
16
+ * and `wireScrollDividers` `targets` can name a toolbar as chrome, which
17
+ * then draws its facing side the same way.
18
+ */
12
19
  dividerSides?: Sides;
13
20
  /** Horizontal treatment of the center zone. Defaults to centered content. */
14
21
  centerAlign?: 'center' | 'stretch';
package/dist/toolbar.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Toolbar } from './chunk-G33XC2NB.js';
1
+ export { Toolbar } from './chunk-6YFHCLHG.js';
2
2
 
3
3
  //# sourceMappingURL=toolbar.js.map
@@ -56,7 +56,7 @@ interface CatalogSidebarConfig extends Pick<WorkbenchPanel, 'resizable' | 'separ
56
56
  * `wireCatalog`'s `sidebarSize`, which writes each committed resize to it.
57
57
  */
58
58
  size?: number;
59
- /** The sidebar header toolbar's configuration (default: a bottom divider). */
59
+ /** The sidebar header toolbar's configuration (default: no divider; its pane draws one while scrolled). */
60
60
  toolbar?: ToolbarConfig;
61
61
  }
62
62
  interface CatalogProps {
@@ -73,7 +73,9 @@ declare function wireCatalogGeometryOverlay(root: HTMLElement): () => void;
73
73
  * item selection (and the related-entry popup menu), the sidebar collapse toggle, and
74
74
  * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates
75
75
  * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.
76
- * Returns a disposer.
76
+ * It also wires the scroll dividers below root (`wireScrollDividers`), so the
77
+ * catalog's panes and every example in it show their chrome dividers only
78
+ * while scrolled. Returns a disposer.
77
79
  */
78
80
  declare function wireCatalog(root: HTMLElement, { onSelect, onToggleSidebar, onToggleTheme, onToggleSecondary, urlParam, revealSelection, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, collapsed, sidebarSize, sidebarStorageKey, id: catalogId, }: WireCatalogOptions): () => void;
79
81
 
@@ -1,4 +1,5 @@
1
1
  import { wireWorkbench } from './chunk-KH6VUGHU.js';
2
+ import { wireScrollDividers } from './chunk-JCV4GXWB.js';
2
3
 
3
4
 
4
5
 
@@ -259,6 +260,7 @@ function wireCatalog(root, {
259
260
  }
260
261
  };
261
262
  const disposers = [
263
+ wireScrollDividers(root),
262
264
  // Sidebar items AND the footer's related-entry popup-menu items both carry
263
265
  // `data-action={selectAction}` + `data-item-id`, so one delegated click covers both.
264
266
  delegate(
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/wire-catalog.ts"],"names":[],"mappings":"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","file":"wire-catalog.js","sourcesContent":["import { delegate, type Signal } from 'kerfjs';\n\nimport { wireWorkbench } from './wire-workbench.js';\n\nexport interface WireCatalogOptions {\n /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */\n onSelect: (id: string) => void;\n /** Invoked when the sidebar collapse/expand control is activated. */\n onToggleSidebar?: () => void;\n /** Invoked when the theme toggle is activated. */\n onToggleTheme?: () => void;\n /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */\n onToggleSecondary?: () => void;\n /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */\n urlParam?: string;\n /**\n * Reveal the chosen sidebar row after selection. `true` uses desktop-safe\n * defaults; pass options to customize scroll alignment or the media guard.\n */\n revealSelection?: boolean | CatalogRevealOptions;\n selectAction?: string;\n toggleSidebarAction?: string;\n toggleThemeAction?: string;\n toggleSecondaryAction?: string;\n /**\n * The sidebar's app-owned collapsed flag. With it, the catalog's Workbench\n * sidebar is wired like any Workbench rail: it becomes a transient overlay\n * on a small screen (collapsed as the breakpoint applies, closed on Escape\n * or a press outside, focus handed back), and choosing an entry from the\n * open overlay closes it.\n */\n collapsed?: Signal<boolean>;\n /**\n * The app-owned sidebar width signal for a Catalog rendered with\n * `sidebar={{ resizable: true, size: sidebarSize.value }}`. Each committed\n * drag or keyboard resize is written to it.\n */\n sidebarSize?: Signal<number>;\n /**\n * With `sidebarSize`, load and save the width under this storage key so the\n * catalog remembers the user's sidebar width.\n */\n sidebarStorageKey?: string;\n /** The Catalog's `id`, when it is not the default `kui-catalog`. */\n id?: string;\n}\n\nexport interface CatalogRevealOptions {\n /** Scroll alignment within the sidebar. Default `'nearest'`. */\n block?: ScrollLogicalPosition;\n /** Cross-axis alignment. Default `'nearest'`. */\n inline?: ScrollLogicalPosition;\n /** Scroll behavior. Default `'auto'`. */\n behavior?: ScrollBehavior;\n /**\n * Only reveal when this media query matches. Defaults to the Catalog's\n * desktop layout; pass `false` to reveal at every viewport size.\n */\n media?: string | false;\n}\n\n// The Workbench keeps the sidebar inline above its `narrow` breakpoint (704px).\nconst catalogDesktopMedia = '(min-width: 44.01rem)';\n\n/**\n * Reveal one Catalog sidebar entry after the controlled render settles without\n * moving focus. Returns a cancellation function for rapid selection changes.\n */\nexport function revealCatalogEntry(\n root: HTMLElement,\n id: string,\n {\n block = 'nearest',\n inline = 'nearest',\n behavior = 'auto',\n media = catalogDesktopMedia,\n }: CatalogRevealOptions = {},\n): () => void {\n const view = root.ownerDocument.defaultView;\n if (!view || (media && !view.matchMedia(media).matches)) return () => {};\n const frame = view.requestAnimationFrame(() => {\n for (const item of root.querySelectorAll<HTMLElement>('[data-item-id]')) {\n if (item.dataset.itemId !== id) continue;\n item.scrollIntoView({ block, inline, behavior });\n break;\n }\n });\n return () => view.cancelAnimationFrame(frame);\n}\n\nfunction pixels(value: string): number {\n const number = Number.parseFloat(value);\n if (!Number.isFinite(number)) return 0;\n return value.trim().endsWith('rem') ? number * 16 : number;\n}\n\nfunction isTransparent(color: string): boolean {\n const normalized = color.replace(/\\s+/g, '');\n return (\n normalized === 'transparent' || /(?:,|\\/)0(?:\\.0+)?\\)$/.test(normalized)\n );\n}\n\nfunction geometrySpecimens(canvas: HTMLElement): Element[] {\n const result: Element[] = [];\n for (const example of canvas.querySelectorAll<HTMLElement>(\n '[data-catalog-example]',\n )) {\n if (example.closest('[data-catalog-geometry-overlay-skip]')) continue;\n for (const child of example.children) {\n if (\n child.matches('[data-catalog-geometry-overlay]') ||\n child.matches('[data-catalog-example-label]') ||\n child.matches('[data-catalog-example-note]')\n )\n continue;\n result.push(child);\n }\n }\n for (const element of canvas.querySelectorAll<HTMLElement>(\n '[data-component]',\n )) {\n if (\n element.closest('[data-catalog-geometry-overlay]') ||\n element.closest('[data-catalog-geometry-overlay-skip]') ||\n element.closest('.kui-catalog-example')\n )\n continue;\n const parent =\n element.parentElement?.closest<HTMLElement>('[data-component]');\n if (parent && canvas.contains(parent)) continue;\n result.push(element);\n }\n return result;\n}\n\nfunction geometryBox(\n document: Document,\n className: string,\n left: number,\n top: number,\n width: number,\n height: number,\n): HTMLElement {\n const element = document.createElement('div');\n element.className = className;\n element.style.transform = `translate(${left}px, ${top}px)`;\n element.style.width = `${Math.max(0, width)}px`;\n element.style.height = `${Math.max(0, height)}px`;\n return element;\n}\n\nfunction effectiveBorderWidth(width: string, style: string): number {\n return style === 'none' || style === 'hidden' ? 0 : pixels(width);\n}\n\n/**\n * Keep a Catalog's opt-in geometry overlay synchronized with its preview.\n * Specimens receive computed border highlights (or a dashed bound when they\n * have no border and are transparent), while positive computed margins use\n * devtools-style orange bands. CSS/stylesheet-only changes are observed too.\n * Returns a disposer.\n */\nexport function wireCatalogGeometryOverlay(root: HTMLElement): () => void {\n const catalog = root.matches('[data-component=\"catalog\"]')\n ? root\n : root.querySelector<HTMLElement>('[data-component=\"catalog\"]');\n const canvas = catalog?.querySelector<HTMLElement>('.kui-catalog__canvas');\n const layer = catalog?.querySelector<HTMLElement>(\n '[data-catalog-geometry-overlay]',\n );\n const view = root.ownerDocument.defaultView;\n if (!catalog || !canvas || !layer || !view) return () => {};\n\n let frame = 0;\n const render = (): void => {\n frame = 0;\n layer.replaceChildren();\n if (catalog.dataset.geometryOverlay !== 'true') {\n syncObservedSpecimens([]);\n return;\n }\n const base = canvas.getBoundingClientRect();\n const specimens = geometrySpecimens(canvas);\n syncObservedSpecimens(specimens);\n for (const [specimenIndex, element] of specimens.entries()) {\n const rect = element.getBoundingClientRect();\n const style = view.getComputedStyle(element);\n const align = pixels(\n style.getPropertyValue('--kui-catalog-example-align'),\n );\n const top = pixels(style.marginTop);\n const right = Math.max(\n 0,\n pixels(style.marginRight) - (style.direction === 'rtl' ? align : 0),\n );\n const bottom = pixels(style.marginBottom);\n const left = Math.max(\n 0,\n pixels(style.marginLeft) - (style.direction === 'rtl' ? 0 : align),\n );\n const x = rect.left - base.left + canvas.scrollLeft;\n const y = rect.top - base.top + canvas.scrollTop;\n const borderWidths = [\n effectiveBorderWidth(style.borderTopWidth, style.borderTopStyle),\n effectiveBorderWidth(style.borderRightWidth, style.borderRightStyle),\n effectiveBorderWidth(style.borderBottomWidth, style.borderBottomStyle),\n effectiveBorderWidth(style.borderLeftWidth, style.borderLeftStyle),\n ];\n if (borderWidths.some((width) => width > 0)) {\n const border = geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-border',\n x,\n y,\n rect.width,\n rect.height,\n );\n border.dataset.catalogGeometrySpecimen = String(specimenIndex);\n border.style.borderTopWidth = `${borderWidths[0]}px`;\n border.style.borderRightWidth = `${borderWidths[1]}px`;\n border.style.borderBottomWidth = `${borderWidths[2]}px`;\n border.style.borderLeftWidth = `${borderWidths[3]}px`;\n border.style.borderRadius = style.borderRadius;\n layer.append(border);\n } else if (isTransparent(style.backgroundColor)) {\n const bound = geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-bound',\n x,\n y,\n rect.width,\n rect.height,\n );\n bound.dataset.catalogGeometrySpecimen = String(specimenIndex);\n layer.append(bound);\n }\n const margins: Array<\n readonly [string, number, number, number, number, number]\n > = [\n ['top', top, x - left, y - top, rect.width + left + right, top],\n [\n 'bottom',\n bottom,\n x - left,\n y + rect.height,\n rect.width + left + right,\n bottom,\n ],\n ['left', left, x - left, y, left, rect.height],\n ['right', right, x + rect.width, y, right, rect.height],\n ];\n for (const [side, size, marginX, marginY, width, height] of margins) {\n if (size <= 0) continue;\n const margin = geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-margin',\n marginX,\n marginY,\n width,\n height,\n );\n margin.dataset.catalogGeometrySpecimen = String(specimenIndex);\n margin.dataset.catalogGeometrySide = side;\n layer.append(margin);\n }\n }\n };\n const schedule = (): void => {\n if (!frame) frame = view.requestAnimationFrame(render);\n };\n const resizeObserver = new view.ResizeObserver(schedule);\n resizeObserver.observe(canvas);\n const observedSpecimens = new Set<Element>();\n const syncObservedSpecimens = (specimens: Element[]): void => {\n const next = new Set(specimens);\n for (const specimen of observedSpecimens) {\n if (next.has(specimen)) continue;\n resizeObserver.unobserve(specimen);\n observedSpecimens.delete(specimen);\n }\n for (const specimen of next) {\n if (observedSpecimens.has(specimen)) continue;\n resizeObserver.observe(specimen);\n observedSpecimens.add(specimen);\n }\n };\n const mutationObserver = new view.MutationObserver((records) => {\n if (\n records.some(\n ({ target }) => target !== layer && !layer.contains(target as Node),\n )\n )\n schedule();\n });\n mutationObserver.observe(catalog, {\n attributes: true,\n childList: true,\n subtree: true,\n });\n mutationObserver.observe(root.ownerDocument.documentElement, {\n attributes: true,\n });\n const documentHead = root.ownerDocument.head;\n if (documentHead)\n mutationObserver.observe(documentHead, {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n });\n const stylesheetLoaded = (): void => schedule();\n documentHead?.addEventListener('load', stylesheetLoaded, true);\n view.addEventListener('resize', schedule);\n render();\n return () => {\n if (frame) view.cancelAnimationFrame(frame);\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n documentHead?.removeEventListener('load', stylesheetLoaded, true);\n view.removeEventListener('resize', schedule);\n layer.replaceChildren();\n };\n}\n\n/**\n * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar\n * item selection (and the related-entry popup menu), the sidebar collapse toggle, and\n * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates\n * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.\n * Returns a disposer.\n */\nexport function wireCatalog(\n root: HTMLElement,\n {\n onSelect,\n onToggleSidebar,\n onToggleTheme,\n onToggleSecondary,\n urlParam,\n revealSelection,\n selectAction = 'catalog-select',\n toggleSidebarAction = 'catalog-toggle-sidebar',\n toggleThemeAction = 'catalog-toggle-theme',\n toggleSecondaryAction = 'catalog-toggle-secondary',\n collapsed,\n sidebarSize,\n sidebarStorageKey,\n id: catalogId = 'kui-catalog',\n }: WireCatalogOptions,\n): () => void {\n let cancelReveal: (() => void) | undefined;\n const select = (id: string) => {\n onSelect(id);\n const view = root.ownerDocument.defaultView;\n if (urlParam && view) {\n const url = new URL(view.location.href);\n url.searchParams.set(urlParam, id);\n view.history.replaceState(null, '', url);\n }\n // An open overlay sidebar gives way to the entry just chosen from it.\n const rail = root.ownerDocument.getElementById(`${catalogId}-left-rail`);\n if (\n collapsed &&\n !collapsed.peek() &&\n rail &&\n root.ownerDocument.defaultView?.getComputedStyle(rail).position ===\n 'absolute'\n )\n collapsed.value = true;\n if (revealSelection) {\n cancelReveal?.();\n cancelReveal = revealCatalogEntry(\n root,\n id,\n revealSelection === true ? undefined : revealSelection,\n );\n }\n };\n\n const disposers: Array<() => void> = [\n // Sidebar items AND the footer's related-entry popup-menu items both carry\n // `data-action={selectAction}` + `data-item-id`, so one delegated click covers both.\n delegate(\n root,\n 'click',\n `[data-action=\"${selectAction}\"]`,\n (_event, element) => {\n const id = (element as HTMLElement).dataset.itemId;\n if (id) select(id);\n },\n ),\n ];\n if (onToggleSidebar) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSidebarAction}\"]`, () =>\n onToggleSidebar(),\n ),\n );\n }\n if (onToggleTheme) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleThemeAction}\"]`, () =>\n onToggleTheme(),\n ),\n );\n }\n if (onToggleSecondary) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSecondaryAction}\"]`, () =>\n onToggleSecondary(),\n ),\n );\n }\n if (collapsed || sidebarSize)\n disposers.push(\n wireWorkbench(root, {\n id: catalogId,\n panels: {\n leftRail: {\n collapsed,\n size: sidebarSize,\n storageKey: sidebarStorageKey,\n },\n },\n }),\n );\n return () => {\n cancelReveal?.();\n for (const dispose of disposers.splice(0)) dispose();\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/wire-catalog.ts"],"names":[],"mappings":"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","file":"wire-catalog.js","sourcesContent":["import { delegate, type Signal } from 'kerfjs';\n\nimport { wireScrollDividers } from './wire-scroll-dividers.js';\nimport { wireWorkbench } from './wire-workbench.js';\n\nexport interface WireCatalogOptions {\n /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */\n onSelect: (id: string) => void;\n /** Invoked when the sidebar collapse/expand control is activated. */\n onToggleSidebar?: () => void;\n /** Invoked when the theme toggle is activated. */\n onToggleTheme?: () => void;\n /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */\n onToggleSecondary?: () => void;\n /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */\n urlParam?: string;\n /**\n * Reveal the chosen sidebar row after selection. `true` uses desktop-safe\n * defaults; pass options to customize scroll alignment or the media guard.\n */\n revealSelection?: boolean | CatalogRevealOptions;\n selectAction?: string;\n toggleSidebarAction?: string;\n toggleThemeAction?: string;\n toggleSecondaryAction?: string;\n /**\n * The sidebar's app-owned collapsed flag. With it, the catalog's Workbench\n * sidebar is wired like any Workbench rail: it becomes a transient overlay\n * on a small screen (collapsed as the breakpoint applies, closed on Escape\n * or a press outside, focus handed back), and choosing an entry from the\n * open overlay closes it.\n */\n collapsed?: Signal<boolean>;\n /**\n * The app-owned sidebar width signal for a Catalog rendered with\n * `sidebar={{ resizable: true, size: sidebarSize.value }}`. Each committed\n * drag or keyboard resize is written to it.\n */\n sidebarSize?: Signal<number>;\n /**\n * With `sidebarSize`, load and save the width under this storage key so the\n * catalog remembers the user's sidebar width.\n */\n sidebarStorageKey?: string;\n /** The Catalog's `id`, when it is not the default `kui-catalog`. */\n id?: string;\n}\n\nexport interface CatalogRevealOptions {\n /** Scroll alignment within the sidebar. Default `'nearest'`. */\n block?: ScrollLogicalPosition;\n /** Cross-axis alignment. Default `'nearest'`. */\n inline?: ScrollLogicalPosition;\n /** Scroll behavior. Default `'auto'`. */\n behavior?: ScrollBehavior;\n /**\n * Only reveal when this media query matches. Defaults to the Catalog's\n * desktop layout; pass `false` to reveal at every viewport size.\n */\n media?: string | false;\n}\n\n// The Workbench keeps the sidebar inline above its `narrow` breakpoint (704px).\nconst catalogDesktopMedia = '(min-width: 44.01rem)';\n\n/**\n * Reveal one Catalog sidebar entry after the controlled render settles without\n * moving focus. Returns a cancellation function for rapid selection changes.\n */\nexport function revealCatalogEntry(\n root: HTMLElement,\n id: string,\n {\n block = 'nearest',\n inline = 'nearest',\n behavior = 'auto',\n media = catalogDesktopMedia,\n }: CatalogRevealOptions = {},\n): () => void {\n const view = root.ownerDocument.defaultView;\n if (!view || (media && !view.matchMedia(media).matches)) return () => {};\n const frame = view.requestAnimationFrame(() => {\n for (const item of root.querySelectorAll<HTMLElement>('[data-item-id]')) {\n if (item.dataset.itemId !== id) continue;\n item.scrollIntoView({ block, inline, behavior });\n break;\n }\n });\n return () => view.cancelAnimationFrame(frame);\n}\n\nfunction pixels(value: string): number {\n const number = Number.parseFloat(value);\n if (!Number.isFinite(number)) return 0;\n return value.trim().endsWith('rem') ? number * 16 : number;\n}\n\nfunction isTransparent(color: string): boolean {\n const normalized = color.replace(/\\s+/g, '');\n return (\n normalized === 'transparent' || /(?:,|\\/)0(?:\\.0+)?\\)$/.test(normalized)\n );\n}\n\nfunction geometrySpecimens(canvas: HTMLElement): Element[] {\n const result: Element[] = [];\n for (const example of canvas.querySelectorAll<HTMLElement>(\n '[data-catalog-example]',\n )) {\n if (example.closest('[data-catalog-geometry-overlay-skip]')) continue;\n for (const child of example.children) {\n if (\n child.matches('[data-catalog-geometry-overlay]') ||\n child.matches('[data-catalog-example-label]') ||\n child.matches('[data-catalog-example-note]')\n )\n continue;\n result.push(child);\n }\n }\n for (const element of canvas.querySelectorAll<HTMLElement>(\n '[data-component]',\n )) {\n if (\n element.closest('[data-catalog-geometry-overlay]') ||\n element.closest('[data-catalog-geometry-overlay-skip]') ||\n element.closest('.kui-catalog-example')\n )\n continue;\n const parent =\n element.parentElement?.closest<HTMLElement>('[data-component]');\n if (parent && canvas.contains(parent)) continue;\n result.push(element);\n }\n return result;\n}\n\nfunction geometryBox(\n document: Document,\n className: string,\n left: number,\n top: number,\n width: number,\n height: number,\n): HTMLElement {\n const element = document.createElement('div');\n element.className = className;\n element.style.transform = `translate(${left}px, ${top}px)`;\n element.style.width = `${Math.max(0, width)}px`;\n element.style.height = `${Math.max(0, height)}px`;\n return element;\n}\n\nfunction effectiveBorderWidth(width: string, style: string): number {\n return style === 'none' || style === 'hidden' ? 0 : pixels(width);\n}\n\n/**\n * Keep a Catalog's opt-in geometry overlay synchronized with its preview.\n * Specimens receive computed border highlights (or a dashed bound when they\n * have no border and are transparent), while positive computed margins use\n * devtools-style orange bands. CSS/stylesheet-only changes are observed too.\n * Returns a disposer.\n */\nexport function wireCatalogGeometryOverlay(root: HTMLElement): () => void {\n const catalog = root.matches('[data-component=\"catalog\"]')\n ? root\n : root.querySelector<HTMLElement>('[data-component=\"catalog\"]');\n const canvas = catalog?.querySelector<HTMLElement>('.kui-catalog__canvas');\n const layer = catalog?.querySelector<HTMLElement>(\n '[data-catalog-geometry-overlay]',\n );\n const view = root.ownerDocument.defaultView;\n if (!catalog || !canvas || !layer || !view) return () => {};\n\n let frame = 0;\n const render = (): void => {\n frame = 0;\n layer.replaceChildren();\n if (catalog.dataset.geometryOverlay !== 'true') {\n syncObservedSpecimens([]);\n return;\n }\n const base = canvas.getBoundingClientRect();\n const specimens = geometrySpecimens(canvas);\n syncObservedSpecimens(specimens);\n for (const [specimenIndex, element] of specimens.entries()) {\n const rect = element.getBoundingClientRect();\n const style = view.getComputedStyle(element);\n const align = pixels(\n style.getPropertyValue('--kui-catalog-example-align'),\n );\n const top = pixels(style.marginTop);\n const right = Math.max(\n 0,\n pixels(style.marginRight) - (style.direction === 'rtl' ? align : 0),\n );\n const bottom = pixels(style.marginBottom);\n const left = Math.max(\n 0,\n pixels(style.marginLeft) - (style.direction === 'rtl' ? 0 : align),\n );\n const x = rect.left - base.left + canvas.scrollLeft;\n const y = rect.top - base.top + canvas.scrollTop;\n const borderWidths = [\n effectiveBorderWidth(style.borderTopWidth, style.borderTopStyle),\n effectiveBorderWidth(style.borderRightWidth, style.borderRightStyle),\n effectiveBorderWidth(style.borderBottomWidth, style.borderBottomStyle),\n effectiveBorderWidth(style.borderLeftWidth, style.borderLeftStyle),\n ];\n if (borderWidths.some((width) => width > 0)) {\n const border = geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-border',\n x,\n y,\n rect.width,\n rect.height,\n );\n border.dataset.catalogGeometrySpecimen = String(specimenIndex);\n border.style.borderTopWidth = `${borderWidths[0]}px`;\n border.style.borderRightWidth = `${borderWidths[1]}px`;\n border.style.borderBottomWidth = `${borderWidths[2]}px`;\n border.style.borderLeftWidth = `${borderWidths[3]}px`;\n border.style.borderRadius = style.borderRadius;\n layer.append(border);\n } else if (isTransparent(style.backgroundColor)) {\n const bound = geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-bound',\n x,\n y,\n rect.width,\n rect.height,\n );\n bound.dataset.catalogGeometrySpecimen = String(specimenIndex);\n layer.append(bound);\n }\n const margins: Array<\n readonly [string, number, number, number, number, number]\n > = [\n ['top', top, x - left, y - top, rect.width + left + right, top],\n [\n 'bottom',\n bottom,\n x - left,\n y + rect.height,\n rect.width + left + right,\n bottom,\n ],\n ['left', left, x - left, y, left, rect.height],\n ['right', right, x + rect.width, y, right, rect.height],\n ];\n for (const [side, size, marginX, marginY, width, height] of margins) {\n if (size <= 0) continue;\n const margin = geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-margin',\n marginX,\n marginY,\n width,\n height,\n );\n margin.dataset.catalogGeometrySpecimen = String(specimenIndex);\n margin.dataset.catalogGeometrySide = side;\n layer.append(margin);\n }\n }\n };\n const schedule = (): void => {\n if (!frame) frame = view.requestAnimationFrame(render);\n };\n const resizeObserver = new view.ResizeObserver(schedule);\n resizeObserver.observe(canvas);\n const observedSpecimens = new Set<Element>();\n const syncObservedSpecimens = (specimens: Element[]): void => {\n const next = new Set(specimens);\n for (const specimen of observedSpecimens) {\n if (next.has(specimen)) continue;\n resizeObserver.unobserve(specimen);\n observedSpecimens.delete(specimen);\n }\n for (const specimen of next) {\n if (observedSpecimens.has(specimen)) continue;\n resizeObserver.observe(specimen);\n observedSpecimens.add(specimen);\n }\n };\n const mutationObserver = new view.MutationObserver((records) => {\n if (\n records.some(\n ({ target }) => target !== layer && !layer.contains(target as Node),\n )\n )\n schedule();\n });\n mutationObserver.observe(catalog, {\n attributes: true,\n childList: true,\n subtree: true,\n });\n mutationObserver.observe(root.ownerDocument.documentElement, {\n attributes: true,\n });\n const documentHead = root.ownerDocument.head;\n if (documentHead)\n mutationObserver.observe(documentHead, {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n });\n const stylesheetLoaded = (): void => schedule();\n documentHead?.addEventListener('load', stylesheetLoaded, true);\n view.addEventListener('resize', schedule);\n render();\n return () => {\n if (frame) view.cancelAnimationFrame(frame);\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n documentHead?.removeEventListener('load', stylesheetLoaded, true);\n view.removeEventListener('resize', schedule);\n layer.replaceChildren();\n };\n}\n\n/**\n * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar\n * item selection (and the related-entry popup menu), the sidebar collapse toggle, and\n * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates\n * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.\n * It also wires the scroll dividers below root (`wireScrollDividers`), so the\n * catalog's panes and every example in it show their chrome dividers only\n * while scrolled. Returns a disposer.\n */\nexport function wireCatalog(\n root: HTMLElement,\n {\n onSelect,\n onToggleSidebar,\n onToggleTheme,\n onToggleSecondary,\n urlParam,\n revealSelection,\n selectAction = 'catalog-select',\n toggleSidebarAction = 'catalog-toggle-sidebar',\n toggleThemeAction = 'catalog-toggle-theme',\n toggleSecondaryAction = 'catalog-toggle-secondary',\n collapsed,\n sidebarSize,\n sidebarStorageKey,\n id: catalogId = 'kui-catalog',\n }: WireCatalogOptions,\n): () => void {\n let cancelReveal: (() => void) | undefined;\n const select = (id: string) => {\n onSelect(id);\n const view = root.ownerDocument.defaultView;\n if (urlParam && view) {\n const url = new URL(view.location.href);\n url.searchParams.set(urlParam, id);\n view.history.replaceState(null, '', url);\n }\n // An open overlay sidebar gives way to the entry just chosen from it.\n const rail = root.ownerDocument.getElementById(`${catalogId}-left-rail`);\n if (\n collapsed &&\n !collapsed.peek() &&\n rail &&\n root.ownerDocument.defaultView?.getComputedStyle(rail).position ===\n 'absolute'\n )\n collapsed.value = true;\n if (revealSelection) {\n cancelReveal?.();\n cancelReveal = revealCatalogEntry(\n root,\n id,\n revealSelection === true ? undefined : revealSelection,\n );\n }\n };\n\n const disposers: Array<() => void> = [\n wireScrollDividers(root),\n // Sidebar items AND the footer's related-entry popup-menu items both carry\n // `data-action={selectAction}` + `data-item-id`, so one delegated click covers both.\n delegate(\n root,\n 'click',\n `[data-action=\"${selectAction}\"]`,\n (_event, element) => {\n const id = (element as HTMLElement).dataset.itemId;\n if (id) select(id);\n },\n ),\n ];\n if (onToggleSidebar) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSidebarAction}\"]`, () =>\n onToggleSidebar(),\n ),\n );\n }\n if (onToggleTheme) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleThemeAction}\"]`, () =>\n onToggleTheme(),\n ),\n );\n }\n if (onToggleSecondary) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSecondaryAction}\"]`, () =>\n onToggleSecondary(),\n ),\n );\n }\n if (collapsed || sidebarSize)\n disposers.push(\n wireWorkbench(root, {\n id: catalogId,\n panels: {\n leftRail: {\n collapsed,\n size: sidebarSize,\n storageKey: sidebarStorageKey,\n },\n },\n }),\n );\n return () => {\n cancelReveal?.();\n for (const dispose of disposers.splice(0)) dispose();\n };\n}\n"]}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * An app-owned scroll arrangement outside a `Pane`: the `id` of the element
3
+ * that scrolls and the `id`s of the pinned chrome on each physical side of it.
4
+ * Each named chrome element shows its divider on the side facing the scroller
5
+ * while content is scrolled away beyond that side. Ids are resolved below the
6
+ * wired root on every refresh, so a re-render that replaces an element keeps
7
+ * the pairing.
8
+ */
9
+ interface ScrollDividerTarget {
10
+ /** The `id` of the scrolling element. */
11
+ scroller: string;
12
+ /** Chrome above the scroller: shows its bottom divider once scrolled down. */
13
+ top?: string;
14
+ /**
15
+ * Chrome right of the scroller: shows its left divider while content is
16
+ * hidden beyond the scroller's right edge (never when nothing overflows).
17
+ */
18
+ right?: string;
19
+ /**
20
+ * Chrome below the scroller: shows its top divider while content is hidden
21
+ * beyond the scroller's bottom edge (never when nothing overflows).
22
+ */
23
+ bottom?: string;
24
+ /** Chrome left of the scroller: shows its right divider once scrolled right. */
25
+ left?: string;
26
+ }
27
+ interface WireScrollDividersOptions {
28
+ /**
29
+ * App-owned scroll arrangements to pair beyond the ones found by structure
30
+ * (every `Pane`'s header and footer around its content, every `NavStack`'s
31
+ * and `TabScaffold`'s chrome around its active region, and every `TabBar`
32
+ * strip).
33
+ */
34
+ targets?: readonly ScrollDividerTarget[];
35
+ }
36
+ /**
37
+ * Wire scroll dividers below `root`: a divider between pinned chrome and the
38
+ * content that scrolls beside it shows only while content is scrolled away
39
+ * from that edge. Near edges (top, left) hide at the scroll start; far edges
40
+ * (bottom, right) hide at the scroll end and whenever nothing overflows.
41
+ *
42
+ * The wiring only reports scroll state; each component draws its own divider
43
+ * from it. It writes `data-scroll-overflow` (the edges with hidden content, in
44
+ * canonical `t`/`r`/`b`/`l` order) on each scroller, and `data-scroll-divider`
45
+ * (the sides to draw) on each paired chrome element:
46
+ *
47
+ * - every `Pane` with a header or footer: its header shows a bottom divider and
48
+ * its footer a top divider (drawn by the Pane, per its `chromeDividers`);
49
+ * - every `NavStack`'s top chrome and bottom toolbar around its active view,
50
+ * and every `TabScaffold`'s bar under its active scene: the chrome shows a
51
+ * bottom (top chrome) or top (bottom toolbar, bar) divider. The scroller is
52
+ * whichever element actually scrolls there: the view or scene itself, or,
53
+ * through a sole child with no chrome of its own on that edge, a `Pane`'s
54
+ * content or a nested `NavStack` / `TabScaffold` region (a Pane's own header
55
+ * or footer draws that boundary instead);
56
+ * - every `TabBar` strip: the bar draws a divider on each side of the strip
57
+ * whose tabs are scrolled out of view;
58
+ * - each app-owned `targets` pairing: a `Toolbar` or `List` named as chrome
59
+ * draws the divider on its facing side.
60
+ *
61
+ * Structure is re-read after every DOM change below root (a re-render that
62
+ * drops the attributes gets them back before paint), scroll is tracked with
63
+ * one capturing listener, and size changes of each scroller and its children
64
+ * with a `ResizeObserver`. Returns a disposer that removes every attribute it
65
+ * wrote. See `docs/23-app-layouts.md` §3.7.
66
+ */
67
+ declare function wireScrollDividers(root: HTMLElement | Document, { targets }?: WireScrollDividersOptions): () => void;
68
+
69
+ export { type ScrollDividerTarget, type WireScrollDividersOptions, wireScrollDividers };
@@ -0,0 +1,3 @@
1
+ export { wireScrollDividers } from './chunk-JCV4GXWB.js';
2
+
3
+ //# sourceMappingURL=wire-scroll-dividers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"wire-scroll-dividers.js"}
@@ -3,7 +3,7 @@ import { ListConfig } from './list.js';
3
3
  import { PaneConfig } from './pane.js';
4
4
  import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
5
5
  import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
6
- import { a as PanelToolbar, b as PanelToggle } from './panel-toolbar-C8sF-YZC.js';
6
+ import { a as PanelToolbar, b as PanelToggle } from './panel-toolbar-BN4x0rth.js';
7
7
  import { ToolbarConfig } from './toolbar.js';
8
8
  import './css-values.js';
9
9
  import './flex-alignment-4ms8ZbV8.js';
@@ -24,9 +24,9 @@ type WorkbenchPanelToggle = PanelToggle;
24
24
  type WorkbenchPanelToolbar = PanelToolbar;
25
25
  /**
26
26
  * The work area's top toolbar; collapsed rails add their groups to it. Its
27
- * configuration forwards to its `Toolbar`. By default it draws the divider
28
- * under the work area's header chrome: its own bottom edge, or none when a
29
- * `mainHeader` follows (which then carries the divider).
27
+ * configuration forwards to its `Toolbar`. It draws no divider of its own by
28
+ * default: the work area's `Pane` draws one under its header chrome, wherever
29
+ * that chrome ends, while `main` is scrolled (`mainPane.chromeDividers`).
30
30
  */
31
31
  interface WorkbenchMainToolbar extends ToolbarConfig {
32
32
  label: string;
@@ -40,9 +40,9 @@ interface WorkbenchMainToolbar extends ToolbarConfig {
40
40
  }
41
41
  /**
42
42
  * The work area's bottom toolbar; a collapsed drawer adds its groups to it.
43
- * Its configuration forwards to its `Toolbar`. By default it draws the divider
44
- * over the work area's footer chrome: its own top edge, or none when a
45
- * `mainFooter` precedes it.
43
+ * Its configuration forwards to its `Toolbar`. It draws no divider of its own
44
+ * by default: the work area's `Pane` draws one over its footer chrome while
45
+ * more of `main` lies below (`mainPane.chromeDividers`).
46
46
  */
47
47
  interface WorkbenchMainBottomToolbar extends ToolbarConfig {
48
48
  label: string;
@@ -92,7 +92,7 @@ interface WorkbenchPanel {
92
92
  footer?: KerfUiContent;
93
93
  /**
94
94
  * Configuration for a `toolbar` panel's `Pane` (`contentElement`,
95
- * `contentLabel`, `separators`, `safeAreaEdges`) — for example
95
+ * `contentLabel`, `separators`, `safeAreaEdges`, `chromeDividers`) — for example
96
96
  * `{ contentElement: 'nav', contentLabel: 'Sections' }` for a navigation
97
97
  * rail. Omitted or `undefined` fields keep the `Pane` defaults. Ignored
98
98
  * without a `toolbar`, where `content` renders as given.
@@ -185,7 +185,7 @@ interface WorkbenchProps {
185
185
  mainFooterPlacement?: WorkbenchChromePlacement;
186
186
  /**
187
187
  * Configuration for the work area's `Pane` (`contentElement`,
188
- * `contentLabel`, `separators`, `safeAreaEdges`), which it has whenever it
188
+ * `contentLabel`, `separators`, `safeAreaEdges`, `chromeDividers`), which it has whenever it
189
189
  * has a toolbar, `mainHeader`, or `mainFooter`; without that chrome, `main`
190
190
  * renders as given and this is ignored. Omitted or `undefined` fields keep
191
191
  * the `Pane` defaults.
@@ -194,12 +194,13 @@ interface WorkbenchProps {
194
194
  /**
195
195
  * Configuration for the `List` that holds `mainHeader` (`gap`, `hAlign`,
196
196
  * `vAlign`, `dividerSides`, `textInsets`, `controlInsets`). Omitted or
197
- * `undefined` fields keep the defaults, including its bottom divider.
197
+ * `undefined` fields keep the defaults (no divider: the work area's
198
+ * `Pane` draws the line under its header chrome).
198
199
  */
199
200
  mainHeaderList?: ListConfig;
200
201
  /**
201
- * The same for the `List` that holds `mainFooter`; by default it draws a
202
- * top divider, or none when a `mainBottomToolbar` follows it.
202
+ * The same for the `List` that holds `mainFooter` (no divider by default:
203
+ * the work area's `Pane` draws the line over its footer chrome).
203
204
  */
204
205
  mainFooterList?: ListConfig;
205
206
  leftRail?: WorkbenchPanel;
package/dist/workbench.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Workbench } from './chunk-QQOTQTDD.js';
1
+ export { Workbench } from './chunk-G2RKFVRB.js';
2
2
 
3
3
 
4
4
 
@@ -44,7 +44,9 @@ the layouts handle the insets for you. There is nothing to configure:
44
44
  not inset on that edge. Collapse the rail and the center picks up the edge.
45
45
  - **No double inset.** Whichever region applies an inset clears it for its
46
46
  descendants. A layout region whose only child is a `Pane` or another layout
47
- lets that child own the insets. A `Pane` header or footer whose only child is
47
+ lets that child own the insets. That child fills the region — a `Workbench`
48
+ area or panel, a `NavStack` view, a `TabScaffold` scene — so a sole `Pane`'s
49
+ header stays pinned while its own content scrolls. A `Pane` header or footer whose only child is
48
50
  a `Toolbar` hands the inline insets to the toolbar, so the toolbar's dividers
49
51
  still reach the edge.
50
52
 
@@ -131,6 +133,12 @@ device in tests.
131
133
  `content` a `NavStack`. On a tablet/desktop, render the same sections as a
132
134
  `Workbench` left rail instead of a bottom bar.
133
135
 
136
+ Whatever the layout, call `wireScrollDividers(appRoot)` from
137
+ `@kerfjs/ui/wire-scroll-dividers` once: every `Pane` a layout renders, every
138
+ `NavStack`'s top chrome and bottom toolbar, and every `TabScaffold` bar then
139
+ draw the line between the pinned chrome and the scrolling content only while
140
+ content is scrolled beneath them, and toolbars need no divider of their own. See [Scroll dividers](layout.md#scroll-dividers).
141
+
134
142
  ## Dialogs
135
143
 
136
144
  Pick the dialog's inner layout by the same complexity axis, then apply the device
package/docs/catalog.md CHANGED
@@ -281,7 +281,7 @@ import {
281
281
  wireCatalog,
282
282
  wireCatalogGeometryOverlay,
283
283
  } from "@kerfjs/ui/wire-catalog";
284
- import "@kerfjs/ui/styles.css"; // or import each primitive's CSS + @kerfjs/ui/catalog.css
284
+ // A CSS-aware (browser-condition) bundler loads the Catalog's CSS with its import.
285
285
 
286
286
  type DemoKind = "component" | "composition";
287
287
  type DemoEntry = CatalogSection["entries"][number] & { kind: DemoKind };
@@ -411,7 +411,9 @@ revealCatalogEntry(app, initial, { block: "center" });
411
411
  - **`mainToolbar` / `footerToolbar`** take the entry toolbar's and the resource
412
412
  footer toolbar's `ToolbarConfig` (`dividerSides`, `centerAlign`, `responsive`,
413
413
  `responsiveAt`, `safeAreaEdges`). The defaults are a wrapping entry toolbar
414
- and a divider-free footer toolbar that stacks at `narrow`; an omitted or
414
+ and a footer toolbar that stacks at `narrow`, all without dividers of their
415
+ own: `wireCatalog` wires `wireScrollDividers`, so the shell's panes draw
416
+ their chrome dividers only while scrolled; an omitted or
415
417
  `undefined` field keeps them. Configure these instead of styling the shell.
416
418
  - **`theme`** is a global preference — the app's signal; `wireCatalog` only reports
417
419
  the toggle, the app applies the theme (the shell reads `theme` to show the toggle's
@@ -429,7 +431,10 @@ matching options on `wireCatalog`) if they collide with your own action table.
429
431
  `Catalog` composes public components (`Workbench`, `Pane`, `Toolbar`, `List`,
430
432
  `ListHeader`, `ListItem`, `ListInsetText`, `Text`, …) and owns only the preview stage
431
433
  (the checkerboard and centered canvas), the geometry overlay, the example viewport
432
- options, and the brand mark's size. Import `@kerfjs/ui/styles.css` for the whole
433
- layer, or `@kerfjs/ui/catalog.css` plus `@kerfjs/ui/workbench.css` and each composed
434
- primitive's CSS. Like any Workbench it needs a definite containing height — the
434
+ options, and the brand mark's size. A bundler that honors the `browser` export
435
+ condition loads the Catalog's stylesheets and those of every component it renders
436
+ internally with the `@kerfjs/ui/catalog` import alone — no dependence on which other
437
+ subpaths the app happens to import. Without that condition, import
438
+ `@kerfjs/ui/styles.css` for the whole layer, or `@kerfjs/ui/catalog.css` plus
439
+ `@kerfjs/ui/workbench.css` and each composed primitive's CSS. Like any Workbench it needs a definite containing height — the
435
440
  `@kerfjs/ui/document.css` baseline's `.kui-app-root`.
@@ -10,7 +10,10 @@ subpath-only, tree-shakeable modules that add nothing to the main barrel.
10
10
  npm install @kerfjs/ui # kerfjs is a peer
11
11
  ```
12
12
 
13
- Import the panel CSS (`@kerfjs/ui/collapsible-panel.css`) alongside `foundation.css`.
13
+ A browser bundler that honors the `browser` export condition loads the panel's CSS
14
+ and that of the components it renders (`Pane`, `Toolbar`, `ToolbarControlGroup`,
15
+ `LucideIcon`) with `@kerfjs/ui/collapsible-panel`. Without that condition, import
16
+ `@kerfjs/ui/collapsible-panel.css` plus those stylesheets alongside `foundation.css`.
14
17
 
15
18
  ## The pieces
16
19
 
@@ -132,10 +135,12 @@ top toolbar over a `Pane`, with an optional `footer` below its content:
132
135
  hideLabel? }` trail it.
133
136
 
134
137
  The toolbar also takes the `Toolbar`'s configuration (`dividerSides`,
135
- `centerAlign`, `responsive`, `responsiveAt`, `safeAreaEdges`); it keeps its
136
- bottom divider unless `dividerSides` says otherwise. The `Pane` under it takes
137
- the panel's `pane` (`PaneConfig`: `contentElement`, `contentLabel`,
138
- `separators`, `safeAreaEdges`) — for example `{ contentElement: "nav",
138
+ `centerAlign`, `responsive`, `responsiveAt`, `safeAreaEdges`); it draws no
139
+ divider of its own unless `dividerSides` says so. The `Pane` under it draws
140
+ the line under the toolbar only while its content is scrolled (see
141
+ [Scroll dividers](./layout.md#scroll-dividers)), and takes the panel's `pane`
142
+ (`PaneConfig`: `contentElement`, `contentLabel`, `separators`,
143
+ `safeAreaEdges`, `chromeDividers`) — for example `{ contentElement: "nav",
139
144
  contentLabel: "Sections" }` for a navigation rail. An omitted or `undefined`
140
145
  field keeps the `Pane` default; without a `toolbar` there is no `Pane`, so
141
146
  `pane` is ignored.
@@ -241,7 +246,6 @@ import {
241
246
  CollapsiblePanelToggle,
242
247
  } from "@kerfjs/ui/collapsible-panel";
243
248
  import { wireSidebar } from "@kerfjs/ui/wire-sidebar";
244
- import "@kerfjs/ui/collapsible-panel.css";
245
249
 
246
250
  const device = deviceClass();
247
251
  // Start collapsed on a compact device; `inlineCollapsed` keeps the open inline