@kerfjs/ui 5.0.0-beta.9 → 5.0.0-beta.90

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 (802) hide show
  1. package/README.md +540 -132
  2. package/ai/application-ui-diagnostic-ids-v1.json +113 -0
  3. package/ai/application-ui-profile-sync.cjs +548 -0
  4. package/ai/application-ui-profile.d.mts +1 -0
  5. package/ai/application-ui-profile.d.ts +106 -0
  6. package/ai/application-ui-profile.defaults.json +96 -0
  7. package/ai/application-ui-profile.mjs +653 -0
  8. package/ai/application-ui-profile.schema.json +140 -0
  9. package/ai/catalog-authoring.json +34 -0
  10. package/ai/catalog-authoring.schema.json +105 -0
  11. package/ai/compile-time-contracts-v1.json +149 -0
  12. package/ai/compile-time-contracts-v1.schema.json +52 -0
  13. package/ai/component-catalog-extension.schema.json +104 -0
  14. package/ai/component-catalog.json +4487 -633
  15. package/ai/component-catalog.schema.json +94 -1
  16. package/ai/component-class-owners.cjs +83 -0
  17. package/ai/component-composition-extension.d.ts +7 -0
  18. package/ai/component-composition-extension.schema.json +7 -0
  19. package/ai/component-composition-rules.json +520 -0
  20. package/ai/component-composition.d.ts +129 -0
  21. package/ai/component-composition.json +7470 -0
  22. package/ai/component-composition.schema.json +408 -0
  23. package/ai/components/README.md +172 -0
  24. package/ai/components/anchored-surface.md +86 -0
  25. package/ai/components/application-tabs.md +65 -0
  26. package/ai/components/badge.md +72 -0
  27. package/ai/components/chip.md +72 -0
  28. package/ai/components/collapsible-panel.md +102 -0
  29. package/ai/components/content-item.md +96 -0
  30. package/ai/components/disclosure-arrow.md +77 -0
  31. package/ai/components/document-baseline.md +75 -0
  32. package/ai/components/empty-state.md +80 -0
  33. package/ai/components/feedback.md +71 -0
  34. package/ai/components/floating-toolbar.md +82 -0
  35. package/ai/components/foundation.md +65 -0
  36. package/ai/components/grid.md +100 -0
  37. package/ai/components/headers.md +63 -0
  38. package/ai/components/layout.md +75 -0
  39. package/ai/components/list-action-row.md +91 -0
  40. package/ai/components/list-header.md +91 -0
  41. package/ai/components/list-inset-control.md +74 -0
  42. package/ai/components/list-inset-text.md +72 -0
  43. package/ai/components/list-item.md +94 -0
  44. package/ai/components/list.md +106 -0
  45. package/ai/components/loading-spinner.md +73 -0
  46. package/ai/components/lucide-icon.md +97 -0
  47. package/ai/components/nav-stack.md +105 -0
  48. package/ai/components/pane.md +106 -0
  49. package/ai/components/popup-menu.md +171 -0
  50. package/ai/components/recipe-app-shell.md +57 -0
  51. package/ai/components/recipe-collapsible-sidebar.md +57 -0
  52. package/ai/components/recipe-compact-toolbar.md +57 -0
  53. package/ai/components/recipe-composer-form.md +57 -0
  54. package/ai/components/recipe-list-detail-dialog.md +57 -0
  55. package/ai/components/recipe-list-workspace-states.md +57 -0
  56. package/ai/components/recipe-loading-inspector.md +57 -0
  57. package/ai/components/recipe-navigation-sidebar.md +57 -0
  58. package/ai/components/recipe-navigation-stack.md +57 -0
  59. package/ai/components/recipe-workspace-header.md +57 -0
  60. package/ai/components/resize.md +106 -0
  61. package/ai/components/row.md +96 -0
  62. package/ai/components/segmented-control.md +82 -0
  63. package/ai/components/select.md +115 -0
  64. package/ai/components/skeleton.md +84 -0
  65. package/ai/components/spacer.md +86 -0
  66. package/ai/components/split-view.md +103 -0
  67. package/ai/components/state-banner.md +94 -0
  68. package/ai/components/sunken-panel.md +79 -0
  69. package/ai/components/surface-scaffold.md +87 -0
  70. package/ai/components/tab-bar.md +113 -0
  71. package/ai/components/tab-navigator.md +96 -0
  72. package/ai/components/tabs.md +95 -0
  73. package/ai/components/text.md +87 -0
  74. package/ai/components/token-search-field.md +107 -0
  75. package/ai/components/toolbar-control-group.md +121 -0
  76. package/ai/components/toolbar-text.md +98 -0
  77. package/ai/components/toolbar.md +101 -0
  78. package/ai/components/value-table.md +73 -0
  79. package/ai/components/wa-accordion-item.md +55 -0
  80. package/ai/components/wa-accordion.md +65 -0
  81. package/ai/components/wa-animated-image.md +55 -0
  82. package/ai/components/wa-animation.md +55 -0
  83. package/ai/components/wa-avatar.md +55 -0
  84. package/ai/components/wa-badge.md +55 -0
  85. package/ai/components/wa-breadcrumb-item.md +53 -0
  86. package/ai/components/wa-breadcrumb.md +55 -0
  87. package/ai/components/wa-button-group.md +59 -0
  88. package/ai/components/wa-button.md +55 -0
  89. package/ai/components/wa-callout.md +58 -0
  90. package/ai/components/wa-card.md +59 -0
  91. package/ai/components/wa-carousel-item.md +53 -0
  92. package/ai/components/wa-carousel.md +55 -0
  93. package/ai/components/wa-checkbox-group.md +55 -0
  94. package/ai/components/wa-checkbox.md +55 -0
  95. package/ai/components/wa-color-picker.md +55 -0
  96. package/ai/components/wa-comparison.md +55 -0
  97. package/ai/components/wa-copy-button.md +55 -0
  98. package/ai/components/wa-details.md +61 -0
  99. package/ai/components/wa-dialog.md +59 -0
  100. package/ai/components/wa-divider.md +53 -0
  101. package/ai/components/wa-drawer.md +55 -0
  102. package/ai/components/wa-dropdown-item.md +60 -0
  103. package/ai/components/wa-dropdown.md +60 -0
  104. package/ai/components/wa-format-bytes.md +53 -0
  105. package/ai/components/wa-format-date.md +53 -0
  106. package/ai/components/wa-format-number.md +53 -0
  107. package/ai/components/wa-icon.md +58 -0
  108. package/ai/components/wa-include.md +53 -0
  109. package/ai/components/wa-input.md +59 -0
  110. package/ai/components/wa-intersection-observer.md +53 -0
  111. package/ai/components/wa-known-date.md +53 -0
  112. package/ai/components/wa-markdown.md +53 -0
  113. package/ai/components/wa-mutation-observer.md +53 -0
  114. package/ai/components/wa-number-input.md +55 -0
  115. package/ai/components/wa-option.md +59 -0
  116. package/ai/components/wa-otp-input.md +53 -0
  117. package/ai/components/wa-page.md +53 -0
  118. package/ai/components/wa-pagination.md +55 -0
  119. package/ai/components/wa-popover.md +55 -0
  120. package/ai/components/wa-popup.md +53 -0
  121. package/ai/components/wa-progress-bar.md +53 -0
  122. package/ai/components/wa-progress-ring.md +53 -0
  123. package/ai/components/wa-qr-code.md +53 -0
  124. package/ai/components/wa-radio-group.md +59 -0
  125. package/ai/components/wa-radio.md +55 -0
  126. package/ai/components/wa-random-content.md +53 -0
  127. package/ai/components/wa-rating.md +55 -0
  128. package/ai/components/wa-relative-time.md +53 -0
  129. package/ai/components/wa-resize-observer.md +53 -0
  130. package/ai/components/wa-scroller.md +53 -0
  131. package/ai/components/wa-select.md +63 -0
  132. package/ai/components/wa-skeleton.md +53 -0
  133. package/ai/components/wa-slider.md +55 -0
  134. package/ai/components/wa-spinner.md +58 -0
  135. package/ai/components/wa-split-panel.md +58 -0
  136. package/ai/components/wa-switch.md +53 -0
  137. package/ai/components/wa-tab-group.md +69 -0
  138. package/ai/components/wa-tab-panel.md +60 -0
  139. package/ai/components/wa-tab.md +59 -0
  140. package/ai/components/wa-tag.md +59 -0
  141. package/ai/components/wa-textarea.md +53 -0
  142. package/ai/components/wa-time-input.md +55 -0
  143. package/ai/components/wa-toast-item.md +60 -0
  144. package/ai/components/wa-toast.md +60 -0
  145. package/ai/components/wa-tooltip.md +55 -0
  146. package/ai/components/wa-tree-item.md +55 -0
  147. package/ai/components/wa-tree.md +55 -0
  148. package/ai/components/wa-zoomable-frame.md +55 -0
  149. package/ai/components/workbench.md +119 -0
  150. package/ai/public-api-signatures-v1.md +2835 -351
  151. package/ai/skill.md +401 -63
  152. package/ai/webawesome-jsx-signatures-v1.md +9 -2
  153. package/analyzer/cli.mjs +49 -0
  154. package/analyzer/component-ownership.d.mts +72 -0
  155. package/analyzer/component-ownership.mjs +429 -0
  156. package/analyzer/index.d.mts +53 -0
  157. package/analyzer/index.mjs +2346 -0
  158. package/analyzer/loud-pairs.d.mts +49 -0
  159. package/analyzer/loud-pairs.mjs +357 -0
  160. package/analyzer/report.schema.json +66 -0
  161. package/analyzer/selectors.d.mts +21 -0
  162. package/analyzer/selectors.mjs +212 -0
  163. package/catalog-projection-cli.mjs +96 -0
  164. package/catalog-projection.d.mts +55 -0
  165. package/catalog-projection.mjs +62 -0
  166. package/dist/anchored-surface.d.ts +30 -0
  167. package/dist/anchored-surface.js +3 -0
  168. package/dist/anchored-surface.js.map +1 -0
  169. package/dist/app-tab.d.ts +39 -4
  170. package/dist/app-tab.js +2 -1
  171. package/dist/badge.d.ts +54 -0
  172. package/dist/badge.js +3 -0
  173. package/dist/badge.js.map +1 -0
  174. package/dist/browser/anchored-surface.js +3 -0
  175. package/dist/browser/badge.js +3 -0
  176. package/dist/browser/catalog.js +28 -0
  177. package/dist/browser/chip.js +3 -0
  178. package/dist/browser/collapsible-panel.js +9 -0
  179. package/dist/browser/content-item.js +3 -0
  180. package/dist/browser/floating-toolbar.js +3 -0
  181. package/dist/browser/grid.js +3 -0
  182. package/dist/browser/list-action-row.js +1 -0
  183. package/dist/browser/list-header.js +3 -1
  184. package/dist/browser/list-inset-control.js +3 -0
  185. package/dist/browser/list-inset-text.js +3 -0
  186. package/dist/browser/list-item.js +1 -0
  187. package/dist/browser/list.js +3 -0
  188. package/dist/browser/nav-stack.js +11 -0
  189. package/dist/browser/pane.js +3 -0
  190. package/dist/browser/popup-menu.js +3 -0
  191. package/dist/browser/row.js +3 -0
  192. package/dist/browser/segmented-control.js +0 -1
  193. package/dist/browser/select.js +2 -1
  194. package/dist/browser/spacer.js +3 -0
  195. package/dist/browser/split-view.js +13 -0
  196. package/dist/browser/state-banner.js +1 -0
  197. package/dist/browser/sunken-panel.js +3 -0
  198. package/dist/browser/surface-scaffold.js +3 -0
  199. package/dist/browser/tab-navigator.js +4 -0
  200. package/dist/browser/text.js +3 -0
  201. package/dist/browser/toolbar-control-group.js +1 -0
  202. package/dist/browser/workbench.js +14 -0
  203. package/dist/catalog-resources.d.ts +48 -0
  204. package/dist/catalog-resources.js +22 -0
  205. package/dist/catalog-resources.js.map +1 -0
  206. package/dist/catalog.d.ts +76 -103
  207. package/dist/catalog.js +566 -87
  208. package/dist/catalog.js.map +1 -1
  209. package/dist/chip.d.ts +36 -0
  210. package/dist/chip.js +3 -0
  211. package/dist/chip.js.map +1 -0
  212. package/dist/chunk-2GOCOLQL.js +270 -0
  213. package/dist/chunk-2GOCOLQL.js.map +1 -0
  214. package/dist/{chunk-56RJXPHG.js → chunk-2YPPNFYT.js} +7 -4
  215. package/dist/chunk-2YPPNFYT.js.map +1 -0
  216. package/dist/chunk-3CXRQAYT.js +288 -0
  217. package/dist/chunk-3CXRQAYT.js.map +1 -0
  218. package/dist/chunk-3DXHNLME.js +69 -0
  219. package/dist/chunk-3DXHNLME.js.map +1 -0
  220. package/dist/chunk-3PO7JEK7.js +188 -0
  221. package/dist/chunk-3PO7JEK7.js.map +1 -0
  222. package/dist/chunk-3XPGJUUQ.js +37 -0
  223. package/dist/chunk-3XPGJUUQ.js.map +1 -0
  224. package/dist/chunk-4Y4L5YEI.js +66 -0
  225. package/dist/chunk-4Y4L5YEI.js.map +1 -0
  226. package/dist/chunk-5BZBIRQG.js +452 -0
  227. package/dist/chunk-5BZBIRQG.js.map +1 -0
  228. package/dist/chunk-6IVNMC6R.js +19 -0
  229. package/dist/chunk-6IVNMC6R.js.map +1 -0
  230. package/dist/chunk-7QQ3HE47.js +17 -0
  231. package/dist/chunk-7QQ3HE47.js.map +1 -0
  232. package/dist/chunk-A7DYEERU.js +59 -0
  233. package/dist/chunk-A7DYEERU.js.map +1 -0
  234. package/dist/chunk-AN5IWMTM.js +24 -0
  235. package/dist/chunk-AN5IWMTM.js.map +1 -0
  236. package/dist/chunk-B3TROLC2.js +104 -0
  237. package/dist/chunk-B3TROLC2.js.map +1 -0
  238. package/dist/chunk-BYHGRKC3.js +44 -0
  239. package/dist/chunk-BYHGRKC3.js.map +1 -0
  240. package/dist/chunk-CD5TOO22.js +82 -0
  241. package/dist/chunk-CD5TOO22.js.map +1 -0
  242. package/dist/chunk-CEJ5FK6G.js +288 -0
  243. package/dist/chunk-CEJ5FK6G.js.map +1 -0
  244. package/dist/chunk-DL55BSQX.js +52 -0
  245. package/dist/chunk-DL55BSQX.js.map +1 -0
  246. package/dist/chunk-DQDTZE4W.js +70 -0
  247. package/dist/chunk-DQDTZE4W.js.map +1 -0
  248. package/dist/chunk-FL6J644M.js +116 -0
  249. package/dist/chunk-FL6J644M.js.map +1 -0
  250. package/dist/chunk-GPCAXYMT.js +165 -0
  251. package/dist/chunk-GPCAXYMT.js.map +1 -0
  252. package/dist/chunk-GS2QD3N7.js +14 -0
  253. package/dist/chunk-GS2QD3N7.js.map +1 -0
  254. package/dist/chunk-H45DAZBL.js +491 -0
  255. package/dist/chunk-H45DAZBL.js.map +1 -0
  256. package/dist/chunk-IM3GKYOG.js +202 -0
  257. package/dist/chunk-IM3GKYOG.js.map +1 -0
  258. package/dist/chunk-IOS2NXW7.js +53 -0
  259. package/dist/chunk-IOS2NXW7.js.map +1 -0
  260. package/dist/chunk-J3AWE5I4.js +50 -0
  261. package/dist/chunk-J3AWE5I4.js.map +1 -0
  262. package/dist/chunk-JOR5LGYE.js +55 -0
  263. package/dist/chunk-JOR5LGYE.js.map +1 -0
  264. package/dist/chunk-KAJVVN7Q.js +138 -0
  265. package/dist/chunk-KAJVVN7Q.js.map +1 -0
  266. package/dist/chunk-KL3W6FUN.js +506 -0
  267. package/dist/chunk-KL3W6FUN.js.map +1 -0
  268. package/dist/{chunk-7BR3DO2A.js → chunk-LAZLBC2Q.js} +14 -5
  269. package/dist/chunk-LAZLBC2Q.js.map +1 -0
  270. package/dist/chunk-LFPW4YC6.js +222 -0
  271. package/dist/chunk-LFPW4YC6.js.map +1 -0
  272. package/dist/chunk-MN3POJFN.js +44 -0
  273. package/dist/chunk-MN3POJFN.js.map +1 -0
  274. package/dist/chunk-MORVLMDG.js +59 -0
  275. package/dist/chunk-MORVLMDG.js.map +1 -0
  276. package/dist/chunk-MTCTX25G.js +139 -0
  277. package/dist/chunk-MTCTX25G.js.map +1 -0
  278. package/dist/chunk-MXNIVEVL.js +48 -0
  279. package/dist/chunk-MXNIVEVL.js.map +1 -0
  280. package/dist/chunk-NJZMR7HM.js +135 -0
  281. package/dist/chunk-NJZMR7HM.js.map +1 -0
  282. package/dist/chunk-NMWDHZVM.js +35 -0
  283. package/dist/chunk-NMWDHZVM.js.map +1 -0
  284. package/dist/chunk-NP3FSLNX.js +58 -0
  285. package/dist/chunk-NP3FSLNX.js.map +1 -0
  286. package/dist/{chunk-YGN7OMLT.js → chunk-NS2TBYM4.js} +8 -5
  287. package/dist/chunk-NS2TBYM4.js.map +1 -0
  288. package/dist/chunk-O27D24CA.js +250 -0
  289. package/dist/chunk-O27D24CA.js.map +1 -0
  290. package/dist/chunk-PGL6TXFW.js +48 -0
  291. package/dist/chunk-PGL6TXFW.js.map +1 -0
  292. package/dist/chunk-QQICWWNX.js +71 -0
  293. package/dist/chunk-QQICWWNX.js.map +1 -0
  294. package/dist/chunk-S3ECMF23.js +33 -0
  295. package/dist/chunk-S3ECMF23.js.map +1 -0
  296. package/dist/chunk-S4ZGTDSG.js +18 -0
  297. package/dist/chunk-S4ZGTDSG.js.map +1 -0
  298. package/dist/chunk-SJW5BCEQ.js +174 -0
  299. package/dist/chunk-SJW5BCEQ.js.map +1 -0
  300. package/dist/chunk-TTGIHMDR.js +149 -0
  301. package/dist/chunk-TTGIHMDR.js.map +1 -0
  302. package/dist/chunk-U657IHFB.js +158 -0
  303. package/dist/chunk-U657IHFB.js.map +1 -0
  304. package/dist/chunk-VLGQIYZ5.js +295 -0
  305. package/dist/chunk-VLGQIYZ5.js.map +1 -0
  306. package/dist/chunk-XK4E225T.js +34 -0
  307. package/dist/chunk-XK4E225T.js.map +1 -0
  308. package/dist/chunk-XKZPBRU7.js +135 -0
  309. package/dist/chunk-XKZPBRU7.js.map +1 -0
  310. package/dist/chunk-XNXKIPU5.js +779 -0
  311. package/dist/chunk-XNXKIPU5.js.map +1 -0
  312. package/dist/chunk-Y43B6DS3.js +66 -0
  313. package/dist/chunk-Y43B6DS3.js.map +1 -0
  314. package/dist/chunk-Y6KT7ZTR.js +109 -0
  315. package/dist/chunk-Y6KT7ZTR.js.map +1 -0
  316. package/dist/chunk-YOKEL75M.js +48 -0
  317. package/dist/chunk-YOKEL75M.js.map +1 -0
  318. package/dist/collapsible-panel.d.ts +137 -0
  319. package/dist/collapsible-panel.js +136 -0
  320. package/dist/collapsible-panel.js.map +1 -0
  321. package/dist/content-item.d.ts +99 -0
  322. package/dist/content-item.js +4 -0
  323. package/dist/content-item.js.map +1 -0
  324. package/dist/css-values.d.ts +119 -0
  325. package/dist/css-values.js +3 -0
  326. package/dist/css-values.js.map +1 -0
  327. package/dist/device-class.js +34 -6
  328. package/dist/device-class.js.map +1 -1
  329. package/dist/disclosure-arrow.d.ts +6 -1
  330. package/dist/disclosure-arrow.js +2 -1
  331. package/dist/empty-state.d.ts +5 -2
  332. package/dist/empty-state.js +2 -1
  333. package/dist/flex-alignment-4ms8ZbV8.d.ts +5 -0
  334. package/dist/floating-toolbar.d.ts +51 -0
  335. package/dist/floating-toolbar.js +3 -0
  336. package/dist/floating-toolbar.js.map +1 -0
  337. package/dist/grid.d.ts +58 -0
  338. package/dist/grid.js +6 -0
  339. package/dist/grid.js.map +1 -0
  340. package/dist/help-tags-register.d.ts +13 -0
  341. package/dist/help-tags-register.js +9 -0
  342. package/dist/help-tags-register.js.map +1 -0
  343. package/dist/index.d.ts +41 -17
  344. package/dist/index.js +46 -23
  345. package/dist/list-action-row.d.ts +15 -3
  346. package/dist/list-action-row.js +4 -1
  347. package/dist/list-header.d.ts +65 -10
  348. package/dist/list-header.js +4 -1
  349. package/dist/list-inset-control.d.ts +22 -0
  350. package/dist/list-inset-control.js +3 -0
  351. package/dist/list-inset-control.js.map +1 -0
  352. package/dist/list-inset-text.d.ts +30 -0
  353. package/dist/list-inset-text.js +4 -0
  354. package/dist/list-inset-text.js.map +1 -0
  355. package/dist/list-item.d.ts +34 -5
  356. package/dist/list-item.js +4 -1
  357. package/dist/list.d.ts +64 -0
  358. package/dist/list.js +7 -0
  359. package/dist/list.js.map +1 -0
  360. package/dist/loading-spinner.d.ts +12 -2
  361. package/dist/loading-spinner.js +2 -1
  362. package/dist/lucide-icon.d.ts +14 -2
  363. package/dist/lucide-icon.js +2 -1
  364. package/dist/nav-stack-BvSKY448.d.ts +109 -0
  365. package/dist/nav-stack.d.ts +13 -38
  366. package/dist/nav-stack.js +11 -1
  367. package/dist/pane.d.ts +116 -0
  368. package/dist/pane.js +4 -0
  369. package/dist/pane.js.map +1 -0
  370. package/dist/panel-toolbar-CL9tYOkh.d.ts +72 -0
  371. package/dist/popup-menu-register.d.ts +9 -0
  372. package/dist/popup-menu-register.js +244 -0
  373. package/dist/popup-menu-register.js.map +1 -0
  374. package/dist/popup-menu.d.ts +100 -0
  375. package/dist/popup-menu.js +3 -0
  376. package/dist/popup-menu.js.map +1 -0
  377. package/dist/resizable-region.d.ts +32 -3
  378. package/dist/resizable-region.js +1 -1
  379. package/dist/row.d.ts +49 -0
  380. package/dist/row.js +7 -0
  381. package/dist/row.js.map +1 -0
  382. package/dist/segmented-control.d.ts +12 -9
  383. package/dist/segmented-control.js +1 -2
  384. package/dist/select-register.d.ts +5 -1
  385. package/dist/select-register.js +247 -3
  386. package/dist/select-register.js.map +1 -1
  387. package/dist/select.d.ts +103 -8
  388. package/dist/select.js +3 -1
  389. package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
  390. package/dist/semantic-tone-CdW013KF.d.ts +4 -0
  391. package/dist/sides-BPSWde0A.d.ts +4 -0
  392. package/dist/skeleton.d.ts +14 -7
  393. package/dist/skeleton.js +2 -1
  394. package/dist/spacer.d.ts +18 -0
  395. package/dist/spacer.js +4 -0
  396. package/dist/spacer.js.map +1 -0
  397. package/dist/split-view.d.ts +43 -6
  398. package/dist/split-view.js +125 -10
  399. package/dist/split-view.js.map +1 -1
  400. package/dist/state-banner.d.ts +21 -7
  401. package/dist/state-banner.js +3 -1
  402. package/dist/styles/anchored-surface.css +15 -0
  403. package/dist/styles/app-tab.css +232 -0
  404. package/dist/styles/badge.css +118 -0
  405. package/dist/styles/catalog/components/catalog-example-stack.css +9 -0
  406. package/dist/styles/catalog/components/catalog-example.css +167 -0
  407. package/dist/styles/catalog/components/catalog-stage.css +54 -0
  408. package/dist/styles/catalog/components/catalog.css +26 -0
  409. package/dist/styles/catalog.css +5 -482
  410. package/dist/styles/chip.css +138 -0
  411. package/dist/styles/collapsible-panel.css +580 -0
  412. package/dist/styles/components/actions/floating-toolbar/floating-toolbar.css +119 -0
  413. package/dist/styles/components/actions/popup-menu/popup-menu.css +285 -0
  414. package/dist/styles/components/actions/toolbar/toolbar.css +417 -0
  415. package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +891 -0
  416. package/dist/styles/components/actions/toolbar-text/toolbar-text.css +171 -0
  417. package/dist/styles/components/collections/list/list.css +136 -0
  418. package/dist/styles/components/collections/list-action-row/list-action-row.css +258 -0
  419. package/dist/styles/components/collections/list-header/list-header.css +229 -0
  420. package/dist/styles/components/collections/list-inset-control/list-inset-control.css +30 -0
  421. package/dist/styles/components/collections/list-inset-text/list-inset-text.css +29 -0
  422. package/dist/styles/components/collections/list-item/list-item.css +255 -0
  423. package/dist/styles/components/data-display/value-table/value-table.css +107 -0
  424. package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +15 -0
  425. package/dist/styles/components/feedback/badge/badge.css +118 -0
  426. package/dist/styles/components/feedback/chip/chip.css +138 -0
  427. package/dist/styles/components/feedback/empty-state/empty-state.css +47 -0
  428. package/dist/styles/components/feedback/loading-spinner/loading-spinner.css +25 -0
  429. package/dist/styles/components/feedback/skeleton/skeleton.css +92 -0
  430. package/dist/styles/components/feedback/state-banner/state-banner.css +248 -0
  431. package/dist/styles/components/forms/segmented-control/segmented-control.css +196 -0
  432. package/dist/styles/components/forms/select/select.css +454 -0
  433. package/dist/styles/components/forms/token-search-field/token-search-field.css +392 -0
  434. package/dist/styles/components/layout/collapsible-panel/collapsible-panel.css +580 -0
  435. package/dist/styles/components/layout/grid/grid.css +88 -0
  436. package/dist/styles/components/layout/pane/pane.css +309 -0
  437. package/dist/styles/components/layout/resizable-region/resizable-region.css +545 -0
  438. package/dist/styles/components/layout/row/row.css +114 -0
  439. package/dist/styles/components/layout/spacer/spacer.css +11 -0
  440. package/dist/styles/components/layout/split-view/split-view.css +146 -0
  441. package/dist/styles/components/layout/workbench/workbench.css +1092 -0
  442. package/dist/styles/components/media/lucide-icon/lucide-icon.css +15 -0
  443. package/dist/styles/components/navigation/app-tab/app-tab.css +384 -0
  444. package/dist/styles/components/navigation/disclosure-arrow/disclosure-arrow.css +23 -0
  445. package/dist/styles/components/navigation/nav-stack/nav-stack.css +258 -0
  446. package/dist/styles/components/navigation/tab-bar/tab-bar.css +186 -0
  447. package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +253 -0
  448. package/dist/styles/components/surfaces/content-item/content-item.css +120 -0
  449. package/dist/styles/components/surfaces/sunken-panel/sunken-panel.css +34 -0
  450. package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +111 -0
  451. package/dist/styles/components/typography/text/text.css +158 -0
  452. package/dist/styles/content-item.css +120 -0
  453. package/dist/styles/document.css +33 -0
  454. package/dist/styles/floating-toolbar.css +119 -0
  455. package/dist/styles/foundation.css +187 -45
  456. package/dist/styles/grid.css +88 -0
  457. package/dist/styles/layout.css +3 -41
  458. package/dist/styles/list-action-row.css +104 -6
  459. package/dist/styles/list-header.css +128 -59
  460. package/dist/styles/list-inset-control.css +30 -0
  461. package/dist/styles/list-inset-text.css +29 -0
  462. package/dist/styles/list-item.css +165 -33
  463. package/dist/styles/list.css +136 -0
  464. package/dist/styles/loading-spinner.css +4 -0
  465. package/dist/styles/lucide-icon.css +9 -0
  466. package/dist/styles/nav-stack.css +196 -50
  467. package/dist/styles/pane.css +309 -0
  468. package/dist/styles/popup-menu.css +285 -0
  469. package/dist/styles/resizable-region.css +438 -4
  470. package/dist/styles/row.css +114 -0
  471. package/dist/styles/segmented-control.css +36 -9
  472. package/dist/styles/select.css +337 -21
  473. package/dist/styles/skeleton.css +64 -3
  474. package/dist/styles/spacer.css +11 -0
  475. package/dist/styles/split-view.css +114 -3
  476. package/dist/styles/state-banner.css +94 -19
  477. package/dist/styles/styles.css +10 -1
  478. package/dist/styles/sunken-panel.css +34 -0
  479. package/dist/styles/surface-scaffold.css +111 -0
  480. package/dist/styles/tab-bar.css +150 -9
  481. package/dist/styles/tab-navigator.css +253 -0
  482. package/dist/styles/text.css +158 -0
  483. package/dist/styles/token-search-field.css +167 -56
  484. package/dist/styles/toolbar-control-group.css +710 -30
  485. package/dist/styles/toolbar-text.css +139 -8
  486. package/dist/styles/toolbar.css +367 -11
  487. package/dist/styles/value-table.css +39 -7
  488. package/dist/styles/webawesome.css +392 -17
  489. package/dist/styles/workbench.css +1005 -7
  490. package/dist/sunken-panel.d.ts +31 -0
  491. package/dist/sunken-panel.js +3 -0
  492. package/dist/sunken-panel.js.map +1 -0
  493. package/dist/surface-scaffold-register.d.ts +4 -0
  494. package/dist/surface-scaffold-register.js +59 -0
  495. package/dist/surface-scaffold-register.js.map +1 -0
  496. package/dist/surface-scaffold.d.ts +38 -0
  497. package/dist/surface-scaffold.js +3 -0
  498. package/dist/surface-scaffold.js.map +1 -0
  499. package/dist/tab-bar.d.ts +39 -6
  500. package/dist/tab-bar.js +1 -1
  501. package/dist/tab-navigator.d.ts +93 -0
  502. package/dist/tab-navigator.js +97 -0
  503. package/dist/tab-navigator.js.map +1 -0
  504. package/dist/text.d.ts +63 -0
  505. package/dist/text.js +3 -0
  506. package/dist/text.js.map +1 -0
  507. package/dist/token-search-field.d.ts +3 -43
  508. package/dist/token-search-field.js +2 -1
  509. package/dist/token-search-model-DTcTXWMS.d.ts +154 -0
  510. package/dist/token-search-model.d.ts +3 -0
  511. package/dist/token-search-model.js +3 -0
  512. package/dist/token-search-model.js.map +1 -0
  513. package/dist/toolbar-control-group.d.ts +89 -7
  514. package/dist/toolbar-control-group.js +3 -1
  515. package/dist/toolbar-text.d.ts +45 -9
  516. package/dist/toolbar-text.js +2 -1
  517. package/dist/toolbar.d.ts +59 -7
  518. package/dist/toolbar.js +1 -1
  519. package/dist/types-oLe_N1w0.d.ts +130 -0
  520. package/dist/value-table.d.ts +11 -3
  521. package/dist/value-table.js +2 -1
  522. package/dist/webawesome.d.ts +8 -1
  523. package/dist/wire-catalog.d.ts +76 -4
  524. package/dist/wire-catalog.js +250 -14
  525. package/dist/wire-catalog.js.map +1 -1
  526. package/dist/wire-content-items.d.ts +11 -0
  527. package/dist/wire-content-items.js +139 -0
  528. package/dist/wire-content-items.js.map +1 -0
  529. package/dist/wire-nav-stack.d.ts +4 -1
  530. package/dist/wire-nav-stack.js +191 -15
  531. package/dist/wire-nav-stack.js.map +1 -1
  532. package/dist/wire-resizable-regions.d.ts +2 -1
  533. package/dist/wire-resizable-regions.js +2 -1
  534. package/dist/wire-scroll-dividers.d.ts +72 -0
  535. package/dist/wire-scroll-dividers.js +3 -0
  536. package/dist/wire-scroll-dividers.js.map +1 -0
  537. package/dist/wire-sidebar.d.ts +63 -0
  538. package/dist/wire-sidebar.js +231 -0
  539. package/dist/wire-sidebar.js.map +1 -0
  540. package/dist/wire-tab-bars.d.ts +19 -2
  541. package/dist/wire-tab-bars.js +1 -1
  542. package/dist/wire-tab-navigator.d.ts +11 -0
  543. package/dist/wire-tab-navigator.js +21 -0
  544. package/dist/wire-tab-navigator.js.map +1 -0
  545. package/dist/wire-token-search-fields.d.ts +65 -3
  546. package/dist/wire-token-search-fields.js +2 -1
  547. package/dist/wire-toolbar-visibility.d.ts +13 -0
  548. package/dist/wire-toolbar-visibility.js +3 -0
  549. package/dist/wire-toolbar-visibility.js.map +1 -0
  550. package/dist/wire-workbench.d.ts +116 -0
  551. package/dist/wire-workbench.js +6 -0
  552. package/dist/wire-workbench.js.map +1 -0
  553. package/dist/workbench.d.ts +246 -9
  554. package/dist/workbench.js +18 -15
  555. package/dist/workbench.js.map +1 -1
  556. package/docs/accessibility.md +495 -24
  557. package/docs/anchored-surface.md +51 -0
  558. package/docs/app-layouts.md +141 -10
  559. package/docs/apple-hig.md +1 -0
  560. package/docs/catalog.md +395 -62
  561. package/docs/collapsible-panel.md +382 -0
  562. package/docs/component-contract.md +740 -53
  563. package/docs/component-integration.md +75 -0
  564. package/docs/component-selection.md +682 -97
  565. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  566. package/docs/design/templates/empty-state/filtered.svg +1 -0
  567. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  568. package/docs/design/templates/empty-state/with-action.svg +1 -0
  569. package/docs/design/templates/empty-state-dark.svg +1 -0
  570. package/docs/design/templates/empty-state.svg +1 -0
  571. package/docs/design/templates/list/compact-dark.svg +1 -0
  572. package/docs/design/templates/list/compact.svg +1 -0
  573. package/docs/design/templates/list/stack-dark.svg +1 -0
  574. package/docs/design/templates/list/stack.svg +1 -0
  575. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  576. package/docs/design/templates/list-action-row/default.svg +1 -0
  577. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  578. package/docs/design/templates/list-action-row/selected.svg +1 -0
  579. package/docs/design/templates/list-action-row-dark.svg +1 -0
  580. package/docs/design/templates/list-action-row.svg +1 -0
  581. package/docs/design/templates/list-dark.svg +1 -0
  582. package/docs/design/templates/list-header/action-dark.svg +1 -0
  583. package/docs/design/templates/list-header/action.svg +1 -0
  584. package/docs/design/templates/list-header/count-dark.svg +1 -0
  585. package/docs/design/templates/list-header/count.svg +1 -0
  586. package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
  587. package/docs/design/templates/list-header/inline-action.svg +1 -0
  588. package/docs/design/templates/list-header/size-l-dark.svg +1 -0
  589. package/docs/design/templates/list-header/size-l.svg +1 -0
  590. package/docs/design/templates/list-header/size-m-dark.svg +1 -0
  591. package/docs/design/templates/list-header/size-m.svg +1 -0
  592. package/docs/design/templates/list-header/size-xl-dark.svg +1 -0
  593. package/docs/design/templates/list-header/size-xl.svg +1 -0
  594. package/docs/design/templates/list-header/size-xs-dark.svg +1 -0
  595. package/docs/design/templates/list-header/size-xs.svg +1 -0
  596. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  597. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  598. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  599. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  600. package/docs/design/templates/list-header-dark.svg +1 -0
  601. package/docs/design/templates/list-header.svg +1 -0
  602. package/docs/design/templates/list-item/default-dark.svg +1 -0
  603. package/docs/design/templates/list-item/default.svg +1 -0
  604. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  605. package/docs/design/templates/list-item/multiline.svg +1 -0
  606. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  607. package/docs/design/templates/list-item/selected.svg +1 -0
  608. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  609. package/docs/design/templates/list-item/trailing.svg +1 -0
  610. package/docs/design/templates/list-item-dark.svg +1 -0
  611. package/docs/design/templates/list-item.svg +1 -0
  612. package/docs/design/templates/list.svg +1 -0
  613. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  614. package/docs/design/templates/segmented-control/equal.svg +1 -0
  615. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  616. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  617. package/docs/design/templates/segmented-control-dark.svg +1 -0
  618. package/docs/design/templates/segmented-control.svg +1 -0
  619. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  620. package/docs/design/templates/skeleton/avatar.svg +1 -0
  621. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  622. package/docs/design/templates/skeleton/block.svg +1 -0
  623. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  624. package/docs/design/templates/skeleton/lines.svg +1 -0
  625. package/docs/design/templates/skeleton-dark.svg +1 -0
  626. package/docs/design/templates/skeleton.svg +1 -0
  627. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  628. package/docs/design/templates/state-banner/danger.svg +1 -0
  629. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  630. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  631. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  632. package/docs/design/templates/state-banner/info.svg +1 -0
  633. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  634. package/docs/design/templates/state-banner/success.svg +1 -0
  635. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  636. package/docs/design/templates/state-banner/warning.svg +1 -0
  637. package/docs/design/templates/state-banner-dark.svg +1 -0
  638. package/docs/design/templates/state-banner.svg +1 -0
  639. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  640. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  641. package/docs/design/templates/tab-bar-dark.svg +1 -0
  642. package/docs/design/templates/tab-bar.svg +1 -0
  643. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  644. package/docs/design/templates/token-search-field/empty.svg +1 -0
  645. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  646. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  647. package/docs/design/templates/token-search-field-dark.svg +1 -0
  648. package/docs/design/templates/token-search-field.svg +1 -0
  649. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  650. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  651. package/docs/design/templates/toolbar/width-visibility-320-dark.svg +1 -0
  652. package/docs/design/templates/toolbar/width-visibility-320.svg +1 -0
  653. package/docs/design/templates/toolbar/width-visibility-600-dark.svg +1 -0
  654. package/docs/design/templates/toolbar/width-visibility-600.svg +1 -0
  655. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  656. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  657. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  658. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  659. package/docs/design/templates/toolbar-control-group/icon-tile-brand-dark.svg +1 -0
  660. package/docs/design/templates/toolbar-control-group/icon-tile-brand.svg +1 -0
  661. package/docs/design/templates/toolbar-control-group/icon-tile-danger-dark.svg +1 -0
  662. package/docs/design/templates/toolbar-control-group/icon-tile-danger.svg +1 -0
  663. package/docs/design/templates/toolbar-control-group/icon-tile-neutral-dark.svg +1 -0
  664. package/docs/design/templates/toolbar-control-group/icon-tile-neutral.svg +1 -0
  665. package/docs/design/templates/toolbar-control-group/icon-tile-success-dark.svg +1 -0
  666. package/docs/design/templates/toolbar-control-group/icon-tile-success.svg +1 -0
  667. package/docs/design/templates/toolbar-control-group/icon-tile-warning-dark.svg +1 -0
  668. package/docs/design/templates/toolbar-control-group/icon-tile-warning.svg +1 -0
  669. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  670. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  671. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  672. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  673. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  674. package/docs/design/templates/toolbar-control-group.svg +1 -0
  675. package/docs/design/templates/toolbar-dark.svg +1 -0
  676. package/docs/design/templates/toolbar-text/dark-tone-dark.svg +1 -0
  677. package/docs/design/templates/toolbar-text/dark-tone.svg +1 -0
  678. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  679. package/docs/design/templates/toolbar-text/default.svg +1 -0
  680. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  681. package/docs/design/templates/toolbar-text/large.svg +1 -0
  682. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  683. package/docs/design/templates/toolbar-text/small.svg +1 -0
  684. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  685. package/docs/design/templates/toolbar-text.svg +1 -0
  686. package/docs/design/templates/toolbar.svg +1 -0
  687. package/docs/design/templates/value-table/details-dark.svg +1 -0
  688. package/docs/design/templates/value-table/details.svg +1 -0
  689. package/docs/design/templates/value-table-dark.svg +1 -0
  690. package/docs/design/templates/value-table.svg +1 -0
  691. package/docs/design/templates.md +140 -0
  692. package/docs/design-philosophy.md +56 -19
  693. package/docs/device-class.md +10 -9
  694. package/docs/document-baseline.md +42 -0
  695. package/docs/examples/application-ui-profile.json +60 -0
  696. package/docs/examples/command-palette-adapter.tsx +69 -17
  697. package/docs/examples/component-catalog-extension.json +52 -0
  698. package/docs/examples/component-composition-extension.json +80 -0
  699. package/docs/layout.md +612 -53
  700. package/docs/nav-stack.md +163 -14
  701. package/docs/recipes.md +127 -52
  702. package/docs/split-view.md +76 -5
  703. package/docs/sunken-panel.md +93 -0
  704. package/docs/surface-scaffold.md +74 -0
  705. package/docs/tab-navigator.md +218 -0
  706. package/docs/type-contracts.md +137 -0
  707. package/docs/ui-analyzer.md +404 -0
  708. package/docs/ui-doctor.md +202 -0
  709. package/docs/ui-evaluator.md +139 -0
  710. package/docs/ux-demo.md +45 -15
  711. package/docs/webawesome-theme.md +204 -16
  712. package/docs/workbench.md +623 -12
  713. package/doctor/cli.mjs +120 -0
  714. package/doctor/component-style-checks.mjs +457 -0
  715. package/doctor/config.schema.json +94 -0
  716. package/doctor/eslint-diagnostics.d.mts +24 -0
  717. package/doctor/eslint-diagnostics.mjs +24 -0
  718. package/doctor/index.d.mts +122 -0
  719. package/doctor/index.mjs +1403 -0
  720. package/doctor/report.schema.json +118 -0
  721. package/evaluator/cli.mjs +89 -0
  722. package/evaluator/index.d.mts +90 -0
  723. package/evaluator/index.mjs +1203 -0
  724. package/evaluator/report.schema.json +152 -0
  725. package/llms.txt +562 -49
  726. package/package.json +247 -27
  727. package/scripts/component-catalog-extension-validation.mjs +28 -0
  728. package/traversal-exclusions.mjs +27 -0
  729. package/ux-demo/recipes/app-shell.tsx +204 -16
  730. package/ux-demo/recipes/collapsible-sidebar.tsx +260 -0
  731. package/ux-demo/recipes/compact-toolbar.tsx +135 -14
  732. package/ux-demo/recipes/composer-form.tsx +133 -21
  733. package/ux-demo/recipes/list-detail-dialog.tsx +255 -14
  734. package/ux-demo/recipes/list-workspace-states.tsx +183 -11
  735. package/ux-demo/recipes/loaders.ts +11 -3
  736. package/ux-demo/recipes/loading-inspector.tsx +161 -36
  737. package/ux-demo/recipes/mount-recipe.ts +28 -10
  738. package/ux-demo/recipes/navigation-sidebar.tsx +137 -11
  739. package/ux-demo/recipes/navigation-stack.tsx +79 -24
  740. package/ux-demo/recipes/types.ts +23 -2
  741. package/ux-demo/recipes/workspace-header.tsx +86 -5
  742. package/dist/browser/panel-header.js +0 -7
  743. package/dist/chunk-2RJBFNB6.js +0 -172
  744. package/dist/chunk-2RJBFNB6.js.map +0 -1
  745. package/dist/chunk-2TS7ZSYX.js +0 -20
  746. package/dist/chunk-2TS7ZSYX.js.map +0 -1
  747. package/dist/chunk-2Z5TWHKQ.js +0 -34
  748. package/dist/chunk-2Z5TWHKQ.js.map +0 -1
  749. package/dist/chunk-37T3VNIZ.js +0 -19
  750. package/dist/chunk-37T3VNIZ.js.map +0 -1
  751. package/dist/chunk-55DATV3A.js +0 -10
  752. package/dist/chunk-55DATV3A.js.map +0 -1
  753. package/dist/chunk-56RJXPHG.js.map +0 -1
  754. package/dist/chunk-7BR3DO2A.js.map +0 -1
  755. package/dist/chunk-7JB7VPRI.js +0 -34
  756. package/dist/chunk-7JB7VPRI.js.map +0 -1
  757. package/dist/chunk-7LDCTU7D.js +0 -134
  758. package/dist/chunk-7LDCTU7D.js.map +0 -1
  759. package/dist/chunk-AQNKJRFH.js +0 -79
  760. package/dist/chunk-AQNKJRFH.js.map +0 -1
  761. package/dist/chunk-B24C4A6T.js +0 -22
  762. package/dist/chunk-B24C4A6T.js.map +0 -1
  763. package/dist/chunk-BLK3UEEI.js +0 -23
  764. package/dist/chunk-BLK3UEEI.js.map +0 -1
  765. package/dist/chunk-DAFFDO33.js +0 -14
  766. package/dist/chunk-DAFFDO33.js.map +0 -1
  767. package/dist/chunk-I7INRJO4.js +0 -17
  768. package/dist/chunk-I7INRJO4.js.map +0 -1
  769. package/dist/chunk-L4OKFDJY.js +0 -24
  770. package/dist/chunk-L4OKFDJY.js.map +0 -1
  771. package/dist/chunk-LC3YSAZC.js +0 -39
  772. package/dist/chunk-LC3YSAZC.js.map +0 -1
  773. package/dist/chunk-M5JRI6MN.js +0 -27
  774. package/dist/chunk-M5JRI6MN.js.map +0 -1
  775. package/dist/chunk-MBR3TYNL.js +0 -14
  776. package/dist/chunk-MBR3TYNL.js.map +0 -1
  777. package/dist/chunk-MFKMHK5W.js +0 -10
  778. package/dist/chunk-MFKMHK5W.js.map +0 -1
  779. package/dist/chunk-MQIZAD4A.js +0 -227
  780. package/dist/chunk-MQIZAD4A.js.map +0 -1
  781. package/dist/chunk-RUNWEPR4.js +0 -10
  782. package/dist/chunk-RUNWEPR4.js.map +0 -1
  783. package/dist/chunk-W2FK7ZNY.js +0 -26
  784. package/dist/chunk-W2FK7ZNY.js.map +0 -1
  785. package/dist/chunk-W5EQ3HYQ.js +0 -42
  786. package/dist/chunk-W5EQ3HYQ.js.map +0 -1
  787. package/dist/chunk-WVPYVDXY.js +0 -15
  788. package/dist/chunk-WVPYVDXY.js.map +0 -1
  789. package/dist/chunk-YGN7OMLT.js.map +0 -1
  790. package/dist/panel-header.d.ts +0 -36
  791. package/dist/panel-header.js +0 -7
  792. package/dist/panel-header.js.map +0 -1
  793. package/dist/styles/panel-header.css +0 -64
  794. package/dist/styles/tab-scaffold.css +0 -84
  795. package/dist/tab-scaffold.d.ts +0 -30
  796. package/dist/tab-scaffold.js +0 -16
  797. package/dist/tab-scaffold.js.map +0 -1
  798. package/dist/wire-tab-scaffold.d.ts +0 -11
  799. package/dist/wire-tab-scaffold.js +0 -16
  800. package/dist/wire-tab-scaffold.js.map +0 -1
  801. package/docs/tab-scaffold.md +0 -41
  802. package/ux-demo/recipes/recipes.css +0 -361
