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
|
@@ -36,14 +36,14 @@ function FileModificationCard({
|
|
|
36
36
|
<ChevronRight className="h-3.5 w-3.5 text-gray-400 flex-shrink-0" />
|
|
37
37
|
)}
|
|
38
38
|
<FileCode className="h-3.5 w-3.5 text-green-500 flex-shrink-0" />
|
|
39
|
-
<span className="text-xs font-mono text-gray-700 truncate flex-1">
|
|
39
|
+
<span id="file-modification-card-span-modificationfilepath" className="text-xs font-mono text-gray-700 truncate flex-1">
|
|
40
40
|
{modification.filePath}
|
|
41
41
|
</span>
|
|
42
42
|
</button>
|
|
43
43
|
|
|
44
44
|
{/* Explanation */}
|
|
45
45
|
<div className="px-2 pb-2">
|
|
46
|
-
<p className="text-[10px] text-gray-500">{modification.explanation}</p>
|
|
46
|
+
<p id="file-modification-card-p-modificationexplanat" className="text-[10px] text-gray-500">{modification.explanation}</p>
|
|
47
47
|
</div>
|
|
48
48
|
|
|
49
49
|
{/* Expanded Diff */}
|
|
@@ -78,7 +78,7 @@ function FileModificationCard({
|
|
|
78
78
|
function HMRStatusBadge({ status }: { status: ApplyFirstStatus }) {
|
|
79
79
|
if (status === "waiting-hmr") {
|
|
80
80
|
return (
|
|
81
|
-
<span className="flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded bg-amber-100 text-amber-700">
|
|
81
|
+
<span id="h-m-r-status-badge-span" className="flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded bg-amber-100 text-amber-700">
|
|
82
82
|
<Loader2 className="h-3 w-3 animate-spin" />
|
|
83
83
|
Refreshing...
|
|
84
84
|
</span>
|
|
@@ -87,7 +87,7 @@ function HMRStatusBadge({ status }: { status: ApplyFirstStatus }) {
|
|
|
87
87
|
|
|
88
88
|
if (status === "reviewing") {
|
|
89
89
|
return (
|
|
90
|
-
<span className="flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded bg-green-100 text-green-700">
|
|
90
|
+
<span id="h-m-r-status-badge-span" className="flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded bg-green-100 text-green-700">
|
|
91
91
|
<Check className="h-3 w-3" />
|
|
92
92
|
Changes Live
|
|
93
93
|
</span>
|
|
@@ -142,10 +142,10 @@ export function ApplyFirstPreview({
|
|
|
142
142
|
<div className="flex items-center justify-between">
|
|
143
143
|
<div className="flex items-center gap-2">
|
|
144
144
|
<Zap className={cn("h-4 w-4", isPreviewMode ? "text-blue-600" : "text-green-600")} />
|
|
145
|
-
<span className="text-xs font-semibold text-gray-900">
|
|
145
|
+
<span id="apply-first-preview-span-ispreviewmode-propos" className="text-xs font-semibold text-gray-900">
|
|
146
146
|
{isPreviewMode ? "Proposed Changes" : "Changes Applied"}
|
|
147
147
|
</span>
|
|
148
|
-
<span className={cn(
|
|
148
|
+
<span id="apply-first-preview-span-filecount-filefileco" className={cn(
|
|
149
149
|
"text-[10px] px-1.5 py-0.5 rounded font-medium",
|
|
150
150
|
isPreviewMode
|
|
151
151
|
? "bg-blue-200 text-blue-700"
|
|
@@ -163,7 +163,7 @@ export function ApplyFirstPreview({
|
|
|
163
163
|
{isStaleSession ? (
|
|
164
164
|
<div className="flex items-start gap-2 p-2 rounded bg-amber-50 border border-amber-200">
|
|
165
165
|
<AlertTriangle className="h-3.5 w-3.5 text-amber-600 mt-0.5 flex-shrink-0" />
|
|
166
|
-
<span className="text-xs text-amber-700">
|
|
166
|
+
<span id="apply-first-preview-span" className="text-xs text-amber-700">
|
|
167
167
|
<strong>Stale session detected.</strong> The backup files may no longer exist.
|
|
168
168
|
Use "Force Clear" to dismiss this panel.
|
|
169
169
|
</span>
|
|
@@ -171,7 +171,7 @@ export function ApplyFirstPreview({
|
|
|
171
171
|
) : isPreviewMode ? (
|
|
172
172
|
<div className="flex items-start gap-2 p-2 rounded bg-blue-50 border border-blue-200">
|
|
173
173
|
<Info className="h-3.5 w-3.5 text-blue-600 mt-0.5 flex-shrink-0" />
|
|
174
|
-
<span className="text-xs text-blue-700">
|
|
174
|
+
<span id="apply-first-preview-span" className="text-xs text-blue-700">
|
|
175
175
|
<strong>Review the changes below.</strong> Click Accept to apply them to your files,
|
|
176
176
|
or Reject to discard.
|
|
177
177
|
</span>
|
|
@@ -179,7 +179,7 @@ export function ApplyFirstPreview({
|
|
|
179
179
|
) : (
|
|
180
180
|
<div className="flex items-start gap-2 p-2 rounded bg-blue-50 border border-blue-200">
|
|
181
181
|
<Info className="h-3.5 w-3.5 text-blue-600 mt-0.5 flex-shrink-0" />
|
|
182
|
-
<span className="text-xs text-blue-700">
|
|
182
|
+
<span id="apply-first-preview-span" className="text-xs text-blue-700">
|
|
183
183
|
<strong>Changes are live!</strong> Scroll around to see the actual result.
|
|
184
184
|
Your original files are safely backed up.
|
|
185
185
|
</span>
|
|
@@ -202,7 +202,7 @@ export function ApplyFirstPreview({
|
|
|
202
202
|
{!isPreviewMode && (
|
|
203
203
|
<div className="flex items-start gap-2 p-2 rounded bg-amber-50 border border-amber-200">
|
|
204
204
|
<AlertTriangle className="h-3.5 w-3.5 text-amber-600 mt-0.5 flex-shrink-0" />
|
|
205
|
-
<span className="text-xs text-amber-700">
|
|
205
|
+
<span id="apply-first-preview-span-navigating-away-will" className="text-xs text-amber-700">
|
|
206
206
|
Navigating away will automatically revert changes. Make sure to accept or revert first.
|
|
207
207
|
</span>
|
|
208
208
|
</div>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React, { useRef, useEffect } from "react";
|
|
4
4
|
import { User, Bot, AlertCircle, CheckCircle, RotateCcw } from "lucide-react";
|
|
5
5
|
import { cn } from "../../../lib/utils";
|
|
6
|
-
import { ChatMessage } from "../types";
|
|
6
|
+
import { ChatMessage, ApplyFirstStatus } from "../types";
|
|
7
7
|
import { InlineDiffPreview } from "./InlineDiffPreview";
|
|
8
8
|
|
|
9
9
|
export interface ChatHistoryProps {
|
|
@@ -11,6 +11,8 @@ export interface ChatHistoryProps {
|
|
|
11
11
|
onAcceptChanges?: (messageId: string) => void;
|
|
12
12
|
onRevertChanges?: (messageId: string) => void;
|
|
13
13
|
visionMode?: boolean;
|
|
14
|
+
// Live status for the most recent action
|
|
15
|
+
liveStatus?: ApplyFirstStatus;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export function ChatHistory({
|
|
@@ -18,6 +20,7 @@ export function ChatHistory({
|
|
|
18
20
|
onAcceptChanges,
|
|
19
21
|
onRevertChanges,
|
|
20
22
|
visionMode = false,
|
|
23
|
+
liveStatus,
|
|
21
24
|
}: ChatHistoryProps) {
|
|
22
25
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
23
26
|
const bottomRef = useRef<HTMLDivElement>(null);
|
|
@@ -74,10 +77,10 @@ export function ChatHistory({
|
|
|
74
77
|
{/* Role Label + Timestamp */}
|
|
75
78
|
{message.role !== "system" && (
|
|
76
79
|
<div className="flex items-center gap-2 mb-1">
|
|
77
|
-
<span className="text-[11px] font-semibold text-foreground">
|
|
80
|
+
<span id="span-messagerole-user-you" className="text-[11px] font-semibold text-foreground">
|
|
78
81
|
{message.role === "user" ? "You" : "AI Assistant"}
|
|
79
82
|
</span>
|
|
80
|
-
<span className="text-[10px] text-foreground-muted">
|
|
83
|
+
<span id="span-formattimemessagetim" className="text-[10px] text-foreground-muted">
|
|
81
84
|
{formatTime(message.timestamp)}
|
|
82
85
|
</span>
|
|
83
86
|
</div>
|
|
@@ -92,7 +95,7 @@ export function ChatHistory({
|
|
|
92
95
|
message.role === "system" && "text-xs text-foreground-muted italic"
|
|
93
96
|
)}
|
|
94
97
|
>
|
|
95
|
-
<p className="whitespace-pre-wrap">{message.content}</p>
|
|
98
|
+
<p id="p-messagecontent" className="whitespace-pre-wrap">{message.content}</p>
|
|
96
99
|
</div>
|
|
97
100
|
|
|
98
101
|
{/* Inline Diff Preview for actions */}
|
|
@@ -102,6 +105,7 @@ export function ChatHistory({
|
|
|
102
105
|
action={message.action}
|
|
103
106
|
onAccept={() => onAcceptChanges?.(message.id)}
|
|
104
107
|
onRevert={() => onRevertChanges?.(message.id)}
|
|
108
|
+
liveStatus={message.action.status === "pending" ? liveStatus : undefined}
|
|
105
109
|
/>
|
|
106
110
|
</div>
|
|
107
111
|
)}
|
|
@@ -112,19 +116,19 @@ export function ChatHistory({
|
|
|
112
116
|
{message.action.status === "accepted" && (
|
|
113
117
|
<div className="inline-flex items-center gap-1.5 px-2 py-1 bg-green-50 dark:bg-green-900/30 text-green-700 dark:text-green-400 text-xs rounded-md">
|
|
114
118
|
<CheckCircle className="h-3 w-3" />
|
|
115
|
-
<span>Changes accepted</span>
|
|
119
|
+
<span id="span-changes-accepted">Changes accepted</span>
|
|
116
120
|
</div>
|
|
117
121
|
)}
|
|
118
122
|
{message.action.status === "reverted" && (
|
|
119
123
|
<div className="inline-flex items-center gap-1.5 px-2 py-1 bg-amber-50 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400 text-xs rounded-md">
|
|
120
124
|
<RotateCcw className="h-3 w-3" />
|
|
121
|
-
<span>Changes reverted</span>
|
|
125
|
+
<span id="span-changes-reverted">Changes reverted</span>
|
|
122
126
|
</div>
|
|
123
127
|
)}
|
|
124
128
|
{message.action.status === "error" && (
|
|
125
129
|
<div className="inline-flex items-center gap-1.5 px-2 py-1 bg-red-50 dark:bg-red-900/30 text-red-700 dark:text-red-400 text-xs rounded-md">
|
|
126
130
|
<AlertCircle className="h-3 w-3" />
|
|
127
|
-
<span>Error occurred</span>
|
|
131
|
+
<span id="span-error-occurred">Error occurred</span>
|
|
128
132
|
</div>
|
|
129
133
|
)}
|
|
130
134
|
</div>
|
|
@@ -88,6 +88,11 @@ export interface ChatInterfaceProps {
|
|
|
88
88
|
visionFocusedElements?: VisionFocusedElement[];
|
|
89
89
|
onVisionEditComplete?: (result: VisionPendingEdit) => void;
|
|
90
90
|
onApplyFirstComplete?: (session: ApplyFirstSession) => void;
|
|
91
|
+
// Apply-first session state for inline display in chat
|
|
92
|
+
applyFirstSession?: ApplyFirstSession | null;
|
|
93
|
+
applyFirstStatus?: "idle" | "generating" | "previewing" | "applying" | "waiting-hmr" | "reviewing" | "accepting" | "reverting" | "error";
|
|
94
|
+
onApplyFirstAccept?: () => Promise<void>;
|
|
95
|
+
onApplyFirstRevert?: () => Promise<void>;
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
// Helper to generate a unique session ID
|
|
@@ -110,10 +115,15 @@ export function ChatInterface({
|
|
|
110
115
|
visionFocusedElements = [],
|
|
111
116
|
onVisionEditComplete,
|
|
112
117
|
onApplyFirstComplete,
|
|
118
|
+
applyFirstSession,
|
|
119
|
+
applyFirstStatus = "idle",
|
|
120
|
+
onApplyFirstAccept,
|
|
121
|
+
onApplyFirstRevert,
|
|
113
122
|
}: ChatInterfaceProps) {
|
|
114
123
|
// Session management
|
|
115
124
|
const [sessions, setSessions] = useState<ChatSession[]>([]);
|
|
116
125
|
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
|
126
|
+
const [sessionsInitialized, setSessionsInitialized] = useState(false);
|
|
117
127
|
|
|
118
128
|
// Processing state
|
|
119
129
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
@@ -159,11 +169,13 @@ export function ChatInterface({
|
|
|
159
169
|
} catch (e) {
|
|
160
170
|
console.warn('Failed to load chat sessions from localStorage:', e);
|
|
161
171
|
}
|
|
172
|
+
// Mark as initialized regardless of whether we found sessions
|
|
173
|
+
setSessionsInitialized(true);
|
|
162
174
|
}, []);
|
|
163
175
|
|
|
164
|
-
// Create initial session if none exist
|
|
176
|
+
// Create initial session if none exist (only after initialization is complete)
|
|
165
177
|
useEffect(() => {
|
|
166
|
-
if (sessions.length === 0) {
|
|
178
|
+
if (sessionsInitialized && sessions.length === 0) {
|
|
167
179
|
const initialSession: ChatSession = {
|
|
168
180
|
id: generateSessionId(),
|
|
169
181
|
name: "New Chat",
|
|
@@ -175,7 +187,7 @@ export function ChatInterface({
|
|
|
175
187
|
setSessions([initialSession]);
|
|
176
188
|
setActiveSessionId(initialSession.id);
|
|
177
189
|
}
|
|
178
|
-
}, [sessions.length, visionMode]);
|
|
190
|
+
}, [sessionsInitialized, sessions.length, visionMode]);
|
|
179
191
|
|
|
180
192
|
// Persist sessions to localStorage
|
|
181
193
|
useEffect(() => {
|
|
@@ -602,17 +614,45 @@ export function ChatInterface({
|
|
|
602
614
|
};
|
|
603
615
|
|
|
604
616
|
// Handle accept/revert from inline diff preview
|
|
605
|
-
const handleAcceptChanges = useCallback((messageId: string) => {
|
|
617
|
+
const handleAcceptChanges = useCallback(async (messageId: string) => {
|
|
606
618
|
console.log("[Chat] Accept changes for message:", messageId);
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
619
|
+
// Call actual accept API if we have an active session
|
|
620
|
+
if (onApplyFirstAccept && applyFirstSession) {
|
|
621
|
+
try {
|
|
622
|
+
// Call async accept - parent will set status to "accepting"
|
|
623
|
+
await onApplyFirstAccept();
|
|
624
|
+
// Brief delay so user sees the "Accepting..." state
|
|
625
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
626
|
+
// Update message status after accept completes
|
|
627
|
+
updateMessageAction(messageId, "accepted");
|
|
628
|
+
} catch (error) {
|
|
629
|
+
console.error("[Chat] Accept failed:", error);
|
|
630
|
+
// Keep pending on error so user can retry
|
|
631
|
+
}
|
|
632
|
+
} else {
|
|
633
|
+
updateMessageAction(messageId, "accepted");
|
|
634
|
+
}
|
|
635
|
+
}, [updateMessageAction, onApplyFirstAccept, applyFirstSession]);
|
|
610
636
|
|
|
611
|
-
const handleRevertChanges = useCallback((messageId: string) => {
|
|
637
|
+
const handleRevertChanges = useCallback(async (messageId: string) => {
|
|
612
638
|
console.log("[Chat] Revert changes for message:", messageId);
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
639
|
+
// Call actual revert API if we have an active session
|
|
640
|
+
if (onApplyFirstRevert && applyFirstSession) {
|
|
641
|
+
try {
|
|
642
|
+
// Call async revert - parent will set status to "reverting"
|
|
643
|
+
await onApplyFirstRevert();
|
|
644
|
+
// Brief delay so user sees the "Reverting..." state
|
|
645
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
646
|
+
// Update message status after revert completes
|
|
647
|
+
updateMessageAction(messageId, "reverted");
|
|
648
|
+
} catch (error) {
|
|
649
|
+
console.error("[Chat] Revert failed:", error);
|
|
650
|
+
// Keep pending on error so user can retry
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
updateMessageAction(messageId, "reverted");
|
|
654
|
+
}
|
|
655
|
+
}, [updateMessageAction, onApplyFirstRevert, applyFirstSession]);
|
|
616
656
|
|
|
617
657
|
return (
|
|
618
658
|
<div className="flex flex-col h-full">
|
|
@@ -628,7 +668,7 @@ export function ChatInterface({
|
|
|
628
668
|
)}
|
|
629
669
|
>
|
|
630
670
|
<AlertCircle className="h-3.5 w-3.5 flex-shrink-0" />
|
|
631
|
-
<span className="flex-1">{toastMessage.message}</span>
|
|
671
|
+
<span id="span-toastmessagemessage" className="flex-1">{toastMessage.message}</span>
|
|
632
672
|
<button
|
|
633
673
|
onClick={() => setToastMessage(null)}
|
|
634
674
|
className="p-0.5 hover:bg-white/20 rounded flex-shrink-0 transition-colors"
|
|
@@ -654,15 +694,15 @@ export function ChatInterface({
|
|
|
654
694
|
<Eye className="h-3 w-3 text-white" />
|
|
655
695
|
</div>
|
|
656
696
|
<div className="flex-1">
|
|
657
|
-
<p className="text-[11px] font-semibold text-purple-700 dark:text-purple-300">Vision Mode Active</p>
|
|
658
|
-
<p className="text-[10px] text-purple-500 dark:text-purple-400">
|
|
697
|
+
<p id="p-vision-mode-active" className="text-[11px] font-semibold text-purple-700 dark:text-purple-300">Vision Mode Active</p>
|
|
698
|
+
<p id="p-visionfocusedelement" className="text-[10px] text-purple-500 dark:text-purple-400">
|
|
659
699
|
{visionFocusedElements.length > 0
|
|
660
700
|
? `${visionFocusedElements.length} element${visionFocusedElements.length > 1 ? 's' : ''} selected`
|
|
661
701
|
: 'Click elements to focus AI attention'}
|
|
662
702
|
</p>
|
|
663
703
|
</div>
|
|
664
704
|
{visionFocusedElements.length > 0 && (
|
|
665
|
-
<span className="text-xs px-2 py-0.5 bg-purple-500 text-white rounded-full font-medium">
|
|
705
|
+
<span id="span-visionfocusedelement" className="text-xs px-2 py-0.5 bg-purple-500 text-white rounded-full font-medium">
|
|
666
706
|
{visionFocusedElements.length}
|
|
667
707
|
</span>
|
|
668
708
|
)}
|
|
@@ -676,6 +716,7 @@ export function ChatInterface({
|
|
|
676
716
|
onAcceptChanges={handleAcceptChanges}
|
|
677
717
|
onRevertChanges={handleRevertChanges}
|
|
678
718
|
visionMode={visionMode}
|
|
719
|
+
liveStatus={applyFirstStatus}
|
|
679
720
|
/>
|
|
680
721
|
) : (
|
|
681
722
|
/* Empty State */
|
|
@@ -683,8 +724,8 @@ export function ChatInterface({
|
|
|
683
724
|
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-[#00A3E1] to-[#00D3C8] flex items-center justify-center mb-3 shadow-lg">
|
|
684
725
|
<Sparkles className="h-5 w-5 text-white" />
|
|
685
726
|
</div>
|
|
686
|
-
<p className="text-xs font-medium text-foreground mb-1">AI Design Assistant</p>
|
|
687
|
-
<p className="text-[10px] text-foreground-muted max-w-[180px]">
|
|
727
|
+
<p id="p-ai-design-assistant" className="text-xs font-medium text-foreground mb-1">AI Design Assistant</p>
|
|
728
|
+
<p id="p-visionmode-click-ele" className="text-[10px] text-foreground-muted max-w-[180px]">
|
|
688
729
|
{visionMode
|
|
689
730
|
? "Click elements or draw a focus area, then describe changes"
|
|
690
731
|
: componentType === "all"
|
|
@@ -699,7 +740,7 @@ export function ChatInterface({
|
|
|
699
740
|
{/* Annotation indicator */}
|
|
700
741
|
{annotatedScreenshot && visionMode && (
|
|
701
742
|
<div className="flex items-center justify-between text-[10px] text-[#00D3C8] bg-gradient-to-r from-[#00D3C8]/10 to-transparent dark:from-[#00D3C8]/20 px-3 py-1.5 rounded-lg mb-2">
|
|
702
|
-
<span className="flex items-center gap-1.5 font-medium">
|
|
743
|
+
<span id="span-title" className="flex items-center gap-1.5 font-medium">
|
|
703
744
|
<Crop className="h-3 w-3" />
|
|
704
745
|
Focus area selected
|
|
705
746
|
</span>
|
|
@@ -725,7 +766,7 @@ export function ChatInterface({
|
|
|
725
766
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
726
767
|
<div className="absolute inset-0 rounded-full animate-ping opacity-20 bg-current" />
|
|
727
768
|
</div>
|
|
728
|
-
<span className="font-medium">
|
|
769
|
+
<span id="span-visionmode-analyzing" className="font-medium">
|
|
729
770
|
{visionMode ? "Analyzing page..." : "Generating changes..."}
|
|
730
771
|
</span>
|
|
731
772
|
</div>
|
|
@@ -850,7 +891,7 @@ export function ChatInterface({
|
|
|
850
891
|
</div>
|
|
851
892
|
|
|
852
893
|
{/* Quick tip */}
|
|
853
|
-
<p className="text-[9px] text-foreground-muted text-center mt-2">
|
|
894
|
+
<p id="p-press-enter-to-send-" className="text-[9px] text-foreground-muted text-center mt-2">
|
|
854
895
|
Press Enter to send • Keep chatting to refine changes
|
|
855
896
|
</p>
|
|
856
897
|
</div>
|
|
@@ -45,7 +45,7 @@ export function ChatTabBar({
|
|
|
45
45
|
onClick={() => onSelectSession(session.id)}
|
|
46
46
|
>
|
|
47
47
|
<MessageSquare className="h-3 w-3 flex-shrink-0" />
|
|
48
|
-
<span className="truncate max-w-[72px]">
|
|
48
|
+
<span id="chat-tab-bar-span-getsessionnamesessio" className="truncate max-w-[72px]">
|
|
49
49
|
{getSessionName(session, index)}
|
|
50
50
|
</span>
|
|
51
51
|
{/* Close button - only show if more than 1 session */}
|
|
@@ -118,7 +118,7 @@ export function DiffPreview({
|
|
|
118
118
|
{/* Live Preview Indicator */}
|
|
119
119
|
<div className="flex items-center gap-2 p-2 rounded bg-[#00A3E1]/10 border border-[#00A3E1]/30">
|
|
120
120
|
<Eye className="h-3.5 w-3.5 text-[#00A3E1]" />
|
|
121
|
-
<span className="text-xs text-[#00A3E1]">
|
|
121
|
+
<span id="span-live-preview-active-" className="text-xs text-[#00A3E1]">
|
|
122
122
|
Live preview active - scroll to see changes on the page
|
|
123
123
|
</span>
|
|
124
124
|
</div>
|