@@ -2394,7 +2394,7 @@ take it from there.
person texting and nothing else. Your level of drunkenness will be
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
-with the same level of drunkeness I mentionned. Do not write explanations on
+with the same level of drunkenness I mentioned. Do not write explanations on
replies. My first sentence is “how are you?”
@@ -2636,7 +2636,7 @@ will tell me the right answer. Then you will ask me the next question.
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,
please make a separate list for notes that have numbers and data in them and
-another seperated list for the examples that included in this lecture. The
+another separated list for the examples that included in this lecture. The
notes should be concise and easy to read.
diff --git a/_site/vibe/index.html b/_site/vibe/index.html
index 44208fe..98dd253 100644
--- a/_site/vibe/index.html
+++ b/_site/vibe/index.html
@@ -25,7 +25,7 @@
-
+
diff --git a/embed-preview-script.js b/embed-preview-script.js
index 3d9dd76..d5675b2 100644
--- a/embed-preview-script.js
+++ b/embed-preview-script.js
@@ -24,7 +24,8 @@ class EmbedPreview {
max: this.params.max === 'true',
lightColor: this.params.lightColor || '#3b82f6',
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()) : []
};
}
@@ -188,6 +189,7 @@ class EmbedPreview {
this.renderContextPills();
this.renderPromptText();
this.renderSettingsPills();
+ this.renderFileTree();
}
renderContextPills() {
@@ -413,6 +415,116 @@ class EmbedPreview {
notification.classList.add('opacity-0');
}, 2000);
}
+
+ buildFileTree(paths) {
+ const tree = {};
+
+ paths.forEach(path => {
+ const parts = path.split('/');
+ let current = tree;
+
+ parts.forEach((part, index) => {
+ if (!current[part]) {
+ current[part] = {
+ name: part,
+ isFile: index === parts.length - 1,
+ 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');
+ itemElement.className = 'flex items-center gap-1.5 py-1 px-2 hover:bg-dynamic-primary/10 rounded cursor-pointer text-sm text-dynamic-foreground/80 hover:text-dynamic-foreground transition-colors';
+ itemElement.style.paddingLeft = `${level * 16 + 8}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';
+
+ // 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 = ``;
+ } else {
+ // Folder icon
+ icon.innerHTML = '';
+ }
+
+ // Add name
+ const nameSpan = document.createElement('span');
+ nameSpan.className = 'truncate';
+ nameSpan.textContent = item.name;
+
+ itemElement.appendChild(icon);
+ itemElement.appendChild(nameSpan);
+ 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
diff --git a/embed-preview-style.css b/embed-preview-style.css
index 538bd67..7e0f4fe 100644
--- a/embed-preview-style.css
+++ b/embed-preview-style.css
@@ -109,8 +109,8 @@
@media (max-width: 640px) {
#prompt-container {
- height: calc(100vh - 120px); /* Smaller on mobile */
- max-height: calc(100vh - 120px);
+ height: calc(100vh - 90px); /* Smaller on mobile */
+ max-height: calc(100vh - 90px);
}
}
@@ -186,6 +186,13 @@
}
}
+/* Hide file sidebar on mobile devices */
+@media (max-width: 640px) {
+ #file-sidebar {
+ display: none !important;
+ }
+}
+
/* Ensure proper touch behavior */
@media (hover: none) and (pointer: coarse) {
/* All interactive elements get proper touch targets */
diff --git a/embed-script.js b/embed-script.js
index 7238800..48e38a2 100644
--- a/embed-script.js
+++ b/embed-script.js
@@ -32,7 +32,8 @@ class EmbedDesigner {
lightColor: this.params.lightColor || savedConfig.lightColor || '#3b82f6',
darkColor: this.params.darkColor || savedConfig.darkColor || '#60a5fa',
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 || [])
};
}
@@ -53,7 +54,8 @@ class EmbedDesigner {
lightColor: '#3b82f6',
darkColor: '#60a5fa',
height: '400',
- themeMode: 'auto'
+ themeMode: 'auto',
+ filetree: []
};
}
@@ -64,7 +66,20 @@ class EmbedDesigner {
const config = JSON.parse(saved);
// Validate the loaded config has all required fields
if (config && typeof config === 'object') {
- return config;
+ // Ensure all properties have defaults
+ 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) {
@@ -103,6 +118,7 @@ class EmbedDesigner {
// Populate form with current config
document.getElementById('designer-prompt').value = this.config.prompt;
document.getElementById('designer-context').value = this.config.context.join(', ');
+ document.getElementById('designer-filetree').value = this.config.filetree.join('\n');
// Handle model selection
const modelSelect = document.getElementById('designer-model');
@@ -170,7 +186,7 @@ class EmbedDesigner {
setupDesignerEvents() {
// Form changes update preview
- ['designer-prompt', 'designer-context', 'designer-mode-select', 'designer-thinking', 'designer-max'].forEach(id => {
+ ['designer-prompt', 'designer-context', 'designer-mode-select', 'designer-thinking', 'designer-max', 'designer-filetree'].forEach(id => {
const element = document.getElementById(id);
element.addEventListener('input', () => this.updateConfigFromForm());
element.addEventListener('change', () => this.updateConfigFromForm());
@@ -300,7 +316,8 @@ class EmbedDesigner {
lightColor: '#3b82f6',
darkColor: '#60a5fa',
height: '400',
- themeMode: 'auto'
+ themeMode: 'auto',
+ filetree: []
};
// Update UI to reflect defaults
this.setupDesignerElements();
@@ -348,7 +365,8 @@ class EmbedDesigner {
lightColor: lightColorText ? lightColorText.value : '#3b82f6',
darkColor: darkColorText ? darkColorText.value : '#60a5fa',
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();
@@ -421,6 +439,7 @@ class EmbedDesigner {
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.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');
return `/embed-preview/?${params.toString()}`;
@@ -437,6 +456,7 @@ class EmbedDesigner {
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.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()}`;
}
@@ -518,27 +538,40 @@ class EmbedDesigner {
}
loadExample() {
- // Set example data
- this.config = {
- 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'
- };
+ // Set example values
+ document.getElementById('designer-prompt').value =
+`You are a senior React developer. I need help building a modern e-commerce product listing component.
+
+Requirements:
+- Use React hooks and functional components
+- Implement product filtering by category and price range
+- Add smooth animations for product cards
+- Make it fully responsive with a grid layout
+- 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.`;
- // Update all form elements
- this.setupDesignerElements();
- this.updatePreview();
- this.updateIframeSnippet();
- this.saveToLocalStorage();
+ 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!');
}
}