From fb3607e51fca0d09acaa8e7729f12ec7bcfbdde0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Kadir=20Ak=C4=B1n?= Date: Wed, 18 Jun 2025 17:28:14 +0300 Subject: [PATCH] update --- _layouts/embed-preview.html | 32 +++ _layouts/embed.html | 60 +++++- embed-preview-script.js | 58 ++++- embed-script.js | 414 +++++++++++++++++++++++++++++++----- 4 files changed, 503 insertions(+), 61 deletions(-) diff --git a/_layouts/embed-preview.html b/_layouts/embed-preview.html index 3e2538f..0a35f12 100644 --- a/_layouts/embed-preview.html +++ b/_layouts/embed-preview.html @@ -54,6 +54,38 @@ + + +
diff --git a/_layouts/embed.html b/_layouts/embed.html index 724fbb2..26c3247 100644 --- a/_layouts/embed.html +++ b/_layouts/embed.html @@ -50,6 +50,22 @@
+ +
+
+ + +
+
+
@@ -65,14 +81,6 @@ class="w-full p-3 bg-dynamic-background border border-dynamic-border rounded-lg text-sm resize-none focus-ring custom-scrollbar touch-target" rows="8" placeholder="Enter your prompt..."> -
- - -
@@ -250,6 +258,42 @@ Add * at the end of a filename to highlight it (e.g., main.js*)

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