@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,205 @@
1
+ import '@kerfjs/ui/layout.css';
2
+ import '@kerfjs/ui/webawesome.css';
3
+ import '@awesome.me/webawesome/dist/components/button/button.js';
4
+ import '@awesome.me/webawesome/dist/components/dialog/dialog.js';
5
+ import './recipes.css';
6
+
7
+ import { ListHeader } from '@kerfjs/ui/list-header';
8
+ import { ListItem } from '@kerfjs/ui/list-item';
9
+ import { LucideIcon } from '@kerfjs/ui/lucide-icon';
10
+ import { Text } from '@kerfjs/ui/text';
11
+ import { Toolbar } from '@kerfjs/ui/toolbar';
12
+ import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
13
+ import { ToolbarText } from '@kerfjs/ui/toolbar-text';
14
+ import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
15
+ import { signal } from 'kerfjs';
16
+ import { FileText } from 'lucide';
17
+
18
+ import type { RecipeFactory } from './types.js';
19
+
20
+ const records = {
21
+ alpha: {
22
+ name: 'Northstar migration',
23
+ owner: 'Mara Chen',
24
+ state: 'In review',
25
+ updated: 'Today, 09:42',
26
+ },
27
+ beta: {
28
+ name: 'Tablet navigation',
29
+ owner: 'Sam Rivera',
30
+ state: 'Ready',
31
+ updated: 'Yesterday, 16:20',
32
+ },
33
+ gamma: {
34
+ name: 'Contrast audit',
35
+ owner: 'Inez Okafor',
36
+ state: 'Blocked',
37
+ updated: 'Monday, 11:05',
38
+ },
39
+ } as const;
40
+
41
+ interface RecipeDialogElement extends HTMLElement {
42
+ open: boolean;
43
+ }
44
+
45
+ export const createRecipe: RecipeFactory = (announce) => {
46
+ const selected = signal<keyof typeof records>('alpha');
47
+ const open = { value: false };
48
+ const render = () => (
49
+ <section
50
+ class="kui-recipe kui-content"
51
+ data-recipe="recipe-list-detail-dialog"
52
+ >
53
+ <button
54
+ class="kui-recipe__button kui-content-item"
55
+ data-primary="true"
56
+ type="button"
57
+ data-action="recipe-action"
58
+ data-recipe-command="open"
59
+ >
60
+ Open project details
61
+ </button>
62
+ <Text variant="p" class="kui-recipe__ownership kui-content-item">
63
+ Web Awesome owns modal focus and dismissal. The recipe owns
64
+ header/body/list-detail anatomy; the app owns open state, selection, and
65
+ policy.
66
+ </Text>
67
+ <wa-dialog
68
+ class="recipe-dialog"
69
+ label="Project details"
70
+ without-header
71
+ open={open.value}
72
+ >
73
+ <div class="recipe-dialog__pane kui-pane">
74
+ <div class="recipe-list-detail kui-pane__content">
75
+ <nav
76
+ class="recipe-list-detail__list kui-pane"
77
+ aria-label="Projects"
78
+ >
79
+ <ListHeader label="Recent projects" />
80
+ <div class="kui-pane__content kui-content">
81
+ <section>
82
+ {Object.entries(records).map(([id, record]) => (
83
+ <ListItem
84
+ action="recipe-action"
85
+ itemId={id}
86
+ label={record.name}
87
+ selected={selected.value === id}
88
+ multiline
89
+ />
90
+ ))}
91
+ </section>
92
+ </div>
93
+ </nav>
94
+ <section class="recipe-list-detail__detail kui-pane">
95
+ <Toolbar
96
+ label="Project details"
97
+ dividerSides=""
98
+ leading={
99
+ <>
100
+ <ToolbarControlGroup appearance="borderless" single>
101
+ <LucideIcon icon={FileText} name="file-text" />
102
+ </ToolbarControlGroup>
103
+ <ToolbarText
104
+ text="Project details"
105
+ size="xlarge"
106
+ id="recipe-dialog-title"
107
+ />
108
+ </>
109
+ }
110
+ trailing={
111
+ <ToolbarControlGroup appearance="borderless" single>
112
+ <wa-button
113
+ appearance="plain"
114
+ data-action="recipe-action"
115
+ data-recipe-command="close"
116
+ >
117
+ Close
118
+ </wa-button>
119
+ </ToolbarControlGroup>
120
+ }
121
+ />
122
+ <Text
123
+ variant="p"
124
+ class="kui-recipe__heading-summary"
125
+ id="recipe-dialog-summary"
126
+ >
127
+ Compare delivery state without leaving the workspace.
128
+ </Text>
129
+ <div
130
+ class="recipe-list-detail__body kui-pane__content kui-content"
131
+ aria-live="polite"
132
+ >
133
+ <Text
134
+ variant="h3"
135
+ class="recipe-list-detail__title kui-recipe__pane-title kui-content-item"
136
+ >
137
+ {records[selected.value].name}
138
+ </Text>
139
+ <ValueTable label="Project details">
140
+ <ValueTableRow
141
+ label="Owner"
142
+ value={records[selected.value].owner}
143
+ />
144
+ <ValueTableRow
145
+ label="Status"
146
+ value={records[selected.value].state}
147
+ />
148
+ <ValueTableRow
149
+ label="Updated"
150
+ value={records[selected.value].updated}
151
+ />
152
+ </ValueTable>
153
+ <div class="recipe-list-detail__actions kui-control-cluster">
154
+ <wa-button
155
+ appearance="outlined"
156
+ data-action="recipe-action"
157
+ data-recipe-command="archive"
158
+ >
159
+ Archive
160
+ </wa-button>
161
+ <wa-button
162
+ variant="brand"
163
+ appearance="accent"
164
+ data-action="recipe-action"
165
+ data-recipe-command="open-record"
166
+ >
167
+ Open project
168
+ </wa-button>
169
+ </div>
170
+ </div>
171
+ </section>
172
+ </div>
173
+ </div>
174
+ </wa-dialog>
175
+ </section>
176
+ );
177
+ return {
178
+ render,
179
+ action(command, element) {
180
+ const id = element.dataset.itemId;
181
+ if (id && id in records) {
182
+ selected.value = id as keyof typeof records;
183
+ announce(`Selected ${records[selected.value].name}`);
184
+ return;
185
+ }
186
+ const dialog = element
187
+ .closest('[data-recipe]')
188
+ ?.querySelector<RecipeDialogElement>('wa-dialog');
189
+ if (command === 'open' && dialog) {
190
+ element.focus();
191
+ open.value = true;
192
+ dialog.open = true;
193
+ return;
194
+ }
195
+ if (command === 'close' && dialog) {
196
+ dialog.open = false;
197
+ return;
198
+ }
199
+ announce(`${command} requested`);
200
+ },
201
+ afterHide() {
202
+ open.value = false;
203
+ },
204
+ };
205
+ };
@@ -3,24 +3,191 @@ import './recipes.css';
3
3
 
