prev-cli 0.24.15 → 0.24.18
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/dist/cli.d.ts +1 -1
- package/dist/cli.js +4466 -1681
- package/dist/jsx/adapters/html.d.ts +15 -0
- package/dist/jsx/adapters/react.d.ts +26 -0
- package/dist/jsx/define-component.d.ts +68 -0
- package/dist/jsx/index.d.ts +7 -0
- package/dist/jsx/jsx-runtime.d.ts +34 -0
- package/dist/jsx/migrate.d.ts +24 -0
- package/dist/jsx/schemas/index.d.ts +2 -0
- package/dist/jsx/schemas/primitives.d.ts +355 -0
- package/dist/jsx/schemas/tokens.d.ts +79 -0
- package/dist/jsx/validation.d.ts +28 -0
- package/dist/jsx/vnode.d.ts +58 -0
- package/dist/migrate.d.ts +18 -0
- package/dist/primitives/index.d.ts +5 -0
- package/dist/primitives/migrate.d.ts +25 -0
- package/dist/primitives/parser.d.ts +32 -0
- package/dist/primitives/template-parser.d.ts +33 -0
- package/dist/primitives/template-renderer.d.ts +19 -0
- package/dist/primitives/types.d.ts +164 -0
- package/dist/renderers/html/index.d.ts +6 -0
- package/dist/renderers/index.d.ts +5 -0
- package/dist/renderers/react/index.d.ts +6 -0
- package/dist/renderers/registry.d.ts +41 -0
- package/dist/renderers/render.d.ts +35 -0
- package/dist/renderers/types.d.ts +188 -0
- package/dist/tokens/defaults.d.ts +2 -0
- package/dist/tokens/resolver.d.ts +67 -0
- package/dist/tokens/utils.d.ts +15 -0
- package/dist/tokens/validation.d.ts +36 -0
- package/dist/typecheck/index.d.ts +24 -0
- package/dist/ui/button.d.ts +1 -1
- package/dist/validators/index.d.ts +59 -0
- package/dist/validators/schema-validator.d.ts +27 -0
- package/dist/validators/semantic-validator.d.ts +47 -0
- package/dist/vite/plugins/tokens-plugin.d.ts +2 -0
- package/package.json +8 -4
- package/src/preview-runtime/fast-template.html +115 -0
- package/src/preview-runtime/template.html +50 -8
- package/src/preview-runtime/vendors.ts +9 -0
- package/src/theme/entry.tsx +153 -12
- package/src/theme/previews/TokensPage.tsx +328 -0
package/src/theme/entry.tsx
CHANGED
|
@@ -15,8 +15,10 @@ import {
|
|
|
15
15
|
import { MDXProvider } from '@mdx-js/react'
|
|
16
16
|
import { pages, sidebar } from 'virtual:prev-pages'
|
|
17
17
|
import { pageModules } from 'virtual:prev-page-modules'
|
|
18
|
-
import { previews } from 'virtual:prev-previews'
|
|
18
|
+
import { previews, previewUnits } from 'virtual:prev-previews'
|
|
19
|
+
import type { PreviewUnit, PreviewType } from '../vite/preview-types'
|
|
19
20
|
import { Preview } from './Preview'
|
|
21
|
+
import { TokensPage } from './previews/TokensPage'
|
|
20
22
|
import { useDiagrams } from './diagrams'
|
|
21
23
|
import { Layout } from './Layout'
|
|
22
24
|
import { MetadataBlock } from './MetadataBlock'
|
|
@@ -91,9 +93,119 @@ function PageWrapper({ Component, meta }: { Component: React.ComponentType; meta
|
|
|
91
93
|
)
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
//
|
|
96
|
+
// Category metadata for display
|
|
97
|
+
const CATEGORY_META: Record<PreviewType, { label: string; icon: string; description: string }> = {
|
|
98
|
+
component: {
|
|
99
|
+
label: 'Components',
|
|
100
|
+
icon: '⬡',
|
|
101
|
+
description: 'Reusable UI building blocks',
|
|
102
|
+
},
|
|
103
|
+
screen: {
|
|
104
|
+
label: 'Screens',
|
|
105
|
+
icon: '▣',
|
|
106
|
+
description: 'Full page layouts and views',
|
|
107
|
+
},
|
|
108
|
+
flow: {
|
|
109
|
+
label: 'Flows',
|
|
110
|
+
icon: '⇢',
|
|
111
|
+
description: 'Multi-step user journeys',
|
|
112
|
+
},
|
|
113
|
+
atlas: {
|
|
114
|
+
label: 'Atlas',
|
|
115
|
+
icon: '◎',
|
|
116
|
+
description: 'Information architecture maps',
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Category display order
|
|
121
|
+
const CATEGORY_ORDER: PreviewType[] = ['component', 'screen', 'flow', 'atlas']
|
|
122
|
+
|
|
123
|
+
// Group previews by type
|
|
124
|
+
function groupByType(units: PreviewUnit[]): Map<PreviewType, PreviewUnit[]> {
|
|
125
|
+
const grouped = new Map<PreviewType, PreviewUnit[]>()
|
|
126
|
+
for (const unit of units) {
|
|
127
|
+
const existing = grouped.get(unit.type) || []
|
|
128
|
+
grouped.set(unit.type, [...existing, unit])
|
|
129
|
+
}
|
|
130
|
+
return grouped
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Category section component
|
|
134
|
+
function CategorySection({ type, units }: { type: PreviewType; units: PreviewUnit[] }) {
|
|
135
|
+
const meta = CATEGORY_META[type]
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<section style={{ marginBottom: '32px' }}>
|
|
139
|
+
<div style={{
|
|
140
|
+
display: 'flex',
|
|
141
|
+
alignItems: 'center',
|
|
142
|
+
gap: '10px',
|
|
143
|
+
marginBottom: '16px',
|
|
144
|
+
paddingBottom: '12px',
|
|
145
|
+
borderBottom: '1px solid var(--fd-border)',
|
|
146
|
+
}}>
|
|
147
|
+
<span style={{ fontSize: '20px' }}>{meta.icon}</span>
|
|
148
|
+
<div>
|
|
149
|
+
<h2 style={{
|
|
150
|
+
fontSize: '18px',
|
|
151
|
+
fontWeight: '600',
|
|
152
|
+
margin: 0,
|
|
153
|
+
color: 'var(--fd-foreground)',
|
|
154
|
+
}}>
|
|
155
|
+
{meta.label}
|
|
156
|
+
<span style={{
|
|
157
|
+
marginLeft: '8px',
|
|
158
|
+
fontSize: '14px',
|
|
159
|
+
fontWeight: '400',
|
|
160
|
+
color: 'var(--fd-muted-foreground)',
|
|
161
|
+
}}>
|
|
162
|
+
({units.length})
|
|
163
|
+
</span>
|
|
164
|
+
</h2>
|
|
165
|
+
<p style={{
|
|
166
|
+
fontSize: '13px',
|
|
167
|
+
color: 'var(--fd-muted-foreground)',
|
|
168
|
+
margin: 0,
|
|
169
|
+
}}>
|
|
170
|
+
{meta.description}
|
|
171
|
+
</p>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
<div className="previews-grid">
|
|
175
|
+
{units.map((unit) => {
|
|
176
|
+
// Extract full path name from route (e.g., "/_preview/components/button" -> "components/button")
|
|
177
|
+
const fullName = unit.route.replace(/^\/_preview\//, '')
|
|
178
|
+
return (
|
|
179
|
+
<PreviewCard
|
|
180
|
+
key={fullName}
|
|
181
|
+
name={fullName}
|
|
182
|
+
title={unit.config?.title}
|
|
183
|
+
status={unit.config?.status}
|
|
184
|
+
/>
|
|
185
|
+
)
|
|
186
|
+
})}
|
|
187
|
+
</div>
|
|
188
|
+
</section>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Previews catalog - Storybook-like gallery with categorized sections
|
|
95
193
|
function PreviewsCatalog() {
|
|
96
|
-
|
|
194
|
+
// Use previewUnits for categorization, fall back to legacy previews
|
|
195
|
+
const units = previewUnits || []
|
|
196
|
+
const legacyPreviews = previews || []
|
|
197
|
+
|
|
198
|
+
// If no units but have legacy previews, convert them
|
|
199
|
+
const allUnits: PreviewUnit[] = units.length > 0 ? units : legacyPreviews.map((p: { name: string; route: string }) => ({
|
|
200
|
+
type: 'component' as PreviewType,
|
|
201
|
+
name: p.name,
|
|
202
|
+
path: '',
|
|
203
|
+
route: p.route,
|
|
204
|
+
config: null,
|
|
205
|
+
files: { index: '' },
|
|
206
|
+
}))
|
|
207
|
+
|
|
208
|
+
if (allUnits.length === 0) {
|
|
97
209
|
return (
|
|
98
210
|
<div style={{ padding: '40px 20px', textAlign: 'center' }}>
|
|
99
211
|
<h1 style={{ fontSize: '24px', fontWeight: 'bold', marginBottom: '16px' }}>
|
|
@@ -115,24 +227,30 @@ function PreviewsCatalog() {
|
|
|
115
227
|
)
|
|
116
228
|
}
|
|
117
229
|
|
|
230
|
+
const grouped = groupByType(allUnits)
|
|
231
|
+
const totalCount = allUnits.length
|
|
232
|
+
|
|
118
233
|
return (
|
|
119
234
|
<div className="previews-catalog">
|
|
120
|
-
|
|
235
|
+
{/* Header */}
|
|
236
|
+
<div style={{ marginBottom: '32px' }}>
|
|
121
237
|
<h1 style={{ fontSize: '28px', fontWeight: 'bold', marginBottom: '8px' }}>
|
|
122
238
|
Previews
|
|
123
239
|
</h1>
|
|
124
240
|
<p style={{ color: 'var(--fd-muted-foreground)', margin: 0 }}>
|
|
125
|
-
{
|
|
241
|
+
{totalCount} preview{totalCount !== 1 ? 's' : ''} across {grouped.size} categor{grouped.size !== 1 ? 'ies' : 'y'}.
|
|
126
242
|
Click any preview to open it.
|
|
127
243
|
</p>
|
|
128
244
|
</div>
|
|
129
245
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
134
|
-
|
|
246
|
+
{/* Categorized sections */}
|
|
247
|
+
{CATEGORY_ORDER.map((type) => {
|
|
248
|
+
const units = grouped.get(type)
|
|
249
|
+
if (!units || units.length === 0) return null
|
|
250
|
+
return <CategorySection key={type} type={type} units={units} />
|
|
251
|
+
})}
|
|
135
252
|
|
|
253
|
+
{/* Tip */}
|
|
136
254
|
<div style={{
|
|
137
255
|
marginTop: '32px',
|
|
138
256
|
padding: '14px 16px',
|
|
@@ -154,7 +272,7 @@ function PreviewsCatalog() {
|
|
|
154
272
|
// Individual preview card - clickable thumbnail with WASM preview communication
|
|
155
273
|
import type { PreviewConfig, PreviewMessage } from '../preview-runtime/types'
|
|
156
274
|
|
|
157
|
-
function PreviewCard({ name }: { name: string }) {
|
|
275
|
+
function PreviewCard({ name, title, status }: { name: string; title?: string; status?: 'draft' | 'stable' | 'deprecated' }) {
|
|
158
276
|
const iframeRef = React.useRef<HTMLIFrameElement>(null)
|
|
159
277
|
const [isLoaded, setIsLoaded] = React.useState(false)
|
|
160
278
|
const [loadError, setLoadError] = React.useState(false)
|
|
@@ -255,7 +373,22 @@ function PreviewCard({ name }: { name: string }) {
|
|
|
255
373
|
</div>
|
|
256
374
|
{/* Card footer */}
|
|
257
375
|
<div className="preview-card-footer">
|
|
258
|
-
<
|
|
376
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
377
|
+
<h3 className="preview-card-title">{title || name}</h3>
|
|
378
|
+
{status && status !== 'stable' && (
|
|
379
|
+
<span style={{
|
|
380
|
+
fontSize: '10px',
|
|
381
|
+
fontWeight: '500',
|
|
382
|
+
padding: '2px 6px',
|
|
383
|
+
borderRadius: '4px',
|
|
384
|
+
backgroundColor: status === 'draft' ? 'var(--fd-warning-background, #fef3c7)' : 'var(--fd-muted)',
|
|
385
|
+
color: status === 'draft' ? 'var(--fd-warning-foreground, #92400e)' : 'var(--fd-muted-foreground)',
|
|
386
|
+
textTransform: 'uppercase',
|
|
387
|
+
}}>
|
|
388
|
+
{status}
|
|
389
|
+
</span>
|
|
390
|
+
)}
|
|
391
|
+
</div>
|
|
259
392
|
<code className="preview-card-path">previews/{name}/</code>
|
|
260
393
|
</div>
|
|
261
394
|
</Link>
|
|
@@ -329,6 +462,13 @@ const previewDetailRoute = createRoute({
|
|
|
329
462
|
component: PreviewPage,
|
|
330
463
|
})
|
|
331
464
|
|
|
465
|
+
// Tokens showcase route (design system tokens reference)
|
|
466
|
+
const tokensRoute = createRoute({
|
|
467
|
+
getParentRoute: () => rootRoute,
|
|
468
|
+
path: '/_prev/tokens',
|
|
469
|
+
component: TokensPage,
|
|
470
|
+
})
|
|
471
|
+
|
|
332
472
|
// Check if we have an index page (route '/')
|
|
333
473
|
const hasIndexPage = pages.some((page: { route: string }) => page.route === '/')
|
|
334
474
|
const firstPage = pages[0] as { route: string; file: string; title?: string; description?: string; frontmatter?: Record<string, unknown> } | undefined
|
|
@@ -370,6 +510,7 @@ const previewsRouteWithChildren = previewsLayoutRoute.addChildren([
|
|
|
370
510
|
|
|
371
511
|
const routeTree = rootRoute.addChildren([
|
|
372
512
|
previewsRouteWithChildren,
|
|
513
|
+
tokensRoute,
|
|
373
514
|
...(indexRedirectRoute ? [indexRedirectRoute] : []),
|
|
374
515
|
...pageRoutes,
|
|
375
516
|
])
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// src/theme/previews/TokensPage.tsx
|
|
2
|
+
// Visual showcase page for design tokens at /_prev/tokens
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
import { tokens } from 'virtual:prev-tokens'
|
|
5
|
+
|
|
6
|
+
interface TokenRowProps {
|
|
7
|
+
name: string
|
|
8
|
+
value: string | number
|
|
9
|
+
preview?: React.ReactNode
|
|
10
|
+
category: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function TokenRow({ name, value, preview, category }: TokenRowProps) {
|
|
14
|
+
const [copied, setCopied] = useState(false)
|
|
15
|
+
|
|
16
|
+
const copyToken = () => {
|
|
17
|
+
const prompt = `Use the ${category} token "${name}" (value: ${value})`
|
|
18
|
+
navigator.clipboard.writeText(prompt)
|
|
19
|
+
setCopied(true)
|
|
20
|
+
setTimeout(() => setCopied(false), 2000)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
data-token={name}
|
|
26
|
+
style={{
|
|
27
|
+
display: 'flex',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
gap: '16px',
|
|
30
|
+
padding: '12px 16px',
|
|
31
|
+
borderBottom: '1px solid var(--fd-border)',
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
{/* Preview swatch */}
|
|
35
|
+
<div style={{ width: '48px', height: '48px', flexShrink: 0 }}>
|
|
36
|
+
{preview}
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
{/* Token info */}
|
|
40
|
+
<div style={{ flex: 1 }}>
|
|
41
|
+
<div style={{ fontWeight: 500, color: 'var(--fd-foreground)' }}>{name}</div>
|
|
42
|
+
<div data-value style={{ fontSize: '14px', color: 'var(--fd-muted-foreground)', fontFamily: 'monospace' }}>
|
|
43
|
+
{String(value)}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{/* Copy button */}
|
|
48
|
+
<button
|
|
49
|
+
data-copy
|
|
50
|
+
onClick={copyToken}
|
|
51
|
+
style={{
|
|
52
|
+
padding: '6px 12px',
|
|
53
|
+
fontSize: '12px',
|
|
54
|
+
border: '1px solid var(--fd-border)',
|
|
55
|
+
borderRadius: '4px',
|
|
56
|
+
background: copied ? 'var(--fd-accent)' : 'var(--fd-background)',
|
|
57
|
+
color: copied ? 'var(--fd-accent-foreground)' : 'var(--fd-foreground)',
|
|
58
|
+
cursor: 'pointer',
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
{copied ? 'Copied!' : 'Copy'}
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function ColorSwatch({ color }: { color: string }) {
|
|
68
|
+
return (
|
|
69
|
+
<div style={{
|
|
70
|
+
width: '48px',
|
|
71
|
+
height: '48px',
|
|
72
|
+
backgroundColor: color,
|
|
73
|
+
borderRadius: '6px',
|
|
74
|
+
border: '1px solid var(--fd-border)',
|
|
75
|
+
}} />
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function SpacingSwatch({ size }: { size: string }) {
|
|
80
|
+
return (
|
|
81
|
+
<div style={{
|
|
82
|
+
display: 'flex',
|
|
83
|
+
alignItems: 'center',
|
|
84
|
+
justifyContent: 'center',
|
|
85
|
+
width: '48px',
|
|
86
|
+
height: '48px',
|
|
87
|
+
}}>
|
|
88
|
+
<div style={{
|
|
89
|
+
width: size,
|
|
90
|
+
height: size,
|
|
91
|
+
backgroundColor: 'var(--fd-primary)',
|
|
92
|
+
borderRadius: '2px',
|
|
93
|
+
}} />
|
|
94
|
+
</div>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function RadiusSwatch({ radius }: { radius: string }) {
|
|
99
|
+
return (
|
|
100
|
+
<div style={{
|
|
101
|
+
width: '48px',
|
|
102
|
+
height: '48px',
|
|
103
|
+
backgroundColor: 'var(--fd-primary)',
|
|
104
|
+
borderRadius: radius,
|
|
105
|
+
}} />
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function ShadowSwatch({ shadow }: { shadow: string }) {
|
|
110
|
+
return (
|
|
111
|
+
<div style={{
|
|
112
|
+
width: '48px',
|
|
113
|
+
height: '48px',
|
|
114
|
+
backgroundColor: 'var(--fd-background)',
|
|
115
|
+
borderRadius: '6px',
|
|
116
|
+
boxShadow: shadow,
|
|
117
|
+
border: shadow === 'none' ? '1px solid var(--fd-border)' : 'none',
|
|
118
|
+
}} />
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface SectionProps {
|
|
123
|
+
title: string
|
|
124
|
+
children: React.ReactNode
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function Section({ title, children }: SectionProps) {
|
|
128
|
+
return (
|
|
129
|
+
<section data-section={title.toLowerCase()} style={{ marginBottom: '32px' }}>
|
|
130
|
+
<h2 style={{
|
|
131
|
+
fontSize: '20px',
|
|
132
|
+
fontWeight: 600,
|
|
133
|
+
color: 'var(--fd-foreground)',
|
|
134
|
+
marginBottom: '16px',
|
|
135
|
+
paddingBottom: '8px',
|
|
136
|
+
borderBottom: '2px solid var(--fd-border)',
|
|
137
|
+
}}>
|
|
138
|
+
{title}
|
|
139
|
+
</h2>
|
|
140
|
+
<div style={{
|
|
141
|
+
border: '1px solid var(--fd-border)',
|
|
142
|
+
borderRadius: '8px',
|
|
143
|
+
overflow: 'hidden',
|
|
144
|
+
}}>
|
|
145
|
+
{children}
|
|
146
|
+
</div>
|
|
147
|
+
</section>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function TokensPage() {
|
|
152
|
+
const [copyAllStatus, setCopyAllStatus] = useState<string>('Copy All as Prompt')
|
|
153
|
+
|
|
154
|
+
// Tokens are pre-resolved at build time via virtual:prev-tokens
|
|
155
|
+
|
|
156
|
+
const copyAllAsPrompt = () => {
|
|
157
|
+
const lines = [
|
|
158
|
+
'# Design Tokens',
|
|
159
|
+
'',
|
|
160
|
+
'## Colors',
|
|
161
|
+
...Object.entries(tokens.colors).map(([name, value]) => `- ${name}: ${value}`),
|
|
162
|
+
'',
|
|
163
|
+
'## Backgrounds',
|
|
164
|
+
...Object.entries(tokens.backgrounds).map(([name, value]) => `- ${name}: ${value}`),
|
|
165
|
+
'',
|
|
166
|
+
'## Spacing',
|
|
167
|
+
...Object.entries(tokens.spacing).map(([name, value]) => `- ${name}: ${value}`),
|
|
168
|
+
'',
|
|
169
|
+
'## Typography Sizes',
|
|
170
|
+
...Object.entries(tokens.typography.sizes).map(([name, value]) => `- ${name}: ${value}`),
|
|
171
|
+
'',
|
|
172
|
+
'## Typography Weights',
|
|
173
|
+
...Object.entries(tokens.typography.weights).map(([name, value]) => `- ${name}: ${value}`),
|
|
174
|
+
'',
|
|
175
|
+
'## Border Radius',
|
|
176
|
+
...Object.entries(tokens.radius).map(([name, value]) => `- ${name}: ${value}`),
|
|
177
|
+
'',
|
|
178
|
+
'## Shadows',
|
|
179
|
+
...Object.entries(tokens.shadows).map(([name, value]) => `- ${name}: ${value}`),
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
navigator.clipboard.writeText(lines.join('\n'))
|
|
183
|
+
setCopyAllStatus('Copied!')
|
|
184
|
+
setTimeout(() => setCopyAllStatus('Copy All as Prompt'), 2000)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<div style={{
|
|
189
|
+
maxWidth: '800px',
|
|
190
|
+
margin: '0 auto',
|
|
191
|
+
padding: '32px 24px',
|
|
192
|
+
}}>
|
|
193
|
+
{/* Header */}
|
|
194
|
+
<div style={{
|
|
195
|
+
display: 'flex',
|
|
196
|
+
justifyContent: 'space-between',
|
|
197
|
+
alignItems: 'center',
|
|
198
|
+
marginBottom: '32px',
|
|
199
|
+
}}>
|
|
200
|
+
<h1 style={{
|
|
201
|
+
fontSize: '28px',
|
|
202
|
+
fontWeight: 700,
|
|
203
|
+
color: 'var(--fd-foreground)',
|
|
204
|
+
margin: 0,
|
|
205
|
+
}}>
|
|
206
|
+
Design Tokens
|
|
207
|
+
</h1>
|
|
208
|
+
<button
|
|
209
|
+
data-copy-all
|
|
210
|
+
onClick={copyAllAsPrompt}
|
|
211
|
+
style={{
|
|
212
|
+
padding: '10px 20px',
|
|
213
|
+
fontSize: '14px',
|
|
214
|
+
fontWeight: 500,
|
|
215
|
+
border: '1px solid var(--fd-primary)',
|
|
216
|
+
borderRadius: '6px',
|
|
217
|
+
background: 'var(--fd-primary)',
|
|
218
|
+
color: 'var(--fd-primary-foreground)',
|
|
219
|
+
cursor: 'pointer',
|
|
220
|
+
}}
|
|
221
|
+
>
|
|
222
|
+
{copyAllStatus}
|
|
223
|
+
</button>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{/* Colors Section */}
|
|
227
|
+
<Section title="Colors">
|
|
228
|
+
{Object.entries(tokens.colors as Record<string, string>).map(([name, value]) => (
|
|
229
|
+
<TokenRow
|
|
230
|
+
key={name}
|
|
231
|
+
name={name}
|
|
232
|
+
value={value}
|
|
233
|
+
category="color"
|
|
234
|
+
preview={<ColorSwatch color={value} />}
|
|
235
|
+
/>
|
|
236
|
+
))}
|
|
237
|
+
</Section>
|
|
238
|
+
|
|
239
|
+
{/* Backgrounds Section */}
|
|
240
|
+
<Section title="Backgrounds">
|
|
241
|
+
{Object.entries(tokens.backgrounds as Record<string, string>).map(([name, value]) => (
|
|
242
|
+
<TokenRow
|
|
243
|
+
key={name}
|
|
244
|
+
name={name}
|
|
245
|
+
value={value}
|
|
246
|
+
category="background"
|
|
247
|
+
preview={<ColorSwatch color={value} />}
|
|
248
|
+
/>
|
|
249
|
+
))}
|
|
250
|
+
</Section>
|
|
251
|
+
|
|
252
|
+
{/* Spacing Section */}
|
|
253
|
+
<Section title="Spacing">
|
|
254
|
+
{Object.entries(tokens.spacing as Record<string, string>).map(([name, value]) => (
|
|
255
|
+
<TokenRow
|
|
256
|
+
key={name}
|
|
257
|
+
name={name}
|
|
258
|
+
value={value}
|
|
259
|
+
category="spacing"
|
|
260
|
+
preview={<SpacingSwatch size={value} />}
|
|
261
|
+
/>
|
|
262
|
+
))}
|
|
263
|
+
</Section>
|
|
264
|
+
|
|
265
|
+
{/* Typography Section */}
|
|
266
|
+
<Section title="Typography">
|
|
267
|
+
<div style={{ padding: '12px 16px', backgroundColor: 'var(--fd-muted)', fontSize: '12px', fontWeight: 500, color: 'var(--fd-muted-foreground)' }}>
|
|
268
|
+
Sizes
|
|
269
|
+
</div>
|
|
270
|
+
{Object.entries(tokens.typography.sizes as Record<string, string>).map(([name, value]) => (
|
|
271
|
+
<TokenRow
|
|
272
|
+
key={name}
|
|
273
|
+
name={name}
|
|
274
|
+
value={value}
|
|
275
|
+
category="typography size"
|
|
276
|
+
preview={
|
|
277
|
+
<div style={{ fontSize: value, lineHeight: 1, color: 'var(--fd-foreground)' }}>
|
|
278
|
+
Aa
|
|
279
|
+
</div>
|
|
280
|
+
}
|
|
281
|
+
/>
|
|
282
|
+
))}
|
|
283
|
+
<div style={{ padding: '12px 16px', backgroundColor: 'var(--fd-muted)', fontSize: '12px', fontWeight: 500, color: 'var(--fd-muted-foreground)' }}>
|
|
284
|
+
Weights
|
|
285
|
+
</div>
|
|
286
|
+
{Object.entries(tokens.typography.weights as Record<string, number>).map(([name, value]) => (
|
|
287
|
+
<TokenRow
|
|
288
|
+
key={name}
|
|
289
|
+
name={name}
|
|
290
|
+
value={value}
|
|
291
|
+
category="typography weight"
|
|
292
|
+
preview={
|
|
293
|
+
<div style={{ fontWeight: value, fontSize: '24px', color: 'var(--fd-foreground)' }}>
|
|
294
|
+
Aa
|
|
295
|
+
</div>
|
|
296
|
+
}
|
|
297
|
+
/>
|
|
298
|
+
))}
|
|
299
|
+
</Section>
|
|
300
|
+
|
|
301
|
+
{/* Radius Section */}
|
|
302
|
+
<Section title="Radius">
|
|
303
|
+
{Object.entries(tokens.radius as Record<string, string>).map(([name, value]) => (
|
|
304
|
+
<TokenRow
|
|
305
|
+
key={name}
|
|
306
|
+
name={name}
|
|
307
|
+
value={value}
|
|
308
|
+
category="radius"
|
|
309
|
+
preview={<RadiusSwatch radius={value} />}
|
|
310
|
+
/>
|
|
311
|
+
))}
|
|
312
|
+
</Section>
|
|
313
|
+
|
|
314
|
+
{/* Shadows Section */}
|
|
315
|
+
<Section title="Shadows">
|
|
316
|
+
{Object.entries(tokens.shadows as Record<string, string>).map(([name, value]) => (
|
|
317
|
+
<TokenRow
|
|
318
|
+
key={name}
|
|
319
|
+
name={name}
|
|
320
|
+
value={value}
|
|
321
|
+
category="shadow"
|
|
322
|
+
preview={<ShadowSwatch shadow={value} />}
|
|
323
|
+
/>
|
|
324
|
+
))}
|
|
325
|
+
</Section>
|
|
326
|
+
</div>
|
|
327
|
+
)
|
|
328
|
+
}
|