@kerfjs/ui 5.0.0-beta.4 → 5.0.0-beta.41

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 (498) hide show
  1. package/README.md +342 -108
  2. package/ai/application-ui-diagnostic-ids-v1.json +100 -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 +41 -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 +25 -0
  10. package/ai/catalog-authoring.schema.json +61 -0
  11. package/ai/compile-time-contracts-v1.json +132 -0
  12. package/ai/compile-time-contracts-v1.schema.json +51 -0
  13. package/ai/component-catalog-extension-v2.d.ts +7 -0
  14. package/ai/component-catalog-extension-v2.schema.json +7 -0
  15. package/ai/component-catalog-extension.schema.json +79 -0
  16. package/ai/component-catalog-v2-overrides.json +496 -0
  17. package/ai/component-catalog-v2.d.ts +90 -0
  18. package/ai/component-catalog-v2.json +5678 -0
  19. package/ai/component-catalog-v2.schema.json +298 -0
  20. package/ai/component-catalog.json +2694 -417
  21. package/ai/component-catalog.schema.json +39 -0
  22. package/ai/public-api-signatures-v1.md +1591 -126
  23. package/ai/skill.md +140 -54
  24. package/ai/webawesome-jsx-signatures-v1.md +2 -1
  25. package/analyzer/cli.mjs +49 -0
  26. package/analyzer/index.d.mts +46 -0
  27. package/analyzer/index.mjs +1086 -0
  28. package/analyzer/report.schema.json +66 -0
  29. package/dist/app-tab.d.ts +15 -4
  30. package/dist/app-tab.js +3 -1
  31. package/dist/browser/app-tab.js +1 -0
  32. package/dist/browser/floating-toolbar.js +3 -0
  33. package/dist/browser/list-action-row.js +5 -0
  34. package/dist/browser/list-header.js +7 -0
  35. package/dist/browser/list-inset-control.js +3 -0
  36. package/dist/browser/list-inset-text.js +3 -0
  37. package/dist/browser/list-item.js +5 -0
  38. package/dist/browser/list.js +3 -0
  39. package/dist/browser/pane.js +3 -0
  40. package/dist/browser/row.js +3 -0
  41. package/dist/browser/segmented-control.js +1 -0
  42. package/dist/browser/select.js +1 -0
  43. package/dist/browser/skeleton.js +3 -0
  44. package/dist/browser/spacer.js +3 -0
  45. package/dist/browser/state-banner.js +1 -0
  46. package/dist/browser/sunken-panel.js +3 -0
  47. package/dist/browser/surface-scaffold.js +3 -0
  48. package/dist/browser/text.js +3 -0
  49. package/dist/browser/toolbar-text.js +1 -0
  50. package/dist/browser/value-table.js +1 -0
  51. package/dist/catalog-resources.d.ts +35 -0
  52. package/dist/catalog-resources.js +22 -0
  53. package/dist/catalog-resources.js.map +1 -0
  54. package/dist/catalog.d.ts +151 -0
  55. package/dist/catalog.js +396 -0
  56. package/dist/catalog.js.map +1 -0
  57. package/dist/chunk-2TYUFZUC.js +22 -0
  58. package/dist/chunk-2TYUFZUC.js.map +1 -0
  59. package/dist/chunk-6NDBE2OK.js +32 -0
  60. package/dist/chunk-6NDBE2OK.js.map +1 -0
  61. package/dist/chunk-6WTETUZ6.js +21 -0
  62. package/dist/chunk-6WTETUZ6.js.map +1 -0
  63. package/dist/chunk-AQFWBG23.js +83 -0
  64. package/dist/chunk-AQFWBG23.js.map +1 -0
  65. package/dist/chunk-B6JYRX3P.js +51 -0
  66. package/dist/chunk-B6JYRX3P.js.map +1 -0
  67. package/dist/chunk-C34NZOLJ.js +53 -0
  68. package/dist/chunk-C34NZOLJ.js.map +1 -0
  69. package/dist/chunk-CAB6NQ2T.js +102 -0
  70. package/dist/chunk-CAB6NQ2T.js.map +1 -0
  71. package/dist/chunk-CK6P5AUK.js +136 -0
  72. package/dist/chunk-CK6P5AUK.js.map +1 -0
  73. package/dist/chunk-DAO2VFTT.js +129 -0
  74. package/dist/chunk-DAO2VFTT.js.map +1 -0
  75. package/dist/chunk-DEYOGDE7.js +141 -0
  76. package/dist/chunk-DEYOGDE7.js.map +1 -0
  77. package/dist/chunk-DYBPI5O2.js +25 -0
  78. package/dist/chunk-DYBPI5O2.js.map +1 -0
  79. package/dist/{chunk-TRNBHPNW.js → chunk-E34TE2ZI.js} +12 -8
  80. package/dist/chunk-E34TE2ZI.js.map +1 -0
  81. package/dist/chunk-EYALJQQD.js +25 -0
  82. package/dist/chunk-EYALJQQD.js.map +1 -0
  83. package/dist/chunk-I45QO2IZ.js +88 -0
  84. package/dist/chunk-I45QO2IZ.js.map +1 -0
  85. package/dist/chunk-I4V3UDSA.js +79 -0
  86. package/dist/chunk-I4V3UDSA.js.map +1 -0
  87. package/dist/chunk-OGTJNHRG.js +42 -0
  88. package/dist/chunk-OGTJNHRG.js.map +1 -0
  89. package/dist/chunk-OL4TC3U2.js +156 -0
  90. package/dist/chunk-OL4TC3U2.js.map +1 -0
  91. package/dist/chunk-OMSRSL3B.js +120 -0
  92. package/dist/chunk-OMSRSL3B.js.map +1 -0
  93. package/dist/chunk-R3EBGEAW.js +46 -0
  94. package/dist/chunk-R3EBGEAW.js.map +1 -0
  95. package/dist/chunk-SOK5EUF7.js +25 -0
  96. package/dist/chunk-SOK5EUF7.js.map +1 -0
  97. package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
  98. package/dist/chunk-SRSJO5QE.js.map +1 -0
  99. package/dist/chunk-T7DOWS6T.js +61 -0
  100. package/dist/chunk-T7DOWS6T.js.map +1 -0
  101. package/dist/chunk-TIGSG6BX.js +218 -0
  102. package/dist/chunk-TIGSG6BX.js.map +1 -0
  103. package/dist/chunk-UDHHKZWO.js +41 -0
  104. package/dist/chunk-UDHHKZWO.js.map +1 -0
  105. package/dist/{chunk-MQIZAD4A.js → chunk-UKKSMZYT.js} +76 -22
  106. package/dist/chunk-UKKSMZYT.js.map +1 -0
  107. package/dist/chunk-V6GQKOX2.js +39 -0
  108. package/dist/chunk-V6GQKOX2.js.map +1 -0
  109. package/dist/chunk-V6HCTCVD.js +46 -0
  110. package/dist/chunk-V6HCTCVD.js.map +1 -0
  111. package/dist/chunk-VDUJZF5G.js +515 -0
  112. package/dist/chunk-VDUJZF5G.js.map +1 -0
  113. package/dist/chunk-VXFGMDA7.js +31 -0
  114. package/dist/chunk-VXFGMDA7.js.map +1 -0
  115. package/dist/chunk-WN52USHV.js +115 -0
  116. package/dist/chunk-WN52USHV.js.map +1 -0
  117. package/dist/chunk-WSXUPSID.js +43 -0
  118. package/dist/chunk-WSXUPSID.js.map +1 -0
  119. package/dist/chunk-X5GRDQ7V.js +50 -0
  120. package/dist/chunk-X5GRDQ7V.js.map +1 -0
  121. package/dist/{chunk-56RJXPHG.js → chunk-Y6LYKFXY.js} +1 -1
  122. package/dist/{chunk-56RJXPHG.js.map → chunk-Y6LYKFXY.js.map} +1 -1
  123. package/dist/chunk-ZASJJMGV.js +17 -0
  124. package/dist/chunk-ZASJJMGV.js.map +1 -0
  125. package/dist/chunk-ZPWZIVI6.js +47 -0
  126. package/dist/chunk-ZPWZIVI6.js.map +1 -0
  127. package/dist/collapsible-panel.d.ts +77 -0
  128. package/dist/collapsible-panel.js +84 -0
  129. package/dist/collapsible-panel.js.map +1 -0
  130. package/dist/css-values.d.ts +75 -0
  131. package/dist/css-values.js +3 -0
  132. package/dist/css-values.js.map +1 -0
  133. package/dist/device-class.d.ts +62 -0
  134. package/dist/device-class.js +103 -0
  135. package/dist/device-class.js.map +1 -0
  136. package/dist/disclosure-arrow.js +1 -1
  137. package/dist/divider-sides-BzB6rphT.d.ts +6 -0
  138. package/dist/empty-state.d.ts +3 -2
  139. package/dist/empty-state.js +1 -1
  140. package/dist/flex-alignment-CF8NLj7i.d.ts +4 -0
  141. package/dist/floating-toolbar.d.ts +29 -0
  142. package/dist/floating-toolbar.js +3 -0
  143. package/dist/floating-toolbar.js.map +1 -0
  144. package/dist/index.d.ts +28 -14
  145. package/dist/index.js +34 -23
  146. package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +19 -8
  147. package/dist/list-action-row.js +7 -0
  148. package/dist/list-action-row.js.map +1 -0
  149. package/dist/list-header.d.ts +80 -0
  150. package/dist/list-header.js +9 -0
  151. package/dist/list-header.js.map +1 -0
  152. package/dist/list-inset-control.d.ts +20 -0
  153. package/dist/list-inset-control.js +3 -0
  154. package/dist/list-inset-control.js.map +1 -0
  155. package/dist/list-inset-text.d.ts +29 -0
  156. package/dist/list-inset-text.js +3 -0
  157. package/dist/list-inset-text.js.map +1 -0
  158. package/dist/list-item.d.ts +45 -0
  159. package/dist/list-item.js +7 -0
  160. package/dist/list-item.js.map +1 -0
  161. package/dist/list.d.ts +30 -0
  162. package/dist/list.js +5 -0
  163. package/dist/list.js.map +1 -0
  164. package/dist/loading-spinner.js +1 -1
  165. package/dist/nav-stack.d.ts +41 -0
  166. package/dist/nav-stack.js +6 -0
  167. package/dist/nav-stack.js.map +1 -0
  168. package/dist/pane.d.ts +50 -0
  169. package/dist/pane.js +4 -0
  170. package/dist/pane.js.map +1 -0
  171. package/dist/resizable-region.d.ts +21 -3
  172. package/dist/resizable-region.js +1 -1
  173. package/dist/row.d.ts +28 -0
  174. package/dist/row.js +5 -0
  175. package/dist/row.js.map +1 -0
  176. package/dist/segmented-control.d.ts +11 -8
  177. package/dist/segmented-control.js +3 -1
  178. package/dist/select-register.d.ts +5 -1
  179. package/dist/select-register.js +129 -1
  180. package/dist/select-register.js.map +1 -1
  181. package/dist/select.d.ts +34 -8
  182. package/dist/select.js +3 -1
  183. package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
  184. package/dist/skeleton.d.ts +25 -0
  185. package/dist/skeleton.js +4 -0
  186. package/dist/skeleton.js.map +1 -0
  187. package/dist/spacer.d.ts +16 -0
  188. package/dist/spacer.js +4 -0
  189. package/dist/spacer.js.map +1 -0
  190. package/dist/split-view.d.ts +43 -0
  191. package/dist/split-view.js +82 -0
  192. package/dist/split-view.js.map +1 -0
  193. package/dist/state-banner.d.ts +11 -5
  194. package/dist/state-banner.js +3 -1
  195. package/dist/styles/app-tab.css +26 -0
  196. package/dist/styles/catalog.css +582 -0
  197. package/dist/styles/collapsible-panel.css +212 -0
  198. package/dist/styles/floating-toolbar.css +38 -0
  199. package/dist/styles/foundation.css +80 -0
  200. package/dist/styles/layout.css +2 -20
  201. package/dist/styles/list-action-row.css +224 -0
  202. package/dist/styles/list-header.css +224 -0
  203. package/dist/styles/list-inset-control.css +23 -0
  204. package/dist/styles/list-inset-text.css +30 -0
  205. package/dist/styles/list-item.css +171 -0
  206. package/dist/styles/list.css +118 -0
  207. package/dist/styles/nav-stack.css +141 -0
  208. package/dist/styles/pane.css +62 -0
  209. package/dist/styles/resizable-region.css +158 -0
  210. package/dist/styles/row.css +92 -0
  211. package/dist/styles/segmented-control.css +10 -7
  212. package/dist/styles/select.css +115 -2
  213. package/dist/styles/skeleton.css +31 -0
  214. package/dist/styles/spacer.css +11 -0
  215. package/dist/styles/split-view.css +35 -0
  216. package/dist/styles/state-banner.css +49 -10
  217. package/dist/styles/styles.css +10 -5
  218. package/dist/styles/sunken-panel.css +22 -0
  219. package/dist/styles/surface-scaffold.css +51 -0
  220. package/dist/styles/tab-bar.css +24 -0
  221. package/dist/styles/tab-scaffold.css +85 -0
  222. package/dist/styles/text.css +6 -0
  223. package/dist/styles/token-search-field.css +14 -2
  224. package/dist/styles/toolbar-control-group.css +235 -8
  225. package/dist/styles/toolbar-text.css +65 -2
  226. package/dist/styles/toolbar.css +47 -2
  227. package/dist/styles/webawesome.css +171 -0
  228. package/dist/styles/workbench.css +187 -0
  229. package/dist/sunken-panel.d.ts +20 -0
  230. package/dist/sunken-panel.js +3 -0
  231. package/dist/sunken-panel.js.map +1 -0
  232. package/dist/surface-scaffold.d.ts +26 -0
  233. package/dist/surface-scaffold.js +3 -0
  234. package/dist/surface-scaffold.js.map +1 -0
  235. package/dist/tab-bar.d.ts +24 -6
  236. package/dist/tab-bar.js +1 -1
  237. package/dist/tab-scaffold.d.ts +31 -0
  238. package/dist/tab-scaffold.js +51 -0
  239. package/dist/tab-scaffold.js.map +1 -0
  240. package/dist/text.d.ts +20 -0
  241. package/dist/text.js +3 -0
  242. package/dist/text.js.map +1 -0
  243. package/dist/token-search-field.d.ts +30 -13
  244. package/dist/token-search-field.js +1 -1
  245. package/dist/toolbar-control-group.d.ts +33 -7
  246. package/dist/toolbar-control-group.js +1 -1
  247. package/dist/toolbar-text.d.ts +31 -4
  248. package/dist/toolbar-text.js +3 -1
  249. package/dist/toolbar.d.ts +10 -7
  250. package/dist/toolbar.js +1 -1
  251. package/dist/value-table.d.ts +7 -3
  252. package/dist/value-table.js +3 -1
  253. package/dist/webawesome.d.ts +1 -0
  254. package/dist/wire-catalog.d.ts +57 -0
  255. package/dist/wire-catalog.js +302 -0
  256. package/dist/wire-catalog.js.map +1 -0
  257. package/dist/wire-nav-stack.d.ts +18 -0
  258. package/dist/wire-nav-stack.js +228 -0
  259. package/dist/wire-nav-stack.js.map +1 -0
  260. package/dist/wire-resizable-regions.d.ts +1 -1
  261. package/dist/wire-resizable-regions.js +1 -1
  262. package/dist/wire-sidebar.d.ts +48 -0
  263. package/dist/wire-sidebar.js +147 -0
  264. package/dist/wire-sidebar.js.map +1 -0
  265. package/dist/wire-tab-bars.d.ts +19 -2
  266. package/dist/wire-tab-bars.js +1 -1
  267. package/dist/wire-tab-scaffold.d.ts +11 -0
  268. package/dist/wire-tab-scaffold.js +21 -0
  269. package/dist/wire-tab-scaffold.js.map +1 -0
  270. package/dist/wire-token-search-fields.d.ts +104 -4
  271. package/dist/wire-token-search-fields.js +1 -1
  272. package/dist/workbench.d.ts +44 -0
  273. package/dist/workbench.js +106 -0
  274. package/dist/workbench.js.map +1 -0
  275. package/docs/accessibility.md +155 -20
  276. package/docs/app-layouts.md +61 -0
  277. package/docs/catalog.md +364 -0
  278. package/docs/collapsible-panel.md +142 -0
  279. package/docs/component-contract.md +258 -28
  280. package/docs/component-integration.md +63 -0
  281. package/docs/component-selection.md +219 -57
  282. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  283. package/docs/design/templates/empty-state/filtered.svg +1 -0
  284. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  285. package/docs/design/templates/empty-state/with-action.svg +1 -0
  286. package/docs/design/templates/empty-state-dark.svg +1 -0
  287. package/docs/design/templates/empty-state.svg +1 -0
  288. package/docs/design/templates/list/compact-dark.svg +1 -0
  289. package/docs/design/templates/list/compact.svg +1 -0
  290. package/docs/design/templates/list/stack-dark.svg +1 -0
  291. package/docs/design/templates/list/stack.svg +1 -0
  292. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  293. package/docs/design/templates/list-action-row/default.svg +1 -0
  294. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  295. package/docs/design/templates/list-action-row/selected.svg +1 -0
  296. package/docs/design/templates/list-action-row-dark.svg +1 -0
  297. package/docs/design/templates/list-action-row.svg +1 -0
  298. package/docs/design/templates/list-dark.svg +1 -0
  299. package/docs/design/templates/list-header/action-dark.svg +1 -0
  300. package/docs/design/templates/list-header/action.svg +1 -0
  301. package/docs/design/templates/list-header/count-dark.svg +1 -0
  302. package/docs/design/templates/list-header/count.svg +1 -0
  303. package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
  304. package/docs/design/templates/list-header/inline-action.svg +1 -0
  305. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  306. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  307. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  308. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  309. package/docs/design/templates/list-header-dark.svg +1 -0
  310. package/docs/design/templates/list-header.svg +1 -0
  311. package/docs/design/templates/list-item/default-dark.svg +1 -0
  312. package/docs/design/templates/list-item/default.svg +1 -0
  313. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  314. package/docs/design/templates/list-item/multiline.svg +1 -0
  315. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  316. package/docs/design/templates/list-item/selected.svg +1 -0
  317. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  318. package/docs/design/templates/list-item/trailing.svg +1 -0
  319. package/docs/design/templates/list-item-dark.svg +1 -0
  320. package/docs/design/templates/list-item.svg +1 -0
  321. package/docs/design/templates/list.svg +1 -0
  322. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  323. package/docs/design/templates/segmented-control/equal.svg +1 -0
  324. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  325. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  326. package/docs/design/templates/segmented-control-dark.svg +1 -0
  327. package/docs/design/templates/segmented-control.svg +1 -0
  328. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  329. package/docs/design/templates/skeleton/avatar.svg +1 -0
  330. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  331. package/docs/design/templates/skeleton/block.svg +1 -0
  332. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  333. package/docs/design/templates/skeleton/lines.svg +1 -0
  334. package/docs/design/templates/skeleton-dark.svg +1 -0
  335. package/docs/design/templates/skeleton.svg +1 -0
  336. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  337. package/docs/design/templates/state-banner/danger.svg +1 -0
  338. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  339. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  340. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  341. package/docs/design/templates/state-banner/info.svg +1 -0
  342. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  343. package/docs/design/templates/state-banner/success.svg +1 -0
  344. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  345. package/docs/design/templates/state-banner/warning.svg +1 -0
  346. package/docs/design/templates/state-banner-dark.svg +1 -0
  347. package/docs/design/templates/state-banner.svg +1 -0
  348. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  349. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  350. package/docs/design/templates/tab-bar-dark.svg +1 -0
  351. package/docs/design/templates/tab-bar.svg +1 -0
  352. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  353. package/docs/design/templates/token-search-field/empty.svg +1 -0
  354. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  355. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  356. package/docs/design/templates/token-search-field-dark.svg +1 -0
  357. package/docs/design/templates/token-search-field.svg +1 -0
  358. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  359. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  360. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  361. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  362. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  363. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  364. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  365. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  366. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  367. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  368. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  369. package/docs/design/templates/toolbar-control-group.svg +1 -0
  370. package/docs/design/templates/toolbar-dark.svg +1 -0
  371. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  372. package/docs/design/templates/toolbar-text/default.svg +1 -0
  373. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  374. package/docs/design/templates/toolbar-text/large.svg +1 -0
  375. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  376. package/docs/design/templates/toolbar-text/small.svg +1 -0
  377. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  378. package/docs/design/templates/toolbar-text.svg +1 -0
  379. package/docs/design/templates/toolbar.svg +1 -0
  380. package/docs/design/templates/value-table/details-dark.svg +1 -0
  381. package/docs/design/templates/value-table/details.svg +1 -0
  382. package/docs/design/templates/value-table-dark.svg +1 -0
  383. package/docs/design/templates/value-table.svg +1 -0
  384. package/docs/design/templates.md +128 -0
  385. package/docs/design-philosophy.md +33 -11
  386. package/docs/device-class.md +54 -0
  387. package/docs/examples/application-ui-profile.json +60 -0
  388. package/docs/examples/command-palette-adapter.tsx +68 -17
  389. package/docs/examples/component-catalog-extension-v2.json +80 -0
  390. package/docs/examples/component-catalog-extension.json +45 -0
  391. package/docs/layout.md +169 -38
  392. package/docs/nav-stack.md +77 -0
  393. package/docs/recipes.md +64 -16
  394. package/docs/split-view.md +55 -0
  395. package/docs/sunken-panel.md +53 -0
  396. package/docs/surface-scaffold.md +25 -0
  397. package/docs/tab-scaffold.md +52 -0
  398. package/docs/type-contracts.md +127 -0
  399. package/docs/ui-analyzer.md +118 -0
  400. package/docs/ui-doctor.md +91 -0
  401. package/docs/ui-evaluator.md +122 -0
  402. package/docs/ux-demo.md +29 -10
  403. package/docs/webawesome-theme.md +82 -8
  404. package/docs/workbench.md +123 -0
  405. package/doctor/cli.mjs +115 -0
  406. package/doctor/config.schema.json +68 -0
  407. package/doctor/eslint-diagnostics.d.mts +24 -0
  408. package/doctor/eslint-diagnostics.mjs +24 -0
  409. package/doctor/index.d.mts +116 -0
  410. package/doctor/index.mjs +1272 -0
  411. package/doctor/report.schema.json +118 -0
  412. package/evaluator/cli.mjs +89 -0
  413. package/evaluator/index.d.mts +90 -0
  414. package/evaluator/index.mjs +1138 -0
  415. package/evaluator/report.schema.json +152 -0
  416. package/llms.txt +260 -32
  417. package/package.json +205 -38
  418. package/traversal-exclusions.mjs +27 -0
  419. package/ux-demo/recipes/app-shell.tsx +223 -13
  420. package/ux-demo/recipes/collapsible-sidebar.tsx +230 -0
  421. package/ux-demo/recipes/compact-toolbar.tsx +116 -12
  422. package/ux-demo/recipes/composer-form.tsx +120 -19
  423. package/ux-demo/recipes/list-detail-dialog.tsx +205 -0
  424. package/ux-demo/recipes/list-workspace-states.tsx +176 -9
  425. package/ux-demo/recipes/loaders.ts +13 -4
  426. package/ux-demo/recipes/loading-inspector.tsx +191 -0
  427. package/ux-demo/recipes/mount-recipe.ts +31 -5
  428. package/ux-demo/recipes/navigation-sidebar.tsx +123 -11
  429. package/ux-demo/recipes/navigation-stack.tsx +121 -0
  430. package/ux-demo/recipes/recipes.css +125 -16
  431. package/ux-demo/recipes/types.ts +9 -1
  432. package/ux-demo/recipes/workspace-header.tsx +72 -3
  433. package/dist/browser/dialog-header.js +0 -5
  434. package/dist/browser/menu-action-row.js +0 -3
  435. package/dist/browser/menu-header.js +0 -5
  436. package/dist/browser/menu-item.js +0 -3
  437. package/dist/browser/page-header.js +0 -3
  438. package/dist/chunk-2PES33HS.js +0 -13
  439. package/dist/chunk-2PES33HS.js.map +0 -1
  440. package/dist/chunk-55DATV3A.js +0 -10
  441. package/dist/chunk-55DATV3A.js.map +0 -1
  442. package/dist/chunk-7BR3DO2A.js.map +0 -1
  443. package/dist/chunk-7LDCTU7D.js +0 -134
  444. package/dist/chunk-7LDCTU7D.js.map +0 -1
  445. package/dist/chunk-AQNKJRFH.js +0 -79
  446. package/dist/chunk-AQNKJRFH.js.map +0 -1
  447. package/dist/chunk-CTQKI2XL.js +0 -28
  448. package/dist/chunk-CTQKI2XL.js.map +0 -1
  449. package/dist/chunk-DAFFDO33.js +0 -14
  450. package/dist/chunk-DAFFDO33.js.map +0 -1
  451. package/dist/chunk-EGHL5S4P.js +0 -33
  452. package/dist/chunk-EGHL5S4P.js.map +0 -1
  453. package/dist/chunk-GY5WH7TO.js +0 -10
  454. package/dist/chunk-GY5WH7TO.js.map +0 -1
  455. package/dist/chunk-H5AGGVU5.js +0 -75
  456. package/dist/chunk-H5AGGVU5.js.map +0 -1
  457. package/dist/chunk-K3G72I6D.js +0 -24
  458. package/dist/chunk-K3G72I6D.js.map +0 -1
  459. package/dist/chunk-M5JRI6MN.js +0 -27
  460. package/dist/chunk-M5JRI6MN.js.map +0 -1
  461. package/dist/chunk-MBR3TYNL.js +0 -14
  462. package/dist/chunk-MBR3TYNL.js.map +0 -1
  463. package/dist/chunk-MFKMHK5W.js +0 -10
  464. package/dist/chunk-MFKMHK5W.js.map +0 -1
  465. package/dist/chunk-MQIZAD4A.js.map +0 -1
  466. package/dist/chunk-NPPD2A6O.js +0 -40
  467. package/dist/chunk-NPPD2A6O.js.map +0 -1
  468. package/dist/chunk-R6JHHBKQ.js +0 -19
  469. package/dist/chunk-R6JHHBKQ.js.map +0 -1
  470. package/dist/chunk-S3TPM2LP.js +0 -23
  471. package/dist/chunk-S3TPM2LP.js.map +0 -1
  472. package/dist/chunk-TRNBHPNW.js.map +0 -1
  473. package/dist/chunk-VHW5YNIB.js +0 -17
  474. package/dist/chunk-VHW5YNIB.js.map +0 -1
  475. package/dist/chunk-WVPYVDXY.js +0 -15
  476. package/dist/chunk-WVPYVDXY.js.map +0 -1
  477. package/dist/chunk-XZXRLY5Q.js +0 -33
  478. package/dist/chunk-XZXRLY5Q.js.map +0 -1
  479. package/dist/dialog-header.d.ts +0 -15
  480. package/dist/dialog-header.js +0 -5
  481. package/dist/dialog-header.js.map +0 -1
  482. package/dist/menu-action-row.js +0 -4
  483. package/dist/menu-action-row.js.map +0 -1
  484. package/dist/menu-header.d.ts +0 -41
  485. package/dist/menu-header.js +0 -6
  486. package/dist/menu-header.js.map +0 -1
  487. package/dist/menu-item.d.ts +0 -31
  488. package/dist/menu-item.js +0 -4
  489. package/dist/menu-item.js.map +0 -1
  490. package/dist/page-header.d.ts +0 -9
  491. package/dist/page-header.js +0 -3
  492. package/dist/page-header.js.map +0 -1
  493. package/dist/styles/dialog-header.css +0 -87
  494. package/dist/styles/menu-action-row.css +0 -160
  495. package/dist/styles/menu-header.css +0 -143
  496. package/dist/styles/menu-item.css +0 -123
  497. package/dist/styles/page-header.css +0 -33
  498. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
