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="icon" type="image/x-icon" href="{{ '/favicon.ico' | relative_url }}">
<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>
<body class="bg-dynamic-background text-dynamic-foreground overflow-hidden">
<!-- Viewer Mode -->
@ -39,9 +79,9 @@
</div>
<!-- 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 -->
<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 -->
<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>
</div>
<!-- Main Prompt Interface - Full Height -->
<div class="flex-1 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-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>
<!-- Main Prompt Interface with Floating Diff - Flex-1 to take remaining space -->
<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-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>
</div>
</div>
<!-- Bottom Bar -->
<div class="flex justify-between items-center gap-2 mt-1 sm:mt-2 flex-shrink-0">
<!-- Settings Pills -->
<div id="settings-pills" class="flex gap-1 sm:gap-2 flex-wrap flex-1 min-w-0"></div>
<!-- Bottom Bar - Always visible with reduced height -->
<div class="flex justify-between items-center gap-2 mt-1 pt-1 flex-shrink-0">
<!-- Settings Pills - Smaller -->
<div id="settings-pills" class="flex gap-1 flex-wrap flex-1 min-w-0"></div>
<!-- Send Button (circular with arrow up) -->
<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">
<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">
<!-- Send Button - Smaller -->
<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="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"/>
</svg>
</button>

View File

