@kerfjs/ui 5.0.0-beta.49 → 5.0.0-beta.51

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 (319) hide show
  1. package/README.md +69 -15
  2. package/ai/catalog-authoring.json +12 -1
  3. package/ai/catalog-authoring.schema.json +49 -1
  4. package/ai/compile-time-contracts-v1.json +19 -2
  5. package/ai/compile-time-contracts-v1.schema.json +1 -0
  6. package/ai/component-catalog-v2-overrides.json +7 -0
  7. package/ai/component-catalog-v2.d.ts +19 -1
  8. package/ai/component-catalog-v2.json +920 -149
  9. package/ai/component-catalog-v2.schema.json +33 -1
  10. package/ai/component-catalog.json +689 -153
  11. package/ai/component-catalog.schema.json +21 -1
  12. package/ai/public-api-signatures-v1.md +404 -184
  13. package/ai/skill.md +31 -14
  14. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  15. package/analyzer/index.mjs +105 -35
  16. package/dist/app-tab.d.ts +3 -1
  17. package/dist/app-tab.js +1 -1
  18. package/dist/badge.d.ts +24 -0
  19. package/dist/badge.js +3 -0
  20. package/dist/badge.js.map +1 -0
  21. package/dist/browser/badge.js +3 -0
  22. package/dist/browser/list-header.js +1 -0
  23. package/dist/browser/state-banner.js +1 -0
  24. package/dist/catalog-resources.d.ts +2 -2
  25. package/dist/catalog.d.ts +47 -125
  26. package/dist/catalog.js +429 -262
  27. package/dist/catalog.js.map +1 -1
  28. package/dist/{chunk-W2FK7ZNY.js → chunk-4R777DUZ.js} +10 -2
  29. package/dist/chunk-4R777DUZ.js.map +1 -0
  30. package/dist/{chunk-OMSRSL3B.js → chunk-5FZZMIMD.js} +6 -4
  31. package/dist/chunk-5FZZMIMD.js.map +1 -0
  32. package/dist/{chunk-AQFWBG23.js → chunk-5PMGQDG7.js} +5 -3
  33. package/dist/chunk-5PMGQDG7.js.map +1 -0
  34. package/dist/{chunk-SOK5EUF7.js → chunk-6ZXOQEAN.js} +4 -2
  35. package/dist/chunk-6ZXOQEAN.js.map +1 -0
  36. package/dist/{chunk-I4V3UDSA.js → chunk-7GUQYHKA.js} +25 -6
  37. package/dist/chunk-7GUQYHKA.js.map +1 -0
  38. package/dist/{chunk-XKFRMGMW.js → chunk-7IUSV4XG.js} +3 -1
  39. package/dist/chunk-7IUSV4XG.js.map +1 -0
  40. package/dist/{chunk-IU4QUY5I.js → chunk-AEPXGCNF.js} +20 -3
  41. package/dist/chunk-AEPXGCNF.js.map +1 -0
  42. package/dist/{chunk-VDUJZF5G.js → chunk-CMUIXJZM.js} +2 -2
  43. package/dist/{chunk-VDUJZF5G.js.map → chunk-CMUIXJZM.js.map} +1 -1
  44. package/dist/{chunk-DAO2VFTT.js → chunk-D547PH4L.js} +7 -5
  45. package/dist/chunk-D547PH4L.js.map +1 -0
  46. package/dist/{chunk-2TYUFZUC.js → chunk-D7GWXIAS.js} +4 -2
  47. package/dist/chunk-D7GWXIAS.js.map +1 -0
  48. package/dist/{chunk-E34TE2ZI.js → chunk-DVZCMMB3.js} +6 -4
  49. package/dist/chunk-DVZCMMB3.js.map +1 -0
  50. package/dist/{chunk-6NDBE2OK.js → chunk-E36EVCKG.js} +5 -3
  51. package/dist/chunk-E36EVCKG.js.map +1 -0
  52. package/dist/chunk-EKYZCRCX.js +256 -0
  53. package/dist/chunk-EKYZCRCX.js.map +1 -0
  54. package/dist/{chunk-TIGSG6BX.js → chunk-HJKRCKDR.js} +7 -3
  55. package/dist/chunk-HJKRCKDR.js.map +1 -0
  56. package/dist/{chunk-Y6LYKFXY.js → chunk-HMQ2ZDGG.js} +5 -3
  57. package/dist/chunk-HMQ2ZDGG.js.map +1 -0
  58. package/dist/{chunk-UDHHKZWO.js → chunk-JM53SMVF.js} +9 -5
  59. package/dist/chunk-JM53SMVF.js.map +1 -0
  60. package/dist/{chunk-OGTJNHRG.js → chunk-JMGKM7RK.js} +6 -4
  61. package/dist/chunk-JMGKM7RK.js.map +1 -0
  62. package/dist/{chunk-GYGM363P.js → chunk-KX7DP2GO.js} +17 -7
  63. package/dist/chunk-KX7DP2GO.js.map +1 -0
  64. package/dist/{chunk-OL4TC3U2.js → chunk-MO56ZZLG.js} +6 -2
  65. package/dist/chunk-MO56ZZLG.js.map +1 -0
  66. package/dist/{chunk-VXFGMDA7.js → chunk-MTM43SJO.js} +16 -2
  67. package/dist/chunk-MTM43SJO.js.map +1 -0
  68. package/dist/{chunk-ZPWZIVI6.js → chunk-N3GKMPDW.js} +7 -3
  69. package/dist/chunk-N3GKMPDW.js.map +1 -0
  70. package/dist/{chunk-DYBPI5O2.js → chunk-NDUGJ4TU.js} +4 -2
  71. package/dist/chunk-NDUGJ4TU.js.map +1 -0
  72. package/dist/{chunk-I45QO2IZ.js → chunk-NMGTHYW3.js} +7 -5
  73. package/dist/chunk-NMGTHYW3.js.map +1 -0
  74. package/dist/{chunk-WSXUPSID.js → chunk-NN6GYOA2.js} +7 -3
  75. package/dist/chunk-NN6GYOA2.js.map +1 -0
  76. package/dist/{chunk-OGNE3OFT.js → chunk-NZXWU4R5.js} +24 -3
  77. package/dist/chunk-NZXWU4R5.js.map +1 -0
  78. package/dist/{chunk-V6GQKOX2.js → chunk-O3RCCSQJ.js} +6 -4
  79. package/dist/chunk-O3RCCSQJ.js.map +1 -0
  80. package/dist/{chunk-WVLMZ5SG.js → chunk-S4QRSBFI.js} +25 -3
  81. package/dist/chunk-S4QRSBFI.js.map +1 -0
  82. package/dist/{chunk-CK6P5AUK.js → chunk-UBH743HV.js} +10 -7
  83. package/dist/chunk-UBH743HV.js.map +1 -0
  84. package/dist/{chunk-T7DOWS6T.js → chunk-ULCDZTWL.js} +7 -4
  85. package/dist/chunk-ULCDZTWL.js.map +1 -0
  86. package/dist/{chunk-V6HCTCVD.js → chunk-UPZ6EEJN.js} +9 -5
  87. package/dist/chunk-UPZ6EEJN.js.map +1 -0
  88. package/dist/{chunk-CAB6NQ2T.js → chunk-VOCPXGQN.js} +3 -1
  89. package/dist/chunk-VOCPXGQN.js.map +1 -0
  90. package/dist/chunk-XD7DWYXG.js +34 -0
  91. package/dist/chunk-XD7DWYXG.js.map +1 -0
  92. package/dist/{chunk-B6JYRX3P.js → chunk-ZIYOJFZD.js} +43 -4
  93. package/dist/chunk-ZIYOJFZD.js.map +1 -0
  94. package/dist/{chunk-EYALJQQD.js → chunk-ZK5EGVFP.js} +4 -2
  95. package/dist/chunk-ZK5EGVFP.js.map +1 -0
  96. package/dist/{chunk-6WTETUZ6.js → chunk-ZLWQZUGF.js} +8 -2
  97. package/dist/chunk-ZLWQZUGF.js.map +1 -0
  98. package/dist/collapsible-panel.d.ts +4 -2
  99. package/dist/collapsible-panel.js +4 -2
  100. package/dist/collapsible-panel.js.map +1 -1
  101. package/dist/css-values.d.ts +39 -3
  102. package/dist/css-values.js +1 -1
  103. package/dist/disclosure-arrow.d.ts +3 -1
  104. package/dist/disclosure-arrow.js +1 -1
  105. package/dist/empty-state.d.ts +3 -1
  106. package/dist/empty-state.js +1 -1
  107. package/dist/floating-toolbar.d.ts +3 -1
  108. package/dist/floating-toolbar.js +1 -1
  109. package/dist/grid.d.ts +17 -1
  110. package/dist/grid.js +2 -1
  111. package/dist/index.d.ts +4 -3
  112. package/dist/index.js +34 -33
  113. package/dist/list-action-row.d.ts +3 -1
  114. package/dist/list-action-row.js +1 -1
  115. package/dist/list-header.d.ts +6 -1
  116. package/dist/list-header.js +2 -1
  117. package/dist/list-inset-control.d.ts +3 -1
  118. package/dist/list-inset-control.js +1 -1
  119. package/dist/list-inset-text.d.ts +3 -1
  120. package/dist/list-inset-text.js +1 -1
  121. package/dist/list-item.d.ts +3 -1
  122. package/dist/list-item.js +1 -1
  123. package/dist/list.d.ts +23 -2
  124. package/dist/list.js +2 -1
  125. package/dist/loading-spinner.d.ts +3 -1
  126. package/dist/loading-spinner.js +1 -1
  127. package/dist/lucide-icon.d.ts +3 -1
  128. package/dist/lucide-icon.js +1 -1
  129. package/dist/nav-stack.d.ts +3 -1
  130. package/dist/nav-stack.js +1 -1
  131. package/dist/pane.d.ts +19 -2
  132. package/dist/pane.js +1 -1
  133. package/dist/resizable-region.d.ts +5 -2
  134. package/dist/resizable-region.js +1 -1
  135. package/dist/row.d.ts +21 -1
  136. package/dist/row.js +2 -1
  137. package/dist/segmented-control.d.ts +3 -1
  138. package/dist/segmented-control.js +1 -1
  139. package/dist/select.d.ts +16 -4
  140. package/dist/select.js +1 -1
  141. package/dist/skeleton.d.ts +3 -1
  142. package/dist/skeleton.js +1 -1
  143. package/dist/spacer.d.ts +3 -1
  144. package/dist/spacer.js +1 -1
  145. package/dist/split-view.d.ts +3 -1
  146. package/dist/split-view.js +24 -5
  147. package/dist/split-view.js.map +1 -1
  148. package/dist/state-banner.d.ts +3 -1
  149. package/dist/state-banner.js +2 -1
  150. package/dist/styles/badge.css +86 -0
  151. package/dist/styles/catalog/components/catalog-detail.css +35 -0
  152. package/dist/styles/catalog/components/catalog-example-stack.css +9 -0
  153. package/dist/styles/catalog/components/catalog-example.css +180 -0
  154. package/dist/styles/catalog/components/catalog-resource-footer.css +37 -0
  155. package/dist/styles/catalog/components/catalog-secondary-sections.css +38 -0
  156. package/dist/styles/catalog/components/catalog-section-list.css +22 -0
  157. package/dist/styles/catalog/components/catalog-sidebar.css +82 -0
  158. package/dist/styles/catalog/components/catalog-stage.css +60 -0
  159. package/dist/styles/catalog/components/catalog.css +22 -0
  160. package/dist/styles/catalog.css +10 -587
  161. package/dist/styles/collapsible-panel.css +132 -4
  162. package/dist/styles/document.css +23 -0
  163. package/dist/styles/foundation.css +21 -7
  164. package/dist/styles/grid.css +10 -0
  165. package/dist/styles/list-header.css +49 -60
  166. package/dist/styles/list-inset-control.css +8 -1
  167. package/dist/styles/list-item.css +6 -0
  168. package/dist/styles/list.css +11 -0
  169. package/dist/styles/nav-stack.css +89 -2
  170. package/dist/styles/pane.css +86 -1
  171. package/dist/styles/resizable-region.css +117 -5
  172. package/dist/styles/row.css +10 -0
  173. package/dist/styles/select.css +39 -7
  174. package/dist/styles/split-view.css +108 -2
  175. package/dist/styles/state-banner.css +0 -27
  176. package/dist/styles/styles.css +1 -0
  177. package/dist/styles/tab-bar.css +6 -1
  178. package/dist/styles/tab-scaffold.css +64 -1
  179. package/dist/styles/text.css +10 -0
  180. package/dist/styles/token-search-field.css +5 -40
  181. package/dist/styles/toolbar-control-group.css +198 -25
  182. package/dist/styles/toolbar-text.css +8 -1
  183. package/dist/styles/toolbar.css +169 -5
  184. package/dist/styles/webawesome.css +70 -9
  185. package/dist/styles/workbench.css +136 -4
  186. package/dist/sunken-panel.d.ts +3 -1
  187. package/dist/sunken-panel.js +1 -1
  188. package/dist/surface-scaffold.d.ts +6 -2
  189. package/dist/surface-scaffold.js +1 -1
  190. package/dist/tab-bar.d.ts +8 -1
  191. package/dist/tab-bar.js +1 -1
  192. package/dist/tab-scaffold.d.ts +3 -1
  193. package/dist/tab-scaffold.js +3 -1
  194. package/dist/tab-scaffold.js.map +1 -1
  195. package/dist/text.d.ts +9 -5
  196. package/dist/text.js +1 -1
  197. package/dist/token-search-field.d.ts +5 -1
  198. package/dist/token-search-field.js +1 -1
  199. package/dist/toolbar-control-group.d.ts +33 -3
  200. package/dist/toolbar-control-group.js +1 -1
  201. package/dist/toolbar-text.d.ts +3 -1
  202. package/dist/toolbar-text.js +1 -1
  203. package/dist/toolbar.d.ts +30 -1
  204. package/dist/toolbar.js +1 -1
  205. package/dist/types-CEsieBZi.d.ts +69 -0
  206. package/dist/value-table.d.ts +6 -2
  207. package/dist/value-table.js +1 -1
  208. package/dist/wire-resizable-regions.js +1 -1
  209. package/dist/wire-sidebar.d.ts +18 -3
  210. package/dist/wire-sidebar.js +95 -14
  211. package/dist/wire-sidebar.js.map +1 -1
  212. package/dist/wire-token-search-fields.js +1 -1
  213. package/dist/workbench.d.ts +3 -1
  214. package/dist/workbench.js +3 -1
  215. package/dist/workbench.js.map +1 -1
  216. package/docs/accessibility.md +56 -9
  217. package/docs/app-layouts.md +73 -0
  218. package/docs/catalog.md +48 -9
  219. package/docs/collapsible-panel.md +81 -8
  220. package/docs/component-contract.md +96 -28
  221. package/docs/component-integration.md +9 -4
  222. package/docs/component-selection.md +36 -15
  223. package/docs/design/templates/list/stack-dark.svg +1 -1
  224. package/docs/design/templates/list/stack.svg +1 -1
  225. package/docs/design/templates/list-dark.svg +1 -1
  226. package/docs/design/templates/list-header/action-dark.svg +1 -1
  227. package/docs/design/templates/list-header/action.svg +1 -1
  228. package/docs/design/templates/list-header/count-dark.svg +1 -1
  229. package/docs/design/templates/list-header/count.svg +1 -1
  230. package/docs/design/templates/list-header/inline-action-dark.svg +1 -1
  231. package/docs/design/templates/list-header/inline-action.svg +1 -1
  232. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -1
  233. package/docs/design/templates/list-header/toggle-expanded.svg +1 -1
  234. package/docs/design/templates/list-header-dark.svg +1 -1
  235. package/docs/design/templates/list-header.svg +1 -1
  236. package/docs/design/templates/list.svg +1 -1
  237. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
  238. package/docs/design/templates/state-banner/info-badge.svg +1 -1
  239. package/docs/design/templates/state-banner-dark.svg +1 -1
  240. package/docs/design/templates/state-banner.svg +1 -1
  241. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  242. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  243. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
  244. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
  245. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  246. package/docs/design/templates/toolbar-control-group.svg +1 -1
  247. package/docs/design/templates/toolbar-dark.svg +1 -1
  248. package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
  249. package/docs/design/templates/toolbar-text/default.svg +1 -1
  250. package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
  251. package/docs/design/templates/toolbar-text/large.svg +1 -1
  252. package/docs/design/templates/toolbar-text/small-dark.svg +1 -1
  253. package/docs/design/templates/toolbar-text/small.svg +1 -1
  254. package/docs/design/templates/toolbar-text-dark.svg +1 -1
  255. package/docs/design/templates/toolbar-text.svg +1 -1
  256. package/docs/design/templates/toolbar.svg +1 -1
  257. package/docs/design-philosophy.md +6 -5
  258. package/docs/document-baseline.md +31 -0
  259. package/docs/layout.md +86 -21
  260. package/docs/nav-stack.md +8 -0
  261. package/docs/recipes.md +78 -43
  262. package/docs/split-view.md +8 -0
  263. package/docs/tab-scaffold.md +4 -2
  264. package/docs/type-contracts.md +21 -16
  265. package/docs/ui-analyzer.md +19 -0
  266. package/docs/ui-evaluator.md +17 -0
  267. package/docs/ux-demo.md +8 -8
  268. package/docs/webawesome-theme.md +25 -6
  269. package/docs/workbench.md +20 -1
  270. package/evaluator/index.mjs +73 -8
  271. package/llms.txt +61 -23
  272. package/package.json +12 -3
  273. package/ux-demo/recipes/app-shell.tsx +244 -184
  274. package/ux-demo/recipes/collapsible-sidebar.tsx +175 -116
  275. package/ux-demo/recipes/compact-toolbar.tsx +119 -102
  276. package/ux-demo/recipes/composer-form.tsx +79 -69
  277. package/ux-demo/recipes/list-detail-dialog.tsx +120 -136
  278. package/ux-demo/recipes/list-workspace-states.tsx +85 -79
  279. package/ux-demo/recipes/loaders.ts +5 -2
  280. package/ux-demo/recipes/loading-inspector.tsx +151 -134
  281. package/ux-demo/recipes/navigation-sidebar.tsx +93 -80
  282. package/ux-demo/recipes/navigation-stack.tsx +33 -22
  283. package/ux-demo/recipes/types.ts +14 -0
  284. package/ux-demo/recipes/workspace-header.tsx +73 -57
  285. package/dist/chunk-2TYUFZUC.js.map +0 -1
  286. package/dist/chunk-6NDBE2OK.js.map +0 -1
  287. package/dist/chunk-6WTETUZ6.js.map +0 -1
  288. package/dist/chunk-AQFWBG23.js.map +0 -1
  289. package/dist/chunk-B6JYRX3P.js.map +0 -1
  290. package/dist/chunk-CAB6NQ2T.js.map +0 -1
  291. package/dist/chunk-CK6P5AUK.js.map +0 -1
  292. package/dist/chunk-DAO2VFTT.js.map +0 -1
  293. package/dist/chunk-DYBPI5O2.js.map +0 -1
  294. package/dist/chunk-E34TE2ZI.js.map +0 -1
  295. package/dist/chunk-EYALJQQD.js.map +0 -1
  296. package/dist/chunk-GYGM363P.js.map +0 -1
  297. package/dist/chunk-I45QO2IZ.js.map +0 -1
  298. package/dist/chunk-I4V3UDSA.js.map +0 -1
  299. package/dist/chunk-IU4QUY5I.js.map +0 -1
  300. package/dist/chunk-OGNE3OFT.js.map +0 -1
  301. package/dist/chunk-OGTJNHRG.js.map +0 -1
  302. package/dist/chunk-OL4TC3U2.js.map +0 -1
  303. package/dist/chunk-OMSRSL3B.js.map +0 -1
  304. package/dist/chunk-SOK5EUF7.js.map +0 -1
  305. package/dist/chunk-T7DOWS6T.js.map +0 -1
  306. package/dist/chunk-TIGSG6BX.js.map +0 -1
  307. package/dist/chunk-UDHHKZWO.js.map +0 -1
  308. package/dist/chunk-V6GQKOX2.js.map +0 -1
  309. package/dist/chunk-V6HCTCVD.js.map +0 -1
  310. package/dist/chunk-VXFGMDA7.js.map +0 -1
  311. package/dist/chunk-W2FK7ZNY.js.map +0 -1
  312. package/dist/chunk-WN52USHV.js +0 -115
  313. package/dist/chunk-WN52USHV.js.map +0 -1
  314. package/dist/chunk-WSXUPSID.js.map +0 -1
  315. package/dist/chunk-WVLMZ5SG.js.map +0 -1
  316. package/dist/chunk-XKFRMGMW.js.map +0 -1
  317. package/dist/chunk-Y6LYKFXY.js.map +0 -1
  318. package/dist/chunk-ZPWZIVI6.js.map +0 -1
  319. package/ux-demo/recipes/recipes.css +0 -453
