@astryxdesign/cli 0.1.1 → 0.1.2-canary.043f3fd

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 (185) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/docs/theme.doc.mjs +2 -2
  3. package/docs/working-with-ai.doc.mjs +6 -6
  4. package/package.json +31 -8
  5. package/src/api/component.mjs +253 -12
  6. package/src/api/discover.mjs +87 -28
  7. package/src/api/doctor.mjs +7 -22
  8. package/src/api/index.mjs +1 -0
  9. package/src/api/layout.mjs +302 -0
  10. package/src/api/layout.test.mjs +241 -0
  11. package/src/api/template-integration.test.mjs +225 -0
  12. package/src/api/template.mjs +421 -122
  13. package/src/api/template.test.mjs +2 -0
  14. package/src/api/theme-add.mjs +182 -0
  15. package/src/api/validate-integration.mjs +370 -0
  16. package/src/api/validate-integration.test.mjs +222 -0
  17. package/src/codemod.mjs +93 -0
  18. package/src/codemod.test.mjs +134 -0
  19. package/src/codemods/__tests__/registry.test.mjs +2 -0
  20. package/src/codemods/__tests__/runner.test.mjs +103 -0
  21. package/src/codemods/integration-discovery.mjs +168 -0
  22. package/src/codemods/integration-discovery.test.mjs +234 -0
  23. package/src/codemods/integration-runner.mjs +109 -0
  24. package/src/codemods/registry.mjs +2 -0
  25. package/src/codemods/run-codemod.mjs +207 -0
  26. package/src/codemods/runner.mjs +73 -96
  27. package/src/codemods/transforms/v0.0.15/index.mjs +0 -13
  28. package/src/codemods/transforms/v0.1.0/__tests__/v0.1.0-ordering.test.mjs +81 -0
  29. package/src/codemods/transforms/{v0.0.15 → v0.1.0}/drop-xds-prefix-imports.mjs +15 -4
  30. package/src/codemods/transforms/v0.1.0/index.mjs +15 -6
  31. package/src/codemods/transforms/v0.1.2/__tests__/rename-text-color-active-to-accent.test.mjs +120 -0
  32. package/src/codemods/transforms/v0.1.2/index.mjs +19 -0
  33. package/src/codemods/transforms/v0.1.2/rename-text-color-active-to-accent.mjs +136 -0
  34. package/src/codemods/transforms/v0.1.3/__tests__/migrate-layout-components-to-experimental.test.mjs +360 -0
  35. package/src/codemods/transforms/v0.1.3/index.mjs +19 -0
  36. package/src/codemods/transforms/v0.1.3/migrate-layout-components-to-experimental.mjs +260 -0
  37. package/src/commands/build-theme.import-path.test.mjs +22 -2
  38. package/src/commands/build-theme.mjs +185 -16
  39. package/src/commands/component/index.mjs +44 -10
  40. package/src/commands/component-ownership.test.mjs +227 -0
  41. package/src/commands/discover.mjs +5 -16
  42. package/src/commands/doctor.test.mjs +3 -3
  43. package/src/commands/init.mjs +34 -8
  44. package/src/commands/init.next-steps.test.mjs +46 -0
  45. package/src/commands/json-contract.test.mjs +0 -32
  46. package/src/commands/layout.mjs +139 -0
  47. package/src/commands/swizzle.mjs +241 -224
  48. package/src/commands/swizzle.path-safety.test.mjs +3 -3
  49. package/src/commands/swizzle.routing.test.mjs +279 -0
  50. package/src/commands/template.mjs +33 -37
  51. package/src/commands/upgrade.config-ordering.test.mjs +273 -0
  52. package/src/commands/upgrade.integration-policy.test.mjs +204 -0
  53. package/src/commands/upgrade.mjs +349 -239
  54. package/src/commands/validate-integration.mjs +110 -0
  55. package/src/commands/validate-integration.test.mjs +124 -0
  56. package/src/config.mjs +18 -0
  57. package/src/config.test.mjs +100 -0
  58. package/src/index.mjs +13 -3
  59. package/src/integration.mjs +19 -0
  60. package/src/lib/component-discovery.mjs +177 -0
  61. package/src/lib/config-cache.mjs +91 -0
  62. package/src/lib/config-cache.test.mjs +83 -0
  63. package/src/lib/config-schema.mjs +105 -0
  64. package/src/lib/error-codes.mjs +18 -12
  65. package/src/lib/integration-warnings.mjs +62 -0
  66. package/src/lib/integration-warnings.test.mjs +102 -0
  67. package/src/lib/integrations.mjs +132 -0
  68. package/src/lib/integrations.test.mjs +135 -0
  69. package/src/lib/levenshtein.mjs +29 -0
  70. package/src/lib/manifest.mjs +15 -2
  71. package/src/lib/module-loader.mjs +80 -0
  72. package/src/lib/module-loader.test.mjs +106 -0
  73. package/src/lib/package-scanner.mjs +31 -7
  74. package/src/lib/project.mjs +502 -0
  75. package/src/lib/project.test.mjs +308 -0
  76. package/src/lib/string-utils.mjs +5 -14
  77. package/src/lib/xle/browser.d.ts +100 -0
  78. package/src/lib/xle/browser.mjs +120 -0
  79. package/src/lib/xle/expand.mjs +622 -0
  80. package/src/lib/xle/parse.mjs +581 -0
  81. package/src/lib/xle/print.mjs +174 -0
  82. package/src/lib/xle/registry-core.mjs +170 -0
  83. package/src/lib/xle/registry.mjs +237 -0
  84. package/src/lib/xle/splice.mjs +137 -0
  85. package/src/lib/xle/validate.mjs +356 -0
  86. package/src/lib/xle/xle.test.mjs +333 -0
  87. package/src/template.mjs +73 -0
  88. package/src/template.test.mjs +127 -0
  89. package/src/types/api.d.ts +2 -6
  90. package/src/types/base.d.ts +3 -9
  91. package/src/types/codemod.d.ts +81 -0
  92. package/src/types/component.d.ts +27 -2
  93. package/src/types/config.d.ts +70 -0
  94. package/src/types/error-codes.d.ts +5 -3
  95. package/src/types/index.d.ts +0 -1
  96. package/src/types/integration.d.ts +29 -0
  97. package/src/types/swizzle.d.ts +9 -2
  98. package/src/types/template-api.d.ts +54 -0
  99. package/src/types/template.d.ts +10 -7
  100. package/src/types/upgrade.d.ts +29 -0
  101. package/src/types/validate-integration.d.ts +24 -0
  102. package/src/utils/github.mjs +0 -252
  103. package/src/utils/interactive.mjs +2 -2
  104. package/templates/blocks/components/Card/ClickableCardWithNestedButton.doc.mjs +1 -1
  105. package/templates/blocks/components/Card/SelectableCardMulti.doc.mjs +1 -1
  106. package/templates/blocks/components/ChatComposerDrawer/ChatComposerDrawerWithProgress.tsx +1 -1
  107. package/templates/blocks/components/ChatDictationButton/ChatDictationButtonShowcase.tsx +3 -3
  108. package/templates/blocks/components/CommandPaletteEmpty/CommandPaletteEmptyShowcase.doc.mjs +15 -0
  109. package/templates/blocks/components/CommandPaletteEmpty/CommandPaletteEmptyShowcase.tsx +26 -0
  110. package/templates/blocks/components/DateInput/DateInputDateRange.doc.mjs +2 -2
  111. package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.doc.mjs +14 -0
  112. package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.tsx +46 -0
  113. package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.doc.mjs +14 -0
  114. package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.tsx +52 -0
  115. package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.doc.mjs +14 -0
  116. package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.tsx +43 -0
  117. package/templates/blocks/components/FileInput/FileInputShowcase.tsx +4 -2
  118. package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.doc.mjs +1 -1
  119. package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.tsx +9 -4
  120. package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.doc.mjs +14 -0
  121. package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.tsx +43 -0
  122. package/templates/blocks/components/MultiSelector/MultiSelectorShowcase.tsx +6 -2
  123. package/templates/blocks/components/NumberInput/NumberInputShowcase.tsx +6 -2
  124. package/templates/blocks/components/Outline/OutlineShowcase.doc.mjs +15 -0
  125. package/templates/blocks/components/Outline/OutlineShowcase.tsx +22 -0
  126. package/templates/blocks/components/RadioList/RadioListShowcase.tsx +8 -1
  127. package/templates/blocks/components/SegmentedControl/SegmentedControlShowcase.tsx +3 -1
  128. package/templates/blocks/components/Selector/SelectorShowcase.tsx +4 -1
  129. package/templates/blocks/components/Slider/SliderShowcase.tsx +10 -1
  130. package/templates/blocks/components/Tab/TabShowcase.tsx +3 -1
  131. package/templates/blocks/components/Tab/TabWithSelectedIcon.doc.mjs +13 -0
  132. package/templates/blocks/components/Tab/TabWithSelectedIcon.tsx +39 -0
  133. package/templates/blocks/components/TabList/TabListShowcase.tsx +3 -1
  134. package/templates/blocks/components/TabMenu/TabMenuShowcase.tsx +3 -1
  135. package/templates/blocks/components/Table/ColumnResizeHookUsage.doc.mjs +14 -0
  136. package/templates/blocks/components/Table/ColumnResizeHookUsage.tsx +59 -0
  137. package/templates/blocks/components/Table/StickyColumnsHookUsage.doc.mjs +14 -0
  138. package/templates/blocks/components/Table/StickyColumnsHookUsage.tsx +104 -0
  139. package/templates/blocks/components/Text/TextColors.tsx +20 -5
  140. package/templates/blocks/components/TextArea/TextAreaShowcase.tsx +4 -2
  141. package/templates/blocks/components/TextInput/TextInputShowcase.tsx +4 -2
  142. package/templates/blocks/components/Thumbnail/ThumbnailDisabled.tsx +11 -6
  143. package/templates/blocks/components/Thumbnail/ThumbnailGallery.tsx +43 -7
  144. package/templates/blocks/components/Thumbnail/ThumbnailRemovable.tsx +40 -3
  145. package/templates/blocks/components/Thumbnail/ThumbnailShowcase.tsx +6 -5
  146. package/templates/blocks/components/Thumbnail/ThumbnailStates.tsx +11 -6
  147. package/templates/blocks/components/Timestamp/TimestampColors.tsx +2 -2
  148. package/templates/blocks/components/ToggleButton/ToggleButtonGroup.doc.mjs +1 -1
  149. package/templates/blocks/components/Tokenizer/TokenizerShowcase.tsx +8 -6
  150. package/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +2 -2
  151. package/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +23 -14
  152. package/templates/blocks/components/Typeahead/TypeaheadShowcase.tsx +6 -2
  153. package/templates/pages/shell-nav/page.tsx +321 -0
  154. package/templates/pages/shell-nav/template.doc.mjs +12 -0
  155. package/templates/pages/shell-side-nav/page.tsx +242 -0
  156. package/templates/pages/shell-side-nav/template.doc.mjs +12 -0
  157. package/templates/pages/shell-top-nav/page.tsx +224 -0
  158. package/templates/pages/shell-top-nav/template.doc.mjs +12 -0
  159. package/templates/pages/theme-showcase/page.tsx +8 -19
  160. package/templates/themes/butter/butterTheme.ts +916 -0
  161. package/templates/themes/butter/icons.tsx +77 -0
  162. package/templates/themes/chocolate/chocolateTheme.ts +230 -0
  163. package/templates/themes/chocolate/icons.tsx +77 -0
  164. package/templates/themes/gothic/gothicTheme.ts +657 -0
  165. package/templates/themes/gothic/icons.tsx +77 -0
  166. package/templates/themes/manifest.json +90 -0
  167. package/templates/themes/matcha/icons.tsx +67 -0
  168. package/templates/themes/matcha/matchaTheme.ts +247 -0
  169. package/templates/themes/neutral/icons.tsx +77 -0
  170. package/templates/themes/neutral/neutralTheme.ts +603 -0
  171. package/templates/themes/stone/icons.tsx +77 -0
  172. package/templates/themes/stone/stoneTheme.ts +652 -0
  173. package/templates/themes/y2k/icons.tsx +67 -0
  174. package/templates/themes/y2k/y2kTheme.ts +617 -0
  175. package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-config-surfaces.test.mjs +0 -116
  176. package/src/codemods/transforms/v0.1.0/migrate-xds-config-surfaces.mjs +0 -230
  177. package/src/commands/gap-report.mjs +0 -456
  178. package/src/commands/gap-report.test.mjs +0 -163
  179. package/src/commands/swizzle-gap-safety.test.mjs +0 -273
  180. package/src/lib/config.mjs +0 -86
  181. package/src/lib/config.test.mjs +0 -42
  182. package/src/types/gap-report.d.ts +0 -29
  183. package/templates/blocks/components/MoreMenu/MoreMenuInToolbar.doc.mjs +0 -14
  184. package/templates/blocks/components/MoreMenu/MoreMenuInToolbar.tsx +0 -57
  185. /package/src/codemods/transforms/{v0.0.15 → v0.1.0}/__tests__/drop-xds-prefix-imports.test.mjs +0 -0