@@ -1,6 +1,86 @@
1
1
  # Public API signatures for the UI authoring corpus
2
2
 
3
- Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.4` and `kerfjs@5.0.0-beta.4`. 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.41` and `kerfjs@5.0.0-beta.41`. 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
+ /**
14
+ * A complete typed CSS value minted by a property-specific Kerf UI builder.
15
+ *
16
+ * This brand is an authoring correctness aid, not a sanitizer or security
17
+ * boundary. Prefer the narrower property grammar, such as {@link CssLength},
18
+ * whenever one is available.
19
+ */
20
+ type CssValue = string & {
21
+ readonly [cssValueBrand]: 'CssValue';
22
+ };
23
+ /**
24
+ * A complete CSS length-percentage value suitable for dimension-valued UI
25
+ * props. Despite the concise name, percentages are intentionally included.
26
+ */
27
+ type CssLength = CssValue & {
28
+ readonly [cssLengthBrand]: 'CssLength';
29
+ };
30
+ /**
31
+ * An incomplete arithmetic expression. Wrap it with {@link calc} before
32
+ * passing it to a prop that accepts {@link CssLength}.
33
+ */
34
+ type CssLengthExpression = string & {
35
+ readonly [cssLengthExpressionBrand]: 'CssLengthExpression';
36
+ };
37
+ /** A complete CSS `flex` shorthand. It is not interchangeable with a length. */
38
+ type CssFlex = CssValue & {
39
+ readonly [cssFlexBrand]: 'CssFlex';
40
+ };
41
+ /** A complete CSS color value. It is not interchangeable with a length. */
42
+ type CssColor = CssValue & {
43
+ readonly [cssColorBrand]: 'CssColor';
44
+ };
45
+ type CssFlexKeyword = 'none' | 'auto' | 'initial';
46
+ type CssFlexBasis = CssLength | 'auto' | 'content' | 'min-content' | 'max-content' | 'fit-content';
47
+ type CssSizeKeyword = 'auto' | 'min-content' | 'max-content' | 'fit-content';
48
+ /** A complete width/height value accepted by dimension-valued UI props. */
49
+ type CssSize = CssLength | CssSizeKeyword;
50
+ declare const uiColorNames: readonly ["accent", "accent-text", "border", "border-quiet", "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", "danger-border-loud", "danger-border-normal", "danger-border-quiet", "danger-fill-loud", "danger-fill-normal", "danger-fill-quiet", "danger-on-loud", "danger-on-normal", "danger-on-quiet", "danger-text", "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", "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", "pop-text", "success", "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", "success-text", "surface", "surface-lowered", "surface-raised", "text", "text-link", "text-quiet", "warning", "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", "warning-text"];
51
+ /** Names of the public `--kui-color-*` semantic tokens. */
52
+ type UiColorName = (typeof uiColorNames)[number];
53
+ /** Kerf UI's complete spacing-token vocabulary. `s` and `xl` are exceptions. */
54
+ type UiSpaceName = 'none' | '2xs' | 'xs' | 's' | 'm' | 'l' | 'xl';
55
+ /** Create a complete pixel length. */
56
+ declare function px(value: number): CssLength;
57
+ /** Create a complete root-font-relative length. */
58
+ declare function rem(value: number): CssLength;
59
+ /** Create a complete current-font-relative length. */
60
+ declare function em(value: number): CssLength;
61
+ /** Create a complete percentage length. */
62
+ declare function pct(value: number): CssLength;
63
+ /** Resolve a Kerf UI spacing step to its public custom property. */
64
+ declare function space(name: UiSpaceName): CssLength;
65
+ /**
66
+ * Reference an application-owned custom property whose contract is a CSS
67
+ * length-percentage. The deliberately narrow name grammar keeps this helper
68
+ * from becoming an arbitrary CSS-string constructor.
69
+ */
70
+ declare function lengthVar(name: `--${string}`, fallback?: CssLength): CssLength;
71
+ /** Combine two or more complete lengths into a non-standalone sum. */
72
+ declare function plus(first: CssLength, second: CssLength, ...rest: readonly CssLength[]): CssLengthExpression;
73
+ /** Turn a typed length expression into a complete CSS `calc()` value. */
74
+ declare function calc(expression: CssLengthExpression): CssLength;
75
+ /** Build a complete, structured CSS flex shorthand. */
76
+ declare function flex(grow: number, shrink?: number, basis?: CssFlexBasis): CssFlex;
77
+ /** Resolve a public Kerf UI semantic color token. */
78
+ declare function uiColor(name: UiColorName): CssColor;
79
+ /** Reference an application-owned custom property whose contract is a color. */
80
+ declare function colorVar(name: `--${string}`, fallback?: CssColor): CssColor;
81
+
82
+ export { type CssColor, type CssFlex, type CssFlexBasis, type CssFlexKeyword, type CssLength, type CssLengthExpression, type CssSize, type CssSizeKeyword, type CssValue, type UiColorName, type UiSpaceName, calc, colorVar, em, flex, lengthVar, pct, plus, px, rem, space, uiColor };
83
+ ```
4
84
 
5
85
  ## `@kerfjs/ui/disclosure-arrow`
6
86
 
@@ -24,19 +104,22 @@ export { DisclosureArrow, type DisclosureArrowProps, type DisclosureDirection };
24
104
  ## `@kerfjs/ui/toolbar`
25
105
 
26
106
  ```ts
27
- import { SafeHtml } from 'kerfjs';
107
+ import * as kerfjs from 'kerfjs';
108
+ import { D as DividerSides } from './divider-sides-BzB6rphT.js';
109
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
28
110
 
29
111
  interface ToolbarProps {
30
- leading?: SafeHtml;
31
- center?: SafeHtml;
32
- trailing?: SafeHtml;
112
+ leading?: KerfUiContent;
113
+ center?: KerfUiContent;
114
+ trailing?: KerfUiContent;
33
115
  label?: string;
34
- divider?: boolean;
116
+ /** Physical divider edges in canonical top/right/bottom/left order. Defaults to bottom. */
117
+ dividerSides?: DividerSides;
35
118
  className?: string;
36
119
  }