4
4
  import { EmptyState } from '@kerfjs/ui/empty-state';
5
5
  import { LoadingSpinner } from '@kerfjs/ui/loading-spinner';
6
- import { PageHeader } from '@kerfjs/ui/page-header';
7
6
  import { StateBanner } from '@kerfjs/ui/state-banner';
7
+ import { Text } from '@kerfjs/ui/text';
8
+ import { Toolbar } from '@kerfjs/ui/toolbar';
9
+ import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
10
+ import { ToolbarText } from '@kerfjs/ui/toolbar-text';
8
11
  import { signal } from 'kerfjs';
9
12
 
10
13
  import type { RecipeFactory } from './types.js';
11
14
 
12
15
  type ListState = 'loading' | 'empty' | 'populated' | 'stale' | 'error';
13
- const items = ['Audit keyboard focus order', 'Verify 200% zoom layout', 'Publish package migration notes'];
16
+ const items = [
17
+ 'Audit keyboard focus order',
18
+ 'Verify 200% zoom layout',
19
+ 'Publish package migration notes',
20
+ ];
14
21
 
15
22
  export const createRecipe: RecipeFactory = (announce) => {
16
23
  const state = signal<ListState>('loading');
17
- const populated = () => <ul class="recipe-list__items" aria-label="Release tasks">{items.map((item, index) => <li><strong>{item}</strong><span class="kui-recipe__muted">{index === 0 ? 'In review · Mara' : index === 1 ? 'Ready · Sam' : 'Draft · Inez'}</span></li>)}</ul>;
24
+ const populated = () => (
25
+ <ul class="recipe-list__items" aria-label="Release tasks">
26
+ {items.map((item, index) => (
27
+ <li>
28
+ <strong>{item}</strong>
29
+ <span class="kui-recipe__muted">
30
+ {index === 0
31
+ ? 'In review · Mara'
32
+ : index === 1
33
+ ? 'Ready · Sam'
34
+ : 'Draft · Inez'}
35
+ </span>
36
+ </li>
37
+ ))}
38
+ </ul>
39
+ );
18
40
  const renderBody = () => {
19
- if (state.value === 'loading') return <EmptyState title="Loading release tasks" detail="The current workspace will appear when data is ready." busy action={<button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="load">Complete load</button>} />;
20
- if (state.value === 'empty') return <EmptyState title="No release tasks" detail="Create the first task for this milestone." action={<button class="kui-recipe__button" data-primary="true" type="button" data-action="recipe-action" data-recipe-command="create">Create task</button>} />;
21
- if (state.value === 'error') return <StateBanner title="Release tasks could not be refreshed" detail="Existing filters are preserved. Try again when the connection recovers." tone="danger" urgency="alert" action={<button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="retry">Retry</button>} />;
22
- return <>{state.value === 'stale' && <StateBanner title="Showing saved results" detail="Refreshing in the background." tone="warning" action={<span class="kui-inline-metadata"><LoadingSpinner label="Refreshing release tasks" /><button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="finish">Finish refresh</button></span>} />}{populated()}</>;
41
+ if (state.value === 'loading')
42
+ return (
43
+ <EmptyState
44
+ title="Loading release tasks"
45
+ detail="The current workspace will appear when data is ready."
46
+ busy
47
+ action={
48
+ <button
49
+ class="kui-recipe__button"
50
+ type="button"
51
+ data-action="recipe-action"
52
+ data-recipe-command="load"
53
+ >
54
+ Complete load
55
+ </button>
56
+ }
57
+ />
58
+ );
59
+ if (state.value === 'empty')
60
+ return (
61
+ <EmptyState
62
+ title="No release tasks"
63
+ detail="Create the first task for this milestone."
64
+ action={
65
+ <button
66
+ class="kui-recipe__button"
67
+ data-primary="true"
68
+ type="button"
69
+ data-action="recipe-action"
70
+ data-recipe-command="create"
71
+ >
72
+ Create task
73
+ </button>
74
+ }
75
+ />
76
+ );
77
+ if (state.value === 'error')
78
+ return (
79
+ <StateBanner
80
+ title="Release tasks could not be refreshed"
81
+ detail="Existing filters are preserved. Try again when the connection recovers."
82
+ tone="danger"
83
+ urgency="alert"
84
+ action={
85
+ <button
86
+ class="kui-recipe__button"
87
+ type="button"
88
+ data-action="recipe-action"
89
+ data-recipe-command="retry"
90
+ >
91
+ Retry
92
+ </button>
93
+ }
94
+ />
95
+ );
96
+ return (
97
+ <>
98
+ {state.value === 'stale' && (
99
+ <StateBanner
100
+ title="Showing saved results"
101
+ detail="Refreshing in the background."
102
+ tone="warning"
103
+ action={
104
+ <span class="kui-inline-metadata">
105
+ <LoadingSpinner label="Refreshing release tasks" />
106
+ <button
107
+ class="kui-recipe__button"
108
+ type="button"
109
+ data-action="recipe-action"
110
+ data-recipe-command="finish"
111
+ >
112
+ Finish refresh
113
+ </button>
114
+ </span>
115
+ }
116
+ />
117
+ )}
118
+ {populated()}
119
+ </>
120
+ );
121
+ };
122
+ const render = () => (
123
+ <section
124
+ class="kui-recipe recipe-list kui-recipe__surface kui-pane"
125
+ data-recipe="recipe-list-workspace-states"
126
+ data-list-state={state.value}
127
+ >
128
+ <Toolbar
129
+ label="Release tasks"
130
+ dividerSides=""
131
+ leading={
132
+ <ToolbarText
133
+ text="Release tasks"
134
+ size="xlarge"
135
+ id="recipe-list-title"
136
+ />
137
+ }
138
+ trailing={
139
+ <ToolbarControlGroup appearance="borderless">
140
+ <button
141
+ class="kui-recipe__button"
142
+ type="button"
143
+ data-action="recipe-action"
144
+ data-recipe-command="empty"
145
+ >
146
+ Clear
147
+ </button>
148
+ <button
149
+ class="kui-recipe__button"
150
+ type="button"
151
+ data-action="recipe-action"
152
+ data-recipe-command="fail"
153
+ >
154
+ Simulate failure
155
+ </button>
156
+ <button
157
+ class="kui-recipe__button"
158
+ data-primary="true"
159
+ type="button"
160
+ data-action="recipe-action"
161
+ data-recipe-command="refresh"
162
+ >
163
+ Refresh
164
+ </button>
165
+ </ToolbarControlGroup>
166
+ }
167
+ />
168
+ <div class="recipe-list__body kui-pane__content kui-content">
169
+ {renderBody()}
170
+ <Text variant="p" class="kui-recipe__ownership kui-content-item">
171
+ The recipe owns feedback placement and stable content. The app owns
172
+ fetching, cache age, retry policy, and domain rows.
173
+ </Text>
174
+ </div>
175
+ </section>
176
+ );
177
+ return {
178
+ render,
179
+ action(command) {
180
+ const next: Record<string, ListState> = {
181
+ load: 'populated',
182
+ empty: 'empty',
183
+ create: 'populated',
184
+ fail: 'error',
185
+ retry: 'populated',
186
+ refresh: 'stale',
187
+ finish: 'populated',
188
+ };
189
+ if (command in next) state.value = next[command]!;
190
+ announce(`List state: ${state.value}`);
191
+ },
23
192
  };
24
- const render = () => <section class="kui-recipe recipe-list kui-recipe__surface kui-pane" data-recipe="recipe-list-workspace-states" data-list-state={state.value}><PageHeader title="Release tasks" action={<div class="kui-control-cluster"><button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="empty">Clear</button><button class="kui-recipe__button" type="button" data-action="recipe-action" data-recipe-command="fail">Simulate failure</button><button class="kui-recipe__button" data-primary="true" type="button" data-action="recipe-action" data-recipe-command="refresh">Refresh</button></div>} /><div class="recipe-list__body kui-pane__content kui-content">{renderBody()}<p class="kui-recipe__ownership kui-content-item">The recipe owns feedback placement and stable content. The app owns fetching, cache age, retry policy, and domain rows.</p></div></section>;
25
- return { render, action(command) { const next: Record<string, ListState> = { load: 'populated', empty: 'empty', create: 'populated', fail: 'error', retry: 'populated', refresh: 'stale', finish: 'populated' }; if (command in next) state.value = next[command]!; announce(`List state: ${state.value}`); } };
26
193
  };
