@kerfjs/ui 4.4.1 → 4.5.0-beta.1

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 (499) hide show
  1. package/README.md +425 -42
  2. package/ai/application-ui-diagnostic-ids-v1.json +92 -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 +102 -0
  12. package/ai/compile-time-contracts-v1.schema.json +49 -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 +319 -0
  17. package/ai/component-catalog-v2.d.ts +67 -0
  18. package/ai/component-catalog-v2.json +4675 -0
  19. package/ai/component-catalog-v2.schema.json +246 -0
  20. package/ai/component-catalog.json +5826 -0
  21. package/ai/component-catalog.schema.json +95 -0
  22. package/ai/public-api-signatures-v1.md +1719 -0
  23. package/ai/skill.md +129 -21
  24. package/ai/webawesome-jsx-signatures-v1.md +106 -0
  25. package/analyzer/cli.mjs +46 -0
  26. package/analyzer/index.d.mts +45 -0
  27. package/analyzer/index.mjs +737 -0
  28. package/analyzer/report.schema.json +66 -0
  29. package/dist/app-tab.d.ts +14 -2
  30. package/dist/app-tab.js +3 -1
  31. package/dist/browser/app-tab.js +3 -2
  32. package/dist/browser/disclosure-arrow.js +4 -0
  33. package/dist/browser/empty-state.js +3 -3
  34. package/dist/browser/floating-toolbar.js +3 -0
  35. package/dist/browser/list-action-row.js +4 -0
  36. package/dist/browser/list-header.js +6 -0
  37. package/dist/browser/list-inset-control.js +3 -0
  38. package/dist/browser/list-inset-text.js +3 -0
  39. package/dist/browser/list-item.js +4 -0
  40. package/dist/browser/list.js +3 -0
  41. package/dist/browser/loading-spinner.js +2 -2
  42. package/dist/browser/lucide-icon.js +2 -2
  43. package/dist/browser/pane.js +3 -0
  44. package/dist/browser/panel-header.js +7 -0
  45. package/dist/browser/resizable-region.js +2 -2
  46. package/dist/browser/segmented-control.js +3 -2
  47. package/dist/browser/select.js +4 -3
  48. package/dist/browser/skeleton.js +3 -0
  49. package/dist/browser/state-banner.js +3 -2
  50. package/dist/browser/sunken-panel.js +3 -0
  51. package/dist/browser/tab-bar.js +2 -2
  52. package/dist/browser/token-search-field.js +4 -0
  53. package/dist/browser/toolbar-control-group.js +2 -2
  54. package/dist/browser/toolbar-text.js +3 -2
  55. package/dist/browser/toolbar.js +2 -2
  56. package/dist/browser/value-table.js +3 -2
  57. package/dist/catalog-resources.d.ts +34 -0
  58. package/dist/catalog-resources.js +22 -0
  59. package/dist/catalog-resources.js.map +1 -0
  60. package/dist/catalog.d.ts +150 -0
  61. package/dist/catalog.js +377 -0
  62. package/dist/catalog.js.map +1 -0
  63. package/dist/chunk-5VTB2BKP.js +35 -0
  64. package/dist/chunk-5VTB2BKP.js.map +1 -0
  65. package/dist/chunk-6WTETUZ6.js +21 -0
  66. package/dist/chunk-6WTETUZ6.js.map +1 -0
  67. package/dist/chunk-7KEUJIIC.js +218 -0
  68. package/dist/chunk-7KEUJIIC.js.map +1 -0
  69. package/dist/chunk-7XRDUG4Y.js +58 -0
  70. package/dist/chunk-7XRDUG4Y.js.map +1 -0
  71. package/dist/chunk-B3A3IVO5.js +83 -0
  72. package/dist/chunk-B3A3IVO5.js.map +1 -0
  73. package/dist/chunk-DPDBVKNA.js +113 -0
  74. package/dist/chunk-DPDBVKNA.js.map +1 -0
  75. package/dist/chunk-FEAMR7S4.js +68 -0
  76. package/dist/chunk-FEAMR7S4.js.map +1 -0
  77. package/dist/chunk-FEY65TBV.js +15 -0
  78. package/dist/chunk-FEY65TBV.js.map +1 -0
  79. package/dist/{chunk-TRNBHPNW.js → chunk-IJBSZ4NX.js} +11 -8
  80. package/dist/chunk-IJBSZ4NX.js.map +1 -0
  81. package/dist/chunk-J5BFYY7Q.js +35 -0
  82. package/dist/chunk-J5BFYY7Q.js.map +1 -0
  83. package/dist/chunk-JRKSK2HX.js +79 -0
  84. package/dist/chunk-JRKSK2HX.js.map +1 -0
  85. package/dist/chunk-KWWR5VMS.js +45 -0
  86. package/dist/chunk-KWWR5VMS.js.map +1 -0
  87. package/dist/chunk-LPOMRC4C.js +25 -0
  88. package/dist/chunk-LPOMRC4C.js.map +1 -0
  89. package/dist/chunk-M7JBER5U.js +41 -0
  90. package/dist/chunk-M7JBER5U.js.map +1 -0
  91. package/dist/chunk-NENBUYX5.js +79 -0
  92. package/dist/chunk-NENBUYX5.js.map +1 -0
  93. package/dist/chunk-NWSEP5RT.js +117 -0
  94. package/dist/chunk-NWSEP5RT.js.map +1 -0
  95. package/dist/chunk-OJWWHFH3.js +426 -0
  96. package/dist/chunk-OJWWHFH3.js.map +1 -0
  97. package/dist/chunk-PSKN344N.js +114 -0
  98. package/dist/chunk-PSKN344N.js.map +1 -0
  99. package/dist/chunk-PX3YSS5C.js +32 -0
  100. package/dist/chunk-PX3YSS5C.js.map +1 -0
  101. package/dist/chunk-QO55FN2Y.js +20 -0
  102. package/dist/chunk-QO55FN2Y.js.map +1 -0
  103. package/dist/chunk-R7ZWUN64.js +38 -0
  104. package/dist/chunk-R7ZWUN64.js.map +1 -0
  105. package/dist/chunk-RKVQEH4J.js +61 -0
  106. package/dist/chunk-RKVQEH4J.js.map +1 -0
  107. package/dist/chunk-SRSJO5QE.js +40 -0
  108. package/dist/chunk-SRSJO5QE.js.map +1 -0
  109. package/dist/chunk-SYDMYBPG.js +31 -0
  110. package/dist/chunk-SYDMYBPG.js.map +1 -0
  111. package/dist/chunk-TB6DY7H5.js +25 -0
  112. package/dist/chunk-TB6DY7H5.js.map +1 -0
  113. package/dist/chunk-UFZKLBCF.js +108 -0
  114. package/dist/chunk-UFZKLBCF.js.map +1 -0
  115. package/dist/{chunk-MQIZAD4A.js → chunk-UKKSMZYT.js} +76 -22
  116. package/dist/chunk-UKKSMZYT.js.map +1 -0
  117. package/dist/chunk-W5L2JSBY.js +40 -0
  118. package/dist/chunk-W5L2JSBY.js.map +1 -0
  119. package/dist/chunk-Y45PPBDV.js +105 -0
  120. package/dist/chunk-Y45PPBDV.js.map +1 -0
  121. package/dist/chunk-Y6LYKFXY.js +43 -0
  122. package/dist/chunk-Y6LYKFXY.js.map +1 -0
  123. package/dist/collapsible-panel.d.ts +66 -0
  124. package/dist/collapsible-panel.js +63 -0
  125. package/dist/collapsible-panel.js.map +1 -0
  126. package/dist/device-class.d.ts +62 -0
  127. package/dist/device-class.js +103 -0
  128. package/dist/device-class.js.map +1 -0
  129. package/dist/disclosure-arrow.d.ts +14 -0
  130. package/dist/disclosure-arrow.js +4 -0
  131. package/dist/disclosure-arrow.js.map +1 -0
  132. package/dist/divider-sides-267FA7sY.d.ts +4 -0
  133. package/dist/empty-state.d.ts +1 -1
  134. package/dist/empty-state.js +1 -1
  135. package/dist/floating-toolbar.d.ts +28 -0
  136. package/dist/floating-toolbar.js +3 -0
  137. package/dist/floating-toolbar.js.map +1 -0
  138. package/dist/index.d.ts +20 -9
  139. package/dist/index.js +29 -18
  140. package/dist/list-action-row.d.ts +52 -0
  141. package/dist/list-action-row.js +5 -0
  142. package/dist/list-action-row.js.map +1 -0
  143. package/dist/list-header.d.ts +64 -0
  144. package/dist/list-header.js +7 -0
  145. package/dist/list-header.js.map +1 -0
  146. package/dist/list-inset-control.d.ts +16 -0
  147. package/dist/list-inset-control.js +3 -0
  148. package/dist/list-inset-control.js.map +1 -0
  149. package/dist/list-inset-text.d.ts +25 -0
  150. package/dist/list-inset-text.js +3 -0
  151. package/dist/list-inset-text.js.map +1 -0
  152. package/dist/list-item.d.ts +33 -0
  153. package/dist/list-item.js +5 -0
  154. package/dist/list-item.js.map +1 -0
  155. package/dist/list.d.ts +19 -0
  156. package/dist/list.js +3 -0
  157. package/dist/list.js.map +1 -0
  158. package/dist/loading-spinner.js +1 -1
  159. package/dist/nav-stack.d.ts +38 -0
  160. package/dist/nav-stack.js +5 -0
  161. package/dist/nav-stack.js.map +1 -0
  162. package/dist/pane.d.ts +49 -0
  163. package/dist/pane.js +4 -0
  164. package/dist/pane.js.map +1 -0
  165. package/dist/panel-header.d.ts +42 -0
  166. package/dist/panel-header.js +7 -0
  167. package/dist/panel-header.js.map +1 -0
  168. package/dist/resizable-region.d.ts +3 -1
  169. package/dist/resizable-region.js +1 -1
  170. package/dist/segmented-control.d.ts +8 -6
  171. package/dist/segmented-control.js +2 -1
  172. package/dist/select-register.d.ts +5 -1
  173. package/dist/select-register.js +3 -0
  174. package/dist/select-register.js.map +1 -1
  175. package/dist/select.d.ts +15 -6
  176. package/dist/select.js +2 -1
  177. package/dist/skeleton.d.ts +24 -0
  178. package/dist/skeleton.js +3 -0
  179. package/dist/skeleton.js.map +1 -0
  180. package/dist/split-view.d.ts +42 -0
  181. package/dist/split-view.js +81 -0
  182. package/dist/split-view.js.map +1 -0
  183. package/dist/state-banner.d.ts +8 -3
  184. package/dist/state-banner.js +2 -1
  185. package/dist/styles/app-tab.css +152 -0
  186. package/dist/styles/catalog.css +575 -0
  187. package/dist/styles/collapsible-panel.css +146 -0
  188. package/dist/styles/disclosure-arrow.css +23 -0
  189. package/dist/styles/empty-state.css +47 -0
  190. package/dist/styles/floating-toolbar.css +38 -0
  191. package/dist/styles/foundation.css +362 -0
  192. package/dist/styles/layout.css +55 -0
  193. package/dist/styles/list-action-row.css +160 -0
  194. package/dist/styles/list-header.css +160 -0
  195. package/dist/styles/list-inset-control.css +7 -0
  196. package/dist/styles/list-inset-text.css +14 -0
  197. package/dist/styles/list-item.css +123 -0
  198. package/dist/styles/list.css +43 -0
  199. package/dist/styles/loading-spinner.css +21 -0
  200. package/dist/styles/lucide-icon.css +6 -0
  201. package/dist/styles/nav-stack.css +111 -0
  202. package/dist/styles/pane.css +62 -0
  203. package/dist/styles/panel-header.css +64 -0
  204. package/dist/styles/resizable-region.css +111 -0
  205. package/{src → dist/styles}/segmented-control.css +58 -31
  206. package/dist/styles/select.css +140 -0
  207. package/dist/styles/skeleton.css +31 -0
  208. package/dist/styles/split-view.css +35 -0
  209. package/dist/styles/state-banner.css +198 -0
  210. package/dist/styles/styles.css +24 -0
  211. package/dist/styles/sunken-panel.css +22 -0
  212. package/dist/styles/tab-bar.css +45 -0
  213. package/dist/styles/tab-scaffold.css +84 -0
  214. package/dist/styles/token-search-field.css +289 -0
  215. package/dist/styles/toolbar-control-group.css +225 -0
  216. package/dist/styles/toolbar-text.css +92 -0
  217. package/dist/styles/toolbar.css +97 -0
  218. package/dist/styles/value-table.css +75 -0
  219. package/dist/styles/webawesome.css +399 -0
  220. package/dist/styles/workbench.css +94 -0
  221. package/dist/sunken-panel.d.ts +19 -0
  222. package/dist/sunken-panel.js +3 -0
  223. package/dist/sunken-panel.js.map +1 -0
  224. package/dist/tab-bar.d.ts +10 -2
  225. package/dist/tab-bar.js +1 -1
  226. package/dist/tab-scaffold.d.ts +30 -0
  227. package/dist/tab-scaffold.js +51 -0
  228. package/dist/tab-scaffold.js.map +1 -0
  229. package/dist/token-search-field.d.ts +59 -0
  230. package/dist/token-search-field.js +4 -0
  231. package/dist/token-search-field.js.map +1 -0
  232. package/dist/toolbar-control-group.d.ts +11 -5
  233. package/dist/toolbar-control-group.js +1 -1
  234. package/dist/toolbar-text.d.ts +31 -4
  235. package/dist/toolbar-text.js +2 -1
  236. package/dist/toolbar.d.ts +5 -3
  237. package/dist/toolbar.js +1 -1
  238. package/dist/value-table.d.ts +13 -3
  239. package/dist/value-table.js +2 -1
  240. package/dist/webawesome.d.ts +100 -0
  241. package/dist/webawesome.js +3 -0
  242. package/dist/webawesome.js.map +1 -0
  243. package/dist/wire-catalog.d.ts +57 -0
  244. package/dist/wire-catalog.js +302 -0
  245. package/dist/wire-catalog.js.map +1 -0
  246. package/dist/wire-nav-stack.d.ts +15 -0
  247. package/dist/wire-nav-stack.js +103 -0
  248. package/dist/wire-nav-stack.js.map +1 -0
  249. package/dist/wire-resizable-regions.d.ts +1 -1
  250. package/dist/wire-resizable-regions.js +1 -1
  251. package/dist/wire-sidebar.d.ts +43 -0
  252. package/dist/wire-sidebar.js +128 -0
  253. package/dist/wire-sidebar.js.map +1 -0
  254. package/dist/wire-tab-bars.d.ts +18 -2
  255. package/dist/wire-tab-bars.js +1 -1
  256. package/dist/wire-tab-scaffold.d.ts +11 -0
  257. package/dist/wire-tab-scaffold.js +21 -0
  258. package/dist/wire-tab-scaffold.js.map +1 -0
  259. package/dist/wire-token-search-fields.d.ts +111 -0
  260. package/dist/wire-token-search-fields.js +5 -0
  261. package/dist/wire-token-search-fields.js.map +1 -0
  262. package/dist/workbench.d.ts +33 -0
  263. package/dist/workbench.js +64 -0
  264. package/dist/workbench.js.map +1 -0
  265. package/docs/accessibility.md +124 -4
  266. package/docs/app-layouts.md +61 -0
  267. package/docs/catalog.md +364 -0
  268. package/docs/collapsible-panel.md +129 -0
  269. package/docs/component-contract.md +312 -5
  270. package/docs/component-selection.md +219 -0
  271. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  272. package/docs/design/templates/empty-state/filtered.svg +1 -0
  273. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  274. package/docs/design/templates/empty-state/with-action.svg +1 -0
  275. package/docs/design/templates/empty-state-dark.svg +1 -0
  276. package/docs/design/templates/empty-state.svg +1 -0
  277. package/docs/design/templates/list/compact-dark.svg +1 -0
  278. package/docs/design/templates/list/compact.svg +1 -0
  279. package/docs/design/templates/list/stack-dark.svg +1 -0
  280. package/docs/design/templates/list/stack.svg +1 -0
  281. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  282. package/docs/design/templates/list-action-row/default.svg +1 -0
  283. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  284. package/docs/design/templates/list-action-row/selected.svg +1 -0
  285. package/docs/design/templates/list-action-row-dark.svg +1 -0
  286. package/docs/design/templates/list-action-row.svg +1 -0
  287. package/docs/design/templates/list-dark.svg +1 -0
  288. package/docs/design/templates/list-header/action-dark.svg +1 -0
  289. package/docs/design/templates/list-header/action.svg +1 -0
  290. package/docs/design/templates/list-header/count-dark.svg +1 -0
  291. package/docs/design/templates/list-header/count.svg +1 -0
  292. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  293. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  294. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  295. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  296. package/docs/design/templates/list-header-dark.svg +1 -0
  297. package/docs/design/templates/list-header.svg +1 -0
  298. package/docs/design/templates/list-item/default-dark.svg +1 -0
  299. package/docs/design/templates/list-item/default.svg +1 -0
  300. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  301. package/docs/design/templates/list-item/multiline.svg +1 -0
  302. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  303. package/docs/design/templates/list-item/selected.svg +1 -0
  304. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  305. package/docs/design/templates/list-item/trailing.svg +1 -0
  306. package/docs/design/templates/list-item-dark.svg +1 -0
  307. package/docs/design/templates/list-item.svg +1 -0
  308. package/docs/design/templates/list.svg +1 -0
  309. package/docs/design/templates/panel-header/icon-actions-dark.svg +1 -0
  310. package/docs/design/templates/panel-header/icon-actions.svg +1 -0
  311. package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +1 -0
  312. package/docs/design/templates/panel-header/icon-summary-actions.svg +1 -0
  313. package/docs/design/templates/panel-header/no-icon-dark.svg +1 -0
  314. package/docs/design/templates/panel-header/no-icon.svg +1 -0
  315. package/docs/design/templates/panel-header/page-heading-dark.svg +1 -0
  316. package/docs/design/templates/panel-header/page-heading.svg +1 -0
  317. package/docs/design/templates/panel-header/title-only-dark.svg +1 -0
  318. package/docs/design/templates/panel-header/title-only.svg +1 -0
  319. package/docs/design/templates/panel-header-dark.svg +1 -0
  320. package/docs/design/templates/panel-header.svg +1 -0
  321. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  322. package/docs/design/templates/segmented-control/equal.svg +1 -0
  323. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  324. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  325. package/docs/design/templates/segmented-control-dark.svg +1 -0
  326. package/docs/design/templates/segmented-control.svg +1 -0
  327. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  328. package/docs/design/templates/skeleton/avatar.svg +1 -0
  329. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  330. package/docs/design/templates/skeleton/block.svg +1 -0
  331. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  332. package/docs/design/templates/skeleton/lines.svg +1 -0
  333. package/docs/design/templates/skeleton-dark.svg +1 -0
  334. package/docs/design/templates/skeleton.svg +1 -0
  335. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  336. package/docs/design/templates/state-banner/danger.svg +1 -0
  337. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  338. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  339. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  340. package/docs/design/templates/state-banner/info.svg +1 -0
  341. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  342. package/docs/design/templates/state-banner/success.svg +1 -0
  343. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  344. package/docs/design/templates/state-banner/warning.svg +1 -0
  345. package/docs/design/templates/state-banner-dark.svg +1 -0
  346. package/docs/design/templates/state-banner.svg +1 -0
  347. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  348. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  349. package/docs/design/templates/tab-bar-dark.svg +1 -0
  350. package/docs/design/templates/tab-bar.svg +1 -0
  351. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  352. package/docs/design/templates/token-search-field/empty.svg +1 -0
  353. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  354. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  355. package/docs/design/templates/token-search-field-dark.svg +1 -0
  356. package/docs/design/templates/token-search-field.svg +1 -0
  357. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  358. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  359. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  360. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  361. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  362. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  363. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  364. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  365. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  366. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  367. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  368. package/docs/design/templates/toolbar-control-group.svg +1 -0
  369. package/docs/design/templates/toolbar-dark.svg +1 -0
  370. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  371. package/docs/design/templates/toolbar-text/default.svg +1 -0
  372. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  373. package/docs/design/templates/toolbar-text/large.svg +1 -0
  374. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  375. package/docs/design/templates/toolbar-text/small.svg +1 -0
  376. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  377. package/docs/design/templates/toolbar-text.svg +1 -0
  378. package/docs/design/templates/toolbar.svg +1 -0
  379. package/docs/design/templates/value-table/details-dark.svg +1 -0
  380. package/docs/design/templates/value-table/details.svg +1 -0
  381. package/docs/design/templates/value-table-dark.svg +1 -0
  382. package/docs/design/templates/value-table.svg +1 -0
  383. package/docs/design/templates.md +130 -0
  384. package/docs/design-philosophy.md +59 -1
  385. package/docs/device-class.md +54 -0
  386. package/docs/examples/application-ui-profile.json +60 -0
  387. package/docs/examples/command-palette-adapter.tsx +120 -0
  388. package/docs/examples/component-catalog-extension-v2.json +81 -0
  389. package/docs/examples/component-catalog-extension.json +45 -0
  390. package/docs/layout.md +150 -0
  391. package/docs/nav-stack.md +56 -0
  392. package/docs/recipes.md +167 -0
  393. package/docs/split-view.md +49 -0
  394. package/docs/sunken-panel.md +53 -0
  395. package/docs/tab-scaffold.md +50 -0
  396. package/docs/type-contracts.md +102 -0
  397. package/docs/ui-analyzer.md +86 -0
  398. package/docs/ui-doctor.md +84 -0
  399. package/docs/ui-evaluator.md +122 -0
  400. package/docs/ux-demo.md +23 -9
  401. package/docs/webawesome-theme.md +87 -7
  402. package/docs/workbench.md +73 -0
  403. package/doctor/cli.mjs +115 -0
  404. package/doctor/config.schema.json +68 -0
  405. package/doctor/eslint-diagnostics.d.mts +8 -0
  406. package/doctor/eslint-diagnostics.mjs +7 -0
  407. package/doctor/index.d.mts +116 -0
  408. package/doctor/index.mjs +1257 -0
  409. package/doctor/report.schema.json +118 -0
  410. package/evaluator/cli.mjs +89 -0
  411. package/evaluator/index.d.mts +90 -0
  412. package/evaluator/index.mjs +1138 -0
  413. package/evaluator/report.schema.json +152 -0
  414. package/llms.txt +197 -14
  415. package/package.json +228 -46
  416. package/traversal-exclusions.mjs +27 -0
  417. package/ux-demo/recipes/app-shell.tsx +228 -0
  418. package/ux-demo/recipes/collapsible-sidebar.tsx +223 -0
  419. package/ux-demo/recipes/compact-toolbar.tsx +145 -0
  420. package/ux-demo/recipes/composer-form.tsx +151 -0
  421. package/ux-demo/recipes/list-detail-dialog.tsx +181 -0
  422. package/ux-demo/recipes/list-workspace-states.tsx +183 -0
  423. package/ux-demo/recipes/loaders.ts +36 -0
  424. package/ux-demo/recipes/loading-inspector.tsx +164 -0
  425. package/ux-demo/recipes/mount-recipe.ts +64 -0
  426. package/ux-demo/recipes/navigation-sidebar.tsx +137 -0
  427. package/ux-demo/recipes/navigation-stack.tsx +118 -0
  428. package/ux-demo/recipes/recipes.css +448 -0
  429. package/ux-demo/recipes/types.ts +19 -0
  430. package/ux-demo/recipes/workspace-header.tsx +67 -0
  431. package/dist/browser/dialog-header.js +0 -3
  432. package/dist/browser/menu-header.js +0 -3
  433. package/dist/browser/menu-item.js +0 -3
  434. package/dist/browser/page-header.js +0 -3
  435. package/dist/chunk-2PES33HS.js +0 -13
  436. package/dist/chunk-2PES33HS.js.map +0 -1
  437. package/dist/chunk-3O7ZEUW2.js +0 -27
  438. package/dist/chunk-3O7ZEUW2.js.map +0 -1
  439. package/dist/chunk-4HHCYSGT.js +0 -79
  440. package/dist/chunk-4HHCYSGT.js.map +0 -1
  441. package/dist/chunk-55DATV3A.js +0 -10
  442. package/dist/chunk-55DATV3A.js.map +0 -1
  443. package/dist/chunk-723YNJBH.js +0 -17
  444. package/dist/chunk-723YNJBH.js.map +0 -1
  445. package/dist/chunk-DAFFDO33.js +0 -14
  446. package/dist/chunk-DAFFDO33.js.map +0 -1
  447. package/dist/chunk-GY5WH7TO.js +0 -10
  448. package/dist/chunk-GY5WH7TO.js.map +0 -1
  449. package/dist/chunk-MBR3TYNL.js +0 -14
  450. package/dist/chunk-MBR3TYNL.js.map +0 -1
  451. package/dist/chunk-MFKMHK5W.js +0 -10
  452. package/dist/chunk-MFKMHK5W.js.map +0 -1
  453. package/dist/chunk-MQIZAD4A.js.map +0 -1
  454. package/dist/chunk-NHXX4JI5.js +0 -28
  455. package/dist/chunk-NHXX4JI5.js.map +0 -1
  456. package/dist/chunk-OPCJRCYO.js +0 -19
  457. package/dist/chunk-OPCJRCYO.js.map +0 -1
  458. package/dist/chunk-T5XNMVOI.js +0 -24
  459. package/dist/chunk-T5XNMVOI.js.map +0 -1
  460. package/dist/chunk-TRNBHPNW.js.map +0 -1
  461. package/dist/chunk-VHW5YNIB.js +0 -17
  462. package/dist/chunk-VHW5YNIB.js.map +0 -1
  463. package/dist/chunk-VKH2JLDZ.js +0 -10
  464. package/dist/chunk-VKH2JLDZ.js.map +0 -1
  465. package/dist/chunk-WVPYVDXY.js +0 -15
  466. package/dist/chunk-WVPYVDXY.js.map +0 -1
  467. package/dist/chunk-WY4CAEPG.js +0 -14
  468. package/dist/chunk-WY4CAEPG.js.map +0 -1
  469. package/dist/dialog-header.d.ts +0 -14
  470. package/dist/dialog-header.js +0 -3
  471. package/dist/dialog-header.js.map +0 -1
  472. package/dist/menu-header.d.ts +0 -15
  473. package/dist/menu-header.js +0 -3
  474. package/dist/menu-header.js.map +0 -1
  475. package/dist/menu-item.d.ts +0 -22
  476. package/dist/menu-item.js +0 -3
  477. package/dist/menu-item.js.map +0 -1
  478. package/dist/page-header.d.ts +0 -9
  479. package/dist/page-header.js +0 -3
  480. package/dist/page-header.js.map +0 -1
  481. package/src/app-tab.css +0 -20
  482. package/src/dialog-header.css +0 -9
  483. package/src/empty-state.css +0 -6
  484. package/src/foundation.css +0 -106
  485. package/src/loading-spinner.css +0 -4
  486. package/src/lucide-icon.css +0 -1
  487. package/src/menu-header.css +0 -13
  488. package/src/menu-item.css +0 -12
  489. package/src/page-header.css +0 -3
  490. package/src/resizable-region.css +0 -19
  491. package/src/select.css +0 -18
  492. package/src/state-banner.css +0 -31
  493. package/src/styles.css +0 -18
  494. package/src/tab-bar.css +0 -4
  495. package/src/toolbar-control-group.css +0 -25
  496. package/src/toolbar-text.css +0 -4
  497. package/src/toolbar.css +0 -9
  498. package/src/value-table.css +0 -5
  499. package/src/webawesome.css +0 -178