@@ -1,11 +1,136 @@
1
1
  # Public API signatures for the UI authoring corpus
2
2
 
3
- Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.9` and `kerfjs@5.0.0-beta.9`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
3
+ Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.90` and `kerfjs@5.0.0-beta.90`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
4
+
5
+ ## `@kerfjs/ui/css-values`
6
+
7
+ ```ts
8
+ declare const cssValueBrand: unique symbol;
9
+ declare const cssLengthBrand: unique symbol;
10
+ declare const cssLengthExpressionBrand: unique symbol;
11
+ declare const cssFlexBrand: unique symbol;
12
+ declare const cssColorBrand: unique symbol;
13
+ declare const cssForegroundColorBrand: unique symbol;
14
+ /**
15
+ * A complete typed CSS value minted by a property-specific Kerf UI builder.
16
+ *
17
+ * This brand is an authoring correctness aid, not a sanitizer or security
18
+ * boundary. Prefer the narrower property grammar, such as {@link CssLength},
19
+ * whenever one is available.
20
+ */
21
+ type CssValue = string & {
22
+ readonly [cssValueBrand]: 'CssValue';
23
+ };
24
+ /**
25
+ * A complete CSS length-percentage value suitable for dimension-valued UI
26
+ * props. Despite the concise name, percentages are intentionally included.
27
+ */
28
+ type CssLength = CssValue & {
29
+ readonly [cssLengthBrand]: 'CssLength';
30
+ };
31
+ /**
32
+ * An incomplete arithmetic expression. Wrap it with {@link calc} before
33
+ * passing it to a prop that accepts {@link CssLength}.
34
+ */
35
+ type CssLengthExpression = string & {
36
+ readonly [cssLengthExpressionBrand]: 'CssLengthExpression';
37
+ };
38
+ /** A complete CSS `flex` shorthand. It is not interchangeable with a length. */
39
+ type CssFlex = CssValue & {
40
+ readonly [cssFlexBrand]: 'CssFlex';
41
+ };
42
+ /** A complete CSS color value. It is not interchangeable with a length. */
43
+ type CssColor = CssValue & {
44
+ readonly [cssColorBrand]: 'CssColor';
45
+ };
46
+ /**
47
+ * A complete CSS color meant to paint a foreground: text or an icon drawn over
48
+ * a surface. Mint it with {@link uiColor} and a foreground token name
49
+ * ({@link UiForegroundColorName}) or with {@link foregroundColorVar}. It is a
50
+ * {@link CssColor}, but a plain `CssColor` is not a foreground color: fill,
51
+ * border, and surface tokens are pale backgrounds that leave a foreground
52
+ * nearly invisible.
53
+ */
54
+ type CssForegroundColor = CssColor & {
55
+ readonly [cssForegroundColorBrand]: 'CssForegroundColor';
56
+ };
57
+ type CssFlexKeyword = 'none' | 'auto' | 'initial';
58
+ type CssFlexBasis = CssLength | 'auto' | 'content' | 'min-content' | 'max-content' | 'fit-content';
59
+ type CssSizeKeyword = 'auto' | 'min-content' | 'max-content' | 'fit-content';
60
+ /** A complete width/height value accepted by dimension-valued UI props. */
61
+ type CssSize = CssLength | CssSizeKeyword;
62
+ declare const uiColorNames: readonly ["brand-border-loud", "brand-border-normal", "brand-border-quiet", "brand-fill-loud", "brand-fill-normal", "brand-fill-quiet", "brand-on-fill", "brand-on-loud", "brand-on-normal", "brand-on-quiet", "danger-border-loud", "danger-border-normal", "danger-border-quiet", "danger-fill-loud", "danger-fill-normal", "danger-fill-quiet", "danger-on-fill", "danger-on-loud", "danger-on-normal", "danger-on-quiet", "neutral-border-loud", "neutral-border-normal", "neutral-border-quiet", "neutral-fill-loud", "neutral-fill-normal", "neutral-fill-quiet", "neutral-on-loud", "neutral-on-normal", "neutral-on-quiet", "pop-border-loud", "pop-border-normal", "pop-border-quiet", "pop-fill-loud", "pop-fill-normal", "pop-fill-quiet", "pop-on-fill", "pop-on-loud", "pop-on-normal", "pop-on-quiet", "success-border-loud", "success-border-normal", "success-border-quiet", "success-fill-loud", "success-fill-normal", "success-fill-quiet", "success-on-fill", "success-on-loud", "success-on-normal", "success-on-quiet", "surface", "surface-lowered", "surface-raised", "text", "text-link", "text-quiet", "warning-border-loud", "warning-border-normal", "warning-border-quiet", "warning-fill-loud", "warning-fill-normal", "warning-fill-quiet", "warning-on-fill", "warning-on-loud", "warning-on-normal", "warning-on-quiet"];
63
+ /** Names of the public `--kui-color-*` semantic tokens. */
64
+ type UiColorName = (typeof uiColorNames)[number];
65
+ /** The semantic tokens that paint a foreground: the `*-on-*` and text roles. */
66
+ type UiForegroundColorName = Extract<UiColorName, `${string}-on-${string}` | 'text' | 'text-quiet' | 'text-link'>;
67
+ /** Kerf UI's complete spacing-token vocabulary. `s` and `xl` are exceptions. */
68
+ type UiSpaceName = 'none' | '2xs' | 'xs' | 's' | 'm' | 'l' | 'xl';
69
+ /** A token or complete typed length for one gap axis. */
70
+ type UiGapValue = UiSpaceName | CssLength;
71
+ /** A shared gap or independent column and row gaps. */
72
+ type UiGap = UiGapValue | Readonly<{
73
+ column: UiGapValue;
74
+ row: UiGapValue;
75
+ }>;
76
+ /** Create a complete pixel length. */
77
+ declare function px(value: number): CssLength;
78
+ /** Create a complete root-font-relative length. */
79
+ declare function rem(value: number): CssLength;
80
+ /** Converts a pixel value into rem units by dividing by 16. */
81
+ declare function remify(value: number): CssLength;
82
+ /** Create a complete current-font-relative length. */
83
+ declare function em(value: number): CssLength;
84
+ /** Create a complete percentage length. */
85
+ declare function pct(value: number): CssLength;
86
+ /** Resolve a Kerf UI spacing step to its public custom property. */
87
+ declare function space(name: UiSpaceName): CssLength;
88
+ /**
89
+ * Reference an application-owned custom property whose contract is a CSS
90
+ * length-percentage. The deliberately narrow name grammar keeps this helper
91
+ * from becoming an arbitrary CSS-string constructor.
92
+ */
93
+ declare function lengthVar(name: `--${string}`, fallback?: CssLength): CssLength;
94
+ /** Combine two or more complete lengths into a non-standalone sum. */
95
+ declare function plus(first: CssLength, second: CssLength, ...rest: readonly CssLength[]): CssLengthExpression;
96
+ /** Turn a typed length expression into a complete CSS `calc()` value. */
97
+ declare function calc(expression: CssLengthExpression): CssLength;
98
+ /** Build a complete, structured CSS flex shorthand. */
99
+ declare function flex(grow: number, shrink?: number, basis?: CssFlexBasis): CssFlex;
100
+ /**
101
+ * The brand {@link uiColor} returns for a token name: a
102
+ * {@link CssForegroundColor} for a foreground token, otherwise a plain
103
+ * {@link CssColor}.
104
+ */
105
+ type UiColor<Name extends UiColorName> = Name extends UiForegroundColorName ? CssForegroundColor : CssColor;
106
+ /**
107
+ * Resolve a public Kerf UI semantic color token. A foreground token name
108
+ * (`success-on-quiet`, `text-quiet`, …) returns a {@link CssForegroundColor};
109
+ * any other token returns a plain {@link CssColor}.
110
+ */
111
+ declare function uiColor<Name extends UiColorName>(name: Name): UiColor<Name>;
112
+ /** Reference an application-owned custom property whose contract is a color. */
113
+ declare function colorVar(name: `--${string}`, fallback?: CssColor): CssColor;
114
+ /**
115
+ * Reference an application-owned custom property whose contract is a
116
+ * foreground color (text or an icon over a surface). Use it where a prop
117
+ * accepts only {@link CssForegroundColor}, such as `SelectChoice.color`.
118
+ */
119
+ declare function foregroundColorVar(name: `--${string}`, fallback?: CssForegroundColor): CssForegroundColor;
120
+ /**
121
+ * Use an application-chosen CSS color for foreground content. The browser
122
+ * resolves the color syntax; this checks only that it is one declaration value.
123
+ */
124
+ declare function foregroundColor(value: string): CssForegroundColor;
125
+
126
+ export { type CssColor, type CssFlex, type CssFlexBasis, type CssFlexKeyword, type CssForegroundColor, type CssLength, type CssLengthExpression, type CssSize, type CssSizeKeyword, type CssValue, type UiColor, type UiColorName, type UiForegroundColorName, type UiGap, type UiGapValue, type UiSpaceName, calc, colorVar, em, flex, foregroundColor, foregroundColorVar, lengthVar, pct, plus, px, rem, remify, space, uiColor };
127
+ ```
4
128
 
5
129
  ## `@kerfjs/ui/disclosure-arrow`
6
130
 
7
131
  ```ts
8
132
  import { SafeHtml } from 'kerfjs';
133
+ import { CssLength } from './css-values.js';
9
134
 
10
135
  type DisclosureDirection = 'up' | 'down' | 'left' | 'right';
11
136
  interface DisclosureArrowProps {
@@ -14,9 +139,13 @@ interface DisclosureArrowProps {
14
139
  closedDirection?: DisclosureDirection;
15
140
  /** Replacement icons should use right as their unrotated orientation. */
16
141
  icon?: SafeHtml;
142
+ /** Typed box size for the square visual. Default: `remify(18px)`. */
143
+ size?: CssLength;
17
144
  className?: string;
145
+ /** Native named-slot assignment when composed inside a web component. */
146
+ slot?: string;
18
147
  }
19
- declare function DisclosureArrow({ open, openDirection, closedDirection, icon, className, }: DisclosureArrowProps): SafeHtml;
148
+ declare function DisclosureArrow({ open, openDirection, closedDirection, icon, size, className, slot, }: DisclosureArrowProps): SafeHtml;
20
149
 
21
150
  export { DisclosureArrow, type DisclosureArrowProps, type DisclosureDirection };
22
151
  ```
@@ -24,83 +153,423 @@ export { DisclosureArrow, type DisclosureArrowProps, type DisclosureDirection };
24
153
  ## `@kerfjs/ui/toolbar`
25
154
 
26
155
  ```ts
27
- import { SafeHtml } from 'kerfjs';
156
+ import * as kerfjs from 'kerfjs';
157
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
158
+ import { S as Sides } from './sides-BPSWde0A.js';
159
+ import { PaneSeparatorSide } from './pane.js';
28
160
 
161
+ type ToolbarPosition = 'header' | 'footer';
29
162
  interface ToolbarProps {
30
- leading?: SafeHtml;
31
- center?: SafeHtml;
32
- trailing?: SafeHtml;
163
+ leading?: KerfUiContent;
164
+ center?: KerfUiContent;
165
+ trailing?: KerfUiContent;
33
166
  label?: string;
34
- divider?: boolean;
167
+ /** Use footer semantics for a bottom toolbar; defaults to header. */
168
+ position?: ToolbarPosition;
169
+ /**
170
+ * Physical divider edges in canonical top/right/bottom/left order, drawn
171
+ * always. Defaults to none: a toolbar pinned over or under scrolling content
172
+ * gets its divider from the scroll state instead — a `Pane` draws the line
173
+ * between its chrome and content while content is scrolled away beneath it,
174
+ * and `wireScrollDividers` `targets` can name a toolbar as chrome, which
175
+ * then draws its facing side the same way.
176
+ */
177
+ dividerSides?: Sides;
178
+ /** `balanced` gives both side zones equal tracks, centering the middle zone on the toolbar even if only one side has controls. */
179
+ centerAlign?: 'center' | 'stretch' | 'balanced';
180
+ /**
181
+ * Component-owned responsive layout; applications choose the policy rather
182
+ * than restyling toolbar internals. Under every policy the trailing zone
183
+ * wraps its groups instead of clipping an action, and the center zone keeps
184
+ * its whole content width: when the zones do not fit one row, the leading
185
+ * title truncates with an ellipsis rather than the center overlapping it.
186
+ * - `none` keeps one row; the leading identity truncates first.
187
+ * - `stack` stacks the zones at `responsiveAt`, wrapping stacked control
188
+ * groups onto further rows.
189
+ * - `wrap` keeps one row while every zone fits at its natural width, and
190
+ * otherwise moves the trailing zone below a whole leading identity.
191
+ * - `center-priority` gives an expanded center control the full row.
192
+ * - `trailing-priority` moves an expanded trailing control to a full row
193
+ * below the leading identity at `responsiveAt`.
194
+ */
195
+ responsive?: 'none' | 'stack' | 'wrap' | 'center-priority' | 'trailing-priority';
196
+ /** Container width at which `stack` or `trailing-priority` activates. */
197
+ responsiveAt?: 'compact' | 'narrow';
198
+ /**
199
+ * Screen edges this toolbar claims for device safe-area compensation, for an
200
+ * app bar or bottom bar that sits directly at a screen edge rather than in a
201
+ * Pane header or footer. Each listed side pads by the inset its surrounding
202
+ * layout reports through `--kui-edge-inset-*`, or the full device inset when
203
+ * nothing routes that edge, while the toolbar's box and dividers paint
204
+ * through. Omitted, the toolbar pads only the inline edges an owner such as
205
+ * a Pane header hands it, and never a block edge.
206
+ */
207
+ safeAreaEdges?: readonly PaneSeparatorSide[];
35
208
  className?: string;
209
+ /** Native named-slot assignment when composed inside a web component. */
210
+ slot?: string;
36
211
  }
37
- declare function Toolbar({ leading, center, trailing, label, divider, className }: ToolbarProps): SafeHtml;
212
+ /**
213
+ * A toolbar's configuration, apart from its content: the props a composite
214
+ * that renders a `Toolbar` for the app (a Workbench or CollapsiblePanel
215
+ * toolbar) forwards, so the app configures that toolbar instead of styling it.
216
+ */
217
+ type ToolbarConfig = Pick<ToolbarProps, 'dividerSides' | 'centerAlign' | 'responsive' | 'responsiveAt' | 'safeAreaEdges'>;
218
+ declare function Toolbar({ leading, center, trailing, label, position, dividerSides, centerAlign, responsive, responsiveAt, safeAreaEdges, className, slot, }: ToolbarProps): kerfjs.SafeHtml;
38
219
 
39
- export { Toolbar, type ToolbarProps };
220
+ export { Sides, Toolbar, type ToolbarConfig, type ToolbarPosition, type ToolbarProps };
40
221
  ```
41
222
 
42
223
  ## `@kerfjs/ui/toolbar-text`
43
224
 
44
225
  ```ts
45
226
  import * as kerfjs from 'kerfjs';
227
+ import { CssLength } from './css-values.js';
46
228
 
47
- type ToolbarTextSize = 'xlarge' | 'large' | 'default' | 'small';
229
+ type ToolbarTextSize = 'xlarge' | 'xlarge-fixed' | 'large' | 'default' | 'small' | 'xsmall';
230
+ type ToolbarTextTone = 'default' | 'dark';
48
231
  /** ARIA heading level for a title exposed as a heading landmark. */
49
232
  type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
50
- interface ToolbarTextProps {
233
+ interface ToolbarTextBaseProps {
51
234
  text: string;
52
235
  size?: ToolbarTextSize;
236
+ /** Use dark-toolbar foregrounds on a dark or photo-backed toolbar. */
237
+ tone?: ToolbarTextTone;
238
+ /** Hide below this Toolbar content width; requires wireToolbarVisibility. */
239
+ hideBelow?: CssLength;
240
+ /** Show only below this Toolbar content width; requires wireToolbarVisibility. */
241
+ showBelow?: CssLength;
53
242
  className?: string;
54
243
  /** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */
55
244
  id?: string;
56
245
  /**
57
- * Expose heading semantics (`role="heading"` + `aria-level`) so the text acts as
58
- * a heading landmark — e.g. a page's primary title. Omit to keep the plain span
59
- * (the default), which suits a dialog title referenced via `aria-labelledby`.
246
+ * Show a trailing ellipsis (…) where the text is truncated — on the single line
247
+ * (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip
248
+ * instead. Default true.
249
+ */
250
+ ellipsis?: boolean;
251
+ /**
252
+ * Grow to fill the free space of the flex row it sits in (a Toolbar zone),
253
+ * truncating at the space left by its siblings instead of taking only its
254
+ * text's width. Default false.
60
255
  */
256
+ fill?: boolean;
257
+ /** Native named-slot assignment when composed inside a web component. */
258
+ slot?: string;
259
+ }
260
+ type ToolbarTextWrappingProps = {
261
+ /** Wrap onto multiple lines; combine with `maxLines` to cap them. */
262
+ wrap: true;
263
+ maxLines?: number | null;
264
+ } | {
265
+ wrap?: false;
266
+ maxLines?: never;
267
+ };
268
+ type ToolbarTextProps = ToolbarTextBaseProps & (({
269
+ action?: never;
270
+ /** Expose `role="heading"` and `aria-level` on read-only text. */
61
271
  headingLevel?: HeadingLevel;
62
- /** Render the text as an unanimated loading skeleton instead of its value. */
272
+ /** Render an unanimated loading skeleton instead of the text. */
63
273
  placeholder?: boolean;
64
- }
65
- declare function ToolbarText({ text, size, className, id, headingLevel, placeholder }: ToolbarTextProps): kerfjs.SafeHtml;
66
-
67
- export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
274
+ } & ToolbarTextWrappingProps) | {
275
+ /** Render a native button with this delegated action; the app owns editing. */
276
+ action: string;
277
+ headingLevel?: never;
278
+ placeholder?: never;
279
+ wrap?: never;
280
+ maxLines?: never;
281
+ });
282
+ declare function ToolbarText({ text, size, tone, hideBelow, showBelow, className, id, headingLevel, placeholder, wrap, ellipsis, maxLines, fill, slot, action, }: ToolbarTextProps): kerfjs.SafeHtml;
283
+
284
+ export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarTextSize, type ToolbarTextTone };
68
285
  ```
