@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
@@ -0,0 +1,21 @@
1
+ import { delegate } from 'kerfjs';
2
+
3
+ // src/wire-tab-scaffold.ts
4
+ function wireTabScaffold(root, options) {
5
+ const section = root.matches('[data-component="tab-scaffold"]') ? root : root.querySelector('[data-component="tab-scaffold"]');
6
+ if (!(section instanceof HTMLElement)) return () => {
7
+ };
8
+ return delegate(
9
+ section,
10
+ "click",
11
+ "[data-tab-scaffold-tab]",
12
+ (_event, target) => {
13
+ const tabId = target.getAttribute("data-tab-scaffold-tab");
14
+ if (tabId) options.onSelect(tabId);
15
+ }
16
+ );
17
+ }
18
+
19
+ export { wireTabScaffold };
20
+
21
+ //# sourceMappingURL=wire-tab-scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wire-tab-scaffold.ts"],"names":[],"mappings":";;;AAWO,SAAS,eAAA,CACd,MACA,OAAA,EACY;AACZ,EAAA,MAAM,OAAA,GAAU,KAAK,OAAA,CAAQ,iCAAiC,IAC1D,IAAA,GACA,IAAA,CAAK,cAAc,iCAAiC,CAAA;AACxD,EAAA,IAAI,EAAE,OAAA,YAAmB,WAAA,CAAA,EAAc,OAAO,MAAM;AAAA,EAAC,CAAA;AACrD,EAAA,OAAO,QAAA;AAAA,IACL,OAAA;AAAA,IACA,OAAA;AAAA,IACA,yBAAA;AAAA,IACA,CAAC,QAAQ,MAAA,KAAW;AAClB,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,YAAA,CAAa,uBAAuB,CAAA;AACzD,MAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,QAAA,CAAS,KAAK,CAAA;AAAA,IACnC;AAAA,GACF;AACF","file":"wire-tab-scaffold.js","sourcesContent":["import { delegate } from 'kerfjs';\n\nexport interface WireTabScaffoldOptions {\n /** Invoked with the selected tab id when a bottom-bar tab is activated. */\n onSelect: (tabId: string) => void;\n}\n\n/**\n * Wire a `TabScaffold`'s bottom tab bar: clicking a tab calls `onSelect` with its\n * id (the app then updates its controlled `active`). Returns a disposer.\n */\nexport function wireTabScaffold(\n root: Element,\n options: WireTabScaffoldOptions,\n): () => void {\n const section = root.matches('[data-component=\"tab-scaffold\"]')\n ? root\n : root.querySelector('[data-component=\"tab-scaffold\"]');\n if (!(section instanceof HTMLElement)) return () => {};\n return delegate(\n section,\n 'click',\n '[data-tab-scaffold-tab]',\n (_event, target) => {\n const tabId = target.getAttribute('data-tab-scaffold-tab');\n if (tabId) options.onSelect(tabId);\n },\n );\n}\n"]}
@@ -1,11 +1,111 @@
1
+ import { Signal } from 'kerfjs';
2
+
1
3
  interface TokenSearchSubmit {
2
4
  id: string;
3
5
  editor: HTMLElement;
4
6
  }
