Compare commits

..

No commits in common. "e06e0e7e616bac3df9b8916551bbd975970cff3a" and "6476e2c4378a537143d3cc94bf4daab5d466db6f" have entirely different histories.

13 changed files with 125 additions and 529 deletions

View File

@ -29,51 +29,40 @@
</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 -->
<div id="viewer-mode" class="viewer-mode h-screen flex"> <div id="viewer-mode" class="viewer-mode h-screen flex flex-col p-2 sm:p-4">
<!-- Sidebar - File Tree --> <!-- Top Bar with Context Pills and Edit Button -->
<div id="file-sidebar" class="hidden w-40 sm:w-44 border-r border-dynamic-border bg-dynamic-muted/30 flex-shrink-0 overflow-y-auto custom-scrollbar"> <div class="flex justify-between items-start gap-2 mb-0 sm:mb-2">
<div class="p-2 border-b border-dynamic-border"> <!-- Context Pills -->
<h3 class="text-[10px] font-semibold text-dynamic-muted-foreground uppercase tracking-wider">Files</h3> <div id="context-pills" class="flex overflow-x-auto gap-2 empty:hidden pb-1 scrollbar-hide flex-1"></div>
</div>
<div id="file-tree" class="p-1"></div> <!-- Edit Button -->
<button id="edit-button" class="p-1 text-dynamic-muted-foreground hover:text-dynamic-foreground transition-colors" title="Edit in designer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg>
</button>
</div> </div>
<!-- Main Content --> <!-- Main Prompt Interface - Full Height -->
<div class="flex-1 flex flex-col p-2 sm:p-4"> <div class="flex-1 flex flex-col">
<!-- Top Bar with Context Pills and Edit Button --> <div id="prompt-container" class="flex-1 bg-dynamic-muted border border-dynamic-border rounded-xl p-3 sm:p-6 relative focus-within:border-dynamic-primary transition-colors flex flex-col">
<div class="flex justify-between items-start gap-2 mb-0 sm:mb-2"> <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>
<!-- Context Pills --> <div id="prompt-placeholder" class="text-dynamic-muted-foreground italic absolute top-3 sm:top-6 left-3 sm:left-6 pointer-events-none text-sm sm:text-base">← Enter your prompt on designer...</div>
<div id="context-pills" class="flex overflow-x-auto gap-2 empty:hidden pb-1 scrollbar-hide flex-1"></div>
<!-- Edit Button -->
<button id="edit-button" class="p-1 text-dynamic-muted-foreground hover:text-dynamic-foreground transition-colors" title="Edit in designer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg>
</button>
</div> </div>
</div>
<!-- Main Prompt Interface - Full Height --> <!-- Bottom Bar -->
<div class="flex-1 flex flex-col"> <div class="flex justify-between items-center gap-2 mt-0 sm:mt-2">
<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"> <!-- Settings Pills -->
<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 sm:gap-2 flex-wrap flex-1 min-w-0"></div>
<div id="prompt-placeholder" class="text-dynamic-muted-foreground italic absolute top-3 sm:top-6 left-3 sm:left-6 pointer-events-none text-sm sm:text-base">← Enter your prompt on designer...</div>
</div>
</div>
<!-- Bottom Bar --> <!-- Send Button (circular with arrow up) -->
<div class="flex justify-between items-center gap-2 mt-0 sm:mt-2"> <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">
<!-- Settings Pills --> <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">
<div id="settings-pills" class="flex gap-1 sm:gap-2 flex-wrap flex-1 min-w-0"></div> <path d="M12 19V5M5 12l7-7 7 7"/>
</svg>
<!-- Send Button (circular with arrow up) --> </button>
<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"/>
</svg>
</button>
</div>
</div> </div>
</div> </div>

View File

@ -226,19 +226,6 @@
<span>800px</span> <span>800px</span>
</div> </div>
</div> </div>
<!-- File Tree -->
<div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">File Tree</label>
<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>
</div> </div>

View File

