This commit is contained in:
Fatih Kadir Akın 2025-06-18 20:26:44 +03:00
parent fb3607e51f
commit e3b9457a46
4 changed files with 324 additions and 102 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,54 +94,98 @@
</button> </button>
</div> </div>
<!-- Diff View (if enabled) --> <!-- Main Prompt Interface with Floating Diff - Flex-1 to take remaining space -->
<div id="diff-view" class="hidden mb-2"> <div class="flex-1 relative min-h-0 overflow-hidden">
<div class="bg-dynamic-muted border border-dynamic-border rounded-lg p-2"> <!-- Floating Diff View (when enabled and viewport is compact) -->
<div class="flex items-center justify-between mb-1.5"> <div id="diff-view" class="hidden absolute top-2 left-2 right-2 z-10 diff-floating">
<div class="flex items-center gap-1.5"> <div class="bg-dynamic-background/95 backdrop-blur-sm border border-dynamic-border shadow-lg rounded-lg p-3">
<svg class="w-3 h-3 text-dynamic-primary flex-shrink-0" viewBox="0 0 20 20" fill="currentColor"> <div class="flex items-center justify-between">
<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"/> <div class="flex items-center gap-1.5">
</svg> <svg class="w-3 h-3 text-dynamic-primary flex-shrink-0" viewBox="0 0 20 20" fill="currentColor">
<span id="diff-filename" class="text-xs font-medium text-dynamic-foreground truncate"></span> <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>
<div class="flex rounded-md overflow-hidden"> <div id="diff-content" class="space-y-1 transition-all duration-300 overflow-hidden">
<button id="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"> <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>
<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor"> <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>
<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="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> </div>
<div class="space-y-0.5"> </div>
<div id="diff-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="diff-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> <!-- 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 transition-all 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> </div>
</div> </div>
<!-- Main Prompt Interface - Full Height --> <!-- Bottom Bar - Always visible with reduced height -->
<div class="flex-1 flex flex-col min-h-0"> <div class="flex justify-between items-center gap-2 mt-1 pt-1 flex-shrink-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"> <!-- Settings Pills - Smaller -->
<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="settings-pills" class="flex gap-1 flex-wrap flex-1 min-w-0"></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>
<!-- Bottom Bar --> <!-- Send Button - Smaller -->
<div class="flex justify-between items-center gap-2 mt-1 sm:mt-2 flex-shrink-0"> <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">
<!-- Settings Pills --> <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">
<div id="settings-pills" class="flex gap-1 sm:gap-2 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">
<path d="M12 19V5M5 12l7-7 7 7"/> <path d="M12 19V5M5 12l7-7 7 7"/>
</svg> </svg>
</button> </button>

View File

@ -275,6 +275,14 @@
class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded-lg text-sm focus-ring touch-target font-mono" class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded-lg text-sm focus-ring touch-target font-mono"
placeholder="components/ProductList.tsx"> placeholder="components/ProductList.tsx">
</div> </div>
<div>
<label class="text-xs text-dynamic-muted-foreground">Flash Button</label>
<select id="designer-flash-button" class="w-full p-2 bg-dynamic-background border border-dynamic-border rounded-lg text-sm focus-ring touch-target">
<option value="none">None</option>
<option value="accept">Flash Accept Button</option>
<option value="reject">Flash Reject Button</option>
</select>
</div>
<div> <div>
<label class="text-xs text-dynamic-muted-foreground">Old Text</label> <label class="text-xs text-dynamic-muted-foreground">Old Text</label>
<textarea id="designer-diff-old" <textarea id="designer-diff-old"

View File

