@kerfjs/ui 5.0.0-beta.6 → 5.0.0-beta.61

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