@kerfjs/ui 5.0.0-beta.16 → 5.0.0-beta.18

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 (218) hide show
  1. package/README.md +111 -81
  2. package/ai/component-catalog-extension.schema.json +79 -0
  3. package/ai/component-catalog.json +499 -0
  4. package/ai/component-catalog.schema.json +15 -0
  5. package/ai/public-api-signatures-v1.md +86 -32
  6. package/ai/skill.md +60 -51
  7. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  8. package/dist/app-tab.d.ts +1 -1
  9. package/dist/app-tab.js +1 -1
  10. package/dist/catalog.d.ts +12 -3
  11. package/dist/catalog.js +283 -90
  12. package/dist/catalog.js.map +1 -1
  13. package/dist/chunk-43FUQH35.js +83 -0
  14. package/dist/chunk-43FUQH35.js.map +1 -0
  15. package/dist/{chunk-YGN7OMLT.js → chunk-4TRDV6RL.js} +2 -2
  16. package/dist/chunk-4TRDV6RL.js.map +1 -0
  17. package/dist/chunk-6ETZKKAE.js +38 -0
  18. package/dist/chunk-6ETZKKAE.js.map +1 -0
  19. package/dist/chunk-6WTETUZ6.js +21 -0
  20. package/dist/chunk-6WTETUZ6.js.map +1 -0
  21. package/dist/chunk-7L6EHUMV.js +31 -0
  22. package/dist/chunk-7L6EHUMV.js.map +1 -0
  23. package/dist/chunk-BBRXX6PF.js +108 -0
  24. package/dist/chunk-BBRXX6PF.js.map +1 -0
  25. package/dist/chunk-DPDBVKNA.js +113 -0
  26. package/dist/chunk-DPDBVKNA.js.map +1 -0
  27. package/dist/chunk-ELSJX4W7.js +68 -0
  28. package/dist/chunk-ELSJX4W7.js.map +1 -0
  29. package/dist/{chunk-LCOBDOCC.js → chunk-ENVTNLYC.js} +153 -67
  30. package/dist/chunk-ENVTNLYC.js.map +1 -0
  31. package/dist/{chunk-AHQI52QO.js → chunk-FEY65TBV.js} +6 -2
  32. package/dist/chunk-FEY65TBV.js.map +1 -0
  33. package/dist/chunk-JTHUXDN2.js +58 -0
  34. package/dist/chunk-JTHUXDN2.js.map +1 -0
  35. package/dist/chunk-LPOMRC4C.js +25 -0
  36. package/dist/chunk-LPOMRC4C.js.map +1 -0
  37. package/dist/chunk-NENBUYX5.js +79 -0
  38. package/dist/chunk-NENBUYX5.js.map +1 -0
  39. package/dist/{chunk-XXMOARTH.js → chunk-NVWXKU37.js} +57 -16
  40. package/dist/chunk-NVWXKU37.js.map +1 -0
  41. package/dist/chunk-PIDTEQ3U.js +105 -0
  42. package/dist/chunk-PIDTEQ3U.js.map +1 -0
  43. package/dist/chunk-PX3YSS5C.js +32 -0
  44. package/dist/chunk-PX3YSS5C.js.map +1 -0
  45. package/dist/chunk-PZQVSDLH.js +61 -0
  46. package/dist/chunk-PZQVSDLH.js.map +1 -0
  47. package/dist/chunk-QIRXYKCE.js +113 -0
  48. package/dist/chunk-QIRXYKCE.js.map +1 -0
  49. package/dist/chunk-QO55FN2Y.js +20 -0
  50. package/dist/chunk-QO55FN2Y.js.map +1 -0
  51. package/dist/chunk-QQFJMRIB.js +45 -0
  52. package/dist/chunk-QQFJMRIB.js.map +1 -0
  53. package/dist/chunk-S3HKPI7I.js +218 -0
  54. package/dist/chunk-S3HKPI7I.js.map +1 -0
  55. package/dist/chunk-S3PCZ7NV.js +38 -0
  56. package/dist/chunk-S3PCZ7NV.js.map +1 -0
  57. package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
  58. package/dist/chunk-SRSJO5QE.js.map +1 -0
  59. package/dist/chunk-U5IFGQCD.js +117 -0
  60. package/dist/chunk-U5IFGQCD.js.map +1 -0
  61. package/dist/{chunk-56RJXPHG.js → chunk-Y6LYKFXY.js} +1 -1
  62. package/dist/{chunk-56RJXPHG.js.map → chunk-Y6LYKFXY.js.map} +1 -1
  63. package/dist/chunk-ZK2G3VR5.js +35 -0
  64. package/dist/chunk-ZK2G3VR5.js.map +1 -0
  65. package/dist/chunk-ZVI5YST2.js +41 -0
  66. package/dist/chunk-ZVI5YST2.js.map +1 -0
  67. package/dist/collapsible-panel.d.ts +2 -2
  68. package/dist/collapsible-panel.js +47 -6
  69. package/dist/collapsible-panel.js.map +1 -1
  70. package/dist/device-class.js +33 -5
  71. package/dist/device-class.js.map +1 -1
  72. package/dist/disclosure-arrow.js +1 -1
  73. package/dist/empty-state.d.ts +1 -1
  74. package/dist/empty-state.js +1 -1
  75. package/dist/floating-toolbar.d.ts +1 -1
  76. package/dist/floating-toolbar.js +1 -1
  77. package/dist/index.js +25 -25
  78. package/dist/list-action-row.d.ts +1 -1
  79. package/dist/list-action-row.js +1 -1
  80. package/dist/list-header.d.ts +1 -1
  81. package/dist/list-header.js +1 -1
  82. package/dist/list-inset-control.d.ts +1 -1
  83. package/dist/list-inset-control.js +1 -1
  84. package/dist/list-inset-text.d.ts +1 -1
  85. package/dist/list-inset-text.js +1 -1
  86. package/dist/list-item.d.ts +1 -1
  87. package/dist/list-item.js +1 -1
  88. package/dist/loading-spinner.js +1 -1
  89. package/dist/nav-stack.d.ts +1 -1
  90. package/dist/nav-stack.js +1 -1
  91. package/dist/panel-header.d.ts +1 -1
  92. package/dist/panel-header.js +1 -1
  93. package/dist/resizable-region.d.ts +1 -1
  94. package/dist/resizable-region.js +1 -1
  95. package/dist/segmented-control.js +1 -1
  96. package/dist/select.d.ts +1 -1
  97. package/dist/select.js +1 -1
  98. package/dist/skeleton.d.ts +1 -1
  99. package/dist/skeleton.js +1 -1
  100. package/dist/split-view.d.ts +1 -1
  101. package/dist/split-view.js +70 -10
  102. package/dist/split-view.js.map +1 -1
  103. package/dist/state-banner.d.ts +1 -1
  104. package/dist/state-banner.js +1 -1
  105. package/dist/styles/catalog.css +48 -1
  106. package/dist/styles/toolbar-text.css +54 -2
  107. package/dist/styles/webawesome.css +131 -0
  108. package/dist/tab-bar.d.ts +1 -1
  109. package/dist/tab-bar.js +1 -1
  110. package/dist/tab-scaffold.d.ts +1 -1
  111. package/dist/tab-scaffold.js +43 -8
  112. package/dist/tab-scaffold.js.map +1 -1
  113. package/dist/token-search-field.js +1 -1
  114. package/dist/toolbar-control-group.d.ts +1 -1
  115. package/dist/toolbar-control-group.js +1 -1
  116. package/dist/toolbar-text.d.ts +17 -1
  117. package/dist/toolbar-text.js +1 -1
  118. package/dist/toolbar.d.ts +1 -1
  119. package/dist/toolbar.js +1 -1
  120. package/dist/value-table.d.ts +2 -2
  121. package/dist/value-table.js +1 -1
  122. package/dist/wire-catalog.d.ts +31 -2
  123. package/dist/wire-catalog.js +203 -8
  124. package/dist/wire-catalog.js.map +1 -1
  125. package/dist/wire-nav-stack.js +22 -7
  126. package/dist/wire-nav-stack.js.map +1 -1
  127. package/dist/wire-resizable-regions.d.ts +1 -1
  128. package/dist/wire-resizable-regions.js +1 -1
  129. package/dist/wire-sidebar.js +52 -41
  130. package/dist/wire-sidebar.js.map +1 -1
  131. package/dist/wire-tab-bars.js +1 -1
  132. package/dist/wire-tab-scaffold.js +9 -4
  133. package/dist/wire-tab-scaffold.js.map +1 -1
  134. package/dist/wire-token-search-fields.d.ts +1 -1
  135. package/dist/wire-token-search-fields.js +1 -1
  136. package/dist/workbench.d.ts +1 -1
  137. package/dist/workbench.js +56 -9
  138. package/dist/workbench.js.map +1 -1
  139. package/docs/app-layouts.md +7 -7
  140. package/docs/catalog.md +110 -25
  141. package/docs/collapsible-panel.md +38 -11
  142. package/docs/component-contract.md +30 -2
  143. package/docs/component-selection.md +76 -65
  144. package/docs/design/templates.md +1 -1
  145. package/docs/design-philosophy.md +5 -5
  146. package/docs/device-class.md +8 -8
  147. package/docs/examples/command-palette-adapter.tsx +67 -17
  148. package/docs/examples/component-catalog-extension.json +45 -0
  149. package/docs/layout.md +19 -19
  150. package/docs/nav-stack.md +15 -6
  151. package/docs/recipes.md +4 -4
  152. package/docs/split-view.md +3 -3
  153. package/docs/tab-scaffold.md +16 -7
  154. package/docs/ux-demo.md +2 -2
  155. package/docs/webawesome-theme.md +73 -8
  156. package/docs/workbench.md +18 -5
  157. package/llms.txt +36 -15
  158. package/package.json +6 -4
  159. package/ux-demo/recipes/app-shell.tsx +206 -10
  160. package/ux-demo/recipes/collapsible-sidebar.tsx +167 -44
  161. package/ux-demo/recipes/compact-toolbar.tsx +114 -12
  162. package/ux-demo/recipes/composer-form.tsx +106 -18
  163. package/ux-demo/recipes/list-detail-dialog.tsx +144 -8
  164. package/ux-demo/recipes/list-workspace-states.tsx +165 -8
  165. package/ux-demo/recipes/loaders.ts +5 -2
  166. package/ux-demo/recipes/loading-inspector.tsx +124 -41
  167. package/ux-demo/recipes/mount-recipe.ts +27 -6
  168. package/ux-demo/recipes/navigation-sidebar.tsx +120 -9
  169. package/ux-demo/recipes/navigation-stack.tsx +61 -19
  170. package/ux-demo/recipes/types.ts +3 -1
  171. package/ux-demo/recipes/workspace-header.tsx +57 -2
  172. package/dist/chunk-2Z5TWHKQ.js +0 -34
  173. package/dist/chunk-2Z5TWHKQ.js.map +0 -1
  174. package/dist/chunk-37T3VNIZ.js +0 -19
  175. package/dist/chunk-37T3VNIZ.js.map +0 -1
  176. package/dist/chunk-55DATV3A.js +0 -10
  177. package/dist/chunk-55DATV3A.js.map +0 -1
  178. package/dist/chunk-5ATCDSQS.js +0 -42
  179. package/dist/chunk-5ATCDSQS.js.map +0 -1
  180. package/dist/chunk-5E2HVJ33.js +0 -10
  181. package/dist/chunk-5E2HVJ33.js.map +0 -1
  182. package/dist/chunk-7BR3DO2A.js.map +0 -1
  183. package/dist/chunk-7JB7VPRI.js +0 -34
  184. package/dist/chunk-7JB7VPRI.js.map +0 -1
  185. package/dist/chunk-7LDCTU7D.js +0 -134
  186. package/dist/chunk-7LDCTU7D.js.map +0 -1
  187. package/dist/chunk-AHQI52QO.js.map +0 -1
  188. package/dist/chunk-AQNKJRFH.js +0 -79
  189. package/dist/chunk-AQNKJRFH.js.map +0 -1
  190. package/dist/chunk-AVZHJN6C.js +0 -14
  191. package/dist/chunk-AVZHJN6C.js.map +0 -1
  192. package/dist/chunk-B24C4A6T.js +0 -22
  193. package/dist/chunk-B24C4A6T.js.map +0 -1
  194. package/dist/chunk-BLK3UEEI.js +0 -23
  195. package/dist/chunk-BLK3UEEI.js.map +0 -1
  196. package/dist/chunk-CH4SQ6H5.js +0 -39
  197. package/dist/chunk-CH4SQ6H5.js.map +0 -1
  198. package/dist/chunk-DAFFDO33.js +0 -14
  199. package/dist/chunk-DAFFDO33.js.map +0 -1
  200. package/dist/chunk-I7INRJO4.js +0 -17
  201. package/dist/chunk-I7INRJO4.js.map +0 -1
  202. package/dist/chunk-L4OKFDJY.js +0 -24
  203. package/dist/chunk-L4OKFDJY.js.map +0 -1
  204. package/dist/chunk-LCOBDOCC.js.map +0 -1
  205. package/dist/chunk-M5JRI6MN.js +0 -27
  206. package/dist/chunk-M5JRI6MN.js.map +0 -1
  207. package/dist/chunk-RNPGFQ33.js +0 -10
  208. package/dist/chunk-RNPGFQ33.js.map +0 -1
  209. package/dist/chunk-RUNWEPR4.js +0 -10
  210. package/dist/chunk-RUNWEPR4.js.map +0 -1
  211. package/dist/chunk-VPCTCUED.js +0 -10
  212. package/dist/chunk-VPCTCUED.js.map +0 -1
  213. package/dist/chunk-WVPYVDXY.js +0 -15
  214. package/dist/chunk-WVPYVDXY.js.map +0 -1
  215. package/dist/chunk-XE5ZLPCC.js +0 -20
  216. package/dist/chunk-XE5ZLPCC.js.map +0 -1
  217. package/dist/chunk-XXMOARTH.js.map +0 -1
  218. package/dist/chunk-YGN7OMLT.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/wire-tab-scaffold.ts"],"names":[],"mappings":";;;AAWO,SAAS,eAAA,CAAgB,MAAe,OAAA,EAA6C;AAC1F,EAAA,MAAM,OAAA,GAAU,KAAK,OAAA,CAAQ,iCAAiC,IAAI,IAAA,GAAO,IAAA,CAAK,cAAc,iCAAiC,CAAA;AAC7H,EAAA,IAAI,EAAE,OAAA,YAAmB,WAAA,CAAA,EAAc,OAAO,MAAM;AAAA,EAAC,CAAA;AACrD,EAAA,OAAO,SAAS,OAAA,EAAS,OAAA,EAAS,yBAAA,EAA2B,CAAC,QAAQ,MAAA,KAAW;AAC/E,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,YAAA,CAAa,uBAAuB,CAAA;AACzD,IAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,QAAA,CAAS,KAAK,CAAA;AAAA,EACnC,CAAC,CAAA;AACH","file":"wire-tab-scaffold.js","sourcesContent":["import { delegate } from 'kerfjs';\n\nexport interface WireTabScaffoldOptions {\n /** Invoked with the selected tab id when a bottom-bar tab is activated. */\n onSelect: (tabId: string) => void;\n}\n\n/**\n * Wire a `TabScaffold`'s bottom tab bar: clicking a tab calls `onSelect` with its\n * id (the app then updates its controlled `active`). Returns a disposer.\n */\nexport function wireTabScaffold(root: Element, options: WireTabScaffoldOptions): () => void {\n const section = root.matches('[data-component=\"tab-scaffold\"]') ? root : root.querySelector('[data-component=\"tab-scaffold\"]');\n if (!(section instanceof HTMLElement)) return () => {};\n return delegate(section, 'click', '[data-tab-scaffold-tab]', (_event, target) => {\n const tabId = target.getAttribute('data-tab-scaffold-tab');\n if (tabId) options.onSelect(tabId);\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/wire-tab-scaffold.ts"],"names":[],"mappings":";;;AAWO,SAAS,eAAA,CACd,MACA,OAAA,EACY;AACZ,EAAA,MAAM,OAAA,GAAU,KAAK,OAAA,CAAQ,iCAAiC,IAC1D,IAAA,GACA,IAAA,CAAK,cAAc,iCAAiC,CAAA;AACxD,EAAA,IAAI,EAAE,OAAA,YAAmB,WAAA,CAAA,EAAc,OAAO,MAAM;AAAA,EAAC,CAAA;AACrD,EAAA,OAAO,QAAA;AAAA,IACL,OAAA;AAAA,IACA,OAAA;AAAA,IACA,yBAAA;AAAA,IACA,CAAC,QAAQ,MAAA,KAAW;AAClB,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,YAAA,CAAa,uBAAuB,CAAA;AACzD,MAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,QAAA,CAAS,KAAK,CAAA;AAAA,IACnC;AAAA,GACF;AACF","file":"wire-tab-scaffold.js","sourcesContent":["import { delegate } from 'kerfjs';\n\nexport interface WireTabScaffoldOptions {\n /** Invoked with the selected tab id when a bottom-bar tab is activated. */\n onSelect: (tabId: string) => void;\n}\n\n/**\n * Wire a `TabScaffold`'s bottom tab bar: clicking a tab calls `onSelect` with its\n * id (the app then updates its controlled `active`). Returns a disposer.\n */\nexport function wireTabScaffold(\n root: Element,\n options: WireTabScaffoldOptions,\n): () => void {\n const section = root.matches('[data-component=\"tab-scaffold\"]')\n ? root\n : root.querySelector('[data-component=\"tab-scaffold\"]');\n if (!(section instanceof HTMLElement)) return () => {};\n return delegate(\n section,\n 'click',\n '[data-tab-scaffold-tab]',\n (_event, target) => {\n const tabId = target.getAttribute('data-tab-scaffold-tab');\n if (tabId) options.onSelect(tabId);\n },\n );\n}\n"]}
@@ -101,6 +101,6 @@ interface TokenSearchFieldsHandle {
101
101
  * expand/collapse/focus. Returns a {@link TokenSearchFieldsHandle} — a disposer that also
102
102
  * exposes the managed `expanded` state per field id.
103
103
  */
104
- declare function wireTokenSearchFields(root: HTMLElement, { onSubmit, onEdit, collapsible, keyboard }?: WireTokenSearchFieldsOptions): TokenSearchFieldsHandle;
104
+ declare function wireTokenSearchFields(root: HTMLElement, { onSubmit, onEdit, collapsible, keyboard, }?: WireTokenSearchFieldsOptions): TokenSearchFieldsHandle;
105
105
 
106
106
  export { type TokenSearchCollapsibleOptions, type TokenSearchEdit, type TokenSearchFieldsHandle, type TokenSearchKeyboardOptions, type TokenSearchSubmit, type TokenSearchTokenRemoval, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
@@ -1,4 +1,4 @@
1
- export { wireTokenSearchFields } from './chunk-LCOBDOCC.js';
1
+ export { wireTokenSearchFields } from './chunk-ENVTNLYC.js';
2
2
 
3
3
 
4
4
 
@@ -28,6 +28,6 @@ interface WorkbenchProps {
28
28
  * work area relayouts once, not per frame. The app owns each `collapsed` flag;
29
29
  * the collapse is pure CSS (no wire). See `docs/23-app-layouts.md` §3.3.
30
30
  */
31
- declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className }: WorkbenchProps): SafeHtml;
31
+ declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className, }: WorkbenchProps): SafeHtml;
32
32
 
33
33
  export { Workbench, type WorkbenchPanel, type WorkbenchProps };
package/dist/workbench.js CHANGED
@@ -1,15 +1,62 @@
1
1
  import { jsxs, jsx } from 'kerfjs/jsx-runtime';
2
2
 
3
3
  // src/workbench.tsx
4
- function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className = "" }) {
5
- return /* @__PURE__ */ jsxs("section", { class: `kui-workbench ${className}`.trim(), id, "data-component": "workbench", "aria-label": label, children: [
6
- leftRail && /* @__PURE__ */ jsx("aside", { class: "kui-workbench__rail kui-workbench__rail--left", "data-workbench-rail": "left", "data-collapsed": String(leftRail.collapsed ?? false), "aria-label": leftRail.label || void 0, style: leftRail.size ? `--kui-workbench-rail-width: ${leftRail.size}px` : void 0, children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: leftRail.content }) }),
7
- /* @__PURE__ */ jsxs("div", { class: "kui-workbench__center", children: [
8
- /* @__PURE__ */ jsx("div", { class: "kui-workbench__main", "data-workbench-main": true, children: main }),
9
- bottomDrawer && /* @__PURE__ */ jsx("section", { class: "kui-workbench__drawer", "data-workbench-drawer": true, "data-collapsed": String(bottomDrawer.collapsed ?? false), "aria-label": bottomDrawer.label || void 0, style: bottomDrawer.size ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px` : void 0, children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: bottomDrawer.content }) })
10
- ] }),
11
- rightRail && /* @__PURE__ */ jsx("aside", { class: "kui-workbench__rail kui-workbench__rail--right", "data-workbench-rail": "right", "data-collapsed": String(rightRail.collapsed ?? false), "aria-label": rightRail.label || void 0, style: rightRail.size ? `--kui-workbench-rail-width: ${rightRail.size}px` : void 0, children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: rightRail.content }) })
12
- ] });
4
+ function Workbench({
5
+ id,
6
+ label,
7
+ main,
8
+ leftRail,
9
+ rightRail,
10
+ bottomDrawer,
11
+ className = ""
12
+ }) {
13
+ return /* @__PURE__ */ jsxs(
14
+ "section",
15
+ {
16
+ class: `kui-workbench ${className}`.trim(),
17
+ id,
18
+ "data-component": "workbench",
19
+ "aria-label": label,
20
+ children: [
21
+ leftRail && /* @__PURE__ */ jsx(
22
+ "aside",
23
+ {
24
+ class: "kui-workbench__rail kui-workbench__rail--left",
25
+ "data-workbench-rail": "left",
26
+ "data-collapsed": String(leftRail.collapsed ?? false),
27
+ "aria-label": leftRail.label || void 0,
28
+ style: leftRail.size ? `--kui-workbench-rail-width: ${leftRail.size}px` : void 0,
29
+ children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: leftRail.content })
30
+ }
31
+ ),
32
+ /* @__PURE__ */ jsxs("div", { class: "kui-workbench__center", children: [
33
+ /* @__PURE__ */ jsx("div", { class: "kui-workbench__main", "data-workbench-main": true, children: main }),
34
+ bottomDrawer && /* @__PURE__ */ jsx(
35
+ "section",
36
+ {
37
+ class: "kui-workbench__drawer",
38
+ "data-workbench-drawer": true,
39
+ "data-collapsed": String(bottomDrawer.collapsed ?? false),
40
+ "aria-label": bottomDrawer.label || void 0,
41
+ style: bottomDrawer.size ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px` : void 0,
42
+ children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: bottomDrawer.content })
43
+ }
44
+ )
45
+ ] }),
46
+ rightRail && /* @__PURE__ */ jsx(
47
+ "aside",
48
+ {
49
+ class: "kui-workbench__rail kui-workbench__rail--right",
50
+ "data-workbench-rail": "right",
51
+ "data-collapsed": String(rightRail.collapsed ?? false),
52
+ "aria-label": rightRail.label || void 0,
53
+ style: rightRail.size ? `--kui-workbench-rail-width: ${rightRail.size}px` : void 0,
54
+ children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: rightRail.content })
55
+ }
56
+ )
57
+ ]
58
+ }
59
+ );
13
60
  }