69
286
 
70
287
  ## `@kerfjs/ui/toolbar-control-group`
71
288
 
72
289
  ```ts
73
- import { SafeHtml } from 'kerfjs';
74
-
290
+ import * as kerfjs from 'kerfjs';
291
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
292
+ import { CssLength } from './css-values.js';
293
+
294
+ type ToolbarControlGroupAppearance = 'contained' | 'borderless';
295
+ type ToolbarControlGroupTone = 'default' | 'dark';
296
+ /** Quiet palette for a contained, single decorative icon tile. */
297
+ type ToolbarControlGroupTileTone = 'neutral' | 'brand' | 'success' | 'warning' | 'danger';
298
+ type ToolbarControlGroupButtonAppearance = 'plain' | 'push';
299
+ type ToolbarControlGroupShape = 'pill' | 'rounded';
300
+ type ToolbarControlGroupSize = 'default' | 'compact';
301
+ type ToolbarControlGroupDensity = 'comfortable' | 'tight';
302
+ type ToolbarControlGroupContent = 'icon' | 'text' | 'mixed' | 'avatar' | 'search';
303
+ type ToolbarControlGroupFocusRing = 'control' | 'outline' | 'halo';
304
+ type ToolbarControlGroupSelectedChrome = 'raised' | 'filled' | 'outline';
305
+ type ToolbarControlGroupSelectedTone = 'brand' | 'neutral' | 'pop';
306
+ type ToolbarControlGroupOverflow = 'visible' | 'scroll' | 'wrap';
307
+ type ToolbarControlGroupExpandedOverflow = 'clip' | 'visible';
308
+ type ToolbarControlGroupMenuInset = 'standard' | 'compact' | 'list-zero';
309
+ type ToolbarControlGroupVisibility = 'always' | 'compact-only' | 'hide-collapsed-tiny' | 'yield-to-expanded-sibling';
310
+ type ToolbarControlGroupSizing = 'content' | 'grow' | 'fill';
311
+ type ToolbarControlGroupPlacement = 'start' | 'end';
312
+ interface ToolbarActionLinkProps {
313
+ href: string;
314
+ label: string;
315
+ icon?: KerfUiContent;
316
+ /** Optional machine-readable detail hidden visually but included in the default accessible name. */
317
+ detail?: string;
318
+ /** Override the accessible name when surrounding context is needed. */
319
+ ariaLabel?: string;
320
+ /** Open in a new tab with a safe rel and announce that behavior. */
321
+ external?: boolean;
322
+ className?: string;
323
+ /** Native named-slot assignment when composed inside a web component. */
324
+ slot?: string;
325
+ }
326
+ /** A semantic anchor with ToolbarControlGroup-owned action geometry. */
327
+ declare function ToolbarActionLink({ href, label, icon, detail, ariaLabel, external, className, slot, }: ToolbarActionLinkProps): kerfjs.SafeHtml;
75
328
  interface ToolbarControlGroupProps {
76
- children: SafeHtml | SafeHtml[];
329
+ children: KerfUiContent;
77
330
  label?: string;
78
331
  className?: string;
79
332
  expanded?: boolean;
80
333
  single?: boolean;
81
- appearance?: 'contained' | 'borderless';
82
- tone?: 'default' | 'dark';
83
- buttonAppearance?: 'plain' | 'push';
334
+ appearance?: ToolbarControlGroupAppearance;
335
+ tone?: ToolbarControlGroupTone;
336
+ /** Quiet fill, matching border and foreground for a contained single group whose only child is a decorative SVG. Ignored for interactive or mixed content. */
337
+ tileTone?: ToolbarControlGroupTileTone;
338
+ buttonAppearance?: ToolbarControlGroupButtonAppearance;
339
+ /** Corner shape: fully round `pill` (default) or a softer `rounded` rectangle. */
340
+ shape?: ToolbarControlGroupShape;
341
+ size?: ToolbarControlGroupSize;
342
+ density?: ToolbarControlGroupDensity;
343
+ content?: ToolbarControlGroupContent;
344
+ /** Whether controls paint focus individually or the group paints an outline/halo on focus-within. */
345
+ focusRing?: ToolbarControlGroupFocusRing;
346
+ selectedChrome?: ToolbarControlGroupSelectedChrome;
347
+ selectedTone?: ToolbarControlGroupSelectedTone;
348
+ /** Size a nested Web Awesome dropdown trigger as part of this group. */
349
+ nestedDropdown?: boolean;
350
+ /** Configure the nested dropdown menu inset without consumer ::part() CSS. */
351
+ menuInset?: ToolbarControlGroupMenuInset;
352
+ /** Keep an overlong row inside the available width by scrolling or wrapping. */
353
+ overflow?: ToolbarControlGroupOverflow;
354
+ /** Let app-owned anchored surfaces extend outside an expanded search group. Collapsed searches retain their clipping. */
355
+ expandedOverflow?: ToolbarControlGroupExpandedOverflow;
356
+ /** Responsive visibility owned by the enclosing Toolbar container. */
357
+ visibility?: ToolbarControlGroupVisibility;
358
+ /** Hide below this Toolbar content width; requires wireToolbarVisibility. */
359
+ hideBelow?: CssLength;
360
+ /** Show only below this Toolbar content width; requires wireToolbarVisibility. */
361
+ showBelow?: CssLength;
362
+ /** Intrinsic (default), grow from a basis, or occupy a full wrapping row. */
363
+ sizing?: ToolbarControlGroupSizing;
364
+ /** Place this group at the end of its flex row in a wrapping Toolbar zone. */
365
+ placement?: ToolbarControlGroupPlacement;
366
+ /** CSS length used as the minimum width and flex basis for `sizing="grow"`; defaults to 19rem. */
367
+ growBasis?: string;
368
+ /** Move this group into the work-area toolbar when its panel collapses. */
369
+ relocateOnCollapse?: boolean;
370
+ /** Add contrast behind photo-backed avatar content. */
371
+ scrim?: boolean;
372
+ /** Block the group's controls and show a spinner without changing its dimensions. */
373
+ busy?: boolean;
374
+ /** Announced status while busy; defaults to “Working”. */
375
+ busyLabel?: string;
376
+ /**
377
+ * Avatar image URL. A single-control group paints it on the group; a
378
+ * multi-control group paints it only on the pressed selection highlight.
379
+ */
380
+ avatarImage?: string;
381
+ /** Native named-slot assignment when composed inside a web component. */
382
+ slot?: string;
383
+ }
384
+ declare function ToolbarControlGroup({ children, label, className, expanded, single, appearance, tone, tileTone, buttonAppearance, shape, size, density, content, focusRing, selectedChrome, selectedTone, nestedDropdown, menuInset, overflow, expandedOverflow, visibility, hideBelow, showBelow, sizing, placement, growBasis, relocateOnCollapse, scrim, busy, busyLabel, avatarImage, slot, }: ToolbarControlGroupProps): kerfjs.SafeHtml;
385
+
386
+ export { ToolbarActionLink, type ToolbarActionLinkProps, ToolbarControlGroup, type ToolbarControlGroupAppearance, type ToolbarControlGroupButtonAppearance, type ToolbarControlGroupContent, type ToolbarControlGroupDensity, type ToolbarControlGroupExpandedOverflow, type ToolbarControlGroupFocusRing, type ToolbarControlGroupMenuInset, type ToolbarControlGroupOverflow, type ToolbarControlGroupPlacement, type ToolbarControlGroupProps, type ToolbarControlGroupSelectedChrome, type ToolbarControlGroupSelectedTone, type ToolbarControlGroupShape, type ToolbarControlGroupSize, type ToolbarControlGroupSizing, type ToolbarControlGroupTileTone, type ToolbarControlGroupTone, type ToolbarControlGroupVisibility };
387
+ ```
388
+
389
+ ## `@kerfjs/ui/floating-toolbar`
390
+
391
+ ```ts
392
+ import * as kerfjs from 'kerfjs';
393
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
394
+ import { CssLength } from './css-values.js';
395
+
396
+ /** Where a {@link FloatingToolbar} floats within its positioned container. */
397
+ type FloatingToolbarPosition = 'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end';
398
+ interface FloatingToolbarProps {
399
+ /** Toolbar contents — normally one or more `ToolbarControlGroup`s. */
400
+ children: KerfUiContent;
401
+ /** Accessible name for the toolbar (required — it exposes `role="toolbar"`). */
402
+ label: string;
403
+ /**
404
+ * Corner or edge it floats to inside its nearest positioned ancestor.
405
+ * Default: `'bottom-end'`.
406
+ */
407
+ position?: FloatingToolbarPosition;
408
+ /** Keep the toolbar in normal document flow instead of floating over content. */
409
+ placement?: 'floating' | 'inline';
410
+ /**
411
+ * Distance from the container edges named by `position`. Default:
412
+ * `space('m')` (16px, 8px past a top toolbar's own inset). Omit it inside a
413
+ * `Workbench`, `CollapsiblePanel`, or `ResizableRegion` restore corner: the
414
+ * corner owns the inset there and sets it to zero.
415
+ */
416
+ inset?: CssLength;
417
+ /** Add device or layout-routed safe-area insets to the positioned edges. */
418
+ safeAreaInsets?: boolean;
419
+ className?: string;
420
+ /** Native named-slot assignment when composed inside a web component. */
421
+ slot?: string;
422
+ }
423
+ /**
424
+ * A toolbar that floats above the main content of its nearest positioned
425
+ * ancestor — a transparent, forced-dark cluster of controls (e.g. a drawer
426
+ * restore button) that sits over the content but NOT over dialogs or overlays
427
+ * (it is not in the top layer). It is inset from the container edges by
428
+ * `inset` (default `--kui-space-m`, i.e. 8px more than a top toolbar's own
429
+ * inset); pass a typed length to move it. Inside a `Workbench`, `CollapsiblePanel`, or
430
+ * `ResizableRegion` restore corner the corner owns the inset, so the toolbar
431
+ * floats from the corner's own position. While one of those layouts has a
432
+ * side overlay open over the toolbar's region (a Workbench rail overlay, a
433
+ * `wireSidebar` compact overlay, an overlay `CollapsiblePanel` or horizontal
434
+ * `ResizableRegion`), the toolbar is hidden — unfocusable and out of the
435
+ * accessibility tree — until the overlay closes. The app owns the controls
436
+ * and their behavior — wire them with `delegate()` as usual. Set
437
+ * `placement="inline"` for normal flow when an owner already positions the
438
+ * control; floating placement can add safe-area insets to its per-edge tokens.
439
+ */
440
+ declare function FloatingToolbar({ children, label, position, placement, inset, safeAreaInsets, className, slot, }: FloatingToolbarProps): kerfjs.SafeHtml;
441
+
442
+ export { FloatingToolbar, type FloatingToolbarPosition, type FloatingToolbarProps };
443
+ ```
444
+
445
+ ## `@kerfjs/ui/popup-menu`
446
+
447
+ ```ts
448
+ import { SafeHtml } from 'kerfjs';
449
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
450
+
451
+ type PopupMenuPlacement = 'bottom-start' | 'bottom' | 'bottom-end' | 'top-start' | 'top' | 'top-end';
452
+ type PopupMenuDataAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
453
+ 'data-action'?: never;
454
+ 'data-component'?: never;
455
+ 'data-key'?: never;
456
+ 'data-morph-skip-children'?: never;
457
+ }>;
458
+ /** One command in the menu. */
459
+ interface PopupMenuItem {
460
+ kind?: 'item';
461
+ label: string;
462
+ /** Delegated `data-action` the app handles when the item is chosen. */
463
+ action?: string;
464
+ /** Native `wa-dropdown-item` value, reported by its `wa-select` event. */
465
+ value?: string;
466
+ /** Leading icon, typically a `LucideIcon`; it is placed in the item's icon slot. */
467
+ icon?: SafeHtml;
468
+ /** A checkbox choice, including nested choices; omit for a plain command. */
469
+ checked?: boolean;
470
+ /** Trailing safe content, such as secondary status. */
471
+ details?: SafeHtml;
472
+ /** Destructive command styling. */
473
+ tone?: 'default' | 'danger';
474
+ disabled?: boolean;
475
+ /** Native tooltip text for a disabled command. */
476
+ disabledReason?: string;
477
+ /** Application `data-*` metadata such as a record id. */
478
+ attributes?: PopupMenuDataAttributes;
479
+ /** Child commands, headings, and dividers opened by hover or keyboard navigation. */
480
+ submenu?: readonly PopupMenuEntry[];
481
+ }
482
+ /** A labeled group heading; items that follow it belong to the group. */
483
+ interface PopupMenuHeading {
484
+ kind: 'heading';
485
+ label: string;
486
+ }
487
+ /** A separator between groups of items. */
488
+ interface PopupMenuDivider {
489
+ kind: 'divider';
84
490
  }
85
- declare function ToolbarControlGroup({ children, label, className, expanded, single, appearance, tone, buttonAppearance }: ToolbarControlGroupProps): SafeHtml;
491
+ type PopupMenuEntry = PopupMenuItem | PopupMenuHeading | PopupMenuDivider;
492
+ type PopupMenuTriggerName = {
493
+ /** Visible trigger text, which is also its accessible name. */
494
+ text: string;
495
+ label?: never;
496
+ } | {
497
+ text?: never;
498
+ /**
499
+ * Accessible name of an icon-only trigger, rendered as visually hidden
500
+ * slotted text (a Web Awesome button takes its name from its content).
501
+ */
502
+ label: string;
503
+ };
504
+ type PopupMenuTrigger = (PopupMenuTriggerName & {
505
+ context?: false;
506
+ }) | {
507
+ context: true;
508
+ label: string;
509
+ text?: never;
510
+ icon?: never;
511
+ caret?: never;
512
+ disabled?: never;
513
+ };
514
+ type PopupMenuProps = PopupMenuTrigger & {
515
+ /** Trigger icon, typically a `LucideIcon`, before any visible text. */
516
+ icon?: KerfUiContent;
517
+ items: readonly PopupMenuEntry[];
518
+ /** Where the menu opens relative to its trigger. Defaults to `bottom-start`. */
519
+ placement?: PopupMenuPlacement;
520
+ /** Show the disclosure caret after the trigger content. Defaults to true. */
521
+ caret?: boolean;
522
+ disabled?: boolean;
523
+ /** Application `data-*` metadata on the menu root. */
524
+ rootAttributes?: PopupMenuDataAttributes;
525
+ /** Native named-slot assignment when composed inside a web component. */
526
+ slot?: string;
527
+ };
528
+ /** A rendered context PopupMenu with Web Awesome's controlled open state. */
529
+ type PopupMenuElement = HTMLElement & {
530
+ open: boolean;
531
+ };
532
+ /** Open a context PopupMenu at viewport pointer coordinates. */
533
+ declare function openPopupMenuAt(menu: PopupMenuElement, x: number, y: number): void;
534
+ /** Close a programmatically opened PopupMenu. */
535
+ declare function closePopupMenu(menu: PopupMenuElement): void;
536
+ /**
537
+ * An action menu: a trigger button that opens a list of commands. Renders the
538
+ * `wa-dropdown` root directly, so a `single` `ToolbarControlGroup` (with
539
+ * `nestedDropdown`) or a `PopupSurface` sizes and insets it as usual. Items
540
+ * dispatch through delegated `data-action`s; the app owns the commands and any
541
+ * open-state reaction. Import `@kerfjs/ui/popup-menu/register` once to register
542
+ * the Web Awesome elements. At viewport widths up to 480px, submenus appear
543
+ * above or below their parent menu so their rows do not cover the parent list.
544
+ */
545
+ declare function PopupMenu({ text, label, icon, items, placement, caret, disabled, rootAttributes, slot, context, }: PopupMenuProps): SafeHtml;
86
546
 
87
- export { ToolbarControlGroup, type ToolbarControlGroupProps };
547
+ export { PopupMenu, type PopupMenuDivider, type PopupMenuElement, type PopupMenuEntry, type PopupMenuHeading, type PopupMenuItem, type PopupMenuPlacement, type PopupMenuProps, closePopupMenu, openPopupMenuAt };
88
548
  ```
89
549
 
90
550
  ## `@kerfjs/ui/list-header`
91
551
 
92
552
  ```ts
93
553
  import { SafeHtml } from 'kerfjs';
554
+ import { HeadingLevel } from './toolbar-text.js';
555
+ import './css-values.js';
94
556
 
95
557
  type ListHeaderRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
96
558
  'data-component'?: never;
97
559
  'data-action'?: never;
98
560
  'data-has-badge'?: never;
99
561
  'data-has-count'?: never;
562
+ 'data-density'?: never;
563
+ 'data-size'?: never;
564
+ 'data-divider'?: never;
565
+ 'data-inline'?: never;
566
+ 'data-width'?: never;
567
+ 'data-indicator-tone'?: never;
100
568
  'data-toggle'?: never;
101
569
  }>;
102
570
  type ListHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
103
571
  'data-action'?: never;
572
+ 'data-kui-disabled'?: never;
104
573
  popoverTarget?: string;
105
574
  popoverTargetAction?: 'toggle' | 'show' | 'hide';
106
575
  'aria-controls'?: string;
@@ -108,33 +577,148 @@ type ListHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | un
108
577
  }>;
109
578
  interface ListHeaderBaseProps {
110
579
  label: string;
111
- action?: string;
112
- actionLabel?: string;
113
- actionIcon?: SafeHtml;
114
- actionDisabled?: boolean;
115
- disabledReason?: string;
116
- expanded?: boolean;
117
- toggle?: boolean;
580
+ /** Visual label size, independent of the native heading level. Default `sm`. */
581
+ size?: 'xs' | 'sm' | 'm' | 'l' | 'xl';
582
+ density?: 'standard' | 'compact';
583
+ divider?: 'none' | 'before' | 'after' | 'both';
584
+ /** Shrink-wrap the header without its default outer margin, border, or padding. */
585
+ inline?: boolean;
586
+ /** Fill the available row or shrink-wrap while retaining normal header geometry. */
587
+ width?: 'fill' | 'content';
588
+ indicatorTone?: 'neutral' | 'accent' | 'pop' | 'danger';
118
589
  /** Render as an unanimated loading skeleton: keep the label and action affordance, disable interaction. */
119
590
  placeholder?: boolean;
120
591
  rootAttributes?: ListHeaderRootAttributes;
121
592
  triggerAttributes?: ListHeaderTriggerAttributes;
593
+ /** Native named-slot assignment when composed inside a web component. */
594
+ slot?: string;
122
595
  }
596
+ type ListHeaderModeProps = {
597
+ /** Render the title as a controlled disclosure trigger. */
598
+ toggle: true;
599
+ /** A toggle's label is its button text, not a heading. */
600
+ headingLevel?: never;
601
+ action: string;
602
+ expanded: boolean;
603
+ actionIcon?: SafeHtml;
604
+ actionLabel?: never;
605
+ actionDisabled?: boolean;
606
+ disabledReason?: string;
607
+ } | {
608
+ /** Render a separately named trailing action. */
609
+ toggle?: false;
610
+ /**
611
+ * Native heading level (`h1`–`h6`) of the label. Default `2`. Match the
612
+ * document outline; the visual treatment does not change.
613
+ */
614
+ headingLevel?: HeadingLevel;
615
+ action: string;
616
+ actionLabel: string;
617
+ actionIcon: SafeHtml;
618
+ expanded?: never;
619
+ actionDisabled?: boolean;
620
+ disabledReason?: string;
621
+ } | {
622
+ /** Render a passive section heading. */
623
+ toggle?: false;
624
+ /**
625
+ * Native heading level (`h1`–`h6`) of the label. Default `2`. Match the
626
+ * document outline; the visual treatment does not change.
627
+ */
628
+ headingLevel?: HeadingLevel;
629
+ action?: never;
630
+ actionLabel?: never;
631
+ actionIcon?: never;
632
+ expanded?: never;
633
+ actionDisabled?: never;
634
+ disabledReason?: never;
635
+ };
123
636
  type ListHeaderIndicatorProps = {
124
637
  count: number;
125
638
  countLabel: string;
126
639
  badge?: never;
640
+ status?: never;
127
641
  } | {
128
642
  count?: never;
129
643
  countLabel?: never;
130
- badge?: SafeHtml;
644
+ badge?: SafeHtml | undefined;
645
+ status?: SafeHtml | undefined;
131
646
  };
132
- type ListHeaderProps = ListHeaderBaseProps & ListHeaderIndicatorProps;
133
- declare function ListHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes }: ListHeaderProps): SafeHtml;
647
+ type ListHeaderProps = ListHeaderBaseProps & ListHeaderIndicatorProps & ListHeaderModeProps;
648
+ declare function ListHeader({ label, size, headingLevel, count, countLabel, badge, status, density, divider, inline, width, indicatorTone, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes, slot, }: ListHeaderProps): SafeHtml;
134
649
 
135
650
  export { ListHeader, type ListHeaderProps };
136
651
  ```
137
652
 
653
+ ## `@kerfjs/ui/list`
654
+
655
+ ```ts
656
+ import * as kerfjs from 'kerfjs';
657
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
658
+ import { H as HorizontalAlignment, L as ListVerticalAlignment } from './flex-alignment-4ms8ZbV8.js';
659
+ export { V as VerticalAlignment } from './flex-alignment-4ms8ZbV8.js';
660
+ import { S as Sides } from './sides-BPSWde0A.js';
661
+ import { UiGap, CssFlexKeyword, CssFlex } from './css-values.js';
662
+ export { CssLength, UiGapValue, UiSpaceName } from './css-values.js';
663
+
664
+ type ListRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
665
+ 'data-component'?: never;
666
+ 'data-gap'?: never;
667
+ 'data-flex'?: never;
668
+ 'data-fill'?: never;
669
+ 'data-h-align'?: never;
670
+ 'data-v-align'?: never;
671
+ 'data-scrollable'?: never;
672
+ 'data-text-insets'?: never;
673
+ 'data-control-insets'?: never;
674
+ }>;
675
+ interface ListProps {
676
+ children?: KerfUiContent;
677
+ /** Make this list a labeled multi-select grid for `ContentItem selectionMode="multiple"` rows. */
678
+ selectionMode?: 'multiple';
679
+ /** Accessible name for the multi-select grid. */
680
+ ariaLabel?: string;
681
+ /** Standard gap (`true`), one spacing value, or separate column and row values. Defaults to no gap. */
682
+ gap?: boolean | UiGap;
683
+ /** Allow this list to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
684
+ flex?: boolean | CssFlexKeyword | CssFlex;
685
+ /**
686
+ * Fill the height of a parent with a definite height, such as an app root or
687
+ * a fixed-height frame, when this list is that parent's layout root. Inside a
688
+ * flex layout use `flex` instead. Defaults to false.
689
+ */
690
+ fill?: boolean;
691
+ /** Horizontal alignment. Defaults to full to preserve stretch-aligned list children. */
692
+ hAlign?: HorizontalAlignment;
693
+ /** Vertical distribution. Defaults to top. */
694
+ vAlign?: ListVerticalAlignment;
695
+ /** Own vertical scrolling and overscroll containment. */
696
+ scrollable?: boolean;
697
+ /** Physical divider edges in canonical top/right/bottom/left order. */
698
+ dividerSides?: Sides;
699
+ /** Physical sides that receive the standard 17px text inset. */
700
+ textInsets?: Sides;
701
+ /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */
702
+ controlInsets?: Sides;
703
+ className?: string;
704
+ /** Safe `data-*` metadata; List-owned structural attributes remain protected. */
705
+ rootAttributes?: ListRootAttributes;
706
+ /** Native named-slot assignment when composed inside a web component. */
707
+ slot?: string;
708
+ }
709
+ /**
710
+ * A list's configuration, apart from its content and styling hooks: the props a
711
+ * composite that renders a `List` for the app (the Workbench's `mainHeader` /
712
+ * `mainFooter` chrome) forwards, so the app configures that list instead of
713
+ * styling it. An omitted or `undefined` field keeps the composite's default.
714
+ */
715
+ type ListConfig = Pick<ListProps, 'gap' | 'hAlign' | 'vAlign' | 'dividerSides' | 'textInsets' | 'controlInsets'>;
716
+ /** A stretch-aligned vertical stack with optional gap, flex, fill, scroll, and dividers. */
717
+ declare function List({ children, selectionMode, ariaLabel, gap, flex, fill, hAlign, vAlign, scrollable, dividerSides, textInsets, controlInsets, className, rootAttributes, slot, }: ListProps): kerfjs.SafeHtml;
718
+
719
+ export { CssFlex, CssFlexKeyword, HorizontalAlignment, List, type ListConfig, type ListProps, ListVerticalAlignment, Sides, UiGap };
720
+ ```
721
+
138
722
  ## `@kerfjs/ui/list-action-row`
139
723
 
140
724
  ```ts
@@ -146,6 +730,10 @@ type ListActionRowRootAttributes = Readonly<Record<`data-${string}`, string | un
146
730
  'data-item-id'?: never;
147
731
  'data-has-icon'?: never;
148
732
  'data-multiline'?: never;
733
+ 'data-density'?: never;
734
+ 'data-divider'?: never;
735
+ 'data-busy'?: never;
736
+ 'data-trailing-visibility'?: never;
149
737
  'data-state'?: never;
150
738
  'data-selected'?: never;
151
739
  'data-pressed'?: never;
@@ -154,6 +742,7 @@ type ListActionRowTrailingAttributes = Readonly<Record<`data-${string}`, string
154
742
  'data-component'?: never;
155
743
  'data-action'?: never;
156
744
  'data-item-id'?: never;
745
+ 'data-kui-disabled'?: never;
157
746
  popoverTarget?: string;
158
747
  popoverTargetAction?: 'toggle' | 'show' | 'hide';
159
748
  'aria-controls'?: string;
@@ -162,6 +751,11 @@ type ListActionRowTrailingAttributes = Readonly<Record<`data-${string}`, string
162
751
  interface ListActionRowProps {
163
752
  /** Visible dormant content for the primary button. Must not contain interactive descendants. */
164
753
  label: string | SafeHtml;
754
+ description?: string | SafeHtml;
755
+ status?: string | SafeHtml;
756
+ busy?: boolean;
757
+ density?: 'standard' | 'compact';
758
+ divider?: 'none' | 'before' | 'after' | 'both';
165
759
  /** Decorative dormant content for the primary button. Must not contain interactive descendants. */
166
760
  icon?: SafeHtml;
167
761
  action: string;
@@ -174,7 +768,7 @@ interface ListActionRowProps {
174
768
  state?: string;
175
769
  disabled?: boolean;
176
770
  tabIndex?: number;
177
- /** Render as an unanimated loading skeleton, disabling both actions. */
771
+ /** Render as an unanimated loading skeleton, disabling both actions: label, description, icon, and status text become skeletons; the `busy` indicator stays live. */
178
772
  placeholder?: boolean;
179
773
  trailingAction: string;
180
774
  trailingActionLabel: string;
@@ -182,12 +776,14 @@ interface ListActionRowProps {
182
776
  trailingActionIcon: SafeHtml;
183
777
  trailingActionDisabled?: boolean;
184
778
  trailingActionTitle?: string;
779
+ trailingActionVisibility?: 'always' | 'interaction';
185
780
  className?: string;
186
- style?: string;
187
781
  rootAttributes?: ListActionRowRootAttributes;
188
782
  trailingActionAttributes?: ListActionRowTrailingAttributes;
783
+ /** Native named-slot assignment when composed inside a web component. */
784
+ slot?: string;
189
785
  }
190
- declare function ListActionRow({ label, icon, action, itemId, selected, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, trailingAction, trailingActionLabel, trailingActionIcon, trailingActionDisabled, trailingActionTitle, className, style, rootAttributes, trailingActionAttributes }: ListActionRowProps): SafeHtml;
786
+ declare function ListActionRow({ label, description, status, busy, density, divider, icon, action, itemId, selected, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, trailingAction, trailingActionLabel, trailingActionIcon, trailingActionDisabled, trailingActionTitle, trailingActionVisibility, className, rootAttributes, trailingActionAttributes, slot, }: ListActionRowProps): SafeHtml;
191
787
 
192
788
  export { ListActionRow, type ListActionRowProps };
193
789
  ```
@@ -196,85 +792,241 @@ export { ListActionRow, type ListActionRowProps };
196
792
 
197
793
  ```ts
198
794
  import { SafeHtml } from 'kerfjs';
795
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
199
796
 
200
797
  type ListItemRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
201
798
  'data-component'?: never;
202
799
  'data-action'?: never;
203
800
  'data-item-id'?: never;
204
801
  'data-has-icon'?: never;
802
+ 'data-has-description'?: never;
205
803
  'data-multiline'?: never;
804
+ 'data-density'?: never;
805
+ 'data-divider'?: never;
806
+ 'data-busy'?: never;
206
807
  'data-state'?: never;
808
+ 'data-kui-disabled'?: never;
809
+ 'data-icon-align'?: never;
207
810
  }>;
208
811
  interface ListItemProps {
209
812
  label: string | SafeHtml;
813
+ /** App-owned supporting text rendered in the component's stable label stack. */
814
+ description?: string | SafeHtml;
210
815
  icon?: SafeHtml;
211
- trailing?: SafeHtml;
816
+ /** Dormant author content at the row's end; a placeholder renders it as a skeleton. */
817
+ trailing?: KerfUiContent;
818
+ /** Dormant status metadata rendered before trailing content; a placeholder renders it as a skeleton. */
819
+ status?: string | SafeHtml;
820
+ /** Show a progress indicator and expose the row as busy without replacing its content; a placeholder keeps the indicator. */
821
+ busy?: boolean;
822
+ density?: 'standard' | 'compact' | 'spacious';
823
+ divider?: 'none' | 'before' | 'after' | 'both';
212
824
  selected?: boolean;
213
825
  action: string;
214
826
  itemId?: string;
215
827
  className?: string;
216
- style?: string;
217
828
  pressed?: boolean;
218
829
  accessibleLabel?: string;
219
830
  title?: string;
220
831
  multiline?: boolean;
832
+ /** Alignment of an icon beside a wrapped label. Defaults to its first line. */
833
+ multilineIconAlign?: 'first-line' | 'center';
221
834
  state?: string;
222
835
  disabled?: boolean;
223
836
  tabIndex?: number;
224
- /** Render the row as an unanimated loading skeleton, disabling its action. */
837
+ /** Render the row as an unanimated loading skeleton, disabling its action: label, description, icon, status, and trailing content become skeletons; the `busy` indicator stays live. */
225
838
  placeholder?: boolean;
226
839
  rootAttributes?: ListItemRootAttributes;
840
+ /** Native named-slot assignment when composed inside a web component. */
841
+ slot?: string;
842
+ }
843
+ interface ListItemLinkProps extends Omit<ListItemProps, 'action' | 'pressed'> {
844
+ /** Native destination for this navigation row. */
845
+ href: string;
846
+ /** Open in a new tab with a safe rel and announce that behavior. */
847
+ external?: boolean;
848
+ action?: never;
849
+ pressed?: never;
227
850
  }
228
- declare function ListItem({ label, icon, trailing, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, rootAttributes }: ListItemProps): SafeHtml;
851
+ declare function ListItem({ label, description, icon, trailing, status, busy, density, divider, selected, action, itemId, className, pressed, accessibleLabel, title, multiline, multilineIconAlign, state, disabled, tabIndex, placeholder, rootAttributes, slot, }: ListItemProps): SafeHtml;
852
+ /** A navigation row with native link activation and ListItem geometry. */
853
+ declare function ListItemLink({ href, external, label, description, icon, trailing, status, busy, density, divider, selected, itemId, className, accessibleLabel, title, multiline, multilineIconAlign, state, disabled, tabIndex, placeholder, rootAttributes, slot, }: ListItemLinkProps): SafeHtml;
229
854
 
230
- export { ListItem, type ListItemProps };
855
+ export { ListItem, ListItemLink, type ListItemLinkProps, type ListItemProps };
231
856
  ```
