sonance-brand-mcp 1.3.110 → 1.3.112

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.
Files changed (84) hide show
  1. package/dist/assets/api/sonance-ai-edit/route.ts +30 -7
  2. package/dist/assets/api/sonance-save-image/route.ts +625 -0
  3. package/dist/assets/api/sonance-vision-apply/image-styling-detection.ts +1360 -0
  4. package/dist/assets/api/sonance-vision-apply/route.ts +1020 -64
  5. package/dist/assets/api/sonance-vision-apply/styling-detection.ts +730 -0
  6. package/dist/assets/api/sonance-vision-apply/theme-discovery.ts +1 -1
  7. package/dist/assets/api/sonance-vision-edit/route.ts +33 -8
  8. package/dist/assets/brand-system.ts +13 -12
  9. package/dist/assets/components/accordion.tsx +15 -7
  10. package/dist/assets/components/alert-dialog.tsx +35 -10
  11. package/dist/assets/components/alert.tsx +11 -10
  12. package/dist/assets/components/avatar.tsx +4 -4
  13. package/dist/assets/components/badge.tsx +16 -12
  14. package/dist/assets/components/button.stories.tsx +3 -3
  15. package/dist/assets/components/button.tsx +50 -31
  16. package/dist/assets/components/calendar.tsx +12 -8
  17. package/dist/assets/components/card.tsx +35 -29
  18. package/dist/assets/components/checkbox.tsx +9 -8
  19. package/dist/assets/components/code.tsx +19 -11
  20. package/dist/assets/components/command.tsx +32 -13
  21. package/dist/assets/components/context-menu.tsx +37 -16
  22. package/dist/assets/components/dialog.tsx +8 -5
  23. package/dist/assets/components/divider.tsx +15 -5
  24. package/dist/assets/components/drawer.tsx +4 -3
  25. package/dist/assets/components/dropdown-menu.tsx +15 -13
  26. package/dist/assets/components/hover-card.tsx +4 -1
  27. package/dist/assets/components/image.tsx +1 -1
  28. package/dist/assets/components/input.tsx +29 -14
  29. package/dist/assets/components/kbd.stories.tsx +3 -3
  30. package/dist/assets/components/kbd.tsx +29 -13
  31. package/dist/assets/components/listbox.tsx +8 -8
  32. package/dist/assets/components/menubar.tsx +50 -23
  33. package/dist/assets/components/navbar.stories.tsx +140 -13
  34. package/dist/assets/components/navbar.tsx +22 -5
  35. package/dist/assets/components/navigation-menu.tsx +28 -6
  36. package/dist/assets/components/pagination.tsx +10 -10
  37. package/dist/assets/components/popover.tsx +10 -8
  38. package/dist/assets/components/progress.tsx +6 -4
  39. package/dist/assets/components/radio-group.tsx +5 -5
  40. package/dist/assets/components/select.tsx +49 -29
  41. package/dist/assets/components/separator.tsx +3 -3
  42. package/dist/assets/components/sheet.tsx +4 -4
  43. package/dist/assets/components/sidebar.tsx +10 -10
  44. package/dist/assets/components/skeleton.tsx +13 -5
  45. package/dist/assets/components/slider.tsx +12 -10
  46. package/dist/assets/components/switch.tsx +4 -4
  47. package/dist/assets/components/table.tsx +5 -5
  48. package/dist/assets/components/tabs.tsx +8 -8
  49. package/dist/assets/components/textarea.tsx +11 -9
  50. package/dist/assets/components/toast.tsx +7 -7
  51. package/dist/assets/components/toggle.tsx +27 -7
  52. package/dist/assets/components/tooltip.tsx +10 -8
  53. package/dist/assets/components/user.tsx +8 -6
  54. package/dist/assets/dev-tools/SonanceDevTools.tsx +851 -708
  55. package/dist/assets/dev-tools/components/ApplyFirstPreview.tsx +10 -10
  56. package/dist/assets/dev-tools/components/ChatHistory.tsx +145 -0
  57. package/dist/assets/dev-tools/components/ChatInterface.tsx +444 -295
  58. package/dist/assets/dev-tools/components/ChatTabBar.tsx +82 -0
  59. package/dist/assets/dev-tools/components/DiffPreview.tsx +1 -1
  60. package/dist/assets/dev-tools/components/InlineDiffPreview.tsx +528 -0
  61. package/dist/assets/dev-tools/components/InspectorOverlay.tsx +21 -18
  62. package/dist/assets/dev-tools/components/PropertiesPanel.tsx +1345 -0
  63. package/dist/assets/dev-tools/components/ScreenshotAnnotator.tsx +1 -1
  64. package/dist/assets/dev-tools/components/SectionHighlight.tsx +1 -1
  65. package/dist/assets/dev-tools/components/VisionDiffPreview.tsx +7 -7
  66. package/dist/assets/dev-tools/components/VisionModeBorder.tsx +12 -63
  67. package/dist/assets/dev-tools/constants.ts +38 -6
  68. package/dist/assets/dev-tools/hooks/index.ts +69 -0
  69. package/dist/assets/dev-tools/hooks/useComponentDetection.ts +132 -0
  70. package/dist/assets/dev-tools/hooks/useComputedStyles.ts +471 -0
  71. package/dist/assets/dev-tools/hooks/useContentHash.ts +212 -0
  72. package/dist/assets/dev-tools/hooks/useElementScanner.ts +398 -0
  73. package/dist/assets/dev-tools/hooks/useImageDetection.ts +162 -0
  74. package/dist/assets/dev-tools/hooks/useTextDetection.ts +217 -0
  75. package/dist/assets/dev-tools/index.ts +3 -0
  76. package/dist/assets/dev-tools/panels/AnalysisPanel.tsx +32 -32
  77. package/dist/assets/dev-tools/panels/ComponentsPanel.tsx +384 -131
  78. package/dist/assets/dev-tools/panels/TextPanel.tsx +10 -10
  79. package/dist/assets/dev-tools/types.ts +93 -2
  80. package/dist/assets/globals.css +225 -9
  81. package/dist/assets/styles/brand-overrides.css +3 -2
  82. package/dist/assets/utils.ts +2 -1
  83. package/dist/index.js +22 -3
  84. package/package.json +2 -1