37
- declare function Toolbar({ leading, center, trailing, label, divider, className }: ToolbarProps): SafeHtml;
120
+ declare function Toolbar({ leading, center, trailing, label, dividerSides, className, }: ToolbarProps): kerfjs.SafeHtml;
38
121
 
39
- export { Toolbar, type ToolbarProps };
122
+ export { DividerSides, Toolbar, type ToolbarProps };
40
123
  ```
41
124
 
42
125
  ## `@kerfjs/ui/toolbar-text`
@@ -44,99 +127,264 @@ export { Toolbar, type ToolbarProps };
44
127
  ```ts
45
128
  import * as kerfjs from 'kerfjs';
46
129
 
47
- type ToolbarTextSize = 'large' | 'default' | 'small';
48
- interface ToolbarTextProps {
130
+ type ToolbarTextSize = 'xlarge' | 'large' | 'default' | 'small';
131
+ /** ARIA heading level for a title exposed as a heading landmark. */
132
+ type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
133
+ interface ToolbarTextBaseProps {
49
134
  text: string;
50
135
  size?: ToolbarTextSize;
51
136
  className?: string;
137
+ /** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */
138
+ id?: string;
139
+ /**
140
+ * Expose heading semantics (`role="heading"` + `aria-level`) so the text acts as
141
+ * a heading landmark — e.g. a page's primary title. Omit to keep the plain span
142
+ * (the default), which suits a dialog title referenced via `aria-labelledby`.
143
+ */
144
+ headingLevel?: HeadingLevel;
145
+ /** Render the text as an unanimated loading skeleton instead of its value. */
146
+ placeholder?: boolean;
147
+ /**
148
+ * Show a trailing ellipsis (…) where the text is truncated — on the single line
149
+ * (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip
150
+ * instead. Default true.
151
+ */
152
+ ellipsis?: boolean;
52
153
  }
53
- declare function ToolbarText({ text, size, className }: ToolbarTextProps): kerfjs.SafeHtml;
154
+ type ToolbarTextWrappingProps = {
155
+ /** Wrap onto multiple lines; combine with `maxLines` to cap them. */
156
+ wrap: true;
157
+ maxLines?: number | null;
158
+ } | {
159
+ wrap?: false;
160
+ maxLines?: never;
161
+ };
162
+ type ToolbarTextProps = ToolbarTextBaseProps & ToolbarTextWrappingProps;
163
+ declare function ToolbarText({ text, size, className, id, headingLevel, placeholder, wrap, ellipsis, maxLines, }: ToolbarTextProps): kerfjs.SafeHtml;
54
164
 
55
- export { ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
165
+ export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
56
166
  ```
57
167
 
58
168
  ## `@kerfjs/ui/toolbar-control-group`
59
169
 
60
170
  ```ts
61
- import { SafeHtml } from 'kerfjs';
62
-
171
+ import * as kerfjs from 'kerfjs';
172
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
173
+
174
+ type ToolbarControlGroupAppearance = 'contained' | 'borderless';
175
+ type ToolbarControlGroupTone = 'default' | 'dark';
176
+ type ToolbarControlGroupButtonAppearance = 'plain' | 'push';
177
+ type ToolbarControlGroupShape = 'pill' | 'rounded';
178
+ type ToolbarControlGroupSize = 'default' | 'compact';
179
+ type ToolbarControlGroupDensity = 'comfortable' | 'tight';
180
+ type ToolbarControlGroupContent = 'icon' | 'text' | 'mixed' | 'avatar';
181
+ type ToolbarControlGroupSelectedChrome = 'raised' | 'filled' | 'outline';
182
+ type ToolbarControlGroupSelectedTone = 'brand' | 'neutral' | 'pop';
63
183
  interface ToolbarControlGroupProps {
64
- children: SafeHtml | SafeHtml[];
184
+ children: KerfUiContent;
65
185
  label?: string;
66
186
  className?: string;
67
187
  expanded?: boolean;
68
188
  single?: boolean;
69
- appearance?: 'contained' | 'borderless';
70
- tone?: 'default' | 'dark';
71
- buttonAppearance?: 'plain' | 'push';
189
+ appearance?: ToolbarControlGroupAppearance;
190
+ tone?: ToolbarControlGroupTone;
191
+ buttonAppearance?: ToolbarControlGroupButtonAppearance;
192
+ /** Corner shape: fully round `pill` (default) or a softer `rounded` rectangle. */
193
+ shape?: ToolbarControlGroupShape;
194
+ size?: ToolbarControlGroupSize;
195
+ density?: ToolbarControlGroupDensity;
196
+ content?: ToolbarControlGroupContent;
197
+ selectedChrome?: ToolbarControlGroupSelectedChrome;
198
+ selectedTone?: ToolbarControlGroupSelectedTone;
199
+ /** Size a nested Web Awesome dropdown trigger as part of this group. */
200
+ nestedDropdown?: boolean;
201
+ /** Add contrast behind photo-backed avatar content. */
202
+ scrim?: boolean;
203
+ /**
204
+ * Avatar image URL. A single-control group paints it on the group; a
205
+ * multi-control group paints it only on the pressed selection highlight.
206
+ */
207
+ avatarImage?: string;
208
+ }
209
+ declare function ToolbarControlGroup({ children, label, className, expanded, single, appearance, tone, buttonAppearance, shape, size, density, content, selectedChrome, selectedTone, nestedDropdown, scrim, avatarImage, }: ToolbarControlGroupProps): kerfjs.SafeHtml;
210
+
211
+ export { ToolbarControlGroup, type ToolbarControlGroupAppearance, type ToolbarControlGroupButtonAppearance, type ToolbarControlGroupContent, type ToolbarControlGroupDensity, type ToolbarControlGroupProps, type ToolbarControlGroupSelectedChrome, type ToolbarControlGroupSelectedTone, type ToolbarControlGroupShape, type ToolbarControlGroupSize, type ToolbarControlGroupTone };
212
+ ```
213
+
214
+ ## `@kerfjs/ui/floating-toolbar`
215
+
216
+ ```ts
217
+ import * as kerfjs from 'kerfjs';
218
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
219
+
220
+ /** Where a {@link FloatingToolbar} floats within its positioned container. */
221
+ type FloatingToolbarPosition = 'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end';
222
+ interface FloatingToolbarProps {
223
+ /** Toolbar contents — normally one or more `ToolbarControlGroup`s. */
224
+ children: KerfUiContent;
225
+ /** Accessible name for the toolbar (required — it exposes `role="toolbar"`). */
226
+ label: string;
227
+ /**
228
+ * Corner or edge it floats to inside its nearest positioned ancestor.
229
+ * Default: `'bottom-end'`.
230
+ */
231
+ position?: FloatingToolbarPosition;
232
+ className?: string;
72
233
  }
73
- declare function ToolbarControlGroup({ children, label, className, expanded, single, appearance, tone, buttonAppearance }: ToolbarControlGroupProps): SafeHtml;
234
+ /**
235
+ * A toolbar that floats above the main content of its nearest positioned
236
+ * ancestor — a transparent, forced-dark cluster of controls (e.g. a drawer
237
+ * restore button) that sits over the content but NOT over dialogs or overlays
238
+ * (it is not in the top layer). It is inset from the container edges by
239
+ * `--kui-floating-toolbar-inset` (default `--kui-space-m`, i.e. 8px more than a
240
+ * top toolbar's own inset); override that token to move it. The app owns the
241
+ * controls and their behavior — wire them with `delegate()` as usual.
242
+ */
243
+ declare function FloatingToolbar({ children, label, position, className, }: FloatingToolbarProps): kerfjs.SafeHtml;
74
244
 
75
- export { ToolbarControlGroup, type ToolbarControlGroupProps };
245
+ export { FloatingToolbar, type FloatingToolbarPosition, type FloatingToolbarProps };
76
246
  ```
77
247
 
78
- ## `@kerfjs/ui/menu-header`
248
+ ## `@kerfjs/ui/list-header`
79
249
 
80
250
  ```ts
81
251
  import { SafeHtml } from 'kerfjs';
82
252
 
83
- type MenuHeaderRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
253
+ type ListHeaderRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
84
254
  'data-component'?: never;
85
255
  'data-action'?: never;
86
256
  'data-has-badge'?: never;
87
257
  'data-has-count'?: never;
258
+ 'data-density'?: never;
259
+ 'data-divider'?: never;
260
+ 'data-inline'?: never;
261
+ 'data-indicator-tone'?: never;
88
262
  'data-toggle'?: never;
89
263
  }>;
90
- type MenuHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
264
+ type ListHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
91
265
  'data-action'?: never;
92
266
  popoverTarget?: string;
93
267
  popoverTargetAction?: 'toggle' | 'show' | 'hide';
94
268
  'aria-controls'?: string;
95
269
  'aria-haspopup'?: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | 'true';
96
270
  }>;
97
- interface MenuHeaderBaseProps {
271
+ interface ListHeaderBaseProps {
98
272
  label: string;
99
- action?: string;
100
- actionLabel?: string;
273
+ density?: 'standard' | 'compact';
274
+ divider?: 'none' | 'before' | 'after' | 'both';
275
+ /** Shrink-wrap the header without its default outer margin, border, or padding. */
276
+ inline?: boolean;
277
+ indicatorTone?: 'neutral' | 'accent' | 'pop' | 'danger';
278
+ /** Render as an unanimated loading skeleton: keep the label and action affordance, disable interaction. */
279
+ placeholder?: boolean;
280
+ rootAttributes?: ListHeaderRootAttributes;
281
+ triggerAttributes?: ListHeaderTriggerAttributes;
282
+ }
283
+ type ListHeaderModeProps = {
284
+ /** Render the title as a controlled disclosure trigger. */
285
+ toggle: true;
286
+ action: string;
287
+ expanded: boolean;
101
288
  actionIcon?: SafeHtml;
289
+ actionLabel?: never;
102
290
  actionDisabled?: boolean;
103
291
  disabledReason?: string;
104
- expanded?: boolean;
105
- toggle?: boolean;
106
- rootAttributes?: MenuHeaderRootAttributes;
107
- triggerAttributes?: MenuHeaderTriggerAttributes;
108
- }
109
- type MenuHeaderIndicatorProps = {
292
+ } | {
293
+ /** Render a separately named trailing action. */
294
+ toggle?: false;
295
+ action: string;
296
+ actionLabel: string;
297
+ actionIcon: SafeHtml;
298
+ expanded?: never;
299
+ actionDisabled?: boolean;
300
+ disabledReason?: string;
301
+ } | {
302
+ /** Render a passive section heading. */
303
+ toggle?: false;
304
+ action?: never;
305
+ actionLabel?: never;
306
+ actionIcon?: never;
307
+ expanded?: never;
308
+ actionDisabled?: never;
309
+ disabledReason?: never;
310
+ };
311
+ type ListHeaderIndicatorProps = {
110
312
  count: number;
111
313
  countLabel: string;
112
314
  badge?: never;
315
+ status?: never;
316
+ } | {
317
+ count?: never;
318
+ countLabel?: never;
319
+ badge: SafeHtml;
320
+ status?: never;
113
321
  } | {
114
322
  count?: never;
115
323
  countLabel?: never;
116
- badge?: SafeHtml;
324
+ badge?: never;
325
+ status?: SafeHtml;
117
326
  };
118
- type MenuHeaderProps = MenuHeaderBaseProps & MenuHeaderIndicatorProps;
119
- declare function MenuHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, rootAttributes, triggerAttributes }: MenuHeaderProps): SafeHtml;
327
+ type ListHeaderProps = ListHeaderBaseProps & ListHeaderIndicatorProps & ListHeaderModeProps;
328
+ declare function ListHeader({ label, count, countLabel, badge, status, density, divider, inline, indicatorTone, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes, }: ListHeaderProps): SafeHtml;
120
329
 
121
- export { MenuHeader, type MenuHeaderProps };
330
+ export { ListHeader, type ListHeaderProps };
122
331
  ```
123
332
 
124
- ## `@kerfjs/ui/menu-action-row`
333
+ ## `@kerfjs/ui/list`
334
+
335
+ ```ts
336
+ import * as kerfjs from 'kerfjs';
337
+ import { UiSpaceName, CssLength, CssFlexKeyword, CssFlex } from './css-values.js';
338
+ import { D as DividerSides, S as Sides } from './divider-sides-BzB6rphT.js';
339
+ import { H as HorizontalAlignment, V as VerticalAlignment } from './flex-alignment-CF8NLj7i.js';
340
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
341
+
342
+ interface ListProps {
343
+ children?: KerfUiContent;
344
+ /** Use the standard item gap, a named UI spacing token, or a typed CSS length. Defaults to no gap. */
345
+ gap?: boolean | UiSpaceName | CssLength;
346
+ /** Allow this list to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
347
+ flex?: boolean | CssFlexKeyword | CssFlex;
348
+ /** Horizontal alignment. Defaults to full to preserve stretch-aligned list children. */
349
+ hAlign?: HorizontalAlignment;
350
+ /** Vertical distribution. Defaults to top. */
351
+ vAlign?: VerticalAlignment;
352
+ /** Own vertical scrolling and overscroll containment. */
353
+ scrollable?: boolean;
354
+ /** Physical divider edges in canonical top/right/bottom/left order. */
355
+ dividerSides?: DividerSides;
356
+ /** Physical sides that receive the standard 17px text inset. */
357
+ textInsets?: Sides;
358
+ /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */
359
+ controlInsets?: Sides;
360
+ className?: string;
361
+ }
362
+ /** A stretch-aligned vertical stack with optional gap, flex, scroll, and dividers. */
363
+ declare function List({ children, gap, flex, hAlign, vAlign, scrollable, dividerSides, textInsets, controlInsets, className, }: ListProps): kerfjs.SafeHtml;
364
+
365
+ export { CssFlex, CssFlexKeyword, CssLength, DividerSides, HorizontalAlignment, List, type ListProps, Sides, UiSpaceName, VerticalAlignment };
366
+ ```
367
+
368
+ ## `@kerfjs/ui/list-action-row`
125
369
 
126
370
  ```ts
127
371
  import { SafeHtml } from 'kerfjs';
128
372
 
129
- type MenuActionRowRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
373
+ type ListActionRowRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
130
374
  'data-component'?: never;
131
375
  'data-action'?: never;
132
376
  'data-item-id'?: never;
133
377
  'data-has-icon'?: never;
134
378
  'data-multiline'?: never;
379
+ 'data-density'?: never;
380
+ 'data-divider'?: never;
381
+ 'data-busy'?: never;
382
+ 'data-trailing-visibility'?: never;
135
383
  'data-state'?: never;
136
384
  'data-selected'?: never;
137
385
  'data-pressed'?: never;
138
386
  }>;
139
- type MenuActionRowTrailingAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
387
+ type ListActionRowTrailingAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
140
388
  'data-component'?: never;
141
389
  'data-action'?: never;
142
390
  'data-item-id'?: never;
@@ -145,9 +393,14 @@ type MenuActionRowTrailingAttributes = Readonly<Record<`data-${string}`, string
145
393
  'aria-controls'?: string;
146
394
  'aria-haspopup'?: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | 'true';
147
395
  }>;
148
- interface MenuActionRowProps {
396
+ interface ListActionRowProps {
149
397
  /** Visible dormant content for the primary button. Must not contain interactive descendants. */
150
398
  label: string | SafeHtml;
399
+ description?: string | SafeHtml;
400
+ status?: string | SafeHtml;
401
+ busy?: boolean;
402
+ density?: 'standard' | 'compact';
403
+ divider?: 'none' | 'before' | 'after' | 'both';
151
404
  /** Decorative dormant content for the primary button. Must not contain interactive descendants. */
152
405
  icon?: SafeHtml;
153
406
  action: string;
@@ -160,44 +413,58 @@ interface MenuActionRowProps {
160
413
  state?: string;
161
414
  disabled?: boolean;
162
415
  tabIndex?: number;
416
+ /** Render as an unanimated loading skeleton, disabling both actions. */
417
+ placeholder?: boolean;
163
418
  trailingAction: string;
164
419
  trailingActionLabel: string;
165
420
  /** Decorative dormant content for the trailing button. Must not contain interactive descendants. */
166
421
  trailingActionIcon: SafeHtml;
167
422
  trailingActionDisabled?: boolean;
168
423
  trailingActionTitle?: string;
424
+ trailingActionVisibility?: 'always' | 'interaction';
169
425
  className?: string;
170
- style?: string;
171
- rootAttributes?: MenuActionRowRootAttributes;
172
- trailingActionAttributes?: MenuActionRowTrailingAttributes;
426
+ rootAttributes?: ListActionRowRootAttributes;
427
+ trailingActionAttributes?: ListActionRowTrailingAttributes;
173
428
  }
174
- 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;
429
+ 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, }: ListActionRowProps): SafeHtml;
175
430
 