232
857
 
233
- ## `@kerfjs/ui/panel-header`
858
+ ## `@kerfjs/ui/list-inset-control`
234
859
 
235
860
  ```ts
236
- import { SafeHtml } from 'kerfjs';
237
- import { HeadingLevel } from './toolbar-text.js';
861
+ import * as kerfjs from 'kerfjs';
862
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
863
+ import { S as Sides } from './sides-BPSWde0A.js';
864
+
865
+ interface ListInsetControlProps {
866
+ /** Control(s) that own their own border and padding (e.g. an input, a `wa-*`). */
867
+ children: KerfUiContent;
868
+ /** Physical inset sides in canonical top/right/bottom/left order. Defaults to all sides. */
869
+ sides?: Sides;
870
+ className?: string;
871
+ /** Native named-slot assignment when composed inside a web component. */
872
+ slot?: string;
873
+ }
874
+ /**
875
+ * Insets a control into a pane/list content region: an 8px inline margin (so its
876
+ * edges line up with `.kui-content` items) and a stretch flex row with an 8px gap.
877
+ * Use it for controls that carry their own border and padding but no outer margin
878
+ * — the wrapper adds only the alignment margin and layout, not a second inset.
879
+ */
880
+ declare function ListInsetControl({ children, sides, className, slot, }: ListInsetControlProps): kerfjs.SafeHtml;
238
881
 
239
- interface PanelHeaderProps {
240
- title: string;
241
- titleId: string;
242
- summary?: string;
243
- summaryId?: string;
244
- icon?: SafeHtml;
245
- iconClassName?: string;
246
- actions?: SafeHtml;
882
+ export { ListInsetControl, type ListInsetControlProps, Sides };
883
+ ```
884
+
885
+ ## `@kerfjs/ui/list-inset-text`
886
+
887
+ ```ts
888
+ import * as kerfjs from 'kerfjs';
889
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
890
+ import { S as Sides } from './sides-BPSWde0A.js';
891
+
892
+ type ListInsetTextRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
893
+ 'data-component'?: never;
894
+ 'data-sides'?: never;
895
+ }>;
896
+ interface ListInsetTextProps {
897
+ /** Text (or inline content) that carries no margin, border, or padding of its own. */
898
+ children: KerfUiContent | string;
899
+ /** Physical inset sides in canonical top/right/bottom/left order. Defaults to all sides. */
900
+ sides?: Sides;
901
+ className?: string;
902
+ /** Safe `data-*` metadata; component-owned structural attributes stay protected. */
903
+ rootAttributes?: ListInsetTextRootAttributes;
904
+ /** Native named-slot assignment when composed inside a web component. */
905
+ slot?: string;
906
+ }
907
+ /**
908
+ * Gives bare text the content-item geometry — an 8px inline margin, a 1px
909
+ * transparent border, and 8px padding — so a plain string lines up with
910
+ * bordered `.kui-content` items (its text edge lands at the same 17px inset).
911
+ * Use it for text elements that have no margin, border, or padding of their own.
912
+ * Pass `sides="rl"` to keep the horizontal inset but drop the vertical box
913
+ * space for tight text layout.
914
+ */
915
+ declare function ListInsetText({ children, sides, className, rootAttributes, slot, }: ListInsetTextProps): kerfjs.SafeHtml;
916
+
917
+ export { ListInsetText, type ListInsetTextProps, Sides };
918
+ ```
919
+
920
+ ## `@kerfjs/ui/content-item`
921
+
922
+ ```ts
923
+ import * as kerfjs from 'kerfjs';
924
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
925
+ import { S as SemanticTone } from './semantic-tone-CdW013KF.js';
926
+
927
+ /** Base surface treatment or semantic status colors. */
928
+ type ContentItemAppearance = 'transparent' | 'surface' | SemanticTone;
929
+ /** Whether the item's always-reserved 1px border is transparent or visible. */
930
+ type ContentItemFrame = 'none' | 'framed';
931
+ /** Corner shape: the 12px rounded rectangle or the 22px pill. */
932
+ type ContentItemShape = 'rounded' | 'pill';
933
+ /** `single` uses an option; `multiple` uses a grid row; `toggle` uses a pressed button. */
934
+ type ContentItemSelectionMode = 'none' | 'single' | 'multiple' | 'toggle';
935
+ type ContentItemRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
936
+ 'data-component'?: never;
937
+ 'data-appearance'?: never;
938
+ 'data-outlined'?: never;
939
+ }>;
940
+ type ContentItemInteractiveRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
941
+ 'data-component'?: never;
942
+ 'data-appearance'?: never;
943
+ 'data-outlined'?: never;
944
+ 'data-action'?: never;
945
+ 'data-item-id'?: never;
946
+ 'data-interactive'?: never;
947
+ 'data-selected'?: never;
948
+ 'data-disabled'?: never;
949
+ 'data-selection-mode'?: never;
950
+ 'data-kui-pressed'?: never;
951
+ }>;
952
+ interface ContentItemBaseProps {
247
953
  /**
248
- * Expose the title as a heading landmark (`role="heading"` + `aria-level`). Set it
249
- * for a PAGE or view heading so screen-reader heading navigation works and the view
250
- * has a primary heading; omit it (the default) for a dialog title, which is instead
251
- * referenced via `aria-labelledby={titleId}` and needs no heading landmark.
954
+ * Coordinated background, border, and foreground colors. `transparent`
955
+ * preserves the surrounding surface; `surface` paints the base surface;
956
+ * semantic values match StateBanner. Defaults to `transparent`.
252
957
  */
253
- headingLevel?: HeadingLevel;
254
- /** Render the title and summary as unanimated loading skeletons, keeping the icon and actions. */
255
- placeholder?: boolean;
958
+ appearance?: ContentItemAppearance;
959
+ /** Item content; a plain string is allowed for bare copy. */
960
+ children?: KerfUiContent | string;
961
+ /**
962
+ * `framed` paints the standard neutral border in the 1px the item always
963
+ * reserves, so framing never changes geometry. Frame only an item that
964
+ * marks a real distinction. Defaults to `none` (transparent border).
965
+ */
966
+ frame?: ContentItemFrame;
967
+ /** Remove block padding and block borders while retaining inline geometry. */
968
+ flush?: boolean;
969
+ /** Corner shape. Defaults to `rounded`. */
970
+ shape?: ContentItemShape;
971
+ /** Native hover tooltip. */
972
+ title?: string;
973
+ /**
974
+ * Names the item as a distinct region (`role="region"`). Omit it for an
975
+ * ordinary item, which stays a non-landmark grouping.
976
+ */
977
+ ariaLabel?: string;
978
+ /**
979
+ * Makes the item a programmatic focus target (`tabindex="-1"`, never a tab
980
+ * stop), for example the preferred initial focus of a NavStack view when
981
+ * combined with `data-nav-focus` in `rootAttributes`.
982
+ */
983
+ focusTarget?: boolean;
984
+ /** Keep the standard focus outline visible, such as for a drop target. */
985
+ outlined?: boolean;
986
+ className?: string;
987
+ /** Native named-slot assignment when composed inside a web component. */
988
+ slot?: string;
256
989
  }
990
+ type ContentItemProps = ContentItemBaseProps & ({
991
+ interactive?: false;
992
+ action?: never;
993
+ itemId?: never;
994
+ selectionMode?: never;
995
+ selected?: never;
996
+ disabled?: never;
997
+ /** Opt into keyboard (`0`) or programmatic (`-1`) focus. */
998
+ tabIndex?: 0 | -1;
999
+ /** Existing static `data-*` metadata remains available. */
1000
+ rootAttributes?: ContentItemRootAttributes;
1001
+ } | {
1002
+ /** Enable a keyboard reachable card; wire `wireContentItems` once on its containing root. */
1003
+ interactive: true;
1004
+ /** Delegated action dispatched by pointer, Enter, or Space. */
1005
+ action: string;
1006
+ itemId?: string;
1007
+ selectionMode?: ContentItemSelectionMode;
1008
+ selected?: boolean;
1009
+ disabled?: boolean;
1010
+ tabIndex?: never;
1011
+ /** Safe app metadata; interaction attributes are component-owned. */
1012
+ rootAttributes?: ContentItemInteractiveRootAttributes;
1013
+ });
257
1014
  /**
258
- * The heading of a panel, dialog, or page: a plain `Toolbar` whose leading zone
259
- * holds an optional icon control group and the title as extra-large `ToolbarText`,
260
- * whose trailing zone holds the app's action controls, and with an optional
261
- * subtitle on its own row, left-aligned with the title.
262
- *
263
- * PanelHeader overrides no Toolbar styles — it is just a Toolbar with an xl title.
264
- * The only styling it adds is the icon group's fill/border color and the subtitle.
265
- * When no icon is provided, the icon group is omitted entirely. The `actions` slot
266
- * is passed straight into the toolbar's trailing zone; the app supplies whatever
267
- * trailing controls it needs (typically a `ToolbarControlGroup`).
1015
+ * One self-contained `.kui-content` child: an 8px inline margin, a real 1px
1016
+ * border (transparent unless `framed`), 8px padding, and a rounded or pill
1017
+ * radius. It owns that whole geometry, so wrappers must not add more.
268
1018
  */
269
- declare function PanelHeader({ title, titleId, summary, summaryId, icon, iconClassName, actions, headingLevel, placeholder }: PanelHeaderProps): SafeHtml;
1019
+ declare function ContentItem({ appearance, children, frame, flush, shape, title, ariaLabel, focusTarget, outlined, tabIndex, interactive, action, itemId, selectionMode, selected, disabled, className, rootAttributes, slot, }: ContentItemProps): kerfjs.SafeHtml;
270
1020
 
271
- export { PanelHeader, type PanelHeaderProps };
1021
+ export { ContentItem, type ContentItemAppearance, type ContentItemFrame, type ContentItemProps, type ContentItemSelectionMode, type ContentItemShape, SemanticTone };
272
1022
  ```
273
1023
 
274
1024
  ## `@kerfjs/ui/value-table`
275
1025
 
276
1026
  ```ts
1027
+ import * as kerfjs from 'kerfjs';
277
1028
  import { SafeHtml } from 'kerfjs';
1029
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
278
1030
 
279
1031
  interface ValueTableRowProps {
280
1032
  label: string | SafeHtml;
@@ -283,15 +1035,21 @@ interface ValueTableRowProps {
283
1035
  className?: string;
284
1036
  /** Render the value as an unanimated loading skeleton, keeping the field label. */
285
1037
  placeholder?: boolean;
1038
+ /** Native named-slot assignment when composed inside a web component. */
1039
+ slot?: string;
286
1040
  }
287
- declare function ValueTableRow({ label, value, icon, className, placeholder }: ValueTableRowProps): SafeHtml;
1041
+ declare function ValueTableRow({ label, value, icon, className, placeholder, slot, }: ValueTableRowProps): SafeHtml;
288
1042
 
289
1043
  interface ValueTableProps {
290
1044
  label: string;
1045
+ /** Compact row spacing for metadata-dense surfaces. */
1046
+ density?: 'default' | 'compact';
291
1047
  className?: string;
292
- children: SafeHtml | readonly SafeHtml[];
1048
+ children: KerfUiContent;
1049
+ /** Native named-slot assignment when composed inside a web component. */
1050
+ slot?: string;
293
1051
  }
294
- declare function ValueTable({ label, className, children }: ValueTableProps): SafeHtml;
1052
+ declare function ValueTable({ label, density, className, children, slot, }: ValueTableProps): kerfjs.SafeHtml;
295
1053
 
296
1054
  export { ValueTable, type ValueTableProps, ValueTableRow, type ValueTableRowProps };
297
1055
  ```