7
+ /** Reported to {@link WireTokenSearchFieldsOptions.onEdit} on every editor input. */
8
+ interface TokenSearchEdit extends TokenSearchSubmit {
9
+ /**
10
+ * The `InputEvent` that mutated the editor — read `event.inputType` / `event.data`
11
+ * to gate commit behavior (e.g. only parse a chip on whitespace-terminated input)
12
+ * without keeping a separate `input` listener.
13
+ */
14
+ event: InputEvent;
15
+ }
16
+ /** Reported when adjacent-token keyboard deletion asks the app to drop a chip. */
17
+ interface TokenSearchTokenRemoval {
18
+ id: string;
19
+ /** The `data-token-value` of the token the app should remove from its state. */
20
+ value: string;
21
+ editor: HTMLElement;
22
+ /** `'backward'` = the token before the caret (Backspace); `'forward'` = after (Delete). */
23
+ direction: 'backward' | 'forward';
24
+ }
25
+ /**
26
+ * Opt-in keyboard behavior for the atomic token chips. Off unless `keyboard` is
27
+ * set; each piece defaults on once opted in. The helper never mutates app state:
28
+ * a removal is reported through {@link TokenSearchKeyboardOptions.onRemoveToken}
29
+ * for the caller to apply, while caret movement past a chip is a pure ephemeral
30
+ * mechanic the helper performs itself.
31
+ */
32
+ interface TokenSearchKeyboardBaseOptions {
33
+ /**
34
+ * From a collapsed caret with no selection, Backspace removes the token
35
+ * immediately before it and Delete the token immediately after — reported via
36
+ * `onRemoveToken` — instead of deleting a character. Default: true.
37
+ */
38
+ /**
39
+ * ArrowRight moves the caret past a trailing atomic token so text typed next
40
+ * lands after the chip. Default: true.
41
+ */
42
+ moveCaretPastToken?: boolean;
43
+ }
44
+ type TokenSearchKeyboardOptions = TokenSearchKeyboardBaseOptions & ({
45
+ removeAdjacentToken?: true;
46
+ /** Apply the reported removal to your controlled state, then re-render. */
47
+ onRemoveToken: (removal: TokenSearchTokenRemoval) => void;
48
+ } | {
49
+ removeAdjacentToken: false;
50
+ onRemoveToken?: never;
51
+ });
52
+ /**
53
+ * Managed collapsible behavior for the iconic TokenSearchField. Every piece is on
54
+ * by default; disable a specific one to own it in the app. Provide `signals` to
55
+ * drive app-owned `expanded` signals per field id instead of helper-created ones.
56
+ */
57
+ interface TokenSearchCollapsibleOptions {
58
+ /** Expand the field and focus its editor when the iconic trigger is activated. Default: true. */
59
+ expandOnActivate?: boolean;
60
+ /** Collapse the field when focus leaves it while it is empty. Default: true. */
61
+ collapseOnEmptyBlur?: boolean;
62
+ /** Collapse an empty field on Escape and restore focus to its trigger. Default: true. */
63
+ collapseOnEscape?: boolean;
64
+ /** Focus the editor on expand/controlled clear and the trigger on Escape-collapse. Default: true. */
65
+ manageFocus?: boolean;
66
+ /**
67
+ * Keep an empty field expanded when focus moves to a caller-owned surface
68
+ * rendered outside the field — a suggestions dropdown, date picker, or help
69
+ * popover shown beside it. Return true for any focus target that must NOT
70
+ * trigger collapse-on-empty-blur. An element carrying `data-token-search-keep-open`
71
+ * (or any node inside one) is always exempt, so this predicate is only needed
72
+ * for surfaces you cannot mark declaratively.
73
+ */
74
+ keepOpenOn?: (target: Node | null) => boolean;
75
+ /** App-owned `expanded` signals keyed by field id; adopted instead of helper-created. */
76
+ signals?: Readonly<Record<string, Signal<boolean>>>;
77
+ }
5
78
  interface WireTokenSearchFieldsOptions {
6
- onSubmit: (submission: TokenSearchSubmit) => void;
79
+ onSubmit?: (submission: TokenSearchSubmit) => void;
80
+ /** Fired on every editor `input`, after the browser mutates it, so a caller can drop its own `input` listener. */
81
+ onEdit?: (edit: TokenSearchEdit) => void;
82
+ /** Managed collapsible transient behavior. `true`/omitted = on with defaults; `false` = fully off. */
83
+ collapsible?: boolean | TokenSearchCollapsibleOptions;
84
+ /** Opt-in atomic-chip keyboard behavior (off by default). */
85
+ keyboard?: false | TokenSearchKeyboardOptions;
86
+ }
87
+ /**
88
+ * The value returned from {@link wireTokenSearchFields}: call it (or `dispose()`) to
89
+ * tear down. When collapsible behavior is managed, it also exposes the transient
90
+ * `expanded` state per field id so the app can read it in render, hand in its own
91
+ * signal, or drive it imperatively.
92
+ */
93
+ interface TokenSearchFieldsHandle {
94
+ (): void;
95
+ dispose(): void;
96
+ /** The managed `expanded` signal for a field id (adopted or helper-created); undefined when unmanaged. */
97
+ expanded(id: string): Signal<boolean> | undefined;
98
+ /** Expand the field (and, when focus is managed, focus its editor). */
99
+ open(id: string): void;
100
+ /** Collapse the field (and, when focus is managed, restore focus to its trigger). */
101
+ close(id: string): void;
7
102
  }
8
- /** Keep TokenSearchField wrapping, submit Enter, and preserve its caret across controlled token deletion. */
9
- declare function wireTokenSearchFields(root: HTMLElement, { onSubmit }: WireTokenSearchFieldsOptions): () => void;
103
+ /**
104
+ * Wire every TokenSearchField under `root`: submit on Enter, preserve the caret across
105
+ * controlled token deletion, and (by default) manage the collapsible field's transient
106
+ * expand/collapse/focus. Returns a {@link TokenSearchFieldsHandle} — a disposer that also
107
+ * exposes the managed `expanded` state per field id.
108
+ */
109
+ declare function wireTokenSearchFields(root: HTMLElement, { onSubmit, onEdit, collapsible, keyboard, }?: WireTokenSearchFieldsOptions): TokenSearchFieldsHandle;
10
110
 