@ -1906,7 +1906,7 @@ Contributed by: [@tanoojoy](https://github.com/tanoojoy)
> person texting and nothing else. Your level of drunkenness will be > person texting and nothing else. Your level of drunkenness will be
> deliberately and randomly make a lot of grammar and spelling mistakes in your > deliberately and randomly make a lot of grammar and spelling mistakes in your
> answers. You will also randomly ignore what I said and say something random > answers. You will also randomly ignore what I said and say something random
> with the same level of drunkenness I mentioned. Do not write explanations on > with the same level of drunkeness I mentionned. Do not write explanations on
> replies. My first sentence is "how are you?" > replies. My first sentence is "how are you?"
## Act as a Mathematical History Teacher ## Act as a Mathematical History Teacher
@ -2116,7 +2116,7 @@ Contributed by: [@TheLime1](https://github.com/TheLime1)
> provide a detailed note list that includes examples from the lecture and > provide a detailed note list that includes examples from the lecture and
> focuses on notes that you believe will end up in quiz questions. Additionally, > focuses on notes that you believe will end up in quiz questions. Additionally,
> please make a separate list for notes that have numbers and data in them and > please make a separate list for notes that have numbers and data in them and
> another separated list for the examples that included in this lecture. The > another seperated list for the examples that included in this lecture. The
> notes should be concise and easy to read. > notes should be concise and easy to read.
## Act as a Literary Critic ## Act as a Literary Critic

View File

@ -24,8 +24,7 @@ class EmbedPreview {
max: this.params.max === 'true', max: this.params.max === 'true',
lightColor: this.params.lightColor || '#3b82f6', lightColor: this.params.lightColor || '#3b82f6',
darkColor: this.params.darkColor || '#60a5fa', darkColor: this.params.darkColor || '#60a5fa',
themeMode: this.params.themeMode || 'auto', themeMode: this.params.themeMode || 'auto'
filetree: this.params.filetree ? decodeURIComponent(this.params.filetree).split('\n').filter(f => f.trim()) : []
}; };
} }
@ -189,7 +188,6 @@ class EmbedPreview {
this.renderContextPills(); this.renderContextPills();
this.renderPromptText(); this.renderPromptText();
this.renderSettingsPills(); this.renderSettingsPills();
this.renderFileTree();
} }
renderContextPills() { renderContextPills() {
@ -343,7 +341,7 @@ class EmbedPreview {
} }
highlightMentions(text) { highlightMentions(text) {
return text.replace(/@([\w\.\/\:\-\&\=\?]+)/g, '<span class="mention">@$1</span>'); return text.replace(/@(\w+)/g, '<span class="mention">@$1</span>');
} }
capitalizeFirst(str) { capitalizeFirst(str) {
@ -415,143 +413,6 @@ class EmbedPreview {
notification.classList.add('opacity-0'); notification.classList.add('opacity-0');
}, 2000); }, 2000);
} }
buildFileTree(paths) {
const tree = {};
paths.forEach(path => {
// Check if the path ends with an asterisk
const isHighlighted = path.endsWith('*');
// Remove asterisk if present
const cleanPath = isHighlighted ? path.slice(0, -1) : path;
const parts = cleanPath.split('/');
let current = tree;
parts.forEach((part, index) => {
if (!current[part]) {
current[part] = {
name: part,
isFile: index === parts.length - 1,
isHighlighted: index === parts.length - 1 && isHighlighted,
children: {}
};
}
if (index < parts.length - 1) {
current = current[part].children;
}
});
});
return tree;
}
renderFileTree() {
const sidebar = document.getElementById('file-sidebar');
const treeContainer = document.getElementById('file-tree');
if (!sidebar || !treeContainer) return;
// Show/hide sidebar based on whether there are files
if (this.config.filetree && this.config.filetree.length > 0) {
sidebar.classList.remove('hidden');
// Build tree structure
const tree = this.buildFileTree(this.config.filetree);
// Clear existing content
treeContainer.innerHTML = '';
// Render tree
this.renderTreeNode(tree, treeContainer, 0);
} else {
sidebar.classList.add('hidden');
}
}
renderTreeNode(node, container, level) {
const sortedKeys = Object.keys(node).sort((a, b) => {
// Folders first, then files
const aIsFile = node[a].isFile;
const bIsFile = node[b].isFile;
if (aIsFile !== bIsFile) return aIsFile ? 1 : -1;
return a.localeCompare(b);
});
sortedKeys.forEach(key => {
const item = node[key];
const itemElement = document.createElement('div');
// Add highlighting class if the file is marked
if (item.isHighlighted) {
itemElement.className = 'flex items-center gap-1 py-0.5 px-1.5 bg-dynamic-primary/20 rounded cursor-pointer text-xs text-dynamic-foreground font-medium transition-all hover:bg-dynamic-primary/30';
} else {
itemElement.className = 'flex items-center gap-1 py-0.5 px-1.5 hover:bg-dynamic-primary/10 rounded cursor-pointer text-xs text-dynamic-foreground/80 hover:text-dynamic-foreground transition-colors';
}
itemElement.style.paddingLeft = `${level * 12 + 6}px`;
// Add icon
const icon = document.createElement('span');
icon.className = 'flex-shrink-0';
if (item.isFile) {
// File icon with different colors based on extension
const ext = key.split('.').pop().toLowerCase();
let iconColor = 'text-dynamic-muted-foreground';
// If highlighted, use primary color for icon
if (item.isHighlighted) {
iconColor = 'text-dynamic-primary';
} else {
// Color code common file types
if (['js', 'jsx', 'ts', 'tsx'].includes(ext)) {
iconColor = 'text-yellow-500';
} else if (['css', 'scss', 'sass', 'less'].includes(ext)) {
iconColor = 'text-blue-500';
} else if (['html', 'htm'].includes(ext)) {
iconColor = 'text-orange-500';
} else if (['vue', 'svelte'].includes(ext)) {
iconColor = 'text-green-500';
} else if (['json', 'xml', 'yaml', 'yml'].includes(ext)) {
iconColor = 'text-purple-500';
} else if (['md', 'mdx'].includes(ext)) {
iconColor = 'text-gray-500';
} else if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp'].includes(ext)) {
iconColor = 'text-pink-500';
}
}
icon.innerHTML = `<svg class="w-3 h-3 ${iconColor}" 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>`;
} else {
// Folder icon
icon.innerHTML = '<svg class="w-3 h-3 text-dynamic-primary" viewBox="0 0 20 20" fill="currentColor"><path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"/></svg>';
}
// Add name
const nameSpan = document.createElement('span');
nameSpan.className = 'truncate flex-1';
nameSpan.textContent = item.name;
itemElement.appendChild(icon);
itemElement.appendChild(nameSpan);
// Add a star indicator for highlighted files
if (item.isHighlighted) {
const starIcon = document.createElement('span');
starIcon.className = 'ml-auto text-dynamic-primary';
starIcon.innerHTML = '<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg>';
itemElement.appendChild(starIcon);
}
container.appendChild(itemElement);
// Recursively render children
if (!item.isFile && Object.keys(item.children).length > 0) {
this.renderTreeNode(item.children, container, level + 1);
}
});
}
} }
// Initialize when DOM is ready // Initialize when DOM is ready

