@kerfjs/ui 5.0.0-beta.3 → 5.0.0-beta.31

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 (470) hide show
  1. package/README.md +320 -107
  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 +131 -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 +486 -0
  17. package/ai/component-catalog-v2.d.ts +90 -0
  18. package/ai/component-catalog-v2.json +5455 -0
  19. package/ai/component-catalog-v2.schema.json +298 -0
  20. package/ai/component-catalog.json +2437 -419
  21. package/ai/component-catalog.schema.json +39 -0
  22. package/ai/public-api-signatures-v1.md +1442 -124
  23. package/ai/skill.md +131 -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/{menu-header.js → list-header.js} +3 -2
  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/segmented-control.js +1 -0
  41. package/dist/browser/select.js +1 -0
  42. package/dist/browser/skeleton.js +3 -0
  43. package/dist/browser/state-banner.js +1 -0
  44. package/dist/browser/sunken-panel.js +3 -0
  45. package/dist/browser/surface-scaffold.js +3 -0
  46. package/dist/browser/toolbar-text.js +1 -0
  47. package/dist/browser/value-table.js +1 -0
  48. package/dist/catalog-resources.d.ts +35 -0
  49. package/dist/catalog-resources.js +22 -0
  50. package/dist/catalog-resources.js.map +1 -0
  51. package/dist/catalog.d.ts +151 -0
  52. package/dist/catalog.js +379 -0
  53. package/dist/catalog.js.map +1 -0
  54. package/dist/chunk-6NDBE2OK.js +32 -0
  55. package/dist/chunk-6NDBE2OK.js.map +1 -0
  56. package/dist/chunk-6WTETUZ6.js +21 -0
  57. package/dist/chunk-6WTETUZ6.js.map +1 -0
  58. package/dist/chunk-AQFWBG23.js +83 -0
  59. package/dist/chunk-AQFWBG23.js.map +1 -0
  60. package/dist/chunk-B6JYRX3P.js +51 -0
  61. package/dist/chunk-B6JYRX3P.js.map +1 -0
  62. package/dist/chunk-CAB6NQ2T.js +102 -0
  63. package/dist/chunk-CAB6NQ2T.js.map +1 -0
  64. package/dist/chunk-CK6P5AUK.js +136 -0
  65. package/dist/chunk-CK6P5AUK.js.map +1 -0
  66. package/dist/chunk-DAO2VFTT.js +129 -0
  67. package/dist/chunk-DAO2VFTT.js.map +1 -0
  68. package/dist/chunk-DYBPI5O2.js +25 -0
  69. package/dist/chunk-DYBPI5O2.js.map +1 -0
  70. package/dist/{chunk-TRNBHPNW.js → chunk-E34TE2ZI.js} +12 -8
  71. package/dist/chunk-E34TE2ZI.js.map +1 -0
  72. package/dist/chunk-I45QO2IZ.js +88 -0
  73. package/dist/chunk-I45QO2IZ.js.map +1 -0
  74. package/dist/chunk-I4V3UDSA.js +79 -0
  75. package/dist/chunk-I4V3UDSA.js.map +1 -0
  76. package/dist/chunk-KFUUFNTS.js +15 -0
  77. package/dist/chunk-KFUUFNTS.js.map +1 -0
  78. package/dist/chunk-MFR2IIFG.js +20 -0
  79. package/dist/chunk-MFR2IIFG.js.map +1 -0
  80. package/dist/chunk-OC275HHS.js +128 -0
  81. package/dist/chunk-OC275HHS.js.map +1 -0
  82. package/dist/chunk-OL4TC3U2.js +156 -0
  83. package/dist/chunk-OL4TC3U2.js.map +1 -0
  84. package/dist/chunk-OMSRSL3B.js +120 -0
  85. package/dist/chunk-OMSRSL3B.js.map +1 -0
  86. package/dist/chunk-RL6COPKF.js +44 -0
  87. package/dist/chunk-RL6COPKF.js.map +1 -0
  88. package/dist/chunk-SOK5EUF7.js +25 -0
  89. package/dist/chunk-SOK5EUF7.js.map +1 -0
  90. package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
  91. package/dist/chunk-SRSJO5QE.js.map +1 -0
  92. package/dist/chunk-T7DOWS6T.js +61 -0
  93. package/dist/chunk-T7DOWS6T.js.map +1 -0
  94. package/dist/chunk-TIGSG6BX.js +218 -0
  95. package/dist/chunk-TIGSG6BX.js.map +1 -0
  96. package/dist/chunk-UDHHKZWO.js +41 -0
  97. package/dist/chunk-UDHHKZWO.js.map +1 -0
  98. package/dist/{chunk-MQIZAD4A.js → chunk-UKKSMZYT.js} +76 -22
  99. package/dist/chunk-UKKSMZYT.js.map +1 -0
  100. package/dist/chunk-V6GQKOX2.js +39 -0
  101. package/dist/chunk-V6GQKOX2.js.map +1 -0
  102. package/dist/chunk-V6HCTCVD.js +46 -0
  103. package/dist/chunk-V6HCTCVD.js.map +1 -0
  104. package/dist/chunk-VDUJZF5G.js +515 -0
  105. package/dist/chunk-VDUJZF5G.js.map +1 -0
  106. package/dist/chunk-VXFGMDA7.js +31 -0
  107. package/dist/chunk-VXFGMDA7.js.map +1 -0
  108. package/dist/chunk-WN52USHV.js +115 -0
  109. package/dist/chunk-WN52USHV.js.map +1 -0
  110. package/dist/chunk-WSXUPSID.js +43 -0
  111. package/dist/chunk-WSXUPSID.js.map +1 -0
  112. package/dist/{chunk-56RJXPHG.js → chunk-Y6LYKFXY.js} +1 -1
  113. package/dist/{chunk-56RJXPHG.js.map → chunk-Y6LYKFXY.js.map} +1 -1
  114. package/dist/chunk-ZPWZIVI6.js +47 -0
  115. package/dist/chunk-ZPWZIVI6.js.map +1 -0
  116. package/dist/collapsible-panel.d.ts +77 -0
  117. package/dist/collapsible-panel.js +84 -0
  118. package/dist/collapsible-panel.js.map +1 -0
  119. package/dist/css-values.d.ts +75 -0
  120. package/dist/css-values.js +3 -0
  121. package/dist/css-values.js.map +1 -0
  122. package/dist/device-class.d.ts +62 -0
  123. package/dist/device-class.js +103 -0
  124. package/dist/device-class.js.map +1 -0
  125. package/dist/disclosure-arrow.js +1 -1
  126. package/dist/divider-sides-267FA7sY.d.ts +4 -0
  127. package/dist/empty-state.d.ts +3 -2
  128. package/dist/empty-state.js +1 -1
  129. package/dist/floating-toolbar.d.ts +29 -0
  130. package/dist/floating-toolbar.js +3 -0
  131. package/dist/floating-toolbar.js.map +1 -0
  132. package/dist/index.d.ts +23 -14
  133. package/dist/index.js +30 -23
  134. package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +19 -8
  135. package/dist/list-action-row.js +7 -0
  136. package/dist/list-action-row.js.map +1 -0
  137. package/dist/list-header.d.ts +77 -0
  138. package/dist/list-header.js +8 -0
  139. package/dist/list-header.js.map +1 -0
  140. package/dist/list-inset-control.d.ts +17 -0
  141. package/dist/list-inset-control.js +3 -0
  142. package/dist/list-inset-control.js.map +1 -0
  143. package/dist/list-inset-text.d.ts +26 -0
  144. package/dist/list-inset-text.js +3 -0
  145. package/dist/list-inset-text.js.map +1 -0
  146. package/dist/list-item.d.ts +45 -0
  147. package/dist/list-item.js +7 -0
  148. package/dist/list-item.js.map +1 -0
  149. package/dist/list.d.ts +21 -0
  150. package/dist/list.js +4 -0
  151. package/dist/list.js.map +1 -0
  152. package/dist/loading-spinner.js +1 -1
  153. package/dist/nav-stack.d.ts +41 -0
  154. package/dist/nav-stack.js +6 -0
  155. package/dist/nav-stack.js.map +1 -0
  156. package/dist/pane.d.ts +50 -0
  157. package/dist/pane.js +4 -0
  158. package/dist/pane.js.map +1 -0
  159. package/dist/resizable-region.d.ts +21 -3
  160. package/dist/resizable-region.js +1 -1
  161. package/dist/segmented-control.d.ts +11 -8
  162. package/dist/segmented-control.js +3 -1
  163. package/dist/select-register.d.ts +5 -1
  164. package/dist/select-register.js +3 -0
  165. package/dist/select-register.js.map +1 -1
  166. package/dist/select.d.ts +34 -8
  167. package/dist/select.js +3 -1
  168. package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
  169. package/dist/skeleton.d.ts +25 -0
  170. package/dist/skeleton.js +4 -0
  171. package/dist/skeleton.js.map +1 -0
  172. package/dist/split-view.d.ts +43 -0
  173. package/dist/split-view.js +82 -0
  174. package/dist/split-view.js.map +1 -0
  175. package/dist/state-banner.d.ts +11 -5
  176. package/dist/state-banner.js +3 -1
  177. package/dist/styles/app-tab.css +26 -0
  178. package/dist/styles/catalog.css +582 -0
  179. package/dist/styles/collapsible-panel.css +212 -0
  180. package/dist/styles/floating-toolbar.css +38 -0
  181. package/dist/styles/foundation.css +80 -0
  182. package/dist/styles/layout.css +2 -20
  183. package/dist/styles/list-action-row.css +224 -0
  184. package/dist/styles/list-header.css +198 -0
  185. package/dist/styles/list-inset-control.css +7 -0
  186. package/dist/styles/list-inset-text.css +14 -0
  187. package/dist/styles/list-item.css +171 -0
  188. package/dist/styles/list.css +43 -0
  189. package/dist/styles/nav-stack.css +141 -0
  190. package/dist/styles/pane.css +62 -0
  191. package/dist/styles/resizable-region.css +158 -0
  192. package/dist/styles/segmented-control.css +10 -7
  193. package/dist/styles/select.css +115 -2
  194. package/dist/styles/skeleton.css +31 -0
  195. package/dist/styles/split-view.css +35 -0
  196. package/dist/styles/state-banner.css +49 -10
  197. package/dist/styles/styles.css +7 -5
  198. package/dist/styles/sunken-panel.css +22 -0
  199. package/dist/styles/surface-scaffold.css +51 -0
  200. package/dist/styles/tab-bar.css +24 -0
  201. package/dist/styles/tab-scaffold.css +85 -0
  202. package/dist/styles/token-search-field.css +14 -2
  203. package/dist/styles/toolbar-control-group.css +224 -6
  204. package/dist/styles/toolbar-text.css +65 -2
  205. package/dist/styles/toolbar.css +47 -2
  206. package/dist/styles/webawesome.css +165 -0
  207. package/dist/styles/workbench.css +187 -0
  208. package/dist/sunken-panel.d.ts +20 -0
  209. package/dist/sunken-panel.js +3 -0
  210. package/dist/sunken-panel.js.map +1 -0
  211. package/dist/surface-scaffold.d.ts +26 -0
  212. package/dist/surface-scaffold.js +3 -0
  213. package/dist/surface-scaffold.js.map +1 -0
  214. package/dist/tab-bar.d.ts +24 -6
  215. package/dist/tab-bar.js +1 -1
  216. package/dist/tab-scaffold.d.ts +31 -0
  217. package/dist/tab-scaffold.js +51 -0
  218. package/dist/tab-scaffold.js.map +1 -0
  219. package/dist/token-search-field.d.ts +30 -13
  220. package/dist/token-search-field.js +1 -1
  221. package/dist/toolbar-control-group.d.ts +33 -7
  222. package/dist/toolbar-control-group.js +1 -1
  223. package/dist/toolbar-text.d.ts +31 -4
  224. package/dist/toolbar-text.js +3 -1
  225. package/dist/toolbar.d.ts +10 -7
  226. package/dist/toolbar.js +1 -1
  227. package/dist/value-table.d.ts +7 -3
  228. package/dist/value-table.js +3 -1
  229. package/dist/webawesome.d.ts +1 -0
  230. package/dist/wire-catalog.d.ts +57 -0
  231. package/dist/wire-catalog.js +302 -0
  232. package/dist/wire-catalog.js.map +1 -0
  233. package/dist/wire-nav-stack.d.ts +18 -0
  234. package/dist/wire-nav-stack.js +228 -0
  235. package/dist/wire-nav-stack.js.map +1 -0
  236. package/dist/wire-resizable-regions.d.ts +1 -1
  237. package/dist/wire-resizable-regions.js +1 -1
  238. package/dist/wire-sidebar.d.ts +48 -0
  239. package/dist/wire-sidebar.js +147 -0
  240. package/dist/wire-sidebar.js.map +1 -0
  241. package/dist/wire-tab-bars.d.ts +19 -2
  242. package/dist/wire-tab-bars.js +1 -1
  243. package/dist/wire-tab-scaffold.d.ts +11 -0
  244. package/dist/wire-tab-scaffold.js +21 -0
  245. package/dist/wire-tab-scaffold.js.map +1 -0
  246. package/dist/wire-token-search-fields.d.ts +104 -4
  247. package/dist/wire-token-search-fields.js +1 -1
  248. package/dist/workbench.d.ts +44 -0
  249. package/dist/workbench.js +106 -0
  250. package/dist/workbench.js.map +1 -0
  251. package/docs/accessibility.md +137 -20
  252. package/docs/app-layouts.md +61 -0
  253. package/docs/catalog.md +364 -0
  254. package/docs/collapsible-panel.md +142 -0
  255. package/docs/component-contract.md +253 -26
  256. package/docs/component-selection.md +213 -57
  257. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  258. package/docs/design/templates/empty-state/filtered.svg +1 -0
  259. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  260. package/docs/design/templates/empty-state/with-action.svg +1 -0
  261. package/docs/design/templates/empty-state-dark.svg +1 -0
  262. package/docs/design/templates/empty-state.svg +1 -0
  263. package/docs/design/templates/list/compact-dark.svg +1 -0
  264. package/docs/design/templates/list/compact.svg +1 -0
  265. package/docs/design/templates/list/stack-dark.svg +1 -0
  266. package/docs/design/templates/list/stack.svg +1 -0
  267. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  268. package/docs/design/templates/list-action-row/default.svg +1 -0
  269. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  270. package/docs/design/templates/list-action-row/selected.svg +1 -0
  271. package/docs/design/templates/list-action-row-dark.svg +1 -0
  272. package/docs/design/templates/list-action-row.svg +1 -0
  273. package/docs/design/templates/list-dark.svg +1 -0
  274. package/docs/design/templates/list-header/action-dark.svg +1 -0
  275. package/docs/design/templates/list-header/action.svg +1 -0
  276. package/docs/design/templates/list-header/count-dark.svg +1 -0
  277. package/docs/design/templates/list-header/count.svg +1 -0
  278. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  279. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  280. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  281. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  282. package/docs/design/templates/list-header-dark.svg +1 -0
  283. package/docs/design/templates/list-header.svg +1 -0
  284. package/docs/design/templates/list-item/default-dark.svg +1 -0
  285. package/docs/design/templates/list-item/default.svg +1 -0
  286. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  287. package/docs/design/templates/list-item/multiline.svg +1 -0
  288. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  289. package/docs/design/templates/list-item/selected.svg +1 -0
  290. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  291. package/docs/design/templates/list-item/trailing.svg +1 -0
  292. package/docs/design/templates/list-item-dark.svg +1 -0
  293. package/docs/design/templates/list-item.svg +1 -0
  294. package/docs/design/templates/list.svg +1 -0
  295. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  296. package/docs/design/templates/segmented-control/equal.svg +1 -0
  297. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  298. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  299. package/docs/design/templates/segmented-control-dark.svg +1 -0
  300. package/docs/design/templates/segmented-control.svg +1 -0
  301. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  302. package/docs/design/templates/skeleton/avatar.svg +1 -0
  303. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  304. package/docs/design/templates/skeleton/block.svg +1 -0
  305. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  306. package/docs/design/templates/skeleton/lines.svg +1 -0
  307. package/docs/design/templates/skeleton-dark.svg +1 -0
  308. package/docs/design/templates/skeleton.svg +1 -0
  309. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  310. package/docs/design/templates/state-banner/danger.svg +1 -0
  311. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  312. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  313. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  314. package/docs/design/templates/state-banner/info.svg +1 -0
  315. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  316. package/docs/design/templates/state-banner/success.svg +1 -0
  317. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  318. package/docs/design/templates/state-banner/warning.svg +1 -0
  319. package/docs/design/templates/state-banner-dark.svg +1 -0
  320. package/docs/design/templates/state-banner.svg +1 -0
  321. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  322. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  323. package/docs/design/templates/tab-bar-dark.svg +1 -0
  324. package/docs/design/templates/tab-bar.svg +1 -0
  325. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  326. package/docs/design/templates/token-search-field/empty.svg +1 -0
  327. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  328. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  329. package/docs/design/templates/token-search-field-dark.svg +1 -0
  330. package/docs/design/templates/token-search-field.svg +1 -0
  331. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  332. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  333. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  334. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  335. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  336. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  337. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  338. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  339. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  340. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  341. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  342. package/docs/design/templates/toolbar-control-group.svg +1 -0
  343. package/docs/design/templates/toolbar-dark.svg +1 -0
  344. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  345. package/docs/design/templates/toolbar-text/default.svg +1 -0
  346. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  347. package/docs/design/templates/toolbar-text/large.svg +1 -0
  348. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  349. package/docs/design/templates/toolbar-text/small.svg +1 -0
  350. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  351. package/docs/design/templates/toolbar-text.svg +1 -0
  352. package/docs/design/templates/toolbar.svg +1 -0
  353. package/docs/design/templates/value-table/details-dark.svg +1 -0
  354. package/docs/design/templates/value-table/details.svg +1 -0
  355. package/docs/design/templates/value-table-dark.svg +1 -0
  356. package/docs/design/templates/value-table.svg +1 -0
  357. package/docs/design/templates.md +128 -0
  358. package/docs/design-philosophy.md +33 -11
  359. package/docs/device-class.md +54 -0
  360. package/docs/examples/application-ui-profile.json +60 -0
  361. package/docs/examples/command-palette-adapter.tsx +67 -17
  362. package/docs/examples/component-catalog-extension-v2.json +80 -0
  363. package/docs/examples/component-catalog-extension.json +45 -0
  364. package/docs/layout.md +98 -37
  365. package/docs/nav-stack.md +77 -0
  366. package/docs/recipes.md +75 -19
  367. package/docs/split-view.md +55 -0
  368. package/docs/sunken-panel.md +53 -0
  369. package/docs/surface-scaffold.md +25 -0
  370. package/docs/tab-scaffold.md +52 -0
  371. package/docs/type-contracts.md +127 -0
  372. package/docs/ui-analyzer.md +118 -0
  373. package/docs/ui-doctor.md +91 -0
  374. package/docs/ui-evaluator.md +122 -0
  375. package/docs/ux-demo.md +24 -13
  376. package/docs/webawesome-theme.md +82 -8
  377. package/docs/workbench.md +123 -0
  378. package/doctor/cli.mjs +115 -0
  379. package/doctor/config.schema.json +68 -0
  380. package/doctor/eslint-diagnostics.d.mts +24 -0
  381. package/doctor/eslint-diagnostics.mjs +24 -0
  382. package/doctor/index.d.mts +116 -0
  383. package/doctor/index.mjs +1272 -0
  384. package/doctor/report.schema.json +118 -0
  385. package/evaluator/cli.mjs +89 -0
  386. package/evaluator/index.d.mts +90 -0
  387. package/evaluator/index.mjs +1138 -0
  388. package/evaluator/report.schema.json +152 -0
  389. package/llms.txt +253 -43
  390. package/package.json +180 -37
  391. package/traversal-exclusions.mjs +27 -0
  392. package/ux-demo/recipes/app-shell.tsx +222 -13
  393. package/ux-demo/recipes/collapsible-sidebar.tsx +229 -0
  394. package/ux-demo/recipes/compact-toolbar.tsx +115 -12
  395. package/ux-demo/recipes/composer-form.tsx +119 -18
  396. package/ux-demo/recipes/list-detail-dialog.tsx +197 -0
  397. package/ux-demo/recipes/list-workspace-states.tsx +175 -9
  398. package/ux-demo/recipes/loaders.ts +13 -4
  399. package/ux-demo/recipes/loading-inspector.tsx +186 -0
  400. package/ux-demo/recipes/mount-recipe.ts +31 -5
  401. package/ux-demo/recipes/navigation-sidebar.tsx +122 -11
  402. package/ux-demo/recipes/navigation-stack.tsx +118 -0
  403. package/ux-demo/recipes/recipes.css +143 -18
  404. package/ux-demo/recipes/types.ts +9 -1
  405. package/ux-demo/recipes/workspace-header.tsx +68 -3
  406. package/dist/browser/dialog-header.js +0 -5
  407. package/dist/browser/menu-action-row.js +0 -3
  408. package/dist/browser/menu-item.js +0 -3
  409. package/dist/browser/page-header.js +0 -3
  410. package/dist/chunk-2PES33HS.js +0 -13
  411. package/dist/chunk-2PES33HS.js.map +0 -1
  412. package/dist/chunk-55DATV3A.js +0 -10
  413. package/dist/chunk-55DATV3A.js.map +0 -1
  414. package/dist/chunk-7BR3DO2A.js.map +0 -1
  415. package/dist/chunk-7LDCTU7D.js +0 -134
  416. package/dist/chunk-7LDCTU7D.js.map +0 -1
  417. package/dist/chunk-AQNKJRFH.js +0 -79
  418. package/dist/chunk-AQNKJRFH.js.map +0 -1
  419. package/dist/chunk-CTQKI2XL.js +0 -28
  420. package/dist/chunk-CTQKI2XL.js.map +0 -1
  421. package/dist/chunk-DAFFDO33.js +0 -14
  422. package/dist/chunk-DAFFDO33.js.map +0 -1
  423. package/dist/chunk-EGHL5S4P.js +0 -33
  424. package/dist/chunk-EGHL5S4P.js.map +0 -1
  425. package/dist/chunk-GY5WH7TO.js +0 -10
  426. package/dist/chunk-GY5WH7TO.js.map +0 -1
  427. package/dist/chunk-H5AGGVU5.js +0 -75
  428. package/dist/chunk-H5AGGVU5.js.map +0 -1
  429. package/dist/chunk-K3G72I6D.js +0 -24
  430. package/dist/chunk-K3G72I6D.js.map +0 -1
  431. package/dist/chunk-M5JRI6MN.js +0 -27
  432. package/dist/chunk-M5JRI6MN.js.map +0 -1
  433. package/dist/chunk-MBR3TYNL.js +0 -14
  434. package/dist/chunk-MBR3TYNL.js.map +0 -1
  435. package/dist/chunk-MFKMHK5W.js +0 -10
  436. package/dist/chunk-MFKMHK5W.js.map +0 -1
  437. package/dist/chunk-MQIZAD4A.js.map +0 -1
  438. package/dist/chunk-NPPD2A6O.js +0 -40
  439. package/dist/chunk-NPPD2A6O.js.map +0 -1
  440. package/dist/chunk-R6JHHBKQ.js +0 -19
  441. package/dist/chunk-R6JHHBKQ.js.map +0 -1
  442. package/dist/chunk-S3TPM2LP.js +0 -23
  443. package/dist/chunk-S3TPM2LP.js.map +0 -1
  444. package/dist/chunk-TRNBHPNW.js.map +0 -1
  445. package/dist/chunk-VHW5YNIB.js +0 -17
  446. package/dist/chunk-VHW5YNIB.js.map +0 -1
  447. package/dist/chunk-WVPYVDXY.js +0 -15
  448. package/dist/chunk-WVPYVDXY.js.map +0 -1
  449. package/dist/chunk-XZXRLY5Q.js +0 -33
  450. package/dist/chunk-XZXRLY5Q.js.map +0 -1
  451. package/dist/dialog-header.d.ts +0 -15
  452. package/dist/dialog-header.js +0 -5
  453. package/dist/dialog-header.js.map +0 -1
  454. package/dist/menu-action-row.js +0 -4
  455. package/dist/menu-action-row.js.map +0 -1
  456. package/dist/menu-header.d.ts +0 -41
  457. package/dist/menu-header.js +0 -6
  458. package/dist/menu-header.js.map +0 -1
  459. package/dist/menu-item.d.ts +0 -31
  460. package/dist/menu-item.js +0 -4
  461. package/dist/menu-item.js.map +0 -1
  462. package/dist/page-header.d.ts +0 -9
  463. package/dist/page-header.js +0 -3
  464. package/dist/page-header.js.map +0 -1
  465. package/dist/styles/dialog-header.css +0 -87
  466. package/dist/styles/menu-action-row.css +0 -154
  467. package/dist/styles/menu-header.css +0 -143
  468. package/dist/styles/menu-item.css +0 -118
  469. package/dist/styles/page-header.css +0 -26
  470. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