14
61
 
15
62
  export { Workbench };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/workbench.tsx"],"names":[],"mappings":";;;AAgCO,SAAS,SAAA,CAAU,EAAE,EAAA,EAAI,KAAA,EAAO,IAAA,EAAM,UAAU,SAAA,EAAW,YAAA,EAAc,SAAA,GAAY,EAAA,EAAG,EAAmB;AAChH,EAAA,uBAAO,IAAA,CAAC,SAAA,EAAA,EAAQ,KAAA,EAAO,CAAA,cAAA,EAAiB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,EAAG,EAAA,EAAQ,gBAAA,EAAe,WAAA,EAAY,YAAA,EAAY,KAAA,EACxG,QAAA,EAAA;AAAA,IAAA,QAAA,oBAAY,GAAA,CAAC,OAAA,EAAA,EAAM,KAAA,EAAM,+CAAA,EAAgD,qBAAA,EAAoB,MAAA,EAAO,gBAAA,EAAgB,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,KAAK,CAAA,EAAG,cAAY,QAAA,CAAS,KAAA,IAAS,MAAA,EAAW,KAAA,EAAO,QAAA,CAAS,IAAA,GAAO,CAAA,4BAAA,EAA+B,QAAA,CAAS,IAAI,CAAA,EAAA,CAAA,GAAO,MAAA,EAC3Q,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8BAAA,EAAgC,QAAA,EAAA,QAAA,CAAS,SAAQ,CAAA,EAC9D,CAAA;AAAA,oBACA,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,uBAAA,EACT,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,qBAAA,EAAsB,qBAAA,EAAmB,MAAE,QAAA,EAAA,IAAA,EAAK,CAAA;AAAA,MAC1D,YAAA,oBAAgB,GAAA,CAAC,SAAA,EAAA,EAAQ,KAAA,EAAM,uBAAA,EAAwB,uBAAA,EAAqB,IAAA,EAAC,gBAAA,EAAgB,MAAA,CAAO,YAAA,CAAa,SAAA,IAAa,KAAK,GAAG,YAAA,EAAY,YAAA,CAAa,KAAA,IAAS,MAAA,EAAW,KAAA,EAAO,YAAA,CAAa,IAAA,GAAO,CAAA,+BAAA,EAAkC,aAAa,IAAI,CAAA,EAAA,CAAA,GAAO,MAAA,EACvQ,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8BAAA,EAAgC,QAAA,EAAA,YAAA,CAAa,SAAQ,CAAA,EAClE;AAAA,KAAA,EACF,CAAA;AAAA,IACC,SAAA,oBAAa,GAAA,CAAC,OAAA,EAAA,EAAM,KAAA,EAAM,gDAAA,EAAiD,qBAAA,EAAoB,OAAA,EAAQ,gBAAA,EAAgB,MAAA,CAAO,SAAA,CAAU,SAAA,IAAa,KAAK,GAAG,YAAA,EAAY,SAAA,CAAU,KAAA,IAAS,MAAA,EAAW,KAAA,EAAO,SAAA,CAAU,IAAA,GAAO,CAAA,4BAAA,EAA+B,UAAU,IAAI,CAAA,EAAA,CAAA,GAAO,MAAA,EAClR,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8BAAA,EAAgC,QAAA,EAAA,SAAA,CAAU,SAAQ,CAAA,EAC/D;AAAA,GAAA,EACF,CAAA;AACF","file":"workbench.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\n/** A collapsible Workbench panel — a side rail or the bottom drawer. */\nexport interface WorkbenchPanel {\n content: SafeHtml;\n /** Whether the panel is currently collapsed (the app owns this). */\n collapsed?: boolean;\n /** Rail width, or drawer height, in px. Overrides the CSS default. */\n size?: number;\n /** Accessible name for the panel region. */\n label?: string;\n}\n\nexport interface WorkbenchProps {\n id: string;\n label: string;\n /** The central work area. */\n main: SafeHtml;\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n bottomDrawer?: WorkbenchPanel;\n className?: string;\n}\n\n/**\n * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and\n * bottom drawer around a central work area (any absent). Collapsing snaps the\n * panel's track to zero in one reflow while its fixed-size content slides out via\n * a composited transform — the instant-width / sliding-content technique, so the\n * work area relayouts once, not per frame. The app owns each `collapsed` flag;\n * the collapse is pure CSS (no wire). See `docs/23-app-layouts.md` §3.3.\n */\nexport function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className = '' }: WorkbenchProps) {\n return <section class={`kui-workbench ${className}`.trim()} id={id} data-component=\"workbench\" aria-label={label}>\n {leftRail && <aside class=\"kui-workbench__rail kui-workbench__rail--left\" data-workbench-rail=\"left\" data-collapsed={String(leftRail.collapsed ?? false)} aria-label={leftRail.label || undefined} style={leftRail.size ? `--kui-workbench-rail-width: ${leftRail.size}px` : undefined}>\n <div class=\"kui-workbench__panel-content\">{leftRail.content}</div>\n </aside>}\n <div class=\"kui-workbench__center\">\n <div class=\"kui-workbench__main\" data-workbench-main>{main}</div>\n {bottomDrawer && <section class=\"kui-workbench__drawer\" data-workbench-drawer data-collapsed={String(bottomDrawer.collapsed ?? false)} aria-label={bottomDrawer.label || undefined} style={bottomDrawer.size ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px` : undefined}>\n <div class=\"kui-workbench__panel-content\">{bottomDrawer.content}</div>\n </section>}\n </div>\n {rightRail && <aside class=\"kui-workbench__rail kui-workbench__rail--right\" data-workbench-rail=\"right\" data-collapsed={String(rightRail.collapsed ?? false)} aria-label={rightRail.label || undefined} style={rightRail.size ? `--kui-workbench-rail-width: ${rightRail.size}px` : undefined}>\n <div class=\"kui-workbench__panel-content\">{rightRail.content}</div>\n </aside>}\n </section>;\n}\n"]}