11
- export { type TokenSearchSubmit, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
111
+ export { type TokenSearchCollapsibleOptions, type TokenSearchEdit, type TokenSearchFieldsHandle, type TokenSearchKeyboardOptions, type TokenSearchSubmit, type TokenSearchTokenRemoval, type WireTokenSearchFieldsOptions, wireTokenSearchFields };
@@ -1,4 +1,4 @@
1
- export { wireTokenSearchFields } from './chunk-H5AGGVU5.js';
1
+ export { wireTokenSearchFields } from './chunk-VDUJZF5G.js';
2
2
 
3
3
 
4
4
 
@@ -0,0 +1,44 @@
1
+ import { SafeHtml } from 'kerfjs';
2
+ import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
3
+ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
4
+
5
+ /** A collapsible Workbench panel — a side rail or the bottom drawer. */
6
+ interface WorkbenchPanel {
7
+ content: KerfUiContent;
8
+ /** Whether the panel is currently collapsed (the app owns this). */
9
+ collapsed?: boolean;
10
+ /** Rail width, or drawer height, in px. Overrides the CSS default. */
11
+ size?: number;
12
+ /** Accessible name for the panel region. */
13
+ label?: string;
14
+ separator?: ResizableRegionSeparator;
15
+ collapseMotion?: ResizableRegionCollapseMotion;
16
+ contentOverflow?: ResizableRegionContentOverflow;
17
+ presentation?: ResizableRegionPresentation;
18
+ /** Control shown in a safe-area-aware viewport corner while collapsed. */
19
+ restoreControl?: SafeHtml;
20
+ restorePosition?: ResizableRegionRestorePosition;
21
+ }
22
+ interface WorkbenchProps {
23
+ id: string;
24
+ label: string;
25
+ /** The central work area. */
26
+ main: KerfUiContent;
27
+ leftRail?: WorkbenchPanel;
28
+ rightRail?: WorkbenchPanel;
29
+ bottomDrawer?: WorkbenchPanel;
30
+ className?: string;
31
+ }
32
+ /**
33
+ * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and
34
+ * bottom drawer around a central work area (any absent). Collapsing snaps the
35
+ * panel's track to zero in one reflow while its fixed-size content slides out via
36
+ * a composited transform — the instant-width / sliding-content technique, so the
37
+ * work area relayouts once, not per frame. Bottom-drawer content stays anchored
38
+ * to the shell's stable bottom edge throughout that transition. The app owns
39
+ * each `collapsed` flag; the collapse is pure CSS (no wire). See
40
+ * `docs/23-app-layouts.md` §3.3.
41
+ */
42
+ declare function Workbench({ id, label, main, leftRail, rightRail, bottomDrawer, className, }: WorkbenchProps): SafeHtml;
43
+
44
+ export { Workbench, type WorkbenchPanel, type WorkbenchProps };
@@ -0,0 +1,106 @@
1
+ import { jsxs, jsx } from 'kerfjs/jsx-runtime';
2
+
3
+ // src/workbench.tsx
4
+ function Workbench({
5
+ id,
6
+ label,
7
+ main,
8
+ leftRail,
9
+ rightRail,
10
+ bottomDrawer,
11
+ className = ""
12
+ }) {
13
+ return /* @__PURE__ */ jsxs(
14
+ "section",
15
+ {
16
+ class: `kui-workbench ${className}`.trim(),
17
+ id,
18
+ "data-component": "workbench",
19
+ "aria-label": label,
20
+ children: [
21
+ leftRail && /* @__PURE__ */ jsx(
22
+ "aside",
23
+ {
24
+ class: "kui-workbench__rail kui-workbench__rail--left",
25
+ "data-workbench-rail": "left",
26
+ "data-collapsed": String(leftRail.collapsed ?? false),
27
+ "data-separator": leftRail.separator ?? "auto",
28
+ "data-collapse-motion": leftRail.collapseMotion ?? "slide",
29
+ "data-content-overflow": leftRail.contentOverflow ?? "clip",
30
+ "data-presentation": leftRail.presentation ?? "inline",
31
+ "aria-label": leftRail.label || void 0,
32
+ "aria-hidden": leftRail.presentation === "hidden" ? "true" : void 0,
33
+ style: leftRail.size ? `--kui-workbench-rail-width: ${leftRail.size}px` : void 0,
34
+ children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: leftRail.content })
35
+ }
36
+ ),
37
+ leftRail?.collapsed && leftRail.restoreControl && leftRail.presentation !== "hidden" && /* @__PURE__ */ jsx(
38
+ "div",
39
+ {
40
+ class: "kui-workbench__restore",
41
+ "data-panel": "left",
42
+ "data-position": leftRail.restorePosition ?? "bottom-start",
43
+ children: leftRail.restoreControl
44
+ }
45
+ ),
46
+ /* @__PURE__ */ jsxs("div", { class: "kui-workbench__center", children: [
47
+ /* @__PURE__ */ jsx("div", { class: "kui-workbench__main", "data-workbench-main": true, children: main }),
48
+ bottomDrawer && /* @__PURE__ */ jsx(
49
+ "section",
50
+ {
51
+ class: "kui-workbench__drawer",
52
+ "data-workbench-drawer": true,
53
+ "data-collapsed": String(bottomDrawer.collapsed ?? false),
54
+ "data-separator": bottomDrawer.separator ?? "auto",
55
+ "data-collapse-motion": bottomDrawer.collapseMotion ?? "slide",
56
+ "data-content-overflow": bottomDrawer.contentOverflow ?? "clip",
57
+ "data-presentation": bottomDrawer.presentation ?? "inline",
58
+ "aria-label": bottomDrawer.label || void 0,
59
+ "aria-hidden": bottomDrawer.presentation === "hidden" ? "true" : void 0,
60
+ style: bottomDrawer.size ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px` : void 0,
61
+ children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: bottomDrawer.content })
62
+ }
63
+ ),
64
+ bottomDrawer?.collapsed && bottomDrawer.restoreControl && bottomDrawer.presentation !== "hidden" && /* @__PURE__ */ jsx(
65
+ "div",
66
+ {
67
+ class: "kui-workbench__restore",
68
+ "data-panel": "bottom",
69
+ "data-position": bottomDrawer.restorePosition ?? "bottom-end",
70
+ children: bottomDrawer.restoreControl
71
+ }
72
+ )
73
+ ] }),
74
+ rightRail && /* @__PURE__ */ jsx(
75
+ "aside",
76
+ {
77
+ class: "kui-workbench__rail kui-workbench__rail--right",
78
+ "data-workbench-rail": "right",
79
+ "data-collapsed": String(rightRail.collapsed ?? false),
80
+ "data-separator": rightRail.separator ?? "auto",
81
+ "data-collapse-motion": rightRail.collapseMotion ?? "slide",
82
+ "data-content-overflow": rightRail.contentOverflow ?? "clip",
83
+ "data-presentation": rightRail.presentation ?? "inline",
84
+ "aria-label": rightRail.label || void 0,
85
+ "aria-hidden": rightRail.presentation === "hidden" ? "true" : void 0,
86
+ style: rightRail.size ? `--kui-workbench-rail-width: ${rightRail.size}px` : void 0,
87
+ children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: rightRail.content })
88
+ }
89
+ ),
90
+ rightRail?.collapsed && rightRail.restoreControl && rightRail.presentation !== "hidden" && /* @__PURE__ */ jsx(
91
+ "div",
92
+ {
93
+ class: "kui-workbench__restore",
94
+ "data-panel": "right",
95
+ "data-position": rightRail.restorePosition ?? "bottom-end",
96
+ children: rightRail.restoreControl
97
+ }
98
+ )
99
+ ]
100
+ }
101
+ );
102
+ }
103
+
104
+ export { Workbench };
105
+
106
+ //# sourceMappingURL=workbench.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/workbench.tsx"],"names":[],"mappings":"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","file":"workbench.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport type {\n ResizableRegionCollapseMotion,\n ResizableRegionContentOverflow,\n ResizableRegionPresentation,\n ResizableRegionRestorePosition,\n ResizableRegionSeparator,\n} from './resizable-region.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\n/** A collapsible Workbench panel — a side rail or the bottom drawer. */\nexport interface WorkbenchPanel {\n content: KerfUiContent;\n /** Whether the panel is currently collapsed (the app owns this). */\n collapsed?: boolean;\n /** Rail width, or drawer height, in px. Overrides the CSS default. */\n size?: number;\n /** Accessible name for the panel region. */\n label?: string;\n separator?: ResizableRegionSeparator;\n collapseMotion?: ResizableRegionCollapseMotion;\n contentOverflow?: ResizableRegionContentOverflow;\n presentation?: ResizableRegionPresentation;\n /** Control shown in a safe-area-aware viewport corner while collapsed. */\n restoreControl?: SafeHtml;\n restorePosition?: ResizableRegionRestorePosition;\n}\n\nexport interface WorkbenchProps {\n id: string;\n label: string;\n /** The central work area. */\n main: KerfUiContent;\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n bottomDrawer?: WorkbenchPanel;\n className?: string;\n}\n\n/**\n * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and\n * bottom drawer around a central work area (any absent). Collapsing snaps the\n * panel's track to zero in one reflow while its fixed-size content slides out via\n * a composited transform — the instant-width / sliding-content technique, so the\n * work area relayouts once, not per frame. Bottom-drawer content stays anchored\n * to the shell's stable bottom edge throughout that transition. The app owns\n * each `collapsed` flag; the collapse is pure CSS (no wire). See\n * `docs/23-app-layouts.md` §3.3.\n */\nexport function Workbench({\n id,\n label,\n main,\n leftRail,\n rightRail,\n bottomDrawer,\n className = '',\n}: WorkbenchProps) {\n return (\n <section\n class={`kui-workbench ${className}`.trim()}\n id={id}\n data-component=\"workbench\"\n aria-label={label}\n >\n {leftRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--left\"\n data-workbench-rail=\"left\"\n data-collapsed={String(leftRail.collapsed ?? false)}\n data-separator={leftRail.separator ?? 'auto'}\n data-collapse-motion={leftRail.collapseMotion ?? 'slide'}\n data-content-overflow={leftRail.contentOverflow ?? 'clip'}\n data-presentation={leftRail.presentation ?? 'inline'}\n aria-label={leftRail.label || undefined}\n aria-hidden={leftRail.presentation === 'hidden' ? 'true' : undefined}\n style={\n leftRail.size\n ? `--kui-workbench-rail-width: ${leftRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{leftRail.content}</div>\n </aside>\n )}\n {leftRail?.collapsed &&\n leftRail.restoreControl &&\n leftRail.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"left\"\n data-position={leftRail.restorePosition ?? 'bottom-start'}\n >\n {leftRail.restoreControl}\n </div>\n )}\n <div class=\"kui-workbench__center\">\n <div class=\"kui-workbench__main\" data-workbench-main>\n {main}\n </div>\n {bottomDrawer && (\n <section\n class=\"kui-workbench__drawer\"\n data-workbench-drawer\n data-collapsed={String(bottomDrawer.collapsed ?? false)}\n data-separator={bottomDrawer.separator ?? 'auto'}\n data-collapse-motion={bottomDrawer.collapseMotion ?? 'slide'}\n data-content-overflow={bottomDrawer.contentOverflow ?? 'clip'}\n data-presentation={bottomDrawer.presentation ?? 'inline'}\n aria-label={bottomDrawer.label || undefined}\n aria-hidden={\n bottomDrawer.presentation === 'hidden' ? 'true' : undefined\n }\n style={\n bottomDrawer.size\n ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">\n {bottomDrawer.content}\n </div>\n </section>\n )}\n {bottomDrawer?.collapsed &&\n bottomDrawer.restoreControl &&\n bottomDrawer.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"bottom\"\n data-position={bottomDrawer.restorePosition ?? 'bottom-end'}\n >\n {bottomDrawer.restoreControl}\n </div>\n )}\n </div>\n {rightRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--right\"\n data-workbench-rail=\"right\"\n data-collapsed={String(rightRail.collapsed ?? false)}\n data-separator={rightRail.separator ?? 'auto'}\n data-collapse-motion={rightRail.collapseMotion ?? 'slide'}\n data-content-overflow={rightRail.contentOverflow ?? 'clip'}\n data-presentation={rightRail.presentation ?? 'inline'}\n aria-label={rightRail.label || undefined}\n aria-hidden={rightRail.presentation === 'hidden' ? 'true' : undefined}\n style={\n rightRail.size\n ? `--kui-workbench-rail-width: ${rightRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{rightRail.content}</div>\n </aside>\n )}\n {rightRail?.collapsed &&\n rightRail.restoreControl &&\n rightRail.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"right\"\n data-position={rightRail.restorePosition ?? 'bottom-end'}\n >\n {rightRail.restoreControl}\n </div>\n )}\n </section>\n );\n}\n"]}
@@ -6,18 +6,70 @@ These are normative contracts for package components and consuming applications.
6
6
 
7
7
  - All controls have accessible names and visible `:focus-visible` treatment.
8
8
  - Decorative `LucideIcon` output is `aria-hidden`; pass `label` only when the SVG itself conveys meaning.
9
- - `MenuItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
10
- - `MenuHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
11
- - In any `.kui-pane`, menu rows, `MenuHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `MenuHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-menu-header-action-icon-size`; do not reduce the target to the visible icon.
9
+ - `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Selected rows keep the normal foreground over their brand-tinted fill so text retains WCAG AA contrast in light and dark themes. Its leading icon visual is 18px while the row retains its 44px minimum target; multiline icons align to the first text line. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
10
+ - `ListHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
11
+ - Dispose `wireTabBars` and `wireTokenSearchFields` with their owning view. Disposal cancels queued controlled-render focus restoration, so a torn-down view cannot reclaim focus from its successor.
12
+ - `wireNavStack` moves focus into every newly active top view and remembers the last focused descendant of each mounted view for pop restoration. Mark a preferred initial heading or control with `data-nav-focus` (and `tabindex="-1"` when it is not normally tabbable); otherwise the helper uses the first enabled focusable descendant, then a programmatically focusable view-container fallback.
13
+ - In any `.kui-pane`, menu rows, `ListHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `ListHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-list-header-action-icon-size`; do not reduce the target to the visible icon.
12
14
  - `LoadingSpinner` is either labeled (`role="img"`) or hidden. Its rotation stops for reduced motion.
13
- - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure.
15
+ - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure. Its optional `badge` is persistent inline status/count content beside the title and is announced as part of the banner; keep it terse and do not rely on color alone for its meaning.
14
16
  - `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
15
17
  - `Select` follows the Web Awesome host's standard `input`/`change` events. Application tests verify the live `value`, focus, and events—not attributes alone. Its decorative option icons and value-dependent selected content remain present after controlled rerenders, so the visible choice does not silently lose its non-text cue.
16
18
  - `DisclosureArrow` is an 18px root-scaled decorative visual by default, not an interaction target. Its owning native control supplies a stable accessible name, pointer and keyboard interaction, and `aria-expanded`; multiple arrows keep independently controlled state. Consumers may override `--kui-disclosure-arrow-size` without changing that ownership. Configured directions animate over the shortest path; a 180-degree closed-to-open tie uses counterclockwise rotation.
17
-
18
- ## MenuActionRow
19
-
20
- `MenuActionRow` renders a noninteractive visual root containing primary and
19
+ - `List` is layout-only and adds no `list` role. Children own their native or ARIA semantics. Give a scrollable List a bounded block size, avoid nested scroll owners, and keep focused children visible while scrolling.
20
+
21
+ ## Select
22
+
23
+ Supply a visible `label`, or `ariaLabel` when the surrounding interface already
24
+ provides visual context. A nonempty `label` takes precedence when both are
25
+ supplied. Kerf forwards that name through Web Awesome's internal label contract;
26
+ an `aria-label` on the custom-element host alone cannot name its shadow
27
+ combobox. With `ariaLabel` alone, Kerf visually hides the internal label without
28
+ adding height or spacing. This also applies to `renderSelected`: selected content
29
+ stays separate from the control's stable accessible name. Applications do not
30
+ need shadow-DOM patches or extra label styling.
31
+
32
+ Use `presentation="toolbar-borderless"` with `size="compact"` inside a
33
+ `ToolbarControlGroup`; set `focusRingOwner="group"` when that parent paints the
34
+ composed focus ring. `selectedPresentation="icon-only"` hides only the visible
35
+ selected label—the required `label` or `ariaLabel` still names the combobox.
36
+ Navigation selects can use intrinsic `presentation="navigation"` plus
37
+ `labelMaxWidth` for component-owned ellipsis. These props own the control's
38
+ appearance; its parent continues to own outer placement.
39
+
40
+ Use `hint` for persistent supporting text below the control; use
41
+ `placeholderText` only for the empty value shown inside the closed control.
42
+ Kerf passes hint text through Web Awesome's form-control contract, which renders
43
+ the hint part and connects the shadow combobox to it with `aria-describedby`.
44
+ The accessible control is that shadow combobox, not the `wa-select` wrapper;
45
+ inspect or test the element returned by the `combobox` role. Both the `hint`
46
+ attribute used by Kerf and Web Awesome's explicit `hint` slot produce a computed
47
+ accessible description in Chromium, Firefox, and WebKit, so applications do not
48
+ need an `aria-description` mirror or a shadow-DOM patch.
49
+ Loading placeholders keep the same visible hint while replacing the interactive
50
+ control with inert chrome.
51
+
52
+ `tests/unit/components.test.tsx` covers name projection and visible/hidden label
53
+ variants. `tests/browser/select-accessibility.spec.ts` verifies actual accessible
54
+ names, unchanged unlabeled geometry, keyboard selection, controlled rerenders,
55
+ native hint wiring, and wide/narrow presentation in Chromium, Firefox, and WebKit.
56
+
57
+ The explicit `@kerfjs/ui/select/register` import also installs the canonical
58
+ Select lifecycle adapter. The latest open/close request owns animation completion
59
+ and deferred option focus. A stale close cannot hide a reopened popup after
60
+ resize; stale completion events are not emitted. Preventing `wa-show` or
61
+ `wa-hide` retains the accepted closed/open state, and removal prevents deferred
62
+ activation. Native option selection, keyboard behavior, dismissal listeners,
63
+ and popup anchor placement remain with Web Awesome. Raw `wa-select` elements
64
+ without the canonical `data-component="select"` marker are unchanged.
65
+ `tests/unit/select-lifecycle.test.ts` and `animate-select-popup.test.ts` cover
66
+ repeated and out-of-order completions, cancellation, instance isolation, and
67
+ removal. `tests/browser/select-lifecycle.spec.ts` repeats animation/resize/reopen
68
+ sequences at wide and narrow widths in Chromium, Firefox, and WebKit.
69
+
70
+ ## ListActionRow
71
+
72
+ `ListActionRow` renders a noninteractive visual root containing primary and
21
73
  trailing native buttons as siblings. The primary comes first in DOM and Tab
22
74
  order and alone receives `aria-current` or `aria-pressed`; the trailing button
23
75
  has its own required accessible name and disabled state. Both controls carry
@@ -25,7 +77,9 @@ the row's item id for application delegation, while the root deliberately has
25
77
  no action or role. `label`, `icon`, and `trailingActionIcon` are dormant visual
26
78
  content and must not contain buttons, links, interactive roles, or other
27
79
  controls. A trailing click, double click, context menu, Enter, or Space
28
- must never activate the primary control. The application owns controlled
80
+ must never activate the primary control. Selected rows keep the normal
81
+ foreground over their brand-tinted fill so text retains WCAG AA contrast in
82
+ light and dark themes. The application owns controlled
29
83
  selection and the lifecycle, focus, and dismissal policy of any related
30
84
  popover or context menu.
31
85
 
@@ -41,21 +95,48 @@ The handle exposes separator role, orientation, name, minimum, maximum, and curr
41
95
 
42
96
  The application owns persistence and collapsed/expanded policy. Keep the last expanded size outside the component and restore it when reopening. An optional `handleIcon` replaces only decorative dormant content; it must not contain controls or interactive roles because the separator remains the sole focus and interaction owner.
43
97
 
44
- ## DialogHeader
45
-
46
- `DialogHeader` keeps its icon and `h2` together in the leading borderless
47
- `ToolbarControlGroup`, with optional actions in a separate trailing group. Pass
48
- localized `actionsLabel` text when the action group needs an accessible name;
49
- the component does not synthesize an English label. The application connects
50
- `titleId` and an optional `summaryId` to the owning dialog through
51
- `aria-labelledby` and `aria-describedby`. Pass action children directly so the
52
- component can group them; existing pre-grouped actions remain compatible, but
53
- their inner groups retain their own labels and semantics.
98
+ ## Toolbar headings
99
+
100
+ For headings and paragraphs outside toolbar identity/title zones, use `Text`.
101
+ It renders the selected native `h1`–`h6` or `p` element, so heading navigation
102
+ follows the variant directly; choose a level that preserves the document
103
+ outline. Native global, `data-*`, and `aria-*` attributes pass through.
104
+
105
+ Compose panel, dialog, and page headings as a plain `Toolbar`. Its leading zone
106
+ holds an optional icon `ToolbarControlGroup` and a direct extra-large
107
+ `ToolbarText`; controls belong in a trailing `ToolbarControlGroup`. By default
108
+ the title carries no native heading role, so for a **dialog or panel** the
109
+ application connects the title id and optional supporting-copy id to the owning
110
+ host through `aria-labelledby` and `aria-describedby`. For a **page or view**
111
+ title, pass `headingLevel` (usually `1`) to `ToolbarText`: it then exposes
112
+ `role="heading"` with a matching `aria-level`, giving the view a heading landmark
113
+ so screen-reader heading navigation and "main heading" semantics work — the same
114
+ role/level pair used throughout Kerf UI. Keep the levels meaningful and
115
+ non-skipping within a view. Pass trailing
116
+ controls as a labeled `ToolbarControlGroup` when that group needs an accessible
117
+ name. Keep supporting copy as app-owned content below the toolbar.
118
+
119
+ ## FloatingToolbar
120
+
121
+ `FloatingToolbar` is a `role="toolbar"` region with a **required** accessible
122
+ `label`; it floats over the content of its nearest positioned ancestor and is
123
+ forced to a dark color scheme, but it is **not** in the top layer, so it never
124
+ covers dialogs, popovers, or other overlays. Its children are the app's controls
125
+ (normally `ToolbarControlGroup`s), which keep their own names, focus, and
126
+ keyboard behavior; the application owns their actions, the toolbar's visibility,
127
+ and — via `position` and `--kui-floating-toolbar-inset` — where it sits.
54
128
 
55
129
  ## Tabs
56
130
 
57
131
  `AppTab` renders one controlled tab. `TabBar` supplies the containing tab list,
58
132
  fixed leading/trailing regions, and a horizontally scrollable strip.
133
+ Use `TabBar.presentation` for rail, segmented, or inspector chrome,
134
+ `allocation="fill"` when peers should divide the strip, and
135
+ `trailingPlacement="adjacent"` when an action belongs beside the final tab.
136
+ `AppTab` provides compact 32px, segmented, truncating-label, and icon-only
137
+ presentations. Icon-only tabs keep the required `name` as the tab button's
138
+ accessible name while visually hiding the duplicate label. These props own
139
+ component appearance only; the application still owns the bar's outer placement.
59
140
  `AppTab.rootAttributes` accepts application `data-*` metadata only; runtime
60
141
  filtering rejects roles plus case variants of component- or wiring-owned
61
142
  action, identity, selection, drag, drop, and component attributes. An optional
@@ -68,6 +149,16 @@ autoscroll at either horizontal edge. Edge autoscroll is direct manipulation,
68
149
  stops on drop/drag end/disposal, and does not change keyboard behavior. It
69
150
  returns a disposer.
70
151
 
152
+ Arrow / Home / End follow the ARIA Tabs **automatic-activation** pattern by
153
+ default: they move roving focus and select the focused tab. If selection synchronously
154
+ replaces the strip, focus returns to the replacement with the same bar and tab IDs. Pass
155
+ `activation: 'manual'` (or set `data-tab-activation="manual"` on a strip via the
156
+ `TabBar` `activation` prop, which overrides the option per bar) for
157
+ **manual activation**: arrow keys move roving focus only and the user selects
158
+ with Enter / Space (native on the tab button) or click. Use manual activation
159
+ when selecting a tab is a heavy or side-effecting action — e.g. a tab that loads
160
+ a project — so arrowing through the strip does not trigger it on every tab.
161
+
71
162
  The application owns the ordered tab array, selection, panels, close policy,
72
163
  routing, and persistence. On `onReorder`, synchronously render the reported
73
164
  order so the helper can restore focus to the moved tab. Pair tabs with
@@ -97,7 +188,12 @@ and `placeTokenSearchCaret()` to restore a text caret without landing inside a
97
188
  chip. Call `wireTokenSearchFields()` once at a stable root so Enter submits
98
189
  without inserting a contenteditable line break and keyboard chip deletion
99
190
  restores focus plus the text-relative caret after controlled rendering replaces
100
- the editor; the editor still wraps text visually at its inline edge. Editable text is DOM-owned between token changes; a clear handler empties
191
+ the editor. Restoration finishes after synchronous input listeners and before
192
+ another keystroke; it does not wait for an animation frame that could overwrite
193
+ a later selection. Select All + Backspace/Delete shares that replacement path
194
+ and keeps the adopted expanded signal open while managed focus is enabled.
195
+ Real outside focus, empty Escape, disposal, and removed fields retain their usual
196
+ behavior. The editor still wraps text visually at its inline edge. Editable text is DOM-owned between token changes; a clear handler empties
101
197
  the editor's `textContent` before updating application state. Leading and
102
198
  trailing controls share the first text line's fixed vertical center and remain
103
199
  there as the editor wraps. In `collapsible` mode, the closed state is one named
@@ -108,6 +204,45 @@ trigger into the revealed editor, and clear `expanded` only after focus leaves
108
204
  the complete component. Text or tokens keep the field expanded even when that
109
205
  transient state is false. Width animation is disabled under reduced motion.
110
206
 
207
+ When the field is composed with a caller-owned surface rendered outside it — a
208
+ suggestions listbox, date picker, or help popover — mark that surface (or wrap
209
+ it) with `data-token-search-keep-open`, or pass `collapsible.keepOpenOn(target)`,
210
+ so focus or pointer activation moving into it does not collapse an empty field.
211
+ The helper preserves the pointer target when a browser omits
212
+ `focusout.relatedTarget`, preventing the field or target from disappearing
213
+ between pointerdown and click. `wireTokenSearchFields`
214
+ can also, opt-in via `keyboard`, own atomic-chip editing keys: from a collapsed
215
+ caret with no selection, Backspace removes the token before the caret and Delete
216
+ the token after it (reported through `onRemoveToken` for the app to apply to its
217
+ controlled state), and ArrowRight moves the caret past a trailing chip so typed
218
+ text lands after it. An optional `onEdit({ id, editor, event })` — with the
219
+ originating `InputEvent` so a caller can gate on `inputType`/`data` — fires on every editor
220
+ `input`, letting a caller drop its own `input` listener; the application still
221
+ owns query parsing and result-count/loading announcements.
222
+
223
+ Managed clear captures the action before application handlers run, keeps the adopted
224
+ expanded signal open during editor replacement, and restores focus at the actual
225
+ mutation checkpoint before the next input task. It also returns keyboard activation
226
+ from the clear button to a surviving editor immediately. No animation frame owns
227
+ clear focus, so fast typing cannot escape to page shortcuts or lose its first letter. The app still owns clearing query/tokens and emptying
228
+ DOM-owned text. This focus step respects `manageFocus: false`, disposal, removed fields,
229
+ and focus deliberately moved to another control; no app-level reopen callback is needed.
230
+
231
+ ## CollapsiblePanel / sidebar
232
+
233
+ `CollapsiblePanel` is a labeled `aside` region that is `aria-hidden` while
234
+ collapsed; its content slides via transform (disabled under reduced motion).
235
+ `CollapsiblePanelToggle` is a named button carrying `aria-expanded` and the
236
+ standard per-side collapse/expand glyph (`PanelLeft*` / `PanelRight*` /
237
+ `PanelBottom*`); place a collapse toggle inside the panel and an expand toggle in
238
+ an always-visible location so it is reachable while collapsed. `wireSidebar()`
239
+ moves focus into the panel when it opens and restores it to the trigger when it
240
+ closes. When a `deviceClass()` reports `compact`, an open panel becomes an overlay
241
+ with a dismissable backdrop, Escape and backdrop-click collapse it, and Tab is
242
+ trapped within it (the ARIA dialog pattern). The application owns each `collapsed`
243
+ signal, the panels, their sizes, and content; the wire may persist the collapsed
244
+ state per panel.
245
+
111
246
  ## Verification matrix
112
247
 
113
248
  For each changed component, inspect default, hover, focus, disabled, selected/pressed, busy/error, long-content, wide, narrow, light, dark, increased-contrast, reduced-motion, keyboard-only, and 200%-zoom states where applicable. DOM order must match reading and focus order, with no clipping or unreachable action.
@@ -0,0 +1,61 @@
1
+ # Choosing an app layout
2
+
3
+ `@kerfjs/ui` ships four opt-in, tree-shakeable whole-screen layouts plus the
4
+ [`device-class`](device-class.md) signal that drives their responsive behavior.
5
+ This guide maps a **data + interaction + device** situation to the layout to
6
+ reach for, and states the device-class threshold at which the presentation
7
+ changes. The layouts:
8
+
9
+ - [`NavStack`](nav-stack.md) — push/pop navigation (a single pane is a one-entry stack).
10
+ - [`SplitView`](split-view.md) — list-detail (two panes, collapsing to a stack).
11
+ - [`Workbench`](workbench.md) — the Xcode-like collapsible rails + drawer.
12
+ - [`TabScaffold`](tab-scaffold.md) — the iOS bottom tab bar (each tab a stack).
13
+ - [`CollapsiblePanel` + `wireSidebar`](collapsible-panel.md) — a standalone
14
+ collapsible rail or bottom drawer (with `CollapsiblePanelToggle`), outside a full
15
+ shell: the standard collapse animation, icon convention, and `wireSidebar`
16
+ semantics (focus, compact overlay, persistence).
17
+
18
+ Derive responsiveness from `deviceClass()`: `compact` (a handset or portrait
19
+ tablet) means "one pane at a time"; `atLeast('tablet')` / `atLeast('desktop')`
20
+ gate the roomier presentations.
21
+
22
+ ## Decision matrix
23
+
24
+ | Situation | Layout | Device threshold |
25
+ | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
26
+ | Simple app, a few flat sections | `NavStack` with one entry (single pane); add `TabScaffold` for 2–5 co-equal sections on handset | `TabScaffold` on `compact`; promote its tabs to a `Workbench` rail / sidebar `atLeast('desktop')` |
27
+ | Drill-down browsing (list → item → sub-item) | `NavStack`; upgrade to `SplitView` once list + detail fit together | `SplitView` two-pane `atLeast('tablet')` landscape / non-`compact`; `NavStack` form on `compact` |
28
+ | Two related panes, selecting on the left updates the right | `SplitView` | two panes when not `compact`; collapses to `NavStack` (list → detail) on `compact` |
29
+ | Complex tool / editor with peripheral panels (navigator, inspector, console) | `Workbench` | full three-panel `atLeast('desktop')`; on smaller classes present the rails via `NavStack` / overlay drawers, not a shrunken shell |
30
+ | Mobile app with 2–5 top-level destinations, each its own drill-down | `TabScaffold`, each tab a `NavStack` | bottom bar on `compact`; promote to a rail / sidebar `atLeast('desktop')` |
31
+
32
+ ### Worked examples
33
+
34
+ - **Settings screen (simple):** one `NavStack` entry per screen; push a subpage
35
+ on tap. No `SplitView`/`Workbench` — it is a single flow.
36
+ - **Mail (drill-down + two-pane):** `SplitView` with `list={<ThreadList/>}` and
37
+ `detail={<Message/>}`, `compact={device.value.compact}`,
38
+ `detailActive={selected != null}`. On desktop both panes show with a resizable
39
+ separator; on a phone it is a `NavStack` (threads → message, back clears the
40
+ selection).
41
+ - **IDE (complex tool):** `Workbench` with a left navigator rail, a right
42
+ inspector rail, and a bottom console drawer, each `collapsed` bound to a
43
+ signal. Only offer this `atLeast('desktop')`.
44
+ - **Social app (tabbed):** `TabScaffold` with Home / Search / Profile tabs, each
45
+ `content` a `NavStack`. On a tablet/desktop, render the same sections as a
46
+ `Workbench` left rail instead of a bottom bar.
47
+
48
+ ## Dialogs
49
+
50
+ Pick the dialog's inner layout by the same complexity axis, then apply the device
51
+ class to how it is presented (compose with [`overlay`](../../docs/19-native-overlay-backing.md)):
52
+
53
+ - **desktop:** an inline dialog — a `SplitView` two-pane body, or a `NavStack`
54
+ for a wizard.
55
+ - **portrait tablet / handset:** present a `SplitView`/complex dialog as a
56
+ full-screen modal (its `compact` `NavStack` form).
57
+ - **landscape tablet:** a large partial-cover modal (does not need to go full
58
+ screen).
59
+
60
+ A `NavStack` works as a dialog body at every size — a wizard pushes and pops its
61
+ steps with cross-faded chrome.