@@ -0,0 +1,197 @@
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 { Toolbar } from '@kerfjs/ui/toolbar';
11
+ import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
12
+ import { ToolbarText } from '@kerfjs/ui/toolbar-text';
13
+ import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
14
+ import { signal } from 'kerfjs';
15
+ import { FileText } from 'lucide';
16
+
17
+ import type { RecipeFactory } from './types.js';
18
+
19
+ const records = {
20
+ alpha: {
21
+ name: 'Northstar migration',
22
+ owner: 'Mara Chen',
23
+ state: 'In review',
24
+ updated: 'Today, 09:42',
25
+ },
26
+ beta: {
27
+ name: 'Tablet navigation',
28
+ owner: 'Sam Rivera',
29
+ state: 'Ready',
30
+ updated: 'Yesterday, 16:20',
31
+ },
32
+ gamma: {
33
+ name: 'Contrast audit',
34
+ owner: 'Inez Okafor',
35
+ state: 'Blocked',
36
+ updated: 'Monday, 11:05',
37
+ },
38
+ } as const;
39
+
40
+ interface RecipeDialogElement extends HTMLElement {
41
+ open: boolean;
42
+ }
43
+
44
+ export const createRecipe: RecipeFactory = (announce) => {
45
+ const selected = signal<keyof typeof records>('alpha');
46
+ const open = { value: false };
47
+ const render = () => (
48
+ <section
49
+ class="kui-recipe kui-content"
50
+ data-recipe="recipe-list-detail-dialog"
51
+ >
52
+ <button
53
+ class="kui-recipe__button kui-content-item"
54
+ data-primary="true"
55
+ type="button"
56
+ data-action="recipe-action"
57
+ data-recipe-command="open"
58
+ >
59
+ Open project details
60
+ </button>
61
+ <p class="kui-recipe__ownership kui-content-item">
62
+ Web Awesome owns modal focus and dismissal. The recipe owns
63
+ header/body/list-detail anatomy; the app owns open state, selection, and
64
+ policy.
65
+ </p>
66
+ <wa-dialog
67
+ class="recipe-dialog"
68
+ label="Project details"
69
+ without-header
70
+ open={open.value}
71
+ >
72
+ <div class="recipe-dialog__pane kui-pane">
73
+ <div class="recipe-list-detail kui-pane__content">
74
+ <nav
75
+ class="recipe-list-detail__list kui-pane"
76
+ aria-label="Projects"
77
+ >
78
+ <ListHeader label="Recent projects" />
79
+ <div class="kui-pane__content kui-content">
80
+ <section>
81
+ {Object.entries(records).map(([id, record]) => (
82
+ <ListItem
83
+ action="recipe-action"
84
+ itemId={id}
85
+ label={record.name}
86
+ selected={selected.value === id}
87
+ multiline
88
+ />
89
+ ))}
90
+ </section>
91
+ </div>
92
+ </nav>
93
+ <section class="recipe-list-detail__detail kui-pane">
94
+ <Toolbar
95
+ label="Project details"
96
+ dividerSides=""
97
+ leading={
98
+ <>
99
+ <ToolbarControlGroup appearance="borderless" single>
100
+ <LucideIcon icon={FileText} name="file-text" />
101
+ </ToolbarControlGroup>
102
+ <ToolbarText
103
+ text="Project details"
104
+ size="xlarge"
105
+ id="recipe-dialog-title"
106
+ />
107
+ </>
108
+ }
109
+ trailing={
110
+ <ToolbarControlGroup appearance="borderless" single>
111
+ <wa-button
112
+ appearance="plain"
113
+ data-action="recipe-action"
114
+ data-recipe-command="close"
115
+ >
116
+ Close
117
+ </wa-button>
118
+ </ToolbarControlGroup>
119
+ }
120
+ />
121
+ <p class="kui-recipe__heading-summary" id="recipe-dialog-summary">
122
+ Compare delivery state without leaving the workspace.
123
+ </p>
124
+ <div
125
+ class="recipe-list-detail__body kui-pane__content kui-content"
126
+ aria-live="polite"
127
+ >
128
+ <h3 class="recipe-list-detail__title kui-recipe__pane-title kui-content-item">
129
+ {records[selected.value].name}
130
+ </h3>
131
+ <ValueTable label="Project details">
132
+ <ValueTableRow
133
+ label="Owner"
134
+ value={records[selected.value].owner}
135
+ />
136
+ <ValueTableRow
137
+ label="Status"
138
+ value={records[selected.value].state}
139
+ />
140
+ <ValueTableRow
141
+ label="Updated"
142
+ value={records[selected.value].updated}
143
+ />
144
+ </ValueTable>
145
+ <div class="recipe-list-detail__actions kui-control-cluster">
146
+ <wa-button
147
+ appearance="outlined"
148
+ data-action="recipe-action"
149
+ data-recipe-command="archive"
150
+ >
151
+ Archive
152
+ </wa-button>
153
+ <wa-button
154
+ variant="brand"
155
+ appearance="accent"
156
+ data-action="recipe-action"
157
+ data-recipe-command="open-record"
158
+ >
159
+ Open project
160
+ </wa-button>
161
+ </div>
162
+ </div>
163
+ </section>
164
+ </div>
165
+ </div>
166
+ </wa-dialog>
167
+ </section>
168
+ );
169
+ return {
170
+ render,
171
+ action(command, element) {
172
+ const id = element.dataset.itemId;
173
+ if (id && id in records) {
174
+ selected.value = id as keyof typeof records;
175
+ announce(`Selected ${records[selected.value].name}`);
176
+ return;
177
+ }
178
+ const dialog = element
179
+ .closest('[data-recipe]')
180
+ ?.querySelector<RecipeDialogElement>('wa-dialog');
181
+ if (command === 'open' && dialog) {
182
+ element.focus();
183
+ open.value = true;
184
+ dialog.open = true;
185
+ return;
186
+ }
187
+ if (command === 'close' && dialog) {
188
+ dialog.open = false;
189
+ return;
190
+ }
191
+ announce(`${command} requested`);
192
+ },
193
+ afterHide() {
194
+ open.value = false;
195
+ },
196
+ };
197
+ };
@@ -3,24 +3,190 @@ 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 { Toolbar } from '@kerfjs/ui/toolbar';
8
+ import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
9
+ import { ToolbarText } from '@kerfjs/ui/toolbar-text';
8
10
  import { signal } from 'kerfjs';
