astryx-dracula 0.3.1 → 0.5.0
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/USAGE.md +25 -0
- package/astryx-dracula.d.ts +2 -2
- package/astryx-dracula.js +384 -3
- package/astryx-dracula.variants.d.ts +2 -14
- package/icons.mjs +125 -0
- package/package.json +21 -9
- package/shared/auth-chrome.tsx +156 -6
- package/shared/chaptered-doc.tsx +3 -4
- package/shared/chart-hues.ts +0 -5
- package/shared/chart-legend.tsx +2 -3
- package/shared/data-bar.tsx +5 -5
- package/shared/format-money.ts +3 -0
- package/shared/metric-card.tsx +127 -0
- package/shared/revenue-chart.tsx +63 -7
- package/shared/scene-hues.ts +0 -8
- package/shared/scene-tile.tsx +3 -11
- package/shared/settings-data.ts +71 -1
- package/shared/settings-rows.tsx +76 -30
- package/shared/stars.tsx +28 -0
- package/templates/ai-chat-landing.tsx +105 -67
- package/templates/ai-chat.tsx +67 -80
- package/templates/centered-hero.tsx +2 -2
- package/templates/contact-form.tsx +11 -10
- package/templates/dashboard-portfolio.tsx +77 -155
- package/templates/dashboard.tsx +24 -137
- package/templates/detail-page.tsx +25 -22
- package/templates/documentation-design.tsx +39 -478
- package/templates/documentation-technical.tsx +13 -31
- package/templates/editor.tsx +168 -185
- package/templates/file-explorer.tsx +91 -93
- package/templates/form-two-column.tsx +1 -1
- package/templates/ide.tsx +18 -24
- package/templates/incident-console.tsx +67 -42
- package/templates/kanban-board.tsx +36 -85
- package/templates/library.tsx +42 -66
- package/templates/login-card.tsx +33 -123
- package/templates/login-split.tsx +96 -119
- package/templates/login-sso.tsx +69 -74
- package/templates/login.tsx +21 -118
- package/templates/messaging-shell.tsx +162 -168
- package/templates/payment-form.tsx +63 -71
- package/templates/product-detail.tsx +11 -46
- package/templates/product-tour.tsx +38 -60
- package/templates/settings-dialog.tsx +93 -118
- package/templates/settings-sidebar.tsx +122 -143
- package/templates/settings.tsx +208 -121
- package/templates/shell-nav.tsx +20 -75
- package/templates/shell-side-nav.tsx +16 -90
- package/templates/shell-top-nav.tsx +20 -53
- package/templates/side-gallery.tsx +19 -19
- package/templates/table-grouped.tsx +86 -136
- package/templates/table-page-chart.tsx +12 -11
- package/templates/table-page-heatmap-status.tsx +0 -17
- package/templates/table-page-shoe-store-heatmap.tsx +12 -12
- package/templates/table-page.tsx +4 -31
- package/templates/table.tsx +18 -2
- package/templates/tech-report.tsx +59 -73
- package/templates/theme-showcase.tsx +124 -415
- package/theme.css +211 -119
package/templates/ai-chat.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
3
|
// L > LC[p=0] > H > (SI[fill] > ChL > ChML > (ChM > ChB)*4 + ChC"Ask a follow up...") + (C.transparent > Tbar + S.section > MD) ;; Dlg#artifact[variant=fullscreen] > L > (DH"JWT Token Refresh" + (LC[p=0] > S.section > MD))
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {useState, type CSSProperties} from 'react';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
HStack,
|
|
@@ -40,6 +40,7 @@ import {DropdownMenu} from '@astryxdesign/core/DropdownMenu';
|
|
|
40
40
|
import {MoreMenu} from '@astryxdesign/core/MoreMenu';
|
|
41
41
|
import {Toolbar} from '@astryxdesign/core/Toolbar';
|
|
42
42
|
import {useResizable, ResizeHandle} from '@astryxdesign/core/Resizable';
|
|
43
|
+
import {useMediaQuery} from '@astryxdesign/core/hooks';
|
|
43
44
|
|
|
44
45
|
import {
|
|
45
46
|
FileText,
|
|
@@ -52,19 +53,17 @@ import {
|
|
|
52
53
|
} from 'lucide-react';
|
|
53
54
|
|
|
54
55
|
// Below this container width the split-pane collapses to a single chat column
|
|
55
|
-
// and the artifact opens as a full-screen dialog instead.
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
56
|
+
// and the artifact opens as a full-screen dialog instead. Single source:
|
|
57
|
+
// MOBILE_MAX_WIDTH feeds useMediaQuery below, which both gates the panel and
|
|
58
|
+
// routes openArtifact — no container query, no manual width read to drift.
|
|
59
|
+
// 1023 keeps tablet widths from squeezing the chat into a ~100px rail beside
|
|
60
|
+
// the 640px default artifact panel.
|
|
59
61
|
const MOBILE_MAX_WIDTH = 1023;
|
|
60
|
-
|
|
61
62
|
const root: CSSProperties = {
|
|
62
63
|
// The viewer owns its chrome: the root fills the viewport and the message
|
|
63
64
|
// list plus artifact body scroll inside it (editor pattern).
|
|
64
65
|
height: '100dvh',
|
|
65
66
|
width: '100%',
|
|
66
|
-
containerType: 'inline-size',
|
|
67
|
-
containerName: 'artifact',
|
|
68
67
|
};
|
|
69
68
|
const chatColumn: CSSProperties = {
|
|
70
69
|
flex: 1,
|
|
@@ -91,38 +90,16 @@ const articleBody: CSSProperties = {
|
|
|
91
90
|
marginInline: 'auto',
|
|
92
91
|
};
|
|
93
92
|
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
// plain <style> tag below so it needs NO CSS compiler.
|
|
93
|
+
// Width for the artifact panel. Plain style object — the panel unmounts below
|
|
94
|
+
// the breakpoint (see useMediaQuery in the component), so no container query
|
|
95
|
+
// or <style> tag is needed to hide it.
|
|
98
96
|
const artifactPanelWidthVar = (size: number | string): CSSProperties =>
|
|
99
97
|
({
|
|
100
|
-
|
|
98
|
+
width: typeof size === 'number' ? `${size}px` : size,
|
|
99
|
+
flexShrink: 0,
|
|
100
|
+
overflow: 'hidden',
|
|
101
101
|
}) as CSSProperties;
|
|
102
102
|
|
|
103
|
-
const AI_CHAT_CSS = `
|
|
104
|
-
.ai-chat-resize-handle {
|
|
105
|
-
display: flex;
|
|
106
|
-
}
|
|
107
|
-
.ai-chat-artifact-panel {
|
|
108
|
-
overflow: hidden;
|
|
109
|
-
display: flex;
|
|
110
|
-
flex-direction: column;
|
|
111
|
-
width: var(--artifact-panel-width);
|
|
112
|
-
flex-shrink: 0;
|
|
113
|
-
}
|
|
114
|
-
@container artifact (max-width: ${MOBILE_MAX_WIDTH}px) {
|
|
115
|
-
.ai-chat-resize-handle {
|
|
116
|
-
display: none;
|
|
117
|
-
}
|
|
118
|
-
.ai-chat-artifact-panel {
|
|
119
|
-
display: none;
|
|
120
|
-
width: 100%;
|
|
121
|
-
flex-shrink: 1;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
`;
|
|
125
|
-
|
|
126
103
|
// Artifact content
|
|
127
104
|
|
|
128
105
|
const MENTION_TOKENS = [
|
|
@@ -175,6 +152,13 @@ The refresh path is covered end to end:
|
|
|
175
152
|
|
|
176
153
|
// Artifact subviews
|
|
177
154
|
|
|
155
|
+
// Copy/Share live in both surfaces: desktop toolbar buttons and the mobile
|
|
156
|
+
// overflow menu. One items array so the two can't drift.
|
|
157
|
+
const ARTIFACT_ACTION_ITEMS = [
|
|
158
|
+
{label: 'Copy', icon: Copy},
|
|
159
|
+
{label: 'Share', icon: Share2},
|
|
160
|
+
] as const;
|
|
161
|
+
|
|
178
162
|
// Header actions: version menu, copy, share. Pass `onClose` for the desktop
|
|
179
163
|
// close button. A fragment so each control is a direct child of the toolbar.
|
|
180
164
|
function ArtifactActions({onClose}: {onClose?: () => void}) {
|
|
@@ -188,20 +172,16 @@ function ArtifactActions({onClose}: {onClose?: () => void}) {
|
|
|
188
172
|
}}
|
|
189
173
|
items={[{label: 'v2 (current)'}, {label: 'v1'}]}
|
|
190
174
|
/>
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
size="sm"
|
|
202
|
-
icon={<Icon icon={Share2} size="sm" />}
|
|
203
|
-
isIconOnly
|
|
204
|
-
/>
|
|
175
|
+
{ARTIFACT_ACTION_ITEMS.map(item => (
|
|
176
|
+
<Button
|
|
177
|
+
key={item.label}
|
|
178
|
+
label={item.label}
|
|
179
|
+
variant="ghost"
|
|
180
|
+
size="sm"
|
|
181
|
+
icon={<Icon icon={item.icon} size="sm" />}
|
|
182
|
+
isIconOnly
|
|
183
|
+
/>
|
|
184
|
+
))}
|
|
205
185
|
{onClose != null && (
|
|
206
186
|
<Button
|
|
207
187
|
label="Close document"
|
|
@@ -226,23 +206,23 @@ function MobileArtifactActions() {
|
|
|
226
206
|
{
|
|
227
207
|
type: 'section',
|
|
228
208
|
title: 'Version',
|
|
229
|
-
items: [
|
|
230
|
-
{label: 'v2 (current)', onClick: () => {}},
|
|
231
|
-
{label: 'v1', onClick: () => {}},
|
|
232
|
-
],
|
|
209
|
+
items: [{label: 'v2 (current)'}, {label: 'v1'}],
|
|
233
210
|
},
|
|
234
211
|
{type: 'divider'},
|
|
235
|
-
|
|
236
|
-
{label: 'Share', icon: Share2},
|
|
212
|
+
...ARTIFACT_ACTION_ITEMS,
|
|
237
213
|
]}
|
|
238
214
|
/>
|
|
239
215
|
);
|
|
240
216
|
}
|
|
241
217
|
|
|
242
|
-
// Scrollable artifact
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
function
|
|
218
|
+
// Scrollable artifact content — the formatted document, no heading. The
|
|
219
|
+
// desktop panel renders ArtifactBody above it; the mobile dialog renders its
|
|
220
|
+
// title in DialogHeader instead, so both share this rather than a flag prop.
|
|
221
|
+
function ArtifactContent() {
|
|
222
|
+
return <Markdown>{ARTIFACT_CONTENT}</Markdown>;
|
|
223
|
+
}
|
|
224
|
+
// Desktop body: in-body heading plus the shared content.
|
|
225
|
+
function ArtifactBody() {
|
|
246
226
|
return (
|
|
247
227
|
<Section
|
|
248
228
|
variant="transparent"
|
|
@@ -251,13 +231,10 @@ function ArtifactBody({hasOwnTitle = true}: {hasOwnTitle?: boolean}) {
|
|
|
251
231
|
aria-label={ARTIFACT_TITLE}
|
|
252
232
|
tabIndex={0}>
|
|
253
233
|
<VStack gap={2} style={articleBody}>
|
|
254
|
-
{
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
</Heading>
|
|
259
|
-
)}
|
|
260
|
-
<Markdown>{ARTIFACT_CONTENT}</Markdown>
|
|
234
|
+
<Heading level={2} type="display-2">
|
|
235
|
+
{ARTIFACT_TITLE}
|
|
236
|
+
</Heading>
|
|
237
|
+
<ArtifactContent />
|
|
261
238
|
</VStack>
|
|
262
239
|
</Section>
|
|
263
240
|
);
|
|
@@ -298,18 +275,19 @@ export default function AiChat() {
|
|
|
298
275
|
// Mobile shows the artifact as a full-screen dialog; desktop as a side panel.
|
|
299
276
|
const [isArtifactDialogOpen, setIsArtifactDialogOpen] = useState(false);
|
|
300
277
|
const [isArtifactOpen, setIsArtifactOpen] = useState(true);
|
|
301
|
-
const rootRef = useRef<HTMLElement>(null);
|
|
302
278
|
const artifactResize = useResizable({
|
|
303
279
|
defaultSize: 640,
|
|
304
|
-
|
|
305
|
-
|
|
280
|
+
minSize: 480,
|
|
281
|
+
maxSize: 960,
|
|
306
282
|
autoSaveId: 'ai-chat-artifact-panel',
|
|
307
283
|
});
|
|
308
284
|
|
|
309
|
-
//
|
|
285
|
+
// Single source for the breakpoint: the media query both gates the panel
|
|
286
|
+
// below and routes openArtifact, so the split-pane and the dialog target
|
|
287
|
+
// can't disagree about which surface is live.
|
|
288
|
+
const isMobileWidth = useMediaQuery(`(max-width: ${MOBILE_MAX_WIDTH}px)`);
|
|
310
289
|
const openArtifact = () => {
|
|
311
|
-
|
|
312
|
-
if (width <= MOBILE_MAX_WIDTH) {
|
|
290
|
+
if (isMobileWidth) {
|
|
313
291
|
setIsArtifactDialogOpen(true);
|
|
314
292
|
} else {
|
|
315
293
|
setIsArtifactOpen(true);
|
|
@@ -317,8 +295,7 @@ export default function AiChat() {
|
|
|
317
295
|
};
|
|
318
296
|
|
|
319
297
|
return (
|
|
320
|
-
<VStack
|
|
321
|
-
<style>{AI_CHAT_CSS}</style>
|
|
298
|
+
<VStack style={root}>
|
|
322
299
|
<Layout
|
|
323
300
|
height="fill"
|
|
324
301
|
content={
|
|
@@ -700,8 +677,11 @@ The fix is to catch \`TokenExpiredError\` specifically and attempt a refresh bef
|
|
|
700
677
|
</ChatLayout>
|
|
701
678
|
</VStack>
|
|
702
679
|
|
|
703
|
-
{/* Desktop split-pane: resize handle + artifact panel
|
|
704
|
-
|
|
680
|
+
{/* Desktop split-pane: resize handle + artifact panel. Gated on
|
|
681
|
+
the same media query that routes openArtifact, so shrinking
|
|
682
|
+
the window while open moves to the dialog instead of
|
|
683
|
+
squeezing the chat into a rail. */}
|
|
684
|
+
{isArtifactOpen && !isMobileWidth && (
|
|
705
685
|
<>
|
|
706
686
|
<ResizeHandle
|
|
707
687
|
direction="horizontal"
|
|
@@ -710,14 +690,12 @@ The fix is to catch \`TokenExpiredError\` specifically and attempt a refresh bef
|
|
|
710
690
|
pillPlacement="start"
|
|
711
691
|
hasDivider
|
|
712
692
|
label="Resize artifact panel"
|
|
713
|
-
className="ai-chat-resize-handle"
|
|
714
693
|
/>
|
|
715
694
|
|
|
716
695
|
{/* Toolbar as the card header, body below */}
|
|
717
696
|
<Card
|
|
718
697
|
variant="transparent"
|
|
719
698
|
height="100%"
|
|
720
|
-
className="ai-chat-artifact-panel"
|
|
721
699
|
style={artifactPanelWidthVar(artifactResize.size)}>
|
|
722
700
|
<Toolbar
|
|
723
701
|
label="Artifact actions"
|
|
@@ -773,7 +751,16 @@ The fix is to catch \`TokenExpiredError\` specifically and attempt a refresh bef
|
|
|
773
751
|
}
|
|
774
752
|
content={
|
|
775
753
|
<LayoutContent padding={0}>
|
|
776
|
-
<
|
|
754
|
+
<Section
|
|
755
|
+
variant="transparent"
|
|
756
|
+
style={artifactScroll}
|
|
757
|
+
role="region"
|
|
758
|
+
aria-label={ARTIFACT_TITLE}
|
|
759
|
+
tabIndex={0}>
|
|
760
|
+
<VStack gap={2} style={articleBody}>
|
|
761
|
+
<ArtifactContent />
|
|
762
|
+
</VStack>
|
|
763
|
+
</Section>
|
|
777
764
|
</LayoutContent>
|
|
778
765
|
}
|
|
779
766
|
/>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
// L > LC[p=6] > V[g=10] > ((V[g=6 a=center] > (V[g=3 a=center] > Hd"Small joys for creatures of the night"[level=1 t=display-2] + Tx"Sometimes all it takes is one small ritual to turn the whole night around."[t=body] + Tx"astryx-dracula v0.
|
|
3
|
+
// L > LC[p=6] > V[g=10] > ((V[g=6 a=center] > (V[g=3 a=center] > Hd"Small joys for creatures of the night"[level=1 t=display-2] + Tx"Sometimes all it takes is one small ritual to turn the whole night around."[t=body] + Tx"astryx-dracula v0.5.0"[t=supporting]) + (H[j=center g=3] > B.primary"Embrace the night" + B.secondary"Read the lore")) + (S.transparent[p=0] > AR[ratio=16/9]))
|
|
4
4
|
|
|
5
5
|
import type {CSSProperties} from 'react';
|
|
6
6
|
import {VStack, HStack, Layout, LayoutContent} from '@astryxdesign/core/Layout';
|
|
@@ -52,7 +52,7 @@ export default function CenteredHero() {
|
|
|
52
52
|
color="secondary"
|
|
53
53
|
justify="center"
|
|
54
54
|
hasTabularNumbers>
|
|
55
|
-
astryx-dracula v0.
|
|
55
|
+
astryx-dracula v0.5.0
|
|
56
56
|
</Text>
|
|
57
57
|
</VStack>
|
|
58
58
|
<HStack gap={3} wrap="wrap" justify="center">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
// Ctr > V[a=center] > S.transparent[p=6] > V[g=6] > (V[g=2 a=center] > Hd"Let's brew after dark"[level=1] + Tx"Tell us a bit"[t=body]) + (V[g=4] > Hd"Why work with us"[level=2] + (G[c={min:200} g=4] > (C > V[g=3] > Ic + (V[g=1] > Hd"Title"[level=3] + Tx"Desc"[t=body]))*3)) + (V[g=4] > Hd"Your details"[level=2] + (G[c={min:260} g=4] > TI"Full name" + TI"Email") + (G[c={min:260} g=4] > TI"Company" + TI"Phone")) + D + (V[g=4] > Hd"Your project"[level=2] + (Fd"Goals"[req] > (H[g=2] > Tk"Goal"*10)) + SE"Timeline" + SE"Budget" + (RL"Source" > RLI*5) + TA"Notes" + CB"Decision maker") + (V[g=3] > B.primary"Send it into the night" + (
|
|
3
|
+
// Ctr > V[a=center] > S.transparent[p=6] > V[g=6] > (V[g=2 a=center] > Hd"Let's brew after dark"[level=1] + Tx"Tell us a bit"[t=body]) + (V[g=4] > Hd"Why work with us"[level=2] + (G[c={min:200} g=4] > (C > V[g=3] > Ic + (V[g=1] > Hd"Title"[level=3] + Tx"Desc"[t=body]))*3)) + (V[g=4] > Hd"Your details"[level=2] + (G[c={min:260} g=4] > TI"Full name" + TI"Email") + (G[c={min:260} g=4] > TI"Company" + TI"Phone")) + D + (V[g=4] > Hd"Your project"[level=2] + (Fd"Goals"[req] > (H[g=2] > Tk"Goal"*10)) + SE"Timeline" + SE"Budget" + (RL"Source" > RLI*5) + TA"Notes" + CB"Decision maker") + (V[g=3] > B.primary"Send it into the night" + (Tx[t=supporting] > Lk"Privacy Policy"))
|
|
4
4
|
|
|
5
5
|
import {useState} from 'react';
|
|
6
6
|
import {VStack, HStack} from '@astryxdesign/core/Layout';
|
|
@@ -296,15 +296,16 @@ export default function ContactForm() {
|
|
|
296
296
|
variant="primary"
|
|
297
297
|
onClick={() => setSubmitted(true)}
|
|
298
298
|
/>
|
|
299
|
-
<
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
</
|
|
307
|
-
|
|
299
|
+
<Text type="supporting" color="secondary">
|
|
300
|
+
By submitting you agree to our{' '}
|
|
301
|
+
<Link
|
|
302
|
+
href="#/templates/contact-form"
|
|
303
|
+
type="supporting"
|
|
304
|
+
hasUnderline>
|
|
305
|
+
Privacy Policy
|
|
306
|
+
</Link>
|
|
307
|
+
.
|
|
308
|
+
</Text>
|
|
308
309
|
</VStack>
|
|
309
310
|
</VStack>
|
|
310
311
|
</Section>
|