@@ -1,28 +1,44 @@
1
1
  import '@kerfjs/ui/layout.css';
2
2
  import '@kerfjs/ui/collapsible-panel.css';
3
- import './recipes.css';
4
3
 
5
4
  import {
6
5
  CollapsiblePanel,
7
6
  CollapsiblePanelToggle,
8
7
  } from '@kerfjs/ui/collapsible-panel';
9
8
  import { deviceClass } from '@kerfjs/ui/device-class';
9
+ import { Grid } from '@kerfjs/ui/grid';
10
+ import { List } from '@kerfjs/ui/list';
10
11
  import { ListHeader } from '@kerfjs/ui/list-header';
11
12
  import { ListItem } from '@kerfjs/ui/list-item';
12
13
  import { LucideIcon } from '@kerfjs/ui/lucide-icon';
14
+ import { Pane } from '@kerfjs/ui/pane';
15
+ import { Row } from '@kerfjs/ui/row';
13
16
  import { Text } from '@kerfjs/ui/text';
14
17
  import { Toolbar } from '@kerfjs/ui/toolbar';
15
18
  import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
16
19
  import { ToolbarText } from '@kerfjs/ui/toolbar-text';
20
+ import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
17
21
  import { type SidebarStorage, wireSidebar } from '@kerfjs/ui/wire-sidebar';