9
11
 
10
12
  import type { RecipeFactory } from './types.js';
11
13
 
12
14
  type ListState = 'loading' | 'empty' | 'populated' | 'stale' | 'error';
13
- const items = ['Audit keyboard focus order', 'Verify 200% zoom layout', 'Publish package migration notes'];
15
+ const items = [
16
+ 'Audit keyboard focus order',
17
+ 'Verify 200% zoom layout',
18
+ 'Publish package migration notes',
19
+ ];
14
20
 
15
21
  export const createRecipe: RecipeFactory = (announce) => {
16
22
  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>;
23
+ const populated = () => (
24
+ <ul class="recipe-list__items" aria-label="Release tasks">
25
+ {items.map((item, index) => (
26
+ <li>
27
+ <strong>{item}</strong>
28
+ <span class="kui-recipe__muted">
29
+ {index === 0
30
+ ? 'In review · Mara'
31
+ : index === 1
32
+ ? 'Ready · Sam'
33
+ : 'Draft · Inez'}
34
+ </span>
35
+ </li>
36
+ ))}
37
+ </ul>
38
+ );
18
39
  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()}</>;
40
+ if (state.value === 'loading')
41
+ return (
42
+ <EmptyState
43
+ title="Loading release tasks"
44
+ detail="The current workspace will appear when data is ready."
45
+ busy
46
+ action={
47
+ <button
48
+ class="kui-recipe__button"
49
+ type="button"
50
+ data-action="recipe-action"
51
+ data-recipe-command="load"
52
+ >
53
+ Complete load
54
+ </button>
55
+ }
56
+ />
57
+ );
58
+ if (state.value === 'empty')
59
+ return (
60
+ <EmptyState
61
+ title="No release tasks"
62
+ detail="Create the first task for this milestone."
63
+ action={
64
+ <button
65
+ class="kui-recipe__button"
66
+ data-primary="true"
67
+ type="button"
68
+ data-action="recipe-action"
69
+ data-recipe-command="create"
70
+ >
71
+ Create task
72
+ </button>
73
+ }
74
+ />
75
+ );
76
+ if (state.value === 'error')
77
+ return (
78
+ <StateBanner
79
+ title="Release tasks could not be refreshed"
80
+ detail="Existing filters are preserved. Try again when the connection recovers."
81
+ tone="danger"
82
+ urgency="alert"
83
+ action={
84
+ <button
85
+ class="kui-recipe__button"
86
+ type="button"
87
+ data-action="recipe-action"
88
+ data-recipe-command="retry"
89
+ >
90
+ Retry
91
+ </button>
92
+ }
93
+ />
94
+ );
95
+ return (
96
+ <>
97
+ {state.value === 'stale' && (
98
+ <StateBanner
99
+ title="Showing saved results"
100
+ detail="Refreshing in the background."
101
+ tone="warning"
102
+ action={
103
+ <span class="kui-inline-metadata">
104
+ <LoadingSpinner label="Refreshing release tasks" />
105
+ <button
106
+ class="kui-recipe__button"
107
+ type="button"
108
+ data-action="recipe-action"
109
+ data-recipe-command="finish"
110
+ >
111
+ Finish refresh
112
+ </button>
113
+ </span>
114
+ }
115
+ />
116
+ )}
117
+ {populated()}
118
+ </>
119
+ );
120
+ };
121
+ const render = () => (
122
+ <section
123
+ class="kui-recipe recipe-list kui-recipe__surface kui-pane"
124
+ data-recipe="recipe-list-workspace-states"
125
+ data-list-state={state.value}
126
+ >
127
+ <Toolbar
128
+ label="Release tasks"
129
+ dividerSides=""
130
+ leading={
131
+ <ToolbarText
132
+ text="Release tasks"
133
+ size="xlarge"
134
+ id="recipe-list-title"
135
+ />
136
+ }
137
+ trailing={
138
+ <ToolbarControlGroup appearance="borderless">
139
+ <button
140
+ class="kui-recipe__button"
141
+ type="button"
142
+ data-action="recipe-action"
143
+ data-recipe-command="empty"
144
+ >
145
+ Clear
146
+ </button>
147
+ <button
148
+ class="kui-recipe__button"
149
+ type="button"
150
+ data-action="recipe-action"
151
+ data-recipe-command="fail"
152
+ >
153
+ Simulate failure
154
+ </button>
155
+ <button
156
+ class="kui-recipe__button"
157
+ data-primary="true"
158
+ type="button"
159
+ data-action="recipe-action"
160
+ data-recipe-command="refresh"
161
+ >
162
+ Refresh
163
+ </button>
164
+ </ToolbarControlGroup>
165
+ }
166
+ />
167
+ <div class="recipe-list__body kui-pane__content kui-content">
168
+ {renderBody()}
169
+ <p class="kui-recipe__ownership kui-content-item">
170
+ The recipe owns feedback placement and stable content. The app owns
171
+ fetching, cache age, retry policy, and domain rows.
172
+ </p>
173
+ </div>
174
+ </section>
175
+ );
176
+ return {
177
+ render,
178
+ action(command) {
179
+ const next: Record<string, ListState> = {
180
+ load: 'populated',
181
+ empty: 'empty',
182
+ create: 'populated',
183
+ fail: 'error',
184
+ retry: 'populated',
185
+ refresh: 'stale',
186
+ finish: 'populated',
187
+ };
188
+ if (command in next) state.value = next[command]!;
189
+ announce(`List state: ${state.value}`);
190
+ },
23
191
  };
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
192
  };
