@astryxdesign/cli 0.1.2-canary.b712653 → 0.1.2
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.
- package/package.json +7 -7
- package/templates/pages/shell-nav/page.tsx +0 -321
- package/templates/pages/shell-nav/template.doc.mjs +0 -12
- package/templates/pages/shell-side-nav/page.tsx +0 -242
- package/templates/pages/shell-side-nav/template.doc.mjs +0 -12
- package/templates/pages/shell-top-nav/page.tsx +0 -224
- package/templates/pages/shell-top-nav/template.doc.mjs +0 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/cli",
|
|
3
|
-
"version": "0.1.2
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"displayName": "CLI",
|
|
5
5
|
"description": "Scaffold projects, browse templates, generate themes, and get agent-ready docs from the command line.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"zod": "^4.4.3"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"@astryxdesign/core": "
|
|
67
|
-
"@astryxdesign/lab": "
|
|
68
|
-
"@astryxdesign/theme-neutral": "
|
|
66
|
+
"@astryxdesign/core": "*",
|
|
67
|
+
"@astryxdesign/lab": "*",
|
|
68
|
+
"@astryxdesign/theme-neutral": "*",
|
|
69
69
|
"gpt-tokenizer": "^2.0.0"
|
|
70
70
|
},
|
|
71
71
|
"peerDependenciesMeta": {
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
|
-
"@astryxdesign/core": "
|
|
84
|
-
"@astryxdesign/lab": "
|
|
85
|
-
"@astryxdesign/theme-neutral": "
|
|
83
|
+
"@astryxdesign/core": "*",
|
|
84
|
+
"@astryxdesign/lab": "*",
|
|
85
|
+
"@astryxdesign/theme-neutral": "*",
|
|
86
86
|
"gpt-tokenizer": "^2.0.0"
|
|
87
87
|
},
|
|
88
88
|
"scripts": {
|
|
@@ -1,321 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,242 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
'use client';
|
|
4
|
-
|
|
5
|
-
import * as stylex from '@stylexjs/stylex';
|
|
6
|
-
import {AppShell} from '@astryxdesign/core/AppShell';
|
|
7
|
-
import {
|
|
8
|
-
TopNav,
|
|
9
|
-
TopNavHeading,
|
|
10
|
-
TopNavItem,
|
|
11
|
-
TopNavMegaMenu,
|
|
12
|
-
TopNavMegaMenuItem,
|
|
13
|
-
TopNavMegaMenuFeaturedCard,
|
|
14
|
-
} from '@astryxdesign/core/TopNav';
|
|
15
|
-
import {NavIcon} from '@astryxdesign/core/NavIcon';
|
|
16
|
-
import {Icon} from '@astryxdesign/core/Icon';
|
|
17
|
-
import type {IconType} from '@astryxdesign/core/Icon';
|
|
18
|
-
import {IconButton} from '@astryxdesign/core/IconButton';
|
|
19
|
-
import {Button} from '@astryxdesign/core/Button';
|
|
20
|
-
import {Badge} from '@astryxdesign/core/Badge';
|
|
21
|
-
import {Card} from '@astryxdesign/core/Card';
|
|
22
|
-
import {Grid} from '@astryxdesign/core/Grid';
|
|
23
|
-
import {Stack, VStack} from '@astryxdesign/core/Stack';
|
|
24
|
-
import {
|
|
25
|
-
ShoppingBagIcon,
|
|
26
|
-
ShoppingCartIcon,
|
|
27
|
-
MagnifyingGlassIcon,
|
|
28
|
-
SparklesIcon,
|
|
29
|
-
SwatchIcon,
|
|
30
|
-
TagIcon,
|
|
31
|
-
HomeModernIcon,
|
|
32
|
-
FaceSmileIcon,
|
|
33
|
-
ReceiptPercentIcon,
|
|
34
|
-
GiftIcon,
|
|
35
|
-
CloudIcon,
|
|
36
|
-
BoltIcon,
|
|
37
|
-
SunIcon,
|
|
38
|
-
StarIcon,
|
|
39
|
-
FireIcon,
|
|
40
|
-
GlobeAltIcon,
|
|
41
|
-
MoonIcon,
|
|
42
|
-
} from '@heroicons/react/24/outline';
|
|
43
|
-
|
|
44
|
-
const styles = stylex.create({
|
|
45
|
-
// Cap + center the page body so wide screens show whitespace gutters.
|
|
46
|
-
contentMax: {maxWidth: 1100, marginInline: 'auto'},
|
|
47
|
-
// Lock both mega-menu panels to an identical size. Without this, Shop and
|
|
48
|
-
// Brands size to their own content (different widths); since both anchor to
|
|
49
|
-
// the centered nav, switching between them resizes the panel — which reads
|
|
50
|
-
// as flashing/jumping. Fixed item + featured widths make the panels
|
|
51
|
-
// pixel-identical so the transition is seamless.
|
|
52
|
-
megaItems: {gridColumn: '1 / -1', width: 520},
|
|
53
|
-
megaFeatured: {width: 240},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
type MegaItem = {name: string; tagline: string; icon: IconType};
|
|
57
|
-
|
|
58
|
-
// Shop and Brands each render 8 items — the mega menu's built-in 2-column grid
|
|
59
|
-
// lays them out as 2 columns × 4 rows, alongside a featured card.
|
|
60
|
-
const SHOP_ITEMS: MegaItem[] = [
|
|
61
|
-
{name: 'New Arrivals', tagline: 'The latest drops', icon: SparklesIcon},
|
|
62
|
-
{name: 'Womenswear', tagline: 'Dresses, knitwear & more', icon: SwatchIcon},
|
|
63
|
-
{name: 'Menswear', tagline: 'Shirts, tailoring & more', icon: TagIcon},
|
|
64
|
-
{name: 'Home', tagline: 'Bedding, lighting & décor', icon: HomeModernIcon},
|
|
65
|
-
{
|
|
66
|
-
name: 'Beauty',
|
|
67
|
-
tagline: 'Skincare, fragrance & makeup',
|
|
68
|
-
icon: FaceSmileIcon,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'Accessories',
|
|
72
|
-
tagline: 'Bags, hats & sunglasses',
|
|
73
|
-
icon: ShoppingBagIcon,
|
|
74
|
-
},
|
|
75
|
-
{name: 'Sale', tagline: 'Up to 50% off', icon: ReceiptPercentIcon},
|
|
76
|
-
{name: 'Gift Cards', tagline: 'The perfect present', icon: GiftIcon},
|
|
77
|
-
];
|
|
78
|
-
|
|
79
|
-
const BRAND_ITEMS: MegaItem[] = [
|
|
80
|
-
{name: 'Aether', tagline: 'Performance essentials', icon: SparklesIcon},
|
|
81
|
-
{name: 'Northwind', tagline: 'Outdoor & technical', icon: CloudIcon},
|
|
82
|
-
{name: 'Loomwell', tagline: 'Everyday knitwear', icon: BoltIcon},
|
|
83
|
-
{name: 'Verdant', tagline: 'Sustainable basics', icon: SunIcon},
|
|
84
|
-
{name: 'Studio Mara', tagline: 'Modern tailoring', icon: StarIcon},
|
|
85
|
-
{name: 'Atelier Kos', tagline: 'Limited ateliers', icon: FireIcon},
|
|
86
|
-
{name: 'Rue & Co', tagline: 'City streetwear', icon: GlobeAltIcon},
|
|
87
|
-
{name: 'Halden', tagline: 'Minimal staples', icon: MoonIcon},
|
|
88
|
-
];
|
|
89
|
-
|
|
90
|
-
const CATEGORY_TILES = [
|
|
91
|
-
'New Arrivals',
|
|
92
|
-
'Womenswear',
|
|
93
|
-
'Menswear',
|
|
94
|
-
'Home & Living',
|
|
95
|
-
'Beauty',
|
|
96
|
-
'Accessories',
|
|
97
|
-
];
|
|
98
|
-
|
|
99
|
-
// Wraps the 8 items in a fixed-width 2-column grid so every mega menu's item
|
|
100
|
-
// area is exactly the same width regardless of its content.
|
|
101
|
-
function MegaItems({items}: {items: MegaItem[]}) {
|
|
102
|
-
return (
|
|
103
|
-
<Stack xstyle={styles.megaItems}>
|
|
104
|
-
<Grid columns={2} gap={2}>
|
|
105
|
-
{items.map(item => (
|
|
106
|
-
<TopNavMegaMenuItem
|
|
107
|
-
key={item.name}
|
|
108
|
-
title={item.name}
|
|
109
|
-
description={item.tagline}
|
|
110
|
-
icon={<Icon icon={item.icon} size="md" color="secondary" />}
|
|
111
|
-
href="#"
|
|
112
|
-
/>
|
|
113
|
-
))}
|
|
114
|
-
</Grid>
|
|
115
|
-
</Stack>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Pins the featured card to a fixed width so both panels match exactly.
|
|
120
|
-
function MegaFeatured(props: {
|
|
121
|
-
title: string;
|
|
122
|
-
description: string;
|
|
123
|
-
image: string;
|
|
124
|
-
imageAlt: string;
|
|
125
|
-
linkLabel: string;
|
|
126
|
-
linkHref: string;
|
|
127
|
-
}) {
|
|
128
|
-
return (
|
|
129
|
-
<Stack xstyle={styles.megaFeatured}>
|
|
130
|
-
<TopNavMegaMenuFeaturedCard {...props} />
|
|
131
|
-
</Stack>
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export default function ShellTopNav() {
|
|
136
|
-
return (
|
|
137
|
-
<AppShell
|
|
138
|
-
variant="surface"
|
|
139
|
-
contentPadding={6}
|
|
140
|
-
topNav={
|
|
141
|
-
<TopNav
|
|
142
|
-
label="Lumen storefront navigation"
|
|
143
|
-
heading={
|
|
144
|
-
<TopNavHeading
|
|
145
|
-
heading="Lumen"
|
|
146
|
-
logo={
|
|
147
|
-
<NavIcon icon={<Icon icon={ShoppingBagIcon} size="sm" />} />
|
|
148
|
-
}
|
|
149
|
-
href="#"
|
|
150
|
-
/>
|
|
151
|
-
}
|
|
152
|
-
centerContent={
|
|
153
|
-
<>
|
|
154
|
-
<TopNavMegaMenu
|
|
155
|
-
label="Shop"
|
|
156
|
-
items={<MegaItems items={SHOP_ITEMS} />}
|
|
157
|
-
featured={
|
|
158
|
-
<MegaFeatured
|
|
159
|
-
title="The Autumn Edit"
|
|
160
|
-
description="Layering staples in warm, earthy tones."
|
|
161
|
-
image="https://lookaside.facebook.com/assets/astryx/texture-beige-horizontal-1.png"
|
|
162
|
-
imageAlt="Autumn collection lookbook"
|
|
163
|
-
linkLabel="Shop the edit"
|
|
164
|
-
linkHref="#autumn-edit"
|
|
165
|
-
/>
|
|
166
|
-
}
|
|
167
|
-
/>
|
|
168
|
-
<TopNavMegaMenu
|
|
169
|
-
label="Brands"
|
|
170
|
-
items={<MegaItems items={BRAND_ITEMS} />}
|
|
171
|
-
featured={
|
|
172
|
-
<MegaFeatured
|
|
173
|
-
title="Meet Studio Mara"
|
|
174
|
-
description="Modern tailoring, made to last."
|
|
175
|
-
image="https://lookaside.facebook.com/assets/astryx/texture-beige-horizontal-2.png"
|
|
176
|
-
imageAlt="Studio Mara lookbook"
|
|
177
|
-
linkLabel="Discover the label"
|
|
178
|
-
linkHref="#studio-mara"
|
|
179
|
-
/>
|
|
180
|
-
}
|
|
181
|
-
/>
|
|
182
|
-
<TopNavItem label="Sale" href="#" />
|
|
183
|
-
<TopNavItem label="Service" href="#" />
|
|
184
|
-
</>
|
|
185
|
-
}
|
|
186
|
-
endContent={
|
|
187
|
-
<>
|
|
188
|
-
<IconButton
|
|
189
|
-
label="Search products"
|
|
190
|
-
tooltip="Search"
|
|
191
|
-
variant="ghost"
|
|
192
|
-
icon={<Icon icon={MagnifyingGlassIcon} size="sm" />}
|
|
193
|
-
/>
|
|
194
|
-
<Button label="Sign in" variant="ghost" />
|
|
195
|
-
<Button
|
|
196
|
-
label="Checkout"
|
|
197
|
-
variant="primary"
|
|
198
|
-
icon={<Icon icon={ShoppingCartIcon} size="sm" />}
|
|
199
|
-
endContent={<Badge label={3} />}
|
|
200
|
-
/>
|
|
201
|
-
</>
|
|
202
|
-
}
|
|
203
|
-
/>
|
|
204
|
-
}>
|
|
205
|
-
<VStack gap={10} xstyle={styles.contentMax}>
|
|
206
|
-
<Card variant="muted" padding={0} width="100%" height={360} />
|
|
207
|
-
|
|
208
|
-
{[0, 1, 2].map(section => (
|
|
209
|
-
<VStack key={section} gap={4}>
|
|
210
|
-
<Card variant="muted" padding={0} width={200} height={24} />
|
|
211
|
-
<Grid minChildWidth={160} gap={4}>
|
|
212
|
-
{CATEGORY_TILES.map(tile => (
|
|
213
|
-
<VStack key={tile} gap={2}>
|
|
214
|
-
<Card variant="muted" padding={0} width="100%" height={120} />
|
|
215
|
-
<Card variant="muted" padding={0} width="60%" height={14} />
|
|
216
|
-
</VStack>
|
|
217
|
-
))}
|
|
218
|
-
</Grid>
|
|
219
|
-
</VStack>
|
|
220
|
-
))}
|
|
221
|
-
</VStack>
|
|
222
|
-
</AppShell>
|
|
223
|
-
);
|
|
224
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
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: 'Top Nav',
|
|
7
|
-
displayName: 'Top Nav',
|
|
8
|
-
description:
|
|
9
|
-
'A top-nav app shell with a centered TopNav — Shop and Brands mega menus (a 2x4 item grid plus a featured card each), Sale/Service links, and a Checkout button — over static grey-card hero and category sections.',
|
|
10
|
-
isReady: true,
|
|
11
|
-
category: 'Shell - Top Nav',
|
|
12
|
-
};
|