View File

@ -109,8 +109,8 @@
@media (max-width: 640px) { @media (max-width: 640px) {
#prompt-container { #prompt-container {
height: calc(100vh - 90px); /* Smaller on mobile */ height: calc(100vh - 120px); /* Smaller on mobile */
max-height: calc(100vh - 90px); max-height: calc(100vh - 120px);
} }
} }
@ -119,6 +119,7 @@
height: 100%; height: 100%;
overflow-y: auto !important; overflow-y: auto !important;
flex: 1; flex: 1;
white-space: wrap;
} }
/* Responsive improvements */ /* Responsive improvements */
@ -185,13 +186,6 @@
} }
} }
/* Hide file sidebar on mobile devices */
@media (max-width: 640px) {
#file-sidebar {
display: none !important;
}
}
/* Ensure proper touch behavior */ /* Ensure proper touch behavior */
@media (hover: none) and (pointer: coarse) { @media (hover: none) and (pointer: coarse) {
/* All interactive elements get proper touch targets */ /* All interactive elements get proper touch targets */

View File

@ -29,51 +29,40 @@
</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 -->
<div id="viewer-mode" class="viewer-mode h-screen flex"> <div id="viewer-mode" class="viewer-mode h-screen flex flex-col p-2 sm:p-4">
<!-- Sidebar - File Tree --> <!-- Top Bar with Context Pills and Edit Button -->
<div id="file-sidebar" class="hidden w-40 sm:w-44 border-r border-dynamic-border bg-dynamic-muted/30 flex-shrink-0 overflow-y-auto custom-scrollbar"> <div class="flex justify-between items-start gap-2 mb-0 sm:mb-2">
<div class="p-2 border-b border-dynamic-border"> <!-- Context Pills -->
<h3 class="text-[10px] font-semibold text-dynamic-muted-foreground uppercase tracking-wider">Files</h3> <div id="context-pills" class="flex overflow-x-auto gap-2 empty:hidden pb-1 scrollbar-hide flex-1"></div>
</div>
<div id="file-tree" class="p-1"></div> <!-- Edit Button -->
<button id="edit-button" class="p-1 text-dynamic-muted-foreground hover:text-dynamic-foreground transition-colors" title="Edit in designer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg>
</button>
</div> </div>
<!-- Main Content --> <!-- Main Prompt Interface - Full Height -->
<div class="flex-1 flex flex-col p-2 sm:p-4"> <div class="flex-1 flex flex-col">
<!-- Top Bar with Context Pills and Edit Button --> <div id="prompt-container" class="flex-1 bg-dynamic-muted border border-dynamic-border rounded-xl p-3 sm:p-6 relative focus-within:border-dynamic-primary transition-colors flex flex-col">
<div class="flex justify-between items-start gap-2 mb-0 sm:mb-2"> <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>
<!-- Context Pills --> <div id="prompt-placeholder" class="text-dynamic-muted-foreground italic absolute top-3 sm:top-6 left-3 sm:left-6 pointer-events-none text-sm sm:text-base">← Enter your prompt on designer...</div>
<div id="context-pills" class="flex overflow-x-auto gap-2 empty:hidden pb-1 scrollbar-hide flex-1"></div>
<!-- Edit Button -->
<button id="edit-button" class="p-1 text-dynamic-muted-foreground hover:text-dynamic-foreground transition-colors" title="Edit in designer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg>
</button>
</div> </div>
</div>
<!-- Main Prompt Interface - Full Height --> <!-- Bottom Bar -->
<div class="flex-1 flex flex-col"> <div class="flex justify-between items-center gap-2 mt-0 sm:mt-2">
<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"> <!-- Settings Pills -->
<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 sm:gap-2 flex-wrap flex-1 min-w-0"></div>
<div id="prompt-placeholder" class="text-dynamic-muted-foreground italic absolute top-3 sm:top-6 left-3 sm:left-6 pointer-events-none text-sm sm:text-base">← Enter your prompt on designer...</div>
</div>
</div>
<!-- Bottom Bar --> <!-- Send Button (circular with arrow up) -->
<div class="flex justify-between items-center gap-2 mt-0 sm:mt-2"> <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">
<!-- Settings Pills --> <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">
<div id="settings-pills" class="flex gap-1 sm:gap-2 flex-wrap flex-1 min-w-0"></div> <path d="M12 19V5M5 12l7-7 7 7"/>
</svg>
<!-- Send Button (circular with arrow up) --> </button>
<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"/>
</svg>
</button>
</div>
</div> </div>
</div> </div>

View File

@ -32,8 +32,7 @@ class EmbedDesigner {
lightColor: this.params.lightColor || savedConfig.lightColor || '#3b82f6', lightColor: this.params.lightColor || savedConfig.lightColor || '#3b82f6',
darkColor: this.params.darkColor || savedConfig.darkColor || '#60a5fa', darkColor: this.params.darkColor || savedConfig.darkColor || '#60a5fa',
height: this.params.height || savedConfig.height || '400', height: this.params.height || savedConfig.height || '400',
themeMode: this.params.themeMode || savedConfig.themeMode || 'auto', themeMode: this.params.themeMode || savedConfig.themeMode || 'auto'
filetree: this.params.filetree ? decodeURIComponent(this.params.filetree).split('\n').filter(f => f.trim()) : (savedConfig.filetree || [])
}; };
} }
@ -54,8 +53,7 @@ class EmbedDesigner {
lightColor: '#3b82f6', lightColor: '#3b82f6',
darkColor: '#60a5fa', darkColor: '#60a5fa',
height: '400', height: '400',
themeMode: 'auto', themeMode: 'auto'
filetree: []
}; };
} }
@ -66,20 +64,7 @@ class EmbedDesigner {
const config = JSON.parse(saved); const config = JSON.parse(saved);
// Validate the loaded config has all required fields // Validate the loaded config has all required fields
if (config && typeof config === 'object') { if (config && typeof config === 'object') {
// Ensure all properties have defaults return config;
return {
prompt: config.prompt || '',
context: config.context || [],
model: config.model || 'gpt-4o',
mode: config.mode || 'chat',
thinking: config.thinking || false,
max: config.max || false,
lightColor: config.lightColor || '#3b82f6',
darkColor: config.darkColor || '#60a5fa',
height: config.height || '400',
themeMode: config.themeMode || 'auto',
filetree: config.filetree || []
};
} }
} }
} catch (e) { } catch (e) {
@ -118,7 +103,6 @@ class EmbedDesigner {
// Populate form with current config // Populate form with current config
document.getElementById('designer-prompt').value = this.config.prompt; document.getElementById('designer-prompt').value = this.config.prompt;
document.getElementById('designer-context').value = this.config.context.join(', '); document.getElementById('designer-context').value = this.config.context.join(', ');
document.getElementById('designer-filetree').value = this.config.filetree.join('\n');
// Handle model selection // Handle model selection
const modelSelect = document.getElementById('designer-model'); const modelSelect = document.getElementById('designer-model');
@ -186,7 +170,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'].forEach(id => { ['designer-prompt', 'designer-context', 'designer-mode-select', 'designer-thinking', 'designer-max'].forEach(id => {
const element = document.getElementById(id); const element = document.getElementById(id);
element.addEventListener('input', () => this.updateConfigFromForm()); element.addEventListener('input', () => this.updateConfigFromForm());
element.addEventListener('change', () => this.updateConfigFromForm()); element.addEventListener('change', () => this.updateConfigFromForm());
@ -316,8 +300,7 @@ class EmbedDesigner {
lightColor: '#3b82f6', lightColor: '#3b82f6',
darkColor: '#60a5fa', darkColor: '#60a5fa',
height: '400', height: '400',
themeMode: 'auto', themeMode: 'auto'
filetree: []
}; };
// Update UI to reflect defaults // Update UI to reflect defaults
this.setupDesignerElements(); this.setupDesignerElements();
@ -365,8 +348,7 @@ class EmbedDesigner {
lightColor: lightColorText ? lightColorText.value : '#3b82f6', lightColor: lightColorText ? lightColorText.value : '#3b82f6',
darkColor: darkColorText ? darkColorText.value : '#60a5fa', darkColor: darkColorText ? darkColorText.value : '#60a5fa',
height: heightSlider ? heightSlider.value : '400', height: heightSlider ? heightSlider.value : '400',
themeMode: this.config.themeMode || 'auto', themeMode: this.config.themeMode || 'auto'
filetree: document.getElementById('designer-filetree').value.split('\n').map(f => f.trim()).filter(f => f)
}; };
this.updatePreview(); this.updatePreview();
@ -439,7 +421,6 @@ class EmbedDesigner {
if (this.config.lightColor !== '#3b82f6') params.set('lightColor', this.config.lightColor); if (this.config.lightColor !== '#3b82f6') params.set('lightColor', this.config.lightColor);
if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor); if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor);
if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode); if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode);
if (this.config.filetree && this.config.filetree.length > 0) params.set('filetree', encodeURIComponent(this.config.filetree.join('\n')));
params.set('preview', 'true'); params.set('preview', 'true');
return `/embed-preview/?${params.toString()}`; return `/embed-preview/?${params.toString()}`;
@ -456,7 +437,6 @@ class EmbedDesigner {
if (this.config.lightColor !== '#3b82f6') params.set('lightColor', this.config.lightColor); if (this.config.lightColor !== '#3b82f6') params.set('lightColor', this.config.lightColor);
if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor); if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor);
if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode); if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode);
if (this.config.filetree && this.config.filetree.length > 0) params.set('filetree', encodeURIComponent(this.config.filetree.join('\n')));
return `${window.location.origin}/embed-preview/?${params.toString()}`; return `${window.location.origin}/embed-preview/?${params.toString()}`;
} }
@ -538,40 +518,27 @@ class EmbedDesigner {
} }
loadExample() { loadExample() {
// Set example values // Set example data
document.getElementById('designer-prompt').value = this.config = {
`You are a senior React developer. I need help building a modern e-commerce product listing component. prompt: 'Build an MCP server that works with Weather API. See @Web for cool weather APIs.',
context: ['@Web', 'https://modelcontextprotocol.io/full-llms.txt'],
model: 'Claude 4 Sonnet',
mode: 'agent',
thinking: true,
max: true,
lightColor: '#3b82f6',
darkColor: '#60a5fa',
height: '250',
themeMode: 'auto'
};
Requirements: // Update all form elements
- Use React hooks and functional components this.setupDesignerElements();
- Implement product filtering by category and price range this.updatePreview();
- Add smooth animations for product cards this.updateIframeSnippet();
- Make it fully responsive with a grid layout this.saveToLocalStorage();
- Include loading states and error handling
The component should fetch data from a REST API and display products with images, titles, prices, and ratings. Please provide clean, well-commented code following React best practices.`;
document.getElementById('designer-context').value = '@codebase, ProductList.jsx';
document.getElementById('designer-filetree').value =
`src/components/ProductList.jsx*
src/components/ProductCard.jsx
src/components/Filters.jsx
src/hooks/useProducts.js
src/api/products.js
src/styles/products.css
src/utils/formatters.js
public/index.html
package.json
README.md`;
// Set some example settings
document.getElementById('designer-model').value = 'Claude 3.7 Sonnet';
document.getElementById('designer-mode-select').value = 'agent';
document.getElementById('designer-thinking').checked = true;
// Update config from form
this.updateConfigFromForm();
// Show notification
this.showNotification('Example loaded!'); this.showNotification('Example loaded!');
} }
} }