@ -27,8 +27,30 @@
<link rel="stylesheet" href="{{ '/embed-style.css' | relative_url }}">
<link rel="icon" type="image/x-icon" href="{{ '/favicon.ico' | relative_url }}">
<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>
<body class="bg-dynamic-background text-dynamic-foreground overflow-hidden">
<body class="bg-dynamic-background text-dynamic-foreground overflow-hidden checkerboard-bg">
<!-- Site Header -->
<header class="site-header">
<div class="header-left">
@ -37,6 +59,13 @@
<p class="site-slogan sm:hidden">Design and share AI prompts</p>
</div>
<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">
<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>
@ -49,127 +78,144 @@
<!-- 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="flex-1 overflow-y-auto custom-scrollbar">
<div class="p-4 lg:p-6 lg:pt-0 space-y-4">
<!-- Context -->
<div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">Context</label>
<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"
placeholder="file.py, @web, @codebase, #image">
</div>
<div class="p-3 lg:p-4 space-y-3">
<!-- Prompt Text -->
<!-- CONTENT SECTION -->
<div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">Prompt Text</label>
<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"
rows="8"
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>
<h3 class="text-xs font-semibold text-dynamic-foreground uppercase tracking-wider border-b border-dynamic-border pb-1">Prompt</h3>
<!-- Model & Mode -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">Model</label>
<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">
<option value="o3">o3</option>
<option value="GPT 4.1">GPT 4.1</option>
<option value="GPT 4o" selected>GPT 4o</option>
<option value="Claude 3.7 Sonnet">Claude 3.7 Sonnet</option>
<option value="Claude 4 Sonnet">Claude 4 Sonnet</option>
<option value="Claude 4 Opus">Claude 4 Opus</option>
<option value="Gemini 2.5 Pro">Gemini 2.5 Pro</option>
<option value="DeepSeek R1">DeepSeek R1</option>
<option value="custom">[Custom]</option>
</select>
<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"
placeholder="Enter custom model name">
</div>
<div class="space-y-2">
<label class="text-sm 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">
<option value="chat">Chat</option>
<option value="agent">Agent</option>
<option value="manual">Manual</option>
<option value="cloud">Cloud</option>
<!-- 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>
</div>
<!-- Options -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 justify-items-start">
<label class="flex items-center space-x-2 touch-target">
<input type="checkbox" id="designer-thinking" class="rounded border-dynamic-border w-4 h-4">
<span class="text-sm text-dynamic-muted-foreground">Thinking</span>
</label>
<label class="flex items-center space-x-2 touch-target">
<input type="checkbox" id="designer-max" class="rounded border-dynamic-border w-4 h-4">
<span class="text-sm text-dynamic-muted-foreground">MAX mode</span>
</label>
</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"
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">
</div>
<!-- Theme Mode Selector -->
<div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">Theme Mode</label>
<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">
Light
</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">
Dark
</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">
Auto
</button>
<!-- Prompt Text -->
<div class="space-y-1">
<label class="text-xs font-medium text-dynamic-muted-foreground">Prompt Text</label>
<textarea id="designer-prompt"
class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-xs resize-none focus-ring custom-scrollbar touch-target"
rows="6"
placeholder="Enter your prompt..."></textarea>
</div>
</div>
<!-- Color Scheme -->
<div class="space-y-3">
<label class="text-sm font-medium text-dynamic-muted-foreground">Color Scheme</label>
<!-- AI SETTINGS 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">AI Settings</h3>
<!-- 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="GPT 4.1">GPT 4.1</option>
<option value="GPT 4o" selected>GPT 4o</option>
<option value="Claude 3.7 Sonnet">Claude 3.7 Sonnet</option>
<option value="Claude 4 Sonnet">Claude 4 Sonnet</option>
<option value="Claude 4 Opus">Claude 4 Opus</option>
<option value="Gemini 2.5 Pro">Gemini 2.5 Pro</option>
<option value="DeepSeek R1">DeepSeek R1</option>
<option value="custom">[Custom]</option>
</select>
<input type="text" id="designer-custom-model"
class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded text-xs focus-ring hidden touch-target"
placeholder="Custom model">
</div>
<div class="space-y-1">
<label class="text-xs font-medium text-dynamic-muted-foreground">Mode</label>
<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="agent">Agent</option>
<option value="manual">Manual</option>
<option value="cloud">Cloud</option>
</select>
</div>
</div>
<!-- Options -->
<div class="flex gap-4">
<label class="flex items-center space-x-1.5 touch-target">
<input type="checkbox" id="designer-thinking" class="rounded border-dynamic-border w-3.5 h-3.5">
<span class="text-xs text-dynamic-muted-foreground">Thinking</span>
</label>
<label class="flex items-center space-x-1.5 touch-target">
<input type="checkbox" id="designer-max" class="rounded border-dynamic-border w-3.5 h-3.5">
<span class="text-xs text-dynamic-muted-foreground">MAX mode</span>
</label>
</div>
</div>
<!-- APPEARANCE 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">Appearance</h3>
<!-- 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
</button>
<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
</button>
<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
</button>
</div>
</div>
<!-- Color Presets -->
<div class="space-y-2">
<label class="text-xs text-dynamic-muted-foreground uppercase tracking-wider">Presets</label>
<div class="grid grid-cols-4 gap-2">
<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"
<div class="space-y-1">
<label class="text-xs font-medium text-dynamic-muted-foreground">Colors</label>
<div class="grid grid-cols-8 gap-1">
<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"
style="background: linear-gradient(135deg, #6b7280 50%, #e5e7eb 50%)"
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"
style="background: linear-gradient(135deg, #1f2937 50%, #9ca3af 50%)"
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"
style="background: linear-gradient(135deg, #64748b 50%, #94a3b8 50%)"
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"
style="background: linear-gradient(135deg, #3b82f6 50%, #60a5fa 50%)"
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"
style="background: linear-gradient(135deg, #10b981 50%, #34d399 50%)"
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"
style="background: linear-gradient(135deg, #8b5cf6 50%, #a78bfa 50%)"
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"
style="background: linear-gradient(135deg, #f97316 50%, #fb923c 50%)"
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"
style="background: linear-gradient(135deg, #ec4899 50%, #f472b6 50%)"
title="Pink"></button>
@ -177,88 +223,115 @@
</div>
<!-- Custom Colors -->
<div class="space-y-2">
<label class="text-xs text-dynamic-muted-foreground uppercase tracking-wider">Custom Colors</label>
<div class="space-y-3">
<div class="flex items-center gap-3">
<div class="flex items-center gap-2 flex-1">
<div class="relative">
<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 class="grid grid-cols-2 gap-2">
<div class="space-y-1">
<label class="text-[10px] text-dynamic-muted-foreground">Light</label>
<div class="flex gap-1">
<input type="color" id="designer-light-color" value="#3b82f6" class="w-6 h-6 rounded border border-dynamic-border cursor-pointer touch-target">
<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}$">
</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 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>
<!-- Height -->
<div class="space-y-1">
<label class="text-xs font-medium text-dynamic-muted-foreground">
Height: <span id="height-value" class="text-dynamic-foreground">400</span>px
</label>
<input type="range" id="designer-height"
class="w-full h-1.5 bg-dynamic-muted rounded-lg appearance-none cursor-pointer slider"
min="200" max="800" value="400" step="50">
<div class="flex justify-between text-[10px] text-dynamic-muted-foreground opacity-60">
<span>200px</span>
<span>800px</span>
</div>
</div>
</div>
<!-- Height -->
<!-- FEATURES SECTION -->
<div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">
Height: <span id="height-value" class="text-dynamic-foreground">400</span>px
</label>
<input type="range" id="designer-height"
class="w-full h-2 bg-dynamic-muted rounded-lg appearance-none cursor-pointer slider"
min="200" max="800" value="400" step="50">
<div class="flex justify-between text-xs text-dynamic-muted-foreground">
<span>200px</span>
<span>800px</span>
</div>
</div>
<h3 class="text-xs font-semibold text-dynamic-foreground uppercase tracking-wider border-b border-dynamic-border pb-1">Features</h3>
<!-- File Tree -->
<div class="space-y-2">
<div class="flex items-center justify-between">
<label class="text-sm font-medium text-dynamic-muted-foreground">File Tree</label>
<label class="flex items-center space-x-1.5 text-xs">
<input type="checkbox" id="designer-show-filetree" class="rounded border-dynamic-border w-3.5 h-3.5" checked>
<span class="text-dynamic-muted-foreground">Show in preview</span>
</label>
<!-- File Tree -->
<div class="space-y-1">
<div class="flex items-center justify-between">
<label class="text-xs font-medium text-dynamic-muted-foreground">File Tree</label>
<label class="flex items-center space-x-1 text-[10px]">
<input type="checkbox" id="designer-show-filetree" class="rounded border-dynamic-border w-3 h-3" checked>
<span class="text-dynamic-muted-foreground">Show</span>
</label>
</div>
<textarea id="designer-filetree"
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="4"
placeholder="index.html&#10;styles/main.css&#10;scripts/app.js&#10;components/header.vue"></textarea>
<p class="text-[9px] text-dynamic-muted-foreground opacity-60">
One per line. Use / for folders. Add * to highlight.
</p>
</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>
<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"
rows="6"
placeholder="Enter files (one per line):&#10;index.html&#10;styles/main.css&#10;scripts/app.js&#10;components/header.vue"></textarea>
<p class="text-xs text-dynamic-muted-foreground">
Enter filenames one per line. Use forward slashes for folders (e.g., folder/file.js).
Add * at the end of a filename to highlight it (e.g., main.js*)
</p>
</div>
</div>
</div>
<!-- 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">
<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">
<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-2.5 rounded text-xs font-medium hover:opacity-90 transition-opacity touch-target">
Generate Embed Code
</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
</button>
</div>