@@ -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,186 @@
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 { Toolbar } from '@kerfjs/ui/toolbar';
13
+ import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
14
+ import { ToolbarText } from '@kerfjs/ui/toolbar-text';
15
+ import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
16
+ import { signal } from 'kerfjs';
17
+ import { Columns3, FileText, Inbox, List } from 'lucide';
18
+
19
+ import type { RecipeFactory } from './types.js';
20
+
21
+ const icon = (node: Parameters<typeof LucideIcon>[0]['icon'], name: string) => (
22
+ <LucideIcon icon={node} name={name} />
23
+ );
24
+
25
+ /**
26
+ * A record inspector whose per-record values load asynchronously. Every
27
+ * value-bearing component takes `placeholder` from one loading flag, so the same
28
+ * chrome renders a faithful loading state and then the populated record — no
29
+ * separate skeleton markup. The composition is the point; `Skeleton` is the
30
+ * primitive it is built on (see the Skeleton component demo).
31
+ */
32
+ export const createRecipe: RecipeFactory = (announce) => {
33
+ const loading = signal(true);
34
+ const render = () => {
35
+ const p = loading.value;
36
+ return (
37
+ <section
38
+ class="kui-recipe recipe-inspector kui-recipe__surface kui-pane"
39
+ data-recipe="recipe-loading-inspector"
40
+ data-inspector-loading={String(p)}
41
+ >
42
+ <Toolbar
43
+ label="Ticket inspector"
44
+ dividerSides=""
45
+ leading={
46
+ <>
47
+ <ToolbarControlGroup appearance="borderless" single>
48
+ {icon(FileText, 'file-text')}
49
+ </ToolbarControlGroup>
50
+ <ToolbarText
51
+ text="Ticket · KF-2048"
52
+ size="xlarge"
53
+ id="recipe-inspector-title"
54
+ placeholder={p}
55
+ />
56
+ </>
57
+ }
58
+ trailing={
59
+ <ToolbarControlGroup appearance="borderless" single>
60
+ <button
61
+ class="kui-recipe__button"
62
+ data-primary="true"
63
+ type="button"
64
+ data-action="recipe-action"
65
+ data-recipe-command="toggle"
66
+ >
67
+ {p ? 'Show loaded' : 'Show loading'}
68
+ </button>
69
+ </ToolbarControlGroup>
70
+ }
71
+ />
72
+ <p class="kui-recipe__heading-summary" id="recipe-inspector-summary">
73
+ {p ? (
74
+ <Skeleton width={em(18)} />
75
+ ) : (
76
+ 'Restore keyboard focus after a dialog closes'
77
+ )}
78
+ </p>
79
+ <div class="recipe-inspector__body kui-pane__content kui-content">
80
+ <section>
81
+ <ValueTable label="Ticket details">
82
+ <ValueTableRow
83
+ label="Status"
84
+ value="In review"
85
+ icon={icon(Inbox, 'inbox')}
86
+ placeholder={p}
87
+ />
88
+ <ValueTableRow label="Owner" value="Mara Lopez" placeholder={p} />
89
+ <ValueTableRow label="Priority" value="High" placeholder={p} />
90
+ <ValueTableRow
91
+ label="Updated"
92
+ value="2 hours ago"
93
+ placeholder={p}
94
+ />
95
+ </ValueTable>
96
+ </section>
97
+ <div class="recipe-inspector__controls">
98
+ <Select
99
+ name="recipe-inspector-status"
100
+ value="review"
101
+ label="Status"
102
+ placeholder={p}
103
+ choices={[
104
+ { value: 'review', label: 'In review' },
105
+ { value: 'ready', label: 'Ready' },
106
+ { value: 'done', label: 'Done' },
107
+ ]}
108
+ />
109
+ <SegmentedControl
110
+ id="recipe-inspector-view"
111
+ label="Inspector view"
112
+ value="details"
113
+ appearance="toolbar"
114
+ shape="pill"
115
+ size="small"
116
+ placeholder={p}
117
+ choices={[
118
+ {
119
+ value: 'details',
120
+ label: 'Details',
121
+ content: icon(List, 'list'),
122
+ },
123
+ {
124
+ value: 'activity',
125
+ label: 'Activity',
126
+ content: icon(Columns3, 'columns-3'),
127
+ },
128
+ {
129
+ value: 'files',
130
+ label: 'Files',
131
+ content: icon(FileText, 'file-text'),
132
+ },
133
+ ]}
134
+ />
135
+ </div>
136
+ <section>
137
+ <ListItem
138
+ action="recipe-action"
139
+ itemId="reassign"
140
+ label="Reassign ticket"
141
+ icon={icon(Inbox, 'inbox')}
142
+ placeholder={p}
143
+ />
144
+ <ListItem
145
+ action="recipe-action"
146
+ itemId="watch"
147
+ label="Watch for changes"
148
+ placeholder={p}
149
+ />
150
+ </section>
151
+ {p ? (
152
+ <StateBanner
153
+ tone="info"
154
+ title=""
155
+ detail=""
156
+ icon={icon(FileText, 'file-text')}
157
+ placeholder
158
+ />
159
+ ) : (
160
+ <StateBanner
161
+ tone="success"
162
+ title="Up to date"
163
+ detail="All checks passed on the latest revision."
164
+ icon={icon(FileText, 'file-text')}
165
+ />
166
+ )}
167
+ <p class="kui-recipe__ownership kui-content-item">
168
+ Each value-bearing component's <code>placeholder</code> prop renders
169
+ skeletons in its value slots while loading, so the recipe composes a
170
+ faithful loading inspector from real chrome. The app owns the
171
+ loading lifecycle and which values are still unknown.
172
+ </p>
173
+ </div>
174
+ </section>
175
+ );
176
+ };
177
+ return {
178
+ render,
179
+ action(command) {
180
+ if (command === 'toggle') {
181
+ loading.value = !loading.value;
182
+ announce(loading.value ? 'Inspector is loading' : 'Inspector loaded');
183
+ }
184
+ },
185
+ };
186
+ };
@@ -1,3 +1,4 @@
1
+ import { wireNavStack } from '@kerfjs/ui/wire-nav-stack';
1
2
  import { wireResizableRegions } from '@kerfjs/ui/wire-resizable-regions';
