@kerfjs/ui 5.0.0-beta.50 → 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 (205) hide show
  1. package/README.md +29 -5
  2. package/ai/compile-time-contracts-v1.json +5 -2
  3. package/ai/component-catalog-v2.d.ts +19 -1
  4. package/ai/component-catalog-v2.json +842 -127
  5. package/ai/component-catalog-v2.schema.json +33 -1
  6. package/ai/component-catalog.json +603 -126
  7. package/ai/component-catalog.schema.json +21 -1
  8. package/ai/public-api-signatures-v1.md +201 -29
  9. package/ai/skill.md +12 -9
  10. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  11. package/analyzer/index.mjs +105 -35
  12. package/dist/app-tab.js +1 -1
  13. package/dist/catalog-resources.d.ts +1 -1
  14. package/dist/catalog.d.ts +28 -5
  15. package/dist/catalog.js +66 -25
  16. package/dist/catalog.js.map +1 -1
  17. package/dist/{chunk-COUPLRZG.js → chunk-5FZZMIMD.js} +3 -3
  18. package/dist/{chunk-COUPLRZG.js.map → chunk-5FZZMIMD.js.map} +1 -1
  19. package/dist/{chunk-DARTMI5Z.js → chunk-5PMGQDG7.js} +2 -2
  20. package/dist/{chunk-DARTMI5Z.js.map → chunk-5PMGQDG7.js.map} +1 -1
  21. package/dist/{chunk-V6RZWDII.js → chunk-7GUQYHKA.js} +19 -2
  22. package/dist/chunk-7GUQYHKA.js.map +1 -0
  23. package/dist/{chunk-ZNZ2JDBW.js → chunk-7IUSV4XG.js} +1 -1
  24. package/dist/chunk-7IUSV4XG.js.map +1 -0
  25. package/dist/{chunk-ELRMBQEC.js → chunk-AEPXGCNF.js} +17 -2
  26. package/dist/chunk-AEPXGCNF.js.map +1 -0
  27. package/dist/{chunk-5CE64WGC.js → chunk-D547PH4L.js} +3 -3
  28. package/dist/{chunk-5CE64WGC.js.map → chunk-D547PH4L.js.map} +1 -1
  29. package/dist/{chunk-S6ZTTTV7.js → chunk-DVZCMMB3.js} +3 -3
  30. package/dist/{chunk-S6ZTTTV7.js.map → chunk-DVZCMMB3.js.map} +1 -1
  31. package/dist/chunk-EKYZCRCX.js +256 -0
  32. package/dist/chunk-EKYZCRCX.js.map +1 -0
  33. package/dist/{chunk-3V75EQIP.js → chunk-JM53SMVF.js} +3 -3
  34. package/dist/{chunk-3V75EQIP.js.map → chunk-JM53SMVF.js.map} +1 -1
  35. package/dist/{chunk-2WMSE4MK.js → chunk-JMGKM7RK.js} +2 -2
  36. package/dist/{chunk-2WMSE4MK.js.map → chunk-JMGKM7RK.js.map} +1 -1
  37. package/dist/{chunk-2YOUUEWR.js → chunk-KX7DP2GO.js} +4 -4
  38. package/dist/{chunk-2YOUUEWR.js.map → chunk-KX7DP2GO.js.map} +1 -1
  39. package/dist/{chunk-OL4TC3U2.js → chunk-MO56ZZLG.js} +6 -2
  40. package/dist/chunk-MO56ZZLG.js.map +1 -0
  41. package/dist/{chunk-OQI7RCRP.js → chunk-MTM43SJO.js} +7 -1
  42. package/dist/chunk-MTM43SJO.js.map +1 -0
  43. package/dist/{chunk-ZSYLBVWG.js → chunk-NMGTHYW3.js} +3 -3
  44. package/dist/{chunk-ZSYLBVWG.js.map → chunk-NMGTHYW3.js.map} +1 -1
  45. package/dist/{chunk-AQSKJ6U6.js → chunk-NZXWU4R5.js} +21 -2
  46. package/dist/chunk-NZXWU4R5.js.map +1 -0
  47. package/dist/{chunk-DJKWFYAA.js → chunk-O3RCCSQJ.js} +3 -3
  48. package/dist/{chunk-DJKWFYAA.js.map → chunk-O3RCCSQJ.js.map} +1 -1
  49. package/dist/{chunk-UMGY5JSP.js → chunk-S4QRSBFI.js} +22 -2
  50. package/dist/chunk-S4QRSBFI.js.map +1 -0
  51. package/dist/{chunk-KKNK3JUT.js → chunk-UBH743HV.js} +5 -5
  52. package/dist/chunk-UBH743HV.js.map +1 -0
  53. package/dist/{chunk-54HM36IQ.js → chunk-ULCDZTWL.js} +2 -2
  54. package/dist/{chunk-54HM36IQ.js.map → chunk-ULCDZTWL.js.map} +1 -1
  55. package/dist/{chunk-SIU2AC5W.js → chunk-UPZ6EEJN.js} +3 -3
  56. package/dist/{chunk-SIU2AC5W.js.map → chunk-UPZ6EEJN.js.map} +1 -1
  57. package/dist/collapsible-panel.d.ts +1 -1
  58. package/dist/collapsible-panel.js.map +1 -1
  59. package/dist/css-values.d.ts +39 -3
  60. package/dist/css-values.js +1 -1
  61. package/dist/grid.d.ts +15 -1
  62. package/dist/grid.js +2 -1
  63. package/dist/index.d.ts +1 -1
  64. package/dist/index.js +19 -19
  65. package/dist/list-action-row.js +1 -1
  66. package/dist/list-header.js +1 -1
  67. package/dist/list-item.js +1 -1
  68. package/dist/list.d.ts +21 -2
  69. package/dist/list.js +2 -1
  70. package/dist/nav-stack.js +1 -1
  71. package/dist/pane.d.ts +17 -2
  72. package/dist/pane.js +1 -1
  73. package/dist/row.d.ts +19 -1
  74. package/dist/row.js +2 -1
  75. package/dist/segmented-control.js +1 -1
  76. package/dist/select.d.ts +13 -3
  77. package/dist/select.js +1 -1
  78. package/dist/skeleton.js +1 -1
  79. package/dist/spacer.js +1 -1
  80. package/dist/split-view.js +1 -1
  81. package/dist/state-banner.js +1 -1
  82. package/dist/styles/catalog/components/catalog-example-stack.css +4 -1
  83. package/dist/styles/catalog/components/catalog-example.css +145 -0
  84. package/dist/styles/collapsible-panel.css +132 -4
  85. package/dist/styles/document.css +23 -0
  86. package/dist/styles/foundation.css +21 -7
  87. package/dist/styles/grid.css +10 -0
  88. package/dist/styles/list-header.css +45 -0
  89. package/dist/styles/list-inset-control.css +8 -1
  90. package/dist/styles/list-item.css +6 -0
  91. package/dist/styles/list.css +11 -0
  92. package/dist/styles/nav-stack.css +89 -2
  93. package/dist/styles/pane.css +86 -1
  94. package/dist/styles/resizable-region.css +93 -5
  95. package/dist/styles/row.css +10 -0
  96. package/dist/styles/select.css +39 -4
  97. package/dist/styles/split-view.css +107 -1
  98. package/dist/styles/tab-scaffold.css +64 -1
  99. package/dist/styles/text.css +6 -0
  100. package/dist/styles/toolbar-control-group.css +90 -11
  101. package/dist/styles/toolbar-text.css +4 -1
  102. package/dist/styles/toolbar.css +130 -38
  103. package/dist/styles/webawesome.css +48 -2
  104. package/dist/styles/workbench.css +136 -4
  105. package/dist/text.d.ts +4 -3
  106. package/dist/text.js +1 -1
  107. package/dist/toolbar-text.js +1 -1
  108. package/dist/toolbar.d.ts +24 -3
  109. package/dist/toolbar.js +1 -1
  110. package/dist/{types-CzgSrFXP.d.ts → types-CEsieBZi.d.ts} +6 -1
  111. package/dist/value-table.js +1 -1
  112. package/dist/wire-resizable-regions.js +1 -1
  113. package/dist/wire-sidebar.d.ts +18 -3
  114. package/dist/wire-sidebar.js +95 -14
  115. package/dist/wire-sidebar.js.map +1 -1
  116. package/docs/accessibility.md +45 -8
  117. package/docs/app-layouts.md +73 -0
  118. package/docs/catalog.md +23 -2
  119. package/docs/collapsible-panel.md +81 -8
  120. package/docs/component-contract.md +28 -9
  121. package/docs/component-integration.md +9 -4
  122. package/docs/component-selection.md +25 -6
  123. package/docs/design/templates/list/stack-dark.svg +1 -1
  124. package/docs/design/templates/list/stack.svg +1 -1
  125. package/docs/design/templates/list-dark.svg +1 -1
  126. package/docs/design/templates/list-header/action-dark.svg +1 -1
  127. package/docs/design/templates/list-header/action.svg +1 -1
  128. package/docs/design/templates/list-header/count-dark.svg +1 -1
  129. package/docs/design/templates/list-header/count.svg +1 -1
  130. package/docs/design/templates/list-header/inline-action-dark.svg +1 -1
  131. package/docs/design/templates/list-header/inline-action.svg +1 -1
  132. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -1
  133. package/docs/design/templates/list-header/toggle-expanded.svg +1 -1
  134. package/docs/design/templates/list-header-dark.svg +1 -1
  135. package/docs/design/templates/list-header.svg +1 -1
  136. package/docs/design/templates/list.svg +1 -1
  137. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
  138. package/docs/design/templates/state-banner/info-badge.svg +1 -1
  139. package/docs/design/templates/state-banner-dark.svg +1 -1
  140. package/docs/design/templates/state-banner.svg +1 -1
  141. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  142. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  143. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
  144. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
  145. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  146. package/docs/design/templates/toolbar-control-group.svg +1 -1
  147. package/docs/design/templates/toolbar-dark.svg +1 -1
  148. package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
  149. package/docs/design/templates/toolbar-text/default.svg +1 -1
  150. package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
  151. package/docs/design/templates/toolbar-text/large.svg +1 -1
  152. package/docs/design/templates/toolbar-text/small-dark.svg +1 -1
  153. package/docs/design/templates/toolbar-text/small.svg +1 -1
  154. package/docs/design/templates/toolbar-text-dark.svg +1 -1
  155. package/docs/design/templates/toolbar-text.svg +1 -1
  156. package/docs/design/templates/toolbar.svg +1 -1
  157. package/docs/document-baseline.md +31 -0
  158. package/docs/layout.md +86 -21
  159. package/docs/nav-stack.md +8 -0
  160. package/docs/recipes.md +78 -43
  161. package/docs/split-view.md +8 -0
  162. package/docs/tab-scaffold.md +4 -2
  163. package/docs/type-contracts.md +21 -17
  164. package/docs/ui-analyzer.md +19 -0
  165. package/docs/ui-evaluator.md +17 -0
  166. package/docs/ux-demo.md +4 -4
  167. package/docs/webawesome-theme.md +20 -2
  168. package/docs/workbench.md +20 -1
  169. package/evaluator/index.mjs +73 -8
  170. package/llms.txt +32 -14
  171. package/package.json +4 -2
  172. package/ux-demo/recipes/app-shell.tsx +209 -178
  173. package/ux-demo/recipes/collapsible-sidebar.tsx +188 -159
  174. package/ux-demo/recipes/compact-toolbar.tsx +48 -35
  175. package/ux-demo/recipes/composer-form.tsx +85 -79
  176. package/ux-demo/recipes/list-detail-dialog.tsx +124 -142
  177. package/ux-demo/recipes/list-workspace-states.tsx +83 -83
  178. package/ux-demo/recipes/loaders.ts +5 -2
  179. package/ux-demo/recipes/loading-inspector.tsx +157 -160
  180. package/ux-demo/recipes/navigation-sidebar.tsx +111 -109
  181. package/ux-demo/recipes/navigation-stack.tsx +50 -49
  182. package/ux-demo/recipes/types.ts +14 -0
  183. package/ux-demo/recipes/workspace-header.tsx +73 -56
  184. package/dist/chunk-AQSKJ6U6.js.map +0 -1
  185. package/dist/chunk-ELRMBQEC.js.map +0 -1
  186. package/dist/chunk-J3EE6XWD.js +0 -115
  187. package/dist/chunk-J3EE6XWD.js.map +0 -1
  188. package/dist/chunk-KKNK3JUT.js.map +0 -1
  189. package/dist/chunk-OL4TC3U2.js.map +0 -1
  190. package/dist/chunk-OQI7RCRP.js.map +0 -1
  191. package/dist/chunk-UMGY5JSP.js.map +0 -1
  192. package/dist/chunk-V6RZWDII.js.map +0 -1
  193. package/dist/chunk-ZNZ2JDBW.js.map +0 -1
  194. package/ux-demo/recipes/app-shell.css +0 -87
  195. package/ux-demo/recipes/collapsible-sidebar.css +0 -83
  196. package/ux-demo/recipes/compact-toolbar.css +0 -35
  197. package/ux-demo/recipes/composer-form.css +0 -19
  198. package/ux-demo/recipes/list-detail-dialog.css +0 -42
  199. package/ux-demo/recipes/list-workspace-states.css +0 -30
  200. package/ux-demo/recipes/loading-inspector.css +0 -7
  201. package/ux-demo/recipes/navigation-sidebar.css +0 -12
  202. package/ux-demo/recipes/navigation-stack.css +0 -12
  203. package/ux-demo/recipes/recipe-root.css +0 -67
  204. package/ux-demo/recipes/recipe-root.tsx +0 -87
  205. package/ux-demo/recipes/workspace-header.css +0 -7