1
+ {"version":3,"sources":["../src/workbench.tsx"],"names":[],"mappings":";;;AAgCO,SAAS,SAAA,CAAU;AAAA,EACxB,EAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,SAAA,GAAY;AACd,CAAA,EAAmB;AACjB,EAAA,uBACE,IAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,cAAA,EAAiB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACzC,EAAA;AAAA,MACA,gBAAA,EAAe,WAAA;AAAA,MACf,YAAA,EAAY,KAAA;AAAA,MAEX,QAAA,EAAA;AAAA,QAAA,QAAA,oBACC,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,+CAAA;AAAA,YACN,qBAAA,EAAoB,MAAA;AAAA,YACpB,gBAAA,EAAgB,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,KAAK,CAAA;AAAA,YAClD,YAAA,EAAY,SAAS,KAAA,IAAS,MAAA;AAAA,YAC9B,OACE,QAAA,CAAS,IAAA,GACL,CAAA,4BAAA,EAA+B,QAAA,CAAS,IAAI,CAAA,EAAA,CAAA,GAC5C,MAAA;AAAA,YAGN,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8BAAA,EAAgC,mBAAS,OAAA,EAAQ;AAAA;AAAA,SAC9D;AAAA,wBAEF,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,uBAAA,EACT,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,qBAAA,EAAsB,qBAAA,EAAmB,MACjD,QAAA,EAAA,IAAA,EACH,CAAA;AAAA,UACC,YAAA,oBACC,GAAA;AAAA,YAAC,SAAA;AAAA,YAAA;AAAA,cACC,KAAA,EAAM,uBAAA;AAAA,cACN,uBAAA,EAAqB,IAAA;AAAA,cACrB,gBAAA,EAAgB,MAAA,CAAO,YAAA,CAAa,SAAA,IAAa,KAAK,CAAA;AAAA,cACtD,YAAA,EAAY,aAAa,KAAA,IAAS,MAAA;AAAA,cAClC,OACE,YAAA,CAAa,IAAA,GACT,CAAA,+BAAA,EAAkC,YAAA,CAAa,IAAI,CAAA,EAAA,CAAA,GACnD,MAAA;AAAA,cAGN,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8BAAA,EACR,uBAAa,OAAA,EAChB;AAAA;AAAA;AACF,SAAA,EAEJ,CAAA;AAAA,QACC,SAAA,oBACC,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,gDAAA;AAAA,YACN,qBAAA,EAAoB,OAAA;AAAA,YACpB,gBAAA,EAAgB,MAAA,CAAO,SAAA,CAAU,SAAA,IAAa,KAAK,CAAA;AAAA,YACnD,YAAA,EAAY,UAAU,KAAA,IAAS,MAAA;AAAA,YAC/B,OACE,SAAA,CAAU,IAAA,GACN,CAAA,4BAAA,EAA+B,SAAA,CAAU,IAAI,CAAA,EAAA,CAAA,GAC7C,MAAA;AAAA,YAGN,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8BAAA,EAAgC,oBAAU,OAAA,EAAQ;AAAA;AAAA;AAC/D;AAAA;AAAA,GAEJ;AAEJ","file":"workbench.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\n/** A collapsible Workbench panel — a side rail or the bottom drawer. */\nexport interface WorkbenchPanel {\n content: SafeHtml;\n /** Whether the panel is currently collapsed (the app owns this). */\n collapsed?: boolean;\n /** Rail width, or drawer height, in px. Overrides the CSS default. */\n size?: number;\n /** Accessible name for the panel region. */\n label?: string;\n}\n\nexport interface WorkbenchProps {\n id: string;\n label: string;\n /** The central work area. */\n main: SafeHtml;\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n bottomDrawer?: WorkbenchPanel;\n className?: string;\n}\n\n/**\n * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and\n * bottom drawer around a central work area (any absent). Collapsing snaps the\n * panel's track to zero in one reflow while its fixed-size content slides out via\n * a composited transform — the instant-width / sliding-content technique, so the\n * work area relayouts once, not per frame. The app owns each `collapsed` flag;\n * the collapse is pure CSS (no wire). See `docs/23-app-layouts.md` §3.3.\n */\nexport function Workbench({\n id,\n label,\n main,\n leftRail,\n rightRail,\n bottomDrawer,\n className = '',\n}: WorkbenchProps) {\n return (\n <section\n class={`kui-workbench ${className}`.trim()}\n id={id}\n data-component=\"workbench\"\n aria-label={label}\n >\n {leftRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--left\"\n data-workbench-rail=\"left\"\n data-collapsed={String(leftRail.collapsed ?? false)}\n aria-label={leftRail.label || undefined}\n style={\n leftRail.size\n ? `--kui-workbench-rail-width: ${leftRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{leftRail.content}</div>\n </aside>\n )}\n <div class=\"kui-workbench__center\">\n <div class=\"kui-workbench__main\" data-workbench-main>\n {main}\n </div>\n {bottomDrawer && (\n <section\n class=\"kui-workbench__drawer\"\n data-workbench-drawer\n data-collapsed={String(bottomDrawer.collapsed ?? false)}\n aria-label={bottomDrawer.label || undefined}\n style={\n bottomDrawer.size\n ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">\n {bottomDrawer.content}\n </div>\n </section>\n )}\n </div>\n {rightRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--right\"\n data-workbench-rail=\"right\"\n data-collapsed={String(rightRail.collapsed ?? false)}\n aria-label={rightRail.label || undefined}\n style={\n rightRail.size\n ? `--kui-workbench-rail-width: ${rightRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{rightRail.content}</div>\n </aside>\n )}\n </section>\n );\n}\n"]}
@@ -21,13 +21,13 @@ gate the roomier presentations.
21
21
 