18
22
  import { signal } from 'kerfjs';
19
23
  import { Bell, Folder, Inbox, Users } from 'lucide';
20
24
 
21
- import type { RecipeFactory } from './types.js';
25
+ import type { RecipeFactory, RecipePresentation } from './types.js';
22
26
 
23
27
  const RAIL_ACTION = 'recipe-sidebar-rail';
24
28
  const DRAWER_ACTION = 'recipe-sidebar-drawer';
25
29
 
30
+ export const presentation: RecipePresentation = {
31
+ viewport: {
32
+ width: 'full',
33
+ height: 'app',
34
+ frame: 'solid',
35
+ surface: 'default',
36
+ overflow: 'hidden',
37
+ shadow: true,
38
+ },
39
+ note: "The app owns each panel's collapsed signal, sizes, and content; wireSidebar owns the toggle, focus move/restore, the compact overlay, and persistence.",
40
+ };
41
+
26
42
  /**
27
43
  * A memory-backed store so the recipe demonstrates the persistence hook without
28
44
  * writing to the shared `localStorage` the catalog page runs in.
@@ -37,23 +53,31 @@ function memoryStorage(): SidebarStorage {
37
53
  };
38
54
  }
39
55
 
56
+ const labels: Record<string, string> = {
57
+ inbox: 'Inbox',
58
+ projects: 'Projects',
59
+ shared: 'Shared with me',
60
+ };
61
+
40
62
  export const createRecipe: RecipeFactory = (announce) => {
41
63
  const selected = signal('inbox');
42
- const railCollapsed = signal(false);
43
- const drawerCollapsed = signal(true);
44
- const storage = memoryStorage();
45
64
  // A shared window-backed device class: when the viewport is compact the wire
46
65
  // switches the rail/drawer to a dismissable overlay presentation.
47
66
  const device = deviceClass();
67
+ // A compact overlay only opens on a user action, so the first render already
68
+ // matches: collapsed on a compact device, open inline otherwise. The wire's
69
+ // `inlineCollapsed` keeps the open inline default for a later wide crossing.
70
+ const railCollapsed = signal(device.value.compact);
71
+ const drawerCollapsed = signal(true);
72
+ const storage = memoryStorage();
48
73
 
49
- const railToggle = (inPanel: boolean) => (
74
+ const railToggle = () => (
50
75
  <CollapsiblePanelToggle
51
76
  side="left"
52
77
  collapsed={railCollapsed.value}
53
78
  action={RAIL_ACTION}
54
79
  panelId="sidebar-rail"
55
80
  label={railCollapsed.value ? 'Show navigation' : 'Hide navigation'}
56
- className={inPanel ? '' : 'recipe-collapsible-sidebar__reveal'}
57
81
  />
58
82
  );
59
83
  const drawerToggle = () => (
@@ -66,133 +90,167 @@ export const createRecipe: RecipeFactory = (announce) => {
66
90
  />
67
91
  );
68
92
 
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)}
93
+ const rail = () => (
94
+ <CollapsiblePanel
95
+ id="sidebar-rail"
96
+ side="left"
97
+ size={232}
98
+ collapsed={railCollapsed.value}
99
+ label="Workspace navigation"
75
100
  >
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"
101
+ <Pane
102
+ contentElement="nav"
103
+ contentLabel="Workspace"
104
+ header={
105
+ <Toolbar
106
+ label="Workspace navigation"
107
+ dividerSides=""
108
+ leading={<ToolbarText text="Atlas" />}
109
+ trailing={
110
+ <ToolbarControlGroup appearance="borderless" single>
111
+ {railToggle()}
112
+ </ToolbarControlGroup>
113
+ }
114
+ />
115
+ }
83
116
  >
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">
117
+ <section>
118
+ <ListHeader label="Workspace" />
119
+ <ListItem
120
+ action="recipe-action"
121
+ itemId="inbox"
122
+ label="Inbox"
123
+ icon={<LucideIcon icon={Inbox} name="inbox" />}
124
+ trailing={<span>12</span>}
125
+ selected={selected.value === 'inbox'}
126
+ />
127
+ <ListItem
128
+ action="recipe-action"
129
+ itemId="projects"
130
+ label="Projects"
131
+ icon={<LucideIcon icon={Folder} name="folder" />}
132
+ selected={selected.value === 'projects'}
133
+ />
134
+ <ListItem
135
+ action="recipe-action"
136
+ itemId="shared"
137
+ label="Shared with me"
138
+ icon={<LucideIcon icon={Users} name="users" />}
139
+ selected={selected.value === 'shared'}
140
+ />
141
+ </section>
142
+ </Pane>
143
+ </CollapsiblePanel>
144
+ );
145
+
146
+ // Each panel's own header holds its collapse toggle; the always-visible main
147
+ // header holds its expand toggle only while it is collapsed, so exactly one
148
+ // control owns each action. wireSidebar hands focus between them.
149
+ const main = () => (
150
+ <Pane
151
+ element="main"
152
+ header={
122
153
  <Toolbar
123
154
  label="Current navigation view"
124
155
  dividerSides=""
125
156
  leading={
126
157
  <>
127
- <ToolbarControlGroup appearance="borderless" single>
128
- {railToggle(false)}
129
- </ToolbarControlGroup>
158
+ {railCollapsed.value ? (
159
+ <ToolbarControlGroup appearance="borderless" single>
160
+ {railToggle()}
161
+ </ToolbarControlGroup>
162
+ ) : null}
130
163
  <ToolbarText
131
- text={
132
- selected.value === 'projects'
133
- ? 'Projects'
134
- : selected.value === 'shared'
135
- ? 'Shared with me'
136
- : 'Inbox'
137
- }
164
+ text={labels[selected.value] ?? 'Inbox'}
138
165
  size="xlarge"
139
166
  id="recipe-collapsible-main-title"
140
167
  />
141
168
  </>
142
169
  }
143
170
  trailing={
144
- <ToolbarControlGroup appearance="borderless" single>
145
- {drawerToggle()}
146
- </ToolbarControlGroup>
171
+ drawerCollapsed.value ? (
172
+ <ToolbarControlGroup appearance="borderless" single>
173
+ {drawerToggle()}
174
+ </ToolbarControlGroup>
175
+ ) : undefined
147
176
  }
148
177
  />
149
- <div class="recipe-collapsible-sidebar__body kui-pane__content kui-content">
150
- <Text class="kui-recipe__ownership kui-content-item">
151
- The app owns each panel's <code>collapsed</code> signal, sizes, and
152
- content; <code>wireSidebar</code> owns the toggle, focus
153
- move/restore, the compact overlay, and persistence.
154
- </Text>
155
- <article class="recipe-collapsible-sidebar__card kui-content-item">
178
+ }
179
+ >
180
+ <div class="kui-content-item">
181
+ <List gap="2xs">
182
+ <Text variant="span">
156
183
  <strong>Narrow the window to a compact width</strong>
157
- <Text class="kui-recipe__muted">
158
- The rail and drawer become a dismissable overlay: a backdrop,
159
- Escape, and a trapped Tab ring, all managed by the wire.
160
- </Text>
161
- </article>
162
- </div>
163
- </main>
164
- <CollapsiblePanel
165
- id="sidebar-console"
166
- side="bottom"
167
- size={168}
168
- collapsed={drawerCollapsed.value}
169
- label="Activity"
170
- className="recipe-collapsible-sidebar__drawer kui-pane"
184
+ </Text>
185
+ <Text variant="span" tone="quiet" size="compact">
186
+ The rail and drawer start closed and open as a dismissable overlay:
187
+ a backdrop, Escape, and a trapped Tab ring, all managed by the wire.
188
+ </Text>
189
+ </List>
190
+ </div>
191
+ </Pane>
192
+ );
193
+
194
+ const drawer = () => (
195
+ <CollapsiblePanel
196
+ id="sidebar-console"
197
+ side="bottom"
198
+ size={168}
199
+ collapsed={drawerCollapsed.value}
200
+ label="Activity"
201
+ >
202
+ <Pane
203
+ header={
204
+ <Toolbar
205
+ label="Activity"
206
+ dividerSides="b"
207
+ leading={<ToolbarText text="Activity" size="small" />}
208
+ trailing={
209
+ <ToolbarControlGroup appearance="borderless" single>
210
+ {drawerToggle()}
211
+ </ToolbarControlGroup>
212
+ }
213
+ />
214
+ }
171
215
  >
172
- <Toolbar
173
- label="Activity"
174
- dividerSides="b"
175
- leading={<ToolbarText text="Activity" size="small" />}
176
- trailing={
177
- <ToolbarControlGroup appearance="borderless" single>
178
- {drawerToggle()}
179
- </ToolbarControlGroup>
180
- }
181
- />
182
- <div class="recipe-collapsible-sidebar__drawer-body kui-pane__content kui-content">
183
- <ul class="recipe-collapsible-sidebar__log">
184
- <li>
185
- <LucideIcon icon={Bell} name="bell" />
186
- Deploy finished · 2m ago
187
- </li>
188
- <li>
189
- <LucideIcon icon={Bell} name="bell" />
190
- Review requested · 9m ago
191
- </li>
192
- </ul>
193
- </div>
194
- </CollapsiblePanel>
195
- </section>
216
+ <ValueTable label="Recent activity">
217
+ <ValueTableRow
218
+ label="Deploy finished"
219
+ value="2m ago"
220
+ icon={<LucideIcon icon={Bell} name="bell" />}
221
+ />
222
+ <ValueTableRow
223
+ label="Review requested"
224
+ value="9m ago"
225
+ icon={<LucideIcon icon={Bell} name="bell" />}
226
+ />
227
+ </ValueTable>
228
+ </Pane>
229
+ </CollapsiblePanel>
230
+ );
231
+
232
+ // The root Row fills the frame's definite height with the rail beside a
233
+ // column whose main pane grows above the bottom drawer. Each CollapsiblePanel
234
+ // docks to a real frame edge, so it paints through that edge's safe area and
235
+ // hands the edge back to its siblings when it collapses.
236
+ const render = () => (
237
+ <Row
238
+ gap="none"
239
+ fill
240
+ rootAttributes={{
241
+ 'data-recipe': 'recipe-collapsible-sidebar',
242
+ 'data-rail-collapsed': String(railCollapsed.value),
243
+ 'data-drawer-collapsed': String(drawerCollapsed.value),
244
+ }}
245
+ >
246
+ {rail()}
247
+ <List flex>
248
+ <Grid columns={1} gap="none" flex>
249
+ {main()}
250
+ </Grid>
251
+ {drawer()}
252
+ </List>
253
+ </Row>
196
254
  );
197
255
 
198
256
  return {
@@ -216,6 +274,7 @@ export const createRecipe: RecipeFactory = (announce) => {
216
274
  collapsed: railCollapsed,
217
275
  toggleAction: RAIL_ACTION,
218
276
  storageKey: 'recipe-sidebar-rail',
277
+ inlineCollapsed: false,
219
278
  },
220
279
  {
221
280
  id: 'sidebar-console',
@@ -1,7 +1,10 @@
1
1
  import '@kerfjs/ui/layout.css';
2
2
  import '@kerfjs/ui/select/register';
3
- import './recipes.css';
4
3
 
4
+ import { deviceClass } from '@kerfjs/ui/device-class';
5
+ import { Grid } from '@kerfjs/ui/grid';
6
+ import { List } from '@kerfjs/ui/list';
7
+ import { ListInsetText } from '@kerfjs/ui/list-inset-text';
5
8
  import { LucideIcon } from '@kerfjs/ui/lucide-icon';
6
9
  import { SegmentedControl } from '@kerfjs/ui/segmented-control';
7
10
  import { Select } from '@kerfjs/ui/select';
@@ -12,114 +15,128 @@ import { ToolbarText } from '@kerfjs/ui/toolbar-text';
12
15
  import { signal } from 'kerfjs';
13
16
  import { Filter, MoreHorizontal, RefreshCw } from 'lucide';
14
17
 
15
- import type { RecipeFactory } from './types.js';
18
+ import type { RecipeFactory, RecipePresentation } from './types.js';
19
+
20
+ const guide = [
21
+ ['ToolbarControlGroup', 'Related commands or pressed tools.'],
22
+ ['SegmentedControl', 'A few visible exclusive choices.'],
23
+ ['Select', 'A longer controlled value list.'],
24
+ ['Ordinary button', 'One independent command.'],
25
+ ] as const;
26
+
27
+ export const presentation: RecipePresentation = {
28
+ viewport: {
29
+ width: 'full',
30
+ frame: 'solid',
31
+ surface: 'default',
32
+ overflow: 'hidden',
33
+ shadow: true,
34
+ },
35
+ note: 'The recipe owns control semantics and wrapping. The app owns values, actions, persistence, and responsive priority.',
36
+ };
16
37
 
17
38
  export const createRecipe: RecipeFactory = (announce) => {
18
39
  const view = signal('list');
19
40
  const sort = signal('updated');
20
41
  const filtered = signal(false);
42
+ const device = deviceClass();
21
43
  const render = () => (
22
- <section
23
- class="kui-recipe recipe-compact-toolbar kui-recipe__surface kui-layout"
24
- data-recipe="recipe-compact-toolbar"
25
- >
26
- <Toolbar
27
- label="Task workspace controls"
28
- leading={
29
- <ToolbarControlGroup appearance="borderless" single>
30
- <ToolbarText text="Tasks" size="small" />
31
- </ToolbarControlGroup>
32
- }
33
- center={
34
- <ToolbarControlGroup>
35
- <SegmentedControl<string>
36
- id="recipe-view"
37
- label="View"
38
- value={view.value}
39
- action="recipe-action"
40
- appearance="toolbar"
41
- shape="pill"
42
- size="small"
43
- choices={[
44
- { value: 'list', label: 'List' },
45
- { value: 'board', label: 'Board' },
46
- { value: 'timeline', label: 'Timeline' },
47
- ]}
48
- />
49
- </ToolbarControlGroup>
50
- }
51
- trailing={
52
- <div class="kui-control-cluster">
53
- <Select<string>
54
- name="recipe-sort"
55
- value={sort.value}
56
- ariaLabel="Sort tasks"
57
- fitMenu
58
- choices={[
59
- { value: 'updated', label: 'Recently updated' },
60
- { value: 'priority', label: 'Priority' },
61
- { value: 'owner', label: 'Owner' },
62
- { value: 'created', label: 'Created date' },
63
- ]}
64
- />
65
- <ToolbarControlGroup label="Task actions" buttonAppearance="push">
66
- <button
67
- type="button"
68
- aria-label="Toggle filters"
69
- aria-pressed={String(filtered.value)}
70
- data-action="recipe-action"
71
- data-recipe-command="filter"
72
- >
73
- <LucideIcon icon={Filter} name="filter" />
74
- </button>
75
- <button
76
- type="button"
77
- aria-label="Refresh tasks"
78
- data-action="recipe-action"
79
- data-recipe-command="refresh"
80
- >
81
- <LucideIcon icon={RefreshCw} name="refresh-cw" />
82
- </button>
83
- </ToolbarControlGroup>
84
- <ToolbarControlGroup label="More task actions" single>
85
- <button
86
- type="button"
87
- aria-label="More task actions"
88
- data-action="recipe-action"
89
- data-recipe-command="more"
90
- >
91
- <LucideIcon icon={MoreHorizontal} name="more-horizontal" />
92
- </button>
44
+ <section data-recipe="recipe-compact-toolbar" aria-label="Task workspace">
45
+ <List gap="l" controlInsets="b">
46
+ <Toolbar
47
+ label="Task workspace controls"
48
+ responsive="stack"
49
+ responsiveAt="compact"
50
+ leading={<ToolbarText text="Tasks" size="small" />}
51
+ center={
52
+ <ToolbarControlGroup>
53
+ <SegmentedControl<string>
54
+ id="recipe-view"
55
+ label="View"
56
+ value={view.value}
57
+ action="recipe-action"
58
+ appearance="toolbar"
59
+ shape="pill"
60
+ size="small"
61
+ choices={[
62
+ { value: 'list', label: 'List' },
63
+ { value: 'board', label: 'Board' },
64
+ { value: 'timeline', label: 'Timeline' },
65
+ ]}
66
+ />
93
67
  </ToolbarControlGroup>
94
- </div>
95
- }
96
- />
97
- <div class="recipe-compact-toolbar__guide kui-content-item">
98
- <div>
99
- <strong>ToolbarControlGroup</strong>
100
- <span class="kui-recipe__muted">
101
- Related commands or pressed tools.
102
- </span>
103
- </div>
104
- <div>
105
- <strong>SegmentedControl</strong>
106
- <span class="kui-recipe__muted">
107
- A few visible exclusive choices.
108
- </span>
109
- </div>
110
- <div>
111
- <strong>Select</strong>
112
- <span class="kui-recipe__muted">A longer controlled value list.</span>
113
- </div>
114
- <div>
115
- <strong>Ordinary button</strong>
116
- <span class="kui-recipe__muted">One independent command.</span>
117
- </div>
118
- </div>
119
- <Text class="kui-recipe__ownership kui-content-item">
120
- The recipe owns control semantics and wrapping. The app owns values,
121
- actions, persistence, and responsive priority.
122
- </Text>
68
+ }
69
+ trailing={
70
+ <>
71
+ <ToolbarControlGroup single focusRing="outline">
72
+ <Select<string>
73
+ name="recipe-sort"
74
+ value={sort.value}
75
+ ariaLabel="Sort tasks"
76
+ fitMenu
77
+ presentation="toolbar-borderless"
78
+ size="compact"
79
+ focusRingOwner="group"
80
+ choices={[
81
+ { value: 'updated', label: 'Recently updated' },
82
+ { value: 'priority', label: 'Priority' },
83
+ { value: 'owner', label: 'Owner' },
84
+ { value: 'created', label: 'Created date' },
85
+ ]}
86
+ />
87
+ </ToolbarControlGroup>
88
+ <ToolbarControlGroup label="Task actions" buttonAppearance="push">
89
+ <button
90
+ type="button"
91
+ aria-label="Toggle filters"
92
+ aria-pressed={String(filtered.value)}
93
+ data-action="recipe-action"
94
+ data-recipe-command="filter"
95
+ >
96
+ <LucideIcon icon={Filter} name="filter" />
97
+ </button>
98
+ <button
99
+ type="button"
100
+ aria-label="Refresh tasks"
101
+ data-action="recipe-action"
102
+ data-recipe-command="refresh"
103
+ >
104
+ <LucideIcon icon={RefreshCw} name="refresh-cw" />
105
+ </button>
106
+ </ToolbarControlGroup>
107
+ <ToolbarControlGroup label="More task actions" single>
108
+ <button
109
+ type="button"
110
+ aria-label="More task actions"
111
+ data-action="recipe-action"
112
+ data-recipe-command="more"
113
+ >
114
+ <LucideIcon icon={MoreHorizontal} name="more-horizontal" />
115
+ </button>
116
+ </ToolbarControlGroup>
117
+ </>
118
+ }
119
+ />
120
+ <Grid
121
+ columns={
122
+ device.value.compact ? 1 : device.value.atLeast('desktop') ? 4 : 2
123
+ }
124
+ gap="none"
125
+ >
126
+ {guide.map(([name, use]) => (
127
+ <ListInsetText>
128
+ <List gap="2xs">
129
+ <Text variant="span">
130
+ <strong>{name}</strong>
131
+ </Text>
132
+ <Text variant="span" tone="quiet" size="compact">
133
+ {use}
134
+ </Text>
135
+ </List>
136
+ </ListInsetText>
137
+ ))}
138
+ </Grid>
139
+ </List>
123
140
  </section>
124
141
  );
125
142
  return {