176
- export { MenuActionRow, type MenuActionRowProps };
431
+ export { ListActionRow, type ListActionRowProps };
177
432
  ```
178
433
 
179
- ## `@kerfjs/ui/menu-item`
434
+ ## `@kerfjs/ui/list-item`
180
435
 
181
436
  ```ts
182
437
  import { SafeHtml } from 'kerfjs';
438
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
183
439
 
184
- type MenuItemRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
440
+ type ListItemRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
185
441
  'data-component'?: never;
186
442
  'data-action'?: never;
187
443
  'data-item-id'?: never;
188
444
  'data-has-icon'?: never;
445
+ 'data-has-description'?: never;
189
446
  'data-multiline'?: never;
447
+ 'data-density'?: never;
448
+ 'data-divider'?: never;
449
+ 'data-busy'?: never;
190
450
  'data-state'?: never;
191
451
  }>;
192
- interface MenuItemProps {
452
+ interface ListItemProps {
193
453
  label: string | SafeHtml;
454
+ /** App-owned supporting text rendered in the component's stable label stack. */
455
+ description?: string | SafeHtml;
194
456
  icon?: SafeHtml;
195
- trailing?: SafeHtml;
457
+ trailing?: KerfUiContent;
458
+ /** Dormant status metadata rendered before trailing content. */
459
+ status?: string | SafeHtml;
460
+ /** Show a progress indicator and expose the row as busy without replacing its content. */
461
+ busy?: boolean;
462
+ density?: 'standard' | 'compact';
463
+ divider?: 'none' | 'before' | 'after' | 'both';
196
464
  selected?: boolean;
197
465
  action: string;
198
466
  itemId?: string;
199
467
  className?: string;
200
- style?: string;
201
468
  pressed?: boolean;
202
469
  accessibleLabel?: string;
203
470
  title?: string;
@@ -205,66 +472,97 @@ interface MenuItemProps {
205
472
  state?: string;
206
473
  disabled?: boolean;
207
474
  tabIndex?: number;
208
- rootAttributes?: MenuItemRootAttributes;
475
+ /** Render the row as an unanimated loading skeleton, disabling its action. */
476
+ placeholder?: boolean;
477
+ rootAttributes?: ListItemRootAttributes;
209
478
  }
210
- declare function MenuItem({ label, icon, trailing, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, rootAttributes }: MenuItemProps): SafeHtml;
479
+ declare function ListItem({ label, description, icon, trailing, status, busy, density, divider, selected, action, itemId, className, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, rootAttributes, }: ListItemProps): SafeHtml;
211
480
 
212
- export { MenuItem, type MenuItemProps };
481
+ export { ListItem, type ListItemProps };
213
482
  ```
214
483
 
215
- ## `@kerfjs/ui/page-header`
484
+ ## `@kerfjs/ui/list-inset-control`
216
485
 
217
486
  ```ts
218
- import { SafeHtml } from 'kerfjs';
219
-
220
- interface PageHeaderProps {
221
- title: string | SafeHtml;
222
- action?: SafeHtml;
487
+ import * as kerfjs from 'kerfjs';
488
+ import { S as Sides } from './divider-sides-BzB6rphT.js';
489
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
490
+
491
+ interface ListInsetControlProps {
492
+ /** Control(s) that own their own border and padding (e.g. an input, a `wa-*`). */
493
+ children: KerfUiContent;
494
+ /** Physical inset sides in canonical top/right/bottom/left order. Defaults to all sides. */
495
+ sides?: Sides;
496
+ className?: string;
223
497
  }
224
- declare function PageHeader({ title, action }: PageHeaderProps): SafeHtml;
498
+ /**
499
+ * Insets a control into a pane/list content region: an 8px inline margin (so its
500
+ * edges line up with `.kui-content` items) and a stretch flex row with an 8px gap.
501
+ * Use it for controls that carry their own border and padding but no outer margin
502
+ * — the wrapper adds only the alignment margin and layout, not a second inset.
503
+ */
504
+ declare function ListInsetControl({ children, sides, className, }: ListInsetControlProps): kerfjs.SafeHtml;
225
505
 
226
- export { PageHeader, type PageHeaderProps };
506
+ export { ListInsetControl, type ListInsetControlProps, Sides };
227
507
  ```
228
508
 
229
- ## `@kerfjs/ui/dialog-header`
509
+ ## `@kerfjs/ui/list-inset-text`
230
510
 
231
511
  ```ts
232
- import { SafeHtml } from 'kerfjs';
233
-
234
- interface DialogHeaderProps {
235
- title: string;
236
- titleId: string;
237
- summary?: string;
238
- summaryId?: string;
239
- icon?: SafeHtml;
240
- iconClassName?: string;
241
- actions?: SafeHtml;
242
- actionsLabel?: string;
512
+ import * as kerfjs from 'kerfjs';
513
+ import { S as Sides } from './divider-sides-BzB6rphT.js';
514
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
515
+
516
+ interface ListInsetTextProps {
517
+ /** Text (or inline content) that carries no margin, border, or padding of its own. */
518
+ children: KerfUiContent | string;
519
+ /** Physical inset sides in canonical top/right/bottom/left order. Defaults to all sides. */
520
+ sides?: Sides;
521
+ /**
522
+ * @deprecated Pass `sides="rl"` instead. Keep only the horizontal geometry (inline margin, left/right border, and
523
+ * left/right padding) and drop the vertical margin, border, and padding. Use it
524
+ * when the text edge must still align with bordered items but the line should not
525
+ * add its own vertical box space — tight text layout inside a content region.
526
+ */
527
+ horizontalOnly?: boolean;
528
+ className?: string;
243
529
  }
244
- declare function DialogHeader({ title, titleId, summary, summaryId, icon, iconClassName, actions, actionsLabel }: DialogHeaderProps): SafeHtml;
530
+ /**
531
+ * Gives bare text the content-item geometry — an 8px inline margin, a 1px
532
+ * transparent border, and 8px padding — so a plain string lines up with
533
+ * bordered `.kui-content` items (its text edge lands at the same 17px inset).
534
+ * Use it for text elements that have no margin, border, or padding of their own.
535
+ * Pass `horizontalOnly` to keep the horizontal inset but drop the vertical box
536
+ * space for tight text layout.
537
+ */
538
+ declare function ListInsetText({ children, sides, horizontalOnly, className, }: ListInsetTextProps): kerfjs.SafeHtml;
245
539
 
246
- export { DialogHeader, type DialogHeaderProps };
540
+ export { ListInsetText, type ListInsetTextProps, Sides };
247
541
  ```
248
542
 
249
543
  ## `@kerfjs/ui/value-table`
250
544
 
251
545
  ```ts
546
+ import * as kerfjs from 'kerfjs';
252
547
  import { SafeHtml } from 'kerfjs';
548
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
253
549
 
254
550
  interface ValueTableRowProps {
255
551
  label: string | SafeHtml;
256
552
  value: string | SafeHtml;
257
553
  icon?: SafeHtml;
258
554
  className?: string;
555
+ /** Render the value as an unanimated loading skeleton, keeping the field label. */
556
+ placeholder?: boolean;
259
557
  }
260
- declare function ValueTableRow({ label, value, icon, className }: ValueTableRowProps): SafeHtml;
558
+ declare function ValueTableRow({ label, value, icon, className, placeholder, }: ValueTableRowProps): SafeHtml;
261
559
 
262
560
  interface ValueTableProps {
263
561
  label: string;
264
562
  className?: string;
265
- children: SafeHtml | readonly SafeHtml[];
563
+ children: KerfUiContent;
266
564
  }
267
- declare function ValueTable({ label, className, children }: ValueTableProps): SafeHtml;
565
+ declare function ValueTable({ label, className, children, }: ValueTableProps): kerfjs.SafeHtml;
268
566
 
269
567
  export { ValueTable, type ValueTableProps, ValueTableRow, type ValueTableRowProps };
270
568
  ```
@@ -273,6 +571,7 @@ export { ValueTable, type ValueTableProps, ValueTableRow, type ValueTableRowProp
273
571
 
274
572
  ```ts
275
573
  import { SafeHtml } from 'kerfjs';
574
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
276
575
 
277
576
  type AppTabRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
278
577
  'data-component'?: never;
@@ -282,48 +581,80 @@ type AppTabRootAttributes = Readonly<Record<`data-${string}`, string | undefined
282
581
  'data-tab-dragging'?: never;
283
582
  'data-tab-drop-position'?: never;
284
583
  }>;
584
+ type AppTabPresentation = 'pill' | 'segmented' | 'icon-only';
585
+ type AppTabSize = 'default' | 'compact';
285
586
  interface AppTabProps {
286
587
  id: string;
287
588
  name: string;
288
589
  selected?: boolean;
289
590
  closable?: boolean;
290
591
  draggable?: boolean;
291
- leading?: SafeHtml;
292
- trailing?: SafeHtml;
592
+ leading?: KerfUiContent;
593
+ trailing?: KerfUiContent;
594
+ /** Visual treatment within a TabBar. Icon-only tabs retain `name` as their accessible name. */
595
+ presentation?: AppTabPresentation;
596
+ /** Compact tabs use the 32px application-rail height. */
597
+ size?: AppTabSize;
598
+ /** Maximum visible label width in CSS pixels before ellipsis. */
599
+ labelMaxWidth?: number;
293
600
  /** Decorative dormant content for the close button. Must not contain interactive descendants. */
294
601
  closeIcon?: SafeHtml;
295
602
  selectAction?: string;
296
603
  closeAction?: string;
297
604
  className?: string;
605
+ /** Render as an unanimated loading skeleton, disabling select/close and dragging. */
606
+ placeholder?: boolean;
298
607
  rootAttributes?: AppTabRootAttributes;
299
608
  }
300
- declare function AppTab({ id, name, selected, closable, draggable, leading, trailing, closeIcon, selectAction, closeAction, className, rootAttributes }: AppTabProps): SafeHtml;
609
+ declare function AppTab({ id, name, selected, closable, draggable, leading, trailing, presentation, size, labelMaxWidth, closeIcon, selectAction, closeAction, className, placeholder, rootAttributes, }: AppTabProps): SafeHtml;
301
610
 
302
- export { AppTab, type AppTabProps };
611
+ export { AppTab, type AppTabPresentation, type AppTabProps, type AppTabSize };
303
612
  ```
304
613
 
305
614
  ## `@kerfjs/ui/tab-bar`
306
615
 
307
616
  ```ts
308
- import { SafeHtml } from 'kerfjs';
617
+ import * as kerfjs from 'kerfjs';
618
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
309
619
 
620
+ type TabActivation = 'automatic' | 'manual';
621
+ type TabBarAllocation = 'intrinsic' | 'fill';
622
+ type TabBarPresentation = 'rail' | 'segmented' | 'inspector';
623
+ type TabBarTrailingPlacement = 'separate' | 'adjacent';
310
624
  interface TabBarProps {
311
625
  id: string;
312
626
  label: string;
313
- children: SafeHtml | readonly SafeHtml[];
314
- leading?: SafeHtml;
315
- trailing?: SafeHtml;
627
+ children: KerfUiContent;
628
+ leading?: KerfUiContent;
629
+ trailing?: KerfUiContent;
316
630
  className?: string;
631
+ /**
632
+ * Keyboard activation mode for this strip, emitted as `data-tab-activation` for
633
+ * `wireTabBars` to read (overrides its `activation` option). `'automatic'` (default)
634
+ * selects on arrow / Home / End; `'manual'` moves roving focus only and the user
635
+ * selects with Enter / Space / click — use it when selecting a tab is a heavy action.
636
+ */
637
+ activation?: TabActivation;
638
+ /** How available strip width is allocated across child AppTabs. */
639
+ allocation?: TabBarAllocation;
640
+ /** Named strip chrome for application rails, segmented tabs, or inspectors. */
641
+ presentation?: TabBarPresentation;
642
+ /** Keep a trailing action beside the final tab or at the far edge of the bar. */
643
+ trailingPlacement?: TabBarTrailingPlacement;
317
644
  }
318
645
  /** Render a controlled tab strip. The application owns selection, order, and persistence. */
319
- declare function TabBar({ id, label, children, leading, trailing, className }: TabBarProps): SafeHtml;
646
+ declare function TabBar({ id, label, children, leading, trailing, className, activation, allocation, presentation, trailingPlacement, }: TabBarProps): kerfjs.SafeHtml;
320
647
 
321
- export { TabBar, type TabBarProps };
648
+ export { type TabActivation, TabBar, type TabBarAllocation, type TabBarPresentation, type TabBarProps, type TabBarTrailingPlacement };
322
649
  ```
323
650
 
324
651
  ## `@kerfjs/ui/wire-tab-bars`
325
652
 
326
653
  ```ts
654
+ import { TabActivation } from './tab-bar.js';
655
+ import 'kerfjs';
656
+ import './semantic-content-BbzjvSu9.js';
657
+
327
658
  type TabReorderSource = 'pointer' | 'keyboard';
328
659
  type TabDropPosition = 'before' | 'after';
329
660
  interface TabReorder {
@@ -333,23 +664,450 @@ interface TabReorder {
333
664
  position: TabDropPosition;
334
665
  source: TabReorderSource;
335
666
  }
667
+
336
668
  interface WireTabBarsOptions {
337
669
  onReorder: (change: TabReorder) => void;
670
+ /**
671
+ * How arrow / Home / End keys activate tabs (default `'automatic'`):
672
+ * - `'automatic'` moves roving focus **and** selects the focused tab (clicks it).
673
+ * - `'manual'` moves roving focus only; the user selects with Enter / Space / click
674
+ * (the ARIA Tabs manual-activation pattern). Use this when activation is a heavy or
675
+ * side-effecting action (e.g. a tab that loads a project) so arrowing through the
676
+ * strip doesn't trigger it on every tab.
677
+ *
678
+ * A per-bar `data-tab-activation="manual" | "automatic"` attribute (see the `TabBar`
679
+ * `activation` prop) overrides this option for that strip.
680
+ */
681
+ activation?: TabActivation;
338
682
  }
339
683
  declare function reorderTabs<T>(items: readonly T[], getId: (item: T) => string, sourceId: string, targetId: string, position: TabDropPosition): T[];
340
684
  /** Wire reordering and keyboard navigation while leaving controlled state in the application. */
341
- declare function wireTabBars(root: HTMLElement | Document, { onReorder }: WireTabBarsOptions): () => void;
685
+ declare function wireTabBars(root: HTMLElement | Document, { onReorder, activation }: WireTabBarsOptions): () => void;
686
+
687
+ export { TabActivation, type TabDropPosition, type TabReorder, type TabReorderSource, type WireTabBarsOptions, reorderTabs, wireTabBars };
688
+ ```
689
+
690
+ ## `@kerfjs/ui/nav-stack`
691
+
692
+ ```ts
693
+ import * as kerfjs from 'kerfjs';
694
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
695
+
696
+ /**
697
+ * One entry in a {@link NavStack}. The app owns the stack as an array (usually a
698
+ * signal); `NavStack` renders it and `wireNavStack` animates the transitions.
699
+ */
700
+ interface NavStackView {
701
+ /** Stable identity for keyed reconcile and transition direction. */
702
+ key: string;
703
+ content: KerfUiContent;
704
+ /** Title shown in the top toolbar for this view. */
705
+ title?: string;
706
+ /** Trailing actions for this view's top toolbar. */
707
+ toolbar?: KerfUiContent;
708
+ /** Bottom toolbar for this view. Cross-fades with the top chrome on navigation. */
709
+ bottomToolbar?: KerfUiContent;
710
+ }
711
+ interface NavStackProps {
712
+ id: string;
713
+ /** Accessible name for the stack region. */
714
+ label: string;
715
+ /** The stack, root first; the last entry is the active top view. */
716
+ views: NavStackView[];
717
+ /** Accessible label for the back control (default "Back"). */
718
+ backLabel?: string;
719
+ /** Hide the top toolbar entirely (rare — a fully custom-chrome view). */
720
+ hideToolbar?: boolean;
721
+ /** Optional persistent bottom toolbar used when the active view does not provide one. */
722
+ bottomToolbar?: KerfUiContent;
723
+ className?: string;
724
+ }
725
+ /**
726
+ * A navigation stack (iOS-style push/pop). Renders every entry stacked, the last
727
+ * one active; `@kerfjs/ui/wire-nav-stack`'s `wireNavStack` slides the content and
728
+ * cross-fades the chrome across a change. A single-pane layout is a `NavStack`
729
+ * with one entry. See `docs/23-app-layouts.md` §3.1.
730
+ */
731
+ declare function NavStack({ id, label, views, backLabel, hideToolbar, bottomToolbar, className, }: NavStackProps): kerfjs.SafeHtml;
732
+
733
+ export { NavStack, type NavStackProps, type NavStackView };
734
+ ```
735
+
736
+ ## `@kerfjs/ui/wire-nav-stack`
342
737
 
343
- export { type TabDropPosition, type TabReorder, type TabReorderSource, type WireTabBarsOptions, reorderTabs, wireTabBars };
738
+ ```ts
739
+ interface WireNavStackOptions {
740
+ /** Invoked when the back control is activated. The app pops its own stack. */
741
+ onBack?: () => void;
742
+ /** Transition duration in ms (default 200). Set 0 to disable animation. */
743
+ duration?: number;
744
+ }
745
+ /**
746
+ * Animate a `NavStack`'s push/pop transitions and wire its back control. The app
747
+ * owns the stack (a signal of `NavStackView[]`) and re-renders `NavStack` when it
748
+ * changes; this helper slides the content and settles the chrome across each
749
+ * change, moves focus into every new top view, restores that view's last focused
750
+ * descendant on pop, and calls `onBack` when the back control is used. Mark a
751
+ * preferred initial target with `data-nav-focus`; otherwise the first focusable
752
+ * descendant (or the view itself) receives focus. Returns a disposer.
753
+ */
754
+ declare function wireNavStack(root: Element, options?: WireNavStackOptions): () => void;
755
+
756
+ export { type WireNavStackOptions, wireNavStack };
757
+ ```
758
+
759
+ ## `@kerfjs/ui/split-view`
760
+
761
+ ```ts
762
+ import * as kerfjs from 'kerfjs';
763
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
764
+
765
+ interface SplitViewResizable {
766
+ size: number;
767
+ min: number;
768
+ max: number;
769
+ }
770
+ interface SplitViewProps {
771
+ id: string;
772
+ label: string;
773
+ /** The list (primary) pane. */
774
+ list: KerfUiContent;
775
+ /** The detail (secondary) pane. */
776
+ detail: KerfUiContent;
777
+ /**
778
+ * Compact ("one pane at a time") classes — a handset or portrait tablet.
779
+ * Derive from `deviceClass().value.compact`. When true the split collapses to
780
+ * a `NavStack`: the list is the root and the detail is pushed over it.
781
+ */
782
+ compact?: boolean;
783
+ /** In compact mode, whether the detail is currently pushed over the list. */
784
+ detailActive?: boolean;
785
+ /** Title/label for the list (compact NavStack root + region label). */
786
+ listTitle?: string;
787
+ /** Title/label for the detail (compact NavStack pushed view + region label). */
788
+ detailTitle?: string;
789
+ /** Back label for the compact NavStack (default "Back"). */
790
+ backLabel?: string;
791
+ /** A resizable separator on roomy classes (min/max px). Omit for a fixed split. */
792
+ resizable?: SplitViewResizable;
793
+ className?: string;
794
+ }
795
+ /**
796
+ * A list-detail split. On roomy classes it shows both panes side
797
+ * by side with an optional resizable separator; on compact classes it collapses
798
+ * to a `NavStack` (list → detail). See `docs/23-app-layouts.md` §3.2. Compose the
799
+ * resizable wiring with `wireResizableRegions` and the compact back with
800
+ * `wireNavStack`.
801
+ */
802
+ declare function SplitView({ id, label, list, detail, compact, detailActive, listTitle, detailTitle, backLabel, resizable, className, }: SplitViewProps): kerfjs.SafeHtml;
803
+
804
+ export { SplitView, type SplitViewProps, type SplitViewResizable };
805
+ ```
806
+
807
+ ## `@kerfjs/ui/pane`
808
+
809
+ ```ts
810
+ import * as kerfjs from 'kerfjs';
811
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
812
+
813
+ /** Logical sides that can show a {@link Pane} separator. */
814
+ type PaneSeparatorSide = 'block-start' | 'block-end' | 'inline-start' | 'inline-end';
815
+ /** Semantic root elements supported by {@link Pane}. */
816
+ type PaneElement = 'article' | 'aside' | 'div' | 'main' | 'section';
817
+ /** Semantic elements supported by the scrolling content slot. */
818
+ type PaneContentElement = 'div' | 'main' | 'nav' | 'section';
819
+ type PaneRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
820
+ 'data-component'?: never;
821
+ 'data-separator-block-start'?: never;
822
+ 'data-separator-block-end'?: never;
823
+ 'data-separator-inline-start'?: never;
824
+ 'data-separator-inline-end'?: never;
825
+ }>;
826
+ interface PaneProps {
827
+ /** Optional fixed chrome above the scrolling content, arranged vertically. */
828
+ header?: KerfUiContent;
829
+ /** The pane's primary vertical, scrolling content stack. */
830
+ children?: KerfUiContent;
831
+ /** Optional fixed chrome below the scrolling content. */
832
+ footer?: KerfUiContent;
833
+ /** Root semantics. Defaults to `div`. */
834
+ element?: PaneElement;
835
+ /** Scrolling content semantics. Defaults to `div`. */
836
+ contentElement?: PaneContentElement;
837
+ /** Independent logical-edge separator lines. Defaults to none. */
838
+ separators?: readonly PaneSeparatorSide[];
839
+ id?: string;
840
+ /** Accessible name for a landmark root such as `aside` or `main`. */
841
+ label?: string;
842
+ /** Accessible name for a landmark scrolling slot such as `nav`. */
843
+ contentLabel?: string;
844
+ className?: string;
845
+ headerClassName?: string;
846
+ contentClassName?: string;
847
+ footerClassName?: string;
848
+ /** Safe `data-*` metadata; Pane-owned structural attributes remain protected. */
849
+ rootAttributes?: PaneRootAttributes;
850
+ }
851
+ /**
852
+ * An unpadded application column with optional fixed header/footer slots and one
853
+ * scrolling vertical content owner. Separator lines are independently opt-in on
854
+ * each logical edge, so the same component works as a sidebar, main area,
855
+ * inspector, or dialog column.
856
+ */
857
+ declare function Pane({ header, children, footer, element, contentElement, separators, id, label, contentLabel, className, headerClassName, contentClassName, footerClassName, rootAttributes, }: PaneProps): kerfjs.SafeHtml;
858
+
859
+ export { Pane, type PaneContentElement, type PaneElement, type PaneProps, type PaneSeparatorSide };
860
+ ```
861
+
862
+ ## `@kerfjs/ui/workbench`
863
+
864
+ ```ts
865
+ import { SafeHtml } from 'kerfjs';
866
+ import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
867
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
868
+
869
+ /** A collapsible Workbench panel — a side rail or the bottom drawer. */
870
+ interface WorkbenchPanel {
871
+ content: KerfUiContent;
872
+ /** Whether the panel is currently collapsed (the app owns this). */
873
+ collapsed?: boolean;
874
+ /** Rail width, or drawer height, in px. Overrides the CSS default. */
875
+ size?: number;
876
+ /** Accessible name for the panel region. */
877
+ label?: string;
878
+ separator?: ResizableRegionSeparator;
879
+ collapseMotion?: ResizableRegionCollapseMotion;
880
+ contentOverflow?: ResizableRegionContentOverflow;
881
+ presentation?: ResizableRegionPresentation;
882
+ /** Control shown in a safe-area-aware viewport corner while collapsed. */
883
+ restoreControl?: SafeHtml;
884
+ restorePosition?: ResizableRegionRestorePosition;
885
+ }
886
+ interface WorkbenchProps {
887
+ id: string;
888
+ label: string;
889
+ /** The central work area. */
890
+ main: KerfUiContent;
891
+ leftRail?: WorkbenchPanel;
892
+ rightRail?: WorkbenchPanel;
893
+ bottomDrawer?: WorkbenchPanel;
894
+ className?: string;
895
+ }
896
+ /**
897
+ * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and
898
+ * bottom drawer around a central work area (any absent). Collapsing snaps the
899
+ * panel's track to zero in one reflow while its fixed-size content slides out via
900
+ * a composited transform — the instant-width / sliding-content technique, so the
901
+ * work area relayouts once, not per frame. Bottom-drawer content stays anchored
902
+ * to the shell's stable bottom edge throughout that transition. The app owns
903
+ * each `collapsed` flag; the collapse is pure CSS (no wire). See
904
+ * `docs/23-app-layouts.md` §3.3.
905
+ */
906
+ declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className, }: WorkbenchProps): SafeHtml;
907
+
908
+ export { Workbench, type WorkbenchPanel, type WorkbenchProps };
909
+ ```
910
+
911
+ ## `@kerfjs/ui/collapsible-panel`
912
+
913
+ ```ts
914
+ import { SafeHtml } from 'kerfjs';
915
+ import { LucideIcon } from './lucide-icon.js';
916
+ import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
917
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
918
+ import 'lucide';
919
+
920
+ /** Which edge a {@link CollapsiblePanel} docks to. */
921
+ type CollapsiblePanelSide = 'left' | 'right' | 'bottom';
922
+ /**
923
+ * The standard collapse/expand icon for a panel `side` and `collapsed` state,
924
+ * so every app's sidebars and drawers use one recognizable convention:
925
+ * `PanelLeft*` for a left rail, `PanelRight*` for a right rail, `PanelBottom*`
926
+ * for a bottom drawer — the `Close` glyph while open, the `Open` glyph while
927
+ * collapsed. Exposed so an app can render its own toggle affordance.
928
+ */
929
+ declare function collapsiblePanelToggleIcon(side: CollapsiblePanelSide, collapsed: boolean): {
930
+ icon: Parameters<typeof LucideIcon>[0]['icon'];
931
+ name: string;
932
+ };
933
+ interface CollapsiblePanelToggleProps {
934
+ /** The panel this toggle controls. */
935
+ side: CollapsiblePanelSide;
936
+ /** The panel's current collapsed state (drives the icon direction). */
937
+ collapsed: boolean;
938
+ /** `data-action` the button carries so `wireSidebar` can delegate its click. */
939
+ action: string;
940
+ /** The panel id the button targets (`data-tab-panel`-style: `data-collapsible-panel`). */
941
+ panelId?: string;
942
+ /** Accessible label; defaults to "Collapse"/"Expand". */
943
+ label?: string;
944
+ className?: string;
945
+ }
946
+ /**
947
+ * A standard collapse/expand toggle button for a {@link CollapsiblePanel}: the
948
+ * recognizable per-side icon (see {@link collapsiblePanelToggleIcon}) plus the
949
+ * `data-action` / `aria-expanded` `wireSidebar` reads. Placement is the app's —
950
+ * put it in the panel's own header (to collapse) and somewhere always-visible
951
+ * (to expand while collapsed).
952
+ */
953
+ declare function CollapsiblePanelToggle({ side, collapsed, action, panelId, label, className, }: CollapsiblePanelToggleProps): SafeHtml;
954
+ interface CollapsiblePanelProps {
955
+ /** A stable id for the panel — `wireSidebar` targets it and toggles reference it. */
956
+ id: string;
957
+ /** Which edge the panel docks to: a left/right rail or a bottom drawer. */
958
+ side: CollapsiblePanelSide;
959
+ /** Whether the panel is currently collapsed (the app owns this signal). */
960
+ collapsed?: boolean;
961
+ /** Rail width or drawer height in px. Overrides the CSS default. */
962
+ size?: number;
963
+ /** Accessible label for the panel region. */
964
+ label?: string;
965
+ /** Panel content. */
966
+ children?: KerfUiContent;
967
+ separator?: ResizableRegionSeparator;
968
+ collapseMotion?: ResizableRegionCollapseMotion;
969
+ contentOverflow?: ResizableRegionContentOverflow;
970
+ presentation?: ResizableRegionPresentation;
971
+ /** Control shown in a safe-area-aware viewport corner while collapsed. */
972
+ restoreControl?: SafeHtml;
973
+ restorePosition?: ResizableRegionRestorePosition;
974
+ className?: string;
975
+ }
976
+ /**
977
+ * A standalone collapsible side rail or bottom drawer, outside the full
978
+ * {@link Workbench} shell. It owns only the presentation: a fixed-size content
979
+ * area that stays laid out while the panel's track snaps to zero and the content
980
+ * slides out via `transform` (one reflow, composited — the same technique
981
+ * `Workbench` and the catalog sidebar use). Bottom-drawer content stays anchored
982
+ * to the panel's fixed bottom edge, so the track cannot move its layout origin
983
+ * underneath the transform transition. The app owns the `collapsed` signal;
984
+ * pair it with `wireSidebar` for the toggle, focus, compact-overlay, keyboard,
985
+ * and persistence semantics, and with `CollapsiblePanelToggle` for the standard
986
+ * affordance. See `docs/24-collapsible-panel.md`.
987
+ */
988
+ declare function CollapsiblePanel({ id, side, collapsed, size, label, children, separator, collapseMotion, contentOverflow, presentation, restoreControl, restorePosition, className, }: CollapsiblePanelProps): SafeHtml;
989
+
990
+ export { CollapsiblePanel, type CollapsiblePanelProps, type CollapsiblePanelSide, CollapsiblePanelToggle, type CollapsiblePanelToggleProps, collapsiblePanelToggleIcon };
991
+ ```
992
+
993
+ ## `@kerfjs/ui/wire-sidebar`
994
+
995
+ ```ts
996
+ import { Signal, ReadonlySignal } from 'kerfjs';
997
+ import { DeviceClass } from './device-class.js';
998
+
999
+ /** Minimal `localStorage`-shaped store, so the persistence hook is testable. */
1000
+ interface SidebarStorage {
1001
+ getItem(key: string): string | null;
1002
+ setItem(key: string, value: string): void;
1003
+ }
1004
+ interface WireSidebarPanel {
1005
+ /** The panel id — matches `CollapsiblePanel`'s `id` and a toggle's `panelId`. */
1006
+ id: string;
1007
+ /** The app-owned collapsed signal. `wireSidebar` reads it (focus, overlay) and
1008
+ * writes it (toggle, Escape, backdrop, persistence). */
1009
+ collapsed: Signal<boolean>;
1010
+ /** `data-action` value the panel's toggle button(s) carry. */
1011
+ toggleAction: string;
1012
+ /** When set, the collapsed state is loaded from and saved to `storage` under
1013
+ * this key (a persistence hook), so the panel remembers its state. */
1014
+ storageKey?: string;
1015
+ }
1016
+ interface WireSidebarOptions {
1017
+ panels: readonly WireSidebarPanel[];
1018
+ /**
1019
+ * When provided, the sidebar adopts a compact **overlay** presentation while
1020
+ * `deviceClass.compact` is true: an open panel floats over the content with a
1021
+ * dismissable backdrop, Escape and backdrop-click collapse it, and focus is
1022
+ * trapped within the open panel (the ARIA dialog pattern). Without it the panel
1023
+ * is always inline.
1024
+ */
1025
+ deviceClass?: ReadonlySignal<DeviceClass>;
1026
+ /** Compact devices either overlay the panels (default) or hide them in favor
1027
+ * of an application-owned responsive replacement. */
1028
+ compactPresentation?: 'overlay' | 'hidden';
1029
+ /** Collapse the other panels when one opens in compact overlay mode. */
1030
+ exclusiveCompact?: boolean;
1031
+ /** Persistence store (default `globalThis.localStorage`, if present). */
1032
+ storage?: SidebarStorage;
1033
+ }
1034
+ /**
1035
+ * The reusable sidebar-semantics layer for {@link CollapsiblePanel}s: toggle
1036
+ * delegation with focus restore, focus-into on open, an optional compact overlay
1037
+ * (backdrop + Escape + focus trap) driven by {@link deviceClass}, and an optional
1038
+ * persistence hook. The app owns each `collapsed` signal and the layout; this wire
1039
+ * owns the interaction. Returns a disposer. See `docs/24-collapsible-panel.md`.
1040
+ */
1041
+ declare function wireSidebar(root: HTMLElement, { panels, deviceClass, compactPresentation, exclusiveCompact, storage, }: WireSidebarOptions): () => void;
1042
+
1043
+ export { type SidebarStorage, type WireSidebarOptions, type WireSidebarPanel, wireSidebar };
1044
+ ```
1045
+
1046
+ ## `@kerfjs/ui/tab-scaffold`
1047
+
1048
+ ```ts
1049
+ import { SafeHtml } from 'kerfjs';
1050
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1051
+
1052
+ interface TabScaffoldTab<Id extends string = string> {
1053
+ id: Id;
1054
+ label: string;
1055
+ /** Decorative icon shown above the label in the bottom bar. */
1056
+ icon?: SafeHtml;
1057
+ /** The tab's content — typically a `NavStack` so each tab keeps its own stack. */
1058
+ content: KerfUiContent;
1059
+ }
1060
+ interface TabScaffoldProps<Id extends string = string> {
1061
+ id: string;
1062
+ /** Accessible name for the tab bar. */
1063
+ label: string;
1064
+ tabs: readonly TabScaffoldTab<Id>[];
1065
+ /** The controlled active tab id (the app owns selection). */
1066
+ active: NoInfer<Id>;
1067
+ className?: string;
1068
+ }
1069
+ /**
1070
+ * A mobile-first, iOS-like bottom tab scaffold: a bottom tab bar that switches
1071
+ * between major sections, each tab keeping its own content (usually a `NavStack`)
1072
+ * mounted so its stack and scroll survive a switch. Controlled — the app owns
1073
+ * `active`; wire selection with `@kerfjs/ui/wire-tab-scaffold`'s `wireTabScaffold`.
1074
+ * On larger classes, promote the tabs to a `Workbench` rail or sidebar instead of
1075
+ * a bottom bar. See `docs/23-app-layouts.md` §3.4.
1076
+ */
1077
+ declare function TabScaffold<Id extends string>({ id, label, tabs, active, className, }: TabScaffoldProps<Id>): SafeHtml;
1078
+
1079
+ export { TabScaffold, type TabScaffoldProps, type TabScaffoldTab };
1080
+ ```
1081
+
1082
+ ## `@kerfjs/ui/wire-tab-scaffold`
1083
+
1084
+ ```ts
1085
+ interface WireTabScaffoldOptions {
1086
+ /** Invoked with the selected tab id when a bottom-bar tab is activated. */
1087
+ onSelect: (tabId: string) => void;
1088
+ }
1089
+ /**
1090
+ * Wire a `TabScaffold`'s bottom tab bar: clicking a tab calls `onSelect` with its
1091
+ * id (the app then updates its controlled `active`). Returns a disposer.
1092
+ */
1093
+ declare function wireTabScaffold(root: Element, options: WireTabScaffoldOptions): () => void;
1094
+
1095
+ export { type WireTabScaffoldOptions, wireTabScaffold };
344
1096
  ```
345
1097
 
346
1098
  ## `@kerfjs/ui/resizable-region`
347
1099
 
348
1100
  ```ts
349
1101
  import { SafeHtml } from 'kerfjs';
1102
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
350
1103
 
351
1104
  type ResizableRegionAxis = 'horizontal' | 'vertical';
352
1105
  type ResizableRegionEdge = 'start' | 'end';
1106
+ type ResizableRegionSeparator = 'auto' | 'hidden';
1107
+ type ResizableRegionCollapseMotion = 'none' | 'slide' | 'fade-slide';
1108
+ type ResizableRegionContentOverflow = 'clip' | 'auto' | 'visible';
1109
+ type ResizableRegionPresentation = 'inline' | 'overlay' | 'hidden';
1110
+ type ResizableRegionRestorePosition = 'bottom-start' | 'bottom-end';
353
1111
  interface ResizableRegionProps {
354
1112
  id: string;
355
1113
  label: string;
@@ -360,15 +1118,27 @@ interface ResizableRegionProps {
360
1118
  edge?: ResizableRegionEdge;
361
1119
  collapsed?: boolean;
362
1120
  transitioning?: boolean;
1121
+ /** Whether the separator line is painted. The resize hit target remains available. */
1122
+ separator?: ResizableRegionSeparator;
1123
+ /** Keep the track change instant while optionally sliding the fixed-size content. */
1124
+ collapseMotion?: ResizableRegionCollapseMotion;
1125
+ /** Overflow policy for content such as an open popup inside a bottom drawer. */
1126
+ contentOverflow?: ResizableRegionContentOverflow;
1127
+ /** Inline layout, an edge overlay, or a responsive replacement that removes the region. */
1128
+ presentation?: ResizableRegionPresentation;
1129
+ /** Always-available control rendered while collapsed, outside the clipped region. */
1130
+ restoreControl?: SafeHtml;
1131
+ /** Safe-area-aware viewport corner for `restoreControl`. */
1132
+ restorePosition?: ResizableRegionRestorePosition;
363
1133
  /** Decorative dormant content for the separator handle. Must not contain interactive descendants. */
364
1134
  handleIcon?: SafeHtml;
365
- children: SafeHtml | SafeHtml[];
1135
+ children: KerfUiContent;
366
1136
  }
367
1137
  declare const clampRegionSize: (size: number, min: number, max: number) => number;
368
1138
  declare const resizeRegionFromPointer: (startSize: number, delta: number, edge: ResizableRegionEdge) => number;
369
- declare function ResizableRegion({ id, label, size, min, max, axis, edge, collapsed, transitioning, handleIcon, children }: ResizableRegionProps): SafeHtml;
1139
+ declare function ResizableRegion({ id, label, size, min, max, axis, edge, collapsed, transitioning, separator, collapseMotion, contentOverflow, presentation, restoreControl, restorePosition, handleIcon, children, }: ResizableRegionProps): SafeHtml;
370
1140
 
371
- export { ResizableRegion, type ResizableRegionAxis, type ResizableRegionEdge, type ResizableRegionProps, clampRegionSize, resizeRegionFromPointer };
1141
+ export { ResizableRegion, type ResizableRegionAxis, type ResizableRegionCollapseMotion, type ResizableRegionContentOverflow, type ResizableRegionEdge, type ResizableRegionPresentation, type ResizableRegionProps, type ResizableRegionRestorePosition, type ResizableRegionSeparator, clampRegionSize, resizeRegionFromPointer };
372
1142
  ```
373
1143
 
374
1144
  ## `@kerfjs/ui/wire-resizable-regions`
@@ -386,78 +1156,472 @@ interface WireResizableRegionsOptions {
386
1156
  onCommit: (change: ResizeCommit) => void;
387
1157
  }
388
1158
  /** Wire pointer and separator-keyboard behavior for every ResizableRegion below root. */
389
- declare function wireResizableRegions(root: HTMLElement, { step, largeStep, onPreview, onCommit }: WireResizableRegionsOptions): () => void;
1159
+ declare function wireResizableRegions(root: HTMLElement, { step, largeStep, onPreview, onCommit, }: WireResizableRegionsOptions): () => void;
390
1160
 
391
1161
  export { type ResizeCommit, type WireResizableRegionsOptions, wireResizableRegions };
392
1162
  ```
393
1163
 
394
- ## `@kerfjs/ui/segmented-control`
1164
+ ## `@kerfjs/ui/device-class`
1165
+
1166
+ ```ts
1167
+ import { ReadonlySignal } from 'kerfjs';
1168
+
1169
+ /**
1170
+ * Reactive device-class detection for `@kerfjs/ui` (see `docs/23-app-layouts.md`
1171
+ * §2). `deviceClass()` returns a `ReadonlySignal<DeviceClass>` describing the
1172
+ * current viewport as a size bucket × orientation × viewport-segment count, so a
1173
+ * layout can pick its presentation reactively instead of hand-wiring `matchMedia`.
1174
+ *
1175
+ * One shared viewport source backs every reader; the pure `classifyViewport`
1176
+ * core is DOM-free and directly unit-tested.
1177
+ */
1178
+ type DeviceSize = 'xs-mobile' | 'mobile' | 'tablet' | 'desktop' | 'xl-desktop';
1179
+ type DeviceOrientation = 'portrait' | 'landscape';
1180
+ /** Minimum widths (px) at which each larger bucket begins. `xs-mobile` is 0. */
1181
+ interface DeviceBreakpoints {
1182
+ mobile: number;
1183
+ tablet: number;
1184
+ desktop: number;
1185
+ 'xl-desktop': number;
1186
+ }
1187
+ interface DeviceClass {
1188
+ size: DeviceSize;
1189
+ orientation: DeviceOrientation;
1190
+ /** Horizontal viewport segments (foldables / dual-screen); 1 on ordinary devices. */
1191
+ segments: number;
1192
+ /** Vertical viewport segments; 1 on ordinary devices. */
1193
+ verticalSegments: number;
1194
+ /** Small phones — `xs-mobile` or `mobile`. */
1195
+ handset: boolean;
1196
+ /** "One pane at a time" — a handset or a portrait tablet. */
1197
+ compact: boolean;
1198
+ /** True when the current size is `size` or larger, e.g. `atLeast('tablet')`. */
1199
+ atLeast(size: DeviceSize): boolean;
1200
+ }
1201
+ /** A raw viewport snapshot, before breakpoints are applied. */
1202
+ interface Viewport {
1203
+ width: number;
1204
+ height: number;
1205
+ segments: number;
1206
+ verticalSegments: number;
1207
+ }
1208
+ interface DeviceClassOptions {
1209
+ /** Override any of the default bucket thresholds. */
1210
+ breakpoints?: Partial<DeviceBreakpoints>;
1211
+ /** The viewport assumed when there is no DOM (SSR). Defaults to 1024×768, one segment. */
1212
+ ssr?: Partial<Viewport>;
1213
+ }
1214
+ declare const DEFAULT_BREAKPOINTS: DeviceBreakpoints;
1215
+ /**
1216
+ * Classify a raw viewport into a {@link DeviceClass}. Pure and DOM-free — the
1217
+ * single source of truth for the bucketing rules.
1218
+ */
1219
+ declare function classifyViewport(width: number, orientation: DeviceOrientation, segments?: number, verticalSegments?: number, breakpoints?: DeviceBreakpoints): DeviceClass;
1220
+ /**
1221
+ * A reactive signal of the current {@link DeviceClass}. Reading it inside an
1222
+ * `effect`/`computed` re-runs when the viewport crosses a breakpoint, rotates,
1223
+ * or changes its segment count. Without a DOM it resolves to `options.ssr`
1224
+ * (default 1024×768, landscape, one segment).
1225
+ */
1226
+ declare function deviceClass(options?: DeviceClassOptions): ReadonlySignal<DeviceClass>;
1227
+
1228
+ export { DEFAULT_BREAKPOINTS, type DeviceBreakpoints, type DeviceClass, type DeviceClassOptions, type DeviceOrientation, type DeviceSize, type Viewport, classifyViewport, deviceClass };
1229
+ ```
1230
+
1231
+ ## `@kerfjs/ui/catalog`
395
1232
 
396
1233
  ```ts
397
1234
  import { SafeHtml } from 'kerfjs';
1235
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1236
+
1237
+ /** A reference link shown in the detail footer for the active entry. */
1238
+ interface CatalogResource {
1239
+ label: string;
1240
+ href: string;
1241
+ /** Optional monospace detail (e.g. a file path) shown after the label. */
1242
+ detail?: string;
1243
+ }
1244
+ /** A related entry offered in the detail footer's "Related entries" popup menu. */
1245
+ interface CatalogRelated {
1246
+ id: string;
1247
+ name: string;
1248
+ /** Group heading in the menu, e.g. "Uses" / "Used by". */
1249
+ group: string;
1250
+ }
1251
+ interface CatalogEntry {
1252
+ id: string;
1253
+ name: string;
1254
+ description?: string;
1255
+ /** Short metadata tags shown at the trailing edge of the sidebar row. */
1256
+ tags?: readonly string[];
1257
+ resources?: readonly CatalogResource[];
1258
+ related?: readonly CatalogRelated[];
1259
+ }
1260
+ interface CatalogSection {
1261
+ category: string;
1262
+ entries: readonly CatalogEntry[];
1263
+ }
1264
+ /**
1265
+ * A secondary group of sections shown below the primary sidebar sections with a
1266
+ * quieter "ecosystem" treatment (e.g. third-party components). Optionally
1267
+ * collapsible — the app owns `expanded` and toggles it from `wireCatalog`'s
1268
+ * `onToggleSecondary`.
1269
+ */
1270
+ interface CatalogSecondaryGroup {
1271
+ label: string;
1272
+ sections: readonly CatalogSection[];
1273
+ /** When true, the group's label is a disclosure toggle controlling `expanded`. */
1274
+ collapsible?: boolean;
1275
+ /** Whether the group is expanded (controlled). Ignored unless `collapsible`. */
1276
+ expanded?: boolean;
1277
+ }
1278
+ interface CatalogBrand {
1279
+ title: string;
1280
+ subtitle?: string;
1281
+ /** Logo image URL (rendered decorative). Omit for a text-only brand. */
1282
+ logoUrl?: string;
1283
+ }
1284
+ interface CatalogProps {
1285
+ brand: CatalogBrand;
1286
+ sections: readonly CatalogSection[];
1287
+ /** The controlled active entry id — the app owns this signal. */
1288
+ active: string;
1289
+ /** The rendered preview for the active entry; the app computes it from `active`. */
1290
+ content: KerfUiContent;
1291
+ /** Whether the sidebar is collapsed (controlled). */
1292
+ collapsed?: boolean;
1293
+ /** Current theme; when set, a theme toggle is shown that switches to the opposite. Omit to hide it. */
1294
+ theme?: 'light' | 'dark';
1295
+ /** Extra header controls placed before the theme toggle (each a `ToolbarControlGroup`). */
1296
+ headerActions?: KerfUiContent;
1297
+ /** A secondary "ecosystem" group of sections below the primary category groups. */
1298
+ secondarySections?: CatalogSecondaryGroup;
1299
+ /** Extra sidebar content below the category groups (and the secondary group). */
1300
+ sidebarFooter?: KerfUiContent;
1301
+ /** Status line content shown at the start of the detail footer. */
1302
+ status?: KerfUiContent;
1303
+ /**
1304
+ * Whether to highlight specimens' computed borders (or transparent outer
1305
+ * bounds) and non-zero margins. Pass a boolean (rather than omitting the
1306
+ * prop) when the active entry can switch between component and composition
1307
+ * previews; `wireCatalogGeometryOverlay` keeps the overlay synchronized.
1308
+ */
1309
+ geometryOverlay?: boolean;
1310
+ selectAction?: string;
1311
+ toggleSidebarAction?: string;
1312
+ toggleThemeAction?: string;
1313
+ /** Action fired by the secondary group's disclosure toggle (when collapsible). */
1314
+ toggleSecondaryAction?: string;
1315
+ className?: string;
1316
+ }
1317
+ /**
1318
+ * A reusable component-catalog shell: a collapsible category sidebar, a titled
1319
+ * detail stage that renders the active entry's preview, and a footer with
1320
+ * reference links and a related-entry popup menu. Built entirely from public
1321
+ * `@kerfjs/ui` primitives. Controlled and stateless — the app owns the `active`,
1322
+ * `collapsed`, and `theme` signals and computes `content` from `active` in its own
1323
+ * render; wire the sidebar/collapse/theme actions with `wireCatalog`.
1324
+ */
1325
+ declare function Catalog({ brand, sections, active, content, collapsed, theme, headerActions, secondarySections, sidebarFooter, status, geometryOverlay, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, className, }: CatalogProps): SafeHtml;
1326
+ /**
1327
+ * How a {@link CatalogExample}'s content aligns its visible left edge with the
1328
+ * example's `ListHeader` label (which sits 16px in — 8px title + 8px label):
1329
+ * - `'glyph'` — a bare glyph/text specimen with no inline geometry insets the full 16px.
1330
+ * - `'inline-control'` — a control that already carries ~8px of its own inline padding insets 8px so its content lands on the same line.
1331
+ * - `'none'` — a content-item / composition that owns its geometry and already aligns; no inset (default).
1332
+ */
1333
+ type CatalogExampleAlign = 'glyph' | 'inline-control' | 'none';
1334
+ type CatalogExampleRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
1335
+ 'data-catalog-example'?: never;
1336
+ 'data-catalog-example-stack'?: never;
1337
+ 'data-catalog-example-label'?: never;
1338
+ 'data-catalog-example-note'?: never;
1339
+ 'data-align'?: never;
1340
+ }>;
1341
+ type CatalogExampleStackRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
1342
+ 'data-catalog-example'?: never;
1343
+ 'data-catalog-example-stack'?: never;
1344
+ 'data-catalog-example-label'?: never;
1345
+ 'data-catalog-example-note'?: never;
1346
+ 'data-align'?: never;
1347
+ }>;
1348
+ interface CatalogExampleProps {
1349
+ /** The example's label, shown as a `ListHeader` above the specimen. Omit for a bare specimen. */
1350
+ label?: string;
1351
+ /** Optional explanatory note between the label and the specimen. */
1352
+ note?: SafeHtml | string;
1353
+ /** Alignment inset for the specimen — see {@link CatalogExampleAlign}. Default `'none'`. */
1354
+ align?: CatalogExampleAlign;
1355
+ /** Safe authoring `data-*` metadata for the rendered section. Helper-owned structural attributes remain protected. */
1356
+ rootAttributes?: CatalogExampleRootAttributes;
1357
+ className?: string;
1358
+ children?: KerfUiContent;
1359
+ }
1360
+ /**
1361
+ * One labeled example in a catalog preview: a `ListHeader` label, an optional
1362
+ * note, and the specimen. `align` insets the specimen so its visible left edge
1363
+ * lines up with the label text, encoding the catalog's alignment rules as a
1364
+ * first-class prop instead of per-demo CSS. The inset is published as the
1365
+ * `--kui-catalog-example-align` custom property so a debug overlay can exclude it
1366
+ * from a specimen's measured margin.
1367
+ */
1368
+ declare function CatalogExample({ label, note, align, rootAttributes, className, children, }: CatalogExampleProps): SafeHtml;
1369
+ interface CatalogExampleStackProps {
1370
+ /** Accessible label for the stack region. */
1371
+ label?: string;
1372
+ /** Safe authoring `data-*` metadata for the rendered stack. Helper-owned structural attributes remain protected. */
1373
+ rootAttributes?: CatalogExampleStackRootAttributes;
1374
+ className?: string;
1375
+ children?: KerfUiContent;
1376
+ }
1377
+ /**
1378
+ * A vertically-stacked group of {@link CatalogExample}s with the catalog's
1379
+ * example rhythm. The semantic `section` becomes a named `region` when `label`
1380
+ * is supplied; an unlabeled stack remains an ordinary grouping.
1381
+ */
1382
+ declare function CatalogExampleStack({ label, rootAttributes, className, children, }: CatalogExampleStackProps): SafeHtml;
1383
+
1384
+ export { Catalog, type CatalogBrand, type CatalogEntry, CatalogExample, type CatalogExampleAlign, type CatalogExampleProps, CatalogExampleStack, type CatalogExampleStackProps, type CatalogProps, type CatalogRelated, type CatalogResource, type CatalogSecondaryGroup, type CatalogSection };
1385
+ ```
1386
+
1387
+ ## `@kerfjs/ui/catalog-resources`
1388
+
1389
+ ```ts
1390
+ import { CatalogResource } from './catalog.js';
1391
+ import 'kerfjs';
1392
+ import './semantic-content-BbzjvSu9.js';
1393
+
1394
+ /**
1395
+ * Standard resource labels for a Kerf catalog detail footer. Keep these labels
1396
+ * stable across catalogs so people and AI-generated integrations see the same
1397
+ * choices in the same vocabulary.
1398
+ */
1399
+ type CatalogResourceKind = 'demoSource' | 'componentSource' | 'designTemplate' | 'guidance' | 'integrationGuidance';
1400
+ type CatalogGuidanceKind = 'guidance' | 'integrationGuidance';
1401
+ interface CatalogResourceTarget {
1402
+ href: string;
1403
+ /** Optional monospace detail, normally the repository-relative source path. */
1404
+ detail?: string;
1405
+ }
1406
+ interface CatalogResourcesInput {
1407
+ /** Required source for the runnable demonstration. */
1408
+ demoSource: CatalogResourceTarget;
1409
+ /** Source for the production component; omit for recipes and integrations. */
1410
+ componentSource?: CatalogResourceTarget;
1411
+ /** Optional design-tool template associated with the component. */
1412
+ designTemplate?: CatalogResourceTarget;
1413
+ /** Required UI or integration guidance. */
1414
+ guidance: CatalogResourceTarget;
1415
+ /** Use `integrationGuidance` when the component implementation is upstream. */
1416
+ guidanceKind?: CatalogGuidanceKind;
1417
+ }
1418
+ /**
1419
+ * Build the standard catalog resource group in its canonical order: demo,
1420
+ * component, optional design template, then guidance.
1421
+ */
1422
+ declare function catalogResources(input: CatalogResourcesInput): CatalogResource[];
1423
+
1424
+ export { type CatalogGuidanceKind, type CatalogResourceKind, type CatalogResourceTarget, type CatalogResourcesInput, catalogResources };
1425
+ ```
1426
+
1427
+ ## `@kerfjs/ui/wire-catalog`
1428
+
1429
+ ```ts
1430
+ interface WireCatalogOptions {
1431
+ /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */
1432
+ onSelect: (id: string) => void;
1433
+ /** Invoked when the sidebar collapse/expand control is activated. */
1434
+ onToggleSidebar?: () => void;
1435
+ /** Invoked when the theme toggle is activated. */
1436
+ onToggleTheme?: () => void;
1437
+ /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */
1438
+ onToggleSecondary?: () => void;
1439
+ /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */
1440
+ urlParam?: string;
1441
+ /**
1442
+ * Reveal the chosen sidebar row after selection. `true` uses desktop-safe
1443
+ * defaults; pass options to customize scroll alignment or the media guard.
1444
+ */
1445
+ revealSelection?: boolean | CatalogRevealOptions;
1446
+ selectAction?: string;
1447
+ toggleSidebarAction?: string;
1448
+ toggleThemeAction?: string;
1449
+ toggleSecondaryAction?: string;
1450
+ }
1451
+ interface CatalogRevealOptions {
1452
+ /** Scroll alignment within the sidebar. Default `'nearest'`. */
1453
+ block?: ScrollLogicalPosition;
1454
+ /** Cross-axis alignment. Default `'nearest'`. */
1455
+ inline?: ScrollLogicalPosition;
1456
+ /** Scroll behavior. Default `'auto'`. */
1457
+ behavior?: ScrollBehavior;
1458
+ /**
1459
+ * Only reveal when this media query matches. Defaults to the Catalog's
1460
+ * desktop layout; pass `false` to reveal at every viewport size.
1461
+ */
1462
+ media?: string | false;
1463
+ }
1464
+ /**
1465
+ * Reveal one Catalog sidebar entry after the controlled render settles without
1466
+ * moving focus. Returns a cancellation function for rapid selection changes.
1467
+ */
1468
+ declare function revealCatalogEntry(root: HTMLElement, id: string, { block, inline, behavior, media, }?: CatalogRevealOptions): () => void;
1469
+ /**
1470
+ * Keep a Catalog's opt-in geometry overlay synchronized with its preview.
1471
+ * Specimens receive computed border highlights (or a dashed bound when they
1472
+ * have no border and are transparent), while positive computed margins use
1473
+ * devtools-style orange bands. CSS/stylesheet-only changes are observed too.
1474
+ * Returns a disposer.
1475
+ */
1476
+ declare function wireCatalogGeometryOverlay(root: HTMLElement): () => void;
1477
+ /**
1478
+ * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar
1479
+ * item selection (and the related-entry popup menu), the sidebar collapse toggle, and
1480
+ * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates
1481
+ * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.
1482
+ * Returns a disposer.
1483
+ */
1484
+ declare function wireCatalog(root: HTMLElement, { onSelect, onToggleSidebar, onToggleTheme, onToggleSecondary, urlParam, revealSelection, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, }: WireCatalogOptions): () => void;
1485
+
1486
+ export { type CatalogRevealOptions, type WireCatalogOptions, revealCatalogEntry, wireCatalog, wireCatalogGeometryOverlay };
1487
+ ```
1488
+
1489
+ ## `@kerfjs/ui/segmented-control`
1490
+
1491
+ ```ts
1492
+ import * as kerfjs from 'kerfjs';
1493
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
398
1494
 
399
1495
  type SegmentedControlAppearance = 'filled' | 'outlined' | 'toolbar';
400
1496
  type SegmentedControlShape = 'rounded' | 'pill';
401
1497
  type SegmentedControlSize = 'small' | 'default';
402
1498
  type SegmentedControlLayout = 'content' | 'equal';
403
- interface SegmentedControlChoice {
404
- value: string;
1499
+ interface SegmentedControlChoice<Value extends string = string> {
1500
+ value: Value;
405
1501
  label: string;
406
- content?: SafeHtml;
1502
+ content?: KerfUiContent;
407
1503
  title?: string;
408
1504
  disabled?: boolean;
409
1505
  }
410
- interface SegmentedControlProps {
1506
+ interface SegmentedControlProps<Value extends string = string> {
411
1507
  id: string;
412
1508
  label: string;
413
- value: string;
414
- choices: readonly SegmentedControlChoice[];
1509
+ value: NoInfer<Value>;
1510
+ choices: readonly SegmentedControlChoice<Value>[];
415
1511
  action?: string;
416
1512
  appearance?: SegmentedControlAppearance;
417
1513
  shape?: SegmentedControlShape;
418
1514
  size?: SegmentedControlSize;
419
1515
  layout?: SegmentedControlLayout;
420
1516
  className?: string;
1517
+ /** Render as an unanimated loading skeleton, disabling every segment. */
1518
+ placeholder?: boolean;
421
1519
  }
422
- declare function SegmentedControl({ id, label, value, choices, action, appearance, shape, size, layout, className, }: SegmentedControlProps): SafeHtml;
1520
+ declare function SegmentedControl<Value extends string>({ id, label, value, choices, action, appearance, shape, size, layout, className, placeholder, }: SegmentedControlProps<Value>): kerfjs.SafeHtml;
423
1521
 
424
1522
  export { SegmentedControl, type SegmentedControlAppearance, type SegmentedControlChoice, type SegmentedControlLayout, type SegmentedControlProps, type SegmentedControlShape, type SegmentedControlSize };
425
1523
  ```
426
1524
 
1525
+ ## `@kerfjs/ui/select`
1526
+
1527
+ ```ts
1528
+ import { SafeHtml } from 'kerfjs';
1529
+ import { CssColor } from './css-values.js';
1530
+ import { LucideNode } from './lucide-icon.js';
1531
+ import 'lucide';
1532
+
1533
+ interface SelectChoice<Value extends string = string> {
1534
+ value: Value;
1535
+ label: string;
1536
+ icon?: LucideNode;
1537
+ iconName?: string;
1538
+ /** Typed semantic or application-owned color for the optional icon. */
1539
+ color?: CssColor;
1540
+ group?: string;
1541
+ separatorBefore?: boolean;
1542
+ }
1543
+ type SelectAccessibleName = {
1544
+ label: string;
1545
+ ariaLabel?: string;
1546
+ } | {
1547
+ label?: never;
1548
+ ariaLabel: string;
1549
+ };
1550
+ type SelectPresentation = 'form' | 'toolbar-borderless' | 'navigation';
1551
+ type SelectSize = 'default' | 'compact';
1552
+ type SelectSelectedPresentation = 'label' | 'icon-only';
1553
+ type SelectFocusRingOwner = 'select' | 'group';
1554
+ interface SelectBaseProps<Value extends string = string> {
1555
+ name: string;
1556
+ value: NoInfer<Value>;
1557
+ choices: readonly SelectChoice<Value>[];
1558
+ className?: string;
1559
+ /** Empty-value hint text shown in the closed control (the native select placeholder). */
1560
+ placeholderText?: string;
1561
+ /** Supporting text shown below the control and associated with its combobox. */
1562
+ hint?: string;
1563
+ disabled?: boolean;
1564
+ fitMenu?: boolean;
1565
+ renderSelected?: (choice: SelectChoice<Value>) => SafeHtml;
1566
+ /** Render as an unanimated loading skeleton: the label above a static, empty control box. */
1567
+ placeholder?: boolean;
1568
+ /** Form (default), borderless toolbar, or intrinsic navigation chrome. */
1569
+ presentation?: SelectPresentation;
1570
+ size?: SelectSize;
1571
+ /** Show only the selected choice icon while retaining the Select's accessible name. */
1572
+ selectedPresentation?: SelectSelectedPresentation;
1573
+ /** Let an enclosing ToolbarControlGroup paint the composed focus ring. */
1574
+ focusRingOwner?: SelectFocusRingOwner;
1575
+ /** Maximum closed-control label width in CSS pixels before ellipsis. */
1576
+ labelMaxWidth?: number;
1577
+ }
1578
+ type SelectProps<Value extends string = string> = SelectBaseProps<Value> & SelectAccessibleName;
1579
+ declare function Select<Value extends string>({ name, value, label, ariaLabel, choices, className, placeholderText, hint, disabled, fitMenu, renderSelected, placeholder, presentation, size, selectedPresentation, focusRingOwner, labelMaxWidth, }: SelectProps<Value>): SafeHtml;
1580
+
1581
+ export { Select, type SelectChoice, type SelectFocusRingOwner, type SelectPresentation, type SelectProps, type SelectSelectedPresentation, type SelectSize };
1582
+ ```
1583
+
427
1584
  ## `@kerfjs/ui/state-banner`
428
1585
 
429
1586
  ```ts
430
1587
  import { SafeHtml } from 'kerfjs';
1588
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
431
1589
 
432
- type StateBannerTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
1590
+ type StateBannerTone = 'neutral' | 'info' | 'pop' | 'success' | 'warning' | 'danger';
1591
+ type StateBannerUrgency = 'status' | 'alert';
433
1592
  interface StateBannerProps {
434
1593
  title: string;
435
1594
  detail?: string;
1595
+ /** Optional compact status or count shown beside the title. */
1596
+ badge?: string;
436
1597
  icon?: SafeHtml;
437
- action?: SafeHtml;
1598
+ action?: KerfUiContent;
438
1599
  tone?: StateBannerTone;
439
- urgency?: 'status' | 'alert';
1600
+ urgency?: StateBannerUrgency;
440
1601
  className?: string;
1602
+ /** Render the title, badge, and detail as unanimated loading skeletons, keeping the icon and tone. */
1603
+ placeholder?: boolean;
441
1604
  }
442
- declare function StateBanner({ title, detail, icon, action, tone, urgency, className }: StateBannerProps): SafeHtml;
1605
+ declare function StateBanner({ title, detail, badge, icon, action, tone, urgency, className, placeholder, }: StateBannerProps): SafeHtml;
443
1606
 
444
- export { StateBanner, type StateBannerProps, type StateBannerTone };
1607
+ export { StateBanner, type StateBannerProps, type StateBannerTone, type StateBannerUrgency };
445
1608
  ```
446
1609
 
447
1610
  ## `@kerfjs/ui/empty-state`
448
1611
 
449
1612
  ```ts
450
1613
  import { SafeHtml } from 'kerfjs';
1614
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
451
1615
 
452
1616
  interface EmptyStateProps {
453
1617
  title: string;
454
1618
  detail?: string;
455
1619
  icon?: SafeHtml;
456
- action?: SafeHtml;
1620
+ action?: KerfUiContent;
457
1621
  busy?: boolean;
458
1622
  className?: string;
459
1623
  }
460
- declare function EmptyState({ title, detail, icon, action, busy, className }: EmptyStateProps): SafeHtml;
1624
+ declare function EmptyState({ title, detail, icon, action, busy, className, }: EmptyStateProps): SafeHtml;
461
1625
 
462
1626
  export { EmptyState, type EmptyStateProps };
463
1627
  ```
@@ -477,10 +1641,66 @@ declare function LoadingSpinner({ className, label }: LoadingSpinnerProps): kerf
477
1641
  export { LoadingSpinner, type LoadingSpinnerProps };
478
1642
  ```
479
1643
 
1644
+ ## `@kerfjs/ui/skeleton`
1645
+
1646
+ ```ts
1647
+ import * as kerfjs from 'kerfjs';
1648
+ import { CssSize, CssLength } from './css-values.js';
1649
+
1650
+ interface SkeletonProps {
1651
+ /** Typed width or intrinsic sizing keyword. Defaults to filling its slot. */
1652
+ width?: CssSize;
1653
+ /** Typed height or intrinsic sizing keyword. Defaults to a single text line. */
1654
+ height?: CssSize;
1655
+ /** Typed corner-radius override. Defaults to the small radius token. */
1656
+ radius?: CssLength;
1657
+ /** Render this many stacked lines (the last one shorter), for multi-line text. */
1658
+ lines?: number;
1659
+ /** Accessible label. Omit to keep the block decorative (`aria-hidden`). */
1660
+ label?: string;
1661
+ className?: string;
1662
+ }
1663
+ /**
1664
+ * A subtle, deliberately **unanimated** loading placeholder block. Use it for a
1665
+ * value slot whose content is not yet known, on its own or via a component's
1666
+ * `placeholder` prop. Decorative by default (`aria-hidden`); pass `label` to
1667
+ * announce it. Sizes to its slot unless `width`/`height` are given.
1668
+ */
1669
+ declare function Skeleton({ width, height, radius, lines, label, className, }: SkeletonProps): kerfjs.SafeHtml;
1670
+
1671
+ export { Skeleton, type SkeletonProps };
1672
+ ```
1673
+
1674
+ ## `@kerfjs/ui/sunken-panel`
1675
+
1676
+ ```ts
1677
+ import * as kerfjs from 'kerfjs';
1678
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1679
+
1680
+ type SunkenPanelShape = 'rounded' | 'square';
1681
+ interface SunkenPanelProps {
1682
+ children?: KerfUiContent;
1683
+ /** Optional accessible landmark name for a distinct application region. */
1684
+ ariaLabel?: string;
1685
+ /** Corner shape: a rounded rectangle (default) or square corners. */
1686
+ shape?: SunkenPanelShape;
1687
+ className?: string;
1688
+ }
1689
+ /**
1690
+ * A lowered application surface with one compact inset and a vertical content
1691
+ * stack. The panel owns its background and padding; children own their own
1692
+ * borders and internal geometry.
1693
+ */
1694
+ declare function SunkenPanel({ children, ariaLabel, shape, className, }: SunkenPanelProps): kerfjs.SafeHtml;
1695
+
1696
+ export { SunkenPanel, type SunkenPanelProps, type SunkenPanelShape };
1697
+ ```
1698
+
480
1699
  ## `@kerfjs/ui/token-search-field`
481
1700
 
482
1701
  ```ts
483
- import { SafeHtml } from 'kerfjs';
1702
+ import * as kerfjs from 'kerfjs';
1703
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
484
1704
 
485
1705
  interface TokenSearchToken {
486
1706
  value: string;
@@ -488,7 +1708,15 @@ interface TokenSearchToken {
488
1708
  offset?: number;
489
1709
  accessibleLabel?: string;
490
1710
  }
491
- interface TokenSearchFieldProps {
1711
+ type TokenSearchEditorAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
1712
+ 'data-component'?: never;
1713
+ 'data-key'?: never;
1714
+ 'data-morph-skip'?: never;
1715
+ 'data-token-search-editor'?: never;
1716
+ 'data-token-count'?: never;
1717
+ 'data-placeholder'?: never;
1718
+ }>;
1719
+ interface TokenSearchFieldBaseProps {
492
1720
  id: string;
493
1721
  label: string;
494
1722
  query?: string;
@@ -497,48 +1725,156 @@ interface TokenSearchFieldProps {
497
1725
  tokenPlaceholder?: string;
498
1726
  disabled?: boolean;
499
1727
  autofocus?: boolean;
500
- /** Allow an empty field to render as one iconic action. */
501
- collapsible?: boolean;
502
- /** Keep an empty collapsible field open while the application owns focus. */
503
- expanded?: boolean;
504
- expandAction?: string;
505
- expandLabel?: string;
506
- leading?: SafeHtml;
507
- trailing?: SafeHtml;
1728
+ leading?: KerfUiContent;
1729
+ trailing?: KerfUiContent;
508
1730
  editAction?: string;
509
1731
  removeAction?: string;
510
1732
  clearAction?: string;
511
1733
  clearLabel?: string;
512
1734
  className?: string;
513
- editorAttributes?: Readonly<Record<`data-${string}`, string>>;
1735
+ editorAttributes?: TokenSearchEditorAttributes;
514
1736
  }
1737
+ type TokenSearchCollapsibleProps = {
1738
+ /** Allow an empty field to render as one iconic action. */
1739
+ collapsible: true;
1740
+ /** Keep an empty collapsible field open while the application owns focus. */
1741
+ expanded?: boolean;
1742
+ expandAction?: string;
1743
+ expandLabel?: string;
1744
+ } | {
1745
+ collapsible?: false;
1746
+ expanded?: never;
1747
+ expandAction?: never;
1748
+ expandLabel?: never;
1749
+ };
1750
+ type TokenSearchFieldProps = TokenSearchFieldBaseProps & TokenSearchCollapsibleProps;
515
1751
  interface TokenSearchFieldValue {
516
1752
  query: string;
517
1753
  tokens: TokenSearchToken[];
518
1754
  }
519
- 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;
1755
+ declare function TokenSearchField({ id, label, query, tokens, placeholder, tokenPlaceholder, disabled, autofocus, collapsible, expanded, expandAction, expandLabel, leading, trailing, editAction, removeAction, clearAction, clearLabel, className, editorAttributes, }: TokenSearchFieldProps): kerfjs.SafeHtml;
520
1756
  /** Read editable text and ordered token offsets from a rendered TokenSearchField editor. */
521
1757
  declare function readTokenSearchField(editor: HTMLElement, knownTokens?: readonly TokenSearchToken[]): TokenSearchFieldValue;
522
1758
  /** Focus an editor and place its caret at a text offset, skipping atomic token chips. */
523
1759
  declare function placeTokenSearchCaret(editor: HTMLElement, offset?: number): void;
524
1760
 
525
- export { TokenSearchField, type TokenSearchFieldProps, type TokenSearchFieldValue, type TokenSearchToken, placeTokenSearchCaret, readTokenSearchField };
1761
+ export { type TokenSearchEditorAttributes, TokenSearchField, type TokenSearchFieldProps, type TokenSearchFieldValue, type TokenSearchToken, placeTokenSearchCaret, readTokenSearchField };
526
1762
  ```
527
1763
 
528
1764
  ## `@kerfjs/ui/wire-token-search-fields`
529
1765
 
530
1766
  ```ts
1767
+ import { Signal } from 'kerfjs';
1768
+
531
1769
  interface TokenSearchSubmit {
532
1770
  id: string;
533
1771
  editor: HTMLElement;
534
1772
  }
1773
+ /** Reported to {@link WireTokenSearchFieldsOptions.onEdit} on every editor input. */
1774
+ interface TokenSearchEdit extends TokenSearchSubmit {
1775
+ /**
1776
+ * The `InputEvent` that mutated the editor — read `event.inputType` / `event.data`
1777
+ * to gate commit behavior (e.g. only parse a chip on whitespace-terminated input)
1778
+ * without keeping a separate `input` listener.
1779
+ */
1780
+ event: InputEvent;
1781
+ }
1782
+ /** Reported when adjacent-token keyboard deletion asks the app to drop a chip. */
1783
+ interface TokenSearchTokenRemoval {
1784
+ id: string;
1785
+ /** The `data-token-value` of the token the app should remove from its state. */
1786
+ value: string;
1787
+ editor: HTMLElement;
1788
+ /** `'backward'` = the token before the caret (Backspace); `'forward'` = after (Delete). */
1789
+ direction: 'backward' | 'forward';
1790
+ }
1791
+ /**
1792
+ * Opt-in keyboard behavior for the atomic token chips. Off unless `keyboard` is
1793
+ * set; each piece defaults on once opted in. The helper never mutates app state:
1794
+ * a removal is reported through {@link TokenSearchKeyboardOptions.onRemoveToken}
1795
+ * for the caller to apply, while caret movement past a chip is a pure ephemeral
1796
+ * mechanic the helper performs itself.
1797
+ */
1798
+ interface TokenSearchKeyboardBaseOptions {
1799
+ /**
1800
+ * From a collapsed caret with no selection, Backspace removes the token
1801
+ * immediately before it and Delete the token immediately after — reported via
1802
+ * `onRemoveToken` — instead of deleting a character. Default: true.
1803
+ */
1804
+ /**
1805
+ * ArrowRight moves the caret past a trailing atomic token so text typed next
1806
+ * lands after the chip. Default: true.
1807
+ */
1808
+ moveCaretPastToken?: boolean;
1809
+ }
1810
+ type TokenSearchKeyboardOptions = TokenSearchKeyboardBaseOptions & ({
1811
+ removeAdjacentToken?: true;
1812
+ /** Apply the reported removal to your controlled state, then re-render. */
1813
+ onRemoveToken: (removal: TokenSearchTokenRemoval) => void;
1814
+ } | {
1815
+ removeAdjacentToken: false;
1816
+ onRemoveToken?: never;
1817
+ });
1818
+ /**
1819
+ * Managed collapsible behavior for the iconic TokenSearchField. Every piece is on
1820
+ * by default; disable a specific one to own it in the app. Provide `signals` to
1821
+ * drive app-owned `expanded` signals per field id instead of helper-created ones.
1822
+ */
1823
+ interface TokenSearchCollapsibleOptions {
1824
+ /** Expand the field and focus its editor when the iconic trigger is activated. Default: true. */
1825
+ expandOnActivate?: boolean;
1826
+ /** Collapse the field when focus leaves it while it is empty. Default: true. */
1827
+ collapseOnEmptyBlur?: boolean;
1828
+ /** Collapse an empty field on Escape and restore focus to its trigger. Default: true. */
1829
+ collapseOnEscape?: boolean;
1830
+ /** Focus the editor on expand/controlled clear and the trigger on Escape-collapse. Default: true. */
1831
+ manageFocus?: boolean;
1832
+ /**
1833
+ * Keep an empty field expanded when focus moves to a caller-owned surface
1834
+ * rendered outside the field — a suggestions dropdown, date picker, or help
1835
+ * popover shown beside it. Return true for any focus target that must NOT
1836
+ * trigger collapse-on-empty-blur. An element carrying `data-token-search-keep-open`
1837
+ * (or any node inside one) is always exempt, so this predicate is only needed
1838
+ * for surfaces you cannot mark declaratively.
1839
+ */
1840
+ keepOpenOn?: (target: Node | null) => boolean;
1841
+ /** App-owned `expanded` signals keyed by field id; adopted instead of helper-created. */
1842
+ signals?: Readonly<Record<string, Signal<boolean>>>;
1843
+ }
535
1844
  interface WireTokenSearchFieldsOptions {
536
- onSubmit: (submission: TokenSearchSubmit) => void;
1845
+ onSubmit?: (submission: TokenSearchSubmit) => void;
1846
+ /** Fired on every editor `input`, after the browser mutates it, so a caller can drop its own `input` listener. */
1847
+ onEdit?: (edit: TokenSearchEdit) => void;
1848
+ /** Managed collapsible transient behavior. `true`/omitted = on with defaults; `false` = fully off. */
1849
+ collapsible?: boolean | TokenSearchCollapsibleOptions;
1850
+ /** Opt-in atomic-chip keyboard behavior (off by default). */
1851
+ keyboard?: false | TokenSearchKeyboardOptions;
1852
+ }
1853
+ /**
1854
+ * The value returned from {@link wireTokenSearchFields}: call it (or `dispose()`) to
1855
+ * tear down. When collapsible behavior is managed, it also exposes the transient
1856
+ * `expanded` state per field id so the app can read it in render, hand in its own
1857
+ * signal, or drive it imperatively.
1858
+ */
1859
+ interface TokenSearchFieldsHandle {
1860
+ (): void;
1861
+ dispose(): void;
1862
+ /** The managed `expanded` signal for a field id (adopted or helper-created); undefined when unmanaged. */
1863
+ expanded(id: string): Signal<boolean> | undefined;
1864
+ /** Expand the field (and, when focus is managed, focus its editor). */
1865
+ open(id: string): void;
1866
+ /** Collapse the field (and, when focus is managed, restore focus to its trigger). */
1867
+ close(id: string): void;
537
1868
  }
538
- /** Keep TokenSearchField wrapping, submit Enter, and preserve its caret across controlled token deletion. */
539
- declare function wireTokenSearchFields(root: HTMLElement, { onSubmit }: WireTokenSearchFieldsOptions): () => void;
1869
+ /**
1870
+ * Wire every TokenSearchField under `root`: submit on Enter, preserve the caret across
1871
+ * controlled token deletion, and (by default) manage the collapsible field's transient
1872
+ * expand/collapse/focus. Returns a {@link TokenSearchFieldsHandle} — a disposer that also
1873
+ * exposes the managed `expanded` state per field id.
1874
+ */
1875
+ declare function wireTokenSearchFields(root: HTMLElement, { onSubmit, onEdit, collapsible, keyboard, }?: WireTokenSearchFieldsOptions): TokenSearchFieldsHandle;
540
1876
 
541
- export { type TokenSearchSubmit, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
1877
+ export { type TokenSearchCollapsibleOptions, type TokenSearchEdit, type TokenSearchFieldsHandle, type TokenSearchKeyboardOptions, type TokenSearchSubmit, type TokenSearchTokenRemoval, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
542
1878
  ```
543
1879
 
544
1880
  ## `kerfjs/actions`
@@ -617,3 +1953,132 @@ declare function delegateActions<E extends Element = Element>(root: HTMLElement,
617
1953
 
618
1954
  export { type ActionHandler, type DelegateActionsOptions, action, delegateActions };
619
1955
  ```
1956
+
1957
+ ## `@kerfjs/ui/lucide-icon`
1958
+
1959
+ ```ts
1960
+ import * as kerfjs from 'kerfjs';
1961
+ import { IconNode } from 'lucide';
1962
+
1963
+ type LucideNode = IconNode;
1964
+ interface LucideIconProps {
1965
+ icon: LucideNode;
1966
+ name: string;
1967
+ className?: string;
1968
+ label?: string;
1969
+ }
1970
+ /** Render a Lucide-compatible icon node without copying icon SVG strings. */
1971
+ declare function LucideIcon({ icon, name, className, label }: LucideIconProps): kerfjs.SafeHtml;
1972
+
1973
+ export { LucideIcon, type LucideIconProps, type LucideNode };
1974
+ ```
1975
+
1976
+ ## `@kerfjs/ui/surface-scaffold`
1977
+
1978
+ ```ts
1979
+ import * as kerfjs from 'kerfjs';
1980
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
1981
+
1982
+ type DialogSurfaceSize = 'small' | 'medium' | 'large';
1983
+ type DialogSurfacePresentation = 'modal' | 'side-sheet' | 'fullscreen';
1984
+ type SurfaceInset = 'none' | 'compact' | 'comfortable';
1985
+ interface DialogSurfaceProps {
1986
+ children: KerfUiContent;
1987
+ size?: DialogSurfaceSize;
1988
+ presentation?: DialogSurfacePresentation;
1989
+ bodyInset?: SurfaceInset;
1990
+ footerInset?: SurfaceInset;
1991
+ className?: string;
1992
+ }
1993
+ /** Configure recurring Web Awesome dialog geometry without consumer ::part() CSS. */
1994
+ declare function DialogSurface({ children, size, presentation, bodyInset, footerInset, className, }: DialogSurfaceProps): kerfjs.SafeHtml;
1995
+ type PopupSurfaceInset = 'standard' | 'compact' | 'list-zero';
1996
+ interface PopupSurfaceProps {
1997
+ children: KerfUiContent;
1998
+ inset?: PopupSurfaceInset;
1999
+ className?: string;
2000
+ }
2001
+ /** Configure recurring Web Awesome dropdown-menu geometry without consumer ::part() CSS. */
2002
+ declare function PopupSurface({ children, inset, className, }: PopupSurfaceProps): kerfjs.SafeHtml;
2003
+
2004
+ export { DialogSurface, type DialogSurfacePresentation, type DialogSurfaceProps, type DialogSurfaceSize, PopupSurface, type PopupSurfaceInset, type PopupSurfaceProps, type SurfaceInset };
2005
+ ```
2006
+
2007
+ ## `@kerfjs/ui/text`
2008
+
2009
+ ```ts
2010
+ import * as kerfjs from 'kerfjs';
2011
+ import { KerfBaseAttrs } from 'kerfjs/jsx-runtime';
2012
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2013
+
2014
+ type TextVariant = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p';
2015
+ type TextContent = KerfUiContent | string | number | readonly TextContent[];
2016
+ type TextProps = Omit<KerfBaseAttrs, 'children' | 'class' | 'className'> & {
2017
+ /** Native heading or paragraph element to render. */
2018
+ variant: TextVariant;
2019
+ children: TextContent;
2020
+ class?: string;
2021
+ className?: string;
2022
+ };
2023
+ /**
2024
+ * Semantic heading or paragraph text with the standard content-item padding.
2025
+ * All ordinary native heading/paragraph attributes pass through to the element.
2026
+ */
2027
+ declare function Text({ variant: Variant, children, class: classValue, className, ...attributes }: TextProps): kerfjs.SafeHtml;
2028
+
2029
+ export { Text, type TextContent, type TextProps, type TextVariant };
2030
+ ```
2031
+
2032
+ ## `@kerfjs/ui/row`
2033
+
2034
+ ```ts
2035
+ import * as kerfjs from 'kerfjs';
2036
+ import { UiSpaceName, CssLength, CssFlexKeyword, CssFlex } from './css-values.js';
2037
+ import { S as Sides } from './divider-sides-BzB6rphT.js';
2038
+ import { H as HorizontalAlignment, V as VerticalAlignment } from './flex-alignment-CF8NLj7i.js';
2039
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
2040
+
2041
+ interface RowProps {
2042
+ children?: KerfUiContent;
2043
+ /** Horizontal distribution. Defaults to left. */
2044
+ hAlign?: HorizontalAlignment;
2045
+ /** Vertical alignment and wrapped-line distribution. Defaults to full. */
2046
+ vAlign?: VerticalAlignment;
2047
+ /** A named UI spacing token or typed CSS length. Defaults to xs. */
2048
+ gap?: UiSpaceName | CssLength;
2049
+ /** Allow this row to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
2050
+ flex?: boolean | CssFlexKeyword | CssFlex;
2051
+ /** Allow children to wrap onto additional lines. */
2052
+ wrap?: boolean;
2053
+ /** Physical sides that receive the standard 17px text inset. */
2054
+ textInsets?: Sides;
2055
+ /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */
2056
+ controlInsets?: Sides;
2057
+ className?: string;
2058
+ }
2059
+ /** A horizontal flex row with explicit physical-axis alignment and spacing. */
2060
+ declare function Row({ children, hAlign, vAlign, gap, flex, wrap, textInsets, controlInsets, className, }: RowProps): kerfjs.SafeHtml;
2061
+
2062
+ export { CssFlex, CssFlexKeyword, CssLength, HorizontalAlignment, Row, type RowProps, Sides, UiSpaceName, VerticalAlignment };
2063
+ ```
2064
+
2065
+ ## `@kerfjs/ui/spacer`
2066
+
2067
+ ```ts
2068
+ import * as kerfjs from 'kerfjs';
2069
+ import { UiSpaceName, CssLength } from './css-values.js';
2070
+
2071
+ interface SpacerProps {
2072
+ /** Grow and shrink to fill the available space along a parent's flex axis. */
2073
+ flex?: boolean;
2074
+ /** A named UI spacing token or typed physical width. */
2075
+ width?: UiSpaceName | CssLength;
2076
+ /** A named UI spacing token or typed physical height. */
2077
+ height?: UiSpaceName | CssLength;
2078
+ className?: string;
2079
+ }
2080
+ /** A decorative fixed-size or flexible gap for Row, List, and other flex layouts. */
2081
+ declare function Spacer({ flex, width, height, className, }: SpacerProps): kerfjs.SafeHtml;
2082
+
2083
+ export { CssLength, Spacer, type SpacerProps, UiSpaceName };
2084
+ ```