@@ -0,0 +1,217 @@
1
+ "use client";
2
+
3
+ import { useCallback, useRef } from "react";
4
+ import { DetectedElement, OriginalTextState } from "../types";
5
+ import { generateTextElementId, isLegacyId } from "./useContentHash";
6
+
7
+ /**
8
+ * Text detection configuration
9
+ */
10
+ export interface TextDetectionConfig {
11
+ /** Callback to check if element is in active layer */
12
+ isInActiveLayer: (el: Element) => boolean;
13
+ /** Current original states (for preserving existing state) */
14
+ existingOriginalStates?: Record<string, OriginalTextState>;
15
+ }
16
+
17
+ /**
18
+ * Text detection result
19
+ */
20
+ export interface TextDetectionResult {
21
+ elements: DetectedElement[];
22
+ originalStates: Record<string, OriginalTextState>;
23
+ }
24
+
25
+ /**
26
+ * Semantic text element selectors
27
+ * Only select meaningful text containers - exclude spans as they're usually nested
28
+ */
29
+ const TEXT_SELECTORS = "h1, h2, h3, h4, h5, h6, p, a, label, blockquote, figcaption, li";
30
+
31
+ /**
32
+ * Minimum dimensions to filter out decorative/icon elements
33
+ */
34
+ const MIN_WIDTH = 20;
35
+ const MIN_HEIGHT = 10;
36
+ const MIN_TEXT_LENGTH = 2;
37
+
38
+ /**
39
+ * Check if element has meaningful text content
40
+ */
41
+ function hasMeaningfulContent(element: Element): boolean {
42
+ const textContent = element.textContent?.trim() || "";
43
+
44
+ if (textContent.length < MIN_TEXT_LENGTH) return false;
45
+
46
+ // Skip elements that only contain whitespace/invisible chars
47
+ if (/^[\s\u200B-\u200D\uFEFF]+$/.test(textContent)) return false;
48
+
49
+ return true;
50
+ }
51
+
52
+ /**
53
+ * Check if element is nested inside another text element we'd track
54
+ */
55
+ function isNestedTextElement(element: Element): boolean {
56
+ const parentTextEl = element.parentElement?.closest(TEXT_SELECTORS);
57
+
58
+ if (!parentTextEl) return false;
59
+
60
+ // Skip if parent is part of DevTools
61
+ if (parentTextEl.closest("[data-sonance-devtools]")) return false;
62
+
63
+ // Allow links inside paragraphs to be selectable
64
+ const tagName = element.tagName.toLowerCase();
65
+ const parentTagName = parentTextEl.tagName.toLowerCase();
66
+
67
+ if (tagName === "a" && parentTagName === "p") return false;
68
+
69
+ return true;
70
+ }
71
+
72
+ /**
73
+ * Get display name for text element
74
+ */
75
+ function getDisplayName(element: Element, textContent: string): string {
76
+ const tagName = element.tagName.toLowerCase();
77
+
78
+ let displayName: string;
79
+ switch (tagName) {
80
+ case "a":
81
+ displayName = "Link";
82
+ break;
83
+ case "li":
84
+ displayName = "List Item";
85
+ break;
86
+ default:
87
+ displayName = tagName.toUpperCase();
88
+ }
89
+
90
+ const truncatedContent = textContent.length > 30
91
+ ? textContent.substring(0, 30) + "..."
92
+ : textContent;
93
+
94
+ return `${displayName}: ${truncatedContent}`;
95
+ }
96
+
97
+ /**
98
+ * Detect text elements on the page
99
+ * Filters noise and captures original state
100
+ */
101
+ export function detectTextElements(config: TextDetectionConfig): TextDetectionResult {
102
+ const { isInActiveLayer, existingOriginalStates = {} } = config;
103
+ const detected: DetectedElement[] = [];
104
+ const originalStates: Record<string, OriginalTextState> = { ...existingOriginalStates };
105
+
106
+ // Track added elements to avoid duplicates
107
+ const addedElements = new Set<Element>();
108
+
109
+ const textElements = document.querySelectorAll(TEXT_SELECTORS);
110
+ textElements.forEach((el) => {
111
+ // Skip elements outside active layer
112
+ if (!isInActiveLayer(el)) return;
113
+
114
+ // Skip nested text elements (avoid duplicates)
115
+ if (isNestedTextElement(el)) return;
116
+
117
+ // Skip already processed elements
118
+ if (addedElements.has(el)) return;
119
+
120
+ const rect = el.getBoundingClientRect();
121
+
122
+ // Filter out small/decorative elements
123
+ if (rect.width < MIN_WIDTH || rect.height < MIN_HEIGHT) return;
124
+
125
+ const textContent = el.textContent?.trim() || "";
126
+
127
+ // Skip elements without meaningful content
128
+ if (!hasMeaningfulContent(el)) return;
129
+
130
+ // Skip invisible elements
131
+ if (rect.width <= 0 || rect.height <= 0) return;
132
+
133
+ // Get or generate stable ID
134
+ let textId = el.getAttribute("data-sonance-text-id");
135
+
136
+ // Check if using legacy sequential ID and migrate if needed
137
+ if (textId && isLegacyId(textId)) {
138
+ const newId = generateTextElementId(el);
139
+ el.setAttribute("data-sonance-text-id", newId);
140
+
141
+ // Migrate original state if exists
142
+ if (originalStates[textId]) {
143
+ originalStates[newId] = originalStates[textId];
144
+ delete originalStates[textId];
145
+ }
146
+
147
+ textId = newId;
148
+ }
149
+
150
+ if (!textId) {
151
+ textId = generateTextElementId(el);
152
+ el.setAttribute("data-sonance-text-id", textId);
153
+ }
154
+
155
+ // Capture original state if not already captured
156
+ if (!originalStates[textId]) {
157
+ const computed = window.getComputedStyle(el);
158
+ originalStates[textId] = {
159
+ textContent: el.textContent,
160
+ fontSize: computed.fontSize,
161
+ fontWeight: computed.fontWeight,
162
+ lineHeight: computed.lineHeight,
163
+ letterSpacing: computed.letterSpacing,
164
+ color: computed.color,
165
+ fontFamily: computed.fontFamily,
166
+ };
167
+ }
168
+
169
+ const displayName = getDisplayName(el, textContent);
170
+
171
+ addedElements.add(el);
172
+ detected.push({
173
+ name: displayName,
174
+ rect,
175
+ type: "text",
176
+ textId,
177
+ textContent,
178
+ });
179
+ });
180
+
181
+ return { elements: detected, originalStates };
182
+ }
183
+
184
+ /**
185
+ * Hook for text detection
186
+ * Maintains original state across scans
187
+ */
188
+ export function useTextDetection() {
189
+ const originalStatesRef = useRef<Record<string, OriginalTextState>>({});
190
+
191
+ const detect = useCallback((config: Omit<TextDetectionConfig, "existingOriginalStates">): TextDetectionResult => {
192
+ const result = detectTextElements({
193
+ ...config,
194
+ existingOriginalStates: originalStatesRef.current,
195
+ });
196
+
197
+ // Update ref with new states
198
+ originalStatesRef.current = result.originalStates;
199
+
200
+ return result;
201
+ }, []);
202
+
203
+ const resetOriginalStates = useCallback(() => {
204
+ originalStatesRef.current = {};
205
+ }, []);
206
+
207
+ const getOriginalStates = useCallback(() => {
208
+ return originalStatesRef.current;
209
+ }, []);
210
+
211
+ return {
212
+ detectTextElements: detect,
213
+ resetOriginalStates,
214
+ getOriginalStates,
215
+ originalStatesRef,
216
+ };
217
+ }
@@ -21,6 +21,9 @@ export {
21
21
  export { Section, ColorSwatch, SelectField } from "./components/common";
22
22
  export { InspectorOverlay } from "./components/InspectorOverlay";
23
23
  export { ChatInterface } from "./components/ChatInterface";
24
+ export { ChatTabBar } from "./components/ChatTabBar";
25
+ export { ChatHistory } from "./components/ChatHistory";
26
+ export { InlineDiffPreview } from "./components/InlineDiffPreview";
24
27
  export { DiffPreview } from "./components/DiffPreview";
25
28
 
26
29
  // Panels
@@ -25,24 +25,24 @@ export function AnalysisPanel({
25
25
  onBulkTag,
26
26
  }: AnalysisPanelProps) {
27
27
  return (
28
- <div className="space-y-4">
28
+ <div className="space-y-3">
29
29
  {/* Initial State */}
30
30
  {analysisStatus === "idle" && !analysisResult && (
31
- <div className="text-center py-6 space-y-4">
32
- <div className="mx-auto w-14 h-14 rounded-full bg-gray-100 flex items-center justify-center">
33
- <Scan className="h-7 w-7 text-gray-400" />
31
+ <div className="text-center py-4 space-y-3">
32
+ <div className="mx-auto w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center">
33
+ <Scan className="h-5 w-5 text-gray-400" />
34
34
  </div>
35
35
  <div>
36
- <h3 id="analysis-panel-h3-analyze-your-project" className="text-base font-medium">Analyze Your Project</h3>
37
- <p id="analysis-panel-p-scan-your-codebase-t" className="text-xs text-gray-500 mt-1">
38
- Scan your codebase to index all images, logos, and theme files.
36
+ <h3 id="analysis-panel-h3-analyze-your-project" className="text-sm font-medium">Analyze Project</h3>
37
+ <p id="analysis-panel-p-scan-your-codebase-t" className="text-[11px] text-gray-500 mt-0.5">
38
+ Index images, logos, and theme files.
39
39
  </p>
40
40
  </div>
41
41
  <button
42
42
  onClick={onRunAnalysis}
43
- className="inline-flex items-center gap-2 px-4 py-2 bg-[#333F48] text-white text-sm font-medium rounded hover:bg-[#2a343c] transition-colors"
43
+ className="inline-flex items-center gap-1.5 px-3 py-1.5 bg-[#333F48] text-white text-xs font-medium rounded hover:bg-[#2a343c] transition-colors"
44
44
  >
45
- <Scan className="h-4 w-4" />
45
+ <Scan className="h-3.5 w-3.5" />
46
46
  Scan Project
47
47
  </button>
48
48
  </div>
@@ -50,14 +50,14 @@ export function AnalysisPanel({
50
50
 
51
51
  {/* Scanning State */}
52
52
  {analysisStatus === "scanning" && (
53
- <div className="text-center py-6 space-y-4">
54
- <div className="mx-auto w-14 h-14 rounded-full bg-blue-50 flex items-center justify-center">
55
- <Loader2 className="h-7 w-7 text-[#00A3E1] animate-spin" />
53
+ <div className="text-center py-4 space-y-3">
54
+ <div className="mx-auto w-10 h-10 rounded-full bg-blue-50 flex items-center justify-center">
55
+ <Loader2 className="h-5 w-5 text-[#00A3E1] animate-spin" />
56
56
  </div>
57
57
  <div>
58
- <h3 id="analysis-panel-h3-scanning" className="text-base font-medium">Scanning...</h3>
59
- <p id="analysis-panel-p-analyzing-your-sourc" className="text-xs text-gray-500 mt-1">
60
- Analyzing your source files for design assets.
58
+ <h3 id="analysis-panel-h3-scanning" className="text-sm font-medium">Scanning...</h3>
59
+ <p id="analysis-panel-p-analyzing-your-sourc" className="text-[11px] text-gray-500 mt-0.5">
60
+ Analyzing source files.
61
61
  </p>
62
62
  </div>
63
63
  </div>
@@ -65,19 +65,19 @@ export function AnalysisPanel({
65
65
 
66
66
  {/* Error State */}
67
67
  {analysisStatus === "error" && (
68
- <div className="text-center py-6 space-y-4">
69
- <div className="mx-auto w-14 h-14 rounded-full bg-red-50 flex items-center justify-center">
70
- <AlertCircle className="h-7 w-7 text-red-500" />
68
+ <div className="text-center py-4 space-y-3">
69
+ <div className="mx-auto w-10 h-10 rounded-full bg-red-50 flex items-center justify-center">
70
+ <AlertCircle className="h-5 w-5 text-red-500" />
71
71
  </div>
72
72
  <div>
73
- <h3 id="analysis-panel-h3-analysis-failed" className="text-base font-medium text-red-600">Analysis Failed</h3>
74
- <p id="analysis-panel-p-analysiserror" className="text-xs text-gray-500 mt-1">{analysisError}</p>
73
+ <h3 id="analysis-panel-h3-analysis-failed" className="text-sm font-medium text-red-600">Analysis Failed</h3>
74
+ <p id="analysis-panel-p-analysiserror" className="text-[11px] text-gray-500 mt-0.5">{analysisError}</p>
75
75
  </div>
76
76
  <button
77
77
  onClick={onRunAnalysis}
78
- className="inline-flex items-center gap-2 px-4 py-2 bg-[#333F48] text-white text-sm font-medium rounded hover:bg-[#2a343c] transition-colors"
78
+ className="inline-flex items-center gap-1.5 px-3 py-1.5 bg-[#333F48] text-white text-xs font-medium rounded hover:bg-[#2a343c] transition-colors"
79
79
  >
80
- <RotateCcw className="h-4 w-4" />
80
+ <RotateCcw className="h-3.5 w-3.5" />
81
81
  Try Again
82
82
  </button>
83
83
  </div>
@@ -87,18 +87,18 @@ export function AnalysisPanel({
87
87
  {analysisStatus === "complete" && analysisResult && (
88
88
  <>
89
89
  {/* Overview Stats */}
90
- <div className="grid grid-cols-3 gap-2">
91
- <div className="p-2 rounded border border-gray-200 bg-gray-50 text-center">
92
- <div className="text-[10px] text-gray-500 uppercase tracking-wide">Files</div>
93
- <div className="text-lg font-semibold">{analysisResult.filesScanned}</div>
90
+ <div className="grid grid-cols-3 gap-1.5">
91
+ <div className="p-1.5 rounded border border-gray-200 bg-gray-50 text-center">
92
+ <div className="text-[9px] text-gray-500 uppercase tracking-wide">Files</div>
93
+ <div className="text-base font-semibold">{analysisResult.filesScanned}</div>
94
94
  </div>
95
- <div className="p-2 rounded border border-green-200 bg-green-50 text-center">
96
- <div className="text-[10px] text-green-600 uppercase tracking-wide">With IDs</div>
97
- <div className="text-lg font-semibold text-green-700">{analysisResult.summary.elementsWithId}</div>
95
+ <div className="p-1.5 rounded border border-green-200 bg-green-50 text-center">
96
+ <div className="text-[9px] text-green-600 uppercase tracking-wide">With IDs</div>
97
+ <div className="text-base font-semibold text-green-700">{analysisResult.summary.elementsWithId}</div>
98
98
  </div>
99
- <div className="p-2 rounded border border-amber-200 bg-amber-50 text-center">
100
- <div className="text-[10px] text-amber-600 uppercase tracking-wide">Missing</div>
101
- <div className="text-lg font-semibold text-amber-700">{analysisResult.summary.elementsMissingId}</div>
99
+ <div className="p-1.5 rounded border border-amber-200 bg-amber-50 text-center">
100
+ <div className="text-[9px] text-amber-600 uppercase tracking-wide">Missing</div>
101
+ <div className="text-base font-semibold text-amber-700">{analysisResult.summary.elementsMissingId}</div>
102
102
  </div>
103
103
  </div>
104
104