View File

@ -226,19 +226,6 @@
<span>800px</span> <span>800px</span>
</div> </div>
</div> </div>
<!-- File Tree -->
<div class="space-y-2">
<label class="text-sm font-medium text-dynamic-muted-foreground">File Tree</label>
<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>
</div> </div>

View File

@ -25,7 +25,7 @@
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="/style.css?v=6476e2c4378a537143d3cc94bf4daab5d466db6f"> <link rel="stylesheet" href="/style.css?v=a8a69caf7649bb8f02c225029af543967973a65f">
</head> </head>
<body class=""> <body class="">
<div class="layout-wrapper"> <div class="layout-wrapper">
@ -2394,7 +2394,7 @@ take it from there.</p>
person texting and nothing else. Your level of drunkenness will be person texting and nothing else. Your level of drunkenness will be
deliberately and randomly make a lot of grammar and spelling mistakes in your deliberately and randomly make a lot of grammar and spelling mistakes in your
answers. You will also randomly ignore what I said and say something random answers. You will also randomly ignore what I said and say something random
with the same level of drunkenness I mentioned. Do not write explanations on with the same level of drunkeness I mentionned. Do not write explanations on
replies. My first sentence is “how are you?”</p> replies. My first sentence is “how are you?”</p>
</blockquote> </blockquote>
@ -2636,7 +2636,7 @@ will tell me the right answer. Then you will ask me the next question.</p>
provide a detailed note list that includes examples from the lecture and provide a detailed note list that includes examples from the lecture and
focuses on notes that you believe will end up in quiz questions. Additionally, focuses on notes that you believe will end up in quiz questions. Additionally,
please make a separate list for notes that have numbers and data in them and please make a separate list for notes that have numbers and data in them and
another separated list for the examples that included in this lecture. The another seperated list for the examples that included in this lecture. The
notes should be concise and easy to read.</p> notes should be concise and easy to read.</p>
</blockquote> </blockquote>

