sonance-brand-mcp 1.3.111 → 1.3.113
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-save-image/route.ts +625 -0
- package/dist/assets/api/sonance-vision-apply/image-styling-detection.ts +1360 -0
- package/dist/assets/api/sonance-vision-apply/route.ts +988 -57
- package/dist/assets/api/sonance-vision-apply/styling-detection.ts +730 -0
- package/dist/assets/api/sonance-vision-apply/theme-discovery.ts +1 -1
- package/dist/assets/brand-system.ts +13 -12
- package/dist/assets/components/accordion.tsx +15 -7
- package/dist/assets/components/alert-dialog.tsx +35 -10
- package/dist/assets/components/alert.tsx +11 -10
- package/dist/assets/components/avatar.tsx +4 -4
- package/dist/assets/components/badge.tsx +16 -12
- package/dist/assets/components/button.stories.tsx +3 -3
- package/dist/assets/components/button.tsx +50 -31
- package/dist/assets/components/calendar.tsx +12 -8
- package/dist/assets/components/card.tsx +35 -29
- package/dist/assets/components/checkbox.tsx +9 -8
- package/dist/assets/components/code.tsx +19 -11
- package/dist/assets/components/command.tsx +32 -13
- package/dist/assets/components/context-menu.tsx +37 -16
- package/dist/assets/components/dialog.tsx +8 -5
- package/dist/assets/components/divider.tsx +15 -5
- package/dist/assets/components/drawer.tsx +4 -3
- package/dist/assets/components/dropdown-menu.tsx +15 -13
- package/dist/assets/components/hover-card.tsx +4 -1
- package/dist/assets/components/image.tsx +1 -1
- package/dist/assets/components/input.tsx +29 -14
- package/dist/assets/components/kbd.stories.tsx +3 -3
- package/dist/assets/components/kbd.tsx +29 -13
- package/dist/assets/components/listbox.tsx +8 -8
- package/dist/assets/components/menubar.tsx +50 -23
- package/dist/assets/components/navbar.stories.tsx +140 -13
- package/dist/assets/components/navbar.tsx +22 -5
- package/dist/assets/components/navigation-menu.tsx +28 -6
- package/dist/assets/components/pagination.tsx +10 -10
- package/dist/assets/components/popover.tsx +10 -8
- package/dist/assets/components/progress.tsx +6 -4
- package/dist/assets/components/radio-group.tsx +5 -5
- package/dist/assets/components/select.tsx +49 -29
- package/dist/assets/components/separator.tsx +3 -3
- package/dist/assets/components/sheet.tsx +4 -4
- package/dist/assets/components/sidebar.tsx +10 -10
- package/dist/assets/components/skeleton.tsx +13 -5
- package/dist/assets/components/slider.tsx +12 -10
- package/dist/assets/components/switch.tsx +4 -4
- package/dist/assets/components/table.tsx +5 -5
- package/dist/assets/components/tabs.tsx +8 -8
- package/dist/assets/components/textarea.tsx +11 -9
- package/dist/assets/components/toast.tsx +7 -7
- package/dist/assets/components/toggle.tsx +27 -7
- package/dist/assets/components/tooltip.tsx +10 -8
- package/dist/assets/components/user.tsx +8 -6
- package/dist/assets/dev-tools/SonanceDevTools.tsx +429 -362
- package/dist/assets/dev-tools/components/ApplyFirstPreview.tsx +10 -10
- package/dist/assets/dev-tools/components/ChatHistory.tsx +11 -7
- package/dist/assets/dev-tools/components/ChatInterface.tsx +61 -20
- package/dist/assets/dev-tools/components/ChatTabBar.tsx +1 -1
- package/dist/assets/dev-tools/components/DiffPreview.tsx +1 -1
- package/dist/assets/dev-tools/components/InlineDiffPreview.tsx +360 -36
- package/dist/assets/dev-tools/components/InspectorOverlay.tsx +9 -9
- package/dist/assets/dev-tools/components/PropertiesPanel.tsx +743 -93
- package/dist/assets/dev-tools/components/ScreenshotAnnotator.tsx +1 -1
- package/dist/assets/dev-tools/components/SectionHighlight.tsx +1 -1
- package/dist/assets/dev-tools/components/VisionDiffPreview.tsx +7 -7
- package/dist/assets/dev-tools/components/VisionModeBorder.tsx +4 -64
- package/dist/assets/dev-tools/hooks/index.ts +69 -0
- package/dist/assets/dev-tools/hooks/useComponentDetection.ts +132 -0
- package/dist/assets/dev-tools/hooks/useComputedStyles.ts +171 -65
- package/dist/assets/dev-tools/hooks/useContentHash.ts +212 -0
- package/dist/assets/dev-tools/hooks/useElementScanner.ts +398 -0
- package/dist/assets/dev-tools/hooks/useImageDetection.ts +162 -0
- package/dist/assets/dev-tools/hooks/useTextDetection.ts +217 -0
- package/dist/assets/dev-tools/panels/ComponentsPanel.tsx +160 -57
- package/dist/assets/dev-tools/panels/TextPanel.tsx +10 -10
- package/dist/assets/dev-tools/types.ts +42 -0
- package/dist/assets/globals.css +225 -9
- package/dist/assets/styles/brand-overrides.css +3 -2
- package/dist/assets/utils.ts +2 -1
- package/dist/index.js +32 -1
- package/package.json +1 -1
|
@@ -1,34 +1,206 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import React, { useState } from "react";
|
|
3
|
+
import React, { useState, useMemo } from "react";
|
|
4
4
|
import {
|
|
5
5
|
ChevronDown,
|
|
6
6
|
ChevronRight,
|
|
7
7
|
Check,
|
|
8
8
|
RotateCcw,
|
|
9
9
|
FileCode,
|
|
10
|
-
Clock,
|
|
11
10
|
CheckCircle,
|
|
12
11
|
XCircle,
|
|
12
|
+
Loader2,
|
|
13
|
+
AlertTriangle,
|
|
14
|
+
Info,
|
|
15
|
+
RefreshCw,
|
|
16
|
+
Plus,
|
|
17
|
+
Minus,
|
|
18
|
+
Code,
|
|
13
19
|
} from "lucide-react";
|
|
14
20
|
import { cn } from "../../../lib/utils";
|
|
15
|
-
import { ChatMessageAction,
|
|
21
|
+
import { ChatMessageAction, ApplyFirstStatus } from "../types";
|
|
16
22
|
|
|
17
23
|
export interface InlineDiffPreviewProps {
|
|
18
24
|
action: ChatMessageAction;
|
|
19
25
|
onAccept?: () => void;
|
|
20
26
|
onRevert?: () => void;
|
|
27
|
+
// Live status from parent (for HMR indicator, loading states)
|
|
28
|
+
liveStatus?: ApplyFirstStatus;
|
|
29
|
+
isProcessing?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Helper to refresh the page to see component changes
|
|
33
|
+
function handleRefreshPage() {
|
|
34
|
+
// Session is already persisted to localStorage, so it will survive the reload
|
|
35
|
+
window.location.reload();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extract meaningful changes from a diff string
|
|
40
|
+
* Instead of showing full lines, extract what was actually modified
|
|
41
|
+
*/
|
|
42
|
+
interface ChangeSummary {
|
|
43
|
+
type: 'added' | 'removed' | 'modified';
|
|
44
|
+
element?: string; // e.g., "Card", "Button"
|
|
45
|
+
change: string; // e.g., "rounded-none", "bg-blue-500"
|
|
46
|
+
fullLine?: string; // Original full line for tooltip
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function extractChangeSummary(diff: string): ChangeSummary[] {
|
|
50
|
+
const lines = diff.split('\n');
|
|
51
|
+
const changes: ChangeSummary[] = [];
|
|
52
|
+
|
|
53
|
+
// Group consecutive - and + lines for comparison
|
|
54
|
+
const removedLines: string[] = [];
|
|
55
|
+
const addedLines: string[] = [];
|
|
56
|
+
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
if (line.startsWith('-') && !line.startsWith('---')) {
|
|
59
|
+
removedLines.push(line.substring(1).trim());
|
|
60
|
+
} else if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
61
|
+
addedLines.push(line.substring(1).trim());
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// If we have both removed and added lines, find the differences
|
|
66
|
+
if (removedLines.length > 0 && addedLines.length > 0) {
|
|
67
|
+
for (let i = 0; i < Math.max(removedLines.length, addedLines.length); i++) {
|
|
68
|
+
const removed = removedLines[i] || '';
|
|
69
|
+
const added = addedLines[i] || '';
|
|
70
|
+
|
|
71
|
+
if (removed && added) {
|
|
72
|
+
// Find what changed between the lines
|
|
73
|
+
const diff = findLineDifference(removed, added);
|
|
74
|
+
if (diff) {
|
|
75
|
+
changes.push({
|
|
76
|
+
type: 'modified',
|
|
77
|
+
element: diff.element,
|
|
78
|
+
change: diff.change,
|
|
79
|
+
fullLine: added
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
} else if (added && !removed) {
|
|
83
|
+
changes.push({
|
|
84
|
+
type: 'added',
|
|
85
|
+
change: truncateChange(added),
|
|
86
|
+
fullLine: added
|
|
87
|
+
});
|
|
88
|
+
} else if (removed && !added) {
|
|
89
|
+
changes.push({
|
|
90
|
+
type: 'removed',
|
|
91
|
+
change: truncateChange(removed),
|
|
92
|
+
fullLine: removed
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} else if (addedLines.length > 0) {
|
|
97
|
+
// Only additions
|
|
98
|
+
for (const line of addedLines) {
|
|
99
|
+
changes.push({
|
|
100
|
+
type: 'added',
|
|
101
|
+
change: truncateChange(line),
|
|
102
|
+
fullLine: line
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
} else if (removedLines.length > 0) {
|
|
106
|
+
// Only removals
|
|
107
|
+
for (const line of removedLines) {
|
|
108
|
+
changes.push({
|
|
109
|
+
type: 'removed',
|
|
110
|
+
change: truncateChange(line),
|
|
111
|
+
fullLine: line
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return changes;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Find the actual difference between two similar lines
|
|
121
|
+
*/
|
|
122
|
+
function findLineDifference(removed: string, added: string): { element?: string; change: string } | null {
|
|
123
|
+
// Try to find className differences
|
|
124
|
+
const classNameRegex = /className=["']([^"']+)["']/;
|
|
125
|
+
const removedClass = removed.match(classNameRegex);
|
|
126
|
+
const addedClass = added.match(classNameRegex);
|
|
127
|
+
|
|
128
|
+
if (removedClass && addedClass) {
|
|
129
|
+
const removedClasses = new Set(removedClass[1].split(/\s+/));
|
|
130
|
+
const addedClasses = new Set(addedClass[1].split(/\s+/));
|
|
131
|
+
|
|
132
|
+
// Find added classes
|
|
133
|
+
const newClasses = [...addedClasses].filter(c => !removedClasses.has(c));
|
|
134
|
+
// Find removed classes
|
|
135
|
+
const deletedClasses = [...removedClasses].filter(c => !addedClasses.has(c));
|
|
136
|
+
|
|
137
|
+
if (newClasses.length > 0 || deletedClasses.length > 0) {
|
|
138
|
+
// Try to extract element name (e.g., <Card, <Button)
|
|
139
|
+
const elementMatch = added.match(/<(\w+)/);
|
|
140
|
+
const element = elementMatch ? elementMatch[1] : undefined;
|
|
141
|
+
|
|
142
|
+
const changes: string[] = [];
|
|
143
|
+
if (newClasses.length > 0) {
|
|
144
|
+
changes.push(`+${newClasses.join(' ')}`);
|
|
145
|
+
}
|
|
146
|
+
if (deletedClasses.length > 0) {
|
|
147
|
+
changes.push(`-${deletedClasses.join(' ')}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { element, change: changes.join(', ') };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Try to find text content differences
|
|
155
|
+
const textRegex = />([^<]+)</;
|
|
156
|
+
const removedText = removed.match(textRegex);
|
|
157
|
+
const addedText = added.match(textRegex);
|
|
158
|
+
|
|
159
|
+
if (removedText && addedText && removedText[1] !== addedText[1]) {
|
|
160
|
+
const elementMatch = added.match(/<(\w+)/);
|
|
161
|
+
return {
|
|
162
|
+
element: elementMatch ? elementMatch[1] : undefined,
|
|
163
|
+
change: `"${addedText[1].substring(0, 30)}${addedText[1].length > 30 ? '...' : ''}"`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Generic difference - show a summary
|
|
168
|
+
if (removed !== added) {
|
|
169
|
+
const elementMatch = added.match(/<(\w+)/);
|
|
170
|
+
return {
|
|
171
|
+
element: elementMatch ? elementMatch[1] : undefined,
|
|
172
|
+
change: 'modified'
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Truncate a change for display
|
|
181
|
+
*/
|
|
182
|
+
function truncateChange(line: string): string {
|
|
183
|
+
// Try to extract just the meaningful part
|
|
184
|
+
const elementMatch = line.match(/<(\w+)/);
|
|
185
|
+
if (elementMatch) {
|
|
186
|
+
return `<${elementMatch[1]}...>`;
|
|
187
|
+
}
|
|
188
|
+
return line.length > 40 ? line.substring(0, 40) + '...' : line;
|
|
21
189
|
}
|
|
22
190
|
|
|
23
191
|
export function InlineDiffPreview({
|
|
24
192
|
action,
|
|
25
193
|
onAccept,
|
|
26
194
|
onRevert,
|
|
195
|
+
liveStatus,
|
|
196
|
+
isProcessing = false,
|
|
27
197
|
}: InlineDiffPreviewProps) {
|
|
28
198
|
const [expandedFiles, setExpandedFiles] = useState<Set<string>>(
|
|
29
199
|
// Expand first file by default
|
|
30
200
|
new Set(action.files?.slice(0, 1).map((f) => f.path) || [])
|
|
31
201
|
);
|
|
202
|
+
// Track which files show full diff vs summary
|
|
203
|
+
const [showFullDiff, setShowFullDiff] = useState<Set<string>>(new Set());
|
|
32
204
|
|
|
33
205
|
const toggleFile = (path: string) => {
|
|
34
206
|
setExpandedFiles((prev) => {
|
|
@@ -42,10 +214,38 @@ export function InlineDiffPreview({
|
|
|
42
214
|
});
|
|
43
215
|
};
|
|
44
216
|
|
|
217
|
+
const toggleFullDiff = (path: string) => {
|
|
218
|
+
setShowFullDiff((prev) => {
|
|
219
|
+
const next = new Set(prev);
|
|
220
|
+
if (next.has(path)) {
|
|
221
|
+
next.delete(path);
|
|
222
|
+
} else {
|
|
223
|
+
next.add(path);
|
|
224
|
+
}
|
|
225
|
+
return next;
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Pre-compute change summaries for all files
|
|
230
|
+
const fileSummaries = useMemo(() => {
|
|
231
|
+
const summaries: Record<string, ChangeSummary[]> = {};
|
|
232
|
+
for (const file of action.files || []) {
|
|
233
|
+
if (file.diff) {
|
|
234
|
+
summaries[file.path] = extractChangeSummary(file.diff);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return summaries;
|
|
238
|
+
}, [action.files]);
|
|
239
|
+
|
|
240
|
+
// Derive loading state from liveStatus or isProcessing
|
|
241
|
+
const isLoading = isProcessing || liveStatus === "accepting" || liveStatus === "reverting";
|
|
242
|
+
const isWaitingHMR = liveStatus === "waiting-hmr";
|
|
243
|
+
const isLive = liveStatus === "reviewing" || action.status === "pending";
|
|
244
|
+
|
|
45
245
|
const getStatusColor = () => {
|
|
46
246
|
switch (action.status) {
|
|
47
247
|
case "pending":
|
|
48
|
-
return "border-
|
|
248
|
+
return "border-green-300 dark:border-green-600 bg-green-50 dark:bg-green-900/20";
|
|
49
249
|
case "accepted":
|
|
50
250
|
return "border-green-300 dark:border-green-600 bg-green-50 dark:bg-green-900/20";
|
|
51
251
|
case "reverted":
|
|
@@ -58,12 +258,22 @@ export function InlineDiffPreview({
|
|
|
58
258
|
};
|
|
59
259
|
|
|
60
260
|
const getStatusBadge = () => {
|
|
261
|
+
// Show HMR status first if applicable
|
|
262
|
+
if (isWaitingHMR) {
|
|
263
|
+
return (
|
|
264
|
+
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-[10px] font-medium bg-amber-100 dark:bg-amber-900/40 text-amber-700 dark:text-amber-400 rounded-full">
|
|
265
|
+
<Loader2 className="h-3 w-3 animate-spin" />
|
|
266
|
+
Refreshing...
|
|
267
|
+
</span>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
61
271
|
switch (action.status) {
|
|
62
272
|
case "pending":
|
|
63
273
|
return (
|
|
64
|
-
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-[10px] font-medium bg-
|
|
65
|
-
<
|
|
66
|
-
|
|
274
|
+
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-[10px] font-medium bg-green-100 dark:bg-green-900/40 text-green-700 dark:text-green-400 rounded-full">
|
|
275
|
+
<Check className="h-3 w-3" />
|
|
276
|
+
Changes Live
|
|
67
277
|
</span>
|
|
68
278
|
);
|
|
69
279
|
case "accepted":
|
|
@@ -114,7 +324,7 @@ export function InlineDiffPreview({
|
|
|
114
324
|
|
|
115
325
|
return (
|
|
116
326
|
<div key={i} className={cn("px-2 py-0.5", bgClass)}>
|
|
117
|
-
<span className={lineClass}>{line}</span>
|
|
327
|
+
<span id="inline-diff-preview-span-line" className={lineClass}>{line}</span>
|
|
118
328
|
</div>
|
|
119
329
|
);
|
|
120
330
|
})}
|
|
@@ -133,33 +343,34 @@ export function InlineDiffPreview({
|
|
|
133
343
|
</span>
|
|
134
344
|
{getStatusBadge()}
|
|
135
345
|
</div>
|
|
346
|
+
</div>
|
|
136
347
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
Revert
|
|
146
|
-
</button>
|
|
147
|
-
<button
|
|
148
|
-
onClick={onAccept}
|
|
149
|
-
className="inline-flex items-center gap-1 px-2.5 py-1 text-[11px] font-medium text-white bg-green-600 dark:bg-green-700 rounded-md hover:bg-green-700 dark:hover:bg-green-600 transition-colors"
|
|
150
|
-
>
|
|
151
|
-
<Check className="h-3 w-3" />
|
|
152
|
-
Accept
|
|
153
|
-
</button>
|
|
348
|
+
{/* Info Banner - Show when changes are live */}
|
|
349
|
+
{action.status === "pending" && (
|
|
350
|
+
<div className="flex items-start justify-between gap-2 px-3 py-2 bg-blue-50 dark:bg-blue-900/20 border-b border-inherit">
|
|
351
|
+
<div className="flex items-start gap-2">
|
|
352
|
+
<Info className="h-3.5 w-3.5 text-blue-600 dark:text-blue-400 mt-0.5 flex-shrink-0" />
|
|
353
|
+
<span className="text-[11px] text-blue-700 dark:text-blue-300">
|
|
354
|
+
<strong>Changes saved!</strong> If you don't see them, click Refresh.
|
|
355
|
+
</span>
|
|
154
356
|
</div>
|
|
155
|
-
|
|
156
|
-
|
|
357
|
+
<button
|
|
358
|
+
onClick={handleRefreshPage}
|
|
359
|
+
className="flex items-center gap-1 px-2 py-1 text-[10px] font-medium text-blue-700 dark:text-blue-300 bg-blue-100 dark:bg-blue-800/50 hover:bg-blue-200 dark:hover:bg-blue-700/50 rounded transition-colors flex-shrink-0"
|
|
360
|
+
>
|
|
361
|
+
<RefreshCw className="h-3 w-3" />
|
|
362
|
+
Refresh
|
|
363
|
+
</button>
|
|
364
|
+
</div>
|
|
365
|
+
)}
|
|
157
366
|
|
|
158
367
|
{/* File List */}
|
|
159
368
|
<div className="divide-y divide-inherit">
|
|
160
369
|
{action.files?.map((file) => {
|
|
161
370
|
const isExpanded = expandedFiles.has(file.path);
|
|
371
|
+
const isShowingFullDiff = showFullDiff.has(file.path);
|
|
162
372
|
const fileName = file.path.split("/").pop() || file.path;
|
|
373
|
+
const summary = fileSummaries[file.path] || [];
|
|
163
374
|
|
|
164
375
|
return (
|
|
165
376
|
<div key={file.path}>
|
|
@@ -176,15 +387,77 @@ export function InlineDiffPreview({
|
|
|
176
387
|
<span className="text-[11px] font-mono text-foreground truncate">
|
|
177
388
|
{fileName}
|
|
178
389
|
</span>
|
|
179
|
-
<span className="text-[10px] text-foreground-muted truncate">
|
|
390
|
+
<span className="text-[10px] text-foreground-muted truncate flex-1">
|
|
180
391
|
{file.path}
|
|
181
392
|
</span>
|
|
182
393
|
</button>
|
|
183
394
|
|
|
184
|
-
{/*
|
|
185
|
-
{isExpanded && file.diff && (
|
|
186
|
-
<div className="bg-background border-t border-inherit
|
|
187
|
-
{
|
|
395
|
+
{/* Change Summary (default view) */}
|
|
396
|
+
{isExpanded && file.diff && !isShowingFullDiff && (
|
|
397
|
+
<div className="bg-background border-t border-inherit px-3 py-2 space-y-1">
|
|
398
|
+
{summary.length > 0 ? (
|
|
399
|
+
<>
|
|
400
|
+
{summary.map((change, i) => (
|
|
401
|
+
<div
|
|
402
|
+
key={i}
|
|
403
|
+
className="flex items-center gap-2 text-[11px]"
|
|
404
|
+
title={change.fullLine}
|
|
405
|
+
>
|
|
406
|
+
{change.type === 'added' || change.type === 'modified' ? (
|
|
407
|
+
<Plus className="h-3 w-3 text-green-600 dark:text-green-400 flex-shrink-0" />
|
|
408
|
+
) : (
|
|
409
|
+
<Minus className="h-3 w-3 text-red-600 dark:text-red-400 flex-shrink-0" />
|
|
410
|
+
)}
|
|
411
|
+
{change.element && (
|
|
412
|
+
<span className="font-mono text-blue-600 dark:text-blue-400 flex-shrink-0">
|
|
413
|
+
{change.element}:
|
|
414
|
+
</span>
|
|
415
|
+
)}
|
|
416
|
+
<span className={cn(
|
|
417
|
+
"font-mono",
|
|
418
|
+
change.type === 'removed'
|
|
419
|
+
? "text-red-600 dark:text-red-400"
|
|
420
|
+
: "text-green-600 dark:text-green-400"
|
|
421
|
+
)}>
|
|
422
|
+
{change.change}
|
|
423
|
+
</span>
|
|
424
|
+
</div>
|
|
425
|
+
))}
|
|
426
|
+
{/* Toggle to show full diff */}
|
|
427
|
+
<button
|
|
428
|
+
onClick={(e) => {
|
|
429
|
+
e.stopPropagation();
|
|
430
|
+
toggleFullDiff(file.path);
|
|
431
|
+
}}
|
|
432
|
+
className="flex items-center gap-1 mt-2 text-[10px] text-foreground-muted hover:text-foreground transition-colors"
|
|
433
|
+
>
|
|
434
|
+
<Code className="h-3 w-3" />
|
|
435
|
+
Show full diff
|
|
436
|
+
</button>
|
|
437
|
+
</>
|
|
438
|
+
) : (
|
|
439
|
+
<span className="text-[11px] text-foreground-muted">No significant changes detected</span>
|
|
440
|
+
)}
|
|
441
|
+
</div>
|
|
442
|
+
)}
|
|
443
|
+
|
|
444
|
+
{/* Full Diff Content (toggle view) */}
|
|
445
|
+
{isExpanded && file.diff && isShowingFullDiff && (
|
|
446
|
+
<div className="bg-background border-t border-inherit">
|
|
447
|
+
<div className="overflow-x-auto">
|
|
448
|
+
{renderDiff(file.diff)}
|
|
449
|
+
</div>
|
|
450
|
+
{/* Toggle back to summary */}
|
|
451
|
+
<button
|
|
452
|
+
onClick={(e) => {
|
|
453
|
+
e.stopPropagation();
|
|
454
|
+
toggleFullDiff(file.path);
|
|
455
|
+
}}
|
|
456
|
+
className="flex items-center gap-1 px-3 py-2 text-[10px] text-foreground-muted hover:text-foreground transition-colors border-t border-inherit w-full"
|
|
457
|
+
>
|
|
458
|
+
<ChevronRight className="h-3 w-3" />
|
|
459
|
+
Show summary
|
|
460
|
+
</button>
|
|
188
461
|
</div>
|
|
189
462
|
)}
|
|
190
463
|
</div>
|
|
@@ -192,10 +465,61 @@ export function InlineDiffPreview({
|
|
|
192
465
|
})}
|
|
193
466
|
</div>
|
|
194
467
|
|
|
195
|
-
{/*
|
|
196
|
-
{action.
|
|
197
|
-
<div className="px-3 py-2 border-t border-inherit
|
|
198
|
-
<
|
|
468
|
+
{/* Warning about navigation - only show for pending */}
|
|
469
|
+
{action.status === "pending" && (
|
|
470
|
+
<div className="flex items-start gap-2 px-3 py-2 bg-amber-50 dark:bg-amber-900/20 border-t border-inherit">
|
|
471
|
+
<AlertTriangle className="h-3.5 w-3.5 text-amber-600 dark:text-amber-400 mt-0.5 flex-shrink-0" />
|
|
472
|
+
<span className="text-[11px] text-amber-700 dark:text-amber-300">
|
|
473
|
+
Navigating away will automatically revert changes. Make sure to accept or revert first.
|
|
474
|
+
</span>
|
|
475
|
+
</div>
|
|
476
|
+
)}
|
|
477
|
+
|
|
478
|
+
{/* Action Buttons - only show for pending */}
|
|
479
|
+
{action.status === "pending" && (
|
|
480
|
+
<div className="flex gap-2 px-3 py-2 border-t border-inherit bg-background/50">
|
|
481
|
+
<button
|
|
482
|
+
onClick={onAccept}
|
|
483
|
+
disabled={isLoading}
|
|
484
|
+
className={cn(
|
|
485
|
+
"flex-1 inline-flex items-center justify-center gap-1.5 px-3 py-2 text-[11px] font-medium rounded-md transition-colors",
|
|
486
|
+
"bg-green-600 dark:bg-green-700 text-white hover:bg-green-700 dark:hover:bg-green-600",
|
|
487
|
+
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
488
|
+
)}
|
|
489
|
+
>
|
|
490
|
+
{liveStatus === "accepting" ? (
|
|
491
|
+
<>
|
|
492
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
493
|
+
Accepting...
|
|
494
|
+
</>
|
|
495
|
+
) : (
|
|
496
|
+
<>
|
|
497
|
+
<Check className="h-3.5 w-3.5" />
|
|
498
|
+
Keep Changes
|
|
499
|
+
</>
|
|
500
|
+
)}
|
|
501
|
+
</button>
|
|
502
|
+
<button
|
|
503
|
+
onClick={onRevert}
|
|
504
|
+
disabled={isLoading}
|
|
505
|
+
className={cn(
|
|
506
|
+
"flex-1 inline-flex items-center justify-center gap-1.5 px-3 py-2 text-[11px] font-medium rounded-md transition-colors",
|
|
507
|
+
"bg-background border border-border text-foreground-secondary hover:bg-secondary",
|
|
508
|
+
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
509
|
+
)}
|
|
510
|
+
>
|
|
511
|
+
{liveStatus === "reverting" ? (
|
|
512
|
+
<>
|
|
513
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
514
|
+
Reverting...
|
|
515
|
+
</>
|
|
516
|
+
) : (
|
|
517
|
+
<>
|
|
518
|
+
<RotateCcw className="h-3.5 w-3.5" />
|
|
519
|
+
Revert
|
|
520
|
+
</>
|
|
521
|
+
)}
|
|
522
|
+
</button>
|
|
199
523
|
</div>
|
|
200
524
|
)}
|
|
201
525
|
</div>
|
|
@@ -312,26 +312,26 @@ export function InspectorOverlay({
|
|
|
312
312
|
{isChangedElement ? (
|
|
313
313
|
<>
|
|
314
314
|
<Check className="h-3 w-3" />
|
|
315
|
-
<span>Changed: {el.name}</span>
|
|
316
|
-
{el.variantId && <span className="ml-1 opacity-80 font-mono text-[9px]">#{el.variantId.substring(0, 4)}</span>}
|
|
315
|
+
<span id="span-changed-elname">Changed: {el.name}</span>
|
|
316
|
+
{el.variantId && <span id="span-elvariantidsubstring" className="ml-1 opacity-80 font-mono text-[9px]">#{el.variantId.substring(0, 4)}</span>}
|
|
317
317
|
</>
|
|
318
318
|
) : previewMode && isMatchingType ? (
|
|
319
319
|
<>
|
|
320
320
|
<Sparkles className="h-3 w-3" />
|
|
321
|
-
<span>Preview: {el.name}</span>
|
|
322
|
-
{el.variantId && <span className="ml-1 opacity-80 font-mono text-[9px]">#{el.variantId.substring(0, 4)}</span>}
|
|
321
|
+
<span id="span-preview-elname">Preview: {el.name}</span>
|
|
322
|
+
{el.variantId && <span id="span-elvariantidsubstring" className="ml-1 opacity-80 font-mono text-[9px]">#{el.variantId.substring(0, 4)}</span>}
|
|
323
323
|
</>
|
|
324
324
|
) : visionMode && isVisionFocused ? (
|
|
325
325
|
<>
|
|
326
326
|
<Eye className="h-3 w-3" />
|
|
327
|
-
<span>Focused: {el.name}</span>
|
|
328
|
-
{el.variantId && <span className="ml-1 opacity-80 font-mono text-[9px]">#{el.variantId.substring(0, 4)}</span>}
|
|
327
|
+
<span id="span-focused-elname">Focused: {el.name}</span>
|
|
328
|
+
{el.variantId && <span id="span-elvariantidsubstring" className="ml-1 opacity-80 font-mono text-[9px]">#{el.variantId.substring(0, 4)}</span>}
|
|
329
329
|
</>
|
|
330
330
|
) : visionMode ? (
|
|
331
331
|
<>
|
|
332
332
|
<Eye className="h-3 w-3 opacity-60" />
|
|
333
333
|
{el.name}
|
|
334
|
-
{el.variantId && <span className="ml-1 opacity-80 font-mono text-[9px] border-l border-white/30 pl-1">#{el.variantId.substring(0, 4)}</span>}
|
|
334
|
+
{el.variantId && <span id="span-elvariantidsubstring" className="ml-1 opacity-80 font-mono text-[9px] border-l border-white/30 pl-1">#{el.variantId.substring(0, 4)}</span>}
|
|
335
335
|
</>
|
|
336
336
|
) : (
|
|
337
337
|
<>
|
|
@@ -339,8 +339,8 @@ export function InspectorOverlay({
|
|
|
339
339
|
{el.type === "component" && <Box className="h-3 w-3" />}
|
|
340
340
|
{el.type === "text" && <Type className="h-3 w-3" />}
|
|
341
341
|
{el.name}
|
|
342
|
-
{el.variantId && <span className="ml-1 opacity-80 font-mono text-[9px] border-l border-white/30 pl-1">#{el.variantId.substring(0, 4)}</span>}
|
|
343
|
-
{isSelected && <span className="ml-1">✓</span>}
|
|
342
|
+
{el.variantId && <span id="span-elvariantidsubstring" className="ml-1 opacity-80 font-mono text-[9px] border-l border-white/30 pl-1">#{el.variantId.substring(0, 4)}</span>}
|
|
343
|
+
{isSelected && <span id="span-title" className="ml-1">✓</span>}
|
|
344
344
|
</>
|
|
345
345
|
)}
|
|
346
346
|
</div>
|