Compare commits

..

5 Commits

Author SHA1 Message Date
Fatih Kadir Akın
9a5acf16fb update 2025-06-18 20:50:34 +03:00
Fatih Kadir Akın
fd8782ae62 update 2025-06-18 20:41:57 +03:00
Fatih Kadir Akın
e3b9457a46 update 2025-06-18 20:26:44 +03:00
Fatih Kadir Akın
fb3607e51f update 2025-06-18 17:28:22 +03:00
Fatih Kadir Akın
a9efb68d97 update 2025-06-18 16:38:40 +03:00
5 changed files with 923 additions and 236 deletions

View File

@ -26,6 +26,46 @@
<link rel="stylesheet" href="{{ '/embed-preview-style.css' | relative_url }}"> <link rel="stylesheet" href="{{ '/embed-preview-style.css' | relative_url }}">
<link rel="icon" type="image/x-icon" href="{{ '/favicon.ico' | relative_url }}"> <link rel="icon" type="image/x-icon" href="{{ '/favicon.ico' | relative_url }}">
<meta name="description" content="AI prompt viewer"> <meta name="description" content="AI prompt viewer">
<style>
@keyframes flash {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.flash-animation {
animation: flash 1.5s ease-in-out infinite;
}
@keyframes slideUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slideDown {
from {
transform: translateY(0);
opacity: 1;
}
to {
transform: translateY(100%);
opacity: 0;
}
}
.diff-enter {
animation: slideUp 0.3s ease-out;
}
.diff-exit {
animation: slideDown 0.3s ease-out;
}
</style>
</head> </head>
<body class="bg-dynamic-background text-dynamic-foreground overflow-hidden"> <body class="bg-dynamic-background text-dynamic-foreground overflow-hidden">
<!-- Viewer Mode --> <!-- Viewer Mode -->
@ -39,9 +79,9 @@
</div> </div>
<!-- Main Content --> <!-- Main Content -->
<div class="flex-1 flex flex-col p-2 sm:p-4 min-h-0"> <div class="flex-1 flex flex-col p-2 sm:p-4 h-full overflow-hidden">
<!-- Top Bar with Context Pills and Edit Button --> <!-- Top Bar with Context Pills and Edit Button -->
<div class="flex justify-between items-start gap-2 mb-1 sm:mb-2 max-w-full flex-shrink-0"> <div class="flex justify-between items-start gap-2 mb-1 sm:mb-2 flex-shrink-0">
<!-- Context Pills --> <!-- Context Pills -->
<div id="context-pills" class="flex flex-wrap gap-1.5 empty:hidden flex-1 min-w-0 pr-2"></div> <div id="context-pills" class="flex flex-wrap gap-1.5 empty:hidden flex-1 min-w-0 pr-2"></div>
@ -54,22 +94,98 @@
</button> </button>
</div> </div>
<!-- Main Prompt Interface - Full Height --> <!-- Main Prompt Interface with Floating Diff - Flex-1 to take remaining space -->
<div class="flex-1 flex flex-col min-h-0"> <div class="flex-1 relative min-h-0 overflow-hidden">
<!-- Floating Diff View (when enabled and viewport is compact) -->
<div id="diff-view" class="hidden absolute top-2 left-2 right-2 z-10 diff-floating">
<div class="bg-dynamic-background/95 backdrop-blur-sm border border-dynamic-border shadow-lg rounded-lg p-3">
<div class="flex items-center justify-between">
<div class="flex items-center gap-1.5">
<svg class="w-3 h-3 text-dynamic-primary flex-shrink-0" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4 2a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V7.414A2 2 0 0017.414 6L14 2.586A2 2 0 0012.586 2H4zm2 4a1 1 0 011-1h4a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7zm-1 5a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1z" clip-rule="evenodd"/>
</svg>
<span id="diff-filename" class="text-xs font-medium text-dynamic-foreground truncate"></span>
</div>
<div class="flex gap-1 items-center">
<div class="flex rounded-md overflow-hidden">
<button id="diff-accept-diff" class="px-2 py-0.5 bg-green-500 hover:bg-green-600 text-white text-[10px] font-medium transition-colors flex items-center gap-0.5">
<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
<span class="hidden sm:inline">Accept</span>
</button>
<button id="diff-reject-diff" class="px-2 py-0.5 bg-red-500 hover:bg-red-600 text-white text-[10px] font-medium transition-colors flex items-center gap-0.5">
<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
<span class="hidden sm:inline">Reject</span>
</button>
</div>
<!-- Collapse/Expand button -->
<button id="toggle-diff" class="p-0.5 text-dynamic-muted-foreground hover:text-dynamic-foreground transition-colors" title="Toggle diff view">
<svg class="w-3 h-3 transform transition-transform" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
</button>
</div>
</div>
<div id="diff-content" class="space-y-1 transition-all duration-300 overflow-hidden">
<div id="diff-old-content" class="bg-red-50/70 dark:bg-red-950/20 p-2 rounded text-[10px] font-mono whitespace-pre-wrap text-red-700 dark:text-red-400 overflow-x-auto border border-red-200/50 dark:border-red-900/30 max-h-20 overflow-y-auto custom-scrollbar"></div>
<div id="diff-new-content" class="bg-green-50/70 dark:bg-green-950/20 p-2 rounded text-[10px] font-mono whitespace-pre-wrap text-green-700 dark:text-green-400 overflow-x-auto border border-green-200/50 dark:border-green-900/30 max-h-20 overflow-y-auto custom-scrollbar"></div>
</div>
</div>
</div>
<!-- Container for inline diff and prompt -->
<div class="h-full flex flex-col">
<!-- Inline Diff View (when enabled and viewport has space) -->
<div id="diff-view-inline" class="hidden mb-2 flex-shrink-0">
<div class="bg-dynamic-muted border border-dynamic-border rounded-lg p-2">
<div class="flex items-center justify-between mb-1.5">
<div class="flex items-center gap-1.5">
<svg class="w-3 h-3 text-dynamic-primary flex-shrink-0" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4 2a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V7.414A2 2 0 0017.414 6L14 2.586A2 2 0 0012.586 2H4zm2 4a1 1 0 011-1h4a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7zm-1 5a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1z" clip-rule="evenodd"/>
</svg>
<span id="inline-diff-filename" class="text-xs font-medium text-dynamic-foreground truncate"></span>
</div>
<div class="flex rounded-md overflow-hidden">
<button id="inline-accept-diff" class="px-2 py-0.5 bg-green-500 hover:bg-green-600 text-white text-[10px] font-medium transition-colors flex items-center gap-0.5">
<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
<span class="hidden sm:inline">Accept</span>
</button>
<button id="inline-reject-diff" class="px-2 py-0.5 bg-red-500 hover:bg-red-600 text-white text-[10px] font-medium transition-colors flex items-center gap-0.5">
<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
<span class="hidden sm:inline">Reject</span>
</button>
</div>
</div>
<div class="space-y-0.5">
<div id="inline-old-content" class="bg-red-50/70 dark:bg-red-950/20 p-1 rounded text-[10px] font-mono whitespace-pre-wrap text-red-700 dark:text-red-400 overflow-x-auto border border-red-200/50 dark:border-red-900/30 max-h-20 overflow-y-auto custom-scrollbar"></div>
<div id="inline-new-content" class="bg-green-50/70 dark:bg-green-950/20 p-1 rounded text-[10px] font-mono whitespace-pre-wrap text-green-700 dark:text-green-400 overflow-x-auto border border-green-200/50 dark:border-green-900/30 max-h-20 overflow-y-auto custom-scrollbar"></div>
</div>
</div>
</div>
<!-- Prompt Container - Full height of parent -->
<div id="prompt-container" class="flex-1 bg-dynamic-muted border border-dynamic-border rounded-xl p-3 relative focus-within:border-dynamic-primary transition-colors flex flex-col min-h-0"> <div id="prompt-container" class="flex-1 bg-dynamic-muted border border-dynamic-border rounded-xl p-3 relative focus-within:border-dynamic-primary transition-colors flex flex-col min-h-0">
<div id="prompt-text" class="flex-1 text-dynamic-foreground leading-relaxed whitespace-pre-wrap overflow-y-auto custom-scrollbar text-sm sm:text-base"></div> <div id="prompt-text" class="flex-1 text-dynamic-foreground leading-relaxed whitespace-pre-wrap overflow-y-auto custom-scrollbar text-sm sm:text-base duration-300"></div>
<div id="prompt-placeholder" class="text-dynamic-muted-foreground italic absolute top-3 left-3 pointer-events-none text-sm sm:text-base">← Enter your prompt on designer...</div> <div id="prompt-placeholder" class="text-dynamic-muted-foreground italic absolute top-3 left-3 pointer-events-none text-sm sm:text-base">← Enter your prompt on designer...</div>
</div> </div>
</div> </div>
</div>
<!-- Bottom Bar --> <!-- Bottom Bar - Always visible with reduced height -->
<div class="flex justify-between items-center gap-2 mt-1 sm:mt-2 flex-shrink-0"> <div class="flex justify-between items-center gap-2 mt-1 pt-1 flex-shrink-0">
<!-- Settings Pills --> <!-- Settings Pills - Smaller -->
<div id="settings-pills" class="flex gap-1 sm:gap-2 flex-wrap flex-1 min-w-0"></div> <div id="settings-pills" class="flex gap-1 flex-wrap flex-1 min-w-0"></div>
<!-- Send Button (circular with arrow up) --> <!-- Send Button - Smaller -->
<button id="copy-button" class="w-8 h-8 sm:w-10 sm:h-10 bg-dynamic-primary text-white rounded-full flex items-center justify-center hover:opacity-90 transition-opacity focus-ring flex-shrink-0 shadow-lg touch-target" title="Send prompt"> <button id="copy-button" class="w-7 h-7 sm:w-8 sm:h-8 bg-dynamic-primary text-white rounded-full flex items-center justify-center hover:opacity-90 transition-opacity flex-shrink-0 shadow-md" title="Send prompt">
<svg width="16" height="16" class="sm:w-5 sm:h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"> <svg width="14" height="14" class="sm:w-4 sm:h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<path d="M12 19V5M5 12l7-7 7 7"/> <path d="M12 19V5M5 12l7-7 7 7"/>
</svg> </svg>
</button> </button>

View File

@ -27,8 +27,30 @@
<link rel="stylesheet" href="{{ '/embed-style.css' | relative_url }}"> <link rel="stylesheet" href="{{ '/embed-style.css' | relative_url }}">
<link rel="icon" type="image/x-icon" href="{{ '/favicon.ico' | relative_url }}"> <link rel="icon" type="image/x-icon" href="{{ '/favicon.ico' | relative_url }}">
<meta name="description" content="Design and customize embeddable AI prompts"> <meta name="description" content="Design and customize embeddable AI prompts">
<style>
.checkerboard-bg {
background-image:
linear-gradient(45deg, rgba(0,0,0,0.06) 25%, transparent 25%),
linear-gradient(-45deg, rgba(0,0,0,0.06) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgba(0,0,0,0.06) 75%),
linear-gradient(-45deg, transparent 75%, rgba(0,0,0,0.06) 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
.dark .checkerboard-bg {
background-color: #1e1e1e;
background-image:
linear-gradient(45deg, #353535 25%, transparent 25%),
linear-gradient(-45deg, #353535 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #353535 75%),
linear-gradient(-45deg, transparent 75%, #353535 75%);
background-size: 16px 16px;
background-position: 0 0, 0 8px, 8px -8px, -8px 0px;
}
</style>
</head> </head>
<body class="bg-dynamic-background text-dynamic-foreground overflow-hidden"> <body class="bg-dynamic-background text-dynamic-foreground overflow-hidden checkerboard-bg">
<!-- Site Header --> <!-- Site Header -->
<header class="site-header"> <header class="site-header">
<div class="header-left"> <div class="header-left">
@ -37,6 +59,13 @@
<p class="site-slogan sm:hidden">Design and share AI prompts</p> <p class="site-slogan sm:hidden">Design and share AI prompts</p>
</div> </div>
<div class="header-right"> <div class="header-right">
<a href="https://blog.fka.dev/blog/2025-06-18-building-a-react-hook-with-ai-a-step-by-step-guide-using-vibe-an-example-post-that-uses-prompts-chat-embed/"
target="_blank"
rel="noopener noreferrer"
class="flex items-center text-xs text-dynamic-muted-foreground hover:text-dynamic-foreground transition-colors mr-3 touch-target hidden sm:inline-flex"
title="View example blog post using prompts.chat embed">
View example blog post
</a>
<button class="dark-mode-toggle touch-target" onclick="toggleDarkMode()" title="Toggle dark mode"> <button class="dark-mode-toggle touch-target" onclick="toggleDarkMode()" title="Toggle dark mode">
<svg class="mode-icon sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg> <svg class="mode-icon sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>
<svg class="mode-icon moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: none;"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg> <svg class="mode-icon moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: none;"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
@ -49,37 +78,53 @@
<!-- Left Panel - Customization --> <!-- Left Panel - Customization -->
<div class="designer-panel w-full lg:w-80 flex flex-col h-auto lg:h-full overflow-hidden order-2 lg:order-1 flex-shrink-0"> <div class="designer-panel w-full lg:w-80 flex flex-col h-auto lg:h-full overflow-hidden order-2 lg:order-1 flex-shrink-0">
<div class="flex-1 overflow-y-auto custom-scrollbar"> <div class="flex-1 overflow-y-auto custom-scrollbar">
<div class="p-4 lg:p-6 lg:pt-0 space-y-4"> <div class="p-3 lg:p-4 space-y-3">
<!-- Context -->
<!-- CONTENT SECTION -->
<div class="space-y-2"> <div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">Context</label> <h3 class="text-xs font-semibold text-dynamic-foreground uppercase tracking-wider border-b border-dynamic-border pb-1">Prompt</h3>
<!-- Example Selector -->
<div class="space-y-1">
<label class="text-xs font-medium text-dynamic-muted-foreground">Load Example</label>
<select id="example-select" class="w-full p-2 bg-dynamic-muted border border-dynamic-border rounded text-xs focus-ring touch-target">
<option value="">Choose an example...</option>
<option value="vibe-coding">Vibe coding (no diff)</option>
<option value="vibe-coding-diff">Vibe coding with diff</option>
<option value="chatgpt">ChatGPT example</option>
<option value="claude">Claude example</option>
<option value="image-analysis">Image analysis</option>
<option value="api-design">API design</option>
</select>
</div>
<!-- Context -->
<div class="space-y-1">
<label class="text-xs font-medium text-dynamic-muted-foreground">Context</label>
<input type="text" id="designer-context" <input type="text" id="designer-context"
class="w-full p-3 lg:p-2 bg-dynamic-background border border-dynamic-border rounded-lg text-sm focus-ring touch-target" class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-xs focus-ring touch-target"
placeholder="file.py, @web, @codebase, #image"> placeholder="file.py, @web, @codebase, #image">
</div> </div>
<!-- Prompt Text --> <!-- Prompt Text -->
<div class="space-y-2"> <div class="space-y-1">
<label class="text-sm font-medium text-dynamic-muted-foreground">Prompt Text</label> <label class="text-xs font-medium text-dynamic-muted-foreground">Prompt Text</label>
<textarea id="designer-prompt" <textarea id="designer-prompt"
class="w-full p-3 bg-dynamic-background border border-dynamic-border rounded-lg text-sm resize-none focus-ring custom-scrollbar touch-target" class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-xs resize-none focus-ring custom-scrollbar touch-target"
rows="8" rows="6"
placeholder="Enter your prompt..."></textarea> placeholder="Enter your prompt..."></textarea>
<div class="flex gap-3">
<button type="button" id="vibe-example" class="text-xs text-dynamic-primary hover:text-opacity-80 transition-opacity touch-target py-1">
Vibe coding example →
</button>
<button type="button" id="chat-example" class="text-xs text-dynamic-primary hover:text-opacity-80 transition-opacity touch-target py-1">
Chat example →
</button>
</div> </div>
</div> </div>
<!-- Model & Mode --> <!-- AI SETTINGS SECTION -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div class="space-y-2"> <div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">Model</label> <h3 class="text-xs font-semibold text-dynamic-foreground uppercase tracking-wider border-b border-dynamic-border pb-1">AI Settings</h3>
<select id="designer-model" class="w-full p-3 lg:p-2 bg-dynamic-background border border-dynamic-border rounded-lg text-sm focus-ring touch-target">
<!-- Model & Mode Grid -->
<div class="grid grid-cols-2 gap-2">
<div class="space-y-1">
<label class="text-xs font-medium text-dynamic-muted-foreground">Model</label>
<select id="designer-model" class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-xs focus-ring touch-target">
<option value="o3">o3</option> <option value="o3">o3</option>
<option value="GPT 4.1">GPT 4.1</option> <option value="GPT 4.1">GPT 4.1</option>
<option value="GPT 4o" selected>GPT 4o</option> <option value="GPT 4o" selected>GPT 4o</option>
@ -91,12 +136,12 @@
<option value="custom">[Custom]</option> <option value="custom">[Custom]</option>
</select> </select>
<input type="text" id="designer-custom-model" <input type="text" id="designer-custom-model"
class="w-full p-3 lg:p-2 bg-dynamic-background border border-dynamic-border rounded-lg text-sm focus-ring hidden touch-target" class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-xs focus-ring hidden touch-target"
placeholder="Enter custom model name"> placeholder="Custom model">
</div> </div>
<div class="space-y-2"> <div class="space-y-1">
<label class="text-sm font-medium text-dynamic-muted-foreground">Mode</label> <label class="text-xs font-medium text-dynamic-muted-foreground">Mode</label>
<select id="designer-mode-select" class="w-full p-3 lg:p-2 bg-dynamic-background border border-dynamic-border rounded-lg text-sm focus-ring touch-target"> <select id="designer-mode-select" class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-xs focus-ring touch-target">
<option value="chat">Chat</option> <option value="chat">Chat</option>
<option value="agent">Agent</option> <option value="agent">Agent</option>
<option value="manual">Manual</option> <option value="manual">Manual</option>
@ -106,70 +151,71 @@
</div> </div>
<!-- Options --> <!-- Options -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 justify-items-start"> <div class="flex gap-4">
<label class="flex items-center space-x-2 touch-target"> <label class="flex items-center space-x-1.5 touch-target">
<input type="checkbox" id="designer-thinking" class="rounded border-dynamic-border w-4 h-4"> <input type="checkbox" id="designer-thinking" class="rounded border-dynamic-border w-3.5 h-3.5">
<span class="text-sm text-dynamic-muted-foreground">Thinking</span> <span class="text-xs text-dynamic-muted-foreground">Thinking</span>
</label> </label>
<label class="flex items-center space-x-2 touch-target"> <label class="flex items-center space-x-1.5 touch-target">
<input type="checkbox" id="designer-max" class="rounded border-dynamic-border w-4 h-4"> <input type="checkbox" id="designer-max" class="rounded border-dynamic-border w-3.5 h-3.5">
<span class="text-sm text-dynamic-muted-foreground">MAX mode</span> <span class="text-xs text-dynamic-muted-foreground">MAX mode</span>
</label> </label>
</div> </div>
</div>
<!-- Theme Mode Selector --> <!-- APPEARANCE SECTION -->
<div class="space-y-2"> <div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">Theme Mode</label> <h3 class="text-xs font-semibold text-dynamic-foreground uppercase tracking-wider border-b border-dynamic-border pb-1">Appearance</h3>
<div class="grid grid-cols-3 gap-2">
<button type="button" id="theme-light" class="theme-mode-btn px-3 py-3 lg:py-2 text-xs font-medium rounded-lg border border-dynamic-border bg-dynamic-background hover:bg-dynamic-muted transition-colors text-center touch-target"> <!-- Theme Mode -->
<div class="space-y-1">
<label class="text-xs font-medium text-dynamic-muted-foreground">Theme</label>
<div class="grid grid-cols-3 gap-1">
<button type="button" id="theme-light" class="theme-mode-btn px-2 py-1.5 text-xs font-medium rounded border border-dynamic-border bg-dynamic-background hover:bg-dynamic-muted transition-colors text-center touch-target">
Light Light
</button> </button>
<button type="button" id="theme-dark" class="theme-mode-btn px-3 py-3 lg:py-2 text-xs font-medium rounded-lg border border-dynamic-border bg-dynamic-background hover:bg-dynamic-muted transition-colors text-center touch-target"> <button type="button" id="theme-dark" class="theme-mode-btn px-2 py-1.5 text-xs font-medium rounded border border-dynamic-border bg-dynamic-background hover:bg-dynamic-muted transition-colors text-center touch-target">
Dark Dark
</button> </button>
<button type="button" id="theme-auto" class="theme-mode-btn px-3 py-3 lg:py-2 text-xs font-medium rounded-lg border border-dynamic-border bg-dynamic-primary text-white transition-colors text-center touch-target"> <button type="button" id="theme-auto" class="theme-mode-btn px-2 py-1.5 text-xs font-medium rounded border border-dynamic-border bg-dynamic-primary text-white transition-colors text-center touch-target">
Auto Auto
</button> </button>
</div> </div>
</div> </div>
<!-- Color Scheme -->
<div class="space-y-3">
<label class="text-sm font-medium text-dynamic-muted-foreground">Color Scheme</label>
<!-- Color Presets --> <!-- Color Presets -->
<div class="space-y-2"> <div class="space-y-1">
<label class="text-xs text-dynamic-muted-foreground uppercase tracking-wider">Presets</label> <label class="text-xs font-medium text-dynamic-muted-foreground">Colors</label>
<div class="grid grid-cols-4 gap-2"> <div class="grid grid-cols-8 gap-1">
<button type="button" class="color-preset w-full h-10 lg:h-8 rounded-lg border-2 border-transparent hover:border-dynamic-primary transition-colors touch-target" <button type="button" class="color-preset w-full h-6 rounded border border-dynamic-border hover:border-dynamic-primary transition-colors touch-target"
data-light="#6b7280" data-dark="#e5e7eb" data-light="#6b7280" data-dark="#e5e7eb"
style="background: linear-gradient(135deg, #6b7280 50%, #e5e7eb 50%)" style="background: linear-gradient(135deg, #6b7280 50%, #e5e7eb 50%)"
title="Minimal"></button> title="Minimal"></button>
<button type="button" class="color-preset w-full h-10 lg:h-8 rounded-lg border-2 border-transparent hover:border-dynamic-primary transition-colors touch-target" <button type="button" class="color-preset w-full h-6 rounded border border-dynamic-border hover:border-dynamic-primary transition-colors touch-target"
data-light="#1f2937" data-dark="#9ca3af" data-light="#1f2937" data-dark="#9ca3af"
style="background: linear-gradient(135deg, #1f2937 50%, #9ca3af 50%)" style="background: linear-gradient(135deg, #1f2937 50%, #9ca3af 50%)"
title="Dark Gray"></button> title="Dark Gray"></button>
<button type="button" class="color-preset w-full h-10 lg:h-8 rounded-lg border-2 border-transparent hover:border-dynamic-primary transition-colors touch-target" <button type="button" class="color-preset w-full h-6 rounded border border-dynamic-border hover:border-dynamic-primary transition-colors touch-target"
data-light="#64748b" data-dark="#94a3b8" data-light="#64748b" data-dark="#94a3b8"
style="background: linear-gradient(135deg, #64748b 50%, #94a3b8 50%)" style="background: linear-gradient(135deg, #64748b 50%, #94a3b8 50%)"
title="Dimmed"></button> title="Dimmed"></button>
<button type="button" class="color-preset w-full h-10 lg:h-8 rounded-lg border-2 border-transparent hover:border-dynamic-primary transition-colors touch-target" <button type="button" class="color-preset w-full h-6 rounded border border-dynamic-border hover:border-dynamic-primary transition-colors touch-target"
data-light="#3b82f6" data-dark="#60a5fa" data-light="#3b82f6" data-dark="#60a5fa"
style="background: linear-gradient(135deg, #3b82f6 50%, #60a5fa 50%)" style="background: linear-gradient(135deg, #3b82f6 50%, #60a5fa 50%)"
title="Blue"></button> title="Blue"></button>
<button type="button" class="color-preset w-full h-10 lg:h-8 rounded-lg border-2 border-transparent hover:border-dynamic-primary transition-colors touch-target" <button type="button" class="color-preset w-full h-6 rounded border border-dynamic-border hover:border-dynamic-primary transition-colors touch-target"
data-light="#10b981" data-dark="#34d399" data-light="#10b981" data-dark="#34d399"
style="background: linear-gradient(135deg, #10b981 50%, #34d399 50%)" style="background: linear-gradient(135deg, #10b981 50%, #34d399 50%)"
title="Green"></button> title="Green"></button>
<button type="button" class="color-preset w-full h-10 lg:h-8 rounded-lg border-2 border-transparent hover:border-dynamic-primary transition-colors touch-target" <button type="button" class="color-preset w-full h-6 rounded border border-dynamic-border hover:border-dynamic-primary transition-colors touch-target"
data-light="#8b5cf6" data-dark="#a78bfa" data-light="#8b5cf6" data-dark="#a78bfa"
style="background: linear-gradient(135deg, #8b5cf6 50%, #a78bfa 50%)" style="background: linear-gradient(135deg, #8b5cf6 50%, #a78bfa 50%)"
title="Purple"></button> title="Purple"></button>
<button type="button" class="color-preset w-full h-10 lg:h-8 rounded-lg border-2 border-transparent hover:border-dynamic-primary transition-colors touch-target" <button type="button" class="color-preset w-full h-6 rounded border border-dynamic-border hover:border-dynamic-primary transition-colors touch-target"
data-light="#f97316" data-dark="#fb923c" data-light="#f97316" data-dark="#fb923c"
style="background: linear-gradient(135deg, #f97316 50%, #fb923c 50%)" style="background: linear-gradient(135deg, #f97316 50%, #fb923c 50%)"
title="Orange"></button> title="Orange"></button>
<button type="button" class="color-preset w-full h-10 lg:h-8 rounded-lg border-2 border-transparent hover:border-dynamic-primary transition-colors touch-target" <button type="button" class="color-preset w-full h-6 rounded border border-dynamic-border hover:border-dynamic-primary transition-colors touch-target"
data-light="#ec4899" data-dark="#f472b6" data-light="#ec4899" data-dark="#f472b6"
style="background: linear-gradient(135deg, #ec4899 50%, #f472b6 50%)" style="background: linear-gradient(135deg, #ec4899 50%, #f472b6 50%)"
title="Pink"></button> title="Pink"></button>
@ -177,88 +223,115 @@
</div> </div>
<!-- Custom Colors --> <!-- Custom Colors -->
<div class="space-y-2"> <div class="grid grid-cols-2 gap-2">
<label class="text-xs text-dynamic-muted-foreground uppercase tracking-wider">Custom Colors</label> <div class="space-y-1">
<div class="space-y-3"> <label class="text-[10px] text-dynamic-muted-foreground">Light</label>
<div class="flex items-center gap-3"> <div class="flex gap-1">
<div class="flex items-center gap-2 flex-1"> <input type="color" id="designer-light-color" value="#3b82f6" class="w-6 h-6 rounded border border-dynamic-border cursor-pointer touch-target">
<div class="relative"> <input type="text" id="designer-light-color-text" value="#3b82f6" class="flex-1 px-1 py-1 bg-dynamic-background border border-dynamic-border rounded text-[10px] focus-ring font-mono touch-target" pattern="^#[0-9A-Fa-f]{6}$">
<input type="color" id="designer-light-color"
value="#3b82f6"
class="w-12 h-12 lg:w-10 lg:h-10 rounded-lg border-2 border-dynamic-border cursor-pointer hover:border-dynamic-primary transition-colors touch-target">
<div class="absolute inset-0 rounded-lg pointer-events-none" style="background: linear-gradient(135deg, transparent 50%, rgba(0,0,0,0.1) 50%)"></div>
</div>
<div class="flex-1">
<label class="text-xs text-dynamic-muted-foreground block mb-1">Light Mode</label>
<input type="text" id="designer-light-color-text"
value="#3b82f6"
class="w-full px-3 py-2 lg:px-2 lg:py-1 bg-dynamic-background border border-dynamic-border rounded text-xs focus-ring font-mono touch-target"
pattern="^#[0-9A-Fa-f]{6}$">
</div>
</div>
</div>
<div class="flex items-center gap-3">
<div class="flex items-center gap-2 flex-1">
<div class="relative">
<input type="color" id="designer-dark-color"
value="#60a5fa"
class="w-12 h-12 lg:w-10 lg:h-10 rounded-lg border-2 border-dynamic-border cursor-pointer hover:border-dynamic-primary transition-colors touch-target">
<div class="absolute inset-0 rounded-lg pointer-events-none" style="background: linear-gradient(135deg, transparent 50%, rgba(255,255,255,0.1) 50%)"></div>
</div>
<div class="flex-1">
<label class="text-xs text-dynamic-muted-foreground block mb-1">Dark Mode</label>
<input type="text" id="designer-dark-color-text"
value="#60a5fa"
class="w-full px-3 py-2 lg:px-2 lg:py-1 bg-dynamic-background border border-dynamic-border rounded text-xs focus-ring font-mono touch-target"
pattern="^#[0-9A-Fa-f]{6}$">
</div>
</div> </div>
</div> </div>
<div class="space-y-1">
<label class="text-[10px] text-dynamic-muted-foreground">Dark</label>
<div class="flex gap-1">
<input type="color" id="designer-dark-color" value="#60a5fa" class="w-6 h-6 rounded border border-dynamic-border cursor-pointer touch-target">
<input type="text" id="designer-dark-color-text" value="#60a5fa" class="flex-1 px-1 py-1 bg-dynamic-background border border-dynamic-border rounded text-[10px] focus-ring font-mono touch-target" pattern="^#[0-9A-Fa-f]{6}$">
</div> </div>
</div> </div>
</div> </div>
<!-- Height --> <!-- Height -->
<div class="space-y-2"> <div class="space-y-1">
<label class="text-sm font-medium text-dynamic-muted-foreground"> <label class="text-xs font-medium text-dynamic-muted-foreground">
Height: <span id="height-value" class="text-dynamic-foreground">400</span>px Height: <span id="height-value" class="text-dynamic-foreground">400</span>px
</label> </label>
<input type="range" id="designer-height" <input type="range" id="designer-height"
class="w-full h-2 bg-dynamic-muted rounded-lg appearance-none cursor-pointer slider" class="w-full h-1.5 bg-dynamic-muted rounded-lg appearance-none cursor-pointer slider"
min="200" max="800" value="400" step="50"> min="200" max="800" value="400" step="50">
<div class="flex justify-between text-xs text-dynamic-muted-foreground"> <div class="flex justify-between text-[10px] text-dynamic-muted-foreground opacity-60">
<span>200px</span> <span>200px</span>
<span>800px</span> <span>800px</span>
</div> </div>
</div> </div>
</div>
<!-- FEATURES SECTION -->
<div class="space-y-2">
<h3 class="text-xs font-semibold text-dynamic-foreground uppercase tracking-wider border-b border-dynamic-border pb-1">Features</h3>
<!-- File Tree --> <!-- File Tree -->
<div class="space-y-2"> <div class="space-y-1">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<label class="text-sm font-medium text-dynamic-muted-foreground">File Tree</label> <label class="text-xs font-medium text-dynamic-muted-foreground">File Tree</label>
<label class="flex items-center space-x-1.5 text-xs"> <label class="flex items-center space-x-1 text-[10px]">
<input type="checkbox" id="designer-show-filetree" class="rounded border-dynamic-border w-3.5 h-3.5" checked> <input type="checkbox" id="designer-show-filetree" class="rounded border-dynamic-border w-3 h-3" checked>
<span class="text-dynamic-muted-foreground">Show in preview</span> <span class="text-dynamic-muted-foreground">Show</span>
</label> </label>
</div> </div>
<textarea id="designer-filetree" <textarea id="designer-filetree"
class="w-full p-3 bg-dynamic-background border border-dynamic-border rounded-lg text-sm resize-none focus-ring custom-scrollbar touch-target font-mono" class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-[10px] resize-none focus-ring custom-scrollbar touch-target font-mono"
rows="6" rows="4"
placeholder="Enter files (one per line):&#10;index.html&#10;styles/main.css&#10;scripts/app.js&#10;components/header.vue"></textarea> placeholder="index.html&#10;styles/main.css&#10;scripts/app.js&#10;components/header.vue"></textarea>
<p class="text-xs text-dynamic-muted-foreground"> <p class="text-[9px] text-dynamic-muted-foreground opacity-60">
Enter filenames one per line. Use forward slashes for folders (e.g., folder/file.js). One per line. Use / for folders. Add * to highlight.
Add * at the end of a filename to highlight it (e.g., main.js*)
</p> </p>
</div> </div>
<!-- Diff View -->
<div class="space-y-1">
<div class="flex items-center justify-between">
<label class="text-xs font-medium text-dynamic-muted-foreground">Diff View</label>
<label class="flex items-center space-x-1 text-[10px]">
<input type="checkbox" id="designer-show-diff" class="rounded border-dynamic-border w-3 h-3">
<span class="text-dynamic-muted-foreground">Show</span>
</label>
</div>
<div id="diff-fields" class="space-y-2 hidden">
<div class="grid grid-cols-2 gap-2">
<div>
<label class="text-[10px] text-dynamic-muted-foreground">Filename</label>
<input type="text" id="designer-diff-filename"
class="w-full p-1.5 bg-dynamic-background border border-dynamic-border rounded text-[10px] focus-ring touch-target font-mono"
placeholder="file.tsx">
</div>
<div>
<label class="text-[10px] text-dynamic-muted-foreground">Flash</label>
<select id="designer-flash-button" class="w-full p-1.5 bg-dynamic-background border border-dynamic-border rounded text-[10px] focus-ring touch-target">
<option value="none">None</option>
<option value="accept">Accept</option>
<option value="reject">Reject</option>
</select>
</div>
</div>
<div>
<label class="text-[10px] text-dynamic-muted-foreground">Old Text</label>
<textarea id="designer-diff-old"
class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-[10px] resize-none focus-ring custom-scrollbar touch-target font-mono"
rows="3"
placeholder="// Original code..."></textarea>
</div>
<div>
<label class="text-[10px] text-dynamic-muted-foreground">New Text</label>
<textarea id="designer-diff-new"
class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-[10px] resize-none focus-ring custom-scrollbar touch-target font-mono"
rows="3"
placeholder="// Updated code..."></textarea>
</div>
<p class="text-[9px] text-dynamic-muted-foreground opacity-50">
Keep short for URL sharing.
</p>
</div>
</div>
</div>
</div> </div>
</div> </div>
<!-- Generate Buttons - Fixed at bottom --> <!-- Generate Buttons - Fixed at bottom -->
<div class="p-4 lg:p-6 space-y-3 border-t border-dynamic-border bg-dynamic-muted lg:pb-0"> <div class="p-3 lg:p-4 space-y-2 border-t border-dynamic-border bg-dynamic-muted">
<button id="generate-embed" class="w-full bg-dynamic-primary text-white p-3 lg:p-2 rounded-lg text-sm font-medium hover:opacity-90 transition-opacity touch-target"> <button id="generate-embed" class="w-full bg-dynamic-primary text-white p-2.5 rounded text-xs font-medium hover:opacity-90 transition-opacity touch-target">
Generate Embed Code Generate Embed Code
</button> </button>
<button id="reset-settings" class="text-[11px] text-dynamic-muted-foreground hover:text-dynamic-foreground transition-colors opacity-60 hover:opacity-100 touch-target"> <button id="reset-settings" class="text-[10px] text-dynamic-muted-foreground hover:text-dynamic-foreground transition-colors opacity-50 hover:opacity-100 touch-target">
Reset settings Reset settings
</button> </button>
</div> </div>

View File

@ -26,7 +26,12 @@ class EmbedPreview {
lightColor: this.params.lightColor || '#3b82f6', lightColor: this.params.lightColor || '#3b82f6',
darkColor: this.params.darkColor || '#60a5fa', darkColor: this.params.darkColor || '#60a5fa',
themeMode: this.params.themeMode || 'auto', themeMode: this.params.themeMode || 'auto',
filetree: this.params.filetree ? decodeURIComponent(this.params.filetree).split('\n').filter(f => f.trim()) : [] filetree: this.params.filetree ? decodeURIComponent(this.params.filetree).split('\n').filter(f => f.trim()) : [],
showDiff: this.params.showDiff === 'true',
diffFilename: this.params.diffFilename || '',
diffOldText: this.params.diffOldText ? decodeURIComponent(this.params.diffOldText) : '',
diffNewText: this.params.diffNewText ? decodeURIComponent(this.params.diffNewText) : '',
flashButton: this.params.flashButton || 'none'
}; };
} }
@ -34,6 +39,7 @@ class EmbedPreview {
this.setupColors(); this.setupColors();
this.setupElements(); this.setupElements();
this.render(); this.render();
this.setupResizeListener();
} }
setupColors() { setupColors() {
@ -187,6 +193,7 @@ class EmbedPreview {
} }
render() { render() {
this.renderDiffView();
this.renderContextPills(); this.renderContextPills();
this.renderPromptText(); this.renderPromptText();
this.renderSettingsPills(); this.renderSettingsPills();
@ -315,21 +322,21 @@ class EmbedPreview {
createSettingPill(text, type) { createSettingPill(text, type) {
const pill = document.createElement('div'); const pill = document.createElement('div');
pill.className = 'rounded-full text-xs font-medium flex items-center gap-1.5'; pill.className = 'rounded-full text-[10px] font-medium flex items-center gap-1';
let icon = ''; let icon = '';
// Use different styling based on pill type // Use different styling based on pill type
if (type === 'mode') { if (type === 'mode') {
// Mode pill keeps the background // Mode pill keeps the background with reduced padding
if (this.isDarkMode) { if (this.isDarkMode) {
pill.className += ' px-3 py-2 bg-dynamic-primary/20 text-dynamic-foreground border border-dynamic-primary/30'; pill.className += ' px-2 py-1 bg-dynamic-primary/20 text-dynamic-foreground border border-dynamic-primary/30';
} else { } else {
pill.className += ' px-3 py-2 bg-dynamic-primary/10 text-dynamic-foreground border border-dynamic-primary/20'; pill.className += ' px-2 py-1 bg-dynamic-primary/10 text-dynamic-foreground border border-dynamic-primary/20';
} }
} else { } else {
// Model, thinking, and max pills only have text color // Model, thinking, and max pills only have text color
pill.className += ' pl-1 text-dynamic-primary'; pill.className += ' pl-0.5 text-dynamic-primary';
} }
switch (type) { switch (type) {
@ -341,16 +348,16 @@ class EmbedPreview {
const modeType = text.toLowerCase(); const modeType = text.toLowerCase();
switch (modeType) { switch (modeType) {
case 'agent': case 'agent':
icon = '<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M18.18 8.04c-.78-.84-1.92-1.54-3.18-1.54-1.44 0-2.7.93-3.48 2.25-.78-1.32-2.04-2.25-3.48-2.25-1.26 0-2.4.7-3.18 1.54-.87.94-1.36 2.11-1.36 3.46 0 1.35.49 2.52 1.36 3.46.78.84 1.92 1.54 3.18 1.54 1.44 0 2.7-.93 3.48-2.25.78 1.32 2.04 2.25 3.48 2.25 1.26 0 2.4-.7 3.18-1.54.87-.94 1.36-2.11 1.36-3.46 0-1.35-.49-2.52-1.36-3.46z"/></svg>'; icon = '<svg class="w-2.5 h-2.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M18.18 8.04c-.78-.84-1.92-1.54-3.18-1.54-1.44 0-2.7.93-3.48 2.25-.78-1.32-2.04-2.25-3.48-2.25-1.26 0-2.4.7-3.18 1.54-.87.94-1.36 2.11-1.36 3.46 0 1.35.49 2.52 1.36 3.46.78.84 1.92 1.54 3.18 1.54 1.44 0 2.7-.93 3.48-2.25.78 1.32 2.04 2.25 3.48 2.25 1.26 0 2.4-.7 3.18-1.54.87-.94 1.36-2.11 1.36-3.46 0-1.35-.49-2.52-1.36-3.46z"/></svg>';
break; break;
case 'chat': case 'chat':
icon = '<svg class="w-3 h-3" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z" clip-rule="evenodd"/></svg>'; icon = '<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z" clip-rule="evenodd"/></svg>';
break; break;
case 'manual': case 'manual':
icon = '<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 2v20M2 12h20"/><circle cx="12" cy="12" r="3" fill="currentColor"/></svg>'; icon = '<svg class="w-2.5 h-2.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 2v20M2 12h20"/><circle cx="12" cy="12" r="3" fill="currentColor"/></svg>';
break; break;
case 'cloud': case 'cloud':
icon = '<svg class="w-3 h-3" viewBox="0 0 20 20" fill="currentColor"><path d="M5.5 16a3.5 3.5 0 01-.369-6.98 4 4 0 117.753-1.977A4.5 4.5 0 1113.5 16h-8z"/></svg>'; icon = '<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor"><path d="M5.5 16a3.5 3.5 0 01-.369-6.98 4 4 0 117.753-1.977A4.5 4.5 0 1113.5 16h-8z"/></svg>';
break; break;
default: default:
icon = ''; icon = '';
@ -360,12 +367,12 @@ class EmbedPreview {
break; break;
case 'thinking': case 'thinking':
// Brain icon for thinking mode // Brain icon for thinking mode
icon = '<svg class="w-3 h-3" viewBox="0 0 24 24" fill="currentColor"><path d="M19.864 8.465a3.505 3.505 0 0 0-3.03-4.449A3.005 3.005 0 0 0 14 2a2.98 2.98 0 0 0-2 .78A2.98 2.98 0 0 0 10 2c-1.301 0-2.41.831-2.825 2.015a3.505 3.505 0 0 0-3.039 4.45A4.028 4.028 0 0 0 2 12c0 1.075.428 2.086 1.172 2.832A4.067 4.067 0 0 0 3 16c0 1.957 1.412 3.59 3.306 3.934A3.515 3.515 0 0 0 9.5 22c.979 0 1.864-.407 2.5-1.059A3.484 3.484 0 0 0 14.5 22a3.51 3.51 0 0 0 3.19-2.06 4.006 4.006 0 0 0 3.138-5.108A4.003 4.003 0 0 0 22 12a4.028 4.028 0 0 0-2.136-3.535zM9.5 20c-.711 0-1.33-.504-1.47-1.198L7.818 18H7c-1.103 0-2-.897-2-2 0-.352.085-.682.253-.981l.456-.816-.784-.51A2.019 2.019 0 0 1 4 12c0-.977.723-1.824 1.682-1.972l1.693-.26-1.059-1.346a1.502 1.502 0 0 1 1.498-2.39L9 6.207V5a1 1 0 0 1 2 0v13.5c0 .827-.673 1.5-1.5 1.5zm9.575-6.308-.784.51.456.816c.168.3.253.63.253.982 0 1.103-.897 2-2.05 2h-.818l-.162.802A1.502 1.502 0 0 1 14.5 20c-.827 0-1.5-.673-1.5-1.5V5c0-.552.448-1 1-1s1 .448 1 1.05v1.207l1.186-.225a1.502 1.502 0 0 1 1.498 2.39l-1.059 1.347 1.693.26A2.002 2.002 0 0 1 20 12c0 .683-.346 1.315-.925 1.692z"></path></svg>'; icon = '<svg class="w-2.5 h-2.5" viewBox="0 0 24 24" fill="currentColor"><path d="M19.864 8.465a3.505 3.505 0 0 0-3.03-4.449A3.005 3.005 0 0 0 14 2a2.98 2.98 0 0 0-2 .78A2.98 2.98 0 0 0 10 2c-1.301 0-2.41.831-2.825 2.015a3.505 3.505 0 0 0-3.039 4.45A4.028 4.028 0 0 0 2 12c0 1.075.428 2.086 1.172 2.832A4.067 4.067 0 0 0 3 16c0 1.957 1.412 3.59 3.306 3.934A3.515 3.515 0 0 0 9.5 22c.979 0 1.864-.407 2.5-1.059A3.484 3.484 0 0 0 14.5 22a3.51 3.51 0 0 0 3.19-2.06 4.006 4.006 0 0 0 3.138-5.108A4.003 4.003 0 0 0 22 12a4.028 4.028 0 0 0-2.136-3.535zM9.5 20c-.711 0-1.33-.504-1.47-1.198L7.818 18H7c-1.103 0-2-.897-2-2 0-.352.085-.682.253-.981l.456-.816-.784-.51A2.019 2.019 0 0 1 4 12c0-.977.723-1.824 1.682-1.972l1.693-.26-1.059-1.346a1.502 1.502 0 0 1 1.498-2.39L9 6.207V5a1 1 0 0 1 2 0v13.5c0 .827-.673 1.5-1.5 1.5zm9.575-6.308-.784.51.456.816c.168.3.253.63.253.982 0 1.103-.897 2-2.05 2h-.818l-.162.802A1.502 1.502 0 0 1 14.5 20c-.827 0-1.5-.673-1.5-1.5V5c0-.552.448-1 1-1s1 .448 1 1.05v1.207l1.186-.225a1.502 1.502 0 0 1 1.498 2.39l-1.059 1.347 1.693.26A2.002 2.002 0 0 1 20 12c0 .683-.346 1.315-.925 1.692z"></path></svg>';
pill.innerHTML = icon + '<span>' + text + '</span>'; pill.innerHTML = icon + '<span>' + text + '</span>';
break; break;
case 'max': case 'max':
// Lightning bolt icon for MAX mode // Lightning bolt icon for MAX mode
icon = '<svg class="w-3 h-3" viewBox="0 0 20 20" fill="currentColor"><path d="M11.3 1.046A1 1 0 0112 2v5h4a1 1 0 01.82 1.573l-7 10A1 1 0 018 18v-5H4a1 1 0 01-.82-1.573l7-10a1 1 0 011.12-.38z"/></svg>'; icon = '<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor"><path d="M11.3 1.046A1 1 0 0112 2v5h4a1 1 0 01.82 1.573l-7 10A1 1 0 018 18v-5H4a1 1 0 01-.82-1.573l7-10a1 1 0 011.12-.38z"/></svg>';
pill.innerHTML = icon + '<span>' + text + '</span>'; pill.innerHTML = icon + '<span>' + text + '</span>';
break; break;
} }
@ -373,8 +380,171 @@ class EmbedPreview {
return pill; return pill;
} }
setupResizeListener() {
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
this.renderDiffView();
}, 100);
});
}
renderDiffView() {
const diffView = document.getElementById('diff-view');
const diffViewInline = document.getElementById('diff-view-inline');
const promptText = document.getElementById('prompt-text');
if (!diffView || !diffViewInline) return;
const viewportHeight = window.innerHeight;
const isCompact = viewportHeight < 400;
if (this.config.showDiff && (this.config.diffOldText || this.config.diffNewText)) {
// Determine which view to show based on viewport height
if (isCompact) {
// Show floating diff view
diffView.classList.remove('hidden');
diffView.classList.add('diff-enter');
diffViewInline.classList.add('hidden');
this.setupDiffContent('diff-view');
} else {
// Show inline diff view
diffViewInline.classList.remove('hidden');
diffView.classList.add('hidden');
this.setupDiffContent('diff-view-inline');
// Reset margin when switching to inline view
if (promptText) {
promptText.style.marginTop = '0';
}
}
} else {
diffView.classList.add('hidden');
diffViewInline.classList.add('hidden');
// Reset margin when diff is hidden
if (promptText) {
promptText.style.marginTop = '0';
}
}
}
setupDiffContent(containerId) {
const container = document.getElementById(containerId);
if (!container) return;
// Count lines in old and new content
const oldLines = this.config.diffOldText ? this.config.diffOldText.split('\n').length : 0;
const newLines = this.config.diffNewText ? this.config.diffNewText.split('\n').length : 0;
// Set filename with line counts
const filenameElement = container.querySelector('[id$="-filename"]');
if (filenameElement) {
const filename = this.config.diffFilename || 'untitled';
filenameElement.innerHTML = `
<span>${filename}</span>
<span class="ml-2 text-[10px] font-mono">
<span class="text-green-600 dark:text-green-400">+${newLines}</span>
<span class="text-red-600 dark:text-red-400">-${oldLines}</span>
</span>
`;
}
// Set diff content
const oldContent = container.querySelector('[id$="-old-content"]');
const newContent = container.querySelector('[id$="-new-content"]');
if (oldContent) {
oldContent.textContent = this.config.diffOldText || '(empty)';
// Update colors based on dark mode
if (this.isDarkMode) {
oldContent.style.backgroundColor = 'rgba(127, 29, 29, 0.15)';
oldContent.style.color = '#fca5a5';
oldContent.style.borderColor = 'rgba(127, 29, 29, 0.3)';
} else {
oldContent.style.backgroundColor = 'rgba(254, 226, 226, 0.7)';
oldContent.style.color = '#b91c1c';
oldContent.style.borderColor = 'rgba(252, 165, 165, 0.5)';
}
}
if (newContent) {
newContent.textContent = this.config.diffNewText || '(empty)';
// Update colors based on dark mode
if (this.isDarkMode) {
newContent.style.backgroundColor = 'rgba(20, 83, 45, 0.15)';
newContent.style.color = '#86efac';
newContent.style.borderColor = 'rgba(20, 83, 45, 0.3)';
} else {
newContent.style.backgroundColor = 'rgba(220, 252, 231, 0.7)';
newContent.style.color = '#15803d';
newContent.style.borderColor = 'rgba(134, 239, 172, 0.5)';
}
}
// Apply flash animation to buttons if configured
const acceptButton = container.querySelector('[id$="-accept-diff"]');
const rejectButton = container.querySelector('[id$="-reject-diff"]');
if (this.config.flashButton === 'accept' && acceptButton) {
acceptButton.classList.add('flash-animation');
// Remove from reject button if it exists
if (rejectButton) rejectButton.classList.remove('flash-animation');
} else if (this.config.flashButton === 'reject' && rejectButton) {
rejectButton.classList.add('flash-animation');
// Remove from accept button if it exists
if (acceptButton) acceptButton.classList.remove('flash-animation');
} else {
// Remove animation from both if 'none'
if (acceptButton) acceptButton.classList.remove('flash-animation');
if (rejectButton) rejectButton.classList.remove('flash-animation');
}
// Setup toggle button for floating view only
if (containerId === 'diff-view') {
const toggleButton = container.querySelector('#toggle-diff');
const diffContent = container.querySelector('#diff-content');
const promptText = document.getElementById('prompt-text');
if (toggleButton && diffContent && promptText) {
// Remove existing listeners
const newToggleButton = toggleButton.cloneNode(true);
toggleButton.parentNode.replaceChild(newToggleButton, toggleButton);
// Set initial state - collapsed when compact
let isExpanded = false;
diffContent.style.maxHeight = '0';
diffContent.style.overflow = 'hidden';
newToggleButton.querySelector('svg').style.transform = 'rotate(-90deg)';
// Function to update prompt margin based on diff view height
const updatePromptMargin = () => {
const diffHeight = container.offsetHeight;
promptText.parentElement.style.paddingTop = `${diffHeight + 16}px`; // 16px for some breathing room
};
// Set initial margin
setTimeout(updatePromptMargin, 0);
newToggleButton.addEventListener('click', () => {
isExpanded = !isExpanded;
if (isExpanded) {
diffContent.style.maxHeight = diffContent.scrollHeight + 'px';
newToggleButton.querySelector('svg').style.transform = 'rotate(0deg)';
// Update margin after animation
setTimeout(updatePromptMargin, 300);
} else {
diffContent.style.maxHeight = '0';
diffContent.style.overflow = 'hidden';
newToggleButton.querySelector('svg').style.transform = 'rotate(-90deg)';
// Update margin after animation
setTimeout(updatePromptMargin, 300);
}
});
}
}
}
highlightMentions(text) { highlightMentions(text) {
return text.replace(/@(\w+)/g, '<span class="mention">@$1</span>'); return text.replaceAll(/@(https?:\/\/[^\s]+.*?|\w+\.\w+|\w+)/g, '<span class="mention">@$1</span>');
} }
capitalizeFirst(str) { capitalizeFirst(str) {

View File

@ -34,7 +34,12 @@ class EmbedDesigner {
height: this.params.height || savedConfig.height || '400', height: this.params.height || savedConfig.height || '400',
themeMode: this.params.themeMode || savedConfig.themeMode || 'auto', themeMode: this.params.themeMode || savedConfig.themeMode || 'auto',
filetree: this.params.filetree ? decodeURIComponent(this.params.filetree).split('\n').filter(f => f.trim()) : (savedConfig.filetree || []), filetree: this.params.filetree ? decodeURIComponent(this.params.filetree).split('\n').filter(f => f.trim()) : (savedConfig.filetree || []),
showFiletree: savedConfig.showFiletree !== undefined ? savedConfig.showFiletree : true showFiletree: savedConfig.showFiletree !== undefined ? savedConfig.showFiletree : true,
showDiff: this.params.showDiff === 'true' || savedConfig.showDiff || false,
diffFilename: this.params.diffFilename || savedConfig.diffFilename || '',
diffOldText: this.params.diffOldText ? decodeURIComponent(this.params.diffOldText) : (savedConfig.diffOldText || ''),
diffNewText: this.params.diffNewText ? decodeURIComponent(this.params.diffNewText) : (savedConfig.diffNewText || ''),
flashButton: this.params.flashButton || savedConfig.flashButton || 'none'
}; };
} }
@ -57,7 +62,12 @@ class EmbedDesigner {
height: '400', height: '400',
themeMode: 'auto', themeMode: 'auto',
filetree: [], filetree: [],
showFiletree: true showFiletree: true,
showDiff: false,
diffFilename: '',
diffOldText: '',
diffNewText: '',
flashButton: 'none'
}; };
} }
@ -81,7 +91,12 @@ class EmbedDesigner {
height: config.height || '400', height: config.height || '400',
themeMode: config.themeMode || 'auto', themeMode: config.themeMode || 'auto',
filetree: config.filetree || [], filetree: config.filetree || [],
showFiletree: config.showFiletree !== undefined ? config.showFiletree : true showFiletree: config.showFiletree !== undefined ? config.showFiletree : true,
showDiff: config.showDiff || false,
diffFilename: config.diffFilename || '',
diffOldText: config.diffOldText || '',
diffNewText: config.diffNewText || '',
flashButton: config.flashButton || 'none'
}; };
} }
} }
@ -177,6 +192,27 @@ class EmbedDesigner {
darkColorPicker.value = this.config.darkColor; darkColorPicker.value = this.config.darkColor;
darkColorText.value = this.config.darkColor; darkColorText.value = this.config.darkColor;
} }
// Set up diff view
const showDiffCheckbox = document.getElementById('designer-show-diff');
const diffFields = document.getElementById('diff-fields');
if (showDiffCheckbox) {
showDiffCheckbox.checked = this.config.showDiff || false;
if (diffFields) {
diffFields.classList.toggle('hidden', !this.config.showDiff);
}
}
// Set diff field values
const diffFilename = document.getElementById('designer-diff-filename');
const diffOldText = document.getElementById('designer-diff-old');
const diffNewText = document.getElementById('designer-diff-new');
const flashButton = document.getElementById('designer-flash-button');
if (diffFilename) diffFilename.value = this.config.diffFilename || '';
if (diffOldText) diffOldText.value = this.config.diffOldText || '';
if (diffNewText) diffNewText.value = this.config.diffNewText || '';
if (flashButton) flashButton.value = this.config.flashButton || 'none';
} }
updateThemeModeButtons() { updateThemeModeButtons() {
@ -195,7 +231,7 @@ class EmbedDesigner {
setupDesignerEvents() { setupDesignerEvents() {
// Form changes update preview // Form changes update preview
['designer-prompt', 'designer-context', 'designer-mode-select', 'designer-thinking', 'designer-max', 'designer-filetree', 'designer-show-filetree'].forEach(id => { ['designer-prompt', 'designer-context', 'designer-mode-select', 'designer-thinking', 'designer-max', 'designer-filetree', 'designer-show-filetree', 'designer-diff-filename', 'designer-diff-old', 'designer-diff-new', 'designer-flash-button'].forEach(id => {
const element = document.getElementById(id); const element = document.getElementById(id);
if (element) { if (element) {
element.addEventListener('input', () => this.updateConfigFromForm()); element.addEventListener('input', () => this.updateConfigFromForm());
@ -203,6 +239,18 @@ class EmbedDesigner {
} }
}); });
// Diff view toggle
const showDiffCheckbox = document.getElementById('designer-show-diff');
const diffFields = document.getElementById('diff-fields');
if (showDiffCheckbox) {
showDiffCheckbox.addEventListener('change', (e) => {
if (diffFields) {
diffFields.classList.toggle('hidden', !e.target.checked);
}
this.updateConfigFromForm();
});
}
// Theme mode buttons // Theme mode buttons
document.querySelectorAll('.theme-mode-btn').forEach(btn => { document.querySelectorAll('.theme-mode-btn').forEach(btn => {
btn.addEventListener('click', (e) => { btn.addEventListener('click', (e) => {
@ -329,7 +377,11 @@ class EmbedDesigner {
height: '400', height: '400',
themeMode: 'auto', themeMode: 'auto',
filetree: [], filetree: [],
showFiletree: true showFiletree: true,
showDiff: false,
diffFilename: '',
diffOldText: '',
diffNewText: ''
}; };
// Update UI to reflect defaults // Update UI to reflect defaults
this.setupDesignerElements(); this.setupDesignerElements();
@ -338,14 +390,37 @@ class EmbedDesigner {
} }
}); });
// Example buttons // Example select
document.getElementById('vibe-example').addEventListener('click', () => { const exampleSelect = document.getElementById('example-select');
this.loadVibeExample(); if (exampleSelect) {
}); exampleSelect.addEventListener('change', (e) => {
const example = e.target.value;
document.getElementById('chat-example').addEventListener('click', () => { if (example) {
this.loadChatExample(); switch (example) {
case 'vibe-coding':
this.loadVibeCodingExample();
break;
case 'vibe-coding-diff':
this.loadVibeCodingDiffExample();
break;
case 'chatgpt':
this.loadChatGPTExample();
break;
case 'claude':
this.loadClaudeExample();
break;
case 'image-analysis':
this.loadImageAnalysisExample();
break;
case 'api-design':
this.loadAPIDesignExample();
break;
}
// Reset select to placeholder after loading
exampleSelect.value = '';
}
}); });
}
// Modal events // Modal events
document.getElementById('close-modal').addEventListener('click', () => this.hideEmbedModal()); document.getElementById('close-modal').addEventListener('click', () => this.hideEmbedModal());
@ -383,7 +458,12 @@ class EmbedDesigner {
height: heightSlider ? heightSlider.value : '400', height: heightSlider ? heightSlider.value : '400',
themeMode: this.config.themeMode || 'auto', themeMode: this.config.themeMode || 'auto',
filetree: document.getElementById('designer-filetree').value.split('\n').map(f => f.trim()).filter(f => f), filetree: document.getElementById('designer-filetree').value.split('\n').map(f => f.trim()).filter(f => f),
showFiletree: document.getElementById('designer-show-filetree').checked showFiletree: document.getElementById('designer-show-filetree').checked,
showDiff: document.getElementById('designer-show-diff').checked,
diffFilename: document.getElementById('designer-diff-filename').value,
diffOldText: document.getElementById('designer-diff-old').value,
diffNewText: document.getElementById('designer-diff-new').value,
flashButton: document.getElementById('designer-flash-button').value
}; };
this.updatePreview(); this.updatePreview();
@ -457,6 +537,20 @@ class EmbedDesigner {
if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor); if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor);
if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode); if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode);
if (this.config.showFiletree && this.config.filetree && this.config.filetree.length > 0) params.set('filetree', encodeURIComponent(this.config.filetree.join('\n'))); if (this.config.showFiletree && this.config.filetree && this.config.filetree.length > 0) params.set('filetree', encodeURIComponent(this.config.filetree.join('\n')));
if (this.config.showDiff) {
params.set('showDiff', 'true');
if (this.config.diffFilename) params.set('diffFilename', this.config.diffFilename);
if (this.config.flashButton && this.config.flashButton !== 'none') params.set('flashButton', this.config.flashButton);
// Truncate diff text if too long to prevent URL length issues
if (this.config.diffOldText) {
const truncated = this.config.diffOldText.substring(0, 100);
params.set('diffOldText', encodeURIComponent(truncated)+'...');
}
if (this.config.diffNewText) {
const truncated = this.config.diffNewText.substring(0, 100);
params.set('diffNewText', encodeURIComponent(truncated)+'...');
}
}
params.set('preview', 'true'); params.set('preview', 'true');
return `/embed-preview/?${params.toString()}`; return `/embed-preview/?${params.toString()}`;
@ -474,6 +568,20 @@ class EmbedDesigner {
if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor); if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor);
if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode); if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode);
if (this.config.showFiletree && this.config.filetree && this.config.filetree.length > 0) params.set('filetree', encodeURIComponent(this.config.filetree.join('\n'))); if (this.config.showFiletree && this.config.filetree && this.config.filetree.length > 0) params.set('filetree', encodeURIComponent(this.config.filetree.join('\n')));
if (this.config.showDiff) {
params.set('showDiff', 'true');
if (this.config.diffFilename) params.set('diffFilename', this.config.diffFilename);
if (this.config.flashButton && this.config.flashButton !== 'none') params.set('flashButton', this.config.flashButton);
// Truncate diff text if too long to prevent URL length issues
if (this.config.diffOldText) {
const truncated = this.config.diffOldText.substring(0, 150);
params.set('diffOldText', encodeURIComponent(truncated));
}
if (this.config.diffNewText) {
const truncated = this.config.diffNewText.substring(0, 150);
params.set('diffNewText', encodeURIComponent(truncated));
}
}
return `${window.location.origin}/embed-preview/?${params.toString()}`; return `${window.location.origin}/embed-preview/?${params.toString()}`;
} }
@ -554,42 +662,26 @@ class EmbedDesigner {
}, duration); }, duration);
} }
loadVibeExample() { loadVibeCodingExample() {
// Vibe coding example WITHOUT diff
// Set vibe coding example values // Set vibe coding example values
document.getElementById('designer-prompt').value = document.getElementById('designer-prompt').value =
`I'm working on a React e-commerce app. The current ProductList component is getting messy with too much logic. `Refactor my React ProductList component:
- Extract filtering logic to custom hook
- Split into smaller components
- Add TypeScript types
- Implement virtualization
Can you help me refactor it to: Currently handles display, filtering, sorting, and pagination in one file.
1. Extract the filtering logic into a custom hook
2. Split the large component into smaller, reusable pieces
3. Add proper TypeScript types
4. Implement virtualization for better performance with large product lists
The component currently handles products display, filtering by category/price, sorting, and pagination all in one file. I want a cleaner architecture.`; @web Check React Query docs for data fetching patterns.`;
document.getElementById('designer-context').value = '@codebase, ProductList.tsx, components/, hooks/'; document.getElementById('designer-context').value = '@codebase, ProductList.tsx';
document.getElementById('designer-filetree').value = document.getElementById('designer-filetree').value =
`src/ `src/components/ProductList.tsx*
src/components/
src/components/ProductList.tsx*
src/components/ProductCard.tsx src/components/ProductCard.tsx
src/components/ProductGrid.tsx
src/components/Filters/
src/components/Filters/CategoryFilter.tsx
src/components/Filters/PriceRangeFilter.tsx
src/components/Filters/index.tsx
src/hooks/
src/hooks/useProducts.ts src/hooks/useProducts.ts
src/hooks/useFilters.ts src/types/product.ts`;
src/hooks/useInfiniteScroll.ts
src/types/
src/types/product.ts
src/api/
src/api/products.ts
src/utils/
src/utils/formatters.ts
package.json
tsconfig.json`;
// Set vibe coding settings // Set vibe coding settings
document.getElementById('designer-model').value = 'Claude 4 Opus'; document.getElementById('designer-model').value = 'Claude 4 Opus';
@ -618,30 +710,98 @@ tsconfig.json`;
this.config.themeMode = 'dark'; this.config.themeMode = 'dark';
this.updateThemeModeButtons(); this.updateThemeModeButtons();
// No diff for this example
document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden');
document.getElementById('designer-flash-button').value = 'none';
// Update config from form // Update config from form
this.updateConfigFromForm(); this.updateConfigFromForm();
this.showNotification('Vibe coding example loaded!'); this.showNotification('Vibe coding example loaded!');
} }
loadChatExample() { loadVibeCodingDiffExample() {
// Set chat example values // Vibe coding WITH diff - user giving feedback on suggested changes
document.getElementById('designer-prompt').value =
`Actually, don't use Error | null for the error state. Instead:
- Create a custom ApiError type with code, message, and retry()
- Add an AbortController for request cancellation
- Include error boundary integration
- Show me how to handle network vs API errors differently
Also add a refetch function that the UI can call.`;
document.getElementById('designer-context').value = '@codebase, useProducts.ts';
document.getElementById('designer-filetree').value =
`src/hooks/useProducts.ts*
src/types/errors.ts
src/utils/api.ts`;
// Set same settings as regular vibe coding
document.getElementById('designer-model').value = 'Claude 4 Opus';
document.getElementById('designer-mode-select').value = 'agent';
document.getElementById('designer-thinking').checked = true;
document.getElementById('designer-max').checked = true;
document.getElementById('designer-show-filetree').checked = true;
// Set height and colors
const heightSlider = document.getElementById('designer-height');
const heightValue = document.getElementById('height-value');
if (heightSlider) {
heightSlider.value = '500';
if (heightValue) {
heightValue.textContent = '500';
}
}
document.getElementById('designer-light-color').value = '#8b5cf6';
document.getElementById('designer-light-color-text').value = '#8b5cf6';
document.getElementById('designer-dark-color').value = '#a78bfa';
document.getElementById('designer-dark-color-text').value = '#a78bfa';
this.config.themeMode = 'dark';
this.updateThemeModeButtons();
// Add diff view showing the previous suggestion
document.getElementById('designer-show-diff').checked = true;
document.getElementById('diff-fields').classList.remove('hidden');
document.getElementById('designer-diff-filename').value = 'useProducts.ts';
document.getElementById('designer-diff-old').value =
`const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);`;
document.getElementById('designer-diff-new').value =
`const [products, setProducts] = useState<Product[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);`;
// Flash accept button for this example with diff
document.getElementById('designer-flash-button').value = 'accept';
// Update config from form
this.updateConfigFromForm();
this.showNotification('Vibe coding with diff loaded!');
}
loadChatGPTExample() {
// ChatGPT example with green colors
document.getElementById('designer-prompt').value = document.getElementById('designer-prompt').value =
`I'm planning a dinner party for 8 people this weekend. One person is vegetarian, another is gluten-free, and I want to make something impressive but not too complicated. `I'm planning a dinner party for 8 people this weekend. One person is vegetarian, another is gluten-free, and I want to make something impressive but not too complicated.
Can you suggest a menu with appetizers, main course, and dessert that would work for everyone? I have about 4 hours to prepare everything and a moderate cooking skill level.`; Can you suggest a menu with appetizers, main course, and dessert that would work for everyone? I have about 4 hours to prepare everything and a moderate cooking skill level.`;
document.getElementById('designer-context').value = '#Screenshot Kitchen Layout.png, Recipes Collection.pdf'; document.getElementById('designer-context').value = '#Kitchen Layout.png, #Pantry Inventory.jpg';
document.getElementById('designer-filetree').value = ''; document.getElementById('designer-filetree').value = '';
// Set chat settings // ChatGPT settings
document.getElementById('designer-model').value = 'GPT 4o'; document.getElementById('designer-model').value = 'GPT 4o';
document.getElementById('designer-mode-select').value = 'chat'; document.getElementById('designer-mode-select').value = 'chat';
document.getElementById('designer-thinking').checked = false; document.getElementById('designer-thinking').checked = false;
document.getElementById('designer-max').checked = false; document.getElementById('designer-max').checked = false;
document.getElementById('designer-show-filetree').checked = false; document.getElementById('designer-show-filetree').checked = false;
// Set height and colors for chat example // Set height and colors for ChatGPT
const heightSlider = document.getElementById('designer-height'); const heightSlider = document.getElementById('designer-height');
const heightValue = document.getElementById('height-value'); const heightValue = document.getElementById('height-value');
if (heightSlider) { if (heightSlider) {
@ -651,20 +811,188 @@ Can you suggest a menu with appetizers, main course, and dessert that would work
} }
} }
// Set friendly color scheme (green) // ChatGPT green color scheme
document.getElementById('designer-light-color').value = '#10b981'; document.getElementById('designer-light-color').value = '#10b981';
document.getElementById('designer-light-color-text').value = '#10b981'; document.getElementById('designer-light-color-text').value = '#10b981';
document.getElementById('designer-dark-color').value = '#34d399'; document.getElementById('designer-dark-color').value = '#34d399';
document.getElementById('designer-dark-color-text').value = '#34d399'; document.getElementById('designer-dark-color-text').value = '#34d399';
// Set light theme mode for chat // Light theme for ChatGPT
this.config.themeMode = 'light'; this.config.themeMode = 'light';
this.updateThemeModeButtons(); this.updateThemeModeButtons();
// Clear diff view
document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden');
document.getElementById('designer-flash-button').value = 'none';
// Update config from form // Update config from form
this.updateConfigFromForm(); this.updateConfigFromForm();
this.showNotification('Chat example loaded!'); this.showNotification('ChatGPT example loaded!');
}
loadClaudeExample() {
// Claude example with orange colors - daily usage
document.getElementById('designer-prompt').value =
`Help me write a professional email to decline a job offer while keeping the door open for future opportunities.
Context:
- Received offer from TechCorp as Senior Engineer
- Great team and compensation, but role doesn't align with my career goals
- Want to maintain good relationship with the hiring manager Sarah
- Interested in their upcoming ML team expansion next year
Keep it warm but professional, around 150-200 words.`;
document.getElementById('designer-context').value = '';
document.getElementById('designer-filetree').value = '';
// Claude settings
document.getElementById('designer-model').value = 'Claude 3.7 Sonnet';
document.getElementById('designer-mode-select').value = 'chat';
document.getElementById('designer-thinking').checked = false;
document.getElementById('designer-max').checked = false;
document.getElementById('designer-show-filetree').checked = false;
// Set height and colors for Claude
const heightSlider = document.getElementById('designer-height');
const heightValue = document.getElementById('height-value');
if (heightSlider) {
heightSlider.value = '350';
if (heightValue) {
heightValue.textContent = '350';
}
}
// Claude orange color scheme
document.getElementById('designer-light-color').value = '#f97316';
document.getElementById('designer-light-color-text').value = '#f97316';
document.getElementById('designer-dark-color').value = '#fb923c';
document.getElementById('designer-dark-color-text').value = '#fb923c';
// Auto theme for Claude
this.config.themeMode = 'auto';
this.updateThemeModeButtons();
// Clear diff view
document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden');
document.getElementById('designer-flash-button').value = 'none';
// Update config from form
this.updateConfigFromForm();
this.showNotification('Claude example loaded!');
}
loadImageAnalysisExample() {
// Image analysis example
document.getElementById('designer-prompt').value =
`Analyze these UI screenshots and provide detailed feedback on:
- Visual hierarchy and layout
- Color scheme and contrast
- Typography choices
- Accessibility concerns
- Mobile responsiveness issues`;
document.getElementById('designer-context').value = '#Homepage Desktop.png, #Homepage Mobile.png, #Dashboard View.jpg';
document.getElementById('designer-filetree').value = '';
// Image analysis settings
document.getElementById('designer-model').value = 'GPT 4.1';
document.getElementById('designer-mode-select').value = 'chat';
document.getElementById('designer-thinking').checked = false;
document.getElementById('designer-max').checked = false;
document.getElementById('designer-show-filetree').checked = false;
// Set height and colors
const heightSlider = document.getElementById('designer-height');
const heightValue = document.getElementById('height-value');
if (heightSlider) {
heightSlider.value = '400';
if (heightValue) {
heightValue.textContent = '400';
}
}
// Pink/purple color scheme for design
document.getElementById('designer-light-color').value = '#ec4899';
document.getElementById('designer-light-color-text').value = '#ec4899';
document.getElementById('designer-dark-color').value = '#f472b6';
document.getElementById('designer-dark-color-text').value = '#f472b6';
// Light theme
this.config.themeMode = 'light';
this.updateThemeModeButtons();
// Clear diff view
document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden');
document.getElementById('designer-flash-button').value = 'none';
// Update config from form
this.updateConfigFromForm();
this.showNotification('Image analysis example loaded!');
}
loadAPIDesignExample() {
// API design example
document.getElementById('designer-prompt').value =
`Design a RESTful API for a task management system with:
- User authentication
- Project and task CRUD operations
- Team collaboration features
- Real-time notifications
Include endpoint definitions, request/response schemas, and error handling patterns.`;
document.getElementById('designer-context').value = '@Web, openapi.yaml';
document.getElementById('designer-filetree').value =
`api/
api/v1/
api/v1/users/
api/v1/projects/
api/v1/tasks/
docs/openapi.yaml*`;
// API design settings
document.getElementById('designer-model').value = 'Claude 4 Opus';
document.getElementById('designer-mode-select').value = 'manual';
document.getElementById('designer-thinking').checked = true;
document.getElementById('designer-max').checked = false;
document.getElementById('designer-show-filetree').checked = true;
// Set height and colors
const heightSlider = document.getElementById('designer-height');
const heightValue = document.getElementById('height-value');
if (heightSlider) {
heightSlider.value = '500';
if (heightValue) {
heightValue.textContent = '500';
}
}
// Blue color scheme for API
document.getElementById('designer-light-color').value = '#3b82f6';
document.getElementById('designer-light-color-text').value = '#3b82f6';
document.getElementById('designer-dark-color').value = '#60a5fa';
document.getElementById('designer-dark-color-text').value = '#60a5fa';
// Dark theme for technical
this.config.themeMode = 'dark';
this.updateThemeModeButtons();
// Clear diff view
document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden');
document.getElementById('designer-flash-button').value = 'none';
// Update config from form
this.updateConfigFromForm();
this.showNotification('API design example loaded!');
} }
} }

View File

@ -249,7 +249,7 @@ body.dark-mode .designer-panel {
/* Adjust the scrollable content area */ /* Adjust the scrollable content area */
.designer-panel > .flex-1 { .designer-panel > .flex-1 {
padding-top: calc(var(--header-height) + 1rem); padding-top: calc(var(--header-height));
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;