@@ -1,11 +1,14 @@
1
1
  import '@kerfjs/ui/layout.css';
2
- import './app-shell.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';
7
- import { Pane } from '@kerfjs/ui/pane';
9
+ import { Pane, type PaneSeparatorSide } from '@kerfjs/ui/pane';
8
10
  import { ResizableRegion } from '@kerfjs/ui/resizable-region';
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,70 +16,203 @@ 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 {
17
- RecipeMutedText,
18
- RecipeOwnershipNote,
19
- RecipeRoot,
20
- } from './recipe-root.js';
21
- 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
+ };
22
49
 
23
50
  export const createRecipe: RecipeFactory = (announce) => {
24
51
  const selected = signal('inbox');
25
52
  const navigationSize = signal(224);
26
53
  const inspectorSize = signal(240);
27
- const responsivePane = signal<'content' | 'navigation' | 'inspector'>(
28
- '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>
29
75
  );
30
- const render = () => (
31
- <RecipeRoot
32
- recipe="recipe-app-shell"
33
- dataAttributes={{ '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"
34
84
  >
35
- <div
36
- class="recipe-shell kui-layout"
37
- data-responsive-pane={responsivePane.value}
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>
126
+ <button
127
+ type="button"
128
+ data-action="recipe-action"
129
+ data-recipe-command="new"
130
+ >
131
+ New task
132
+ </button>
133
+ </ToolbarControlGroup>
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
+ }}
38
188
  >
39
189
  <Toolbar
40
190
  label="Atlas workspace"
191
+ safeAreaEdges={appBarEdges}
41
192
  leading={<ToolbarText text="Atlas" />}
42
193
  trailing={
43
194
  <>
44
- <ToolbarControlGroup
45
- appearance="borderless"
46
- label="Workspace panes"
47
- visibility="compact-only"
48
- >
49
- <button
50
- type="button"
51
- aria-label="Show navigation"
52
- aria-controls="recipe-shell-navigation"
53
- aria-pressed={String(responsivePane.value === 'navigation')}
54
- data-action="recipe-action"
55
- data-recipe-command="show-navigation"
56
- >
57
- <LucideIcon icon={Folder} name="folder" />
58
- </button>
59
- <button
60
- type="button"
61
- aria-label="Show content"
62
- aria-controls="recipe-shell-content"
63
- aria-pressed={String(responsivePane.value === 'content')}
64
- data-action="recipe-action"
65
- data-recipe-command="show-content"
66
- >
67
- <LucideIcon icon={Inbox} name="inbox" />
68
- </button>
69
- <button
70
- type="button"
71
- aria-label="Show inspector"
72
- aria-controls="recipe-shell-inspector"
73
- aria-pressed={String(responsivePane.value === 'inspector')}
74
- data-action="recipe-action"
75
- data-recipe-command="show-inspector"
195
+ {onePane ? (
196
+ <ToolbarControlGroup
197
+ appearance="borderless"
198
+ buttonAppearance="push"
199
+ label="Workspace panes"
76
200
  >
77
- <LucideIcon icon={Settings} name="settings" />
78
- </button>
79
- </ToolbarControlGroup>
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}
80
216
  <ToolbarControlGroup
81
217
  appearance="borderless"
82
218
  label="Workspace controls"
@@ -101,108 +237,28 @@ export const createRecipe: RecipeFactory = (announce) => {
101
237
  </>
102
238
  }
103
239
  />
104
- <div class="recipe-shell__body">
105
- <div id="recipe-shell-navigation" class="recipe-shell__nav-region">
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>
106
250
  <ResizableRegion
107
251
  id="recipe-navigation"
108
252
  label="Navigation"
109
253
  size={navigationSize.value}
110
254
  min={180}
111
255
  max={320}
112
- responsiveFillAt="compact"
113
256
  >
114
- <div class="recipe-shell__nav">
115
- <Pane
116
- element="aside"
117
- contentElement="nav"
118
- contentClassName="recipe-shell__nav-list"
119
- contentLabel="Workspace"
120
- >
121
- <ListHeader label="Workspace" />
122
- <section>
123
- <ListItem
124
- action="recipe-action"
125
- itemId="inbox"
126
- label="Inbox"
127
- icon={<LucideIcon icon={Inbox} name="inbox" />}
128
- selected={selected.value === 'inbox'}
129
- />
130
- <ListItem
131
- action="recipe-action"
132
- itemId="projects"
133
- label="Projects with a deliberately wrapping title"
134
- icon={<LucideIcon icon={Folder} name="folder" />}
135
- selected={selected.value === 'projects'}
136
- multiline
137
- />
138
- </section>
139
- </Pane>
140
- </div>
257
+ {navigation(['block-end', 'inline-start'])}
141
258
  </ResizableRegion>
142
- </div>
143
- <div class="recipe-shell__main">
144
- <Pane
145
- element="main"
146
- id="recipe-shell-content"
147
- contentClassName="recipe-shell__main-body"
148
- header={
149
- <Toolbar
150
- label="Current workspace view"
151
- dividerSides=""
152
- leading={
153
- <ToolbarText
154
- text={
155
- selected.value === 'inbox'
156
- ? 'Inbox triage'
157
- : 'Active projects'
158
- }
159
- size="xlarge"
160
- id="recipe-shell-main-title"
161
- />
162
- }
163
- trailing={
164
- <ToolbarControlGroup
165
- appearance="borderless"
166
- content="text"
167
- single
168
- >
169
- <button
170
- type="button"
171
- data-action="recipe-action"
172
- data-recipe-command="new"
173
- >
174
- New task
175
- </button>
176
- </ToolbarControlGroup>
177
- }
178
- />
179
- }
180
- >
181
- <RecipeOwnershipNote>
182
- Recipe owns pane geometry and one scroll owner per pane. The app
183
- owns routing, data, pane visibility, sizes, and persistence.
184
- </RecipeOwnershipNote>
185
- <div class="recipe-shell__cards">
186
- {[
187
- 'Release accessibility audit',
188
- 'Prepare tablet navigation',
189
- 'Review stale-data states',
190
- 'Confirm package boundaries',
191
- ].map((title) => (
192
- <article class="recipe-shell__card kui-content-item">
193
- <strong>{title}</strong>
194
- <RecipeMutedText>
195
- Assigned to the interface systems team · due this week
196
- </RecipeMutedText>
197
- </article>
198
- ))}
199
- </div>
200
- </Pane>
201
- </div>
202
- <div
203
- id="recipe-shell-inspector"
204
- class="recipe-shell__inspector-region"
205
- >
259
+ <Grid columns={1} gap="none" flex>
260
+ {content(['block-end'])}
261
+ </Grid>
206
262
  <ResizableRegion
207
263
  id="recipe-inspector"
208
264
  label="Inspector"
@@ -210,45 +266,20 @@ export const createRecipe: RecipeFactory = (announce) => {
210
266
  min={200}
211
267
  max={360}
212
268
  edge="start"
213
- responsiveFillAt="narrow"
214
269
  >
215
- <div class="recipe-shell__inspector">
216
- <Pane
217
- element="aside"
218
- contentClassName="recipe-shell__inspector-body"
219
- header={
220
- <Toolbar
221
- label="Inspector"
222
- dividerSides=""
223
- leading={
224
- <ToolbarText
225
- text="Inspector"
226
- size="xlarge"
227
- id="recipe-shell-inspector-title"
228
- />
229
- }
230
- />
231
- }
232
- >
233
- <ValueTable label="Selected task">
234
- <ValueTableRow label="Status" value="In review" />
235
- <ValueTableRow label="Owner" value="Mara Chen" />
236
- <ValueTableRow label="Priority" value="High" />
237
- </ValueTable>
238
- </Pane>
239
- </div>
270
+ {inspector(['block-end', 'inline-end'])}
240
271
  </ResizableRegion>
241
- </div>
242
- </div>
243
- </div>
244
- </RecipeRoot>
245
- );
272
+ </Row>
273
+ )}
274
+ </List>
275
+ );
276
+ };
246
277
  return {
247
278
  render,
248
279
  action(command, element) {
249
280
  const pane = command.match(
250
281
  /^show-(navigation|content|inspector)$/,
251
- )?.[1] as typeof responsivePane.value | undefined;
282
+ )?.[1] as ShellPane | undefined;
252
283
  if (pane) responsivePane.value = pane;
253
284
  const id = element.dataset.itemId;
254
285
  if (id) selected.value = id;