sonance-brand-mcp 1.3.19 → 1.3.21
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/assets/api/sonance-ai-edit/route.ts +485 -0
- package/dist/assets/api/sonance-component-source/route.ts +78 -0
- package/dist/assets/api/sonance-find-component/route.ts +174 -0
- package/dist/assets/api/sonance-save-colors/route.ts +181 -0
- package/dist/assets/api/sonance-vision-apply/route.ts +652 -0
- package/dist/assets/api/sonance-vision-edit/route.ts +656 -0
- package/dist/assets/components/checkbox.tsx +21 -12
- package/dist/assets/dev-tools/SonanceDevTools.tsx +120 -6
- package/dist/index.js +67 -0
- package/package.json +1 -1
|
@@ -11,6 +11,8 @@ interface CheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>
|
|
|
11
11
|
description?: string;
|
|
12
12
|
/** Visual state for Storybook/Figma documentation */
|
|
13
13
|
state?: CheckboxState;
|
|
14
|
+
/** Radix UI-style callback for checked state changes */
|
|
15
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
// State styles for Storybook/Figma visualization
|
|
@@ -28,17 +30,23 @@ const getStateStyles = (state?: CheckboxState) => {
|
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
31
|
-
({ className, label, description, id, state, disabled, checked, defaultChecked, onChange, style, ...props }, ref) => {
|
|
33
|
+
({ className, label, description, id, state, disabled, checked, defaultChecked, onChange, onCheckedChange, style, ...props }, ref) => {
|
|
32
34
|
const uniqueId = useId();
|
|
33
35
|
const inputId = id || `checkbox-${uniqueId}`;
|
|
34
36
|
const isDisabled = disabled || state === "disabled";
|
|
35
37
|
|
|
36
|
-
// Determine if we're in controlled mode
|
|
37
|
-
const isControlled = checked !== undefined
|
|
38
|
+
// Determine if we're in controlled mode (only when checked is explicitly provided)
|
|
39
|
+
const isControlled = checked !== undefined;
|
|
38
40
|
const isCheckedForState = state === "checked";
|
|
39
41
|
|
|
42
|
+
// Combined change handler that supports both native onChange and Radix-style onCheckedChange
|
|
43
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
44
|
+
onChange?.(e);
|
|
45
|
+
onCheckedChange?.(e.target.checked);
|
|
46
|
+
};
|
|
47
|
+
|
|
40
48
|
return (
|
|
41
|
-
<
|
|
49
|
+
<label data-sonance-name="checkbox" className="flex items-start gap-3 cursor-pointer">
|
|
42
50
|
<div className="relative flex items-center justify-center">
|
|
43
51
|
<input
|
|
44
52
|
type="checkbox"
|
|
@@ -46,10 +54,12 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
46
54
|
ref={ref}
|
|
47
55
|
disabled={isDisabled}
|
|
48
56
|
style={style}
|
|
57
|
+
onChange={handleChange}
|
|
49
58
|
{...(isControlled
|
|
50
|
-
? { checked: checked
|
|
51
|
-
: { defaultChecked: defaultChecked
|
|
59
|
+
? { checked: checked ?? isCheckedForState }
|
|
60
|
+
: { defaultChecked: defaultChecked ?? isCheckedForState }
|
|
52
61
|
)}
|
|
62
|
+
readOnly={isControlled && !onChange && !onCheckedChange}
|
|
53
63
|
className={cn(
|
|
54
64
|
"peer h-5 w-5 shrink-0 appearance-none border border-border bg-input",
|
|
55
65
|
"hover:border-border-hover",
|
|
@@ -59,7 +69,7 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
59
69
|
"transition-colors duration-150",
|
|
60
70
|
getStateStyles(state),
|
|
61
71
|
className
|
|
62
|
-
)}
|
|
72
|
+
)}
|
|
63
73
|
{...props}
|
|
64
74
|
/>
|
|
65
75
|
<Check
|
|
@@ -72,19 +82,18 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
72
82
|
{(label || description) && (
|
|
73
83
|
<div className="flex flex-col gap-0.5">
|
|
74
84
|
{label && (
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
className="text-sm font-medium text-foreground cursor-pointer select-none"
|
|
85
|
+
<span
|
|
86
|
+
className="text-sm font-medium text-foreground select-none"
|
|
78
87
|
>
|
|
79
88
|
{label}
|
|
80
|
-
</
|
|
89
|
+
</span>
|
|
81
90
|
)}
|
|
82
91
|
{description && (
|
|
83
92
|
<p id="p-description" className="text-xs text-foreground-muted">{description}</p>
|
|
84
93
|
)}
|
|
85
94
|
</div>
|
|
86
95
|
)}
|
|
87
|
-
</
|
|
96
|
+
</label>
|
|
88
97
|
);
|
|
89
98
|
}
|
|
90
99
|
);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { useState, useEffect, useCallback, useRef, useMemo } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
-
import { Palette, X, Copy, Check, RotateCcw, ChevronDown, Save, Loader2, AlertCircle, CheckCircle, Sun, Moon, Eye, EyeOff, Zap, Image as ImageIcon, Wand2, Scan, FileCode, Tag, Type, MousePointer, FormInput, Box, Search, Send, Sparkles, RefreshCw } from "lucide-react";
|
|
5
|
+
import { Palette, X, Copy, Check, RotateCcw, ChevronDown, Save, Loader2, AlertCircle, CheckCircle, Sun, Moon, Eye, EyeOff, Zap, Image as ImageIcon, Wand2, Scan, FileCode, Tag, Type, MousePointer, FormInput, Box, Search, Send, Sparkles, RefreshCw, GripHorizontal } from "lucide-react";
|
|
6
6
|
import { useTheme } from "next-themes";
|
|
7
7
|
import { cn } from "../../lib/utils";
|
|
8
8
|
import {
|
|
@@ -118,6 +118,14 @@ export function SonanceDevTools() {
|
|
|
118
118
|
// Track which elements were changed (for highlighting until accept/revert)
|
|
119
119
|
const [changedElements, setChangedElements] = useState<VisionFocusedElement[]>([]);
|
|
120
120
|
|
|
121
|
+
// Drag state for movable panel
|
|
122
|
+
const DEVTOOLS_POSITION_KEY = "sonance-devtools-pos";
|
|
123
|
+
const DEFAULT_POSITION = { x: 0, y: 0 }; // Offset from default bottom-right position
|
|
124
|
+
const [dragPosition, setDragPosition] = useState(DEFAULT_POSITION);
|
|
125
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
126
|
+
const dragOffsetRef = useRef({ x: 0, y: 0 });
|
|
127
|
+
const panelRef = useRef<HTMLDivElement>(null);
|
|
128
|
+
|
|
121
129
|
// Component-specific style overrides (for scalable, project-agnostic styling)
|
|
122
130
|
// Key: component type (e.g., "card", "button-primary", "card:variant123"), Value: style overrides
|
|
123
131
|
const [componentOverrides, setComponentOverrides] = useState<Record<string, ComponentStyle>>({});
|
|
@@ -236,6 +244,93 @@ export function SonanceDevTools() {
|
|
|
236
244
|
setMounted(true);
|
|
237
245
|
}, []);
|
|
238
246
|
|
|
247
|
+
// Load drag position from localStorage on mount
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
if (!mounted) return;
|
|
250
|
+
try {
|
|
251
|
+
const saved = localStorage.getItem(DEVTOOLS_POSITION_KEY);
|
|
252
|
+
if (saved) {
|
|
253
|
+
const parsed = JSON.parse(saved);
|
|
254
|
+
if (typeof parsed.x === "number" && typeof parsed.y === "number") {
|
|
255
|
+
setDragPosition(parsed);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
} catch {
|
|
259
|
+
// Ignore parse errors
|
|
260
|
+
}
|
|
261
|
+
}, [mounted, DEVTOOLS_POSITION_KEY]);
|
|
262
|
+
|
|
263
|
+
// Drag handlers for movable panel
|
|
264
|
+
const headerRef = useRef<HTMLDivElement>(null);
|
|
265
|
+
|
|
266
|
+
const handleDragStart = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
|
267
|
+
// Don't start drag if clicking on a button or interactive element
|
|
268
|
+
const target = e.target as HTMLElement;
|
|
269
|
+
if (target.closest('button')) return;
|
|
270
|
+
|
|
271
|
+
if (!panelRef.current || !headerRef.current) return;
|
|
272
|
+
|
|
273
|
+
// Prevent text selection during drag
|
|
274
|
+
e.preventDefault();
|
|
275
|
+
|
|
276
|
+
// Store the initial mouse position and current panel offset
|
|
277
|
+
dragOffsetRef.current = {
|
|
278
|
+
x: e.clientX - dragPosition.x,
|
|
279
|
+
y: e.clientY - dragPosition.y,
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
setIsDragging(true);
|
|
283
|
+
headerRef.current.setPointerCapture(e.pointerId);
|
|
284
|
+
}, [dragPosition]);
|
|
285
|
+
|
|
286
|
+
const handleDragMove = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
|
287
|
+
if (!isDragging || !panelRef.current) return;
|
|
288
|
+
|
|
289
|
+
const viewportWidth = window.innerWidth;
|
|
290
|
+
const viewportHeight = window.innerHeight;
|
|
291
|
+
const panelWidth = panelRef.current.offsetWidth;
|
|
292
|
+
const panelHeight = panelRef.current.offsetHeight;
|
|
293
|
+
|
|
294
|
+
// Calculate new position directly from mouse movement
|
|
295
|
+
const newX = e.clientX - dragOffsetRef.current.x;
|
|
296
|
+
const newY = e.clientY - dragOffsetRef.current.y;
|
|
297
|
+
|
|
298
|
+
// Clamp to keep panel within viewport (with 24px padding)
|
|
299
|
+
const padding = 24;
|
|
300
|
+
const maxX = viewportWidth - panelWidth - padding;
|
|
301
|
+
const maxY = viewportHeight - panelHeight - padding;
|
|
302
|
+
const minX = -(viewportWidth - panelWidth - padding);
|
|
303
|
+
const minY = -(viewportHeight - panelHeight - padding);
|
|
304
|
+
|
|
305
|
+
setDragPosition({
|
|
306
|
+
x: Math.max(minX, Math.min(maxX, newX)),
|
|
307
|
+
y: Math.max(minY, Math.min(maxY, newY)),
|
|
308
|
+
});
|
|
309
|
+
}, [isDragging]);
|
|
310
|
+
|
|
311
|
+
const handleDragEnd = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
|
|
312
|
+
if (!isDragging || !headerRef.current) return;
|
|
313
|
+
|
|
314
|
+
setIsDragging(false);
|
|
315
|
+
headerRef.current.releasePointerCapture(e.pointerId);
|
|
316
|
+
|
|
317
|
+
// Save position to localStorage
|
|
318
|
+
try {
|
|
319
|
+
localStorage.setItem(DEVTOOLS_POSITION_KEY, JSON.stringify(dragPosition));
|
|
320
|
+
} catch {
|
|
321
|
+
// Ignore storage errors
|
|
322
|
+
}
|
|
323
|
+
}, [isDragging, dragPosition, DEVTOOLS_POSITION_KEY]);
|
|
324
|
+
|
|
325
|
+
const handleResetPosition = useCallback(() => {
|
|
326
|
+
setDragPosition(DEFAULT_POSITION);
|
|
327
|
+
try {
|
|
328
|
+
localStorage.removeItem(DEVTOOLS_POSITION_KEY);
|
|
329
|
+
} catch {
|
|
330
|
+
// Ignore storage errors
|
|
331
|
+
}
|
|
332
|
+
}, [DEVTOOLS_POSITION_KEY]);
|
|
333
|
+
|
|
239
334
|
// Inject/update component-specific preview styles whenever overrides change
|
|
240
335
|
useEffect(() => {
|
|
241
336
|
if (!mounted) return;
|
|
@@ -2172,8 +2267,9 @@ export function SonanceDevTools() {
|
|
|
2172
2267
|
// Close and reset
|
|
2173
2268
|
const handleClose = useCallback(() => {
|
|
2174
2269
|
setIsOpen(false);
|
|
2175
|
-
// Disable inspector so user can interact with the app
|
|
2270
|
+
// Disable inspector and vision mode so user can interact with the app
|
|
2176
2271
|
setInspectorEnabled(false);
|
|
2272
|
+
setVisionModeActive(false);
|
|
2177
2273
|
resetThemeFromDOM();
|
|
2178
2274
|
// Reset color change tracking for next session
|
|
2179
2275
|
setColorsExplicitlyChanged(false);
|
|
@@ -2201,19 +2297,37 @@ export function SonanceDevTools() {
|
|
|
2201
2297
|
|
|
2202
2298
|
const panel = isOpen && (
|
|
2203
2299
|
<div
|
|
2300
|
+
ref={panelRef}
|
|
2204
2301
|
data-sonance-devtools="true"
|
|
2205
2302
|
className={cn(
|
|
2206
2303
|
"fixed bottom-6 right-6 z-[9999]",
|
|
2207
2304
|
"w-[360px] max-h-[80vh]",
|
|
2208
2305
|
"bg-white rounded-lg shadow-2xl border border-gray-200",
|
|
2209
2306
|
"flex flex-col overflow-hidden",
|
|
2210
|
-
"font-['Montserrat',sans-serif]"
|
|
2307
|
+
"font-['Montserrat',sans-serif]",
|
|
2308
|
+
isDragging && "select-none"
|
|
2211
2309
|
)}
|
|
2212
|
-
style={{
|
|
2310
|
+
style={{
|
|
2311
|
+
colorScheme: "light",
|
|
2312
|
+
transform: `translate(${dragPosition.x}px, ${dragPosition.y}px)`,
|
|
2313
|
+
}}
|
|
2213
2314
|
>
|
|
2214
|
-
{/* Header -
|
|
2215
|
-
<div
|
|
2315
|
+
{/* Header - Draggable */}
|
|
2316
|
+
<div
|
|
2317
|
+
ref={headerRef}
|
|
2318
|
+
className={cn(
|
|
2319
|
+
"flex items-center justify-between px-4 py-3 border-b border-gray-200 bg-[#333F48]",
|
|
2320
|
+
"cursor-move touch-none"
|
|
2321
|
+
)}
|
|
2322
|
+
onPointerDown={handleDragStart}
|
|
2323
|
+
onPointerMove={handleDragMove}
|
|
2324
|
+
onPointerUp={handleDragEnd}
|
|
2325
|
+
onPointerCancel={handleDragEnd}
|
|
2326
|
+
onDoubleClick={handleResetPosition}
|
|
2327
|
+
title="Drag to move • Double-click to reset position"
|
|
2328
|
+
>
|
|
2216
2329
|
<div className="flex items-center gap-2">
|
|
2330
|
+
<GripHorizontal className="h-4 w-4 text-white/50" />
|
|
2217
2331
|
<Palette className="h-5 w-5 text-[#00A3E1]" />
|
|
2218
2332
|
<span id="span-sonance-devtools" className="text-sm font-semibold text-white">
|
|
2219
2333
|
Sonance DevTools
|
package/dist/index.js
CHANGED
|
@@ -356,6 +356,12 @@ function runDevToolsInstaller() {
|
|
|
356
356
|
const apiAssetsDir = path.join(targetDir, baseDir, "app/api/sonance-assets");
|
|
357
357
|
const apiInjectIdDir = path.join(targetDir, baseDir, "app/api/sonance-inject-id");
|
|
358
358
|
const apiAnalyzeDir = path.join(targetDir, baseDir, "app/api/sonance-analyze");
|
|
359
|
+
const apiVisionApplyDir = path.join(targetDir, baseDir, "app/api/sonance-vision-apply");
|
|
360
|
+
const apiVisionEditDir = path.join(targetDir, baseDir, "app/api/sonance-vision-edit");
|
|
361
|
+
const apiAiEditDir = path.join(targetDir, baseDir, "app/api/sonance-ai-edit");
|
|
362
|
+
const apiSaveColorsDir = path.join(targetDir, baseDir, "app/api/sonance-save-colors");
|
|
363
|
+
const apiComponentSourceDir = path.join(targetDir, baseDir, "app/api/sonance-component-source");
|
|
364
|
+
const apiFindComponentDir = path.join(targetDir, baseDir, "app/api/sonance-find-component");
|
|
359
365
|
const themeDir = path.join(targetDir, baseDir, "theme");
|
|
360
366
|
// Source resolution
|
|
361
367
|
let sourceBrandSystem;
|
|
@@ -369,6 +375,12 @@ function runDevToolsInstaller() {
|
|
|
369
375
|
let sourceApiAssets;
|
|
370
376
|
let sourceApiInjectId;
|
|
371
377
|
let sourceApiAnalyze;
|
|
378
|
+
let sourceApiVisionApply;
|
|
379
|
+
let sourceApiVisionEdit;
|
|
380
|
+
let sourceApiAiEdit;
|
|
381
|
+
let sourceApiSaveColors;
|
|
382
|
+
let sourceApiComponentSource;
|
|
383
|
+
let sourceApiFindComponent;
|
|
372
384
|
if (IS_BUNDLED) {
|
|
373
385
|
sourceBrandSystem = path.join(BUNDLED_ASSETS, "brand-system.ts");
|
|
374
386
|
sourceBrandContext = path.join(BUNDLED_ASSETS, "brand-context.tsx");
|
|
@@ -381,6 +393,12 @@ function runDevToolsInstaller() {
|
|
|
381
393
|
sourceApiAssets = path.join(BUNDLED_ASSETS, "api/sonance-assets/route.ts");
|
|
382
394
|
sourceApiInjectId = path.join(BUNDLED_ASSETS, "api/sonance-inject-id/route.ts");
|
|
383
395
|
sourceApiAnalyze = path.join(BUNDLED_ASSETS, "api/sonance-analyze/route.ts");
|
|
396
|
+
sourceApiVisionApply = path.join(BUNDLED_ASSETS, "api/sonance-vision-apply/route.ts");
|
|
397
|
+
sourceApiVisionEdit = path.join(BUNDLED_ASSETS, "api/sonance-vision-edit/route.ts");
|
|
398
|
+
sourceApiAiEdit = path.join(BUNDLED_ASSETS, "api/sonance-ai-edit/route.ts");
|
|
399
|
+
sourceApiSaveColors = path.join(BUNDLED_ASSETS, "api/sonance-save-colors/route.ts");
|
|
400
|
+
sourceApiComponentSource = path.join(BUNDLED_ASSETS, "api/sonance-component-source/route.ts");
|
|
401
|
+
sourceApiFindComponent = path.join(BUNDLED_ASSETS, "api/sonance-find-component/route.ts");
|
|
384
402
|
}
|
|
385
403
|
else {
|
|
386
404
|
sourceBrandSystem = path.join(DEV_PROJECT_ROOT, "src/lib/brand-system.ts");
|
|
@@ -394,6 +412,12 @@ function runDevToolsInstaller() {
|
|
|
394
412
|
sourceApiAssets = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-assets/route.ts");
|
|
395
413
|
sourceApiInjectId = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-inject-id/route.ts");
|
|
396
414
|
sourceApiAnalyze = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-analyze/route.ts");
|
|
415
|
+
sourceApiVisionApply = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-vision-apply/route.ts");
|
|
416
|
+
sourceApiVisionEdit = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-vision-edit/route.ts");
|
|
417
|
+
sourceApiAiEdit = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-ai-edit/route.ts");
|
|
418
|
+
sourceApiSaveColors = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-save-colors/route.ts");
|
|
419
|
+
sourceApiComponentSource = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-component-source/route.ts");
|
|
420
|
+
sourceApiFindComponent = path.join(DEV_PROJECT_ROOT, "src/app/api/sonance-find-component/route.ts");
|
|
397
421
|
}
|
|
398
422
|
// Verify sources exist
|
|
399
423
|
if (!fs.existsSync(sourceBrandSystem)) {
|
|
@@ -530,6 +554,49 @@ function runDevToolsInstaller() {
|
|
|
530
554
|
fs.copyFileSync(sourceApiAnalyze, path.join(apiAnalyzeDir, "route.ts"));
|
|
531
555
|
createdFiles.push(`${pathPrefix}app/api/sonance-analyze/route.ts`);
|
|
532
556
|
console.log(` ✓ Created ${pathPrefix}app/api/sonance-analyze/route.ts`);
|
|
557
|
+
// 10. Install Vision and AI API routes
|
|
558
|
+
if (!fs.existsSync(apiVisionApplyDir)) {
|
|
559
|
+
fs.mkdirSync(apiVisionApplyDir, { recursive: true });
|
|
560
|
+
}
|
|
561
|
+
createdDirectories.push(`${pathPrefix}app/api/sonance-vision-apply`);
|
|
562
|
+
fs.copyFileSync(sourceApiVisionApply, path.join(apiVisionApplyDir, "route.ts"));
|
|
563
|
+
createdFiles.push(`${pathPrefix}app/api/sonance-vision-apply/route.ts`);
|
|
564
|
+
console.log(` ✓ Created ${pathPrefix}app/api/sonance-vision-apply/route.ts`);
|
|
565
|
+
if (!fs.existsSync(apiVisionEditDir)) {
|
|
566
|
+
fs.mkdirSync(apiVisionEditDir, { recursive: true });
|
|
567
|
+
}
|
|
568
|
+
createdDirectories.push(`${pathPrefix}app/api/sonance-vision-edit`);
|
|
569
|
+
fs.copyFileSync(sourceApiVisionEdit, path.join(apiVisionEditDir, "route.ts"));
|
|
570
|
+
createdFiles.push(`${pathPrefix}app/api/sonance-vision-edit/route.ts`);
|
|
571
|
+
console.log(` ✓ Created ${pathPrefix}app/api/sonance-vision-edit/route.ts`);
|
|
572
|
+
if (!fs.existsSync(apiAiEditDir)) {
|
|
573
|
+
fs.mkdirSync(apiAiEditDir, { recursive: true });
|
|
574
|
+
}
|
|
575
|
+
createdDirectories.push(`${pathPrefix}app/api/sonance-ai-edit`);
|
|
576
|
+
fs.copyFileSync(sourceApiAiEdit, path.join(apiAiEditDir, "route.ts"));
|
|
577
|
+
createdFiles.push(`${pathPrefix}app/api/sonance-ai-edit/route.ts`);
|
|
578
|
+
console.log(` ✓ Created ${pathPrefix}app/api/sonance-ai-edit/route.ts`);
|
|
579
|
+
if (!fs.existsSync(apiSaveColorsDir)) {
|
|
580
|
+
fs.mkdirSync(apiSaveColorsDir, { recursive: true });
|
|
581
|
+
}
|
|
582
|
+
createdDirectories.push(`${pathPrefix}app/api/sonance-save-colors`);
|
|
583
|
+
fs.copyFileSync(sourceApiSaveColors, path.join(apiSaveColorsDir, "route.ts"));
|
|
584
|
+
createdFiles.push(`${pathPrefix}app/api/sonance-save-colors/route.ts`);
|
|
585
|
+
console.log(` ✓ Created ${pathPrefix}app/api/sonance-save-colors/route.ts`);
|
|
586
|
+
if (!fs.existsSync(apiComponentSourceDir)) {
|
|
587
|
+
fs.mkdirSync(apiComponentSourceDir, { recursive: true });
|
|
588
|
+
}
|
|
589
|
+
createdDirectories.push(`${pathPrefix}app/api/sonance-component-source`);
|
|
590
|
+
fs.copyFileSync(sourceApiComponentSource, path.join(apiComponentSourceDir, "route.ts"));
|
|
591
|
+
createdFiles.push(`${pathPrefix}app/api/sonance-component-source/route.ts`);
|
|
592
|
+
console.log(` ✓ Created ${pathPrefix}app/api/sonance-component-source/route.ts`);
|
|
593
|
+
if (!fs.existsSync(apiFindComponentDir)) {
|
|
594
|
+
fs.mkdirSync(apiFindComponentDir, { recursive: true });
|
|
595
|
+
}
|
|
596
|
+
createdDirectories.push(`${pathPrefix}app/api/sonance-find-component`);
|
|
597
|
+
fs.copyFileSync(sourceApiFindComponent, path.join(apiFindComponentDir, "route.ts"));
|
|
598
|
+
createdFiles.push(`${pathPrefix}app/api/sonance-find-component/route.ts`);
|
|
599
|
+
console.log(` ✓ Created ${pathPrefix}app/api/sonance-find-component/route.ts`);
|
|
533
600
|
// 11. Install brand-overrides.css for production logo sizing
|
|
534
601
|
if (!fs.existsSync(stylesDir)) {
|
|
535
602
|
fs.mkdirSync(stylesDir, { recursive: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonance-brand-mcp",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.21",
|
|
4
4
|
"description": "MCP Server for Sonance Brand Guidelines and Component Library - gives Claude instant access to brand colors, typography, and UI components.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|