sonance-brand-mcp 1.3.49 → 1.3.51
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-vision-apply/route.ts +62 -5
- package/dist/assets/api/sonance-vision-edit/route.ts +62 -5
- package/dist/assets/dev-tools/SonanceDevTools.tsx +18 -0
- package/dist/assets/dev-tools/components/ApplyFirstPreview.tsx +63 -31
- package/dist/assets/dev-tools/panels/ComponentsPanel.tsx +3 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import * as fs from "fs";
|
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import Anthropic from "@anthropic-ai/sdk";
|
|
5
5
|
import { randomUUID } from "crypto";
|
|
6
|
+
import { discoverTheme, formatThemeForPrompt } from "./theme-discovery";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Sonance DevTools API - Apply-First Vision Mode
|
|
@@ -471,10 +472,47 @@ Return search/replace patches (NOT full files). The system applies your patches
|
|
|
471
472
|
- CRITICAL: NEVER invent or guess code. Your "search" string MUST be copied EXACTLY from the provided file content. If you cannot find the exact code to modify, return an empty modifications array.
|
|
472
473
|
- If the file content appears truncated, only modify code that is visible in the provided content.
|
|
473
474
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
475
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
476
|
+
SONANCE BRAND COLOR SYSTEM
|
|
477
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
478
|
+
|
|
479
|
+
**Core Colors:**
|
|
480
|
+
- Primary (Charcoal): #333F48 - main text, dark backgrounds
|
|
481
|
+
- Accent (Cyan "The Beam"): #00D3C8 - highlights, interactive elements, CTAs
|
|
482
|
+
- Success: Green tones - positive states, confirmations
|
|
483
|
+
- Warning: Amber/Orange tones - caution states, alerts
|
|
484
|
+
- Destructive: Red tones - errors, delete actions
|
|
485
|
+
|
|
486
|
+
**CRITICAL CONTRAST RULES:**
|
|
487
|
+
When using colored backgrounds, ALWAYS use high-contrast text:
|
|
488
|
+
|
|
489
|
+
| Background | Use This Text | NEVER Use |
|
|
490
|
+
|-------------------|------------------------------------|-----------------------|
|
|
491
|
+
| bg-accent | text-white | text-accent-foreground |
|
|
492
|
+
| bg-primary | text-primary-foreground, text-white | text-primary |
|
|
493
|
+
| bg-success | text-white | text-success |
|
|
494
|
+
| bg-warning | text-white | text-warning |
|
|
495
|
+
| bg-destructive | text-white | text-destructive |
|
|
496
|
+
|
|
497
|
+
**SEMANTIC TOKEN PATTERNS:**
|
|
498
|
+
- text-{color} = the color itself (use on NEUTRAL backgrounds like white/gray)
|
|
499
|
+
- text-{color}-foreground = INTENDED for text on {color} backgrounds, but MAY have contrast issues
|
|
500
|
+
- bg-{color} = background in that color
|
|
501
|
+
- WHEN IN DOUBT: Use text-white on any colored background for guaranteed contrast
|
|
502
|
+
|
|
503
|
+
**BUTTON PATTERNS (Sonance Standard):**
|
|
504
|
+
- Primary CTA: bg-primary text-primary-foreground
|
|
505
|
+
- Accent/Highlight: bg-accent text-white (NOT text-accent-foreground)
|
|
506
|
+
- Success: bg-success text-white
|
|
507
|
+
- Warning: bg-warning text-white
|
|
508
|
+
- Destructive: bg-destructive text-white
|
|
509
|
+
- Outlined: border-border bg-transparent text-foreground
|
|
510
|
+
|
|
511
|
+
**COMMON MISTAKES TO AVOID:**
|
|
512
|
+
- WRONG: bg-accent text-accent-foreground (accent-foreground is often dark = invisible text)
|
|
513
|
+
- RIGHT: bg-accent text-white (white text on cyan = visible)
|
|
514
|
+
- WRONG: bg-primary text-primary (same color = invisible)
|
|
515
|
+
- RIGHT: bg-primary text-primary-foreground OR text-white
|
|
478
516
|
|
|
479
517
|
**RESPONSE FORMAT:**
|
|
480
518
|
CRITICAL: Return ONLY the JSON object below. Do NOT include any text, explanation, or thinking before or after the JSON. No preamble. No "Looking at the screenshot..." No markdown code blocks. Just raw JSON:
|
|
@@ -666,7 +704,7 @@ export async function POST(request: Request) {
|
|
|
666
704
|
const TOTAL_CONTEXT_BUDGET = 500000; // 500k chars total budget
|
|
667
705
|
const MAX_RECOMMENDED_FILE = Infinity; // NEVER truncate the target file - AI needs full context
|
|
668
706
|
const MAX_PAGE_FILE = 2000; // Page file is just a wrapper
|
|
669
|
-
const MAX_GLOBALS_CSS =
|
|
707
|
+
const MAX_GLOBALS_CSS = 15000; // Increased to capture full theme definitions
|
|
670
708
|
const MAX_FILES = 25;
|
|
671
709
|
|
|
672
710
|
let usedContext = 0;
|
|
@@ -760,6 +798,25 @@ ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
|
|
|
760
798
|
}
|
|
761
799
|
}
|
|
762
800
|
|
|
801
|
+
// ========== THEME DISCOVERY ==========
|
|
802
|
+
// Dynamically discover theme tokens from the target codebase
|
|
803
|
+
const discoveredTheme = await discoverTheme(projectRoot);
|
|
804
|
+
const themeContext = formatThemeForPrompt(discoveredTheme);
|
|
805
|
+
|
|
806
|
+
if (discoveredTheme.discoveredFiles.length > 0) {
|
|
807
|
+
textContent += `
|
|
808
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
809
|
+
${themeContext}
|
|
810
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
811
|
+
|
|
812
|
+
`;
|
|
813
|
+
debugLog("Theme discovery complete", {
|
|
814
|
+
filesFound: discoveredTheme.discoveredFiles,
|
|
815
|
+
cssVariableCount: Object.keys(discoveredTheme.cssVariables).length,
|
|
816
|
+
tailwindColorCount: Object.keys(discoveredTheme.tailwindColors).length,
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
|
|
763
820
|
// ========== GLOBALS CSS ==========
|
|
764
821
|
const globalsTruncated = pageContext.globalsCSS.substring(0, MAX_GLOBALS_CSS);
|
|
765
822
|
textContent += `
|
|
@@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
|
|
|
2
2
|
import * as fs from "fs";
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import Anthropic from "@anthropic-ai/sdk";
|
|
5
|
+
import { discoverTheme, formatThemeForPrompt } from "../sonance-vision-apply/theme-discovery";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Sonance DevTools API - Vision Mode Editor
|
|
@@ -469,10 +470,47 @@ Return search/replace patches (NOT full files). The system applies your patches
|
|
|
469
470
|
- CRITICAL: NEVER invent or guess code. Your "search" string MUST be copied EXACTLY from the provided file content. If you cannot find the exact code to modify, return an empty modifications array.
|
|
470
471
|
- If the file content appears truncated, only modify code that is visible in the provided content.
|
|
471
472
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
473
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
474
|
+
SONANCE BRAND COLOR SYSTEM
|
|
475
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
476
|
+
|
|
477
|
+
**Core Colors:**
|
|
478
|
+
- Primary (Charcoal): #333F48 - main text, dark backgrounds
|
|
479
|
+
- Accent (Cyan "The Beam"): #00D3C8 - highlights, interactive elements, CTAs
|
|
480
|
+
- Success: Green tones - positive states, confirmations
|
|
481
|
+
- Warning: Amber/Orange tones - caution states, alerts
|
|
482
|
+
- Destructive: Red tones - errors, delete actions
|
|
483
|
+
|
|
484
|
+
**CRITICAL CONTRAST RULES:**
|
|
485
|
+
When using colored backgrounds, ALWAYS use high-contrast text:
|
|
486
|
+
|
|
487
|
+
| Background | Use This Text | NEVER Use |
|
|
488
|
+
|-------------------|------------------------------------|-----------------------|
|
|
489
|
+
| bg-accent | text-white | text-accent-foreground |
|
|
490
|
+
| bg-primary | text-primary-foreground, text-white | text-primary |
|
|
491
|
+
| bg-success | text-white | text-success |
|
|
492
|
+
| bg-warning | text-white | text-warning |
|
|
493
|
+
| bg-destructive | text-white | text-destructive |
|
|
494
|
+
|
|
495
|
+
**SEMANTIC TOKEN PATTERNS:**
|
|
496
|
+
- text-{color} = the color itself (use on NEUTRAL backgrounds like white/gray)
|
|
497
|
+
- text-{color}-foreground = INTENDED for text on {color} backgrounds, but MAY have contrast issues
|
|
498
|
+
- bg-{color} = background in that color
|
|
499
|
+
- WHEN IN DOUBT: Use text-white on any colored background for guaranteed contrast
|
|
500
|
+
|
|
501
|
+
**BUTTON PATTERNS (Sonance Standard):**
|
|
502
|
+
- Primary CTA: bg-primary text-primary-foreground
|
|
503
|
+
- Accent/Highlight: bg-accent text-white (NOT text-accent-foreground)
|
|
504
|
+
- Success: bg-success text-white
|
|
505
|
+
- Warning: bg-warning text-white
|
|
506
|
+
- Destructive: bg-destructive text-white
|
|
507
|
+
- Outlined: border-border bg-transparent text-foreground
|
|
508
|
+
|
|
509
|
+
**COMMON MISTAKES TO AVOID:**
|
|
510
|
+
- WRONG: bg-accent text-accent-foreground (accent-foreground is often dark = invisible text)
|
|
511
|
+
- RIGHT: bg-accent text-white (white text on cyan = visible)
|
|
512
|
+
- WRONG: bg-primary text-primary (same color = invisible)
|
|
513
|
+
- RIGHT: bg-primary text-primary-foreground OR text-white
|
|
476
514
|
|
|
477
515
|
**RESPONSE FORMAT:**
|
|
478
516
|
CRITICAL: Return ONLY the JSON object below. Do NOT include any text, explanation, or thinking before or after the JSON. No preamble. No "Looking at the screenshot..." No markdown code blocks. Just raw JSON:
|
|
@@ -675,7 +713,7 @@ export async function POST(request: Request) {
|
|
|
675
713
|
const TOTAL_CONTEXT_BUDGET = 500000; // 500k chars total budget
|
|
676
714
|
const MAX_RECOMMENDED_FILE = Infinity; // NEVER truncate the target file - AI needs full context
|
|
677
715
|
const MAX_PAGE_FILE = 2000; // Page file is just a wrapper
|
|
678
|
-
const MAX_GLOBALS_CSS =
|
|
716
|
+
const MAX_GLOBALS_CSS = 15000; // Increased to capture full theme definitions
|
|
679
717
|
const MAX_FILES = 25;
|
|
680
718
|
|
|
681
719
|
let usedContext = 0;
|
|
@@ -769,6 +807,25 @@ ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
|
|
|
769
807
|
}
|
|
770
808
|
}
|
|
771
809
|
|
|
810
|
+
// ========== THEME DISCOVERY ==========
|
|
811
|
+
// Dynamically discover theme tokens from the target codebase
|
|
812
|
+
const discoveredTheme = await discoverTheme(projectRoot);
|
|
813
|
+
const themeContext = formatThemeForPrompt(discoveredTheme);
|
|
814
|
+
|
|
815
|
+
if (discoveredTheme.discoveredFiles.length > 0) {
|
|
816
|
+
textContent += `
|
|
817
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
818
|
+
${themeContext}
|
|
819
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
820
|
+
|
|
821
|
+
`;
|
|
822
|
+
debugLog("Theme discovery complete", {
|
|
823
|
+
filesFound: discoveredTheme.discoveredFiles,
|
|
824
|
+
cssVariableCount: Object.keys(discoveredTheme.cssVariables).length,
|
|
825
|
+
tailwindColorCount: Object.keys(discoveredTheme.tailwindColors).length,
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
|
|
772
829
|
// ========== GLOBALS CSS ==========
|
|
773
830
|
const globalsTruncated = pageContext.globalsCSS.substring(0, MAX_GLOBALS_CSS);
|
|
774
831
|
textContent += `
|
|
@@ -1228,6 +1228,23 @@ export function SonanceDevTools() {
|
|
|
1228
1228
|
}
|
|
1229
1229
|
}, [applyFirstSession]);
|
|
1230
1230
|
|
|
1231
|
+
// Force clear stale session - clears state without calling API
|
|
1232
|
+
const handleApplyFirstForceClear = useCallback(() => {
|
|
1233
|
+
console.log("[Apply-First] Force clearing stale session");
|
|
1234
|
+
setApplyFirstSession(null);
|
|
1235
|
+
setApplyFirstStatus("idle");
|
|
1236
|
+
setVisionFocusedElements([]);
|
|
1237
|
+
setChangedElements([]);
|
|
1238
|
+
|
|
1239
|
+
// Clear persisted session from localStorage
|
|
1240
|
+
try {
|
|
1241
|
+
localStorage.removeItem("sonance-apply-first-session");
|
|
1242
|
+
console.log("[Apply-First] Session force-cleared from localStorage");
|
|
1243
|
+
} catch (e) {
|
|
1244
|
+
console.warn("[Apply-First] Failed to clear session:", e);
|
|
1245
|
+
}
|
|
1246
|
+
}, []);
|
|
1247
|
+
|
|
1231
1248
|
// Auto-revert on navigation (beforeunload)
|
|
1232
1249
|
useEffect(() => {
|
|
1233
1250
|
if (!applyFirstSession) return;
|
|
@@ -2533,6 +2550,7 @@ export function SonanceDevTools() {
|
|
|
2533
2550
|
onApplyFirstComplete={handleApplyFirstComplete}
|
|
2534
2551
|
onApplyFirstAccept={handleApplyFirstAccept}
|
|
2535
2552
|
onApplyFirstRevert={handleApplyFirstRevert}
|
|
2553
|
+
onApplyFirstForceClear={handleApplyFirstForceClear}
|
|
2536
2554
|
/>
|
|
2537
2555
|
)}
|
|
2538
2556
|
|
|
@@ -10,6 +10,7 @@ export interface ApplyFirstPreviewProps {
|
|
|
10
10
|
status: ApplyFirstStatus;
|
|
11
11
|
onAccept: () => void;
|
|
12
12
|
onRevert: () => void;
|
|
13
|
+
onForceClear?: () => void; // Force clear stale sessions
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function FileModificationCard({
|
|
@@ -100,6 +101,7 @@ export function ApplyFirstPreview({
|
|
|
100
101
|
status,
|
|
101
102
|
onAccept,
|
|
102
103
|
onRevert,
|
|
104
|
+
onForceClear,
|
|
103
105
|
}: ApplyFirstPreviewProps) {
|
|
104
106
|
const [expandedFiles, setExpandedFiles] = useState<Set<string>>(new Set());
|
|
105
107
|
|
|
@@ -124,6 +126,7 @@ export function ApplyFirstPreview({
|
|
|
124
126
|
|
|
125
127
|
const fileCount = session.modifications.length;
|
|
126
128
|
const isLoading = status === "accepting" || status === "reverting";
|
|
129
|
+
const isStaleSession = fileCount === 0 || status === "error";
|
|
127
130
|
|
|
128
131
|
return (
|
|
129
132
|
<div className="space-y-3 p-3 rounded border border-green-300 bg-green-50">
|
|
@@ -141,14 +144,24 @@ export function ApplyFirstPreview({
|
|
|
141
144
|
<HMRStatusBadge status={status} />
|
|
142
145
|
</div>
|
|
143
146
|
|
|
144
|
-
{/* Info Banner */}
|
|
145
|
-
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
{/* Info Banner - show different message for stale sessions */}
|
|
148
|
+
{isStaleSession ? (
|
|
149
|
+
<div className="flex items-start gap-2 p-2 rounded bg-amber-50 border border-amber-200">
|
|
150
|
+
<AlertTriangle className="h-3.5 w-3.5 text-amber-600 mt-0.5 flex-shrink-0" />
|
|
151
|
+
<span className="text-xs text-amber-700">
|
|
152
|
+
<strong>Stale session detected.</strong> The backup files may no longer exist.
|
|
153
|
+
Use "Force Clear" to dismiss this panel.
|
|
154
|
+
</span>
|
|
155
|
+
</div>
|
|
156
|
+
) : (
|
|
157
|
+
<div className="flex items-start gap-2 p-2 rounded bg-blue-50 border border-blue-200">
|
|
158
|
+
<Info className="h-3.5 w-3.5 text-blue-600 mt-0.5 flex-shrink-0" />
|
|
159
|
+
<span className="text-xs text-blue-700">
|
|
160
|
+
<strong>Changes are live!</strong> Scroll around to see the actual result.
|
|
161
|
+
Your original files are safely backed up.
|
|
162
|
+
</span>
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
152
165
|
|
|
153
166
|
{/* File Modifications List */}
|
|
154
167
|
<div className="space-y-2 max-h-80 overflow-y-auto">
|
|
@@ -172,29 +185,31 @@ export function ApplyFirstPreview({
|
|
|
172
185
|
|
|
173
186
|
{/* Action Buttons */}
|
|
174
187
|
<div className="flex gap-2">
|
|
175
|
-
{/* Accept Button */}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
188
|
+
{/* Accept Button - hide for stale sessions */}
|
|
189
|
+
{!isStaleSession && (
|
|
190
|
+
<button
|
|
191
|
+
onClick={onAccept}
|
|
192
|
+
disabled={isLoading}
|
|
193
|
+
className={cn(
|
|
194
|
+
"flex-1 flex items-center justify-center gap-2 py-2.5",
|
|
195
|
+
"text-xs font-medium rounded transition-colors",
|
|
196
|
+
"bg-green-600 text-white hover:bg-green-700",
|
|
197
|
+
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
198
|
+
)}
|
|
199
|
+
>
|
|
200
|
+
{status === "accepting" ? (
|
|
201
|
+
<>
|
|
202
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
203
|
+
Accepting...
|
|
204
|
+
</>
|
|
205
|
+
) : (
|
|
206
|
+
<>
|
|
207
|
+
<Check className="h-3.5 w-3.5" />
|
|
208
|
+
Keep Changes
|
|
209
|
+
</>
|
|
210
|
+
)}
|
|
211
|
+
</button>
|
|
212
|
+
)}
|
|
198
213
|
|
|
199
214
|
{/* Revert Button */}
|
|
200
215
|
<button
|
|
@@ -219,6 +234,23 @@ export function ApplyFirstPreview({
|
|
|
219
234
|
</>
|
|
220
235
|
)}
|
|
221
236
|
</button>
|
|
237
|
+
|
|
238
|
+
{/* Force Clear Button - show for stale sessions or errors */}
|
|
239
|
+
{isStaleSession && onForceClear && (
|
|
240
|
+
<button
|
|
241
|
+
onClick={onForceClear}
|
|
242
|
+
disabled={isLoading}
|
|
243
|
+
className={cn(
|
|
244
|
+
"flex-1 flex items-center justify-center gap-2 py-2.5",
|
|
245
|
+
"text-xs font-medium rounded transition-colors",
|
|
246
|
+
"border border-red-300 text-red-700 bg-red-50 hover:bg-red-100",
|
|
247
|
+
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
248
|
+
)}
|
|
249
|
+
>
|
|
250
|
+
<X className="h-3.5 w-3.5" />
|
|
251
|
+
Force Clear
|
|
252
|
+
</button>
|
|
253
|
+
)}
|
|
222
254
|
</div>
|
|
223
255
|
|
|
224
256
|
{/* Session Info (for debugging) */}
|
|
@@ -56,6 +56,7 @@ export interface ComponentsPanelProps {
|
|
|
56
56
|
onApplyFirstComplete?: (session: ApplyFirstSession) => void;
|
|
57
57
|
onApplyFirstAccept?: () => void;
|
|
58
58
|
onApplyFirstRevert?: () => void;
|
|
59
|
+
onApplyFirstForceClear?: () => void;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
export function ComponentsPanel({
|
|
@@ -95,6 +96,7 @@ export function ComponentsPanel({
|
|
|
95
96
|
onApplyFirstComplete,
|
|
96
97
|
onApplyFirstAccept,
|
|
97
98
|
onApplyFirstRevert,
|
|
99
|
+
onApplyFirstForceClear,
|
|
98
100
|
}: ComponentsPanelProps) {
|
|
99
101
|
// Auto-activate inspector when entering this tab
|
|
100
102
|
useEffect(() => {
|
|
@@ -578,6 +580,7 @@ export function ComponentsPanel({
|
|
|
578
580
|
status={applyFirstStatus}
|
|
579
581
|
onAccept={onApplyFirstAccept}
|
|
580
582
|
onRevert={onApplyFirstRevert}
|
|
583
|
+
onForceClear={onApplyFirstForceClear}
|
|
581
584
|
/>
|
|
582
585
|
)}
|
|
583
586
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonance-brand-mcp",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.51",
|
|
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",
|