22
22
  ## Decision matrix
23
23
 
24
- | Situation | Layout | Device threshold |
25
- | --- | --- | --- |
26
- | Simple app, a few flat sections | `NavStack` with one entry (single pane); add `TabScaffold` for 2–5 co-equal sections on handset | `TabScaffold` on `compact`; promote its tabs to a `Workbench` rail / sidebar `atLeast('desktop')` |
27
- | Drill-down browsing (list → item → sub-item) | `NavStack`; upgrade to `SplitView` once list + detail fit together | `SplitView` two-pane `atLeast('tablet')` landscape / non-`compact`; `NavStack` form on `compact` |
28
- | Two related panes, selecting on the left updates the right | `SplitView` | two panes when not `compact`; collapses to `NavStack` (list → detail) on `compact` |
29
- | Complex tool / editor with peripheral panels (navigator, inspector, console) | `Workbench` | full three-panel `atLeast('desktop')`; on smaller classes present the rails via `NavStack` / overlay drawers, not a shrunken shell |
30
- | Mobile app with 2–5 top-level destinations, each its own drill-down | `TabScaffold`, each tab a `NavStack` | bottom bar on `compact`; promote to a rail / sidebar `atLeast('desktop')` |
24
+ | Situation | Layout | Device threshold |
25
+ | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
26
+ | Simple app, a few flat sections | `NavStack` with one entry (single pane); add `TabScaffold` for 2–5 co-equal sections on handset | `TabScaffold` on `compact`; promote its tabs to a `Workbench` rail / sidebar `atLeast('desktop')` |
27
+ | Drill-down browsing (list → item → sub-item) | `NavStack`; upgrade to `SplitView` once list + detail fit together | `SplitView` two-pane `atLeast('tablet')` landscape / non-`compact`; `NavStack` form on `compact` |
28
+ | Two related panes, selecting on the left updates the right | `SplitView` | two panes when not `compact`; collapses to `NavStack` (list → detail) on `compact` |
29
+ | Complex tool / editor with peripheral panels (navigator, inspector, console) | `Workbench` | full three-panel `atLeast('desktop')`; on smaller classes present the rails via `NavStack` / overlay drawers, not a shrunken shell |
30
+ | Mobile app with 2–5 top-level destinations, each its own drill-down | `TabScaffold`, each tab a `NavStack` | bottom bar on `compact`; promote to a rail / sidebar `atLeast('desktop')` |
31
31
 