@@ -1,5 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
+ 'use client';
4
+
5
+ import {useState} from 'react';
3
6
  import {Typeahead} from '@astryxdesign/core/Typeahead';
4
7
  import type {SearchableItem, SearchSource} from '@astryxdesign/core/Typeahead';
5
8
 
@@ -21,14 +24,15 @@ const fruitSource: SearchSource = {
21
24
  };
22
25
 
23
26
  export default function TypeaheadShowcase() {
27
+ const [value, setValue] = useState<SearchableItem | null>(null);
24
28
  return (
25
29
  <div style={{width: 320}}>
26
30
  <Typeahead
27
31
  label="Fruit"
28
32
  placeholder="Search fruits..."
29
33
  searchSource={fruitSource}
30
- value={null}
31
- onChange={() => {}}
34
+ value={value}
35
+ onChange={setValue}
32
36
  />
33
37
  </div>
34
38
  );
@@ -0,0 +1,321 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {Fragment, useState, useMemo, useEffect} from 'react';
6
+ import {AppShell} from '@astryxdesign/core/AppShell';
7
+ import {Layout, LayoutHeader, LayoutContent} from '@astryxdesign/core/Layout';
8
+ import {TopNav} from '@astryxdesign/core/TopNav';
9
+ import {DropdownMenu, DropdownMenuItem} from '@astryxdesign/core/DropdownMenu';
10
+ import {CommandPalette} from '@astryxdesign/core/CommandPalette';
11
+ import {createStaticSource} from '@astryxdesign/core/Typeahead';
12
+ import {Divider} from '@astryxdesign/core/Divider';
13
+ import {Kbd} from '@astryxdesign/core/Kbd';
14
+ import {SideNav} from '@astryxdesign/core/SideNav';
15
+ import {TreeList} from '@astryxdesign/core/TreeList';
16
+ import type {TreeListItemData} from '@astryxdesign/core/TreeList';
17
+ import {Icon} from '@astryxdesign/core/Icon';
18
+ import {Text} from '@astryxdesign/core/Text';
19
+ import {IconButton} from '@astryxdesign/core/IconButton';
20
+ import {Button} from '@astryxdesign/core/Button';
21
+ import {TextInput} from '@astryxdesign/core/TextInput';
22
+ import {Card} from '@astryxdesign/core/Card';
23
+ import {Stack, VStack, HStack} from '@astryxdesign/core/Stack';
24
+ import {
25
+ PlayIcon,
26
+ MagnifyingGlassIcon,
27
+ FolderIcon,
28
+ DocumentTextIcon,
29
+ } from '@heroicons/react/24/outline';
30
+
31
+ const noop = () => {};
32
+
33
+ const folder = (
34
+ id: string,
35
+ children: TreeListItemData[],
36
+ isExpanded = true,
37
+ ): TreeListItemData => ({
38
+ id,
39
+ label: <Text maxLines={1}>{id}</Text>,
40
+ startContent: <Icon icon={FolderIcon} size="xsm" />,
41
+ isExpanded,
42
+ children,
43
+ });
44
+
45
+ const file = (id: string, isSelected = false): TreeListItemData => ({
46
+ id,
47
+ label: <Text maxLines={1}>{id}</Text>,
48
+ startContent: <Icon icon={DocumentTextIcon} size="xsm" />,
49
+ isSelected,
50
+ });
51
+
52
+ const FILE_TREE: TreeListItemData[] = [
53
+ folder('src', [
54
+ folder('components', [
55
+ file('AppShell.tsx', true),
56
+ file('TopNav.tsx'),
57
+ file('SideNav.tsx'),
58
+ ]),
59
+ folder('hooks', [file('useTheme.ts'), file('useResizable.ts')]),
60
+ file('index.tsx'),
61
+ file('App.tsx'),
62
+ ]),
63
+ folder('public', [file('favicon.ico'), file('robots.txt')], false),
64
+ file('package.json'),
65
+ file('tsconfig.json'),
66
+ file('README.md'),
67
+ ];
68
+
69
+ // Each menu is split into groups; groups are separated by a divider.
70
+ // `[label, shortcut]` — the shortcut renders as a single combined Kbd
71
+ // (e.g. ⌘N); an empty shortcut renders no Kbd.
72
+ type MenuEntry = [label: string, shortcut: string];
73
+
74
+ const MENU_WIDTH = 280;
75
+
76
+ const MENUS: {label: string; groups: MenuEntry[][]}[] = [
77
+ {
78
+ label: 'File',
79
+ groups: [
80
+ [
81
+ ['New File', '⌘N'],
82
+ ['New Window', '⇧⌘N'],
83
+ ],
84
+ [
85
+ ['Open...', '⌘O'],
86
+ ['Save', '⌘S'],
87
+ ['Save As...', '⇧⌘S'],
88
+ ],
89
+ [['Close Editor', '⌘W']],
90
+ ],
91
+ },
92
+ {
93
+ label: 'Edit',
94
+ groups: [
95
+ [
96
+ ['Undo', '⌘Z'],
97
+ ['Redo', '⇧⌘Z'],
98
+ ],
99
+ [
100
+ ['Cut', '⌘X'],
101
+ ['Copy', '⌘C'],
102
+ ['Paste', '⌘V'],
103
+ ],
104
+ [['Find', '⌘F']],
105
+ ],
106
+ },
107
+ {
108
+ label: 'View',
109
+ groups: [
110
+ [['Command Palette', '⇧⌘P']],
111
+ [
112
+ ['Explorer', '⇧⌘E'],
113
+ ['Search', '⇧⌘F'],
114
+ ],
115
+ [
116
+ ['Toggle Terminal', '⌃`'],
117
+ ['Zen Mode', '⌘K'],
118
+ ],
119
+ ],
120
+ },
121
+ {
122
+ label: 'Window',
123
+ groups: [
124
+ [
125
+ ['Minimize', '⌘M'],
126
+ ['Zoom', ''],
127
+ ],
128
+ [
129
+ ['Next Tab', '⌃⇥'],
130
+ ['Previous Tab', '⌃⇧⇥'],
131
+ ],
132
+ [['Bring All to Front', '']],
133
+ ],
134
+ },
135
+ {
136
+ label: 'Help',
137
+ groups: [
138
+ [
139
+ ['Documentation', ''],
140
+ ['Release Notes', ''],
141
+ ['Report Issue', ''],
142
+ ['About', ''],
143
+ ],
144
+ ],
145
+ },
146
+ ];
147
+
148
+ const CODE_LINES = [
149
+ '38%',
150
+ '62%',
151
+ '54%',
152
+ '0%',
153
+ '46%',
154
+ '70%',
155
+ '58%',
156
+ '34%',
157
+ '0%',
158
+ '50%',
159
+ '66%',
160
+ '42%',
161
+ '60%',
162
+ '28%',
163
+ ];
164
+
165
+ const EDITOR_TABS = ['AppShell.tsx', 'TopNav.tsx', 'theme.ts'];
166
+
167
+ const COMMANDS = [
168
+ {id: 'new-file', label: 'New File'},
169
+ {id: 'open-file', label: 'Open File…'},
170
+ {id: 'save-all', label: 'Save All'},
171
+ {id: 'find-in-files', label: 'Find in Files'},
172
+ {id: 'toggle-terminal', label: 'Toggle Terminal'},
173
+ {id: 'go-to-symbol', label: 'Go to Symbol…'},
174
+ {id: 'appshell', label: 'AppShell.tsx'},
175
+ {id: 'topnav', label: 'TopNav.tsx'},
176
+ {id: 'sidenav', label: 'SideNav.tsx'},
177
+ {id: 'use-theme', label: 'useTheme.ts'},
178
+ {id: 'theme', label: 'theme.ts'},
179
+ ];
180
+
181
+ export default function ShellNav() {
182
+ const [isPaletteOpen, setIsPaletteOpen] = useState(false);
183
+ const searchSource = useMemo(() => createStaticSource(COMMANDS), []);
184
+
185
+ useEffect(() => {
186
+ const handleKeyDown = (e: KeyboardEvent) => {
187
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
188
+ e.preventDefault();
189
+ setIsPaletteOpen(true);
190
+ }
191
+ };
192
+ window.addEventListener('keydown', handleKeyDown);
193
+ return () => window.removeEventListener('keydown', handleKeyDown);
194
+ }, []);
195
+
196
+ return (
197
+ <>
198
+ <AppShell
199
+ contentPadding={0}
200
+ topNav={
201
+ <TopNav
202
+ label="Astryx Studio menu bar"
203
+ startContent={
204
+ <>
205
+ {MENUS.map(menu => (
206
+ <DropdownMenu
207
+ key={menu.label}
208
+ button={{label: menu.label, variant: 'ghost', size: 'sm'}}
209
+ hasChevron={false}
210
+ menuWidth={MENU_WIDTH}>
211
+ {menu.groups.map((group, gi) => (
212
+ <Fragment key={gi}>
213
+ {gi > 0 && <Divider />}
214
+ {group.map(([label, shortcut]) => (
215
+ <DropdownMenuItem
216
+ key={label}
217
+ label={label}
218
+ onClick={noop}
219
+ endContent={
220
+ shortcut ? <Kbd keys={shortcut} /> : undefined
221
+ }
222
+ />
223
+ ))}
224
+ </Fragment>
225
+ ))}
226
+ </DropdownMenu>
227
+ ))}
228
+ </>
229
+ }
230
+ endContent={
231
+ <>
232
+ <Stack onClick={() => setIsPaletteOpen(true)}>
233
+ <TextInput
234
+ label="Search files and commands"
235
+ isLabelHidden
236
+ size="sm"
237
+ width={240}
238
+ startIcon={MagnifyingGlassIcon}
239
+ placeholder="Search files and commands…"
240
+ value=""
241
+ onChange={() => {}}
242
+ />
243
+ </Stack>
244
+ <IconButton
245
+ label="Run project"
246
+ tooltip="Run"
247
+ variant="ghost"
248
+ icon={<Icon icon={PlayIcon} size="sm" />}
249
+ />
250
+ <Button label="Share" variant="secondary" />
251
+ </>
252
+ }
253
+ />
254
+ }
255
+ sideNav={
256
+ <SideNav
257
+ resizable={{defaultWidth: 240, minWidth: 180, maxWidth: 400}}>
258
+ <TreeList items={FILE_TREE} density="compact" />
259
+ </SideNav>
260
+ }>
261
+ <Layout
262
+ height="fill"
263
+ header={
264
+ <LayoutHeader hasDivider padding={6}>
265
+ <HStack gap={2}>
266
+ {EDITOR_TABS.map(tab => (
267
+ <Card
268
+ key={tab}
269
+ variant="muted"
270
+ padding={0}
271
+ width={132}
272
+ height={36}
273
+ />
274
+ ))}
275
+ </HStack>
276
+ </LayoutHeader>
277
+ }
278
+ content={
279
+ <LayoutContent padding={6}>
280
+ <VStack gap={2}>
281
+ {CODE_LINES.map((width, i) =>
282
+ width === '0%' ? (
283
+ <Card
284
+ key={i}
285
+ variant="muted"
286
+ padding={0}
287
+ width={1}
288
+ height={14}
289
+ />
290
+ ) : (
291
+ <HStack key={i} gap={3} vAlign="center">
292
+ <Card
293
+ variant="muted"
294
+ padding={0}
295
+ width={20}
296
+ height={14}
297
+ />
298
+ <Card
299
+ variant="muted"
300
+ padding={0}
301
+ width={width}
302
+ height={14}
303
+ />
304
+ </HStack>
305
+ ),
306
+ )}
307
+ </VStack>
308
+ </LayoutContent>
309
+ }
310
+ />
311
+ </AppShell>
312
+ <CommandPalette
313
+ isOpen={isPaletteOpen}
314
+ onOpenChange={setIsPaletteOpen}
315
+ searchSource={searchSource}
316
+ label="Search files and commands"
317
+ onValueChange={() => setIsPaletteOpen(false)}
318
+ />
319
+ </>
320
+ );
321
+ }
@@ -0,0 +1,12 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'page',
6
+ name: 'Shell Nav',
7
+ displayName: 'Shell Nav',
8
+ description:
9
+ 'A top + side nav app shell with a File/Edit/View/Window/Help menu bar plus command-palette search, a resizable file-tree SideNav, and a static grey-card tabbed code editor.',
10
+ isReady: true,
11
+ category: 'Shell - Top Nav + Left Sidebar',
12
+ };
@@ -0,0 +1,242 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {useState} from 'react';
6
+ import {AppShell} from '@astryxdesign/core/AppShell';
7
+ import {Divider} from '@astryxdesign/core/Divider';
8
+ import {Layout, LayoutContent, LayoutFooter} from '@astryxdesign/core/Layout';
9
+ import {
10
+ SideNav,
11
+ SideNavHeading,
12
+ SideNavItem,
13
+ SideNavSection,
14
+ } from '@astryxdesign/core/SideNav';
15
+ import {NavIcon} from '@astryxdesign/core/NavIcon';
16
+ import {Icon} from '@astryxdesign/core/Icon';
17
+ import type {IconType} from '@astryxdesign/core/Icon';
18
+ import {MoreMenu} from '@astryxdesign/core/MoreMenu';
19
+ import {StatusDot} from '@astryxdesign/core/StatusDot';
20
+ import type {StatusDotVariant} from '@astryxdesign/core/StatusDot';
21
+ import {Card} from '@astryxdesign/core/Card';
22
+ import {Stack, VStack, HStack} from '@astryxdesign/core/Stack';
23
+ import {
24
+ SparklesIcon,
25
+ PlusIcon,
26
+ MagnifyingGlassIcon,
27
+ BookOpenIcon,
28
+ Cog6ToothIcon,
29
+ UserCircleIcon,
30
+ UserIcon,
31
+ BuildingOffice2Icon,
32
+ CodeBracketIcon,
33
+ } from '@heroicons/react/24/outline';
34
+
35
+ type Conversation = {
36
+ label: string;
37
+ status: StatusDotVariant;
38
+ statusLabel: string;
39
+ };
40
+
41
+ type Workspace = {
42
+ name: string;
43
+ icon: IconType;
44
+ chats: Conversation[];
45
+ };
46
+
47
+ const WORKSPACES: Workspace[] = [
48
+ {
49
+ name: 'Personal',
50
+ icon: UserIcon,
51
+ chats: [
52
+ {
53
+ label: 'Weekend trip planning',
54
+ status: 'success',
55
+ statusLabel: 'Active',
56
+ },
57
+ {
58
+ label: 'Recipe ideas for the week',
59
+ status: 'neutral',
60
+ statusLabel: 'Idle',
61
+ },
62
+ {
63
+ label: 'Book recommendations',
64
+ status: 'warning',
65
+ statusLabel: 'Needs review',
66
+ },
67
+ {label: 'Home workout plan', status: 'neutral', statusLabel: 'Idle'},
68
+ ],
69
+ },
70
+ {
71
+ name: 'Acme Corp',
72
+ icon: BuildingOffice2Icon,
73
+ chats: [
74
+ {label: 'Q3 roadmap draft', status: 'accent', statusLabel: 'In progress'},
75
+ {
76
+ label: 'Customer onboarding flow',
77
+ status: 'success',
78
+ statusLabel: 'Active',
79
+ },
80
+ {
81
+ label: 'Pricing strategy review',
82
+ status: 'warning',
83
+ statusLabel: 'Needs review',
84
+ },
85
+ {label: 'Standup summary', status: 'neutral', statusLabel: 'Idle'},
86
+ ],
87
+ },
88
+ {
89
+ name: 'Open Source',
90
+ icon: CodeBracketIcon,
91
+ chats: [
92
+ {
93
+ label: 'StyleX migration notes',
94
+ status: 'accent',
95
+ statusLabel: 'In progress',
96
+ },
97
+ {
98
+ label: 'Skeleton loading states',
99
+ status: 'success',
100
+ statusLabel: 'Active',
101
+ },
102
+ {label: 'Accessibility audit', status: 'error', statusLabel: 'Blocked'},
103
+ {label: 'Release notes v4.0', status: 'neutral', statusLabel: 'Idle'},
104
+ ],
105
+ },
106
+ ];
107
+
108
+ const SELECTED_CHAT = 'StyleX migration notes';
109
+
110
+ const MESSAGES = [
111
+ {role: 'assistant', width: '78%', height: 104},
112
+ {role: 'user', width: '48%', height: 48},
113
+ {role: 'assistant', width: '64%', height: 132},
114
+ {role: 'user', width: '38%', height: 40},
115
+ ];
116
+
117
+ function ConversationItem({
118
+ label,
119
+ status,
120
+ statusLabel,
121
+ isSelected,
122
+ }: {
123
+ label: string;
124
+ status: StatusDotVariant;
125
+ statusLabel: string;
126
+ isSelected?: boolean;
127
+ }) {
128
+ const [isHovered, setIsHovered] = useState(false);
129
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
130
+ const showMenu = isHovered || isMenuOpen;
131
+
132
+ return (
133
+ <Stack
134
+ onMouseEnter={() => setIsHovered(true)}
135
+ onMouseLeave={() => setIsHovered(false)}>
136
+ <SideNavItem
137
+ label={label}
138
+ href="#"
139
+ isSelected={isSelected}
140
+ endContent={
141
+ showMenu ? (
142
+ <MoreMenu
143
+ size="sm"
144
+ label="Conversation options"
145
+ hasAutoFocus={false}
146
+ onOpenChange={setIsMenuOpen}
147
+ items={[
148
+ {label: 'Pin', onClick: () => {}},
149
+ {label: 'Rename', onClick: () => {}},
150
+ {label: 'Archive', onClick: () => {}},
151
+ {label: 'Delete', onClick: () => {}},
152
+ ]}
153
+ />
154
+ ) : (
155
+ <StatusDot variant={status} label={statusLabel} />
156
+ )
157
+ }
158
+ />
159
+ </Stack>
160
+ );
161
+ }
162
+
163
+ export default function ShellSideNav() {
164
+ return (
165
+ <AppShell
166
+ contentPadding={0}
167
+ sideNav={
168
+ <SideNav
169
+ collapsible
170
+ resizable={{defaultWidth: 300, minWidth: 220, maxWidth: 420}}
171
+ header={
172
+ <SideNavHeading
173
+ heading="AI Assistant"
174
+ icon={<NavIcon icon={<Icon icon={SparklesIcon} size="sm" />} />}
175
+ headingHref="#"
176
+ />
177
+ }
178
+ footer={
179
+ <SideNavSection title="Account" isHeaderHidden>
180
+ <SideNavItem label="Settings" icon={Cog6ToothIcon} href="#" />
181
+ <SideNavItem label="Sarah Chen" icon={UserCircleIcon} href="#" />
182
+ </SideNavSection>
183
+ }>
184
+ <SideNavSection title="Menu" isHeaderHidden>
185
+ <SideNavItem label="New chat" icon={PlusIcon} href="#" />
186
+ <SideNavItem label="Search" icon={MagnifyingGlassIcon} href="#" />
187
+ <SideNavItem label="Library" icon={BookOpenIcon} href="#" />
188
+ </SideNavSection>
189
+ <Divider />
190
+ <SideNavSection title="Workspaces" isHeaderHidden>
191
+ {WORKSPACES.map(workspace => (
192
+ <SideNavItem
193
+ key={workspace.name}
194
+ label={workspace.name}
195
+ icon={workspace.icon}
196
+ collapsible={{defaultIsCollapsed: false}}>
197
+ <VStack gap={0.5}>
198
+ {workspace.chats.map(chat => (
199
+ <ConversationItem
200
+ key={chat.label}
201
+ label={chat.label}
202
+ status={chat.status}
203
+ statusLabel={chat.statusLabel}
204
+ isSelected={chat.label === SELECTED_CHAT}
205
+ />
206
+ ))}
207
+ </VStack>
208
+ </SideNavItem>
209
+ ))}
210
+ </SideNavSection>
211
+ </SideNav>
212
+ }>
213
+ <Layout
214
+ height="fill"
215
+ contentWidth={768}
216
+ content={
217
+ <LayoutContent padding={6}>
218
+ <VStack gap={5}>
219
+ {MESSAGES.map((message, mi) => (
220
+ <HStack
221
+ key={mi}
222
+ hAlign={message.role === 'assistant' ? 'start' : 'end'}>
223
+ <Card
224
+ variant="muted"
225
+ padding={0}
226
+ width={message.width}
227
+ height={message.height}
228
+ />
229
+ </HStack>
230
+ ))}
231
+ </VStack>
232
+ </LayoutContent>
233
+ }
234
+ footer={
235
+ <LayoutFooter>
236
+ <Card variant="muted" padding={0} width="100%" height={56} />
237
+ </LayoutFooter>
238
+ }
239
+ />
240
+ </AppShell>
241
+ );
242
+ }
@@ -0,0 +1,12 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'page',
6
+ name: 'Side Nav',
7
+ displayName: 'Side Nav',
8
+ description:
9
+ 'A side-nav app shell with a collapsible, resizable SideNav (new chat, search, library, and workspace-grouped conversations with status dots) over static grey-card conversation and composer placeholders.',
10
+ isReady: true,
11
+ category: 'Shell - Left Sidebar',
12
+ };