2
3
  import { delegate, mount } from 'kerfjs';
3
4
  import { delegateActions } from 'kerfjs/actions';
@@ -8,7 +9,10 @@ import type { RecipeController } from './types.js';
8
9
  * Copyable application boundary for a recipe. The caller owns one stable root
9
10
  * and retains the returned disposer for that root's lifetime.
10
11
  */
11
- export function mountRecipe(root: HTMLElement, controller: RecipeController): () => void {
12
+ export function mountRecipe(
13
+ root: HTMLElement,
14
+ controller: RecipeController,
15
+ ): () => void {
12
16
  const stopMount = mount(root, controller.render);
13
17
  const stopActions = delegateActions(root, 'click', {
14
18
  'recipe-action': (_event, element) => {
@@ -16,18 +20,40 @@ export function mountRecipe(root: HTMLElement, controller: RecipeController): ()
16
20
  controller.action(target.dataset.recipeCommand ?? '', target);
17
21
  },
18
22
  });
19
- const dispatchChange = (_event: Event, element: Element) => controller.change?.(element as HTMLElement);
20
- const stopChanges = delegate(root, 'change', 'wa-select, wa-input, wa-textarea', dispatchChange);
21
- const stopInputs = delegate(root, 'input', 'wa-input, wa-textarea', dispatchChange);
22
- const stopDialogs = delegate(root, 'wa-after-hide', 'wa-dialog', (_event, element) => controller.afterHide?.(element as HTMLElement));
23
+ const dispatchChange = (_event: Event, element: Element) =>
24
+ controller.change?.(element as HTMLElement);
25
+ const stopChanges = delegate(
26
+ root,
27
+ 'change',
28
+ 'wa-select, wa-input, wa-textarea',
29
+ dispatchChange,
30
+ );
31
+ const stopInputs = delegate(
32
+ root,
33
+ 'input',
34
+ 'wa-input, wa-textarea',
35
+ dispatchChange,
36
+ );
37
+ const stopDialogs = delegate(
38
+ root,
39
+ 'wa-after-hide',
40
+ 'wa-dialog',
41
+ (_event, element) => controller.afterHide?.(element as HTMLElement),
42
+ );
23
43
  const stopResize = wireResizableRegions(root, {
24
44
  onCommit: ({ id, size }) => controller.resize?.(id, size),
25
45
  });
46
+ const stopNav = wireNavStack(root, {
47
+ onBack: () => controller.action('nav-back', root),
48
+ });
49
+ const stopWire = controller.wire?.(root);
26
50
 
27
51
  let disposed = false;
28
52
  return () => {
29
53
  if (disposed) return;
30
54
  disposed = true;
55
+ stopWire?.();
56
+ stopNav();
31
57
  stopResize();
32
58
  stopDialogs();
33
59
  stopInputs();