View File

@ -26,7 +26,12 @@ class EmbedPreview {
lightColor: this.params.lightColor || '#3b82f6',
darkColor: this.params.darkColor || '#60a5fa',
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.setupElements();
this.render();
this.setupResizeListener();
}
setupColors() {
@ -187,6 +193,7 @@ class EmbedPreview {
}
render() {
this.renderDiffView();
this.renderContextPills();
this.renderPromptText();
this.renderSettingsPills();
@ -315,21 +322,21 @@ class EmbedPreview {
createSettingPill(text, type) {
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 = '';
// Use different styling based on pill type
if (type === 'mode') {
// Mode pill keeps the background
// Mode pill keeps the background with reduced padding
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 {
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 {
// 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) {
@ -341,16 +348,16 @@ class EmbedPreview {
const modeType = text.toLowerCase();
switch (modeType) {
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;
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;
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;
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;
default:
icon = '';
@ -360,12 +367,12 @@ class EmbedPreview {
break;
case 'thinking':
// 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>';
break;
case 'max':
// 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>';
break;
}
@ -373,8 +380,171 @@ class EmbedPreview {
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) {
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) {

View File

@ -34,7 +34,12 @@ class EmbedDesigner {
height: this.params.height || savedConfig.height || '400',
themeMode: this.params.themeMode || savedConfig.themeMode || 'auto',
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',
themeMode: 'auto',
filetree: [],
showFiletree: true
showFiletree: true,
showDiff: false,
diffFilename: '',
diffOldText: '',
diffNewText: '',
flashButton: 'none'
};
}
@ -81,7 +91,12 @@ class EmbedDesigner {
height: config.height || '400',
themeMode: config.themeMode || 'auto',
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;
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() {
@ -195,7 +231,7 @@ class EmbedDesigner {
setupDesignerEvents() {
// 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);
if (element) {
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
document.querySelectorAll('.theme-mode-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
@ -329,7 +377,11 @@ class EmbedDesigner {
height: '400',
themeMode: 'auto',
filetree: [],
showFiletree: true
showFiletree: true,
showDiff: false,
diffFilename: '',
diffOldText: '',
diffNewText: ''
};
// Update UI to reflect defaults
this.setupDesignerElements();
@ -338,14 +390,37 @@ class EmbedDesigner {
}
});
// Example buttons
document.getElementById('vibe-example').addEventListener('click', () => {
this.loadVibeExample();
});
document.getElementById('chat-example').addEventListener('click', () => {
this.loadChatExample();
});
// Example select
const exampleSelect = document.getElementById('example-select');
if (exampleSelect) {
exampleSelect.addEventListener('change', (e) => {
const example = e.target.value;
if (example) {
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
document.getElementById('close-modal').addEventListener('click', () => this.hideEmbedModal());
@ -383,7 +458,12 @@ class EmbedDesigner {
height: heightSlider ? heightSlider.value : '400',
themeMode: this.config.themeMode || 'auto',
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();
@ -457,6 +537,20 @@ class EmbedDesigner {
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.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');
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.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.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()}`;
}
@ -554,42 +662,26 @@ class EmbedDesigner {
}, duration);
}
loadVibeExample() {
loadVibeCodingExample() {
// Vibe coding example WITHOUT diff
// Set vibe coding example values
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:
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
Currently handles display, filtering, sorting, and pagination in one file.
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 =
`src/
src/components/
src/components/ProductList.tsx*
`src/components/ProductList.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/useFilters.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`;
src/types/product.ts`;
// Set vibe coding settings
document.getElementById('designer-model').value = 'Claude 4 Opus';
@ -618,30 +710,98 @@ tsconfig.json`;
this.config.themeMode = 'dark';
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
this.updateConfigFromForm();
this.showNotification('Vibe coding example loaded!');
}
loadChatExample() {
// Set chat example values
loadVibeCodingDiffExample() {
// 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 =
`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.`;
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 = '';
// Set chat settings
// ChatGPT settings
document.getElementById('designer-model').value = 'GPT 4o';
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 chat example
// Set height and colors for ChatGPT
const heightSlider = document.getElementById('designer-height');
const heightValue = document.getElementById('height-value');
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-text').value = '#10b981';
document.getElementById('designer-dark-color').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.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('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 */
.designer-panel > .flex-1 {
padding-top: calc(var(--header-height) + 1rem);
padding-top: calc(var(--header-height));
height: 100%;
box-sizing: border-box;
display: flex;