View File

@ -25,7 +25,7 @@
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="/style.css?v=6476e2c4378a537143d3cc94bf4daab5d466db6f"> <link rel="stylesheet" href="/style.css?v=a8a69caf7649bb8f02c225029af543967973a65f">
</head> </head>
<body class="vibe"> <body class="vibe">
<div class="layout-wrapper"> <div class="layout-wrapper">

View File

@ -24,8 +24,7 @@ class EmbedPreview {
max: this.params.max === 'true', max: this.params.max === 'true',
lightColor: this.params.lightColor || '#3b82f6', lightColor: this.params.lightColor || '#3b82f6',
darkColor: this.params.darkColor || '#60a5fa', darkColor: this.params.darkColor || '#60a5fa',
themeMode: this.params.themeMode || 'auto', themeMode: this.params.themeMode || 'auto'
filetree: this.params.filetree ? decodeURIComponent(this.params.filetree).split('\n').filter(f => f.trim()) : []
}; };
} }
@ -189,7 +188,6 @@ class EmbedPreview {
this.renderContextPills(); this.renderContextPills();
this.renderPromptText(); this.renderPromptText();
this.renderSettingsPills(); this.renderSettingsPills();
this.renderFileTree();
} }
renderContextPills() { renderContextPills() {
@ -415,143 +413,6 @@ class EmbedPreview {
notification.classList.add('opacity-0'); notification.classList.add('opacity-0');
}, 2000); }, 2000);
} }
buildFileTree(paths) {
const tree = {};
paths.forEach(path => {
// Check if the path ends with an asterisk
const isHighlighted = path.endsWith('*');
// Remove asterisk if present
const cleanPath = isHighlighted ? path.slice(0, -1) : path;
const parts = cleanPath.split('/');
let current = tree;
parts.forEach((part, index) => {
if (!current[part]) {
current[part] = {
name: part,
isFile: index === parts.length - 1,
isHighlighted: index === parts.length - 1 && isHighlighted,
children: {}
};
}
if (index < parts.length - 1) {
current = current[part].children;
}
});
});
return tree;
}
renderFileTree() {
const sidebar = document.getElementById('file-sidebar');
const treeContainer = document.getElementById('file-tree');
if (!sidebar || !treeContainer) return;
// Show/hide sidebar based on whether there are files
if (this.config.filetree && this.config.filetree.length > 0) {
sidebar.classList.remove('hidden');
// Build tree structure
const tree = this.buildFileTree(this.config.filetree);
// Clear existing content
treeContainer.innerHTML = '';
// Render tree
this.renderTreeNode(tree, treeContainer, 0);
} else {
sidebar.classList.add('hidden');
}
}
renderTreeNode(node, container, level) {
const sortedKeys = Object.keys(node).sort((a, b) => {
// Folders first, then files
const aIsFile = node[a].isFile;
const bIsFile = node[b].isFile;
if (aIsFile !== bIsFile) return aIsFile ? 1 : -1;
return a.localeCompare(b);
});
sortedKeys.forEach(key => {
const item = node[key];
const itemElement = document.createElement('div');
// Add highlighting class if the file is marked
if (item.isHighlighted) {
itemElement.className = 'flex items-center gap-1 py-0.5 px-1.5 bg-dynamic-primary/20 rounded cursor-pointer text-xs text-dynamic-foreground font-medium transition-all hover:bg-dynamic-primary/30';
} else {
itemElement.className = 'flex items-center gap-1 py-0.5 px-1.5 hover:bg-dynamic-primary/10 rounded cursor-pointer text-xs text-dynamic-foreground/80 hover:text-dynamic-foreground transition-colors';
}
itemElement.style.paddingLeft = `${level * 12 + 6}px`;
// Add icon
const icon = document.createElement('span');
icon.className = 'flex-shrink-0';
if (item.isFile) {
// File icon with different colors based on extension
const ext = key.split('.').pop().toLowerCase();
let iconColor = 'text-dynamic-muted-foreground';
// If highlighted, use primary color for icon
if (item.isHighlighted) {
iconColor = 'text-dynamic-primary';
} else {
// Color code common file types
if (['js', 'jsx', 'ts', 'tsx'].includes(ext)) {
iconColor = 'text-yellow-500';
} else if (['css', 'scss', 'sass', 'less'].includes(ext)) {
iconColor = 'text-blue-500';
} else if (['html', 'htm'].includes(ext)) {
iconColor = 'text-orange-500';
} else if (['vue', 'svelte'].includes(ext)) {
iconColor = 'text-green-500';
} else if (['json', 'xml', 'yaml', 'yml'].includes(ext)) {
iconColor = 'text-purple-500';
} else if (['md', 'mdx'].includes(ext)) {
iconColor = 'text-gray-500';
} else if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp'].includes(ext)) {
iconColor = 'text-pink-500';
}
}
icon.innerHTML = `<svg class="w-3 h-3 ${iconColor}" 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>`;
} else {
// Folder icon
icon.innerHTML = '<svg class="w-3 h-3 text-dynamic-primary" viewBox="0 0 20 20" fill="currentColor"><path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"/></svg>';
}
// Add name
const nameSpan = document.createElement('span');
nameSpan.className = 'truncate flex-1';
nameSpan.textContent = item.name;
itemElement.appendChild(icon);
itemElement.appendChild(nameSpan);
// Add a star indicator for highlighted files
if (item.isHighlighted) {
const starIcon = document.createElement('span');
starIcon.className = 'ml-auto text-dynamic-primary';
starIcon.innerHTML = '<svg class="w-2.5 h-2.5" viewBox="0 0 20 20" fill="currentColor"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg>';
itemElement.appendChild(starIcon);
}
container.appendChild(itemElement);
// Recursively render children
if (!item.isFile && Object.keys(item.children).length > 0) {
this.renderTreeNode(item.children, container, level + 1);
}
});
}
} }
// Initialize when DOM is ready // Initialize when DOM is ready

