@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,11 +1,14 @@
1
1
  import '@kerfjs/ui/layout.css';
2
- import './recipes.css';
3
2
 
3
+ import { deviceClass } from '@kerfjs/ui/device-class';
4
+ import { Grid } from '@kerfjs/ui/grid';
5
+ import { List } from '@kerfjs/ui/list';
4
6
  import { ListHeader } from '@kerfjs/ui/list-header';
5
7
  import { ListItem } from '@kerfjs/ui/list-item';
6
8
  import { LucideIcon } from '@kerfjs/ui/lucide-icon';
9
+ import { Pane, type PaneSeparatorSide } from '@kerfjs/ui/pane';
7
10
  import { ResizableRegion } from '@kerfjs/ui/resizable-region';
8
- import { Text } from '@kerfjs/ui/text';
11
+ import { Row } from '@kerfjs/ui/row';
9
12
  import { Toolbar } from '@kerfjs/ui/toolbar';
10
13
  import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
11
14
  import { ToolbarText } from '@kerfjs/ui/toolbar-text';
@@ -13,213 +16,270 @@ import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
13
16
  import { signal } from 'kerfjs';
14
17
  import { Bell, Folder, Inbox, Settings } from 'lucide';
15
18
 
16
- import type { RecipeFactory } from './types.js';
19
+ import type { RecipeFactory, RecipePresentation } from './types.js';
20
+
21
+ type ShellPane = 'content' | 'navigation' | 'inspector';
22
+ type ScreenEdges = readonly PaneSeparatorSide[];
23
+
24
+ // Safe areas: the app bar claims the top edge and both sides, so it clears the
25
+ // status area. Each pane below compensates only for the screen edges it
26
+ // actually reaches: the bottom, plus its outer side in the three-pane layout or
27
+ // both sides when it is the only pane shown.
28
+ const appBarEdges: ScreenEdges = ['block-start', 'inline-start', 'inline-end'];
29
+ const soleEdges: ScreenEdges = ['block-end', 'inline-start', 'inline-end'];
30
+
31
+ const tasks = [
32
+ ['Release accessibility audit', 'Interface systems · due this week'],
33
+ ['Prepare tablet navigation', 'Interface systems · due this week'],
34
+ ['Review stale-data states', 'Platform · due next week'],
35
+ ['Confirm package boundaries', 'Platform · due next week'],
36
+ ] as const;
37
+
38
+ export const presentation: RecipePresentation = {
39
+ viewport: {
40
+ width: 'full',
41
+ height: 'app',
42
+ frame: 'solid',
43
+ surface: 'default',
44
+ overflow: 'hidden',
45
+ shadow: true,
46
+ },
47
+ note: 'The recipe owns pane geometry and one scroll owner per pane. The app owns routing, data, pane visibility, sizes, and persistence.',
48
+ };
17
49
 
