Skip to content

Commit c401127

Browse files
committed
much more robust featur: with ask ai, save classes and edit
1 parent 554e473 commit c401127

File tree

14 files changed

+1010
-271
lines changed

14 files changed

+1010
-271
lines changed

frontend/src/components/chat/chat-bottombar.tsx

Lines changed: 112 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import React, { useEffect, useRef, useState } from 'react';
33
import { motion, AnimatePresence } from 'framer-motion';
44
import TextareaAutosize from 'react-textarea-autosize';
5-
import { PaperclipIcon, Send, X, Code } from 'lucide-react';
5+
import { PaperclipIcon, Send, X, Code, Wand2 } from 'lucide-react';
66
import { cn } from '@/lib/utils';
77
import { Message } from '../../const/MessageType';
88
import Image from 'next/image';
@@ -12,7 +12,8 @@ import {
1212
TooltipProvider,
1313
TooltipTrigger,
1414
} from '@/components/ui/tooltip';
15-
import { ComponentInspector } from './code-engine/componentInspector';
15+
import { ComponentInspector } from './code-engine/component-inspector';
16+
import { Badge } from '@/components/ui/badge';
1617

1718
interface ChatBottombarProps {
1819
messages: Message[];
@@ -28,6 +29,20 @@ interface ChatBottombarProps {
2829
setIsInspectMode?: React.Dispatch<React.SetStateAction<boolean>>;
2930
}
3031

32+
// Add subtle pulse animation for Component Mode badge
33+
const pulseBadge = {
34+
initial: { scale: 1 },
35+
animate: {
36+
scale: [1, 1.03, 1],
37+
transition: {
38+
repeat: Infinity,
39+
repeatType: "mirror" as const,
40+
duration: 2,
41+
ease: "easeInOut"
42+
}
43+
}
44+
};
45+
3146
export default function ChatBottombar({
3247
messages,
3348
input,
@@ -43,6 +58,7 @@ export default function ChatBottombar({
4358
const [isMobile, setIsMobile] = useState(false);
4459
const [isFocused, setIsFocused] = useState(false);
4560
const [attachments, setAttachments] = useState<File[]>([]);
61+
const [isComponentMode, setIsComponentMode] = useState(false);
4662
const inputRef = useRef<HTMLTextAreaElement>(null);
4763
const fileInputRef = useRef<HTMLInputElement>(null);
4864

@@ -89,6 +105,34 @@ export default function ChatBottombar({
89105
setAttachments([]);
90106
};
91107

108+
const populateChatInput = (content: string) => {
109+
if (setInput) {
110+
setInput(content);
111+
setIsComponentMode(true);
112+
// Focus the input after populating
113+
setTimeout(() => {
114+
inputRef.current?.focus();
115+
}, 100);
116+
}
117+
};
118+
119+
// Check if input still contains component text
120+
useEffect(() => {
121+
if (input.includes('Help me modify this component:')) {
122+
setIsComponentMode(true);
123+
} else {
124+
setIsComponentMode(false);
125+
}
126+
}, [input]);
127+
128+
// Function to exit component mode
129+
const exitComponentMode = () => {
130+
if (setInput) {
131+
setInput('');
132+
setIsComponentMode(false);
133+
}
134+
};
135+
92136
useEffect(() => {
93137
if (inputRef.current) {
94138
inputRef.current.focus();
@@ -179,12 +223,50 @@ export default function ChatBottombar({
179223
</div>
180224
</div>
181225
<div className="flex-1 overflow-auto">
182-
<ComponentInspector />
226+
<ComponentInspector
227+
setIsInspectMode={setIsInspectMode}
228+
populateChatInput={populateChatInput}
229+
/>
183230
</div>
184231
</motion.div>
185232
)}
186233
</AnimatePresence>
187234

235+
{/* Component Mode Badge - outside and above the input box */}
236+
<AnimatePresence>
237+
{isComponentMode && (
238+
<motion.div
239+
initial={{ y: -5, opacity: 0 }}
240+
animate={{ y: 0, opacity: 1 }}
241+
exit={{ y: -5, opacity: 0 }}
242+
transition={{ duration: 0.2 }}
243+
className="flex items-center justify-end gap-1.5 mb-1.5 pr-0.5"
244+
>
245+
<motion.div
246+
variants={pulseBadge}
247+
initial="initial"
248+
animate="animate"
249+
>
250+
<Badge
251+
variant="outline"
252+
className="flex items-center gap-1 text-[10px] py-0.5 px-2 bg-purple-50 text-purple-600 border-purple-200 dark:bg-purple-950/60 dark:text-purple-300 dark:border-purple-800/50 shadow-sm"
253+
>
254+
<Wand2 className="w-2.5 h-2.5" />
255+
<span>Component Mode</span>
256+
</Badge>
257+
</motion.div>
258+
<button
259+
type="button"
260+
onClick={exitComponentMode}
261+
className="h-4 w-4 rounded-full bg-purple-100 flex items-center justify-center text-purple-600 hover:bg-purple-200 dark:bg-purple-900/40 dark:text-purple-300 dark:hover:bg-purple-800/60"
262+
aria-label="Exit component mode"
263+
>
264+
<X className="h-2 w-2" />
265+
</button>
266+
</motion.div>
267+
)}
268+
</AnimatePresence>
269+
188270
<motion.div
189271
initial={{ y: 10, opacity: 0 }}
190272
animate={{ y: 0, opacity: 1 }}
@@ -194,9 +276,16 @@ export default function ChatBottombar({
194276
isFocused
195277
? 'ring-1 ring-blue-500 border-blue-500'
196278
: 'border-gray-200 hover:border-gray-300 dark:border-zinc-700 dark:hover:border-zinc-600',
197-
'bg-white dark:bg-[#1e1e1e]'
279+
isComponentMode
280+
? 'bg-purple-50/20 dark:bg-purple-900/5'
281+
: 'bg-white dark:bg-[#1e1e1e]'
198282
)}
199283
>
284+
{/* Component mode indicator */}
285+
{isComponentMode && (
286+
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r" />
287+
)}
288+
200289
{/* Attachments preview */}
201290
<AnimatePresence>
202291
{attachments.length > 0 && (
@@ -295,10 +384,10 @@ export default function ChatBottombar({
295384
'h-7 w-7 rounded-md flex items-center justify-center',
296385
isInspectMode
297386
? 'bg-blue-100 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400'
298-
: 'bg-gray-100 hover:bg-gray-200 text-gray-700 dark:bg-zinc-700 dark:hover:bg-zinc-600 dark:text-zinc-200'
387+
: 'bg-gray-100 hover:bg-gray-200 text-gray-700 dark:bg-zinc-700 dark:hover:bg-zinc-600 dark:text-zinc-200'
299388
)}
300-
aria-label="Toggle UI Edit Mode"
301-
>
389+
aria-label="Toggle UI Edit Mode"
390+
>
302391
<Code className="h-3.5 w-3.5" />
303392
</button>
304393
</TooltipTrigger>
@@ -311,6 +400,17 @@ export default function ChatBottombar({
311400

312401
{/* Text input */}
313402
<div className="relative flex-1 flex items-center">
403+
<AnimatePresence>
404+
{isComponentMode && (
405+
<motion.div
406+
initial={{ opacity: 0, width: 0 }}
407+
animate={{ opacity: 1, width: "2px" }}
408+
exit={{ opacity: 0, width: 0 }}
409+
transition={{ duration: 0.3, delay: 0.1 }}
410+
className="absolute left-0 top-1/2 transform -translate-y-1/2 h-[60%] bg-gradient-to-b rounded-r-full"
411+
/>
412+
)}
413+
</AnimatePresence>
314414
<TextareaAutosize
315415
autoComplete="off"
316416
value={input}
@@ -320,8 +420,11 @@ export default function ChatBottombar({
320420
onFocus={() => setIsFocused(true)}
321421
onBlur={() => setIsFocused(false)}
322422
name="message"
323-
placeholder="Message Agent..."
324-
className="resize-none px-2 py-2.5 w-full focus:outline-none bg-transparent text-gray-800 dark:text-zinc-200 text-sm placeholder:text-gray-400 dark:placeholder:text-zinc-400"
423+
placeholder={isComponentMode ? "Describe what you want to change..." : "Message Agent..."}
424+
className={cn(
425+
"resize-none px-2 w-full focus:outline-none bg-transparent text-gray-800 dark:text-zinc-200 text-sm placeholder:text-gray-400 dark:placeholder:text-zinc-400",
426+
isComponentMode ? "pt-4 pb-2.5" : "py-2.5"
427+
)}
325428
maxRows={5}
326429
/>
327430
</div>

frontend/src/components/chat/code-engine/component-inspector/components/SpacingControls.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,22 @@ export const SpacingControls: React.FC<SpacingControlsProps> = ({
9696
title="Apply to all sides"
9797
onClick={() => {
9898
if (displayValue) {
99+
// Log to debug the issue
100+
console.log(`ALL button clicked for ${property}, value=${displayValue}`);
101+
99102
// Apply the same value to all sides (top, right, bottom, left)
100103
const sides = property.startsWith('padding')
101104
? ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft']
102105
: ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'];
103106

107+
// First update each individual side with a direct call
104108
sides.forEach(side => {
105-
const pixelValue = addPxUnitIfNeeded(displayValue);
106109
onValueChange(side, displayValue, false);
107110
});
111+
112+
// Then explicitly update the main property separately, telling it *not* to apply
113+
// to both sides since we've already done so individually - this is important!
114+
onValueChange(property, displayValue, false);
108115
}
109116
}}
110117
>

frontend/src/components/chat/code-engine/component-inspector/hooks/useMessageHandler.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,19 @@ export const useMessageHandler = ({
6363
setEditableContent('');
6464
setOriginalContent('');
6565
}
66-
} else if (event.data.type === 'ELEMENT_STYLES') {
66+
}
67+
// Handle styles data received from iframe
68+
else if (event.data.type === 'ELEMENT_STYLES') {
6769
console.log("Processing element styles response:", event.data.payload);
6870
if (event.data.payload && event.data.payload.success) {
6971
console.log("Received computed styles:", event.data.payload.styles);
7072

7173
// Process received styles
7274
const styles = event.data.payload.styles;
7375

76+
// Log background color specifically for debugging
77+
console.log("Background color from computed styles:", styles.backgroundColor);
78+
7479
// Initialize spacing inputs from computed styles
7580
const initialSpacingInputs = initializeSpacingInputs(styles);
7681
setSpacingInputs(initialSpacingInputs);

0 commit comments

Comments
 (0)