@vadenai/mcp-server 0.3.0 → 0.3.1
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/tools/components.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { componentMetadata } from "../guides/component-metadata.js";
|
|
2
2
|
import { generateComponentGuide } from "../guides/generate-guide.js";
|
|
3
|
+
import { resolveVisualProperties, } from "../utils/tailwind-resolver.js";
|
|
3
4
|
let cache = new WeakMap();
|
|
4
5
|
const CACHE_TTL_MS = 5 * 60 * 1000; // 5分
|
|
5
6
|
function getClientCache(client) {
|
|
@@ -307,8 +308,156 @@ function formatStyleSection(data) {
|
|
|
307
308
|
}
|
|
308
309
|
lines.push("");
|
|
309
310
|
}
|
|
311
|
+
// --- Resolved Styles ---
|
|
312
|
+
const resolvedStyleEntries = [];
|
|
313
|
+
if (type === "single") {
|
|
314
|
+
const baseStylesValue = data.baseStyles;
|
|
315
|
+
if (typeof baseStylesValue === "string" && baseStylesValue.trim()) {
|
|
316
|
+
resolvedStyleEntries.push({
|
|
317
|
+
label: "baseStyles",
|
|
318
|
+
props: resolveVisualProperties(baseStylesValue, resolvedColors ?? {}),
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else if (type === "multipart") {
|
|
323
|
+
const slotsObj = data.slots;
|
|
324
|
+
if (slotsObj && typeof slotsObj === "object") {
|
|
325
|
+
for (const [slotName, classes] of Object.entries(slotsObj)) {
|
|
326
|
+
if (typeof classes !== "string" || !classes.trim())
|
|
327
|
+
continue;
|
|
328
|
+
resolvedStyleEntries.push({
|
|
329
|
+
label: slotName,
|
|
330
|
+
props: resolveVisualProperties(classes, resolvedColors ?? {}),
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// --- Variant Resolved Styles ---
|
|
336
|
+
const resolvedVariantEntries = [];
|
|
337
|
+
const variants = data.variants;
|
|
338
|
+
if (variants && typeof variants === "object") {
|
|
339
|
+
for (const [groupName, groupVariants] of Object.entries(variants)) {
|
|
340
|
+
if (!groupVariants || typeof groupVariants !== "object")
|
|
341
|
+
continue;
|
|
342
|
+
for (const [variantName, classes] of Object.entries(groupVariants)) {
|
|
343
|
+
if (typeof classes !== "string" || !classes.trim())
|
|
344
|
+
continue;
|
|
345
|
+
resolvedVariantEntries.push({
|
|
346
|
+
group: groupName,
|
|
347
|
+
name: variantName,
|
|
348
|
+
props: resolveVisualProperties(classes, resolvedColors ?? {}),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (resolvedStyleEntries.length > 0 || resolvedVariantEntries.length > 0) {
|
|
354
|
+
lines.push("## Resolved Styles");
|
|
355
|
+
lines.push("");
|
|
356
|
+
lines.push("Visual properties resolved from Style Config classes and design tokens.");
|
|
357
|
+
lines.push("Use these values for accurate rendering in design tools (Figma, Framer, etc.).");
|
|
358
|
+
lines.push("");
|
|
359
|
+
for (const { label, props } of resolvedStyleEntries) {
|
|
360
|
+
const propLines = formatResolvedProps(props);
|
|
361
|
+
if (propLines.length > 0) {
|
|
362
|
+
lines.push(`**${label}**:`);
|
|
363
|
+
for (const line of propLines) {
|
|
364
|
+
lines.push(`- ${line}`);
|
|
365
|
+
}
|
|
366
|
+
lines.push("");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (resolvedVariantEntries.length > 0) {
|
|
370
|
+
lines.push("### Variants");
|
|
371
|
+
lines.push("");
|
|
372
|
+
let currentGroup = "";
|
|
373
|
+
for (const { group, name, props } of resolvedVariantEntries) {
|
|
374
|
+
const propLines = formatResolvedProps(props);
|
|
375
|
+
if (propLines.length === 0)
|
|
376
|
+
continue;
|
|
377
|
+
if (group !== currentGroup) {
|
|
378
|
+
lines.push(`**${group}**:`);
|
|
379
|
+
currentGroup = group;
|
|
380
|
+
}
|
|
381
|
+
lines.push(` *${name}*:`);
|
|
382
|
+
for (const line of propLines) {
|
|
383
|
+
lines.push(` - ${line}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
lines.push("");
|
|
387
|
+
}
|
|
388
|
+
}
|
|
310
389
|
return lines.join("\n");
|
|
311
390
|
}
|
|
391
|
+
function formatResolvedProps(props) {
|
|
392
|
+
const result = [];
|
|
393
|
+
if (props.display)
|
|
394
|
+
result.push(`display: ${props.display}`);
|
|
395
|
+
if (props.width)
|
|
396
|
+
result.push(`width: ${props.width}`);
|
|
397
|
+
if (props.height)
|
|
398
|
+
result.push(`height: ${props.height}`);
|
|
399
|
+
if (props.aspectRatio)
|
|
400
|
+
result.push(`aspect-ratio: ${props.aspectRatio}`);
|
|
401
|
+
if (props.overflow)
|
|
402
|
+
result.push(`overflow: ${props.overflow}`);
|
|
403
|
+
if (props.borderRadius)
|
|
404
|
+
result.push(`border-radius: ${props.borderRadius}`);
|
|
405
|
+
if (props.border) {
|
|
406
|
+
const style = props.borderStyle ?? (props.border.color ? "solid" : undefined);
|
|
407
|
+
const parts = [
|
|
408
|
+
props.border.width,
|
|
409
|
+
style,
|
|
410
|
+
props.border.color || undefined,
|
|
411
|
+
].filter(Boolean);
|
|
412
|
+
result.push(`border: ${parts.join(" ")}`);
|
|
413
|
+
}
|
|
414
|
+
if (props.backgroundColor)
|
|
415
|
+
result.push(`background-color: ${props.backgroundColor}`);
|
|
416
|
+
if (props.color)
|
|
417
|
+
result.push(`color: ${props.color}`);
|
|
418
|
+
if (props.fontWeight)
|
|
419
|
+
result.push(`font-weight: ${props.fontWeight}`);
|
|
420
|
+
if (props.fontSize)
|
|
421
|
+
result.push(`font-size: ${props.fontSize}`);
|
|
422
|
+
if (props.objectFit)
|
|
423
|
+
result.push(`object-fit: ${props.objectFit}`);
|
|
424
|
+
if (props.alignItems)
|
|
425
|
+
result.push(`align-items: ${props.alignItems}`);
|
|
426
|
+
if (props.justifyContent)
|
|
427
|
+
result.push(`justify-content: ${props.justifyContent}`);
|
|
428
|
+
if (props.opacity)
|
|
429
|
+
result.push(`opacity: ${props.opacity}`);
|
|
430
|
+
if (props.boxShadow)
|
|
431
|
+
result.push(`box-shadow: ${props.boxShadow}`);
|
|
432
|
+
if (props.padding)
|
|
433
|
+
result.push(`padding: ${props.padding}`);
|
|
434
|
+
if (props.paddingX)
|
|
435
|
+
result.push(`padding-inline: ${props.paddingX}`);
|
|
436
|
+
if (props.paddingY)
|
|
437
|
+
result.push(`padding-block: ${props.paddingY}`);
|
|
438
|
+
if (props.paddingTop)
|
|
439
|
+
result.push(`padding-top: ${props.paddingTop}`);
|
|
440
|
+
if (props.paddingBottom)
|
|
441
|
+
result.push(`padding-bottom: ${props.paddingBottom}`);
|
|
442
|
+
if (props.paddingLeft)
|
|
443
|
+
result.push(`padding-left: ${props.paddingLeft}`);
|
|
444
|
+
if (props.paddingRight)
|
|
445
|
+
result.push(`padding-right: ${props.paddingRight}`);
|
|
446
|
+
if (props.gap)
|
|
447
|
+
result.push(`gap: ${props.gap}`);
|
|
448
|
+
if (props.letterSpacing)
|
|
449
|
+
result.push(`letter-spacing: ${props.letterSpacing}`);
|
|
450
|
+
if (props.flexShrink)
|
|
451
|
+
result.push(`flex-shrink: ${props.flexShrink}`);
|
|
452
|
+
if (props.borderStyle && !props.border)
|
|
453
|
+
result.push(`border-style: ${props.borderStyle}`);
|
|
454
|
+
if (props.fontFamily)
|
|
455
|
+
result.push(`font-family: ${props.fontFamily}`);
|
|
456
|
+
if (props.gradient) {
|
|
457
|
+
result.push(`background: ${props.gradient}`);
|
|
458
|
+
}
|
|
459
|
+
return result;
|
|
460
|
+
}
|
|
312
461
|
/** テスト用にキャッシュをクリアする */
|
|
313
462
|
export function clearRegistryCache() {
|
|
314
463
|
cache = new WeakMap();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface ResolvedVisualProperties {
|
|
2
|
+
width?: string;
|
|
3
|
+
height?: string;
|
|
4
|
+
borderRadius?: string;
|
|
5
|
+
border?: {
|
|
6
|
+
width: string;
|
|
7
|
+
color: string;
|
|
8
|
+
};
|
|
9
|
+
backgroundColor?: string;
|
|
10
|
+
color?: string;
|
|
11
|
+
fontWeight?: string;
|
|
12
|
+
fontSize?: string;
|
|
13
|
+
overflow?: string;
|
|
14
|
+
objectFit?: string;
|
|
15
|
+
aspectRatio?: string;
|
|
16
|
+
display?: string;
|
|
17
|
+
alignItems?: string;
|
|
18
|
+
justifyContent?: string;
|
|
19
|
+
opacity?: string;
|
|
20
|
+
boxShadow?: string;
|
|
21
|
+
padding?: string;
|
|
22
|
+
paddingX?: string;
|
|
23
|
+
paddingY?: string;
|
|
24
|
+
paddingTop?: string;
|
|
25
|
+
paddingBottom?: string;
|
|
26
|
+
paddingLeft?: string;
|
|
27
|
+
paddingRight?: string;
|
|
28
|
+
gap?: string;
|
|
29
|
+
letterSpacing?: string;
|
|
30
|
+
flexShrink?: string;
|
|
31
|
+
borderStyle?: string;
|
|
32
|
+
fontFamily?: string;
|
|
33
|
+
gradient?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function resolveVisualProperties(classes: string, resolvedColors: Record<string, unknown>): ResolvedVisualProperties;
|
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
const SIZE_MAP = {
|
|
2
|
+
"0": "0px",
|
|
3
|
+
"0.5": "2px",
|
|
4
|
+
"1": "4px",
|
|
5
|
+
"1.5": "6px",
|
|
6
|
+
"2": "8px",
|
|
7
|
+
"2.5": "10px",
|
|
8
|
+
"3": "12px",
|
|
9
|
+
"3.5": "14px",
|
|
10
|
+
"4": "16px",
|
|
11
|
+
"5": "20px",
|
|
12
|
+
"6": "24px",
|
|
13
|
+
"7": "28px",
|
|
14
|
+
"8": "32px",
|
|
15
|
+
"9": "36px",
|
|
16
|
+
"10": "40px",
|
|
17
|
+
"11": "44px",
|
|
18
|
+
"12": "48px",
|
|
19
|
+
"14": "56px",
|
|
20
|
+
"16": "64px",
|
|
21
|
+
"20": "80px",
|
|
22
|
+
"24": "96px",
|
|
23
|
+
full: "100%",
|
|
24
|
+
};
|
|
25
|
+
const RADIUS_MAP = {
|
|
26
|
+
none: "0px",
|
|
27
|
+
sm: "0.125rem",
|
|
28
|
+
"": "0.25rem",
|
|
29
|
+
md: "0.375rem",
|
|
30
|
+
lg: "0.5rem",
|
|
31
|
+
xl: "0.75rem",
|
|
32
|
+
"2xl": "1rem",
|
|
33
|
+
"3xl": "1.5rem",
|
|
34
|
+
full: "9999px",
|
|
35
|
+
};
|
|
36
|
+
const FONT_WEIGHT_MAP = {
|
|
37
|
+
thin: "100",
|
|
38
|
+
extralight: "200",
|
|
39
|
+
light: "300",
|
|
40
|
+
normal: "400",
|
|
41
|
+
medium: "500",
|
|
42
|
+
semibold: "600",
|
|
43
|
+
bold: "700",
|
|
44
|
+
extrabold: "800",
|
|
45
|
+
black: "900",
|
|
46
|
+
};
|
|
47
|
+
const FONT_SIZE_MAP = {
|
|
48
|
+
xs: "0.75rem",
|
|
49
|
+
sm: "0.875rem",
|
|
50
|
+
base: "1rem",
|
|
51
|
+
lg: "1.125rem",
|
|
52
|
+
xl: "1.25rem",
|
|
53
|
+
"2xl": "1.5rem",
|
|
54
|
+
"3xl": "1.875rem",
|
|
55
|
+
};
|
|
56
|
+
const STATIC_COLORS = {
|
|
57
|
+
white: "#ffffff",
|
|
58
|
+
black: "#000000",
|
|
59
|
+
transparent: "transparent",
|
|
60
|
+
};
|
|
61
|
+
const TAILWIND_PALETTE = {
|
|
62
|
+
// Gray
|
|
63
|
+
"gray-50": "#f9fafb",
|
|
64
|
+
"gray-100": "#f3f4f6",
|
|
65
|
+
"gray-200": "#e5e7eb",
|
|
66
|
+
"gray-300": "#d1d5db",
|
|
67
|
+
"gray-400": "#9ca3af",
|
|
68
|
+
"gray-500": "#6b7280",
|
|
69
|
+
"gray-600": "#4b5563",
|
|
70
|
+
"gray-700": "#374151",
|
|
71
|
+
"gray-800": "#1f2937",
|
|
72
|
+
"gray-900": "#111827",
|
|
73
|
+
// Pink
|
|
74
|
+
"pink-50": "#fdf2f8",
|
|
75
|
+
"pink-100": "#fce7f3",
|
|
76
|
+
"pink-200": "#fbcfe8",
|
|
77
|
+
"pink-300": "#f9a8d4",
|
|
78
|
+
"pink-400": "#f472b6",
|
|
79
|
+
"pink-500": "#ec4899",
|
|
80
|
+
"pink-600": "#db2777",
|
|
81
|
+
"pink-700": "#be185d",
|
|
82
|
+
"pink-800": "#9d174d",
|
|
83
|
+
"pink-900": "#831843",
|
|
84
|
+
// Purple
|
|
85
|
+
"purple-400": "#a78bfa",
|
|
86
|
+
"purple-500": "#8b5cf6",
|
|
87
|
+
"purple-600": "#7c3aed",
|
|
88
|
+
"purple-900": "#4c1d95",
|
|
89
|
+
// Cyan
|
|
90
|
+
"cyan-400": "#22d3ee",
|
|
91
|
+
"cyan-500": "#06b6d4",
|
|
92
|
+
// Yellow
|
|
93
|
+
"yellow-200": "#fef08a",
|
|
94
|
+
"yellow-400": "#facc15",
|
|
95
|
+
// Red
|
|
96
|
+
"red-500": "#ef4444",
|
|
97
|
+
"red-600": "#dc2626",
|
|
98
|
+
// Green
|
|
99
|
+
"green-500": "#22c55e",
|
|
100
|
+
// Blue
|
|
101
|
+
"blue-500": "#3b82f6",
|
|
102
|
+
"blue-600": "#2563eb",
|
|
103
|
+
};
|
|
104
|
+
const SHADOW_MAP = {
|
|
105
|
+
sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
|
106
|
+
"": "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
|
|
107
|
+
md: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
|
|
108
|
+
lg: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
|
|
109
|
+
xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 10px 10px -5px rgb(0 0 0 / 0.1)",
|
|
110
|
+
"2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)",
|
|
111
|
+
none: "none",
|
|
112
|
+
};
|
|
113
|
+
const TRACKING_MAP = {
|
|
114
|
+
tighter: "-0.05em",
|
|
115
|
+
tight: "-0.025em",
|
|
116
|
+
normal: "0em",
|
|
117
|
+
wide: "0.025em",
|
|
118
|
+
wider: "0.05em",
|
|
119
|
+
widest: "0.1em",
|
|
120
|
+
};
|
|
121
|
+
const FONT_FAMILY_MAP = {
|
|
122
|
+
sans: "sans-serif",
|
|
123
|
+
serif: "serif",
|
|
124
|
+
mono: "monospace",
|
|
125
|
+
body: "var(--font-body)",
|
|
126
|
+
heading: "var(--font-heading)",
|
|
127
|
+
};
|
|
128
|
+
const GRADIENT_DIR_MAP = {
|
|
129
|
+
r: "right",
|
|
130
|
+
l: "left",
|
|
131
|
+
t: "top",
|
|
132
|
+
b: "bottom",
|
|
133
|
+
tr: "top right",
|
|
134
|
+
tl: "top left",
|
|
135
|
+
br: "bottom right",
|
|
136
|
+
bl: "bottom left",
|
|
137
|
+
};
|
|
138
|
+
const FONT_SIZE_KEYS = new Set(Object.keys(FONT_SIZE_MAP));
|
|
139
|
+
function normalizeHex(hex) {
|
|
140
|
+
const h = hex.replace("#", "");
|
|
141
|
+
if (h.length === 3) {
|
|
142
|
+
return `#${h[0]}${h[0]}${h[1]}${h[1]}${h[2]}${h[2]}`;
|
|
143
|
+
}
|
|
144
|
+
if (h.length === 4) {
|
|
145
|
+
const r = parseInt(`${h[0]}${h[0]}`, 16);
|
|
146
|
+
const g = parseInt(`${h[1]}${h[1]}`, 16);
|
|
147
|
+
const b = parseInt(`${h[2]}${h[2]}`, 16);
|
|
148
|
+
const a = parseInt(`${h[3]}${h[3]}`, 16) / 255;
|
|
149
|
+
return `rgba(${r},${g},${b},${Number(a.toFixed(3))})`;
|
|
150
|
+
}
|
|
151
|
+
if (h.length === 8) {
|
|
152
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
153
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
154
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
155
|
+
const a = parseInt(h.slice(6, 8), 16) / 255;
|
|
156
|
+
return `rgba(${r},${g},${b},${Number(a.toFixed(3))})`;
|
|
157
|
+
}
|
|
158
|
+
return `#${h.slice(0, 6)}`;
|
|
159
|
+
}
|
|
160
|
+
function parseArbitraryValue(val) {
|
|
161
|
+
const m = val.match(/^\[(.+)\]$/);
|
|
162
|
+
// Tailwind の arbitrary value では _ はスペースとして扱われる
|
|
163
|
+
// e.g. w-[calc(100%_-_1rem)] → calc(100% - 1rem)
|
|
164
|
+
return m ? m[1].replace(/_/g, " ") : null;
|
|
165
|
+
}
|
|
166
|
+
function resolveColorValue(value, resolvedColors) {
|
|
167
|
+
// 0. Arbitrary bracket color: [#hex], [rgba(...)], [hsl(...)], [oklch(...)]
|
|
168
|
+
const arbitrary = parseArbitraryValue(value);
|
|
169
|
+
if (arbitrary) {
|
|
170
|
+
// raw hex
|
|
171
|
+
if (/^#([0-9a-fA-F]{3,8})$/.test(arbitrary)) {
|
|
172
|
+
return normalizeHex(arbitrary);
|
|
173
|
+
}
|
|
174
|
+
// css function: rgba(...), hsl(...), oklch(...)
|
|
175
|
+
if (/^(rgba?|hsla?|oklch)\(.+\)$/i.test(arbitrary)) {
|
|
176
|
+
return arbitrary;
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
// 0.5. Raw hex without brackets (used in gradient stops like from-#hex)
|
|
181
|
+
if (/^#([0-9a-fA-F]{3,8})$/.test(value)) {
|
|
182
|
+
return normalizeHex(value);
|
|
183
|
+
}
|
|
184
|
+
// 1a. Bracket opacity: {name}/[0.05]
|
|
185
|
+
const bracketOpacityMatch = value.match(/^(.+)\/\[([0-9.]+)\]$/);
|
|
186
|
+
if (bracketOpacityMatch) {
|
|
187
|
+
const [, colorName, opacityStr] = bracketOpacityMatch;
|
|
188
|
+
const opacity = Number(opacityStr);
|
|
189
|
+
if (colorName === "white")
|
|
190
|
+
return `rgba(255,255,255,${opacity})`;
|
|
191
|
+
if (colorName === "black")
|
|
192
|
+
return `rgba(0,0,0,${opacity})`;
|
|
193
|
+
const baseColor = resolveColorValue(colorName, resolvedColors);
|
|
194
|
+
if (baseColor?.startsWith("#")) {
|
|
195
|
+
const r = parseInt(baseColor.slice(1, 3), 16);
|
|
196
|
+
const g = parseInt(baseColor.slice(3, 5), 16);
|
|
197
|
+
const b = parseInt(baseColor.slice(5, 7), 16);
|
|
198
|
+
return `rgba(${r},${g},${b},${opacity})`;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// 1b. Opacity-suffixed colors: white/10, black/50, primary/10
|
|
202
|
+
const opacityMatch = value.match(/^(.+)\/(\d+)$/);
|
|
203
|
+
if (opacityMatch) {
|
|
204
|
+
const [, colorName, opacityStr] = opacityMatch;
|
|
205
|
+
const opacity = Number(opacityStr) / 100;
|
|
206
|
+
if (colorName === "white")
|
|
207
|
+
return `rgba(255,255,255,${opacity})`;
|
|
208
|
+
if (colorName === "black")
|
|
209
|
+
return `rgba(0,0,0,${opacity})`;
|
|
210
|
+
// Try resolving semantic color
|
|
211
|
+
const baseColor = resolveColorValue(colorName, resolvedColors);
|
|
212
|
+
if (baseColor?.startsWith("#")) {
|
|
213
|
+
const r = parseInt(baseColor.slice(1, 3), 16);
|
|
214
|
+
const g = parseInt(baseColor.slice(3, 5), 16);
|
|
215
|
+
const b = parseInt(baseColor.slice(5, 7), 16);
|
|
216
|
+
return `rgba(${r},${g},${b},${opacity})`;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// 2. Static colors
|
|
220
|
+
if (value in STATIC_COLORS) {
|
|
221
|
+
return STATIC_COLORS[value];
|
|
222
|
+
}
|
|
223
|
+
// 2.5 Tailwind palette colors (e.g. gray-300, pink-400)
|
|
224
|
+
if (value in TAILWIND_PALETTE) {
|
|
225
|
+
return TAILWIND_PALETTE[value];
|
|
226
|
+
}
|
|
227
|
+
// 3. Semantic tokens from resolvedColors
|
|
228
|
+
// muted-foreground → resolvedColors.muted.foreground
|
|
229
|
+
// foreground → resolvedColors.base.foreground
|
|
230
|
+
// muted → resolvedColors.muted.background
|
|
231
|
+
// primary → resolvedColors.primary.background
|
|
232
|
+
// destructive → resolvedColors.destructive (if string)
|
|
233
|
+
// Handle compound tokens like "muted-foreground", "primary-foreground"
|
|
234
|
+
const dashIndex = value.indexOf("-");
|
|
235
|
+
if (dashIndex !== -1) {
|
|
236
|
+
const prefix = value.substring(0, dashIndex);
|
|
237
|
+
const suffix = value.substring(dashIndex + 1);
|
|
238
|
+
const group = resolvedColors[prefix];
|
|
239
|
+
if (group && typeof group === "object" && group !== null) {
|
|
240
|
+
const resolved = group[suffix];
|
|
241
|
+
if (typeof resolved === "string") {
|
|
242
|
+
return resolved;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
// Handle simple tokens
|
|
248
|
+
if (value === "foreground") {
|
|
249
|
+
const base = resolvedColors.base;
|
|
250
|
+
if (base && typeof base === "object" && base !== null) {
|
|
251
|
+
const fg = base.foreground;
|
|
252
|
+
if (typeof fg === "string")
|
|
253
|
+
return fg;
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
if (value === "background") {
|
|
258
|
+
const base = resolvedColors.base;
|
|
259
|
+
if (base && typeof base === "object" && base !== null) {
|
|
260
|
+
const bg = base.background;
|
|
261
|
+
if (typeof bg === "string")
|
|
262
|
+
return bg;
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
// For tokens like "muted", "primary" → look up .background
|
|
267
|
+
const group = resolvedColors[value];
|
|
268
|
+
if (typeof group === "string") {
|
|
269
|
+
return group;
|
|
270
|
+
}
|
|
271
|
+
if (group && typeof group === "object" && group !== null) {
|
|
272
|
+
const bg = group.background;
|
|
273
|
+
if (typeof bg === "string")
|
|
274
|
+
return bg;
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
export function resolveVisualProperties(classes, resolvedColors) {
|
|
279
|
+
const result = {};
|
|
280
|
+
const tokens = classes.split(/\s+/).filter(Boolean);
|
|
281
|
+
for (const token of tokens) {
|
|
282
|
+
// Height
|
|
283
|
+
if (token.startsWith("h-")) {
|
|
284
|
+
const val = token.slice(2);
|
|
285
|
+
if (val === "screen") {
|
|
286
|
+
result.height = "100vh";
|
|
287
|
+
}
|
|
288
|
+
else if (val in SIZE_MAP) {
|
|
289
|
+
result.height = SIZE_MAP[val];
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
const arb = parseArbitraryValue(val);
|
|
293
|
+
if (arb)
|
|
294
|
+
result.height = arb;
|
|
295
|
+
}
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
// Width
|
|
299
|
+
if (token.startsWith("w-")) {
|
|
300
|
+
const val = token.slice(2);
|
|
301
|
+
if (val === "screen") {
|
|
302
|
+
result.width = "100vw";
|
|
303
|
+
}
|
|
304
|
+
else if (val in SIZE_MAP) {
|
|
305
|
+
result.width = SIZE_MAP[val];
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
const arb = parseArbitraryValue(val);
|
|
309
|
+
if (arb)
|
|
310
|
+
result.width = arb;
|
|
311
|
+
}
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
// Padding (check px-/py-/pt-/pb-/pl-/pr- before p- to avoid false match)
|
|
315
|
+
if (token.startsWith("px-")) {
|
|
316
|
+
const val = token.slice(3);
|
|
317
|
+
if (val in SIZE_MAP)
|
|
318
|
+
result.paddingX = SIZE_MAP[val];
|
|
319
|
+
else {
|
|
320
|
+
const arb = parseArbitraryValue(val);
|
|
321
|
+
if (arb)
|
|
322
|
+
result.paddingX = arb;
|
|
323
|
+
}
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (token.startsWith("py-")) {
|
|
327
|
+
const val = token.slice(3);
|
|
328
|
+
if (val in SIZE_MAP)
|
|
329
|
+
result.paddingY = SIZE_MAP[val];
|
|
330
|
+
else {
|
|
331
|
+
const arb = parseArbitraryValue(val);
|
|
332
|
+
if (arb)
|
|
333
|
+
result.paddingY = arb;
|
|
334
|
+
}
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (token.startsWith("pt-")) {
|
|
338
|
+
const val = token.slice(3);
|
|
339
|
+
if (val in SIZE_MAP)
|
|
340
|
+
result.paddingTop = SIZE_MAP[val];
|
|
341
|
+
else {
|
|
342
|
+
const arb = parseArbitraryValue(val);
|
|
343
|
+
if (arb)
|
|
344
|
+
result.paddingTop = arb;
|
|
345
|
+
}
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
if (token.startsWith("pb-")) {
|
|
349
|
+
const val = token.slice(3);
|
|
350
|
+
if (val in SIZE_MAP)
|
|
351
|
+
result.paddingBottom = SIZE_MAP[val];
|
|
352
|
+
else {
|
|
353
|
+
const arb = parseArbitraryValue(val);
|
|
354
|
+
if (arb)
|
|
355
|
+
result.paddingBottom = arb;
|
|
356
|
+
}
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (token.startsWith("pl-")) {
|
|
360
|
+
const val = token.slice(3);
|
|
361
|
+
if (val in SIZE_MAP)
|
|
362
|
+
result.paddingLeft = SIZE_MAP[val];
|
|
363
|
+
else {
|
|
364
|
+
const arb = parseArbitraryValue(val);
|
|
365
|
+
if (arb)
|
|
366
|
+
result.paddingLeft = arb;
|
|
367
|
+
}
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (token.startsWith("pr-")) {
|
|
371
|
+
const val = token.slice(3);
|
|
372
|
+
if (val in SIZE_MAP)
|
|
373
|
+
result.paddingRight = SIZE_MAP[val];
|
|
374
|
+
else {
|
|
375
|
+
const arb = parseArbitraryValue(val);
|
|
376
|
+
if (arb)
|
|
377
|
+
result.paddingRight = arb;
|
|
378
|
+
}
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (token.startsWith("p-")) {
|
|
382
|
+
const val = token.slice(2);
|
|
383
|
+
if (val in SIZE_MAP)
|
|
384
|
+
result.padding = SIZE_MAP[val];
|
|
385
|
+
else {
|
|
386
|
+
const arb = parseArbitraryValue(val);
|
|
387
|
+
if (arb)
|
|
388
|
+
result.padding = arb;
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
// Gap
|
|
393
|
+
if (token.startsWith("gap-")) {
|
|
394
|
+
const val = token.slice(4);
|
|
395
|
+
if (val in SIZE_MAP)
|
|
396
|
+
result.gap = SIZE_MAP[val];
|
|
397
|
+
else {
|
|
398
|
+
const arb = parseArbitraryValue(val);
|
|
399
|
+
if (arb)
|
|
400
|
+
result.gap = arb;
|
|
401
|
+
}
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
// Letter-spacing (tracking)
|
|
405
|
+
if (token.startsWith("tracking-")) {
|
|
406
|
+
const val = token.slice(9);
|
|
407
|
+
if (val in TRACKING_MAP)
|
|
408
|
+
result.letterSpacing = TRACKING_MAP[val];
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
// Flex-shrink
|
|
412
|
+
if (token === "shrink-0") {
|
|
413
|
+
result.flexShrink = "0";
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (token === "shrink") {
|
|
417
|
+
result.flexShrink = "1";
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
// Border radius
|
|
421
|
+
if (token === "rounded") {
|
|
422
|
+
result.borderRadius = "0.25rem";
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (token.startsWith("rounded-")) {
|
|
426
|
+
const val = token.slice(8);
|
|
427
|
+
if (val in RADIUS_MAP) {
|
|
428
|
+
result.borderRadius = RADIUS_MAP[val];
|
|
429
|
+
}
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
// Display
|
|
433
|
+
if (token === "flex") {
|
|
434
|
+
result.display = "flex";
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (token === "inline-flex") {
|
|
438
|
+
result.display = "inline-flex";
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (token === "grid") {
|
|
442
|
+
result.display = "grid";
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
// Align items
|
|
446
|
+
if (token === "items-center") {
|
|
447
|
+
result.alignItems = "center";
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (token === "items-start") {
|
|
451
|
+
result.alignItems = "flex-start";
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (token === "items-end") {
|
|
455
|
+
result.alignItems = "flex-end";
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
// Justify content
|
|
459
|
+
if (token === "justify-center") {
|
|
460
|
+
result.justifyContent = "center";
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (token === "justify-between") {
|
|
464
|
+
result.justifyContent = "space-between";
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
// Overflow
|
|
468
|
+
if (token === "overflow-hidden") {
|
|
469
|
+
result.overflow = "hidden";
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
// Aspect ratio
|
|
473
|
+
if (token === "aspect-square") {
|
|
474
|
+
result.aspectRatio = "1/1";
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
if (token === "aspect-video") {
|
|
478
|
+
result.aspectRatio = "16/9";
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
// Object fit
|
|
482
|
+
if (token === "object-cover") {
|
|
483
|
+
result.objectFit = "cover";
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
if (token === "object-contain") {
|
|
487
|
+
result.objectFit = "contain";
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
// Font family / weight
|
|
491
|
+
if (token.startsWith("font-")) {
|
|
492
|
+
const val = token.slice(5);
|
|
493
|
+
if (val in FONT_FAMILY_MAP) {
|
|
494
|
+
result.fontFamily = FONT_FAMILY_MAP[val];
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (val in FONT_WEIGHT_MAP) {
|
|
498
|
+
result.fontWeight = FONT_WEIGHT_MAP[val];
|
|
499
|
+
}
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
// Opacity
|
|
503
|
+
if (token.startsWith("opacity-")) {
|
|
504
|
+
const val = token.slice(8);
|
|
505
|
+
const num = Number(val);
|
|
506
|
+
if (!Number.isNaN(num)) {
|
|
507
|
+
result.opacity = String(num / 100);
|
|
508
|
+
}
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
// Shadow: semantic values first, then arbitrary
|
|
512
|
+
if (token === "shadow") {
|
|
513
|
+
result.boxShadow = SHADOW_MAP[""];
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
if (token.startsWith("shadow-[")) {
|
|
517
|
+
const inner = token.slice(8, -1);
|
|
518
|
+
result.boxShadow = inner.replace(/_/g, " ");
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (token.startsWith("shadow-")) {
|
|
522
|
+
const val = token.slice(7);
|
|
523
|
+
if (val in SHADOW_MAP) {
|
|
524
|
+
result.boxShadow = SHADOW_MAP[val];
|
|
525
|
+
}
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
// Border: must check before bg-/text- color resolution
|
|
529
|
+
if (token === "border") {
|
|
530
|
+
result.border = {
|
|
531
|
+
width: "1px",
|
|
532
|
+
color: result.border?.color ?? "",
|
|
533
|
+
};
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
// border-{n} where n is a single digit
|
|
537
|
+
if (token.startsWith("border-")) {
|
|
538
|
+
const val = token.slice(7);
|
|
539
|
+
// border-style: dashed, dotted, solid (must check before color resolution)
|
|
540
|
+
if (val === "dashed" || val === "dotted" || val === "solid") {
|
|
541
|
+
result.borderStyle = val;
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
// border-[{n}px] arbitrary value
|
|
545
|
+
const arbitraryMatch = val.match(/^\[(\d+)px\]$/);
|
|
546
|
+
if (arbitraryMatch) {
|
|
547
|
+
result.border = {
|
|
548
|
+
width: `${arbitraryMatch[1]}px`,
|
|
549
|
+
color: result.border?.color ?? "",
|
|
550
|
+
};
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
// border-{n} numeric
|
|
554
|
+
if (/^\d+$/.test(val)) {
|
|
555
|
+
result.border = {
|
|
556
|
+
width: `${val}px`,
|
|
557
|
+
color: result.border?.color ?? "",
|
|
558
|
+
};
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
// border-{colorValue}
|
|
562
|
+
const color = resolveColorValue(val, resolvedColors);
|
|
563
|
+
if (color) {
|
|
564
|
+
result.border = {
|
|
565
|
+
width: result.border?.width ?? "1px",
|
|
566
|
+
color,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
// text-{size} MUST come before text-{color}
|
|
572
|
+
if (token.startsWith("text-")) {
|
|
573
|
+
const val = token.slice(5);
|
|
574
|
+
if (FONT_SIZE_KEYS.has(val)) {
|
|
575
|
+
result.fontSize = FONT_SIZE_MAP[val];
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
// text-{color}
|
|
579
|
+
const color = resolveColorValue(val, resolvedColors);
|
|
580
|
+
if (color) {
|
|
581
|
+
result.color = color;
|
|
582
|
+
}
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
// bg-gradient-to-{direction} (must check before bg-{color})
|
|
586
|
+
if (token.startsWith("bg-gradient-to-")) {
|
|
587
|
+
const dir = token.slice(15);
|
|
588
|
+
result.gradient = `linear-gradient(to ${GRADIENT_DIR_MAP[dir] ?? dir}`;
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
// bg-{color}
|
|
592
|
+
if (token.startsWith("bg-")) {
|
|
593
|
+
const val = token.slice(3);
|
|
594
|
+
const color = resolveColorValue(val, resolvedColors);
|
|
595
|
+
if (color) {
|
|
596
|
+
result.backgroundColor = color;
|
|
597
|
+
}
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
// from-{color} (gradient)
|
|
601
|
+
if (token.startsWith("from-")) {
|
|
602
|
+
const val = token.slice(5);
|
|
603
|
+
const color = resolveColorValue(val, resolvedColors);
|
|
604
|
+
if (color) {
|
|
605
|
+
result.gradient = `${result.gradient ?? "linear-gradient("}, ${color}`;
|
|
606
|
+
}
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
// via-{color} (gradient middle stop)
|
|
610
|
+
if (token.startsWith("via-")) {
|
|
611
|
+
const val = token.slice(4);
|
|
612
|
+
const color = resolveColorValue(val, resolvedColors);
|
|
613
|
+
if (color) {
|
|
614
|
+
result.gradient = `${result.gradient ?? "linear-gradient("}, ${color}`;
|
|
615
|
+
}
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
// to-{color} (gradient stop)
|
|
619
|
+
if (token.startsWith("to-")) {
|
|
620
|
+
const val = token.slice(3);
|
|
621
|
+
const color = resolveColorValue(val, resolvedColors);
|
|
622
|
+
if (color) {
|
|
623
|
+
result.gradient = `${result.gradient ?? "linear-gradient("}, ${color})`;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
// グラデーションが閉じ括弧なしで終わっている場合は追加
|
|
628
|
+
if (result.gradient && !result.gradient.endsWith(")")) {
|
|
629
|
+
result.gradient = `${result.gradient})`;
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
}
|