18
50
  export const createRecipe: RecipeFactory = (announce) => {
19
51
  const selected = signal('inbox');
20
52
  const navigationSize = signal(224);
21
53
  const inspectorSize = signal(240);
22
- const responsivePane = signal<'content' | 'navigation' | 'inspector'>(
23
- 'content',
54
+ const responsivePane = signal<ShellPane>('content');
55
+ // Desktop-class viewports show all three panes; smaller ones show one pane
56
+ // at a time and let the toolbar switch between them.
57
+ const device = deviceClass();
58
+
59
+ const paneButton = (
60
+ pane: ShellPane,
61
+ label: string,
62
+ icon: typeof Folder,
63
+ iconName: string,
64
+ ) => (
65
+ <button
66
+ type="button"
67
+ aria-label={label}
68
+ aria-controls={`recipe-shell-${pane}`}
69
+ aria-pressed={String(responsivePane.value === pane)}
70
+ data-action="recipe-action"
71
+ data-recipe-command={`show-${pane}`}
72
+ >
73
+ <LucideIcon icon={icon} name={iconName} />
74
+ </button>
24
75
  );
25
- const render = () => (
26
- <section
27
- class="kui-recipe recipe-shell kui-recipe__surface kui-layout"
28
- data-recipe="recipe-app-shell"
29
- data-responsive-pane={responsivePane.value}
76
+
77
+ const navigation = (edges: ScreenEdges) => (
78
+ <Pane
79
+ element="aside"
80
+ safeAreaEdges={edges}
81
+ id="recipe-shell-navigation"
82
+ contentElement="nav"
83
+ contentLabel="Workspace"
30
84
  >
31
- <Toolbar
32
- label="Atlas workspace"
33
- leading={<ToolbarText text="Atlas" />}
34
- trailing={
35
- <div class="recipe-shell__toolbar-actions">
36
- <ToolbarControlGroup
37
- className="recipe-shell__responsive-controls"
38
- appearance="borderless"
39
- label="Workspace panes"
40
- >
41
- <button
42
- type="button"
43
- aria-label="Show navigation"
44
- aria-controls="recipe-shell-navigation"
45
- aria-pressed={String(responsivePane.value === 'navigation')}
46
- data-action="recipe-action"
47
- data-recipe-command="show-navigation"
48
- >
49
- <LucideIcon icon={Folder} name="folder" />
50
- </button>
51
- <button
52
- type="button"
53
- aria-label="Show content"
54
- aria-controls="recipe-shell-content"
55
- aria-pressed={String(responsivePane.value === 'content')}
56
- data-action="recipe-action"
57
- data-recipe-command="show-content"
58
- >
59
- <LucideIcon icon={Inbox} name="inbox" />
60
- </button>
61
- <button
62
- type="button"
63
- aria-label="Show inspector"
64
- aria-controls="recipe-shell-inspector"
65
- aria-pressed={String(responsivePane.value === 'inspector')}
66
- data-action="recipe-action"
67
- data-recipe-command="show-inspector"
68
- >
69
- <LucideIcon icon={Settings} name="settings" />
70
- </button>
71
- </ToolbarControlGroup>
72
- <ToolbarControlGroup
73
- appearance="borderless"
74
- label="Workspace controls"
75
- >
76
- <button
77
- type="button"
78
- aria-label="Notifications"
79
- data-action="recipe-action"
80
- data-recipe-command="notify"
81
- >
82
- <LucideIcon icon={Bell} name="bell" />
83
- </button>
85
+ <section>
86
+ <ListHeader label="Workspace" />
87
+ <ListItem
88
+ action="recipe-action"
89
+ itemId="inbox"
90
+ label="Inbox"
91
+ icon={<LucideIcon icon={Inbox} name="inbox" />}
92
+ selected={selected.value === 'inbox'}
93
+ />
94
+ <ListItem
95
+ action="recipe-action"
96
+ itemId="projects"
97
+ label="Projects with a deliberately wrapping title"
98
+ icon={<LucideIcon icon={Folder} name="folder" />}
99
+ selected={selected.value === 'projects'}
100
+ multiline
101
+ />
102
+ </section>
103
+ </Pane>
104
+ );
105
+
106
+ const content = (edges: ScreenEdges) => (
107
+ <Pane
108
+ element="main"
109
+ safeAreaEdges={edges}
110
+ id="recipe-shell-content"
111
+ header={
112
+ <Toolbar
113
+ label="Current workspace view"
114
+ dividerSides=""
115
+ leading={
116
+ <ToolbarText
117
+ text={
118
+ selected.value === 'inbox' ? 'Inbox triage' : 'Active projects'
119
+ }
120
+ size="xlarge"
121
+ id="recipe-shell-main-title"
122
+ />
123
+ }
124
+ trailing={
125
+ <ToolbarControlGroup appearance="borderless" content="text" single>
84
126
  <button
85
127
  type="button"
86
- aria-label="Settings"
87
128
  data-action="recipe-action"
88
- data-recipe-command="settings"
129
+ data-recipe-command="new"
89
130
  >
90
- <LucideIcon icon={Settings} name="settings" />
131
+ New task
91
132
  </button>
92
133
  </ToolbarControlGroup>
93
- </div>
94
- }
95
- />
96
- <div class="recipe-shell__body">
97
- <div id="recipe-shell-navigation" class="recipe-shell__nav-region">
98
- <ResizableRegion
99
- id="recipe-navigation"
100
- label="Navigation"
101
- size={navigationSize.value}
102
- min={180}
103
- max={320}
104
- >
105
- <aside class="recipe-shell__nav kui-pane">
106
- <ListHeader label="Workspace" />
107
- <nav
108
- class="recipe-shell__nav-list kui-pane__content kui-content"
109
- aria-label="Workspace"
134
+ }
135
+ />
136
+ }
137
+ >
138
+ <section aria-label="Tasks">
139
+ {tasks.map(([title, detail]) => (
140
+ <ListItem
141
+ action="recipe-action"
142
+ label={title}
143
+ description={detail}
144
+ multiline
145
+ rootAttributes={{ 'data-recipe-command': 'open-task' }}
146
+ />
147
+ ))}
148
+ </section>
149
+ </Pane>
150
+ );
151
+
152
+ const inspector = (edges: ScreenEdges) => (
153
+ <Pane
154
+ element="aside"
155
+ safeAreaEdges={edges}
156
+ id="recipe-shell-inspector"
157
+ header={
158
+ <Toolbar
159
+ label="Inspector"
160
+ dividerSides=""
161
+ leading={
162
+ <ToolbarText
163
+ text="Inspector"
164
+ size="xlarge"
165
+ id="recipe-shell-inspector-title"
166
+ />
167
+ }
168
+ />
169
+ }
170
+ >
171
+ <ValueTable label="Selected task">
172
+ <ValueTableRow label="Status" value="In review" />
173
+ <ValueTableRow label="Owner" value="Mara Chen" />
174
+ <ValueTableRow label="Priority" value="High" />
175
+ </ValueTable>
176
+ </Pane>
177
+ );
178
+
179
+ const render = () => {
180
+ const onePane = !device.value.atLeast('desktop');
181
+ return (
182
+ <List
183
+ fill
184
+ rootAttributes={{
185
+ 'data-recipe': 'recipe-app-shell',
186
+ 'data-responsive-pane': onePane ? responsivePane.value : 'all',
187
+ }}
188
+ >
189
+ <Toolbar
190
+ label="Atlas workspace"
191
+ safeAreaEdges={appBarEdges}
192
+ leading={<ToolbarText text="Atlas" />}
193
+ trailing={
194
+ <>
195
+ {onePane ? (
196
+ <ToolbarControlGroup
197
+ appearance="borderless"
198
+ buttonAppearance="push"
199
+ label="Workspace panes"
200
+ >
201
+ {paneButton(
202
+ 'navigation',
203
+ 'Show navigation',
204
+ Folder,
205
+ 'folder',
206
+ )}
207
+ {paneButton('content', 'Show content', Inbox, 'inbox')}
208
+ {paneButton(
209
+ 'inspector',
210
+ 'Show inspector',
211
+ Settings,
212
+ 'settings',
213
+ )}
214
+ </ToolbarControlGroup>
215
+ ) : null}
216
+ <ToolbarControlGroup
217
+ appearance="borderless"
218
+ label="Workspace controls"
110
219
  >
111
- <section>
112
- <ListItem
113
- action="recipe-action"
114
- itemId="inbox"
115
- label="Inbox"
116
- icon={<LucideIcon icon={Inbox} name="inbox" />}
117
- selected={selected.value === 'inbox'}
118
- />
119
- <ListItem
120
- action="recipe-action"
121
- itemId="projects"
122
- label="Projects with a deliberately wrapping title"
123
- icon={<LucideIcon icon={Folder} name="folder" />}
124
- selected={selected.value === 'projects'}
125
- multiline
126
- />
127
- </section>
128
- </nav>
129
- </aside>
130
- </ResizableRegion>
131
- </div>
132
- <main id="recipe-shell-content" class="recipe-shell__main kui-pane">
133
- <Toolbar
134
- label="Current workspace view"
135
- dividerSides=""
136
- leading={
137
- <ToolbarText
138
- text={
139
- selected.value === 'inbox'
140
- ? 'Inbox triage'
141
- : 'Active projects'
142
- }
143
- size="xlarge"
144
- id="recipe-shell-main-title"
145
- />
146
- }
147
- trailing={
148
- <ToolbarControlGroup appearance="borderless" single>
149
220
  <button
150
- class="kui-recipe__button"
151
- data-primary="true"
152
221
  type="button"
222
+ aria-label="Notifications"
223
+ data-action="recipe-action"
224
+ data-recipe-command="notify"
225
+ >
226
+ <LucideIcon icon={Bell} name="bell" />
227
+ </button>
228
+ <button
229
+ type="button"
230
+ aria-label="Settings"
153
231
  data-action="recipe-action"
154
- data-recipe-command="new"
232
+ data-recipe-command="settings"
155
233
  >
156
- New task
234
+ <LucideIcon icon={Settings} name="settings" />
157
235
  </button>
158
236
  </ToolbarControlGroup>
159
- }
160
- />
161
- <div class="recipe-shell__main-body kui-pane__content kui-content">
162
- <Text class="kui-recipe__ownership kui-content-item">
163
- Recipe owns pane geometry and one scroll owner per pane. The app
164
- owns routing, data, pane visibility, sizes, and persistence.
165
- </Text>
166
- <div class="recipe-shell__cards">
167
- {[
168
- 'Release accessibility audit',
169
- 'Prepare tablet navigation',
170
- 'Review stale-data states',
171
- 'Confirm package boundaries',
172
- ].map((title) => (
173
- <article class="recipe-shell__card kui-content-item">
174
- <strong>{title}</strong>
175
- <Text class="kui-recipe__muted">
176
- Assigned to the interface systems team · due this week
177
- </Text>
178
- </article>
179
- ))}
180
- </div>
181
- </div>
182
- </main>
183
- <div id="recipe-shell-inspector" class="recipe-shell__inspector-region">
184
- <ResizableRegion
185
- id="recipe-inspector"
186
- label="Inspector"
187
- size={inspectorSize.value}
188
- min={200}
189
- max={360}
190
- edge="start"
191
- >
192
- <aside class="recipe-shell__inspector kui-pane">
193
- <Toolbar
194
- label="Inspector"
195
- dividerSides=""
196
- leading={
197
- <ToolbarText
198
- text="Inspector"
199
- size="xlarge"
200
- id="recipe-shell-inspector-title"
201
- />
202
- }
203
- />
204
- <div class="recipe-shell__inspector-body kui-pane__content kui-content">
205
- <ValueTable label="Selected task">
206
- <ValueTableRow label="Status" value="In review" />
207
- <ValueTableRow label="Owner" value="Mara Chen" />
208
- <ValueTableRow label="Priority" value="High" />
209
- </ValueTable>
210
- </div>
211
- </aside>
212
- </ResizableRegion>
213
- </div>
214
- </div>
215
- </section>
216
- );
237
+ </>
238
+ }
239
+ />
240
+ {onePane ? (
241
+ <Grid columns={1} gap="none" flex>
242
+ {responsivePane.value === 'navigation'
243
+ ? navigation(soleEdges)
244
+ : responsivePane.value === 'inspector'
245
+ ? inspector(soleEdges)
246
+ : content(soleEdges)}
247
+ </Grid>
248
+ ) : (
249
+ <Row gap="none" flex>
250
+ <ResizableRegion
251
+ id="recipe-navigation"
252
+ label="Navigation"
253
+ size={navigationSize.value}
254
+ min={180}
255
+ max={320}
256
+ >
257
+ {navigation(['block-end', 'inline-start'])}
258
+ </ResizableRegion>
259
+ <Grid columns={1} gap="none" flex>
260
+ {content(['block-end'])}
261
+ </Grid>
262
+ <ResizableRegion
263
+ id="recipe-inspector"
264
+ label="Inspector"
265
+ size={inspectorSize.value}
266
+ min={200}
267
+ max={360}
268
+ edge="start"
269
+ >
270
+ {inspector(['block-end', 'inline-end'])}
271
+ </ResizableRegion>
272
+ </Row>
273
+ )}
274
+ </List>
275
+ );
276
+ };
217
277
  return {
218
278
  render,
219
279
  action(command, element) {
220
280
  const pane = command.match(
221
281
  /^show-(navigation|content|inspector)$/,
222
- )?.[1] as typeof responsivePane.value | undefined;
282
+ )?.[1] as ShellPane | undefined;
223
283
  if (pane) responsivePane.value = pane;
224
284
  const id = element.dataset.itemId;
225
285
  if (id) selected.value = id;