32
32
  ### Worked examples
33
33
 
package/docs/catalog.md CHANGED
@@ -7,7 +7,7 @@ you get the same shell without rebuilding it. Like the app layouts, it is a
7
7
  subpath-only, tree-shakeable module that adds nothing to the main barrel.
8
8
 
9
9
  ```bash
10
- npm install @kerfjs/ui # kerfjs is a peer; @kerfjs/ui/select/register is needed only if entries use `related`
10
+ npm install @kerfjs/ui # kerfjs is a peer; @kerfjs/ui/select/register is needed only if entries use `related`
11
11
  ```
12
12
 
13
13
  - `Catalog(props)` returns the shell as `SafeHtml` (a `<main class="kui-catalog">`).
@@ -17,19 +17,20 @@ npm install @kerfjs/ui # kerfjs is a peer; @kerfjs/ui/select/register is neede
17
17
  - `wireCatalog(root, options)` wires the interactions (sidebar selection, the
18
18
  related-entry popup menu, and the collapse/theme toggles) with one delegated
19
19
  listener set and returns a disposer; it can also mirror the active id into the
20
- URL.
20
+ URL and reveal the active sidebar row after a controlled render.
21
21
 
22
22
  ## What you supply
23
23
 
24
24
  - **`sections`** — category-grouped entries: `{ category, entries: [{ id, name,
25
- description?, resources?, related? }] }`. Each entry becomes a sidebar `ListItem`
26
- under a `ListHeader` for its category.
25
+ description?, tags?, resources?, related? }] }`. Each entry becomes a sidebar
26
+ `ListItem` under a `ListHeader` for its category. Short `tags` render as quiet
27
+ trailing pills for decision metadata such as `Discouraged`.
27
28
  - **`content`** — the rendered preview for the active entry. Keep a map of `id →