View File

@ -109,8 +109,8 @@
@media (max-width: 640px) { @media (max-width: 640px) {
#prompt-container { #prompt-container {
height: calc(100vh - 90px); /* Smaller on mobile */ height: calc(100vh - 120px); /* Smaller on mobile */
max-height: calc(100vh - 90px); max-height: calc(100vh - 120px);
} }
} }
@ -119,6 +119,7 @@
height: 100%; height: 100%;
overflow-y: auto !important; overflow-y: auto !important;
flex: 1; flex: 1;
white-space: wrap;
} }
/* Responsive improvements */ /* Responsive improvements */
@ -185,13 +186,6 @@
} }
} }
/* Hide file sidebar on mobile devices */
@media (max-width: 640px) {
#file-sidebar {
display: none !important;
}
}
/* Ensure proper touch behavior */ /* Ensure proper touch behavior */
@media (hover: none) and (pointer: coarse) { @media (hover: none) and (pointer: coarse) {
/* All interactive elements get proper touch targets */ /* All interactive elements get proper touch targets */

View File

@ -32,8 +32,7 @@ class EmbedDesigner {
lightColor: this.params.lightColor || savedConfig.lightColor || '#3b82f6', lightColor: this.params.lightColor || savedConfig.lightColor || '#3b82f6',
darkColor: this.params.darkColor || savedConfig.darkColor || '#60a5fa', darkColor: this.params.darkColor || savedConfig.darkColor || '#60a5fa',
height: this.params.height || savedConfig.height || '400', height: this.params.height || savedConfig.height || '400',
themeMode: this.params.themeMode || savedConfig.themeMode || 'auto', themeMode: this.params.themeMode || savedConfig.themeMode || 'auto'
filetree: this.params.filetree ? decodeURIComponent(this.params.filetree).split('\n').filter(f => f.trim()) : (savedConfig.filetree || [])
}; };
} }
@ -54,8 +53,7 @@ class EmbedDesigner {
lightColor: '#3b82f6', lightColor: '#3b82f6',
darkColor: '#60a5fa', darkColor: '#60a5fa',
height: '400', height: '400',
themeMode: 'auto', themeMode: 'auto'
filetree: []
}; };
} }
@ -66,20 +64,7 @@ class EmbedDesigner {
const config = JSON.parse(saved); const config = JSON.parse(saved);
// Validate the loaded config has all required fields // Validate the loaded config has all required fields
if (config && typeof config === 'object') { if (config && typeof config === 'object') {
// Ensure all properties have defaults return config;
return {
prompt: config.prompt || '',
context: config.context || [],
model: config.model || 'gpt-4o',
mode: config.mode || 'chat',
thinking: config.thinking || false,
max: config.max || false,
lightColor: config.lightColor || '#3b82f6',
darkColor: config.darkColor || '#60a5fa',
height: config.height || '400',
themeMode: config.themeMode || 'auto',
filetree: config.filetree || []
};
} }
} }
} catch (e) { } catch (e) {
@ -118,7 +103,6 @@ class EmbedDesigner {
// Populate form with current config // Populate form with current config
document.getElementById('designer-prompt').value = this.config.prompt; document.getElementById('designer-prompt').value = this.config.prompt;
document.getElementById('designer-context').value = this.config.context.join(', '); document.getElementById('designer-context').value = this.config.context.join(', ');
document.getElementById('designer-filetree').value = this.config.filetree.join('\n');
// Handle model selection // Handle model selection
const modelSelect = document.getElementById('designer-model'); const modelSelect = document.getElementById('designer-model');
@ -186,7 +170,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'].forEach(id => { ['designer-prompt', 'designer-context', 'designer-mode-select', 'designer-thinking', 'designer-max'].forEach(id => {
const element = document.getElementById(id); const element = document.getElementById(id);
element.addEventListener('input', () => this.updateConfigFromForm()); element.addEventListener('input', () => this.updateConfigFromForm());
element.addEventListener('change', () => this.updateConfigFromForm()); element.addEventListener('change', () => this.updateConfigFromForm());
@ -316,8 +300,7 @@ class EmbedDesigner {
lightColor: '#3b82f6', lightColor: '#3b82f6',
darkColor: '#60a5fa', darkColor: '#60a5fa',
height: '400', height: '400',
themeMode: 'auto', themeMode: 'auto'
filetree: []
}; };
// Update UI to reflect defaults // Update UI to reflect defaults
this.setupDesignerElements(); this.setupDesignerElements();
@ -365,8 +348,7 @@ class EmbedDesigner {
lightColor: lightColorText ? lightColorText.value : '#3b82f6', lightColor: lightColorText ? lightColorText.value : '#3b82f6',
darkColor: darkColorText ? darkColorText.value : '#60a5fa', darkColor: darkColorText ? darkColorText.value : '#60a5fa',
height: heightSlider ? heightSlider.value : '400', height: heightSlider ? heightSlider.value : '400',
themeMode: this.config.themeMode || 'auto', themeMode: this.config.themeMode || 'auto'
filetree: document.getElementById('designer-filetree').value.split('\n').map(f => f.trim()).filter(f => f)
}; };
this.updatePreview(); this.updatePreview();
@ -439,7 +421,6 @@ class EmbedDesigner {
if (this.config.lightColor !== '#3b82f6') params.set('lightColor', this.config.lightColor); if (this.config.lightColor !== '#3b82f6') params.set('lightColor', this.config.lightColor);
if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor); if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor);
if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode); if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode);
if (this.config.filetree && this.config.filetree.length > 0) params.set('filetree', encodeURIComponent(this.config.filetree.join('\n')));
params.set('preview', 'true'); params.set('preview', 'true');
return `/embed-preview/?${params.toString()}`; return `/embed-preview/?${params.toString()}`;
@ -456,7 +437,6 @@ class EmbedDesigner {
if (this.config.lightColor !== '#3b82f6') params.set('lightColor', this.config.lightColor); if (this.config.lightColor !== '#3b82f6') params.set('lightColor', this.config.lightColor);
if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor); if (this.config.darkColor !== '#60a5fa') params.set('darkColor', this.config.darkColor);
if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode); if (this.config.themeMode !== 'auto') params.set('themeMode', this.config.themeMode);
if (this.config.filetree && this.config.filetree.length > 0) params.set('filetree', encodeURIComponent(this.config.filetree.join('\n')));
return `${window.location.origin}/embed-preview/?${params.toString()}`; return `${window.location.origin}/embed-preview/?${params.toString()}`;
} }
@ -538,40 +518,27 @@ class EmbedDesigner {
} }
loadExample() { loadExample() {
// Set example values // Set example data
document.getElementById('designer-prompt').value = this.config = {
`You are a senior React developer. I need help building a modern e-commerce product listing component. prompt: 'Build an MCP server that works with Weather API. See @Web for cool weather APIs.',
context: ['@Web', 'https://modelcontextprotocol.io/full-llms.txt'],
model: 'Claude 4 Sonnet',
mode: 'agent',
thinking: true,
max: true,
lightColor: '#3b82f6',
darkColor: '#60a5fa',
height: '250',
themeMode: 'auto'
};
Requirements: // Update all form elements
- Use React hooks and functional components this.setupDesignerElements();
- Implement product filtering by category and price range this.updatePreview();
- Add smooth animations for product cards this.updateIframeSnippet();
- Make it fully responsive with a grid layout this.saveToLocalStorage();
- Include loading states and error handling
The component should fetch data from a REST API and display products with images, titles, prices, and ratings. Please provide clean, well-commented code following React best practices.`;
document.getElementById('designer-context').value = '@codebase, ProductList.jsx';
document.getElementById('designer-filetree').value =
`src/components/ProductList.jsx*
src/components/ProductCard.jsx
src/components/Filters.jsx
src/hooks/useProducts.js
src/api/products.js
src/styles/products.css
src/utils/formatters.js
public/index.html
package.json
README.md`;
// Set some example settings
document.getElementById('designer-model').value = 'Claude 3.7 Sonnet';
document.getElementById('designer-mode-select').value = 'agent';
document.getElementById('designer-thinking').checked = true;
// Update config from form
this.updateConfigFromForm();
// Show notification
this.showNotification('Example loaded!'); this.showNotification('Example loaded!');
} }
} }