@@ -300,6 +1058,7 @@ export { ValueTable, type ValueTableProps, ValueTableRow, type ValueTableRowProp
300
1058
 
301
1059
  ```ts
302
1060
  import { SafeHtml } from 'kerfjs';
1061
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
303
1062
 
304
1063
  type AppTabRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
305
1064
  'data-component'?: never;
@@ -308,15 +1067,36 @@ type AppTabRootAttributes = Readonly<Record<`data-${string}`, string | undefined
308
1067
  'data-selected'?: never;
309
1068
  'data-tab-dragging'?: never;
310
1069
  'data-tab-drop-position'?: never;
1070
+ 'data-attention'?: never;
1071
+ 'data-drop-target'?: never;
1072
+ 'data-name-overflow'?: never;
1073
+ 'data-pinned'?: never;
311
1074
  }>;
1075
+ type AppTabPresentation = 'pill' | 'segmented' | 'icon-only';
1076
+ type AppTabSize = 'default' | 'compact';
1077
+ type AppTabNameOverflow = 'ellipsis' | 'visible';
312
1078
  interface AppTabProps {
313
1079
  id: string;
314
1080
  name: string;
315
1081
  selected?: boolean;
1082
+ /** Emphasize the visible tab name with the attention color token. */
1083
+ attention?: boolean;
1084
+ /** Highlight this tab as the target of a drag over its content. */
1085
+ dropTarget?: boolean;
1086
+ /** Keep the leading tab visible within its TabBar tablist while peers scroll. */
1087
+ pinned?: boolean;
316
1088
  closable?: boolean;
317
1089
  draggable?: boolean;
318
- leading?: SafeHtml;
319
- trailing?: SafeHtml;
1090
+ leading?: KerfUiContent;
1091
+ trailing?: KerfUiContent;
1092
+ /** Visual treatment within a TabBar. Icon-only tabs retain `name` as their accessible name. */
1093
+ presentation?: AppTabPresentation;
1094
+ /** Compact tabs use the 32px application-rail height. */
1095
+ size?: AppTabSize;
1096
+ /** Maximum visible label width in CSS pixels before ellipsis. */
1097
+ labelMaxWidth?: number;
1098
+ /** Keep the full name visible for an inline loading treatment. */
1099
+ nameOverflow?: AppTabNameOverflow;
320
1100
  /** Decorative dormant content for the close button. Must not contain interactive descendants. */
321
1101
  closeIcon?: SafeHtml;
322
1102
  selectAction?: string;
@@ -324,35 +1104,85 @@ interface AppTabProps {
324
1104
  className?: string;
325
1105
  /** Render as an unanimated loading skeleton, disabling select/close and dragging. */
326
1106
  placeholder?: boolean;
1107
+ /**
1108
+ * Named and still opening: the tab is known but its content is loading.
1109
+ * Keeps `name` visible in the quiet text color and as the tab's accessible
1110
+ * name, shows `trailing` (for example a `LoadingSpinner`), and marks the
1111
+ * tab `aria-busy`. Unlike `placeholder` it stays selectable — the
1112
+ * application shows placeholder content in its panel until loading
1113
+ * completes — while close and dragging stay disabled. Same pill geometry
1114
+ * as the live tab, so swapping it in place does not shift the bar.
1115
+ * `placeholder` wins when both are set.
1116
+ */
1117
+ pending?: boolean;
327
1118
  rootAttributes?: AppTabRootAttributes;
1119
+ /** Native named-slot assignment when composed inside a web component. */
1120
+ slot?: string;
328
1121
  }
329
- declare function AppTab({ id, name, selected, closable, draggable, leading, trailing, closeIcon, selectAction, closeAction, className, placeholder, rootAttributes }: AppTabProps): SafeHtml;
1122
+ declare function AppTab({ id, name, selected, attention, dropTarget, pinned, closable, draggable, leading, trailing, presentation, size, labelMaxWidth, nameOverflow, closeIcon, selectAction, closeAction, className, placeholder, pending, rootAttributes, slot, }: AppTabProps): SafeHtml;
330
1123
 
331
- export { AppTab, type AppTabProps };
1124
+ export { AppTab, type AppTabNameOverflow, type AppTabPresentation, type AppTabProps, type AppTabSize };
332
1125
  ```
333
1126
 
334
1127
  ## `@kerfjs/ui/tab-bar`
335
1128
 
336
1129
  ```ts
337
- import { SafeHtml } from 'kerfjs';
338
-
1130
+ import * as kerfjs from 'kerfjs';
1131
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1132
+
1133
+ type TabActivation = 'automatic' | 'manual';
1134
+ type TabBarAllocation = 'intrinsic' | 'fill';
1135
+ type TabBarPresentation = 'rail' | 'segmented' | 'inspector';
1136
+ type TabBarTrailingPlacement = 'separate' | 'adjacent';
1137
+ /** Tablist width where segmented AppTabs switch to icon-only. */
1138
+ type TabBarIconOnlyAt = 'wide' | 'narrow' | 'compact';
339
1139
  interface TabBarProps {
340
1140
  id: string;
341
1141
  label: string;
342
- children: SafeHtml | readonly SafeHtml[];
343
- leading?: SafeHtml;
344
- trailing?: SafeHtml;
1142
+ children: KerfUiContent;
1143
+ leading?: KerfUiContent;
1144
+ /** Tab-local action: a ToolbarControlGroup or a standalone Web Awesome button. */
1145
+ trailing?: KerfUiContent;
1146
+ /**
1147
+ * Action pinned to the far edge of the bar. Combine with an adjacent `trailing`
1148
+ * action when the tab-local and workspace-level actions must remain distinct.
1149
+ * Accepts a ToolbarControlGroup or a standalone Web Awesome button.
1150
+ */
1151
+ end?: KerfUiContent;
345
1152
  className?: string;
1153
+ /**
1154
+ * Keyboard activation mode for this strip, emitted as `data-tab-activation` for
1155
+ * `wireTabBars` to read (overrides its `activation` option). `'automatic'` (default)
1156
+ * selects on arrow / Home / End; `'manual'` moves roving focus only and the user
1157
+ * selects with Enter / Space / click — use it when selecting a tab is a heavy action.
1158
+ */
1159
+ activation?: TabActivation;
1160
+ /** How available strip width is allocated across child AppTabs. */
1161
+ allocation?: TabBarAllocation;
1162
+ /** Named strip chrome for application rails, segmented tabs, or inspectors. */
1163
+ presentation?: TabBarPresentation;
1164
+ /** Switch segmented AppTabs to icon-only at tablist widths of 832, 704, or 448px. Each tab needs a leading icon. */
1165
+ iconOnlyAt?: TabBarIconOnlyAt;
1166
+ /** Keep a trailing action beside the final tab or at the far edge of the bar. */
1167
+ trailingPlacement?: TabBarTrailingPlacement;
1168
+ /** Settle scrolling peers at whole-tab starts; wireTabBars measures a pinned leading tab's inset. */
1169
+ snapTabs?: boolean;
1170
+ /** Native named-slot assignment when composed inside a web component. */
1171
+ slot?: string;
346
1172
  }
347
1173
  /** Render a controlled tab strip. The application owns selection, order, and persistence. */
348
- declare function TabBar({ id, label, children, leading, trailing, className }: TabBarProps): SafeHtml;
1174
+ declare function TabBar({ id, label, children, leading, trailing, end, className, activation, allocation, presentation, iconOnlyAt, trailingPlacement, snapTabs, slot, }: TabBarProps): kerfjs.SafeHtml;
349
1175
 
350
- export { TabBar, type TabBarProps };
1176
+ export { type TabActivation, TabBar, type TabBarAllocation, type TabBarIconOnlyAt, type TabBarPresentation, type TabBarProps, type TabBarTrailingPlacement };
351
1177
  ```
352
1178
 
353
1179
  ## `@kerfjs/ui/wire-tab-bars`
354
1180
 
355
1181
  ```ts
1182
+ import { TabActivation } from './tab-bar.js';
1183
+ import 'kerfjs';
1184
+ import './semantic-content-BbzjvSu9.js';
1185
+
356
1186
  type TabReorderSource = 'pointer' | 'keyboard';
357
1187
  type TabDropPosition = 'before' | 'after';
358
1188
  interface TabReorder {
@@ -362,57 +1192,122 @@ interface TabReorder {
362
1192
  position: TabDropPosition;
363
1193
  source: TabReorderSource;
364
1194
  }
1195
+
365
1196
  interface WireTabBarsOptions {
366
1197
  onReorder: (change: TabReorder) => void;
1198
+ /**
1199
+ * How arrow / Home / End keys activate tabs (default `'automatic'`):
1200
+ * - `'automatic'` moves roving focus **and** selects the focused tab (clicks it).
1201
+ * - `'manual'` moves roving focus only; the user selects with Enter / Space / click
1202
+ * (the ARIA Tabs manual-activation pattern). Use this when activation is a heavy or
1203
+ * side-effecting action (e.g. a tab that loads a project) so arrowing through the
1204
+ * strip doesn't trigger it on every tab.
1205
+ *
1206
+ * A per-bar `data-tab-activation="manual" | "automatic"` attribute (see the `TabBar`
1207
+ * `activation` prop) overrides this option for that strip.
1208
+ */
1209
+ activation?: TabActivation;
367
1210
  }
368
1211
  declare function reorderTabs<T>(items: readonly T[], getId: (item: T) => string, sourceId: string, targetId: string, position: TabDropPosition): T[];
369
1212
  /** Wire reordering and keyboard navigation while leaving controlled state in the application. */
370
- declare function wireTabBars(root: HTMLElement | Document, { onReorder }: WireTabBarsOptions): () => void;
1213
+ declare function wireTabBars(root: HTMLElement | Document, { onReorder, activation }: WireTabBarsOptions): () => void;
371
1214
 
372
- export { type TabDropPosition, type TabReorder, type TabReorderSource, type WireTabBarsOptions, reorderTabs, wireTabBars };
1215
+ export { TabActivation, type TabDropPosition, type TabReorder, type TabReorderSource, type WireTabBarsOptions, reorderTabs, wireTabBars };
373
1216
  ```
374
1217
 
375
- ## `@kerfjs/ui/nav-stack`
1218
+ ## `@kerfjs/ui/wire-scroll-dividers`
376
1219
 
377
1220
  ```ts
378
- import { SafeHtml } from 'kerfjs';
379
-
380
1221
  /**
381
- * One entry in a {@link NavStack}. The app owns the stack as an array (usually a
382
- * signal); `NavStack` renders it and `wireNavStack` animates the transitions.
1222
+ * An app-owned scroll arrangement outside a `Pane`: the `id` of the element
1223
+ * that scrolls and the `id`s of the pinned chrome on each physical side of it.
1224
+ * Each named chrome element shows its divider on the side facing the scroller
1225
+ * while content is scrolled away beyond that side. Ids are resolved below the
1226
+ * wired root on every refresh, so a re-render that replaces an element keeps
1227
+ * the pairing.
383
1228
  */
384
- interface NavStackView {
385
- /** Stable identity for keyed reconcile and transition direction. */
386
- key: string;
387
- content: SafeHtml;
388
- /** Title shown in the top toolbar for this view. */
389
- title?: string;
390
- /** Trailing actions for this view's top toolbar. */
391
- toolbar?: SafeHtml;
1229
+ interface ScrollDividerTarget {
1230
+ /** The `id` of the scrolling element. */
1231
+ scroller: string;
1232
+ /** Chrome above the scroller: shows its bottom divider once scrolled down. */
1233
+ top?: string;
1234
+ /**
1235
+ * Chrome right of the scroller: shows its left divider while content is
1236
+ * hidden beyond the scroller's right edge (never when nothing overflows).
1237
+ */
1238
+ right?: string;
1239
+ /**
1240
+ * Chrome below the scroller: shows its top divider while content is hidden
1241
+ * beyond the scroller's bottom edge (never when nothing overflows).
1242
+ */
1243
+ bottom?: string;
1244
+ /** Chrome left of the scroller: shows its right divider once scrolled right. */
1245
+ left?: string;
392
1246
  }
393
- interface NavStackProps {
394
- id: string;
395
- /** Accessible name for the stack region. */
396
- label: string;
397
- /** The stack, root first; the last entry is the active top view. */
398
- views: NavStackView[];
399
- /** Accessible label for the back control (default "Back"). */
400
- backLabel?: string;
401
- /** Hide the top toolbar entirely (rare — a fully custom-chrome view). */
402
- hideToolbar?: boolean;
403
- /** Optional persistent bottom toolbar. */
404
- bottomToolbar?: SafeHtml;
405
- className?: string;
1247
+ interface WireScrollDividersOptions {
1248
+ /**
1249
+ * App-owned scroll arrangements to pair beyond the ones found by structure
1250
+ * (every `Pane`'s header and footer around its content, every `NavStack`'s
1251
+ * and `TabNavigator`'s chrome around its active region, and every `TabBar`
1252
+ * strip).
1253
+ */
1254
+ targets?: readonly ScrollDividerTarget[];
406
1255
  }
1256
+ type Root = HTMLElement | Document;
407
1257
  /**
408
- * A navigation stack (iOS-style push/pop). Renders every entry stacked, the last
409
- * one active; `@kerfjs/ui/wire-nav-stack`'s `wireNavStack` slides the content and
410
- * cross-fades the chrome across a change. A single-pane layout is a `NavStack`
411
- * with one entry. See `docs/23-app-layouts.md` §3.1.
1258
+ * Wire scroll dividers below `root`: a divider between pinned chrome and the
1259
+ * content that scrolls beside it shows only while content is scrolled away
1260
+ * from that edge. Near edges (top, left) hide at the scroll start; far edges
1261
+ * (bottom, right) hide at the scroll end and whenever nothing overflows.
1262
+ *
1263
+ * The wiring only reports scroll state; each component draws its own divider
1264
+ * from it. It writes `data-scroll-overflow` (the edges with hidden content, in
1265
+ * canonical `t`/`r`/`b`/`l` order) on each scroller, and `data-scroll-divider`
1266
+ * (the sides to draw) on each paired chrome element:
1267
+ *
1268
+ * - every `Pane` with a header or footer: its header shows a bottom divider and
1269
+ * its footer a top divider (drawn by the Pane, per its `chromeDividers`);
1270
+ * - every `NavStack`'s top chrome and bottom toolbar around its active view,
1271
+ * and every `TabNavigator`'s bar under its active scene: the chrome shows a
1272
+ * bottom (top chrome) or top (bottom toolbar, bar) divider. The scroller is
1273
+ * whichever element actually scrolls there: the view or scene itself, or,
1274
+ * through a sole child with no chrome of its own on that edge, a `Pane`'s
1275
+ * content or a nested `NavStack` / `TabNavigator` region (a Pane's own header
1276
+ * or footer draws that boundary instead);
1277
+ * - every `TabBar` strip: the bar draws a divider on each side of the strip
1278
+ * whose tabs are scrolled out of view;
1279
+ * - each app-owned `targets` pairing: a `Toolbar` or `List` named as chrome
1280
+ * draws the divider on its facing side.
1281
+ *
1282
+ * Structure is re-read after every DOM change below root (a re-render that
1283
+ * drops the attributes gets them back before paint), scroll is tracked with
1284
+ * one capturing listener per registered root, and size changes of each
1285
+ * scroller and its children with a `ResizeObserver`. Overlapping registrations
1286
+ * share one attribute writer; each disposer removes its registration, and the
1287
+ * final disposer removes the wiring-owned attributes. See
1288
+ * `docs/23-app-layouts.md` §3.7.
412
1289
  */
413
- declare function NavStack({ id, label, views, backLabel, hideToolbar, bottomToolbar, className }: NavStackProps): SafeHtml;
1290
+ declare function wireScrollDividers(root: Root, { targets }?: WireScrollDividersOptions): () => void;
1291
+
1292
+ export { type ScrollDividerTarget, type WireScrollDividersOptions, wireScrollDividers };
1293
+ ```
1294
+
1295
+ ## `@kerfjs/ui/nav-stack`
414
1296
 
415
- export { NavStack, type NavStackProps, type NavStackView };
1297
+ ```ts
1298
+ export { c as NavStack, N as NavStackProps, d as NavStackToolbarConfig, a as NavStackView, e as NavStackViewBottomToolbar, f as NavStackViewToolbar } from './nav-stack-BvSKY448.js';
1299
+ import 'kerfjs';
1300
+ import './semantic-content-BbzjvSu9.js';
1301
+ import './list.js';
1302
+ import './flex-alignment-4ms8ZbV8.js';
1303
+ import './sides-BPSWde0A.js';
1304
+ import './css-values.js';
1305
+ import './pane.js';
1306
+ import './panel-toolbar-CL9tYOkh.js';
1307
+ import './toolbar.js';
1308
+ import './lucide-icon.js';
1309
+ import 'lucide';
1310
+ import './toolbar-text.js';
416
1311
  ```
417
1312
 
418
1313
  ## `@kerfjs/ui/wire-nav-stack`
@@ -428,7 +1323,10 @@ interface WireNavStackOptions {
428
1323
  * Animate a `NavStack`'s push/pop transitions and wire its back control. The app
429
1324
  * owns the stack (a signal of `NavStackView[]`) and re-renders `NavStack` when it
430
1325
  * changes; this helper slides the content and settles the chrome across each
431
- * change, and calls `onBack` when the back control is used. Returns a disposer.
1326
+ * change, moves focus into every new top view, restores that view's last focused
1327
+ * descendant on pop, and calls `onBack` when the back control is used. Mark a
1328
+ * preferred initial target with `data-nav-focus`; otherwise the first focusable
1329
+ * descendant (or the view itself) receives focus. Returns a disposer.
432
1330
  */
433
1331
  declare function wireNavStack(root: Element, options?: WireNavStackOptions): () => void;
434
1332
 
@@ -438,20 +1336,53 @@ export { type WireNavStackOptions, wireNavStack };
438
1336
  ## `@kerfjs/ui/split-view`
439
1337
 
440
1338
  ```ts
441
- import { SafeHtml } from 'kerfjs';
1339
+ import * as kerfjs from 'kerfjs';
1340
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1341
+ import { N as NavStackProps, a as NavStackView } from './nav-stack-BvSKY448.js';
1342
+ import { ResizableRegionProps } from './resizable-region.js';
1343
+ import './list.js';
1344
+ import './flex-alignment-4ms8ZbV8.js';
1345
+ import './sides-BPSWde0A.js';
1346
+ import './css-values.js';
1347
+ import './pane.js';
1348
+ import './panel-toolbar-CL9tYOkh.js';
1349
+ import './toolbar.js';
1350
+ import './lucide-icon.js';
1351
+ import 'lucide';
1352
+ import './toolbar-text.js';
442
1353
 
443
- interface SplitViewResizable {
1354
+ /**
1355
+ * The roomy list pane's `ResizableRegion`: its committed `size` and `min` /
1356
+ * `max` limits, plus the region configuration that forwards unchanged
1357
+ * (`separator`, `handleIcon`, `contentOverflow`, and collapse:
1358
+ * `collapsed`, `transitioning`, `collapseMotion`, `restoreControl`,
1359
+ * `restorePosition`). Omitted options keep the region's defaults.
1360
+ */
1361
+ interface SplitViewResizable extends Pick<ResizableRegionProps, 'separator' | 'handleIcon' | 'contentOverflow' | 'collapsed' | 'transitioning' | 'collapseMotion' | 'restoreControl' | 'restorePosition'> {
444
1362
  size: number;
445
1363
  min: number;
446
1364
  max: number;
447
1365
  }
1366
+ /** One compact view's top- and bottom-toolbar content (see `NavStackView`). */
1367
+ type SplitViewCompactViewToolbars = Pick<NavStackView, 'toolbar' | 'bottomToolbar'>;
1368
+ /**
1369
+ * The compact `NavStack`'s configuration: its toolbar configuration, back
1370
+ * control, persistent bottom toolbar, and chrome dividers forward to the
1371
+ * stack, and `list` / `detail` give each view its own toolbar groups.
1372
+ */
1373
+ interface SplitViewCompactStack extends Pick<NavStackProps, 'toolbarConfig' | 'backIcon' | 'backText' | 'hideToolbar' | 'bottomToolbar' | 'chromeDividers'> {
1374
+ /** Toolbar content for the list (root) view. */
1375
+ list?: SplitViewCompactViewToolbars;
1376
+ /** Toolbar content for the pushed detail view. */
1377
+ detail?: SplitViewCompactViewToolbars;
1378
+ }
448
1379
  interface SplitViewProps {
449
1380
  id: string;
450
1381
  label: string;
451
1382
  /** The list (primary) pane. */
452
- list: SafeHtml;
1383
+ list: KerfUiContent;
453
1384
  /** The detail (secondary) pane. */
454
- detail: SafeHtml;
1385
+ detail: KerfUiContent;
455
1386
  /**
456
1387
  * Compact ("one pane at a time") classes — a handset or portrait tablet.
457
1388
  * Derive from `deviceClass().value.compact`. When true the split collapses to
@@ -466,9 +1397,13 @@ interface SplitViewProps {
466
1397
  detailTitle?: string;
467
1398
  /** Back label for the compact NavStack (default "Back"). */
468
1399
  backLabel?: string;
1400
+ /** Compact NavStack configuration and per-view toolbars. */
1401
+ compactStack?: SplitViewCompactStack;
469
1402
  /** A resizable separator on roomy classes (min/max px). Omit for a fixed split. */
470
1403
  resizable?: SplitViewResizable;
471
1404
  className?: string;
1405
+ /** Native named-slot assignment when composed inside a web component. */
1406
+ slot?: string;
472
1407
  }
473
1408
  /**
474
1409
  * A list-detail split. On roomy classes it shows both panes side
@@ -477,107 +1412,867 @@ interface SplitViewProps {
477
1412
  * resizable wiring with `wireResizableRegions` and the compact back with
478
1413
  * `wireNavStack`.
479
1414
  */
480
- declare function SplitView({ id, label, list, detail, compact, detailActive, listTitle, detailTitle, backLabel, resizable, className }: SplitViewProps): SafeHtml;
1415
+ declare function SplitView({ id, label, list, detail, compact, detailActive, listTitle, detailTitle, backLabel, compactStack, resizable, className, slot, }: SplitViewProps): kerfjs.SafeHtml;
481
1416
 
482
- export { SplitView, type SplitViewProps, type SplitViewResizable };
1417
+ export { SplitView, type SplitViewCompactStack, type SplitViewCompactViewToolbars, type SplitViewProps, type SplitViewResizable };
483
1418
  ```
484
1419
 
485
- ## `@kerfjs/ui/workbench`
1420
+ ## `@kerfjs/ui/pane`
486
1421
 
487
1422
  ```ts
488
- import { SafeHtml } from 'kerfjs';
1423
+ import * as kerfjs from 'kerfjs';
1424
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
489
1425
 
490
- /** A collapsible Workbench panel — a side rail or the bottom drawer. */
491
- interface WorkbenchPanel {
492
- content: SafeHtml;
493
- /** Whether the panel is currently collapsed (the app owns this). */
494
- collapsed?: boolean;
495
- /** Rail width, or drawer height, in px. Overrides the CSS default. */
496
- size?: number;
497
- /** Accessible name for the panel region. */
498
- label?: string;
499
- }
500
- interface WorkbenchProps {
501
- id: string;
502
- label: string;
503
- /** The central work area. */
504
- main: SafeHtml;
505
- leftRail?: WorkbenchPanel;
506
- rightRail?: WorkbenchPanel;
1426
+ /**
1427
+ * Logical pane sides: where a {@link Pane} can show a separator and where it can
1428
+ * compensate for a device safe-area inset.
1429
+ */
1430
+ type PaneSeparatorSide = 'block-start' | 'block-end' | 'inline-start' | 'inline-end';
1431
+ /** Semantic root elements supported by {@link Pane}. */
1432
+ type PaneElement = 'article' | 'aside' | 'div' | 'main' | 'section';
1433
+ /** Semantic elements supported by the scrolling content slot. */
1434
+ type PaneContentElement = 'div' | 'main' | 'nav' | 'section';
1435
+ type PaneRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
1436
+ 'data-component'?: never;
1437
+ 'data-separator-block-start'?: never;
1438
+ 'data-separator-block-end'?: never;
1439
+ 'data-separator-inline-start'?: never;
1440
+ 'data-separator-inline-end'?: never;
1441
+ 'data-safe-area-block-start'?: never;
1442
+ 'data-safe-area-block-end'?: never;
1443
+ 'data-safe-area-inline-start'?: never;
1444
+ 'data-safe-area-inline-end'?: never;
1445
+ 'data-chrome-placement'?: never;
1446
+ 'data-chrome-dividers'?: never;
1447
+ 'data-appearance'?: never;
1448
+ 'data-deep-inset'?: never;
1449
+ 'data-outlined'?: never;
1450
+ }>;
1451
+ /** How a Pane's header and footer relate to its scrolling content. */
1452
+ type PaneChromePlacement = 'fixed' | 'auto';
1453
+ /**
1454
+ * When a Pane draws the divider between its pinned header or footer and its
1455
+ * scrolling content: `scroll` while content is scrolled away beneath that
1456
+ * chrome, as `wireScrollDividers` reports; `always`; or `none`.
1457
+ */
1458
+ type PaneChromeDividers = 'scroll' | 'always' | 'none';
1459
+ /** Background treatment of the Pane's scrolling work surface. */
1460
+ type PaneAppearance = 'default' | 'sunken';
1461
+ interface PaneProps {
1462
+ /** Optional fixed chrome above the scrolling content, arranged vertically. */
1463
+ header?: KerfUiContent;
1464
+ /** The pane's primary vertical, scrolling content stack. */
1465
+ children?: KerfUiContent;
1466
+ /** Optional fixed chrome below the scrolling content. */
1467
+ footer?: KerfUiContent;
1468
+ /**
1469
+ * Whether the header and footer stay pinned around the scrolling content.
1470
+ * `fixed` (default) always pins them. `auto` pins them while the pane is
1471
+ * tall enough, and below 480px of height (30rem, so it scales with the text
1472
+ * size) lets the whole pane scroll as one column, so tall chrome can never
1473
+ * squeeze the content to nothing.
1474
+ */
1475
+ chromePlacement?: PaneChromePlacement;
1476
+ /**
1477
+ * The divider under the header and over the footer, where they meet the
1478
+ * scrolling content. Omitted defaults to `scroll` for a plain pane and
1479
+ * `always` for `appearance="sunken"`. `scroll` shows the header's divider only
1480
+ * while the content is scrolled down, and the footer's only while more
1481
+ * content lies below — never when the content fits — once
1482
+ * `wireScrollDividers` (`@kerfjs/ui/wire-scroll-dividers`) is wired above
1483
+ * the pane; unwired, neither shows. `always` shows both; `none` neither.
1484
+ * The line is drawn inside the chrome, so no state moves the content.
1485
+ */
1486
+ chromeDividers?: PaneChromeDividers;
1487
+ /** Paint the scrolling work surface with the shared lowered-surface color. */
1488
+ appearance?: PaneAppearance;
1489
+ /** Add 8px to every side of scrolling content (default: false). */
1490
+ deepInset?: boolean;
1491
+ /** Opt into keyboard (`0`) or programmatic (`-1`) focus on the Pane root. */
1492
+ tabIndex?: 0 | -1;
1493
+ /** Keep the standard focus outline visible, such as for a drop target. */
1494
+ outlined?: boolean;
1495
+ /** Root semantics. Defaults to `div`. */
1496
+ element?: PaneElement;
1497
+ /** Scrolling content semantics. Defaults to `div`. */
1498
+ contentElement?: PaneContentElement;
1499
+ /** Independent logical-edge separator lines. Defaults to none. */
1500
+ separators?: readonly PaneSeparatorSide[];
1501
+ /**
1502
+ * Sides on which the pane may compensate for a device safe-area inset.
1503
+ * Defaults to every side: the pane pads its slots for each side that still
1504
+ * reaches an unsafe screen edge, as its surrounding layout reports, while its
1505
+ * background and separators paint through. List only the sides an app-owned
1506
+ * layout places at a screen edge, or pass `[]` to opt out.
1507
+ */
1508
+ safeAreaEdges?: readonly PaneSeparatorSide[];
1509
+ id?: string;
1510
+ /** Accessible name for a landmark root such as `aside` or `main`. */
1511
+ label?: string;
1512
+ /** Accessible name for a landmark scrolling slot such as `nav`. */
1513
+ contentLabel?: string;
1514
+ className?: string;
1515
+ headerClassName?: string;
1516
+ contentClassName?: string;
1517
+ footerClassName?: string;
1518
+ /** Safe `data-*` metadata; Pane-owned structural attributes remain protected. */
1519
+ rootAttributes?: PaneRootAttributes;
1520
+ /** Native named-slot assignment when composed inside a web component. */
1521
+ slot?: string;
1522
+ }
1523
+ /**
1524
+ * A pane's configuration, apart from its content and styling hooks: the props a
1525
+ * composite that renders a `Pane` for the app (a Workbench panel or work area,
1526
+ * or a CollapsiblePanel) forwards, so the app configures that pane instead of
1527
+ * styling it. An omitted or `undefined` field keeps the composite's default.
1528
+ */
1529
+ type PaneConfig = Pick<PaneProps, 'contentElement' | 'contentLabel' | 'separators' | 'safeAreaEdges' | 'chromeDividers' | 'appearance' | 'deepInset' | 'tabIndex' | 'outlined'>;
1530
+ /**
1531
+ * An unpadded application column with optional fixed header/footer slots and one
1532
+ * scrolling vertical content owner. Separator lines are independently opt-in on
1533
+ * each logical edge, so the same component works as a sidebar, main area,
1534
+ * inspector, or dialog column.
1535
+ */
1536
+ declare function Pane({ header, children, footer, chromePlacement, chromeDividers, appearance, deepInset, tabIndex, outlined, element, contentElement, separators, safeAreaEdges, id, label, contentLabel, className, headerClassName, contentClassName, footerClassName, rootAttributes, slot, }: PaneProps): kerfjs.SafeHtml;
1537
+
1538
+ export { Pane, type PaneAppearance, type PaneChromeDividers, type PaneChromePlacement, type PaneConfig, type PaneContentElement, type PaneElement, type PaneProps, type PaneSeparatorSide };
1539
+ ```
1540
+
1541
+ ## `@kerfjs/ui/workbench`
1542
+
1543
+ ```ts
1544
+ import { SafeHtml } from 'kerfjs';
1545
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1546
+ import { b as NavPane, N as NavStackProps } from './nav-stack-BvSKY448.js';
1547
+ import { ListConfig } from './list.js';
1548
+ import { PaneConfig } from './pane.js';
1549
+ import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
1550
+ import { a as PanelToolbar, c as PanelBottomToolbar, b as PanelChromePlacement, d as PanelToggle } from './panel-toolbar-CL9tYOkh.js';
1551
+ import { ToolbarConfig } from './toolbar.js';
1552
+ import './toolbar-text.js';
1553
+ import './css-values.js';
1554
+ import './flex-alignment-4ms8ZbV8.js';
1555
+ import './sides-BPSWde0A.js';
1556
+ import './lucide-icon.js';
1557
+ import 'lucide';
1558
+
1559
+ /** The standard toggle a Workbench renders for a panel (see {@link PanelToggle}). */
1560
+ type WorkbenchPanelToggle = PanelToggle;
1561
+ /**
1562
+ * A Workbench panel's top toolbar, composed by the Workbench so its groups can
1563
+ * follow the panel's open state (see {@link PanelToolbar} for the roles).
1564
+ * A collapsed rail's marked groups and toggle go to the leading edge of
1565
+ * `mainToolbar` (left rail) or its trailing edge (right rail); a collapsed
1566
+ * drawer's go to the trailing edge of `mainBottomToolbar`, else to a
1567
+ * `FloatingToolbar` in the work area's bottom-end corner.
1568
+ */
1569
+ type WorkbenchPanelToolbar = PanelToolbar;
1570
+ type WorkbenchPanelBottomToolbar = PanelBottomToolbar;
1571
+ /**
1572
+ * The work area's top toolbar; collapsed rails add their groups to it. Its
1573
+ * configuration forwards to its `Toolbar`. It draws no divider of its own by
1574
+ * default: the work area's `Pane` draws one under its header chrome, wherever
1575
+ * that chrome ends, while `main` is scrolled (`mainPane.chromeDividers`).
1576
+ */
1577
+ interface WorkbenchMainToolbar extends ToolbarConfig {
1578
+ label: string;
1579
+ /** The work area's title, usually an extra-large `ToolbarText`. */
1580
+ title?: KerfUiContent;
1581
+ /** Groups after the title. */
1582
+ leading?: KerfUiContent;
1583
+ center?: KerfUiContent;
1584
+ /** Groups at the trailing edge, before a collapsed right rail's groups. */
1585
+ trailing?: KerfUiContent;
1586
+ }
1587
+ /**
1588
+ * The work area's bottom toolbar; a collapsed drawer adds its groups to it.
1589
+ * Its configuration forwards to its `Toolbar`. It draws no divider of its own
1590
+ * by default: the work area's `Pane` draws one over its footer chrome while
1591
+ * more of `main` lies below (`mainPane.chromeDividers`).
1592
+ */
1593
+ interface WorkbenchMainBottomToolbar extends ToolbarConfig {
1594
+ label: string;
1595
+ leading?: KerfUiContent;
1596
+ center?: KerfUiContent;
1597
+ trailing?: KerfUiContent;
1598
+ }
1599
+ /**
1600
+ * Whether the work area's header or footer chrome stays pinned (`fixed`),
1601
+ * scrolls away with the content (`scroll`), or stays pinned while the work
1602
+ * area is tall enough and scrolls with the content when it is short (`auto`,
1603
+ * the Pane's `chromePlacement="auto"`; it applies to the pinned header and
1604
+ * footer together).
1605
+ */
1606
+ type WorkbenchChromePlacement = PanelChromePlacement;
1607
+
1608
+ /**
1609
+ * The Workbench container breakpoint below which a panel presents as an
1610
+ * overlay: `narrow` (704px or less) or `compact` (448px or less) — the same
1611
+ * breakpoints as `ResizableRegion`'s `responsiveFillAt` — or `never` to keep
1612
+ * it inline at every width.
1613
+ */
1614
+ type WorkbenchResponsiveOverlayAt = 'compact' | 'narrow' | 'never' | number;
1615
+ /**
1616
+ * How wide a rail's overlay is in a compact (448px or less) Workbench: `inset`
1617
+ * fills the Workbench less a dismiss margin on the side away from the rail's
1618
+ * edge, so a press beside it closes it; `full` fills the Workbench.
1619
+ */
1620
+ type WorkbenchCompactOverlay = 'inset' | 'full';
1621
+ /** Drag-resize limits for a resizable Workbench panel, in px. */
1622
+ interface WorkbenchPanelResizable {
1623
+ /** Smallest size (default 180 for a rail, 120 for the drawer). */
1624
+ min?: number;
1625
+ /** Largest size (default 480). */
1626
+ max?: number;
1627
+ }
1628
+ /** Region sizing, collapse, and presentation shared by either panel body. */
1629
+ interface WorkbenchPanelBase {
1630
+ /** Whether the panel is currently collapsed (the app owns this). */
1631
+ collapsed?: boolean;
1632
+ /**
1633
+ * Rail width, or drawer height, in px. Overrides the CSS default. For a
1634
+ * resizable panel it is the current size (default 280 for a rail, 220 for
1635
+ * the drawer), clamped to the limits; the app owns it and `wireWorkbench`
1636
+ * reports each resize.
1637
+ */
1638
+ size?: number;
1639
+ /**
1640
+ * Opt in to drag and keyboard resizing on the panel's inner separator:
1641
+ * `true` for the default limits, or `{ min, max }`. Off by default. Pair it
1642
+ * with `wireWorkbench` from `@kerfjs/ui/wire-workbench`.
1643
+ */
1644
+ resizable?: boolean | WorkbenchPanelResizable;
1645
+ /** Accessible name for the panel region. */
1646
+ label?: string;
1647
+ /** Opt into keyboard (`0`) or programmatic (`-1`) focus on the panel region. */
1648
+ tabIndex?: 0 | -1;
1649
+ /** Keep the standard focus outline visible while this panel is expanded. */
1650
+ outlined?: boolean;
1651
+ separator?: ResizableRegionSeparator;
1652
+ collapseMotion?: ResizableRegionCollapseMotion;
1653
+ contentOverflow?: ResizableRegionContentOverflow;
1654
+ presentation?: ResizableRegionPresentation;
1655
+ /**
1656
+ * Present the panel as an overlay, without a separator, below a Workbench
1657
+ * container breakpoint, and inline above it. `compact` and `narrow` use
1658
+ * CSS container queries; a positive number is a pixel threshold measured
1659
+ * by `wireWorkbench` and requires a `collapsed` signal. A rail overlays from its side at full
1660
+ * height, over the work area and an open drawer; the bottom drawer
1661
+ * overlays the bottom of the work-area column. Rails default to `narrow`
1662
+ * (pass `never` to keep one inline); the drawer defaults to inline.
1663
+ */
1664
+ responsiveOverlayAt?: WorkbenchResponsiveOverlayAt;
1665
+ /**
1666
+ * A rail's overlay width in a compact Workbench (default `inset`: the
1667
+ * Workbench less `--kui-workbench-overlay-dismiss-margin`, 44px). Ignored
1668
+ * by the drawer.
1669
+ */
1670
+ compactOverlay?: WorkbenchCompactOverlay;
1671
+ /**
1672
+ * Control shown while collapsed, in a safe-area-aware corner of the
1673
+ * Workbench (not the viewport); the bottom drawer's sits in the work-area
1674
+ * column. Prefer `toolbar.toggle`, which the Workbench relocates into the
1675
+ * work area's toolbar (or this corner when that toolbar is absent).
1676
+ */
1677
+ restoreControl?: SafeHtml;
1678
+ restorePosition?: ResizableRegionRestorePosition;
1679
+ }
1680
+ /** The original static panel body: one toolbar, optional fixed chrome, and one Pane. */
1681
+ interface WorkbenchStaticPanel extends WorkbenchPanelBase, NavPane<WorkbenchPanelToolbar, WorkbenchPanelBottomToolbar> {
1682
+ /** The composed toolbar; marked groups and its toggle relocate on collapse. */
1683
+ toolbar?: WorkbenchPanelToolbar;
1684
+ headerPlacement?: WorkbenchChromePlacement;
1685
+ footerPlacement?: WorkbenchChromePlacement;
1686
+ navStack?: never;
1687
+ }
1688
+ /** A panel whose active NavStack view supplies the toolbar, fixed header, and scroll body. */
1689
+ interface WorkbenchNavigationPanel extends WorkbenchPanelBase {
1690
+ navStack: Omit<NavStackProps, 'panelToggle' | 'hideToolbar' | 'className' | 'slot'>;
1691
+ /** Supplies the standard toggle; active view groups precede it and may relocate on collapse. */
1692
+ toolbar: Omit<WorkbenchPanelToolbar, 'title' | 'leading' | 'center' | 'trailing'> & {
1693
+ toggle: NonNullable<WorkbenchPanelToolbar['toggle']>;
1694
+ };
1695
+ content?: never;
1696
+ header?: never;
1697
+ headerList?: never;
1698
+ headerPlacement?: never;
1699
+ footer?: never;
1700
+ footerList?: never;
1701
+ bottomToolbar?: never;
1702
+ footerPlacement?: never;
1703
+ pane?: never;
1704
+ }
1705
+ /** A collapsible Workbench side rail or bottom drawer. */
1706
+ type WorkbenchPanel = WorkbenchStaticPanel | WorkbenchNavigationPanel;
1707
+ interface WorkbenchProps {
1708
+ /**
1709
+ * The Workbench's `id`. Each panel's `id` derives from it —
1710
+ * `<id>-left-rail`, `<id>-right-rail`, `<id>-bottom-drawer` — so a toggle
1711
+ * can name the panel it shows and hides with `aria-controls`.
1712
+ */
1713
+ id: string;
1714
+ label: string;
1715
+ /** The central work area. */
1716
+ main: KerfUiContent;
1717
+ /**
1718
+ * The work area's top toolbar. A collapsed left rail's marked groups
1719
+ * and toggle lead it; a collapsed right rail's trail it. With it, `main`
1720
+ * renders in a `Pane` below the toolbar.
1721
+ */
1722
+ mainToolbar?: WorkbenchMainToolbar;
1723
+ /**
1724
+ * Fixed content under `mainToolbar` — supporting copy such as a
1725
+ * description — divided from the scrolling `main` below it.
1726
+ */
1727
+ mainHeader?: KerfUiContent;
1728
+ /**
1729
+ * Fixed content over `mainBottomToolbar` — a status line or a resource
1730
+ * toolbar — divided from the scrolling `main` above it.
1731
+ */
1732
+ mainFooter?: KerfUiContent;
1733
+ /**
1734
+ * The work area's bottom toolbar. A collapsed drawer's marked groups and
1735
+ * toggle trail it; without it they float in the work area's corner.
1736
+ */
1737
+ mainBottomToolbar?: WorkbenchMainBottomToolbar;
1738
+ /**
1739
+ * Whether `mainToolbar` and `mainHeader` stay pinned (`fixed`, default),
1740
+ * scroll away with `main` (`scroll`) — useful where large text would leave
1741
+ * pinned chrome little room — or stay pinned while the work area is tall
1742
+ * enough and scroll with `main` when it is short (`auto`).
1743
+ */
1744
+ mainHeaderPlacement?: WorkbenchChromePlacement;
1745
+ /** The same for `mainFooter` and `mainBottomToolbar` (default `fixed`). */
1746
+ mainFooterPlacement?: WorkbenchChromePlacement;
1747
+ /**
1748
+ * Configuration for the work area's `Pane` (`contentElement`,
1749
+ * `contentLabel`, `separators`, `safeAreaEdges`, `chromeDividers`), which it has whenever it
1750
+ * has a toolbar, `mainHeader`, or `mainFooter`; without that chrome, `main`
1751
+ * renders as given and this is ignored. Omitted or `undefined` fields keep
1752
+ * the `Pane` defaults.
1753
+ */
1754
+ mainPane?: PaneConfig;
1755
+ /** Accessible name for the central region when it needs its own landmark. */
1756
+ mainLabel?: string;
1757
+ /** Opt into keyboard (`0`) or programmatic (`-1`) focus on the central region. */
1758
+ mainTabIndex?: 0 | -1;
1759
+ /** Keep the standard focus outline visible on the central region. */
1760
+ mainOutlined?: boolean;
1761
+ /**
1762
+ * Configuration for the `List` that holds `mainHeader` (`gap`, `hAlign`,
1763
+ * `vAlign`, `dividerSides`, `textInsets`, `controlInsets`). Omitted or
1764
+ * `undefined` fields keep the defaults (no divider: the work area's
1765
+ * `Pane` draws the line under its header chrome).
1766
+ */
1767
+ mainHeaderList?: ListConfig;
1768
+ /**
1769
+ * The same for the `List` that holds `mainFooter` (no divider by default:
1770
+ * the work area's `Pane` draws the line over its footer chrome).
1771
+ */
1772
+ mainFooterList?: ListConfig;
1773
+ leftRail?: WorkbenchPanel;
1774
+ rightRail?: WorkbenchPanel;
507
1775
  bottomDrawer?: WorkbenchPanel;
1776
+ /**
1777
+ * Minimum width, in px, the work area keeps beside the inline rails, fixed
1778
+ * or `resizable` (default 320; `0` turns it off). Resizing a rail stops
1779
+ * where the work area would drop below it, and inline rails shrink
1780
+ * proportionally when the Workbench gets narrower. Workbenches without a
1781
+ * rail ignore it.
1782
+ */
1783
+ mainMinSize?: number;
1784
+ /**
1785
+ * Minimum height, in px, the work area keeps above an inline bottom drawer,
1786
+ * fixed or `resizable` (default 120; `0` turns it off) — the drawer's
1787
+ * counterpart of `mainMinSize`. Resizing the drawer stops where the work
1788
+ * area would drop below it, and the drawer shrinks when the Workbench gets
1789
+ * shorter. Workbenches without a drawer ignore it.
1790
+ */
1791
+ mainMinHeight?: number;
1792
+ /** Dim the work area below any open overlay and absorb outside presses; off by default. */
1793
+ overlayBackdrop?: boolean;
508
1794
  className?: string;
1795
+ /** Native named-slot assignment when composed inside a web component. */
1796
+ slot?: string;
509
1797
  }
510
1798
  /**
511
1799
  * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and
512
1800
  * bottom drawer around a central work area (any absent). Collapsing snaps the
513
1801
  * panel's track to zero in one reflow while its fixed-size content slides out via
514
1802
  * a composited transform — the instant-width / sliding-content technique, so the
515
- * work area relayouts once, not per frame. The app owns each `collapsed` flag;
516
- * the collapse is pure CSS (no wire). See `docs/23-app-layouts.md` §3.3.
1803
+ * work area relayouts once, not per frame. Bottom-drawer content stays anchored
1804
+ * to the shell's stable bottom edge throughout that transition. The app owns
1805
+ * each `collapsed` flag; the collapse is pure CSS. A collapsed panel renders
1806
+ * `inert` and `aria-hidden`, so neither Tab nor assistive technology reaches
1807
+ * controls that have slid out of view or an empty landmark (its restore control
1808
+ * lives outside it and stays reachable). A panel may opt in to drag and keyboard resizing with
1809
+ * `resizable`, which `wireWorkbench` drives. See `docs/23-app-layouts.md` §3.3.
1810
+ */
1811
+ declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, mainToolbar, mainHeader, mainFooter, mainBottomToolbar, mainHeaderPlacement, mainFooterPlacement, mainPane, mainLabel, mainTabIndex, mainOutlined, mainHeaderList, mainFooterList, mainMinSize, mainMinHeight, overlayBackdrop, className, slot, }: WorkbenchProps): SafeHtml;
1812
+
1813
+ export { Workbench, type WorkbenchChromePlacement, type WorkbenchCompactOverlay, type WorkbenchMainBottomToolbar, type WorkbenchMainToolbar, type WorkbenchNavigationPanel, type WorkbenchPanel, type WorkbenchPanelBottomToolbar, type WorkbenchPanelResizable, type WorkbenchPanelToggle, type WorkbenchPanelToolbar, type WorkbenchProps, type WorkbenchResponsiveOverlayAt, type WorkbenchStaticPanel };
1814
+ ```
1815
+
1816
+ ## `@kerfjs/ui/wire-workbench`
1817
+
1818
+ ```ts
1819
+ import { Signal, ReadonlySignal } from 'kerfjs';
1820
+ import { DeviceClass } from './device-class.js';
1821
+
1822
+ /** A Workbench panel slot, named after its `WorkbenchProps` prop. */
1823
+ type WorkbenchPanelKey = 'leftRail' | 'rightRail' | 'bottomDrawer';
1824
+
1825
+ /** Minimal `localStorage`-shaped store, so the persistence hook is testable. */
1826
+ interface WorkbenchStorage {
1827
+ getItem(key: string): string | null;
1828
+ setItem(key: string, value: string): void;
1829
+ }
1830
+ /** One Workbench panel's app-owned state. */
1831
+ interface WireWorkbenchPanel {
1832
+ /**
1833
+ * The app-owned size signal a `resizable` panel renders as its `size`.
1834
+ * `wireWorkbench` writes each committed resize here; collapsing never
1835
+ * touches it, so an expanded panel returns at the size it had. Omit it for
1836
+ * a panel that is not resizable.
1837
+ */
1838
+ size?: Signal<number>;
1839
+ /**
1840
+ * When set with `size`, the size is loaded from and saved to `storage`
1841
+ * under this key, so the panel remembers the user's size.
1842
+ */
1843
+ storageKey?: string;
1844
+ /**
1845
+ * The app-owned signal the panel renders as its `collapsed`. With it (and
1846
+ * `dismissOverlays`, on by default) `wireWorkbench` treats the panel as a
1847
+ * transient overlay while it presents as one: it collapses when its
1848
+ * `responsiveOverlayAt` breakpoint begins to apply and gets its inline state
1849
+ * back when the breakpoint stops applying, an open overlay takes focus and
1850
+ * keeps Tab inside it, and it closes on Escape or a press outside it.
1851
+ */
1852
+ collapsed?: Signal<boolean>;
1853
+ /**
1854
+ * Treat presses in a portal surface launched by this panel (for example a
1855
+ * body-level menu or dialog) as inside it. The predicate receives each Node
1856
+ * in the press's composed path; return true for the portal root. Ordinary
1857
+ * presses outside the panel and its allowed surfaces still dismiss it.
1858
+ */
1859
+ keepOpenOn?: (target: Node) => boolean;
1860
+ }
1861
+ /** A resize the user made, after `wireWorkbench` wrote it to the size signal. */
1862
+ interface WorkbenchResize {
1863
+ panel: WorkbenchPanelKey;
1864
+ size: number;
1865
+ source: 'keyboard' | 'pointer';
1866
+ }
1867
+ interface WireWorkbenchOptions {
1868
+ /** The `id` the `Workbench` was rendered with. */
1869
+ id: string;
1870
+ /**
1871
+ * The wired panels, keyed like the `Workbench` props. Render a panel given
1872
+ * a `size` with `resizable` and `size={panel.size.value}`, and one given a
1873
+ * `collapsed` signal with `collapsed={panel.collapsed.value}`.
1874
+ */
1875
+ panels: Partial<Record<WorkbenchPanelKey, WireWorkbenchPanel>>;
1876
+ /**
1877
+ * When provided, resizing is suspended while `deviceClass.compact` is true —
1878
+ * the classes where rails become overlay drawers or are replaced.
1879
+ */
1880
+ deviceClass?: ReadonlySignal<DeviceClass>;
1881
+ /** Persistence store (default `globalThis.localStorage`, if present). */
1882
+ storage?: WorkbenchStorage;
1883
+ /** Keyboard step in px (default 16). */
1884
+ step?: number;
1885
+ /** Shift+arrow step in px (default 64). */
1886
+ largeStep?: number;
1887
+ /** Called after each committed resize. */
1888
+ onResize?: (change: WorkbenchResize) => void;
1889
+ /**
1890
+ * Treat the panels given a `collapsed` signal as transient overlays while
1891
+ * they present as overlays (default `true`), like `wireSidebar`'s compact
1892
+ * overlay: a panel whose `responsiveOverlayAt` breakpoint begins to apply
1893
+ * starts collapsed, with no collapse motion, and gets its inline collapsed
1894
+ * state back when the breakpoint stops applying (and on disposal); an open
1895
+ * overlay panel, responsive or `presentation: "overlay"`, closes on Escape
1896
+ * or a press that starts and ends outside it. A panel that opens as an
1897
+ * overlay takes focus on its first focusable control, and while it is open
1898
+ * Tab and Shift+Tab cycle through its controls instead of reaching the
1899
+ * work area it covers (the ARIA dialog pattern of `wireSidebar`'s compact
1900
+ * overlay); inline panels never move focus. Focus stranded in a closing
1901
+ * panel — however it closed, the app's own control inside it included —
1902
+ * returns to the control that had it when the panel opened, else to the
1903
+ * panel's restore control, else to a control outside the panel whose
1904
+ * `aria-controls` names it (each panel's `id` is its region id, e.g.
1905
+ * `studio-left-rail`) — the fallback for a panel already open at wire-up.
1906
+ * An opener inside a panel that has closed since is skipped. `false` leaves
1907
+ * every `collapsed` write and all focus handling to the app.
1908
+ */
1909
+ dismissOverlays?: boolean;
1910
+ /**
1911
+ * Keep overlays exclusive (default `true`), like `wireSidebar`'s
1912
+ * `exclusiveCompact`: when a panel opens while it presents as an overlay,
1913
+ * every other open overlay panel — rails and the bottom drawer alike —
1914
+ * closes, so one overlay never covers another's controls. Panels presenting
1915
+ * inline are never closed by it. Applies only with `dismissOverlays`.
1916
+ */
1917
+ exclusiveOverlays?: boolean;
1918
+ }
1919
+ /**
1920
+ * Wire a `Workbench`'s panels. For `resizable` panels given a `size` signal:
1921
+ * pointer drags and arrow / Shift+arrow / Home / End on each panel's
1922
+ * separator, clamped to the panel's limits and to the room that leaves the
1923
+ * work area its minimum width and height, committed to the app-owned size signals.
1924
+ * Optional persistence loads and saves each size; optional `deviceClass`
1925
+ * suspends resizing on compact classes. Collapse stays the app's `collapsed`
1926
+ * flag and never changes a size. For panels given a `collapsed` signal,
1927
+ * overlays are transient (`dismissOverlays`): a responsive overlay starts
1928
+ * collapsed, an open overlay takes focus and keeps Tab inside it, it closes
1929
+ * on Escape or an outside press, and
1930
+ * opening one overlay closes the others (`exclusiveOverlays`). Returns a disposer. See `docs/23-app-layouts.md` §3.3.
517
1931
  */
518
- declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className }: WorkbenchProps): SafeHtml;
1932
+ declare function wireWorkbench(root: HTMLElement, { id, panels, deviceClass, storage, step, largeStep, onResize, dismissOverlays, exclusiveOverlays, }: WireWorkbenchOptions): () => void;
519
1933
 
520
- export { Workbench, type WorkbenchPanel, type WorkbenchProps };
1934
+ export { type WireWorkbenchOptions, type WireWorkbenchPanel, type WorkbenchPanelKey, type WorkbenchResize, type WorkbenchStorage, wireWorkbench };
521
1935
  ```
522
1936
 
523
- ## `@kerfjs/ui/tab-scaffold`
1937
+ ## `@kerfjs/ui/collapsible-panel`
524
1938
 
525
1939
  ```ts
526
1940
  import { SafeHtml } from 'kerfjs';
1941
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1942
+ import { P as PanelSide, a as PanelToolbar, b as PanelChromePlacement, c as PanelBottomToolbar, d as PanelToggle } from './panel-toolbar-CL9tYOkh.js';
1943
+ export { e as collapsiblePanelToggleIcon } from './panel-toolbar-CL9tYOkh.js';
1944
+ import { ListConfig } from './list.js';
1945
+ import { PaneConfig } from './pane.js';
1946
+ import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
1947
+ import './toolbar.js';
1948
+ import './sides-BPSWde0A.js';
1949
+ import './lucide-icon.js';
1950
+ import 'lucide';
1951
+ import './css-values.js';
1952
+ import './flex-alignment-4ms8ZbV8.js';
1953
+
1954
+ /** Which edge a {@link CollapsiblePanel} docks to. */
1955
+ type CollapsiblePanelSide = PanelSide;
1956
+ /** The standard toggle a panel toolbar renders (see {@link CollapsiblePanelToolbar}). */
1957
+ type CollapsiblePanelToolbarToggle = PanelToggle;
1958
+ /**
1959
+ * A panel's composed top toolbar. Its groups occupy `leading`, `center`, and
1960
+ * `trailing` while open; groups marked `relocateOnCollapse` and the standard
1961
+ * toggle move to the work-area toolbar through
1962
+ * {@link CollapsiblePanelRelocated} while it is collapsed.
1963
+ */
1964
+ type CollapsiblePanelToolbar = PanelToolbar;
1965
+ type CollapsiblePanelBottomToolbar = PanelBottomToolbar;
1966
+ type CollapsiblePanelChromePlacement = PanelChromePlacement;
1967
+ interface CollapsiblePanelToggleProps {
1968
+ /** The panel this toggle controls. */
1969
+ side: CollapsiblePanelSide;
1970
+ /** The panel's current collapsed state (drives the icon direction). */
1971
+ collapsed: boolean;
1972
+ /** `data-action` the button carries so `wireSidebar` can delegate its click. */
1973
+ action: string;
1974
+ /** The panel id the button targets (`data-tab-panel`-style: `data-collapsible-panel`). */
1975
+ panelId?: string;
1976
+ /** Accessible label; defaults to "Collapse"/"Expand". */
1977
+ label?: string;
1978
+ className?: string;
1979
+ /** Native named-slot assignment when composed inside a web component. */
1980
+ slot?: string;
1981
+ }
1982
+ /**
1983
+ * A standard collapse/expand toggle button for a {@link CollapsiblePanel}: the
1984
+ * recognizable per-side icon (see {@link collapsiblePanelToggleIcon}) plus the
1985
+ * `data-action` / `aria-expanded` `wireSidebar` reads. Placement is the app's —
1986
+ * put it in the panel's own header (to collapse) and somewhere always-visible
1987
+ * (to expand while collapsed).
1988
+ */
1989
+ declare function CollapsiblePanelToggle({ side, collapsed, action, panelId, label, className, slot, }: CollapsiblePanelToggleProps): SafeHtml;
1990
+ interface CollapsiblePanelProps {
1991
+ /** A stable id for the panel — `wireSidebar` targets it and toggles reference it. */
1992
+ id: string;
1993
+ /** Which edge the panel docks to: a left/right rail or a bottom drawer. */
1994
+ side: CollapsiblePanelSide;
1995
+ /** Whether the panel is currently collapsed (the app owns this signal). */
1996
+ collapsed?: boolean;
1997
+ /** Rail width or drawer height in px. Overrides the CSS default. */
1998
+ size?: number;
1999
+ /** Accessible label for the panel region. */
2000
+ label?: string;
2001
+ /** Opt into keyboard (`0`) or programmatic (`-1`) focus on the panel region. */
2002
+ tabIndex?: 0 | -1;
2003
+ /** Keep the standard focus outline visible while the panel is expanded. */
2004
+ outlined?: boolean;
2005
+ /** Panel content. */
2006
+ children?: KerfUiContent;
2007
+ /**
2008
+ * The panel's top toolbar, composed so its groups follow the panel's open
2009
+ * state. With it, `children` renders in a `Pane` below the toolbar; render
2010
+ * {@link CollapsiblePanelRelocated} in the app's work-area toolbar so the
2011
+ * marked groups and toggle stay reachable while the panel is collapsed.
2012
+ */
2013
+ toolbar?: CollapsiblePanelToolbar;
2014
+ /** Fixed content below a toolbar panel's top toolbar. */
2015
+ header?: KerfUiContent;
2016
+ headerList?: ListConfig;
2017
+ headerPlacement?: CollapsiblePanelChromePlacement;
2018
+ /** Fixed content above a toolbar panel's bottom toolbar. */
2019
+ footer?: KerfUiContent;
2020
+ footerList?: ListConfig;
2021
+ bottomToolbar?: CollapsiblePanelBottomToolbar;
2022
+ footerPlacement?: CollapsiblePanelChromePlacement;
2023
+ /**
2024
+ * Configuration for a `toolbar` panel's `Pane` (`contentElement`,
2025
+ * `contentLabel`, `separators`, `safeAreaEdges`, `chromeDividers`) — for example
2026
+ * `{ contentElement: 'nav', contentLabel: 'Sections' }` for a navigation
2027
+ * rail. Omitted or `undefined` fields keep the `Pane` defaults. Ignored
2028
+ * without a `toolbar`, where `children` renders as given.
2029
+ */
2030
+ pane?: PaneConfig;
2031
+ separator?: ResizableRegionSeparator;
2032
+ collapseMotion?: ResizableRegionCollapseMotion;
2033
+ contentOverflow?: ResizableRegionContentOverflow;
2034
+ presentation?: ResizableRegionPresentation;
2035
+ /** Control shown while collapsed, in a safe-area-aware corner of the panel's container (not the viewport). */
2036
+ restoreControl?: SafeHtml;
2037
+ restorePosition?: ResizableRegionRestorePosition;
2038
+ className?: string;
2039
+ }
2040
+ /**
2041
+ * A standalone collapsible side rail or bottom drawer, outside the full
2042
+ * {@link Workbench} shell. It owns only the presentation: a fixed-size content
2043
+ * area that stays laid out while the panel's track snaps to zero and the content
2044
+ * slides out via `transform` (one reflow, composited — the same technique
2045
+ * `Workbench` and the catalog sidebar use). Bottom-drawer content stays anchored
2046
+ * to the panel's fixed bottom edge, so the track cannot move its layout origin
2047
+ * underneath the transform transition. A collapsed panel renders `inert` (with
2048
+ * `aria-hidden`), so neither Tab, pointer, nor assistive technology reaches
2049
+ * content that has slid out of view; its `restoreControl` renders outside the
2050
+ * panel and stays reachable.
2051
+ * The app owns the `collapsed` signal;
2052
+ * pair it with `wireSidebar` for the toggle, focus, compact-overlay, keyboard,
2053
+ * and persistence semantics, and with `CollapsiblePanelToggle` for the standard
2054
+ * affordance. See `ui/docs/collapsible-panel.md` and `docs/23-app-layouts.md`.
2055
+ */
2056
+ declare function CollapsiblePanel({ id, side, collapsed, size, label, tabIndex, outlined, children, toolbar, header, headerList, headerPlacement, footer, footerList, bottomToolbar, footerPlacement, pane, separator, collapseMotion, contentOverflow, presentation, restoreControl, restorePosition, className, }: CollapsiblePanelProps): SafeHtml;
2057
+ interface CollapsiblePanelRelocatedProps {
2058
+ /** The panel's `id`. */
2059
+ panelId: string;
2060
+ side: CollapsiblePanelSide;
2061
+ /** The panel's current collapsed state. */
2062
+ collapsed: boolean;
2063
+ /** The same `toolbar` the panel receives. */
2064
+ toolbar: CollapsiblePanelToolbar;
2065
+ }
2066
+ /**
2067
+ * A collapsed {@link CollapsiblePanel}'s marked groups and standard
2068
+ * toggle, for the app's work-area toolbar — nothing while the panel is open.
2069
+ * Put it first in the leading zone for a left rail, last in the trailing zone
2070
+ * for a right rail, and last in a bottom toolbar (or a `FloatingToolbar`
2071
+ * `restoreControl`) for a bottom drawer. `wireSidebar` hands focus to it when
2072
+ * the panel closes from its own toggle.
2073
+ */
2074
+ declare function CollapsiblePanelRelocated({ panelId, side, collapsed, toolbar, }: CollapsiblePanelRelocatedProps): SafeHtml;
527
2075
 
528
- interface TabScaffoldTab {
2076
+ export { CollapsiblePanel, type CollapsiblePanelBottomToolbar, type CollapsiblePanelChromePlacement, type CollapsiblePanelProps, CollapsiblePanelRelocated, type CollapsiblePanelRelocatedProps, type CollapsiblePanelSide, CollapsiblePanelToggle, type CollapsiblePanelToggleProps, type CollapsiblePanelToolbar, type CollapsiblePanelToolbarToggle };
2077
+ ```
2078
+
2079
+ ## `@kerfjs/ui/wire-sidebar`
2080
+
2081
+ ```ts
2082
+ import { Signal, ReadonlySignal } from 'kerfjs';
2083
+ import { DeviceClass } from './device-class.js';
2084
+
2085
+ /** Minimal `localStorage`-shaped store, so the persistence hook is testable. */
2086
+ interface SidebarStorage {
2087
+ getItem(key: string): string | null;
2088
+ setItem(key: string, value: string): void;
2089
+ }
2090
+ interface WireSidebarPanel {
2091
+ /** The panel id — matches `CollapsiblePanel`'s `id` and a toggle's `panelId`. */
529
2092
  id: string;
2093
+ /** The app-owned collapsed signal. `wireSidebar` reads it (focus, overlay) and
2094
+ * writes it (toggle, Escape, backdrop, persistence). */
2095
+ collapsed: Signal<boolean>;
2096
+ /** `data-action` value the panel's toggle button(s) carry. */
2097
+ toggleAction: string;
2098
+ /** When set, the panel's inline collapsed state is loaded from and saved to
2099
+ * `storage` under this key (a persistence hook), so the panel remembers the
2100
+ * user's inline choice. A compact overlay's open/closed state is transient
2101
+ * and is never persisted. */
2102
+ storageKey?: string;
2103
+ /** The panel's inline (non-compact) collapsed state when `storage` holds no
2104
+ * choice for it; wire-up seeds the signal with it. Defaults to the signal's
2105
+ * own value. Set it when the app seeds the signal from the device class
2106
+ * (`signal(device.value.compact)`) so a compact first render already starts
2107
+ * collapsed while a later crossing to a wide class still restores this
2108
+ * inline default. */
2109
+ inlineCollapsed?: boolean;
2110
+ }
2111
+ interface WireSidebarOptions {
2112
+ panels: readonly WireSidebarPanel[];
2113
+ /**
2114
+ * When provided, the sidebar adopts a compact **overlay** presentation while
2115
+ * `deviceClass.compact` is true: an open panel floats over the content with a
2116
+ * dismissable backdrop, Escape and backdrop-click collapse it, and focus is
2117
+ * trapped within the open panel (the ARIA dialog pattern). Without it the panel
2118
+ * is always inline.
2119
+ *
2120
+ * An overlay is transient and user-initiated: whenever the compact overlay
2121
+ * presentation begins (wire-up on a compact device, or a crossing from a wide
2122
+ * class), every panel starts collapsed so nothing blocks the page until the
2123
+ * user opens it. The inline state is remembered and restored when the device
2124
+ * crosses back to a wide class (and on disposal).
2125
+ */
2126
+ deviceClass?: ReadonlySignal<DeviceClass>;
2127
+ /** Compact devices either overlay the panels (default) or hide them in favor
2128
+ * of an application-owned responsive replacement. */
2129
+ compactPresentation?: 'overlay' | 'hidden';
2130
+ /** Collapse the other panels when one opens in compact overlay mode. */
2131
+ exclusiveCompact?: boolean;
2132
+ /** Persistence store (default `globalThis.localStorage`, if present). */
2133
+ storage?: SidebarStorage;
2134
+ }
2135
+ /**
2136
+ * The reusable sidebar-semantics layer for {@link CollapsiblePanel}s: toggle
2137
+ * delegation with focus restore, focus-into on open, an optional compact overlay
2138
+ * (backdrop + Escape + focus trap) driven by {@link deviceClass}, and an optional
2139
+ * persistence hook. The app owns each `collapsed` signal and the layout; this wire
2140
+ * owns the interaction. Returns a disposer. See `ui/docs/collapsible-panel.md` and `docs/23-app-layouts.md`.
2141
+ */
2142
+ declare function wireSidebar(root: HTMLElement, { panels, deviceClass, compactPresentation, exclusiveCompact, storage, }: WireSidebarOptions): () => void;
2143
+
2144
+ export { type SidebarStorage, type WireSidebarOptions, type WireSidebarPanel, wireSidebar };
2145
+ ```
2146
+
2147
+ ## `@kerfjs/ui/tab-navigator`
2148
+
2149
+ ```ts
2150
+ import { SafeHtml } from 'kerfjs';
2151
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2152
+ import { PaneAppearance } from './pane.js';
2153
+
2154
+ interface TabNavigatorTabBase<Id extends string> {
2155
+ id: Id;
530
2156
  label: string;
531
2157
  /** Decorative icon shown above the label in the bottom bar. */
532
2158
  icon?: SafeHtml;
533
2159
  /** The tab's content — typically a `NavStack` so each tab keeps its own stack. */
534
- content: SafeHtml;
2160
+ content: KerfUiContent;
2161
+ /** Background of this scene's scrolling work surface. */
2162
+ appearance?: PaneAppearance;
2163
+ /** Add 8px to every side of a plain scene's scrolling content. */
2164
+ deepInset?: boolean;
2165
+ /** Opt into keyboard (`0`) or programmatic (`-1`) focus on this scene. */
2166
+ tabIndex?: 0 | -1;
2167
+ /** Keep the standard focus outline visible while this scene is active. */
2168
+ outlined?: boolean;
2169
+ }
2170
+ /** A tab with an optional count or short-status badge. */
2171
+ interface TabNavigatorTabCountBadge {
2172
+ /**
2173
+ * Optional count or short status shown as a solid danger `Badge` at the
2174
+ * top-trailing corner of the tab icon (the iOS tab-bar badge). Omitted, `''`,
2175
+ * or non-finite numbers render no badge. The visual badge is `aria-hidden`;
2176
+ * its meaning reaches assistive technology through `badgeLabel`.
2177
+ *
2178
+ * Pass `true` instead for the text-free dot (new content without a count).
2179
+ */
2180
+ badge?: string | number;
2181
+ /**
2182
+ * Localized phrase folded into the tab's accessible name as
2183
+ * `"<label>, <badgeLabel>"` (for example `"3 unread"` → `"Inbox, 3 unread"`).
2184
+ * Defaults to the badge text itself. Ignored when no badge renders.
2185
+ */
2186
+ badgeLabel?: string;
535
2187
  }
536
- interface TabScaffoldProps {
2188
+ /** A tab with the text-free dot badge (new content without a count). */
2189
+ interface TabNavigatorTabDotBadge {
2190
+ /**
2191
+ * `true` shows a solid danger dot `Badge` at the top-trailing corner of the
2192
+ * tab icon — the iOS tab-bar dot for new content without a count.
2193
+ */
2194
+ badge: true;
2195
+ /**
2196
+ * Required localized phrase folded into the tab's accessible name as
2197
+ * `"<label>, <badgeLabel>"` (for example `"New activity"` →
2198
+ * `"Feed, New activity"`). A dot has no text, so this is its only meaning.
2199
+ */
2200
+ badgeLabel: string;
2201
+ }
2202
+ /** One bottom-bar destination: its label, optional icon and badge, and content. */
2203
+ type TabNavigatorTab<Id extends string = string> = TabNavigatorTabBase<Id> & (TabNavigatorTabCountBadge | TabNavigatorTabDotBadge);
2204
+ interface TabNavigatorProps<Id extends string = string> {
537
2205
  id: string;
538
2206
  /** Accessible name for the tab bar. */
539
2207
  label: string;
540
- tabs: TabScaffoldTab[];
2208
+ tabs: readonly TabNavigatorTab<Id>[];
541
2209
  /** The controlled active tab id (the app owns selection). */
542
- active: string;
2210
+ active: NoInfer<Id>;
2211
+ /**
2212
+ * The bar's top separator. `auto` (default) draws it against a sunken active
2213
+ * work surface and otherwise follows `chromeDividers`; `hidden` suppresses
2214
+ * the line, including a scroll divider.
2215
+ */
2216
+ separator?: 'auto' | 'hidden';
2217
+ /**
2218
+ * The line over the tab bar, where it meets the active scene. `scroll`
2219
+ * (default) shows it only while more of the scene's content lies below —
2220
+ * never at the scroll end or when the content fits — once
2221
+ * `wireScrollDividers` (`@kerfjs/ui/wire-scroll-dividers`) is wired above
2222
+ * the scaffold; unwired, it does not show. `always` shows it without the
2223
+ * wiring; `none` suppresses that scroll divider. A sunken active work
2224
+ * surface still draws the separator unless `separator="hidden"`. The line
2225
+ * is the bar's own 1px top border, so no state moves the bar or a tab.
2226
+ */
2227
+ chromeDividers?: 'scroll' | 'always' | 'none';
543
2228
  className?: string;
2229
+ /** Native named-slot assignment when composed inside a web component. */
2230
+ slot?: string;
544
2231
  }
545
2232
  /**
546
- * A mobile-first, iOS-like bottom tab scaffold: a bottom tab bar that switches
2233
+ * A mobile-first, iOS-like bottom tab navigator: a bottom tab bar that switches
547
2234
  * between major sections, each tab keeping its own content (usually a `NavStack`)
548
2235
  * mounted so its stack and scroll survive a switch. Controlled — the app owns
549
- * `active`; wire selection with `@kerfjs/ui/wire-tab-scaffold`'s `wireTabScaffold`.
2236
+ * `active`; wire selection with `@kerfjs/ui/wire-tab-navigator`'s `wireTabNavigator`.
550
2237
  * On larger classes, promote the tabs to a `Workbench` rail or sidebar instead of
551
2238
  * a bottom bar. See `docs/23-app-layouts.md` §3.4.
552
2239
  */
553
- declare function TabScaffold({ id, label, tabs, active, className }: TabScaffoldProps): SafeHtml;
2240
+ declare function TabNavigator<Id extends string>({ id, label, tabs, active, separator, chromeDividers, className, slot, }: TabNavigatorProps<Id>): SafeHtml;
554
2241
 
555
- export { TabScaffold, type TabScaffoldProps, type TabScaffoldTab };
2242
+ export { TabNavigator, type TabNavigatorProps, type TabNavigatorTab };
556
2243
  ```
557
2244
 
558
- ## `@kerfjs/ui/wire-tab-scaffold`
2245
+ ## `@kerfjs/ui/wire-tab-navigator`
559
2246
 
560
2247
  ```ts
561
- interface WireTabScaffoldOptions {
2248
+ interface WireTabNavigatorOptions {
562
2249
  /** Invoked with the selected tab id when a bottom-bar tab is activated. */
563
2250
  onSelect: (tabId: string) => void;
564
2251
  }
565
2252
  /**
566
- * Wire a `TabScaffold`'s bottom tab bar: clicking a tab calls `onSelect` with its
2253
+ * Wire a `TabNavigator`'s bottom tab bar: clicking a tab calls `onSelect` with its
567
2254
  * id (the app then updates its controlled `active`). Returns a disposer.
568
2255
  */
569
- declare function wireTabScaffold(root: Element, options: WireTabScaffoldOptions): () => void;
2256
+ declare function wireTabNavigator(root: Element, options: WireTabNavigatorOptions): () => void;
570
2257
 
571
- export { type WireTabScaffoldOptions, wireTabScaffold };
2258
+ export { type WireTabNavigatorOptions, wireTabNavigator };
572
2259
  ```
573
2260
 
574
2261
  ## `@kerfjs/ui/resizable-region`
575
2262
 
576
2263
  ```ts
577
2264
  import { SafeHtml } from 'kerfjs';
2265
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2266
+ import { CssLength } from './css-values.js';
578
2267
 
579
2268
  type ResizableRegionAxis = 'horizontal' | 'vertical';
580
2269
  type ResizableRegionEdge = 'start' | 'end';
2270
+ type ResizableRegionSeparator = 'auto' | 'hidden';
2271
+ type ResizableRegionCollapseMotion = 'none' | 'slide' | 'fade-slide';
2272
+ type ResizableRegionContentOverflow = 'clip' | 'auto' | 'visible';
2273
+ type ResizableRegionPresentation = 'inline' | 'overlay' | 'hidden';
2274
+ type ResizableRegionRestorePosition = 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
2275
+ type ResizableRegionResponsiveFillAt = 'compact' | 'narrow';
581
2276
  interface ResizableRegionProps {
582
2277
  id: string;
583
2278
  label: string;
@@ -586,17 +2281,38 @@ interface ResizableRegionProps {
586
2281
  max: number;
587
2282
  axis?: ResizableRegionAxis;
588
2283
  edge?: ResizableRegionEdge;
2284
+ /** Snap the track to zero. The region renders `inert` and `aria-hidden`
2285
+ * while collapsed, so neither Tab nor assistive technology reaches it or
2286
+ * lands on an empty landmark; `restoreControl` renders outside the region. */
589
2287
  collapsed?: boolean;
590
2288
  transitioning?: boolean;
2289
+ /** Whether the separator line is painted. The resize hit target remains available. */
2290
+ separator?: ResizableRegionSeparator;
2291
+ /** Keep the track change instant while optionally sliding the fixed-size content. */
2292
+ collapseMotion?: ResizableRegionCollapseMotion;
2293
+ /** Use `visible` while an anchored popup must escape the content box; it raises the region to the popup layer. */
2294
+ contentOverflow?: ResizableRegionContentOverflow;
2295
+ /** Inline layout, an edge overlay, or a responsive replacement that removes the region. */
2296
+ presentation?: ResizableRegionPresentation;
2297
+ /** Always-available control rendered while collapsed, outside the clipped region. */
2298
+ restoreControl?: SafeHtml;
2299
+ /** Safe-area-aware corner of the region's container (not the viewport) for `restoreControl`. */
2300
+ restorePosition?: ResizableRegionRestorePosition;
2301
+ /** Position a restore control in its container corner or leave it in normal flow. */
2302
+ restorePlacement?: 'corner' | 'inline';
2303
+ /** Corner distance before the container's safe-area inset. */
2304
+ restoreInset?: CssLength;
2305
+ /** Fill the available inline track and hide the separator below a container breakpoint. */
2306
+ responsiveFillAt?: ResizableRegionResponsiveFillAt;
591
2307
  /** Decorative dormant content for the separator handle. Must not contain interactive descendants. */
592
2308
  handleIcon?: SafeHtml;
593
- children: SafeHtml | SafeHtml[];
2309
+ children: KerfUiContent;
594
2310
  }
595
2311
  declare const clampRegionSize: (size: number, min: number, max: number) => number;
596
2312
  declare const resizeRegionFromPointer: (startSize: number, delta: number, edge: ResizableRegionEdge) => number;
597
- declare function ResizableRegion({ id, label, size, min, max, axis, edge, collapsed, transitioning, handleIcon, children }: ResizableRegionProps): SafeHtml;
2313
+ declare function ResizableRegion({ id, label, size, min, max, axis, edge, collapsed, transitioning, separator, collapseMotion, contentOverflow, presentation, restoreControl, restorePosition, restorePlacement, restoreInset, responsiveFillAt, handleIcon, children, }: ResizableRegionProps): SafeHtml;
598
2314
 
599
- export { ResizableRegion, type ResizableRegionAxis, type ResizableRegionEdge, type ResizableRegionProps, clampRegionSize, resizeRegionFromPointer };
2315
+ export { ResizableRegion, type ResizableRegionAxis, type ResizableRegionCollapseMotion, type ResizableRegionContentOverflow, type ResizableRegionEdge, type ResizableRegionPresentation, type ResizableRegionProps, type ResizableRegionResponsiveFillAt, type ResizableRegionRestorePosition, type ResizableRegionSeparator, clampRegionSize, resizeRegionFromPointer };
600
2316
  ```
601
2317
 
602
2318
  ## `@kerfjs/ui/wire-resizable-regions`
@@ -613,8 +2329,9 @@ interface WireResizableRegionsOptions {
613
2329
  onPreview?: (change: ResizeCommit) => void;
614
2330
  onCommit: (change: ResizeCommit) => void;
615
2331
  }
2332
+
616
2333
  /** Wire pointer and separator-keyboard behavior for every ResizableRegion below root. */
617
- declare function wireResizableRegions(root: HTMLElement, { step, largeStep, onPreview, onCommit }: WireResizableRegionsOptions): () => void;
2334
+ declare function wireResizableRegions(root: HTMLElement, options: WireResizableRegionsOptions): () => void;
618
2335
 
619
2336
  export { type ResizeCommit, type WireResizableRegionsOptions, wireResizableRegions };
620
2337
  ```
@@ -689,130 +2406,173 @@ export { DEFAULT_BREAKPOINTS, type DeviceBreakpoints, type DeviceClass, type Dev
689
2406
  ## `@kerfjs/ui/catalog`
690
2407
 
691
2408
  ```ts
2409
+ import * as kerfjs from 'kerfjs';
692
2410
  import { SafeHtml } from 'kerfjs';
2411
+ import { b as CatalogProps } from './types-oLe_N1w0.js';
2412
+ export { a as CatalogBackgroundStyle, c as CatalogBrand, d as CatalogEntry, e as CatalogRelated, C as CatalogResource, f as CatalogSecondaryGroup, g as CatalogSection, h as CatalogStageRootAttributes } from './types-oLe_N1w0.js';
2413
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2414
+ import './toolbar.js';
2415
+ import './sides-BPSWde0A.js';
2416
+ import './pane.js';
2417
+ import './workbench.js';
2418
+ import './nav-stack-BvSKY448.js';
2419
+ import './list.js';
2420
+ import './flex-alignment-4ms8ZbV8.js';
2421
+ import './css-values.js';
2422
+ import './panel-toolbar-CL9tYOkh.js';
2423
+ import './lucide-icon.js';
2424
+ import 'lucide';
2425
+ import './toolbar-text.js';
2426
+ import './resizable-region.js';
693
2427
 
694
- /** A reference link shown in the detail footer for the active entry. */
695
- interface CatalogResource {
696
- label: string;
697
- href: string;
698
- /** Optional monospace detail (e.g. a file path) shown after the label. */
699
- detail?: string;
700
- }
701
- /** A related entry offered in the detail footer's "Related" selector. */
702
- interface CatalogRelated {
703
- id: string;
704
- name: string;
705
- /** Group heading in the selector, e.g. "Uses" / "Used by". */
706
- group: string;
707
- }
708
- interface CatalogEntry {
709
- id: string;
710
- name: string;
711
- description?: string;
712
- resources?: readonly CatalogResource[];
713
- related?: readonly CatalogRelated[];
714
- }
715
- interface CatalogSection {
716
- category: string;
717
- entries: readonly CatalogEntry[];
718
- }
719
2428
  /**
720
- * A secondary group of sections shown below the primary sidebar sections with a
721
- * quieter "ecosystem" treatment (e.g. third-party components). Optionally
722
- * collapsible — the app owns `expanded` and toggles it from `wireCatalog`'s
723
- * `onToggleSecondary`.
2429
+ * Controlled, stateless component-catalog shell: a `Workbench` whose left rail
2430
+ * is the catalog navigation and whose work area is the active entry — its
2431
+ * toolbar, description, preview stage, and resource footer. The sidebar's
2432
+ * standard toggle moves into the entry toolbar while it is collapsed, and on
2433
+ * a small screen the sidebar overlays the stage like any Workbench rail.
724
2434
  */
725
- interface CatalogSecondaryGroup {
726
- label: string;
727
- sections: readonly CatalogSection[];
728
- /** When true, the group's label is a disclosure toggle controlling `expanded`. */
729
- collapsible?: boolean;
730
- /** Whether the group is expanded (controlled). Ignored unless `collapsible`. */
731
- expanded?: boolean;
732
- }
733
- interface CatalogBrand {
734
- title: string;
735
- subtitle?: string;
736
- /** Logo image URL (rendered decorative). Omit for a text-only brand. */
737
- logoUrl?: string;
738
- }
739
- interface CatalogProps {
740
- brand: CatalogBrand;
741
- sections: readonly CatalogSection[];
742
- /** The controlled active entry id — the app owns this signal. */
743
- active: string;
744
- /** The rendered preview for the active entry; the app computes it from `active`. */
745
- content: SafeHtml;
746
- /** Whether the sidebar is collapsed (controlled). */
747
- collapsed?: boolean;
748
- /** Current theme; when set, a theme toggle is shown that switches to the opposite. Omit to hide it. */
749
- theme?: 'light' | 'dark';
750
- /** Extra header controls placed before the theme toggle (each a `ToolbarControlGroup`). */
751
- headerActions?: SafeHtml;
752
- /** A secondary "ecosystem" group of sections below the primary category groups. */
753
- secondarySections?: CatalogSecondaryGroup;
754
- /** Extra sidebar content below the category groups (and the secondary group). */
755
- sidebarFooter?: SafeHtml;
756
- /** Status line content shown at the start of the detail footer. */
757
- status?: SafeHtml;
758
- selectAction?: string;
759
- toggleSidebarAction?: string;
760
- toggleThemeAction?: string;
761
- /** Action fired by the secondary group's disclosure toggle (when collapsible). */
762
- toggleSecondaryAction?: string;
763
- className?: string;
2435
+ declare function Catalog({ id, brand, sections, active, content, collapsed, theme, increasedContrast, reducedMotion, backgroundControl, headerActions, secondarySections, sidebarFooter, status, backgroundStyle, stageRootAttributes, selectAction, toggleSidebarAction, toggleThemeAction, toggleContrastAction, toggleMotionAction, selectBackgroundAction, toggleSecondaryAction, headerPlacement, footerPlacement, sidebar, mainToolbar, footerToolbar, className, slot, }: CatalogProps): kerfjs.SafeHtml;
2436
+
2437
+ /** How a specimen aligns its visible edge with its `ListHeader` label. */
2438
+ type CatalogExampleAlign = 'glyph' | 'inline-control' | 'text-trigger' | 'none';
2439
+ interface CatalogExampleViewport {
2440
+ layout?: 'grid' | 'flex' | 'flex-column';
2441
+ width?: 'full' | 'compact' | 'medium' | 'wide' | 'text' | 'control';
2442
+ /** Fixed specimen height; `app` frames an application-sized recipe or layout.
2443
+ * A fixed height is definite, so a child that fills with `height: 100%`
2444
+ * (a horizontal `ResizableRegion`, say) spans the whole frame. */
2445
+ height?: 'short' | 'reduced' | 'medium' | 'tall' | 'app';
2446
+ /** A minimum the frame may grow past. It is not a definite height: a child
2447
+ * sized with `height: 100%` falls back to its content height, so use
2448
+ * `height` when the specimen must fill the frame. */
2449
+ minHeight?: 'short' | 'medium';
2450
+ frame?: 'solid' | 'dashed';
2451
+ surface?: 'default' | 'lowered';
2452
+ overflow?: 'hidden' | 'auto-x';
2453
+ responsive?: 'roomy-only';
2454
+ shadow?: boolean;
2455
+ /** Public component custom properties applied to the specimen viewport. */
2456
+ tokens?: Readonly<Record<`--${string}`, string>>;
764
2457
  }
765
- /**
766
- * A reusable component-catalog shell: a collapsible category sidebar, a titled
767
- * detail stage that renders the active entry's preview, and a footer with
768
- * reference links and a related-entry selector. Built entirely from public
769
- * `@kerfjs/ui` primitives. Controlled and stateless — the app owns the `active`,
770
- * `collapsed`, and `theme` signals and computes `content` from `active` in its own
771
- * render; wire the sidebar/collapse/theme actions with `wireCatalog`.
772
- */
773
- declare function Catalog({ brand, sections, active, content, collapsed, theme, headerActions, secondarySections, sidebarFooter, status, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, className, }: CatalogProps): SafeHtml;
774
- /**
775
- * How a {@link CatalogExample}'s content aligns its visible left edge with the
776
- * example's `ListHeader` label (which sits 16px in — 8px title + 8px label):
777
- * - `'glyph'` — a bare glyph/text specimen with no inline geometry insets the full 16px.
778
- * - `'inline-control'` — a control that already carries ~8px of its own inline padding insets 8px so its content lands on the same line.
779
- * - `'none'` — a content-item / composition that owns its geometry and already aligns; no inset (default).
780
- */
781
- type CatalogExampleAlign = 'glyph' | 'inline-control' | 'none';
2458
+ type CatalogExampleRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
2459
+ 'data-catalog-example'?: never;
2460
+ 'data-catalog-example-stack'?: never;
2461
+ 'data-catalog-example-label'?: never;
2462
+ 'data-catalog-example-note'?: never;
2463
+ 'data-align'?: never;
2464
+ }>;
782
2465
  interface CatalogExampleProps {
783
- /** The example's label, shown as a `ListHeader` above the specimen. Omit for a bare specimen. */
784
2466
  label?: string;
785
- /** Optional explanatory note between the label and the specimen. */
786
2467
  note?: SafeHtml | string;
787
- /** Alignment inset for the specimen — see {@link CatalogExampleAlign}. Default `'none'`. */
788
2468
  align?: CatalogExampleAlign;
2469
+ /** Optional catalog-owned constraints for demonstrating layout components. */
2470
+ viewport?: CatalogExampleViewport;
2471
+ /** Replacement guidance shown when a roomy-only viewport is hidden. */
2472
+ compactFallback?: SafeHtml | string;
2473
+ rootAttributes?: CatalogExampleRootAttributes;
789
2474
  className?: string;
790
- children?: SafeHtml | readonly SafeHtml[];
2475
+ children?: KerfUiContent;
2476
+ /** Native named-slot assignment when composed inside a web component. */
2477
+ slot?: string;
791
2478
  }
792
- /**
793
- * One labeled example in a catalog preview: a `ListHeader` label, an optional
794
- * note, and the specimen. `align` insets the specimen so its visible left edge
795
- * lines up with the label text, encoding the catalog's alignment rules as a
796
- * first-class prop instead of per-demo CSS. The inset is published as the
797
- * `--kui-catalog-example-align` custom property so a debug overlay can exclude it
798
- * from a specimen's measured margin.
799
- */
800
- declare function CatalogExample({ label, note, align, className, children }: CatalogExampleProps): SafeHtml;
2479
+ /** A labeled catalog specimen with optional explanatory text and alignment. */
2480
+ declare function CatalogExample({ label, note, align, viewport, compactFallback, rootAttributes, className, children, slot, }: CatalogExampleProps): SafeHtml;
2481
+
2482
+ type CatalogExampleStackRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
2483
+ 'data-catalog-example'?: never;
2484
+ 'data-catalog-example-stack'?: never;
2485
+ 'data-catalog-example-label'?: never;
2486
+ 'data-catalog-example-note'?: never;
2487
+ 'data-align'?: never;
2488
+ }>;
801
2489
  interface CatalogExampleStackProps {
802
- /** Accessible label for the stack region. */
803
2490
  label?: string;
2491
+ rootAttributes?: CatalogExampleStackRootAttributes;
804
2492
  className?: string;
805
- children?: SafeHtml | readonly SafeHtml[];
2493
+ children?: KerfUiContent;
2494
+ /** Native named-slot assignment when composed inside a web component. */
2495
+ slot?: string;
806
2496
  }
807
- /** A vertically-stacked group of {@link CatalogExample}s with the catalog's example rhythm. */
808
- declare function CatalogExampleStack({ label, className, children }: CatalogExampleStackProps): SafeHtml;
2497
+ /** A vertically stacked group of catalog examples. */
2498
+ declare function CatalogExampleStack({ label, rootAttributes, className, children, slot, }: CatalogExampleStackProps): kerfjs.SafeHtml;
809
2499
 
810
- export { Catalog, type CatalogBrand, type CatalogEntry, CatalogExample, type CatalogExampleAlign, type CatalogExampleProps, CatalogExampleStack, type CatalogExampleStackProps, type CatalogProps, type CatalogRelated, type CatalogResource, type CatalogSecondaryGroup, type CatalogSection };
2500
+ export { Catalog, CatalogExample, type CatalogExampleAlign, type CatalogExampleProps, CatalogExampleStack, type CatalogExampleStackProps, type CatalogExampleViewport, CatalogProps };
2501
+ ```
2502
+
2503
+ ## `@kerfjs/ui/catalog-resources`
2504
+
2505
+ ```ts
2506
+ import { C as CatalogResource } from './types-oLe_N1w0.js';
2507
+ import './toolbar.js';
2508
+ import 'kerfjs';
2509
+ import './semantic-content-BbzjvSu9.js';
2510
+ import './sides-BPSWde0A.js';
2511
+ import './pane.js';
2512
+ import './workbench.js';
2513
+ import './nav-stack-BvSKY448.js';
2514
+ import './list.js';
2515
+ import './flex-alignment-4ms8ZbV8.js';
2516
+ import './css-values.js';
2517
+ import './panel-toolbar-CL9tYOkh.js';
2518
+ import './lucide-icon.js';
2519
+ import 'lucide';
2520
+ import './toolbar-text.js';
2521
+ import './resizable-region.js';
2522
+
2523
+ /**
2524
+ * Standard resource labels for a Kerf catalog detail footer. Keep these labels
2525
+ * stable across catalogs so people and AI-generated integrations see the same
2526
+ * choices in the same vocabulary.
2527
+ */
2528
+ type CatalogResourceKind = 'demoSource' | 'componentSource' | 'designTemplate' | 'guidance' | 'integrationGuidance';
2529
+ type CatalogGuidanceKind = 'guidance' | 'integrationGuidance';
2530
+ interface CatalogResourceTarget {
2531
+ href: string;
2532
+ /** Optional monospace detail, normally the repository-relative source path. */
2533
+ detail?: string;
2534
+ }
2535
+ interface CatalogResourcesInput {
2536
+ /** Required source for the runnable demonstration. */
2537
+ demoSource: CatalogResourceTarget;
2538
+ /** Source for the production component; omit for recipes and integrations. */
2539
+ componentSource?: CatalogResourceTarget;
2540
+ /** Optional design-tool template associated with the component. */
2541
+ designTemplate?: CatalogResourceTarget;
2542
+ /** Required UI or integration guidance. */
2543
+ guidance: CatalogResourceTarget;
2544
+ /** Use `integrationGuidance` when the component implementation is upstream. */
2545
+ guidanceKind?: CatalogGuidanceKind;
2546
+ }
2547
+ /**
2548
+ * Build the standard catalog resource group in its canonical order: demo,
2549
+ * component, optional design template, then guidance.
2550
+ */
2551
+ declare function catalogResources(input: CatalogResourcesInput): CatalogResource[];
2552
+
2553
+ export { type CatalogGuidanceKind, type CatalogResourceKind, type CatalogResourceTarget, type CatalogResourcesInput, catalogResources };
811
2554
  ```
812
2555
 
813
2556
  ## `@kerfjs/ui/wire-catalog`
814
2557
 
815
2558
  ```ts
2559
+ import { Signal } from 'kerfjs';
2560
+ import { a as CatalogBackgroundStyle } from './types-oLe_N1w0.js';
2561
+ import './toolbar.js';
2562
+ import './semantic-content-BbzjvSu9.js';
2563
+ import './sides-BPSWde0A.js';
2564
+ import './pane.js';
2565
+ import './workbench.js';
2566
+ import './nav-stack-BvSKY448.js';
2567
+ import './list.js';
2568
+ import './flex-alignment-4ms8ZbV8.js';
2569
+ import './css-values.js';
2570
+ import './panel-toolbar-CL9tYOkh.js';
2571
+ import './lucide-icon.js';
2572
+ import 'lucide';
2573
+ import './toolbar-text.js';
2574
+ import './resizable-region.js';
2575
+
816
2576
  interface WireCatalogOptions {
817
2577
  /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */
818
2578
  onSelect: (id: string) => void;
@@ -820,98 +2580,304 @@ interface WireCatalogOptions {
820
2580
  onToggleSidebar?: () => void;
821
2581
  /** Invoked when the theme toggle is activated. */
822
2582
  onToggleTheme?: () => void;
2583
+ /** Report a contrast-toggle press; the app updates its signal and applies the preference. */
2584
+ onToggleContrast?: () => void;
2585
+ /** Report a reduced-motion-toggle press; the app updates its signal and applies the preference. */
2586
+ onToggleMotion?: () => void;
2587
+ /** Report a preview background choice; the app updates `backgroundStyle`. */
2588
+ onSelectBackground?: (style: CatalogBackgroundStyle) => void;
823
2589
  /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */
824
2590
  onToggleSecondary?: () => void;
825
2591
  /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */
826
2592
  urlParam?: string;
2593
+ /**
2594
+ * Reveal the chosen sidebar row after selection. `true` uses desktop-safe
2595
+ * defaults; pass options to customize scroll alignment or the media guard.
2596
+ */
2597
+ revealSelection?: boolean | CatalogRevealOptions;
827
2598
  selectAction?: string;
828
2599
  toggleSidebarAction?: string;
829
2600
  toggleThemeAction?: string;
2601
+ toggleContrastAction?: string;
2602
+ toggleMotionAction?: string;
2603
+ selectBackgroundAction?: string;
830
2604
  toggleSecondaryAction?: string;
2605
+ /**
2606
+ * The sidebar's app-owned collapsed flag. With it, the catalog's Workbench
2607
+ * sidebar is wired like any Workbench rail: it becomes a transient overlay
2608
+ * on a small screen (collapsed as the breakpoint applies, closed on Escape
2609
+ * or a press outside, focus handed back), and choosing an entry from the
2610
+ * open overlay closes it.
2611
+ */
2612
+ collapsed?: Signal<boolean>;
2613
+ /**
2614
+ * The app-owned sidebar width signal for a Catalog rendered with
2615
+ * `sidebar={{ resizable: true, size: sidebarSize.value }}`. Each committed
2616
+ * drag or keyboard resize is written to it.
2617
+ */
2618
+ sidebarSize?: Signal<number>;
2619
+ /**
2620
+ * With `sidebarSize`, load and save the width under this storage key so the
2621
+ * catalog remembers the user's sidebar width.
2622
+ */
2623
+ sidebarStorageKey?: string;
2624
+ /** The Catalog's `id`, when it is not the default `kui-catalog`. */
2625
+ id?: string;
831
2626
  }
2627
+ interface CatalogRevealOptions {
2628
+ /** Scroll alignment within the sidebar. Default `'nearest'`. */
2629
+ block?: ScrollLogicalPosition;
2630
+ /** Cross-axis alignment. Default `'nearest'`. */
2631
+ inline?: ScrollLogicalPosition;
2632
+ /** Scroll behavior. Default `'auto'`. */
2633
+ behavior?: ScrollBehavior;
2634
+ /**
2635
+ * Only reveal when this media query matches. Defaults to the Catalog's
2636
+ * desktop layout; pass `false` to reveal at every viewport size.
2637
+ */
2638
+ media?: string | false;
2639
+ }
2640
+ /**
2641
+ * Reveal one Catalog sidebar entry after the controlled render settles without
2642
+ * moving focus. Returns a cancellation function for rapid selection changes.
2643
+ */
2644
+ declare function revealCatalogEntry(root: HTMLElement, id: string, { block, inline, behavior, media, }?: CatalogRevealOptions): () => void;
832
2645
  /**
833
2646
  * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar
834
- * item selection (and the related-entry selector), the sidebar collapse toggle, and
2647
+ * item selection (and the related-entry popup menu), the sidebar collapse toggle, and
835
2648
  * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates
836
2649
  * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.
837
- * Returns a disposer.
2650
+ * It also wires the scroll dividers below root (`wireScrollDividers`), so the
2651
+ * catalog's panes and every example in it show their chrome dividers only
2652
+ * while scrolled. Returns a disposer.
838
2653
  */
839
- declare function wireCatalog(root: HTMLElement, { onSelect, onToggleSidebar, onToggleTheme, onToggleSecondary, urlParam, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, }: WireCatalogOptions): () => void;
2654
+ declare function wireCatalog(root: HTMLElement, { onSelect, onToggleSidebar, onToggleTheme, onToggleContrast, onToggleMotion, onSelectBackground, onToggleSecondary, urlParam, revealSelection, selectAction, toggleSidebarAction, toggleThemeAction, toggleContrastAction, toggleMotionAction, selectBackgroundAction, toggleSecondaryAction, collapsed, sidebarSize, sidebarStorageKey, id: catalogId, }: WireCatalogOptions): () => void;
840
2655
 
841
- export { type WireCatalogOptions, wireCatalog };
2656
+ export { type CatalogRevealOptions, type WireCatalogOptions, revealCatalogEntry, wireCatalog };
842
2657
  ```
843
2658
 
844
2659
  ## `@kerfjs/ui/segmented-control`
845
2660
 
846
2661
  ```ts
847
- import { SafeHtml } from 'kerfjs';
2662
+ import * as kerfjs from 'kerfjs';
2663
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
848
2664
 
849
2665
  type SegmentedControlAppearance = 'filled' | 'outlined' | 'toolbar';
850
2666
  type SegmentedControlShape = 'rounded' | 'pill';
851
2667
  type SegmentedControlSize = 'small' | 'default';
852
2668
  type SegmentedControlLayout = 'content' | 'equal';
853
- interface SegmentedControlChoice {
854
- value: string;
2669
+ interface SegmentedControlChoice<Value extends string = string> {
2670
+ value: Value;
855
2671
  label: string;
856
- content?: SafeHtml;
2672
+ content?: KerfUiContent;
857
2673
  title?: string;
858
2674
  disabled?: boolean;
859
2675
  }
860
- interface SegmentedControlProps {
2676
+ interface SegmentedControlProps<Value extends string = string> {
861
2677
  id: string;
862
2678
  label: string;
863
- value: string;
864
- choices: readonly SegmentedControlChoice[];
2679
+ value: NoInfer<Value>;
2680
+ choices: readonly SegmentedControlChoice<Value>[];
865
2681
  action?: string;
866
2682
  appearance?: SegmentedControlAppearance;
867
2683
  shape?: SegmentedControlShape;
868
2684
  size?: SegmentedControlSize;
869
2685
  layout?: SegmentedControlLayout;
870
2686
  className?: string;
871
- /** Render as an unanimated loading skeleton, disabling every segment. */
2687
+ /** Render as a loading placeholder: every choice keeps its live label or icon, none is selected, and every segment is disabled. */
872
2688
  placeholder?: boolean;
2689
+ /** Native named-slot assignment when composed inside a web component. */
2690
+ slot?: string;
873
2691
  }
874
- declare function SegmentedControl({ id, label, value, choices, action, appearance, shape, size, layout, className, placeholder, }: SegmentedControlProps): SafeHtml;
2692
+ declare function SegmentedControl<Value extends string>({ id, label, value, choices, action, appearance, shape, size, layout, className, placeholder, slot, }: SegmentedControlProps<Value>): kerfjs.SafeHtml;
875
2693
 
876
2694
  export { SegmentedControl, type SegmentedControlAppearance, type SegmentedControlChoice, type SegmentedControlLayout, type SegmentedControlProps, type SegmentedControlShape, type SegmentedControlSize };
877
2695
  ```
878
2696
 
879
- ## `@kerfjs/ui/state-banner`
2697
+ ## `@kerfjs/ui/select`
880
2698
 
881
2699
  ```ts
882
2700
  import { SafeHtml } from 'kerfjs';
2701
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2702
+ import { CssForegroundColor } from './css-values.js';
2703
+ import { LucideNode } from './lucide-icon.js';
2704
+ import 'lucide';
2705
+
2706
+ interface SelectChoice<Value extends string = string> {
2707
+ value: Value;
2708
+ label: string;
2709
+ icon?: LucideNode;
2710
+ iconName?: string;
2711
+ /**
2712
+ * Foreground color for the optional icon: a semantic foreground token such
2713
+ * as `uiColor('success-on-quiet')`, or an application-owned
2714
+ * `foregroundColorVar('--app-icon-color')`. Fill tokens such as
2715
+ * `uiColor('success-fill-quiet')` are pale background tints and do not
2716
+ * type-check.
2717
+ */
2718
+ color?: CssForegroundColor;
2719
+ group?: string;
2720
+ /** Keep an unavailable choice visible but prevent selection. */
2721
+ disabled?: boolean;
2722
+ /** Optional explanation exposed as the disabled option's native tooltip. */
2723
+ disabledReason?: string;
2724
+ /**
2725
+ * Draw a divider between this choice and the previous choice in the same
2726
+ * list. A group boundary is already a separator, so the first choice of a
2727
+ * group (or of the whole menu) never draws a second one.
2728
+ */
2729
+ separatorBefore?: boolean;
2730
+ }
2731
+ type SelectAccessibleName = {
2732
+ label: string;
2733
+ ariaLabel?: string;
2734
+ } | {
2735
+ label?: never;
2736
+ ariaLabel: string;
2737
+ };
2738
+ type SelectPresentation = 'form' | 'toolbar-borderless' | 'navigation' | 'inline' | 'title';
2739
+ type SelectSize = 'default' | 'compact';
2740
+ type SelectSelectedPresentation = 'label' | 'icon-only';
2741
+ type SelectFocusRingOwner = 'select' | 'group';
2742
+ type SelectTriggerWidth = 'fit-content' | 'max-content' | 'fill';
2743
+ interface SelectBaseProps<Value extends string = string> {
2744
+ name: string;
2745
+ choices: readonly SelectChoice<Value>[];
2746
+ className?: string;
2747
+ /** Empty-value hint text shown in the closed control (the native select placeholder). */
2748
+ placeholderText?: string;
2749
+ /** Supporting text shown below the control and associated with its combobox. */
2750
+ hint?: string;
2751
+ disabled?: boolean;
2752
+ /** Match menu width to the trigger; default menus grow to content within viewport gutters. */
2753
+ fitMenu?: boolean;
2754
+ /** Render as an unanimated loading skeleton: the label above a static, empty control box. */
2755
+ placeholder?: boolean;
2756
+ /** Form (default), borderless toolbar, intrinsic navigation, inline, or view-title chrome. */
2757
+ presentation?: SelectPresentation;
2758
+ size?: SelectSize;
2759
+ /** Closed trigger width; omit to keep the presentation's default. */
2760
+ triggerWidth?: SelectTriggerWidth;
2761
+ /** Show the disclosure caret (default); omit it for a compact icon-only trigger. */
2762
+ caret?: boolean;
2763
+ /** Let an enclosing ToolbarControlGroup paint the composed focus ring. */
2764
+ focusRingOwner?: SelectFocusRingOwner;
2765
+ /** Keep the trigger's focus ring inside its box when an ancestor clips overflow. */
2766
+ focusRingInset?: boolean;
2767
+ /** Maximum closed-control label width in CSS pixels before ellipsis. */
2768
+ labelMaxWidth?: number;
2769
+ /** Native named-slot assignment when composed inside a web component. */
2770
+ slot?: string;
2771
+ }
2772
+ /** One chosen value (the default). */
2773
+ interface SelectSingleValueProps<Value extends string = string> {
2774
+ multiple?: false;
2775
+ value: NoInfer<Value>;
2776
+ renderSelected?: (choice: SelectChoice<Value>) => SafeHtml;
2777
+ /** Show only the selected choice icon while retaining the Select's accessible name. */
2778
+ selectedPresentation?: SelectSelectedPresentation;
2779
+ triggerIcon?: never;
2780
+ selectAllLabel?: never;
2781
+ clearLabel?: never;
2782
+ }
2783
+ /**
2784
+ * Any number of chosen values. The popup stays open while the person toggles
2785
+ * choices and closes on an outside click, Escape, or focus leaving; the closed
2786
+ * control summarizes the chosen labels in choice order.
2787
+ */
2788
+ interface SelectMultipleLabelProps<Value extends string = string> {
2789
+ multiple: true;
2790
+ value: readonly NoInfer<Value>[];
2791
+ renderSelected?: never;
2792
+ selectedPresentation?: 'label';
2793
+ triggerIcon?: never;
2794
+ /** Opt in to a footer action that selects every enabled choice. */
2795
+ selectAllLabel?: string;
2796
+ /** Opt in to a footer action that clears the current selection. */
2797
+ clearLabel?: string;
2798
+ }
2799
+ /**
2800
+ * A multiple Select drawn as an icon-only toolbar trigger, such as a
2801
+ * "Filter by label" funnel. No single choice is selected, so the trigger shows
2802
+ * a fixed `triggerIcon` naming the menu's purpose; while any choice is chosen
2803
+ * a count badge sits beside it, and the combobox's accessible name ends with
2804
+ * the chosen labels in choice order.
2805
+ */
2806
+ interface SelectMultipleIconProps<Value extends string = string> {
2807
+ multiple: true;
2808
+ value: readonly NoInfer<Value>[];
2809
+ renderSelected?: never;
2810
+ selectedPresentation: 'icon-only';
2811
+ /**
2812
+ * The trigger's fixed icon, typically a `LucideIcon` naming the menu's
2813
+ * purpose (a funnel for a filter), independent of the selection.
2814
+ */
2815
+ triggerIcon: KerfUiContent;
2816
+ /** Opt in to a footer action that selects every enabled choice. */
2817
+ selectAllLabel?: string;
2818
+ /** Opt in to a footer action that clears the current selection. */
2819
+ clearLabel?: string;
2820
+ }
2821
+ type SelectMultipleValueProps<Value extends string = string> = SelectMultipleLabelProps<Value> | SelectMultipleIconProps<Value>;
2822
+ type SelectProps<Value extends string = string> = SelectBaseProps<Value> & SelectAccessibleName & (SelectSingleValueProps<Value> | SelectMultipleValueProps<Value>);
2823
+ declare function Select<Value extends string>(props: SelectProps<Value>): SafeHtml;
883
2824
 
884
- type StateBannerTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
2825
+ export { Select, type SelectChoice, type SelectFocusRingOwner, type SelectMultipleIconProps, type SelectMultipleLabelProps, type SelectMultipleValueProps, type SelectPresentation, type SelectProps, type SelectSelectedPresentation, type SelectSingleValueProps, type SelectSize, type SelectTriggerWidth };
2826
+ ```
2827
+
2828
+ ## `@kerfjs/ui/state-banner`
2829
+
2830
+ ```ts
2831
+ import { SafeHtml } from 'kerfjs';
2832
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2833
+ import { S as SemanticTone } from './semantic-tone-CdW013KF.js';
2834
+
2835
+ /** @deprecated Use SemanticTone. */
2836
+ type StateBannerTone = SemanticTone;
2837
+ type StateBannerUrgency = 'status' | 'alert';
2838
+ type StateBannerCopyLayout = 'inline' | 'stacked';
2839
+ type StateBannerActionPlacement = 'trailing' | 'below';
885
2840
  interface StateBannerProps {
886
2841
  title: string;
887
2842
  detail?: string;
2843
+ /** Optional compact status or count shown beside the title. */
2844
+ badge?: string;
888
2845
  icon?: SafeHtml;
889
- action?: SafeHtml;
890
- tone?: StateBannerTone;
891
- urgency?: 'status' | 'alert';
2846
+ action?: KerfUiContent;
2847
+ tone?: SemanticTone;
2848
+ urgency?: StateBannerUrgency;
2849
+ /** Keep the detail beside the title, or give it its own line. */
2850
+ copyLayout?: StateBannerCopyLayout;
2851
+ /** Place the action beside the copy or on a separate trailing row. */
2852
+ actionPlacement?: StateBannerActionPlacement;
892
2853
  className?: string;
893
- /** Render the title and detail as unanimated loading skeletons, keeping the icon and tone. */
2854
+ /** Render the title, badge, and detail as unanimated loading skeletons, keeping the icon and tone. The detail line appears only when `detail` is set, as in the live banner. */
894
2855
  placeholder?: boolean;
2856
+ /** Native named-slot assignment when composed inside a web component. */
2857
+ slot?: string;
895
2858
  }
896
- declare function StateBanner({ title, detail, icon, action, tone, urgency, className, placeholder }: StateBannerProps): SafeHtml;
2859
+ declare function StateBanner({ title, detail, badge, icon, action, tone, urgency, copyLayout, actionPlacement, className, placeholder, slot, }: StateBannerProps): SafeHtml;
897
2860
 
898
- export { StateBanner, type StateBannerProps, type StateBannerTone };
2861
+ export { SemanticTone, StateBanner, type StateBannerActionPlacement, type StateBannerCopyLayout, type StateBannerProps, type StateBannerTone, type StateBannerUrgency };
899
2862
  ```
900
2863
 
901
2864
  ## `@kerfjs/ui/empty-state`
902
2865
 
903
2866
  ```ts
904
2867
  import { SafeHtml } from 'kerfjs';
2868
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
905
2869
 
906
2870
  interface EmptyStateProps {
907
2871
  title: string;
908
2872
  detail?: string;
909
2873
  icon?: SafeHtml;
910
- action?: SafeHtml;
2874
+ action?: KerfUiContent;
911
2875
  busy?: boolean;
912
2876
  className?: string;
2877
+ /** Native named-slot assignment when composed inside a web component. */
2878
+ slot?: string;
913
2879
  }
914
- declare function EmptyState({ title, detail, icon, action, busy, className }: EmptyStateProps): SafeHtml;
2880
+ declare function EmptyState({ title, detail, icon, action, busy, className, slot, }: EmptyStateProps): SafeHtml;
915
2881
 
916
2882
  export { EmptyState, type EmptyStateProps };
917
2883
  ```
@@ -920,34 +2886,51 @@ export { EmptyState, type EmptyStateProps };
920
2886
 
921
2887
  ```ts
922
2888
  import * as kerfjs from 'kerfjs';
2889
+ import { CssForegroundColor } from './css-values.js';
2890
+ import { LucideIconSize } from './lucide-icon.js';
2891
+ import 'lucide';
923
2892
 
2893
+ type LoadingSpinnerSize = LucideIconSize;
924
2894
  interface LoadingSpinnerProps {
925
2895
  className?: string;
2896
+ /** Foreground color; omit to inherit the surrounding text color. */
2897
+ color?: CssForegroundColor;
926
2898
  label?: string;
2899
+ /** Named icon scale or positive pixel size, converted to rem. Omit for 1em. */
2900
+ size?: LoadingSpinnerSize;
2901
+ /** Native named-slot assignment when composed inside a web component. */
2902
+ slot?: string;
927
2903
  }
928
2904
  /** Stable viewBox-centered progress ring based on svg-spinners' MIT-licensed 180-ring. */
929
- declare function LoadingSpinner({ className, label }: LoadingSpinnerProps): kerfjs.SafeHtml;
2905
+ declare function LoadingSpinner({ className, color, label, size, slot, }: LoadingSpinnerProps): kerfjs.SafeHtml;
930
2906
 
931
- export { LoadingSpinner, type LoadingSpinnerProps };
2907
+ export { LoadingSpinner, type LoadingSpinnerProps, type LoadingSpinnerSize };
932
2908
  ```
933
2909
 
934
2910
  ## `@kerfjs/ui/skeleton`
935
2911
 
936
2912
  ```ts
937
2913
  import * as kerfjs from 'kerfjs';
2914
+ import { CssSize, CssLength } from './css-values.js';
938
2915
 
939
2916
  interface SkeletonProps {
940
- /** Width as any CSS length (e.g. `remify(120px)`, `60%`). Defaults to filling its slot. */
941
- width?: string;
942
- /** Height as any CSS length. Defaults to a single text line. */
943
- height?: string;
944
- /** Corner radius override (a CSS length). Defaults to the small radius token. */
945
- radius?: string;
2917
+ /** Typed width or intrinsic sizing keyword. Defaults to filling its slot. */
2918
+ width?: CssSize;
2919
+ /** Typed height or intrinsic sizing keyword. Defaults to a single text line. */
2920
+ height?: CssSize;
2921
+ /** Typed corner-radius override. Defaults to the small radius token. */
2922
+ radius?: CssLength;
946
2923
  /** Render this many stacked lines (the last one shorter), for multi-line text. */
947
2924
  lines?: number;
2925
+ /** Lay the block out as `display: block` instead of the default
2926
+ * `inline-block`, so it takes no text line of its own: no baseline gap
2927
+ * below it, and it centers like an icon in a slot. Defaults to `false`. */
2928
+ block?: boolean;
948
2929
  /** Accessible label. Omit to keep the block decorative (`aria-hidden`). */
949
2930
  label?: string;
950
2931
  className?: string;
2932
+ /** Native named-slot assignment when composed inside a web component. */
2933
+ slot?: string;
951
2934
  }
952
2935
  /**
953
2936
  * A subtle, deliberately **unanimated** loading placeholder block. Use it for a
@@ -955,68 +2938,111 @@ interface SkeletonProps {
955
2938
  * `placeholder` prop. Decorative by default (`aria-hidden`); pass `label` to
956
2939
  * announce it. Sizes to its slot unless `width`/`height` are given.
957
2940
  */
958
- declare function Skeleton({ width, height, radius, lines, label, className }: SkeletonProps): kerfjs.SafeHtml;
2941
+ declare function Skeleton({ width, height, radius, lines, block, label, className, slot, }: SkeletonProps): kerfjs.SafeHtml;
959
2942
 
960
2943
  export { Skeleton, type SkeletonProps };
961
2944
  ```
962
2945
 
963
- ## `@kerfjs/ui/token-search-field`
2946
+ ## `@kerfjs/ui/sunken-panel`
964
2947
 
965
2948
  ```ts
966
- import { SafeHtml } from 'kerfjs';
967
-
968
- interface TokenSearchToken {
969
- value: string;
970
- label: string;
971
- offset?: number;
972
- accessibleLabel?: string;
973
- }
974
- interface TokenSearchFieldProps {
975
- id: string;
976
- label: string;
977
- query?: string;
978
- tokens?: readonly TokenSearchToken[];
979
- placeholder?: string;
980
- tokenPlaceholder?: string;
981
- disabled?: boolean;
982
- autofocus?: boolean;
983
- /** Allow an empty field to render as one iconic action. */
984
- collapsible?: boolean;
985
- /** Keep an empty collapsible field open while the application owns focus. */
986
- expanded?: boolean;
987
- expandAction?: string;
988
- expandLabel?: string;
989
- leading?: SafeHtml;
990
- trailing?: SafeHtml;
991
- editAction?: string;
992
- removeAction?: string;
993
- clearAction?: string;
994
- clearLabel?: string;
2949
+ import * as kerfjs from 'kerfjs';
2950
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2951
+ import { CssFlexKeyword, CssFlex } from './css-values.js';
2952
+
2953
+ type SunkenPanelShape = 'rounded' | 'square';
2954
+ interface SunkenPanelProps {
2955
+ children?: KerfUiContent;
2956
+ /** Optional accessible landmark name for a distinct application region. */
2957
+ ariaLabel?: string;
2958
+ /** Corner shape: a rounded rectangle (default) or square corners. */
2959
+ shape?: SunkenPanelShape;
2960
+ /** Grow or shrink within a flex parent, using Grid's typed flex contract. */
2961
+ flex?: boolean | CssFlexKeyword | CssFlex;
2962
+ /** Fill a parent with a definite height; use flex inside a flex layout. */
2963
+ fill?: boolean;
2964
+ /** Opt into keyboard (`0`) or programmatic (`-1`) focus on the panel. */
2965
+ tabIndex?: 0 | -1;
2966
+ /** Keep the standard focus outline visible, such as for a drop target. */
2967
+ outlined?: boolean;
995
2968
  className?: string;
996
- editorAttributes?: Readonly<Record<`data-${string}`, string>>;
997
- }
998
- interface TokenSearchFieldValue {
999
- query: string;
1000
- tokens: TokenSearchToken[];
2969
+ /** Native named-slot assignment when composed inside a web component. */
2970
+ slot?: string;
1001
2971
  }
1002
- declare function TokenSearchField({ id, label, query, tokens, placeholder, tokenPlaceholder, disabled, autofocus, collapsible, expanded, expandAction, expandLabel, leading, trailing, editAction, removeAction, clearAction, clearLabel, className, editorAttributes, }: TokenSearchFieldProps): SafeHtml;
1003
- /** Read editable text and ordered token offsets from a rendered TokenSearchField editor. */
1004
- declare function readTokenSearchField(editor: HTMLElement, knownTokens?: readonly TokenSearchToken[]): TokenSearchFieldValue;
1005
- /** Focus an editor and place its caret at a text offset, skipping atomic token chips. */
1006
- declare function placeTokenSearchCaret(editor: HTMLElement, offset?: number): void;
2972
+ /**
2973
+ * A lowered application surface with one compact inset and a vertical content
2974
+ * stack. The panel owns its background and padding; children own their own
2975
+ * borders and internal geometry.
2976
+ */
2977
+ declare function SunkenPanel({ children, ariaLabel, shape, flex, fill, tabIndex, outlined, className, slot, }: SunkenPanelProps): kerfjs.SafeHtml;
1007
2978
 
1008
- export { TokenSearchField, type TokenSearchFieldProps, type TokenSearchFieldValue, type TokenSearchToken, placeTokenSearchCaret, readTokenSearchField };
2979
+ export { SunkenPanel, type SunkenPanelProps, type SunkenPanelShape };
2980
+ ```
2981
+
2982
+ ## `@kerfjs/ui/token-search-field`
2983
+
2984
+ ```ts
2985
+ export { T as TokenSearchEditorAttributes, a as TokenSearchField, b as TokenSearchFieldProps, c as TokenSearchFieldValue, j as TokenSearchToken, k as TokenSearchTrailingAction, p as placeTokenSearchCaret, r as readTokenSearchField } from './token-search-model-DTcTXWMS.js';
2986
+ import 'kerfjs';
2987
+ import './semantic-content-BbzjvSu9.js';
1009
2988
  ```
1010
2989
 
1011
2990
  ## `@kerfjs/ui/wire-token-search-fields`
1012
2991
 
1013
2992
  ```ts
1014
2993
  import { Signal } from 'kerfjs';
2994
+ import { d as TokenSearchModel } from './token-search-model-DTcTXWMS.js';
2995
+ import './semantic-content-BbzjvSu9.js';
1015
2996
 
1016
2997
  interface TokenSearchSubmit {
1017
2998
  id: string;
1018
2999
  editor: HTMLElement;
1019
3000
  }
3001
+ /** Reported to {@link WireTokenSearchFieldsOptions.onEdit} on every editor input. */
3002
+ interface TokenSearchEdit extends TokenSearchSubmit {
3003
+ /**
3004
+ * The `InputEvent` that mutated the editor — read `event.inputType` / `event.data`
3005
+ * to gate commit behavior (e.g. only parse a chip on whitespace-terminated input)
3006
+ * without keeping a separate `input` listener.
3007
+ */
3008
+ event: InputEvent;
3009
+ }
3010
+ /** Reported when adjacent-token keyboard deletion asks the app to drop a chip. */
3011
+ interface TokenSearchTokenRemoval {
3012
+ id: string;
3013
+ /** The `data-token-value` of the token the app should remove from its state. */
3014
+ value: string;
3015
+ editor: HTMLElement;
3016
+ /** `'backward'` = the token before the caret (Backspace); `'forward'` = after (Delete). */
3017
+ direction: 'backward' | 'forward';
3018
+ }
3019
+ /**
3020
+ * Opt-in keyboard behavior for the atomic token chips. Off unless `keyboard` is
3021
+ * set; each piece defaults on once opted in. The helper never mutates app state:
3022
+ * a removal is reported through {@link TokenSearchKeyboardOptions.onRemoveToken}
3023
+ * for the caller to apply, while caret movement past a chip is a pure ephemeral
3024
+ * mechanic the helper performs itself.
3025
+ */
3026
+ interface TokenSearchKeyboardBaseOptions {
3027
+ /**
3028
+ * From a collapsed caret with no selection, Backspace removes the token
3029
+ * immediately before it and Delete the token immediately after — reported via
3030
+ * `onRemoveToken` — instead of deleting a character. Default: true.
3031
+ */
3032
+ /**
3033
+ * ArrowRight moves the caret past a trailing atomic token so text typed next
3034
+ * lands after the chip. Default: true.
3035
+ */
3036
+ moveCaretPastToken?: boolean;
3037
+ }
3038
+ type TokenSearchKeyboardOptions = TokenSearchKeyboardBaseOptions & ({
3039
+ removeAdjacentToken?: true;
3040
+ /** Apply the reported removal to your controlled state, then re-render. */
3041
+ onRemoveToken: (removal: TokenSearchTokenRemoval) => void;
3042
+ } | {
3043
+ removeAdjacentToken: false;
3044
+ onRemoveToken?: never;
3045
+ });
1020
3046
  /**
1021
3047
  * Managed collapsible behavior for the iconic TokenSearchField. Every piece is on
1022
3048
  * by default; disable a specific one to own it in the app. Provide `signals` to
@@ -1029,15 +3055,30 @@ interface TokenSearchCollapsibleOptions {
1029
3055
  collapseOnEmptyBlur?: boolean;
1030
3056
  /** Collapse an empty field on Escape and restore focus to its trigger. Default: true. */
1031
3057
  collapseOnEscape?: boolean;
1032
- /** Focus the editor on expand and the trigger on Escape-collapse. Default: true. */
3058
+ /** Focus the editor on expand/controlled clear and the trigger on Escape-collapse. Default: true. */
1033
3059
  manageFocus?: boolean;
3060
+ /**
3061
+ * Keep an empty field expanded when focus moves to a caller-owned surface
3062
+ * rendered outside the field — a suggestions dropdown, date picker, or help
3063
+ * popover shown beside it. Return true for any focus target that must NOT
3064
+ * trigger collapse-on-empty-blur. An element carrying `data-token-search-keep-open`
3065
+ * (or any node inside one) is always exempt, so this predicate is only needed
3066
+ * for surfaces you cannot mark declaratively.
3067
+ */
3068
+ keepOpenOn?: (target: Node | null) => boolean;
1034
3069
  /** App-owned `expanded` signals keyed by field id; adopted instead of helper-created. */
1035
3070
  signals?: Readonly<Record<string, Signal<boolean>>>;
1036
3071
  }
1037
3072
  interface WireTokenSearchFieldsOptions {
3073
+ /** Optional automatic grammar/suggestion models, keyed by TokenSearchField id. */
3074
+ models?: Readonly<Record<string, TokenSearchModel>>;
1038
3075
  onSubmit?: (submission: TokenSearchSubmit) => void;
3076
+ /** Fired on every editor `input`, after the browser mutates it, so a caller can drop its own `input` listener. */
3077
+ onEdit?: (edit: TokenSearchEdit) => void;
1039
3078
  /** Managed collapsible transient behavior. `true`/omitted = on with defaults; `false` = fully off. */
1040
3079
  collapsible?: boolean | TokenSearchCollapsibleOptions;
3080
+ /** Opt-in atomic-chip keyboard behavior (off by default). */
3081
+ keyboard?: false | TokenSearchKeyboardOptions;
1041
3082
  }
1042
3083
  /**
1043
3084
  * The value returned from {@link wireTokenSearchFields}: call it (or `dispose()`) to
@@ -1061,9 +3102,9 @@ interface TokenSearchFieldsHandle {
1061
3102
  * expand/collapse/focus. Returns a {@link TokenSearchFieldsHandle} — a disposer that also
1062
3103
  * exposes the managed `expanded` state per field id.
1063
3104
  */
1064
- declare function wireTokenSearchFields(root: HTMLElement, { onSubmit, collapsible }?: WireTokenSearchFieldsOptions): TokenSearchFieldsHandle;
3105
+ declare function wireTokenSearchFields(root: HTMLElement, { models, onSubmit, onEdit, collapsible, keyboard, }?: WireTokenSearchFieldsOptions): TokenSearchFieldsHandle;
1065
3106
 
1066
- export { type TokenSearchCollapsibleOptions, type TokenSearchFieldsHandle, type TokenSearchSubmit, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
3107
+ export { type TokenSearchCollapsibleOptions, type TokenSearchEdit, type TokenSearchFieldsHandle, type TokenSearchKeyboardOptions, type TokenSearchSubmit, type TokenSearchTokenRemoval, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
1067
3108
  ```
1068
3109
 
1069
3110
  ## `kerfjs/actions`
@@ -1142,3 +3183,446 @@ declare function delegateActions<E extends Element = Element>(root: HTMLElement,
1142
3183
 
1143
3184
  export { type ActionHandler, type DelegateActionsOptions, action, delegateActions };
1144
3185
  ```
3186
+
3187
+ ## `@kerfjs/ui/badge`
3188
+
3189
+ ```ts
3190
+ import { SafeHtml } from 'kerfjs';
3191
+ import { S as SemanticTone } from './semantic-tone-CdW013KF.js';
3192
+
3193
+ /** @deprecated Use SemanticTone. */
3194
+ type BadgeTone = SemanticTone;
3195
+ type BadgeAppearance = 'quiet' | 'solid' | 'outline';
3196
+ type BadgeShape = 'pill' | 'rounded';
3197
+ /** Size of a text badge. A text-free dot is the separate `size: 'dot'` form. */
3198
+ type BadgeSize = 'compact' | 'default';
3199
+ interface BadgeCommonProps {
3200
+ tone?: SemanticTone;
3201
+ className?: string;
3202
+ /** Native named-slot assignment when composed inside a web component. */
3203
+ slot?: string;
3204
+ }
3205
+ /** A badge that shows a short status, count, or category as visible text. */
3206
+ interface BadgeTextProps extends BadgeCommonProps {
3207
+ children: SafeHtml | string | number;
3208
+ appearance?: BadgeAppearance;
3209
+ shape?: BadgeShape;
3210
+ size?: BadgeSize;
3211
+ /** Optional accessible name when the visible content is abbreviated. */
3212
+ label?: string;
3213
+ /** Hide a repeated visual badge from assistive technology. */
3214
+ ariaHidden?: boolean;
3215
+ }
3216
+ /** A labeled dot, announced as an image with its own accessible name. */
3217
+ interface BadgeDotLabeled {
3218
+ /** Accessible name for the dot, for example `"New activity"`. */
3219
+ label: string;
3220
+ ariaHidden?: false;
3221
+ }
3222
+ /** A decorative dot whose meaning the owning component already announces. */
3223
+ interface BadgeDotDecorative {
3224
+ label?: never;
3225
+ ariaHidden: true;
3226
+ }
3227
+ /**
3228
+ * A text-free status dot (the iOS "new content" dot): a small solid circle in
3229
+ * the badge's tone. It has no visible text, so it must either carry its own
3230
+ * accessible `label` (exposed as an image) or be `ariaHidden` because the
3231
+ * surrounding component already folds its meaning into an accessible name.
3232
+ */
3233
+ type BadgeDotProps = BadgeCommonProps & {
3234
+ size: 'dot';
3235
+ children?: never;
3236
+ appearance?: never;
3237
+ shape?: never;
3238
+ } & (BadgeDotLabeled | BadgeDotDecorative);
3239
+ type BadgeProps = BadgeTextProps | BadgeDotProps;
3240
+ /** Compact, non-interactive metadata whose tone, emphasis, and shape are configured by props. */
3241
+ declare function Badge(props: BadgeProps): SafeHtml;
3242
+
3243
+ export { Badge, type BadgeAppearance, type BadgeDotProps, type BadgeProps, type BadgeShape, type BadgeSize, type BadgeTextProps, type BadgeTone, SemanticTone };
3244
+ ```
3245
+
3246
+ ## `@kerfjs/ui/chip`
3247
+
3248
+ ```ts
3249
+ import { SafeHtml } from 'kerfjs';
3250
+ import { S as SemanticTone } from './semantic-tone-CdW013KF.js';
3251
+ import { BadgeAppearance, BadgeShape, BadgeSize } from './badge.js';
3252
+
3253
+ interface ChipCommonProps {
3254
+ /** Decorative leading icon, normally an unsized LucideIcon. */
3255
+ icon?: SafeHtml;
3256
+ tone?: SemanticTone;
3257
+ appearance?: BadgeAppearance;
3258
+ shape?: BadgeShape;
3259
+ size?: BadgeSize;
3260
+ disabled?: boolean;
3261
+ /** Record identifier available to an application delegated action handler. */
3262
+ itemId?: string;
3263
+ className?: string;
3264
+ /** Native named-slot assignment when composed inside a web component. */
3265
+ slot?: string;
3266
+ }
3267
+ /** A removable chip needs both a delegated action and a specific accessible name. */
3268
+ type ChipProps = ChipCommonProps & ({
3269
+ children: SafeHtml | string | number;
3270
+ truncate?: false;
3271
+ } | {
3272
+ children: string;
3273
+ truncate: true;
3274
+ }) & ({
3275
+ removeAction: string;
3276
+ removeLabel: string;
3277
+ } | {
3278
+ removeAction?: never;
3279
+ removeLabel?: never;
3280
+ });
3281
+ /** A short label with an optional native remove button. The application owns removal. */
3282
+ declare function Chip({ children, icon, truncate, tone, appearance, shape, size, disabled, itemId, className, slot, ...remove }: ChipProps): SafeHtml;
3283
+
3284
+ export { Chip, type ChipProps };
3285
+ ```
3286
+
3287
+ ## `@kerfjs/ui/lucide-icon`
3288
+
3289
+ ```ts
3290
+ import * as kerfjs from 'kerfjs';
3291
+ import { IconNode } from 'lucide';
3292
+ import { CssForegroundColor } from './css-values.js';
3293
+
3294
+ type LucideNode = IconNode;
3295
+ type LucideIconSize = 'xs' | 's' | 'm' | 'l' | 'xl' | number;
3296
+ interface LucideIconProps {
3297
+ icon: LucideNode;
3298
+ name: string;
3299
+ /** Outline by default; solid fills compatible glyphs with the current text color. */
3300
+ appearance?: 'outline' | 'solid';
3301
+ className?: string;
3302
+ /** Foreground color; omit to inherit the surrounding text color. */
3303
+ color?: CssForegroundColor;
3304
+ /** Flow with surrounding text instead of occupying a block. */
3305
+ inline?: boolean;
3306
+ label?: string;
3307
+ /** Named icon scale or positive pixel size, converted to rem. Omit for 1em. */
3308
+ size?: LucideIconSize;
3309
+ /** Native named-slot assignment when composed inside a web component. */
3310
+ slot?: string;
3311
+ }
3312
+ /** Render a Lucide-compatible icon node without copying icon SVG strings. */
3313
+ declare function LucideIcon({ icon, name, appearance, className, color, inline, label, size, slot, }: LucideIconProps): kerfjs.SafeHtml;
3314
+
3315
+ export { LucideIcon, type LucideIconProps, type LucideIconSize, type LucideNode };
3316
+ ```
3317
+
3318
+ ## `@kerfjs/ui/wire-toolbar-visibility`
3319
+
3320
+ ```ts
3321
+ type Root = HTMLElement | Document;
3322
+ /**
3323
+ * Wire ToolbarText and ToolbarControlGroup width thresholds below root.
3324
+ * Below is strict: hideBelow hides at width < threshold; showBelow hides at
3325
+ * width >= threshold. Percentages use the closest Toolbar's content box;
3326
+ * em and custom properties use the item's own inherited context. Invalid,
3327
+ * negative, unresolved, or contradictory thresholds leave the item visible.
3328
+ * This helper changes only a component-owned visibility marker. Existing
3329
+ * CSS visibility policies still apply. Dispose with the owning application.
3330
+ */
3331
+ declare function wireToolbarVisibility(root: Root): () => void;
3332
+
3333
+ export { wireToolbarVisibility };
3334
+ ```
3335
+
3336
+ ## `@kerfjs/ui/surface-scaffold`
3337
+
3338
+ ```ts
3339
+ import * as kerfjs from 'kerfjs';
3340
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
3341
+ import { CssLength } from './css-values.js';
3342
+
3343
+ type DialogSurfaceSize = 'small' | 'medium' | 'large';
3344
+ type DialogSurfacePresentation = 'modal' | 'side-sheet' | 'fullscreen';
3345
+ type SurfaceInset = 'none' | 'compact' | 'comfortable';
3346
+ type DialogSurfaceMaxHeight = CssLength | 'viewport';
3347
+ interface DialogSurfaceProps {
3348
+ children: KerfUiContent;
3349
+ size?: DialogSurfaceSize;
3350
+ presentation?: DialogSurfacePresentation;
3351
+ /** Modal preferred width. Takes precedence over size, within the viewport cap. */
3352
+ preferredWidth?: CssLength;
3353
+ bodyInset?: SurfaceInset;
3354
+ footerInset?: SurfaceInset;
3355
+ /** Modal edge clearance. Omit to retain Web Awesome's default viewport cap. */
3356
+ viewportGutter?: CssLength;
3357
+ /** Modal height cap, bounded by the dynamic viewport minus both gutters. */
3358
+ maxHeight?: DialogSurfaceMaxHeight;
3359
+ className?: string;
3360
+ /** Native named-slot assignment when composed inside a web component. */
3361
+ slot?: string;
3362
+ }
3363
+ /** Configure recurring Web Awesome dialog geometry without consumer ::part() CSS. */
3364
+ declare function DialogSurface({ children, size, presentation, preferredWidth, bodyInset, footerInset, viewportGutter, maxHeight, className, slot, }: DialogSurfaceProps): kerfjs.SafeHtml;
3365
+ type PopupSurfaceInset = 'standard' | 'compact' | 'list-zero';
3366
+ interface PopupSurfaceProps {
3367
+ children: KerfUiContent;
3368
+ inset?: PopupSurfaceInset;
3369
+ className?: string;
3370
+ /** Native named-slot assignment when composed inside a web component. */
3371
+ slot?: string;
3372
+ }
3373
+ /** Configure recurring Web Awesome dropdown-menu geometry without consumer ::part() CSS. */
3374
+ declare function PopupSurface({ children, inset, className, slot, }: PopupSurfaceProps): kerfjs.SafeHtml;
3375
+
3376
+ export { DialogSurface, type DialogSurfaceMaxHeight, type DialogSurfacePresentation, type DialogSurfaceProps, type DialogSurfaceSize, PopupSurface, type PopupSurfaceInset, type PopupSurfaceProps, type SurfaceInset };
3377
+ ```
3378
+
3379
+ ## `@kerfjs/ui/text`
3380
+
3381
+ ```ts
3382
+ import * as kerfjs from 'kerfjs';
3383
+ import { KerfBaseAttrs } from 'kerfjs/jsx-runtime';
3384
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
3385
+ import { S as Sides } from './sides-BPSWde0A.js';
3386
+ import { CssForegroundColor } from './css-values.js';
3387
+
3388
+ type FieldLabelProps = Omit<KerfBaseAttrs, 'children' | 'class' | 'className'> & {
3389
+ children: string;
3390
+ class?: string;
3391
+ className?: string;
3392
+ };
3393
+ /** Visible field heading for a read-only preview; use its id with aria-labelledby on the preview group. */
3394
+ declare function FieldLabel({ children, class: classValue, className, ...attributes }: FieldLabelProps): kerfjs.SafeHtml;
3395
+
3396
+ type TextVariant = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';
3397
+ type TextTone = 'default' | 'quiet' | 'danger';
3398
+ type TextSize = 'compact' | 'default' | 'large' | 'xlarge';
3399
+ type TextFont = 'default' | 'monospace';
3400
+ type TextBorder = 'transparent' | 'none';
3401
+ type TextLineHeight = 'default' | 'tight';
3402
+ type TextWrap = 'normal' | 'anywhere' | 'nowrap' | 'truncate';
3403
+ type TextContent = KerfUiContent | string | number | readonly TextContent[];
3404
+ type TextCommonProps = Omit<KerfBaseAttrs, 'children' | 'class' | 'className'> & {
3405
+ /** Native heading, paragraph, or inline span element to render. Defaults to `p`. */
3406
+ variant?: TextVariant;
3407
+ /** Semantic foreground treatment. Defaults to the inherited foreground. */
3408
+ tone?: TextTone;
3409
+ /** Foreground color; overrides tone when supplied, or inherits when omitted. */
3410
+ color?: CssForegroundColor;
3411
+ /** Text sizing independent of the native semantic element. */
3412
+ size?: TextSize;
3413
+ /** Font family independent of the native semantic element. */
3414
+ font?: TextFont;
3415
+ /** Transparent alignment border or no border when embedded in owner chrome. */
3416
+ border?: TextBorder;
3417
+ /** Add the shared control gutter outside selected physical edges. */
3418
+ controlMargins?: Sides;
3419
+ /** Remove the block variant's item padding and border in compact content. */
3420
+ flush?: boolean;
3421
+ /** Use compact leading for short dialog or metadata copy. */
3422
+ lineHeight?: TextLineHeight;
3423
+ children: TextContent;
3424
+ class?: string;
3425
+ className?: string;
3426
+ };
3427
+ type TextProps = TextCommonProps & ({
3428
+ /** Wrap normally or break long unbroken strings. Defaults to normal. */
3429
+ wrap?: 'normal' | 'anywhere';
3430
+ /** Cap wrapped text to this positive number of lines. */
3431
+ maxLines?: number;
3432
+ } | {
3433
+ /** Keep one line, with or without an ellipsis. */
3434
+ wrap: 'nowrap' | 'truncate';
3435
+ maxLines?: never;
3436
+ });
3437
+ /**
3438
+ * Semantic heading, paragraph, or inline text. Block variants use the standard
3439
+ * content-item padding; `span` adds no box geometry.
3440
+ * All ordinary native heading/paragraph attributes pass through to the element.
3441
+ */
3442
+ declare function Text({ variant: Variant, tone, color, size, font, border, flush, controlMargins, lineHeight, wrap, maxLines, children, class: classValue, className, style, ...attributes }: TextProps): kerfjs.SafeHtml;
3443
+
3444
+ export { FieldLabel, type FieldLabelProps, Sides, Text, type TextBorder, type TextContent, type TextFont, type TextLineHeight, type TextProps, type TextSize, type TextTone, type TextVariant, type TextWrap };
3445
+ ```
3446
+
3447
+ ## `@kerfjs/ui/row`
3448
+
3449
+ ```ts
3450
+ import * as kerfjs from 'kerfjs';
3451
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
3452
+ import { H as HorizontalAlignment, V as VerticalAlignment } from './flex-alignment-4ms8ZbV8.js';
3453
+ import { S as Sides } from './sides-BPSWde0A.js';
3454
+ import { UiGap, CssFlexKeyword, CssFlex } from './css-values.js';
3455
+ export { CssLength, UiGapValue, UiSpaceName } from './css-values.js';
3456
+
3457
+ type RowRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
3458
+ 'data-component'?: never;
3459
+ 'data-h-align'?: never;
3460
+ 'data-v-align'?: never;
3461
+ 'data-flex'?: never;
3462
+ 'data-fill'?: never;
3463
+ 'data-wrap'?: never;
3464
+ 'data-text-insets'?: never;
3465
+ 'data-control-insets'?: never;
3466
+ }>;
3467
+ interface RowProps {
3468
+ children?: KerfUiContent;
3469
+ /** Horizontal distribution. Defaults to left. */
3470
+ hAlign?: HorizontalAlignment;
3471
+ /** Vertical alignment and wrapped-line distribution. Defaults to full. */
3472
+ vAlign?: VerticalAlignment;
3473
+ /** One spacing value or separate column and row values. Defaults to xs. */
3474
+ gap?: UiGap;
3475
+ /** Allow this row to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
3476
+ flex?: boolean | CssFlexKeyword | CssFlex;
3477
+ /**
3478
+ * Fill the height of a parent with a definite height, such as an app root or
3479
+ * a fixed-height frame, when this row is that parent's layout root. Inside a
3480
+ * flex layout use `flex` instead. Defaults to false.
3481
+ */
3482
+ fill?: boolean;
3483
+ /** Allow children to wrap onto additional lines. */
3484
+ wrap?: boolean;
3485
+ /** Physical sides that receive the standard 17px text inset. */
3486
+ textInsets?: Sides;
3487
+ /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */
3488
+ controlInsets?: Sides;
3489
+ className?: string;
3490
+ /** Safe `data-*` metadata; Row-owned structural attributes remain protected. */
3491
+ rootAttributes?: RowRootAttributes;
3492
+ /** Native named-slot assignment when composed inside a web component. */
3493
+ slot?: string;
3494
+ }
3495
+ /** A horizontal flex row with explicit physical-axis alignment and spacing. */
3496
+ declare function Row({ children, hAlign, vAlign, gap, flex, fill, wrap, textInsets, controlInsets, className, rootAttributes, slot, }: RowProps): kerfjs.SafeHtml;
3497
+
3498
+ export { CssFlex, CssFlexKeyword, HorizontalAlignment, Row, type RowProps, Sides, UiGap, VerticalAlignment };
3499
+ ```
3500
+
3501
+ ## `@kerfjs/ui/grid`
3502
+
3503
+ ```ts
3504
+ import * as kerfjs from 'kerfjs';
3505
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
3506
+ import { S as Sides } from './sides-BPSWde0A.js';
3507
+ import { UiGap, CssFlexKeyword, CssFlex, CssLength } from './css-values.js';
3508
+ export { UiGapValue, UiSpaceName } from './css-values.js';
3509
+
3510
+ type GridRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
3511
+ 'data-component'?: never;
3512
+ 'data-columns'?: never;
3513
+ 'data-min-column-width'?: never;
3514
+ 'data-auto-fill'?: never;
3515
+ 'data-flex'?: never;
3516
+ 'data-fill'?: never;
3517
+ 'data-text-insets'?: never;
3518
+ 'data-control-insets'?: never;
3519
+ }>;
3520
+ interface GridCommonProps {
3521
+ children?: KerfUiContent;
3522
+ /** Make this a labeled multi-select grid for `ContentItem selectionMode="multiple"` tiles. */
3523
+ selectionMode?: 'multiple';
3524
+ /** Accessible name for the multi-select grid. */
3525
+ ariaLabel?: string;
3526
+ /** One spacing value or separate column and row values. Defaults to xs. */
3527
+ gap?: UiGap;
3528
+ /** Allow this grid to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
3529
+ flex?: boolean | CssFlexKeyword | CssFlex;
3530
+ /**
3531
+ * Fill the height of a parent with a definite height, such as an app root or
3532
+ * a fixed-height frame, when this grid is that parent's layout root. Inside a
3533
+ * flex layout use `flex` instead. Defaults to false.
3534
+ */
3535
+ fill?: boolean;
3536
+ /** Physical sides that receive the standard 17px text inset. */
3537
+ textInsets?: Sides;
3538
+ /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */
3539
+ controlInsets?: Sides;
3540
+ className?: string;
3541
+ /** Safe `data-*` metadata; Grid-owned structural attributes remain protected. */
3542
+ rootAttributes?: GridRootAttributes;
3543
+ /** Native named-slot assignment when composed inside a web component. */
3544
+ slot?: string;
3545
+ }
3546
+ type GridProps = GridCommonProps & ({
3547
+ /** Number of equal-width columns. Must be a positive safe integer. */
3548
+ columns: number;
3549
+ minColumnWidth?: never;
3550
+ autoFill?: never;
3551
+ } | {
3552
+ columns?: never;
3553
+ /** Fit equal columns of at least this width; collapse as the container narrows. */
3554
+ minColumnWidth: CssLength;
3555
+ /** Keep empty tracks instead of stretching a sparse row. Defaults to false. */
3556
+ autoFill?: boolean;
3557
+ });
3558
+ /** Render equal tracks with a fixed count or a responsive minimum width. */
3559
+ declare function Grid({ children, selectionMode, ariaLabel, columns, minColumnWidth, autoFill, gap, flex, fill, textInsets, controlInsets, className, rootAttributes, slot, }: GridProps): kerfjs.SafeHtml;
3560
+
3561
+ export { CssFlex, CssFlexKeyword, CssLength, Grid, type GridProps, Sides, UiGap };
3562
+ ```
3563
+
3564
+ ## `@kerfjs/ui/spacer`
3565
+
3566
+ ```ts
3567
+ import * as kerfjs from 'kerfjs';
3568
+ import { UiSpaceName, CssLength } from './css-values.js';
3569
+
3570
+ interface SpacerProps {
3571
+ /** Grow and shrink to fill the available space along a parent's flex axis. */
3572
+ flex?: boolean;
3573
+ /** A named UI spacing token or typed physical width. */
3574
+ width?: UiSpaceName | CssLength;
3575
+ /** A named UI spacing token or typed physical height. */
3576
+ height?: UiSpaceName | CssLength;
3577
+ className?: string;
3578
+ /** Native named-slot assignment when composed inside a web component. */
3579
+ slot?: string;
3580
+ }
3581
+ /** A decorative fixed-size or flexible gap for Row, List, and other flex layouts. */
3582
+ declare function Spacer({ flex, width, height, className, slot, }: SpacerProps): kerfjs.SafeHtml;
3583
+
3584
+ export { CssLength, Spacer, type SpacerProps, UiSpaceName };
3585
+ ```
3586
+
3587
+ ## `@kerfjs/ui/token-search-model`
3588
+
3589
+ ```ts
3590
+ export { d as TokenSearchModel, e as TokenSearchModelOptions, f as TokenSearchResolvedToken, g as TokenSearchRule, h as TokenSearchState, i as TokenSearchSuggestion, l as createTokenSearchModel } from './token-search-model-DTcTXWMS.js';
3591
+ import 'kerfjs';
3592
+ import './semantic-content-BbzjvSu9.js';
3593
+ ```
3594
+
3595
+ ## `@kerfjs/ui/anchored-surface`
3596
+
3597
+ ```ts
3598
+ import { PopoverOptions, OverlayContent, OverlayHandle } from 'kerfjs/overlay';
3599
+
3600
+ /** App-facing options for a non-modal, arbitrary-content help surface. */
3601
+ interface AnchoredSurfaceOptions extends Pick<PopoverOptions, 'placement' | 'align' | 'gap' | 'dismiss' | 'initialFocus' | 'onDismiss' | 'outsideIgnore'> {
3602
+ /** Accessible name for the non-modal dialog. */
3603
+ label: string;
3604
+ }
3605
+ /** A viewport pointer location; `context` is the element under the pointer. */
3606
+ interface AnchoredSurfacePoint {
3607
+ x: number;
3608
+ y: number;
3609
+ /** Pass the event target when opening from inside a modal dialog. */
3610
+ context?: Element;
3611
+ }
3612
+ /**
3613
+ * Open app-owned content beside an element. The surface owns its Kerf UI chrome;
3614
+ * kerfjs/overlay owns positioning, top-layer/modal hosting, focus, and dismissal.
3615
+ * The caller owns content, action policy, and calling `close()` when an action
3616
+ * completes. `initialFocus` defaults to the first focusable descendant; set it
3617
+ * to `false` for read-only help that should leave focus on its trigger.
3618
+ */
3619
+ declare function openAnchoredSurface(anchor: Element, content: OverlayContent, options: AnchoredSurfaceOptions): OverlayHandle;
3620
+ /**
3621
+ * Open the same surface at pointer coordinates. The temporary anchor has no
3622
+ * layout or hit target. `context` keeps a surface opened inside a modal dialog
3623
+ * in that dialog's interactive host slot; it is removed on close or failure.
3624
+ */
3625
+ declare function openAnchoredSurfaceAt(point: AnchoredSurfacePoint, content: OverlayContent, options: AnchoredSurfaceOptions): OverlayHandle;
3626
+
3627
+ export { type AnchoredSurfaceOptions, type AnchoredSurfacePoint, openAnchoredSurface, openAnchoredSurfaceAt };
3628
+ ```