28
- () => SafeHtml` in your app and call `renderers[active]()` in your render.
29
+ () => SafeHtml` in your app and call `renderers[active]()` in your render.
29
30
  - **`brand`** — `{ title, subtitle?, logoUrl? }` for the sidebar header.
30
31
  - **`secondarySections`** — an optional secondary "ecosystem" group shown below the
31
32
  primary sections with a quieter treatment: `{ label, sections, collapsible?,
32
- expanded? }`. When `collapsible`, the label is a disclosure toggle controlling
33
+ expanded? }`. When `collapsible`, the label is a disclosure toggle controlling
33
34
  `expanded` (the app owns it; wire it with `wireCatalog`'s `onToggleSecondary`).
34
35
  - Optional slots: `headerActions` (extra header controls), `sidebarFooter` (extra
35
36
  sidebar content), and `status` (a footer status line).
@@ -46,7 +47,7 @@ instead of hand-rolled example markup, so labels, notes, and left-edge alignment
46
47
  stay consistent:
47
48
 
48
49
  ```tsx
49
- import { CatalogExample, CatalogExampleStack } from '@kerfjs/ui/catalog';
50
+ import { CatalogExample, CatalogExampleStack } from "@kerfjs/ui/catalog";
50
51
 
51
52
  const buttonPreview = (
52
53
  <CatalogExampleStack label="Button variants">
@@ -71,26 +72,99 @@ content-item/composition that already owns its geometry. The inset is published
71
72
  the `--kui-catalog-example-align` custom property so a debug overlay can exclude it
72
73
  from a specimen's measured margin.
73
74
 
75
+ ## Geometry inspection
76
+
77
+ Pass `geometryOverlay` to `Catalog` when individual component previews should
78
+ show otherwise-invisible geometry. The controlled boolean draws a dashed outer
79
+ bound around specimens with transparent backgrounds and devtools-style orange
80
+ bands over positive margins. Keep the prop present while switching entries so
81
+ `wireCatalog` can reuse one overlay layer; set it to `true` for focused component
82
+ previews and `false` for full compositions whose child geometry should remain
83
+ unmarked.
84
+
85
+ ```tsx
86
+ <Catalog
87
+ {...props}
88
+ geometryOverlay={activeEntry.kind === "component"}
89
+ content={renderers[active.value]()}
90
+ />
91
+ ```
92
+
93
+ Call `wireCatalogGeometryOverlay(root)` after the first render to synchronize the
94
+ opt-in layer across rerenders, theme changes, resizes, and scrolling, and retain
95
+ its disposer alongside `wireCatalog`'s. `CatalogExample` labels and notes are
96
+ excluded; its `align` inset is also subtracted so alignment scaffolding is not
97
+ reported as intrinsic component margin. Put
98
+ `data-catalog-geometry-overlay-skip` on a preview subtree that is intentionally
99
+ explanatory chrome rather than a specimen.
100
+
101
+ Use the overlay together with machine-readable geometry ownership metadata; the
102
+ overlay verifies what is rendered, while metadata tells people and AI tools
103
+ whether the component, its parent, or its children are responsible for margin,
104
+ border, and padding. Catalogs for downstream components should conform to the
105
+ [`component-catalog-extension.schema.json`](../ai/component-catalog-extension.schema.json)
106
+ contract and can start from the checked
107
+ [`component-catalog-extension.json`](./examples/component-catalog-extension.json)
108
+ example; provide those entries beside Kerf's shipped catalog to AI tools.
109
+
110
+ ## Selection reveal
111
+
112
+ Set `revealSelection: true` on `wireCatalog` for a long desktop sidebar. After
113
+ `onSelect` updates controlled state, the helper waits one animation frame, finds
114
+ the exact matching `data-item-id`, and scrolls it into view without changing
115
+ focus. A newer selection or disposal cancels the pending reveal. The default
116
+ media guard is the Catalog desktop layout (`min-width: 52.01rem`), so compact
117
+ layouts keep their existing scroll position.
118
+
119
+ Pass an options object instead of `true` to customize `block`, `inline`,
120
+ `behavior`, or `media`; `media: false` deliberately enables the behavior at all
121
+ sizes. For an initial deep link that did not come through `wireCatalog`, call
122
+ `revealCatalogEntry(app, initialId, { block: "center" })` after the first mount.
123
+
74
124
  ## Complete example
75
125
 
76
126
  ```tsx
77
- import { mount, signal } from 'kerfjs';
78
- import { Catalog, type CatalogSection } from '@kerfjs/ui/catalog';
79
- import { wireCatalog } from '@kerfjs/ui/wire-catalog';
80
- import '@kerfjs/ui/styles.css'; // or import each primitive's CSS + @kerfjs/ui/catalog.css
127
+ import { mount, signal } from "kerfjs";
128
+ import { Catalog, type CatalogSection } from "@kerfjs/ui/catalog";
129
+ import {
130
+ revealCatalogEntry,
131
+ wireCatalog,
132
+ wireCatalogGeometryOverlay,
133
+ } from "@kerfjs/ui/wire-catalog";
134
+ import "@kerfjs/ui/styles.css"; // or import each primitive's CSS + @kerfjs/ui/catalog.css
81
135
 
82
136
  // 1. Describe your components once.
83
137
  const sections: CatalogSection[] = [
84
138
  {
85
- category: 'Controls',
139
+ category: "Controls",
86
140
  entries: [
87
- { id: 'button', name: 'Button', description: 'A pressable control.',
88
- resources: [{ label: 'Source', href: '/src/button.tsx', detail: 'src/button.tsx' }] },
89
- { id: 'field', name: 'Field', description: 'A labeled input.',
90
- related: [{ id: 'button', name: 'Button', group: 'Used with' }] },
141
+ {
142
+ id: "button",
143
+ name: "Button",
144
+ description: "A pressable control.",
145
+ resources: [
146
+ {
147
+ label: "Source",
148
+ href: "/src/button.tsx",
149
+ detail: "src/button.tsx",
150
+ },
151
+ ],
152
+ },
153
+ {
154
+ id: "field",
155
+ name: "Field",
156
+ description: "A labeled input.",
157
+ tags: ["Discouraged"],
158
+ related: [{ id: "button", name: "Button", group: "Used with" }],
159
+ },
160
+ ],
161
+ },
162
+ {
163
+ category: "Feedback",
164
+ entries: [
165
+ { id: "toast", name: "Toast", description: "A transient message." },
91
166
  ],
92
167
  },
93
- { category: 'Feedback', entries: [{ id: 'toast', name: 'Toast', description: 'A transient message.' }] },
94
168
  ];
95
169
 
96
170
  // 2. One preview render per entry id.
@@ -101,32 +175,43 @@ const renderers: Record<string, () => ReturnType<typeof Button>> = {
101
175
  };
102
176
 
103
177
  // 3. App-owned state (domain: which entry; transient: collapsed; global: theme).
104
- const initial = new URLSearchParams(location.search).get('c') ?? sections[0].entries[0].id;
178
+ const initial =
179
+ new URLSearchParams(location.search).get("c") ?? sections[0].entries[0].id;
105
180
  const active = signal(initial);
106
181
  const collapsed = signal(false);
107
- const theme = signal<'light' | 'dark'>('light');
182
+ const theme = signal<"light" | "dark">("light");
108
183
 
109
- const app = document.getElementById('app')!;
184
+ const app = document.getElementById("app")!;
110
185
  mount(app, () => (
111
186
  <Catalog
112
- brand={{ title: 'Acme UI', subtitle: 'Design system' }}
187
+ brand={{ title: "Acme UI", subtitle: "Design system" }}
113
188
  sections={sections}
114
189
  active={active.value}
115
190
  content={renderers[active.value]?.() ?? <></>}
116
191
  collapsed={collapsed.value}
117
192
  theme={theme.value}
193
+ geometryOverlay={true}
118
194
  />
119
195
  ));
120
196
 
121
197
  wireCatalog(app, {
122
- onSelect: (id) => { active.value = id; },
123
- onToggleSidebar: () => { collapsed.value = !collapsed.value; },
198
+ onSelect: (id) => {
199
+ active.value = id;
200
+ },
201
+ onToggleSidebar: () => {
202
+ collapsed.value = !collapsed.value;
203
+ },
124
204
  onToggleTheme: () => {
125
- theme.value = theme.value === 'dark' ? 'light' : 'dark';
205
+ theme.value = theme.value === "dark" ? "light" : "dark";
126
206
  document.documentElement.dataset.theme = theme.value; // apply your theme however you like
127
207
  },
128
- urlParam: 'c', // mirror the active id into ?c=<id>
208
+ urlParam: "c", // mirror the active id into ?c=<id>
209
+ revealSelection: true, // reveal long desktop sidebars without moving focus
129
210
  });
211
+ wireCatalogGeometryOverlay(app);
212
+
213
+ // Optional for an initial deep link whose row may start outside the viewport.
214
+ revealCatalogEntry(app, initial, { block: "center" });
130
215
  ```
131
216
 
132
217
  ## Ownership boundary
@@ -7,7 +7,7 @@ a full shell so you can dock one panel wherever your layout needs it. They are
7
7
  subpath-only, tree-shakeable modules that add nothing to the main barrel.
8
8
 
9
9
  ```bash
10
- npm install @kerfjs/ui # kerfjs is a peer
10
+ npm install @kerfjs/ui # kerfjs is a peer
11
11
  ```
12
12
 
13
13
  Import the panel CSS (`@kerfjs/ui/collapsible-panel.css`) alongside `foundation.css`.
@@ -46,33 +46,60 @@ Import the panel CSS (`@kerfjs/ui/collapsible-panel.css`) alongside `foundation.
46
46
  ## Example
47
47
 
48
48
  ```tsx
49
- import { signal, mount } from 'kerfjs';
50
- import { deviceClass } from '@kerfjs/ui/device-class';
51
- import { CollapsiblePanel, CollapsiblePanelToggle } from '@kerfjs/ui/collapsible-panel';
52
- import { wireSidebar } from '@kerfjs/ui/wire-sidebar';
53
- import '@kerfjs/ui/collapsible-panel.css';
49
+ import { signal, mount } from "kerfjs";
50
+ import { deviceClass } from "@kerfjs/ui/device-class";
51
+ import {
52
+ CollapsiblePanel,
53
+ CollapsiblePanelToggle,
54
+ } from "@kerfjs/ui/collapsible-panel";
55
+ import { wireSidebar } from "@kerfjs/ui/wire-sidebar";
56
+ import "@kerfjs/ui/collapsible-panel.css";
54
57
 
55
58
  const navCollapsed = signal(false);
56
59
  const device = deviceClass();
57
60
 
58
- const app = document.querySelector('#app')!;
61
+ const app = document.querySelector("#app")!;
59
62
  mount(app, () => (
60
63
  <div class="layout">
61
- <CollapsiblePanel id="nav" side="left" collapsed={navCollapsed.value} label="Navigator">
64
+ <CollapsiblePanel
65
+ id="nav"
66
+ side="left"
67
+ collapsed={navCollapsed.value}
68
+ label="Navigator"
69
+ >
62
70
  <header>
63
- <CollapsiblePanelToggle side="left" collapsed={navCollapsed.value} action="toggle-nav" panelId="nav" />
71
+ <CollapsiblePanelToggle
72
+ side="left"
73
+ collapsed={navCollapsed.value}
74
+ action="toggle-nav"
75
+ panelId="nav"
76
+ />
64
77
  </header>
65
78
  {/* nav items */}
66
79
  </CollapsiblePanel>
67
80
  <main>
68
- {navCollapsed.value && <CollapsiblePanelToggle side="left" collapsed action="toggle-nav" label="Show navigator" />}
81
+ {navCollapsed.value && (
82
+ <CollapsiblePanelToggle
83
+ side="left"
84
+ collapsed
85
+ action="toggle-nav"
86
+ label="Show navigator"
87
+ />
88
+ )}
69
89
  {/* content */}
70
90
  </main>
71
91
  </div>
72
92
  ));
73
93
 
74
94
  const stop = wireSidebar(app, {
75
- panels: [{ id: 'nav', collapsed: navCollapsed, toggleAction: 'toggle-nav', storageKey: 'app.nav-collapsed' }],
95
+ panels: [
96
+ {
97
+ id: "nav",
98
+ collapsed: navCollapsed,
99
+ toggleAction: "toggle-nav",
100
+ storageKey: "app.nav-collapsed",
101
+ },
102
+ ],
76
103
  deviceClass: device,
77
104
  });
78
105
  ```
@@ -20,6 +20,34 @@ remains authored rather than generated.
20
20
 
21
21
  ## Ownership boundaries
22
22
 
23
+ ### Geometry ownership metadata
24
+
25
+ The canonical [`component-catalog.json`](../ai/component-catalog.json) records
26
+ the margin, border, and padding owner for every component and composition under
27
+ `geometry`. Read it before adding a wrapper or local spacing rule:
28
+
29
+ - `self` means the entry's public visual contract supplies that geometry.
30
+ - `parent` means the embedding layout must supply it.
31
+ - `child` means composed descendants supply it; the entry's outer shell does not.
32
+ - `none` means that dimension is intentionally absent from the standard contract.
33
+ - `conditional` means a variant, part, or configuration changes ownership; the
34
+ accompanying `notes` explain the boundary.
35
+
36
+ The values describe public ownership, including themed shadow parts, rather
37
+ than merely asking whether a CSS declaration appears on the host element.
38
+ Recipes are exempt because they arrange entries whose individual ownership is
39
+ already cataloged.
40
+
41
+ Downstream packages and applications should publish the same contract for their
42
+ reusable visual components. Use
43
+ [`component-catalog-extension.schema.json`](../ai/component-catalog-extension.schema.json)
44
+ and copy the structure of the checked
45
+ [`component-catalog-extension.json`](./examples/component-catalog-extension.json)
46
+ example. Keep app and Kerf entries as package-qualified inputs, then let people
47
+ or AI tools search their combined entries and compare `geometry` before adding
48
+ wrappers or insets. Do not add app-owned entries to Kerf's canonical catalog or
49
+ claim Kerf delivery paths for them.
50
+
23
51
  - Components own semantic markup, stable anatomy, documented variants, ARIA projection, and package CSS hooks.
24
52
  - Applications own signals/stores, product copy, domain-state mapping, persistence, routing, permissions, and transport.
25
53
  - Actions are `data-action` strings. Wire them at a stable root with `delegate()` or `delegateActions()` and retain the disposer.
@@ -166,14 +194,14 @@ render, hands in its own via `collapsible.signals`, drives it through
166
194
  UI is consistent by default without every app reinventing it.
167
195
 
168
196
  `wireTokenSearchFields` is a deliberate exception, not the rule for `wire…`
169
- helpers. Its collapse behavior was *rich and error-prone* — reveal, focus
197
+ helpers. Its collapse behavior was _rich and error-prone_ — reveal, focus
170
198
  transfer, Escape, empty-blur collapse, focus return — the kind of transient chrome
171
199
  apps kept reimplementing inconsistently, so the helper owns it. Everywhere else the
172
200
  app's state is **domain or persisted, not transient chrome, and stays app-owned**: a
173
201
  `NavStack`'s view stack is navigation history, a `TabBar`/`TabScaffold`'s selection
174
202
  and tab order are data, a `ResizableRegion`'s committed size and a
175
203
  `Workbench`/`SplitView` rail's `collapsed` flag are persisted layout preferences.
176
- Each helper already owns only the *ephemeral mechanics* around that state —
204
+ Each helper already owns only the _ephemeral mechanics_ around that state —
177
205
  `wireNavStack` the push/pop animation, `wireTabBars` the overflow autoscroll and
178
206
  drag preview, `wireResizableRegions` the live drag preview — and reports committed
179
207
  changes through callbacks. A `ListHeader` `toggle` disclosure's `expanded` is