@@ -4,22 +4,31 @@ export const recipeIds = [
4
4
  'recipe-app-shell',
5
5
  'recipe-navigation-sidebar',
6
6
  'recipe-workspace-header',
7
- 'recipe-master-detail-dialog',
7
+ 'recipe-list-detail-dialog',
8
8
  'recipe-composer-form',
9
9
  'recipe-list-workspace-states',
10
10
  'recipe-compact-toolbar',
11
+ 'recipe-navigation-stack',
12
+ 'recipe-loading-inspector',
13
+ 'recipe-collapsible-sidebar',
11
14
  ] as const;
12
15
 
13
- export type RecipeId = typeof recipeIds[number];
16
+ export type RecipeId = (typeof recipeIds)[number];
14
17
 
15
- export const recipeLoaders: Record<RecipeId, () => Promise<{ createRecipe: RecipeFactory }>> = {
18
+ export const recipeLoaders: Record<
19
+ RecipeId,
20
+ () => Promise<{ createRecipe: RecipeFactory }>
21
+ > = {
16
22
  'recipe-app-shell': () => import('./app-shell.js'),
17
23
  'recipe-navigation-sidebar': () => import('./navigation-sidebar.js'),
18
24
  'recipe-workspace-header': () => import('./workspace-header.js'),
19
- 'recipe-master-detail-dialog': () => import('./master-detail-dialog.js'),
25
+ 'recipe-list-detail-dialog': () => import('./list-detail-dialog.js'),
20
26
  'recipe-composer-form': () => import('./composer-form.js'),
21
27
  'recipe-list-workspace-states': () => import('./list-workspace-states.js'),
22
28
  'recipe-compact-toolbar': () => import('./compact-toolbar.js'),
29
+ 'recipe-navigation-stack': () => import('./navigation-stack.js'),
30
+ 'recipe-loading-inspector': () => import('./loading-inspector.js'),
31
+ 'recipe-collapsible-sidebar': () => import('./collapsible-sidebar.js'),
23
32
  };
24
33
 
25
34
  export function isRecipeId(value: string): value is RecipeId {
@@ -0,0 +1,191 @@
1
+ import '@kerfjs/ui/layout.css';
2
+ import '@kerfjs/ui/select/register';
3
+ import './recipes.css';
4
+
5
+ import { em } from '@kerfjs/ui/css-values';
6
+ import { ListItem } from '@kerfjs/ui/list-item';
7
+ import { LucideIcon } from '@kerfjs/ui/lucide-icon';
8
+ import { SegmentedControl } from '@kerfjs/ui/segmented-control';
9
+ import { Select } from '@kerfjs/ui/select';
10
+ import { Skeleton } from '@kerfjs/ui/skeleton';
11
+ import { StateBanner } from '@kerfjs/ui/state-banner';
12
+ import { Text } from '@kerfjs/ui/text';
13
+ import { Toolbar } from '@kerfjs/ui/toolbar';
14
+ import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
15
+ import { ToolbarText } from '@kerfjs/ui/toolbar-text';
16
+ import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
17
+ import { signal } from 'kerfjs';
18
+ import { Columns3, FileText, Inbox, List } from 'lucide';
19
+
20
+ import type { RecipeFactory } from './types.js';
21
+
22
+ const icon = (node: Parameters<typeof LucideIcon>[0]['icon'], name: string) => (
23
+ <LucideIcon icon={node} name={name} />
24
+ );
25
+
26
+ /**
27
+ * A record inspector whose per-record values load asynchronously. Every
28
+ * value-bearing component takes `placeholder` from one loading flag, so the same
29
+ * chrome renders a faithful loading state and then the populated record — no
30
+ * separate skeleton markup. The composition is the point; `Skeleton` is the
31
+ * primitive it is built on (see the Skeleton component demo).
32
+ */
33
+ export const createRecipe: RecipeFactory = (announce) => {
34
+ const loading = signal(true);
35
+ const render = () => {
36
+ const p = loading.value;
37
+ return (
38
+ <section
39
+ class="kui-recipe recipe-inspector kui-recipe__surface kui-pane"
40
+ data-recipe="recipe-loading-inspector"
41
+ data-inspector-loading={String(p)}
42
+ >
43
+ <Toolbar
44
+ label="Ticket inspector"
45
+ dividerSides=""
46
+ leading={
47
+ <>
48
+ <ToolbarControlGroup appearance="borderless" single>
49
+ {icon(FileText, 'file-text')}
50
+ </ToolbarControlGroup>
51
+ <ToolbarText
52
+ text="Ticket · KF-2048"
53
+ size="xlarge"
54
+ id="recipe-inspector-title"
55
+ placeholder={p}
56
+ />
57
+ </>
58
+ }
59
+ trailing={
60
+ <ToolbarControlGroup appearance="borderless" single>
61
+ <button
62
+ class="kui-recipe__button"
63
+ data-primary="true"
64
+ type="button"
65
+ data-action="recipe-action"
66
+ data-recipe-command="toggle"
67
+ >
68
+ {p ? 'Show loaded' : 'Show loading'}
69
+ </button>
70
+ </ToolbarControlGroup>
71
+ }
72
+ />
73
+ <Text
74
+ variant="p"
75
+ class="kui-recipe__heading-summary"
76
+ id="recipe-inspector-summary"
77
+ >
78
+ {p ? (
79
+ <Skeleton width={em(18)} />
80
+ ) : (
81
+ 'Restore keyboard focus after a dialog closes'
82
+ )}
83
+ </Text>
84
+ <div class="recipe-inspector__body kui-pane__content kui-content">
85
+ <section>
86
+ <ValueTable label="Ticket details">
87
+ <ValueTableRow
88
+ label="Status"
89
+ value="In review"
90
+ icon={icon(Inbox, 'inbox')}
91
+ placeholder={p}
92
+ />
93
+ <ValueTableRow label="Owner" value="Mara Lopez" placeholder={p} />
94
+ <ValueTableRow label="Priority" value="High" placeholder={p} />
95
+ <ValueTableRow
96
+ label="Updated"
97
+ value="2 hours ago"
98
+ placeholder={p}
99
+ />
100
+ </ValueTable>
101
+ </section>
102
+ <div class="recipe-inspector__controls">
103
+ <Select
104
+ name="recipe-inspector-status"
105
+ value="review"
106
+ label="Status"
107
+ placeholder={p}
108
+ choices={[
109
+ { value: 'review', label: 'In review' },
110
+ { value: 'ready', label: 'Ready' },
111
+ { value: 'done', label: 'Done' },
112
+ ]}
113
+ />
114
+ <SegmentedControl
115
+ id="recipe-inspector-view"
116
+ label="Inspector view"
117
+ value="details"
118
+ appearance="toolbar"
119
+ shape="pill"
120
+ size="small"
121
+ placeholder={p}
122
+ choices={[
123
+ {
124
+ value: 'details',
125
+ label: 'Details',
126
+ content: icon(List, 'list'),
127
+ },
128
+ {
129
+ value: 'activity',
130
+ label: 'Activity',
131
+ content: icon(Columns3, 'columns-3'),
132
+ },
133
+ {
134
+ value: 'files',
135
+ label: 'Files',
136
+ content: icon(FileText, 'file-text'),
137
+ },
138
+ ]}
139
+ />
140
+ </div>
141
+ <section>
142
+ <ListItem
143
+ action="recipe-action"
144
+ itemId="reassign"
145
+ label="Reassign ticket"
146
+ icon={icon(Inbox, 'inbox')}
147
+ placeholder={p}
148
+ />
149
+ <ListItem
150
+ action="recipe-action"
151
+ itemId="watch"
152
+ label="Watch for changes"
153
+ placeholder={p}
154
+ />
155
+ </section>
156
+ {p ? (
157
+ <StateBanner
158
+ tone="info"
159
+ title=""
160
+ detail=""
161
+ icon={icon(FileText, 'file-text')}
162
+ placeholder
163
+ />
164
+ ) : (
165
+ <StateBanner
166
+ tone="success"
167
+ title="Up to date"
168
+ detail="All checks passed on the latest revision."
169
+ icon={icon(FileText, 'file-text')}
170
+ />
171
+ )}
172
+ <Text variant="p" class="kui-recipe__ownership kui-content-item">
173
+ Each value-bearing component's <code>placeholder</code> prop renders
174
+ skeletons in its value slots while loading, so the recipe composes a
175
+ faithful loading inspector from real chrome. The app owns the
176
+ loading lifecycle and which values are still unknown.
177
+ </Text>
178
+ </div>
179
+ </section>
180
+ );
181
+ };
182
+ return {
183
+ render,
184
+ action(command) {
185
+ if (command === 'toggle') {
186
+ loading.value = !loading.value;
187
+ announce(loading.value ? 'Inspector is loading' : 'Inspector loaded');
188
+ }
189
+ },
190
+ };
191
+ };