@@ -0,0 +1,223 @@
1
+ import '@kerfjs/ui/layout.css';
2
+ import '@kerfjs/ui/collapsible-panel.css';
3
+ import './recipes.css';
4
+
5
+ import {
6
+ CollapsiblePanel,
7
+ CollapsiblePanelToggle,
8
+ } from '@kerfjs/ui/collapsible-panel';
9
+ import { deviceClass } from '@kerfjs/ui/device-class';
10
+ import { ListHeader } from '@kerfjs/ui/list-header';
11
+ import { ListItem } from '@kerfjs/ui/list-item';
12
+ import { LucideIcon } from '@kerfjs/ui/lucide-icon';
13
+ import { PanelHeader } from '@kerfjs/ui/panel-header';
14
+ import { Toolbar } from '@kerfjs/ui/toolbar';
15
+ import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
16
+ import { ToolbarText } from '@kerfjs/ui/toolbar-text';
17
+ import { type SidebarStorage, wireSidebar } from '@kerfjs/ui/wire-sidebar';
18
+ import { signal } from 'kerfjs';
19
+ import { Bell, Folder, Inbox, Users } from 'lucide';
20
+
21
+ import type { RecipeFactory } from './types.js';
22
+
23
+ const RAIL_ACTION = 'recipe-sidebar-rail';
24
+ const DRAWER_ACTION = 'recipe-sidebar-drawer';
25
+
26
+ /**
27
+ * A memory-backed store so the recipe demonstrates the persistence hook without
28
+ * writing to the shared `localStorage` the catalog page runs in.
29
+ */
30
+ function memoryStorage(): SidebarStorage {
31
+ const map = new Map<string, string>();
32
+ return {
33
+ getItem: (key) => map.get(key) ?? null,
34
+ setItem: (key, value) => {
35
+ map.set(key, value);
36
+ },
37
+ };
38
+ }
39
+
40
+ export const createRecipe: RecipeFactory = (announce) => {
41
+ const selected = signal('inbox');
42
+ const railCollapsed = signal(false);
43
+ const drawerCollapsed = signal(true);
44
+ const storage = memoryStorage();
45
+ // A shared window-backed device class: when the viewport is compact the wire
46
+ // switches the rail/drawer to a dismissable overlay presentation.
47
+ const device = deviceClass();
48
+
49
+ const railToggle = (inPanel: boolean) => (
50
+ <CollapsiblePanelToggle
51
+ side="left"
52
+ collapsed={railCollapsed.value}
53
+ action={RAIL_ACTION}
54
+ panelId="sidebar-rail"
55
+ label={railCollapsed.value ? 'Show navigation' : 'Hide navigation'}
56
+ className={inPanel ? '' : 'recipe-collapsible-sidebar__reveal'}
57
+ />
58
+ );
59
+ const drawerToggle = () => (
60
+ <CollapsiblePanelToggle
61
+ side="bottom"
62
+ collapsed={drawerCollapsed.value}
63
+ action={DRAWER_ACTION}
64
+ panelId="sidebar-console"
65
+ label={drawerCollapsed.value ? 'Show activity' : 'Hide activity'}
66
+ />
67
+ );
68
+
69
+ const render = () => (
70
+ <section
71
+ class="kui-recipe recipe-collapsible-sidebar kui-recipe__surface"
72
+ data-recipe="recipe-collapsible-sidebar"
73
+ data-rail-collapsed={String(railCollapsed.value)}
74
+ data-drawer-collapsed={String(drawerCollapsed.value)}
75
+ >
76
+ <CollapsiblePanel
77
+ id="sidebar-rail"
78
+ side="left"
79
+ size={232}
80
+ collapsed={railCollapsed.value}
81
+ label="Workspace navigation"
82
+ className="recipe-collapsible-sidebar__rail kui-pane"
83
+ >
84
+ <div class="recipe-collapsible-sidebar__rail-head">
85
+ <ToolbarText text="Atlas" />
86
+ <ToolbarControlGroup appearance="borderless" single>
87
+ {railToggle(true)}
88
+ </ToolbarControlGroup>
89
+ </div>
90
+ <nav
91
+ class="recipe-collapsible-sidebar__nav kui-pane__content kui-content"
92
+ aria-label="Workspace"
93
+ >
94
+ <section>
95
+ <ListHeader label="Workspace" />
96
+ <ListItem
97
+ action="recipe-action"
98
+ itemId="inbox"
99
+ label="Inbox"
100
+ icon={<LucideIcon icon={Inbox} name="inbox" />}
101
+ trailing={<span>12</span>}
102
+ selected={selected.value === 'inbox'}
103
+ />
104
+ <ListItem
105
+ action="recipe-action"
106
+ itemId="projects"
107
+ label="Projects"
108
+ icon={<LucideIcon icon={Folder} name="folder" />}
109
+ selected={selected.value === 'projects'}
110
+ />
111
+ <ListItem
112
+ action="recipe-action"
113
+ itemId="shared"
114
+ label="Shared with me"
115
+ icon={<LucideIcon icon={Users} name="users" />}
116
+ selected={selected.value === 'shared'}
117
+ />
118
+ </section>
119
+ </nav>
120
+ </CollapsiblePanel>
121
+ <main class="recipe-collapsible-sidebar__main kui-pane">
122
+ <PanelHeader
123
+ title={
124
+ selected.value === 'projects'
125
+ ? 'Projects'
126
+ : selected.value === 'shared'
127
+ ? 'Shared with me'
128
+ : 'Inbox'
129
+ }
130
+ titleId="recipe-collapsible-main-title"
131
+ icon={railToggle(false)}
132
+ actions={
133
+ <ToolbarControlGroup appearance="borderless" single>
134
+ {drawerToggle()}
135
+ </ToolbarControlGroup>
136
+ }
137
+ />
138
+ <div class="recipe-collapsible-sidebar__body kui-pane__content kui-content">
139
+ <p class="kui-recipe__ownership kui-content-item">
140
+ The app owns each panel's <code>collapsed</code> signal, sizes, and
141
+ content; <code>wireSidebar</code> owns the toggle, focus
142
+ move/restore, the compact overlay, and persistence.
143
+ </p>
144
+ <article class="recipe-collapsible-sidebar__card kui-content-item">
145
+ <strong>Narrow the window to a compact width</strong>
146
+ <p class="kui-recipe__muted">
147
+ The rail and drawer become a dismissable overlay: a backdrop,
148
+ Escape, and a trapped Tab ring, all managed by the wire.
149
+ </p>
150
+ </article>
151
+ </div>
152
+ </main>
153
+ <CollapsiblePanel
154
+ id="sidebar-console"
155
+ side="bottom"
156
+ size={168}
157
+ collapsed={drawerCollapsed.value}
158
+ label="Activity"
159
+ className="recipe-collapsible-sidebar__drawer kui-pane"
160
+ >
161
+ <Toolbar
162
+ label="Activity"
163
+ dividerSides="b"
164
+ leading={
165
+ <ToolbarControlGroup appearance="borderless" single>
166
+ <ToolbarText text="Activity" size="small" />
167
+ </ToolbarControlGroup>
168
+ }
169
+ trailing={
170
+ <ToolbarControlGroup appearance="borderless" single>
171
+ {drawerToggle()}
172
+ </ToolbarControlGroup>
173
+ }
174
+ />
175
+ <div class="recipe-collapsible-sidebar__drawer-body kui-pane__content kui-content">
176
+ <ul class="recipe-collapsible-sidebar__log">
177
+ <li>
178
+ <LucideIcon icon={Bell} name="bell" />
179
+ Deploy finished · 2m ago
180
+ </li>
181
+ <li>
182
+ <LucideIcon icon={Bell} name="bell" />
183
+ Review requested · 9m ago
184
+ </li>
185
+ </ul>
186
+ </div>
187
+ </CollapsiblePanel>
188
+ </section>
189
+ );
190
+
191
+ return {
192
+ render,
193
+ action(command, element) {
194
+ const id = element.dataset.itemId;
195
+ if (id) {
196
+ selected.value = id;
197
+ announce(`Opened ${id}`);
198
+ return;
199
+ }
200
+ announce(`${command} requested`);
201
+ },
202
+ wire(root) {
203
+ return wireSidebar(root, {
204
+ deviceClass: device,
205
+ storage,
206
+ panels: [
207
+ {
208
+ id: 'sidebar-rail',
209
+ collapsed: railCollapsed,
210
+ toggleAction: RAIL_ACTION,
211
+ storageKey: 'recipe-sidebar-rail',
212
+ },
213
+ {
214
+ id: 'sidebar-console',
215
+ collapsed: drawerCollapsed,
216
+ toggleAction: DRAWER_ACTION,
217
+ storageKey: 'recipe-sidebar-console',
218
+ },
219
+ ],
220
+ });
221
+ },
222
+ };
223
+ };
@@ -0,0 +1,145 @@
1
+ import '@kerfjs/ui/layout.css';
2
+ import '@kerfjs/ui/select/register';
3
+ import './recipes.css';
4
+
5
+ import { LucideIcon } from '@kerfjs/ui/lucide-icon';
6
+ import { SegmentedControl } from '@kerfjs/ui/segmented-control';
7
+ import { Select } from '@kerfjs/ui/select';
8
+ import { Toolbar } from '@kerfjs/ui/toolbar';
9
+ import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
10
+ import { ToolbarText } from '@kerfjs/ui/toolbar-text';
11
+ import { signal } from 'kerfjs';
12
+ import { Filter, MoreHorizontal, RefreshCw } from 'lucide';
13
+
14
+ import type { RecipeFactory } from './types.js';
15
+
16
+ export const createRecipe: RecipeFactory = (announce) => {
17
+ const view = signal('list');
18
+ const sort = signal('updated');
19
+ const filtered = signal(false);
20
+ const render = () => (
21
+ <section
22
+ class="kui-recipe recipe-compact-toolbar kui-recipe__surface kui-layout"
23
+ data-recipe="recipe-compact-toolbar"
24
+ >
25
+ <Toolbar
26
+ label="Task workspace controls"
27
+ leading={
28
+ <ToolbarControlGroup appearance="borderless" single>
29
+ <ToolbarText text="Tasks" size="small" />
30
+ </ToolbarControlGroup>
31
+ }
32
+ center={
33
+ <ToolbarControlGroup>
34
+ <SegmentedControl<string>
35
+ id="recipe-view"
36
+ label="View"
37
+ value={view.value}
38
+ action="recipe-action"
39
+ appearance="toolbar"
40
+ shape="pill"
41
+ size="small"
42
+ choices={[
43
+ { value: 'list', label: 'List' },
44
+ { value: 'board', label: 'Board' },
45
+ { value: 'timeline', label: 'Timeline' },
46
+ ]}
47
+ />
48
+ </ToolbarControlGroup>
49
+ }
50
+ trailing={
51
+ <div class="kui-control-cluster">
52
+ <Select<string>
53
+ name="recipe-sort"
54
+ value={sort.value}
55
+ ariaLabel="Sort tasks"
56
+ fitMenu
57
+ choices={[
58
+ { value: 'updated', label: 'Recently updated' },
59
+ { value: 'priority', label: 'Priority' },
60
+ { value: 'owner', label: 'Owner' },
61
+ { value: 'created', label: 'Created date' },
62
+ ]}
63
+ />
64
+ <ToolbarControlGroup label="Task actions" buttonAppearance="push">
65
+ <button
66
+ type="button"
67
+ aria-label="Toggle filters"
68
+ aria-pressed={String(filtered.value)}
69
+ data-action="recipe-action"
70
+ data-recipe-command="filter"
71
+ >
72
+ <LucideIcon icon={Filter} name="filter" />
73
+ </button>
74
+ <button
75
+ type="button"
76
+ aria-label="Refresh tasks"
77
+ data-action="recipe-action"
78
+ data-recipe-command="refresh"
79
+ >
80
+ <LucideIcon icon={RefreshCw} name="refresh-cw" />
81
+ </button>
82
+ </ToolbarControlGroup>
83
+ <button
84
+ class="kui-recipe__button"
85
+ type="button"
86
+ aria-label="More task actions"
87
+ data-action="recipe-action"
88
+ data-recipe-command="more"
89
+ >
90
+ <LucideIcon icon={MoreHorizontal} name="more-horizontal" />
91
+ </button>
92
+ </div>
93
+ }
94
+ />
95
+ <div class="recipe-compact-toolbar__guide kui-content-item">
96
+ <div>
97
+ <strong>ToolbarControlGroup</strong>
98
+ <span class="kui-recipe__muted">
99
+ Related commands or pressed tools.
100
+ </span>
101
+ </div>
102
+ <div>
103
+ <strong>SegmentedControl</strong>
104
+ <span class="kui-recipe__muted">
105
+ A few visible exclusive choices.
106
+ </span>
107
+ </div>
108
+ <div>
109
+ <strong>Select</strong>
110
+ <span class="kui-recipe__muted">A longer controlled value list.</span>
111
+ </div>
112
+ <div>
113
+ <strong>Ordinary button</strong>
114
+ <span class="kui-recipe__muted">One independent command.</span>
115
+ </div>
116
+ </div>
117
+ <p class="kui-recipe__ownership kui-content-item">
118
+ The recipe owns control semantics and wrapping. The app owns values,
119
+ actions, persistence, and responsive priority.
120
+ </p>
121
+ </section>
122
+ );
123
+ return {
124
+ render,
125
+ action(command, element) {
126
+ const value = element.dataset.segmentValue;
127
+ if (value) view.value = value;
128
+ if (command === 'filter') filtered.value = !filtered.value;
129
+ announce(
130
+ value
131
+ ? `View: ${value}`
132
+ : command === 'filter'
133
+ ? `Filters ${filtered.value ? 'on' : 'off'}`
134
+ : `${command} requested`,
135
+ );
136
+ },
137
+ change(element) {
138
+ const value = (element as HTMLElement & { value?: string }).value;
139
+ if (element.getAttribute('name') === 'recipe-sort' && value) {
140
+ sort.value = value;
141
+ announce(`Sort: ${value}`);
142
+ }
143
+ },
144
+ };
145
+ };
@@ -0,0 +1,151 @@
1
+ import '@kerfjs/ui/layout.css';
2
+ import '@kerfjs/ui/select/register';
3
+ import '@kerfjs/ui/webawesome.css';
4
+ import '@awesome.me/webawesome/dist/components/button/button.js';
5
+ import '@awesome.me/webawesome/dist/components/input/input.js';
6
+ import '@awesome.me/webawesome/dist/components/textarea/textarea.js';
7
+ import './recipes.css';
8
+
9
+ import { PanelHeader } from '@kerfjs/ui/panel-header';
10
+ import { Select } from '@kerfjs/ui/select';
11
+ import { StateBanner } from '@kerfjs/ui/state-banner';
12
+ import { signal } from 'kerfjs';
13
+
14
+ import type { RecipeFactory } from './types.js';
15
+
16
+ type ValueField = HTMLElement & { value: string };
17
+
18
+ const syncControlledFieldValues = (
19
+ action: HTMLElement,
20
+ values: Record<string, string>,
21
+ ) => {
22
+ const form = action.closest<HTMLElement>(
23
+ '[data-recipe="recipe-composer-form"]',
24
+ );
25
+ if (!form) return;
26
+ for (const [name, value] of Object.entries(values)) {
27
+ const field = form.querySelector<ValueField>(`[name="${name}"]`);
28
+ if (field && field.value !== value) field.value = value;
29
+ }
30
+ };
31
+
32
+ export const createRecipe: RecipeFactory = (announce) => {
33
+ const title = signal('');
34
+ const body = signal(
35
+ 'The tablet layout now keeps navigation, content, and inspector focus order aligned.',
36
+ );
37
+ const audience = signal('team');
38
+ const status = signal<'idle' | 'error' | 'saved'>('idle');
39
+ const render = () => (
40
+ <form
41
+ class="kui-recipe recipe-form kui-recipe__surface kui-content"
42
+ data-recipe="recipe-composer-form"
43
+ aria-labelledby="recipe-composer-title"
44
+ aria-describedby="recipe-composer-summary"
45
+ noValidate
46
+ >
47
+ <PanelHeader
48
+ title="Publish workspace update"
49
+ titleId="recipe-composer-title"
50
+ summary="Share a concise, actionable update with collaborators."
51
+ summaryId="recipe-composer-summary"
52
+ />
53
+ {status.value === 'error' && (
54
+ <StateBanner
55
+ title="Add a title before publishing"
56
+ detail="The update body and audience are preserved."
57
+ tone="danger"
58
+ urgency="alert"
59
+ />
60
+ )}
61
+ {status.value === 'saved' && (
62
+ <StateBanner
63
+ title="Update published"
64
+ detail="The team audience can now read it."
65
+ tone="success"
66
+ />
67
+ )}
68
+ <div class="recipe-form__section recipe-form__fields">
69
+ <wa-input
70
+ name="recipe-title"
71
+ label="Update title"
72
+ hint="Summarize the outcome in one line."
73
+ required
74
+ value={title.value}
75
+ ></wa-input>
76
+ <wa-textarea
77
+ name="recipe-body"
78
+ label="Details"
79
+ hint="Include decisions, owners, and the next checkpoint."
80
+ rows="5"
81
+ maxlength="400"
82
+ with-count
83
+ value={body.value}
84
+ ></wa-textarea>
85
+ <Select<string>
86
+ name="recipe-audience"
87
+ value={audience.value}
88
+ label="Audience"
89
+ choices={[
90
+ { value: 'team', label: 'Workspace team' },
91
+ { value: 'reviewers', label: 'Reviewers' },
92
+ { value: 'organization', label: 'Entire organization' },
93
+ ]}
94
+ />
95
+ </div>
96
+ <footer class="recipe-form__section recipe-form__footer">
97
+ <div class="recipe-form__actions kui-control-cluster">
98
+ <wa-button
99
+ appearance="outlined"
100
+ data-action="recipe-action"
101
+ data-recipe-command="reset"
102
+ >
103
+ Reset
104
+ </wa-button>
105
+ <wa-button
106
+ variant="brand"
107
+ appearance="accent"
108
+ data-action="recipe-action"
109
+ data-recipe-command="submit"
110
+ >
111
+ Publish update
112
+ </wa-button>
113
+ </div>
114
+ <p class="kui-recipe__ownership">
115
+ The recipe owns field, message, and action rhythm. The app owns
116
+ validation rules, draft persistence, permissions, and transport.
117
+ </p>
118
+ </footer>
119
+ </form>
120
+ );
121
+ return {
122
+ render,
123
+ action(command, element) {
124
+ if (command === 'reset') {
125
+ title.value = '';
126
+ body.value = '';
127
+ status.value = 'idle';
128
+ syncControlledFieldValues(element, {
129
+ 'recipe-title': title.value,
130
+ 'recipe-body': body.value,
131
+ });
132
+ announce('Draft reset');
133
+ return;
134
+ }
135
+ if (command === 'submit') {
136
+ status.value = title.value.trim() ? 'saved' : 'error';
137
+ announce(
138
+ status.value === 'saved' ? 'Update published' : 'Title required',
139
+ );
140
+ }
141
+ },
142
+ change(element) {
143
+ const value =
144
+ (element as HTMLElement & { value?: string | null }).value ?? '';
145
+ if (element.getAttribute('name') === 'recipe-title') title.value = value;
146
+ if (element.getAttribute('name') === 'recipe-body') body.value = value;
147
+ if (element.getAttribute('name') === 'recipe-audience')
148
+ audience.value = value;
149
+ },
150
+ };
151
+ };
@@ -0,0 +1,181 @@
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 { PanelHeader } from '@kerfjs/ui/panel-header';
11
+ import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
12
+ import { signal } from 'kerfjs';
13
+ import { FileText } from 'lucide';
14
+
15
+ import type { RecipeFactory } from './types.js';
16
+
17
+ const records = {
18
+ alpha: {
19
+ name: 'Northstar migration',
20
+ owner: 'Mara Chen',
21
+ state: 'In review',
22
+ updated: 'Today, 09:42',
23
+ },
24
+ beta: {
25
+ name: 'Tablet navigation',
26
+ owner: 'Sam Rivera',
27
+ state: 'Ready',
28
+ updated: 'Yesterday, 16:20',
29
+ },
30
+ gamma: {
31
+ name: 'Contrast audit',
32
+ owner: 'Inez Okafor',
33
+ state: 'Blocked',
34
+ updated: 'Monday, 11:05',
35
+ },
36
+ } as const;
37
+
38
+ interface RecipeDialogElement extends HTMLElement {
39
+ open: boolean;
40
+ }
41
+
42
+ export const createRecipe: RecipeFactory = (announce) => {
43
+ const selected = signal<keyof typeof records>('alpha');
44
+ const open = { value: false };
45
+ const render = () => (
46
+ <section
47
+ class="kui-recipe kui-content"
48
+ data-recipe="recipe-list-detail-dialog"
49
+ >
50
+ <button
51
+ class="kui-recipe__button kui-content-item"
52
+ data-primary="true"
53
+ type="button"
54
+ data-action="recipe-action"
55
+ data-recipe-command="open"
56
+ >
57
+ Open project details
58
+ </button>
59
+ <p class="kui-recipe__ownership kui-content-item">
60
+ Web Awesome owns modal focus and dismissal. The recipe owns
61
+ header/body/list-detail anatomy; the app owns open state, selection, and
62
+ policy.
63
+ </p>
64
+ <wa-dialog
65
+ class="recipe-dialog"
66
+ label="Project details"
67
+ without-header
68
+ open={open.value}
69
+ >
70
+ <div class="recipe-dialog__pane kui-pane">
71
+ <div class="recipe-list-detail kui-pane__content">
72
+ <nav
73
+ class="recipe-list-detail__list kui-pane"
74
+ aria-label="Projects"
75
+ >
76
+ <ListHeader label="Recent projects" />
77
+ <div class="kui-pane__content kui-content">
78
+ <section>
79
+ {Object.entries(records).map(([id, record]) => (
80
+ <ListItem
81
+ action="recipe-action"
82
+ itemId={id}
83
+ label={record.name}
84
+ selected={selected.value === id}
85
+ multiline
86
+ />
87
+ ))}
88
+ </section>
89
+ </div>
90
+ </nav>
91
+ <section class="recipe-list-detail__detail kui-pane">
92
+ <PanelHeader
93
+ title="Project details"
94
+ titleId="recipe-dialog-title"
95
+ summary="Compare delivery state without leaving the workspace."
96
+ summaryId="recipe-dialog-summary"
97
+ icon={<LucideIcon icon={FileText} name="file-text" />}
98
+ actions={
99
+ <wa-button
100
+ appearance="plain"
101
+ data-action="recipe-action"
102
+ data-recipe-command="close"
103
+ >
104
+ Close
105
+ </wa-button>
106
+ }
107
+ />
108
+ <div
109
+ class="recipe-list-detail__body kui-pane__content kui-content"
110
+ aria-live="polite"
111
+ >
112
+ <h3 class="recipe-list-detail__title kui-recipe__pane-title kui-content-item">
113
+ {records[selected.value].name}
114
+ </h3>
115
+ <ValueTable label="Project details">
116
+ <ValueTableRow
117
+ label="Owner"
118
+ value={records[selected.value].owner}
119
+ />
120
+ <ValueTableRow
121
+ label="Status"
122
+ value={records[selected.value].state}
123
+ />
124
+ <ValueTableRow
125
+ label="Updated"
126
+ value={records[selected.value].updated}
127
+ />
128
+ </ValueTable>
129
+ <div class="recipe-list-detail__actions kui-control-cluster">
130
+ <wa-button
131
+ appearance="outlined"
132
+ data-action="recipe-action"
133
+ data-recipe-command="archive"
134
+ >
135
+ Archive
136
+ </wa-button>
137
+ <wa-button
138
+ variant="brand"
139
+ appearance="accent"
140
+ data-action="recipe-action"
141
+ data-recipe-command="open-record"
142
+ >
143
+ Open project
144
+ </wa-button>
145
+ </div>
146
+ </div>
147
+ </section>
148
+ </div>
149
+ </div>
150
+ </wa-dialog>
151
+ </section>
152
+ );
153
+ return {
154
+ render,
155
+ action(command, element) {
156
+ const id = element.dataset.itemId;
157
+ if (id && id in records) {
158
+ selected.value = id as keyof typeof records;
159
+ announce(`Selected ${records[selected.value].name}`);
160
+ return;
161
+ }
162
+ const dialog = element
163
+ .closest('[data-recipe]')
164
+ ?.querySelector<RecipeDialogElement>('wa-dialog');
165
+ if (command === 'open' && dialog) {
166
+ element.focus();
167
+ open.value = true;
168
+ dialog.open = true;
169
+ return;
170
+ }
171
+ if (command === 'close' && dialog) {
172
+ dialog.open = false;
173
+ return;
174
+ }
175
+ announce(`${command} requested`);
176
+ },
177
+ afterHide() {
178
+ open.value = false;
179
+ },
180
+ };
181
+ };