@ -30,7 +30,8 @@ class EmbedPreview {
showDiff: this.params.showDiff === 'true', showDiff: this.params.showDiff === 'true',
diffFilename: this.params.diffFilename || '', diffFilename: this.params.diffFilename || '',
diffOldText: this.params.diffOldText ? decodeURIComponent(this.params.diffOldText) : '', diffOldText: this.params.diffOldText ? decodeURIComponent(this.params.diffOldText) : '',
diffNewText: this.params.diffNewText ? decodeURIComponent(this.params.diffNewText) : '' diffNewText: this.params.diffNewText ? decodeURIComponent(this.params.diffNewText) : '',
flashButton: this.params.flashButton || 'none'
}; };
} }
@ -38,6 +39,7 @@ class EmbedPreview {
this.setupColors(); this.setupColors();
this.setupElements(); this.setupElements();
this.render(); this.render();
this.setupResizeListener();
} }
setupColors() { setupColors() {
@ -320,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) {
@ -346,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 = '';
@ -365,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;
} }
@ -378,54 +380,166 @@ class EmbedPreview {
return pill; return pill;
} }
setupResizeListener() {
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
this.renderDiffView();
}, 100);
});
}
renderDiffView() { renderDiffView() {
const diffView = document.getElementById('diff-view'); const diffView = document.getElementById('diff-view');
if (!diffView) return; 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)) { if (this.config.showDiff && (this.config.diffOldText || this.config.diffNewText)) {
diffView.classList.remove('hidden'); // Determine which view to show based on viewport height
if (isCompact) {
// Set filename // Show floating diff view
const filenameElement = document.getElementById('diff-filename'); diffView.classList.remove('hidden');
if (filenameElement) { diffView.classList.add('diff-enter');
filenameElement.textContent = this.config.diffFilename || 'untitled'; diffViewInline.classList.add('hidden');
} this.setupDiffContent('diff-view');
} else {
// Set diff content // Show inline diff view
const oldContent = document.getElementById('diff-old-content'); diffViewInline.classList.remove('hidden');
const newContent = document.getElementById('diff-new-content'); diffView.classList.add('hidden');
this.setupDiffContent('diff-view-inline');
if (oldContent) { // Reset margin when switching to inline view
oldContent.textContent = this.config.diffOldText || '(empty)'; if (promptText) {
// Update colors based on dark mode promptText.style.marginTop = '0';
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)';
}
}
// Buttons are dummy - no click handlers needed
// They're just for visual representation
} else { } else {
diffView.classList.add('hidden'); 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.style.marginTop = `${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);
}
});
}
} }
} }

View File

@ -38,7 +38,8 @@ class EmbedDesigner {
showDiff: this.params.showDiff === 'true' || savedConfig.showDiff || false, showDiff: this.params.showDiff === 'true' || savedConfig.showDiff || false,
diffFilename: this.params.diffFilename || savedConfig.diffFilename || '', diffFilename: this.params.diffFilename || savedConfig.diffFilename || '',
diffOldText: this.params.diffOldText ? decodeURIComponent(this.params.diffOldText) : (savedConfig.diffOldText || ''), diffOldText: this.params.diffOldText ? decodeURIComponent(this.params.diffOldText) : (savedConfig.diffOldText || ''),
diffNewText: this.params.diffNewText ? decodeURIComponent(this.params.diffNewText) : (savedConfig.diffNewText || '') diffNewText: this.params.diffNewText ? decodeURIComponent(this.params.diffNewText) : (savedConfig.diffNewText || ''),
flashButton: this.params.flashButton || savedConfig.flashButton || 'none'
}; };
} }
@ -65,7 +66,8 @@ class EmbedDesigner {
showDiff: false, showDiff: false,
diffFilename: '', diffFilename: '',
diffOldText: '', diffOldText: '',
diffNewText: '' diffNewText: '',
flashButton: 'none'
}; };
} }
@ -93,7 +95,8 @@ class EmbedDesigner {
showDiff: config.showDiff || false, showDiff: config.showDiff || false,
diffFilename: config.diffFilename || '', diffFilename: config.diffFilename || '',
diffOldText: config.diffOldText || '', diffOldText: config.diffOldText || '',
diffNewText: config.diffNewText || '' diffNewText: config.diffNewText || '',
flashButton: config.flashButton || 'none'
}; };
} }
} }
@ -204,10 +207,12 @@ class EmbedDesigner {
const diffFilename = document.getElementById('designer-diff-filename'); const diffFilename = document.getElementById('designer-diff-filename');
const diffOldText = document.getElementById('designer-diff-old'); const diffOldText = document.getElementById('designer-diff-old');
const diffNewText = document.getElementById('designer-diff-new'); const diffNewText = document.getElementById('designer-diff-new');
const flashButton = document.getElementById('designer-flash-button');
if (diffFilename) diffFilename.value = this.config.diffFilename || ''; if (diffFilename) diffFilename.value = this.config.diffFilename || '';
if (diffOldText) diffOldText.value = this.config.diffOldText || ''; if (diffOldText) diffOldText.value = this.config.diffOldText || '';
if (diffNewText) diffNewText.value = this.config.diffNewText || ''; if (diffNewText) diffNewText.value = this.config.diffNewText || '';
if (flashButton) flashButton.value = this.config.flashButton || 'none';
} }
updateThemeModeButtons() { updateThemeModeButtons() {
@ -226,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', 'designer-diff-filename', 'designer-diff-old', 'designer-diff-new'].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());
@ -457,7 +462,8 @@ class EmbedDesigner {
showDiff: document.getElementById('designer-show-diff').checked, showDiff: document.getElementById('designer-show-diff').checked,
diffFilename: document.getElementById('designer-diff-filename').value, diffFilename: document.getElementById('designer-diff-filename').value,
diffOldText: document.getElementById('designer-diff-old').value, diffOldText: document.getElementById('designer-diff-old').value,
diffNewText: document.getElementById('designer-diff-new').value diffNewText: document.getElementById('designer-diff-new').value,
flashButton: document.getElementById('designer-flash-button').value
}; };
this.updatePreview(); this.updatePreview();
@ -534,6 +540,7 @@ class EmbedDesigner {
if (this.config.showDiff) { if (this.config.showDiff) {
params.set('showDiff', 'true'); params.set('showDiff', 'true');
if (this.config.diffFilename) params.set('diffFilename', this.config.diffFilename); 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 // Truncate diff text if too long to prevent URL length issues
if (this.config.diffOldText) { if (this.config.diffOldText) {
const truncated = this.config.diffOldText.substring(0, 100); const truncated = this.config.diffOldText.substring(0, 100);
@ -564,6 +571,7 @@ class EmbedDesigner {
if (this.config.showDiff) { if (this.config.showDiff) {
params.set('showDiff', 'true'); params.set('showDiff', 'true');
if (this.config.diffFilename) params.set('diffFilename', this.config.diffFilename); 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 // Truncate diff text if too long to prevent URL length issues
if (this.config.diffOldText) { if (this.config.diffOldText) {
const truncated = this.config.diffOldText.substring(0, 150); const truncated = this.config.diffOldText.substring(0, 150);
@ -705,6 +713,7 @@ src/types/product.ts`;
// No diff for this example // No diff for this example
document.getElementById('designer-show-diff').checked = false; document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden'); 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();
@ -766,6 +775,9 @@ const [loading, setLoading] = useState(true);`;
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);`; 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 // Update config from form
this.updateConfigFromForm(); this.updateConfigFromForm();
@ -812,6 +824,7 @@ Can you suggest a menu with appetizers, main course, and dessert that would work
// Clear diff view // Clear diff view
document.getElementById('designer-show-diff').checked = false; document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden'); 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();
@ -865,6 +878,7 @@ Keep it warm but professional, around 150-200 words.`;
// Clear diff view // Clear diff view
document.getElementById('designer-show-diff').checked = false; document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden'); 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();
@ -915,6 +929,7 @@ Keep it warm but professional, around 150-200 words.`;
// Clear diff view // Clear diff view
document.getElementById('designer-show-diff').checked = false; document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden'); 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();
@ -972,6 +987,7 @@ docs/openapi.yaml*`;
// Clear diff view // Clear diff view
document.getElementById('designer-show-diff').checked = false; document.getElementById('designer-show-diff').checked = false;
document.getElementById('diff-fields').classList.add('hidden'); 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();