class EmbedPreview { constructor() { this.params = this.parseURLParams(); this.config = this.getInitialConfig(); this.selectedFiles = new Set(); // Track selected files this.init(); } parseURLParams() { const urlParams = new URLSearchParams(window.location.search); const params = {}; for (const [key, value] of urlParams.entries()) { params[key] = decodeURIComponent(value); } return params; } getInitialConfig() { return { prompt: this.params.prompt || '', context: this.params.context ? this.params.context.split(',').map(c => c.trim()) : [], model: this.params.model || 'gpt-4o', mode: this.params.agentMode || 'chat', thinking: this.params.thinking === 'true', max: this.params.max === 'true', 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()) : [], showDiff: this.params.showDiff === 'true', diffFilename: this.params.diffFilename || '', diffOldText: this.params.diffOldText ? decodeURIComponent(this.params.diffOldText) : '', diffNewText: this.params.diffNewText ? decodeURIComponent(this.params.diffNewText) : '', flashButton: this.params.flashButton || 'none' }; } init() { this.setupColors(); this.setupElements(); this.render(); this.setupResizeListener(); } setupColors() { const root = document.documentElement; // Determine if we should use dark mode let isDarkMode; if (this.config.themeMode === 'light') { isDarkMode = false; } else if (this.config.themeMode === 'dark') { isDarkMode = true; } else { // Auto mode - use system preference isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; } const baseColor = isDarkMode ? this.config.darkColor : this.config.lightColor; // Convert hex to RGB const hexToRgb = (hex) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; }; // Convert RGB to HSL const rgbToHsl = (r, g, b) => { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let h, s, l = (max + min) / 2; if (max === min) { h = s = 0; } else { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break; case g: h = ((b - r) / d + 2) / 6; break; case b: h = ((r - g) / d + 4) / 6; break; } } return { h: h * 360, s: s * 100, l: l * 100 }; }; // Convert HSL to RGB const hslToRgb = (h, s, l) => { h /= 360; s /= 100; l /= 100; let r, g, b; if (s === 0) { r = g = b = l; } else { const hue2rgb = (p, q, t) => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1/6) return p + (q - p) * 6 * t; if (t < 1/2) return q; if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; return p; }; const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r = hue2rgb(p, q, h + 1/3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1/3); } return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) }; }; const rgb = hexToRgb(baseColor); if (rgb) { // Get HSL values for color manipulation const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b); // Set primary color root.style.setProperty('--primary', `${rgb.r} ${rgb.g} ${rgb.b}`); // Generate color scheme based on primary color if (isDarkMode) { // Dark mode: darker backgrounds, lighter text const bgHsl = { ...hsl, s: Math.min(hsl.s * 0.15, 20), l: 8 }; const bg = hslToRgb(bgHsl.h, bgHsl.s, bgHsl.l); const mutedHsl = { ...hsl, s: Math.min(hsl.s * 0.2, 25), l: 15 }; const muted = hslToRgb(mutedHsl.h, mutedHsl.s, mutedHsl.l); const borderHsl = { ...hsl, s: Math.min(hsl.s * 0.25, 30), l: 20 }; const border = hslToRgb(borderHsl.h, borderHsl.s, borderHsl.l); root.style.setProperty('--background', `${bg.r} ${bg.g} ${bg.b}`); root.style.setProperty('--foreground', '248 250 252'); root.style.setProperty('--muted', `${muted.r} ${muted.g} ${muted.b}`); root.style.setProperty('--muted-foreground', '148 163 184'); root.style.setProperty('--border', `${border.r} ${border.g} ${border.b}`); } else { // Light mode: lighter backgrounds, darker text const bgHsl = { ...hsl, s: Math.min(hsl.s * 0.05, 5), l: 99 }; const bg = hslToRgb(bgHsl.h, bgHsl.s, bgHsl.l); const mutedHsl = { ...hsl, s: Math.min(hsl.s * 0.1, 10), l: 97 }; const muted = hslToRgb(mutedHsl.h, mutedHsl.s, mutedHsl.l); const borderHsl = { ...hsl, s: Math.min(hsl.s * 0.15, 15), l: 92 }; const border = hslToRgb(borderHsl.h, borderHsl.s, borderHsl.l); root.style.setProperty('--background', `${bg.r} ${bg.g} ${bg.b}`); root.style.setProperty('--foreground', '15 23 42'); root.style.setProperty('--muted', `${muted.r} ${muted.g} ${muted.b}`); root.style.setProperty('--muted-foreground', '100 116 139'); root.style.setProperty('--border', `${border.r} ${border.g} ${border.b}`); } // Set accent (slightly different from primary) const accentHsl = { ...hsl, l: Math.min(hsl.l + 10, 90) }; const accent = hslToRgb(accentHsl.h, accentHsl.s, accentHsl.l); root.style.setProperty('--accent', `${accent.r} ${accent.g} ${accent.b}`); } this.isDarkMode = isDarkMode; } setupElements() { const copyButton = document.getElementById('copy-button'); if (copyButton) { copyButton.addEventListener('click', () => this.handleCopy()); } const editButton = document.getElementById('edit-button'); if (editButton) { editButton.addEventListener('click', () => this.handleEdit()); } } render() { this.renderDiffView(); this.renderContextPills(); this.renderPromptText(); this.renderSettingsPills(); this.renderFileTree(); } renderContextPills() { const container = document.getElementById('context-pills'); if (!container) return; container.innerHTML = ''; // Render initial context from config this.config.context.forEach(context => { const pill = this.createContextPill(context, false); container.appendChild(pill); }); // Render selected files and folders this.selectedFiles.forEach(filePath => { const fileName = this.getFileName(filePath); const pill = this.createContextPill(fileName, true, filePath); pill.title = filePath; // Show full path on hover container.appendChild(pill); }); } createContextPill(context, isRemovable, fullPath = null) { const pill = document.createElement('div'); pill.className = 'px-2 py-0.5 rounded-lg text-[0.65rem] font-medium animate-slide-in flex items-center gap-1 flex-shrink-0 whitespace-nowrap'; let icon = ''; let content = ''; // Use dynamic color classes for all pills if (this.isDarkMode) { pill.className += ' bg-dynamic-primary/10 text-dynamic-foreground border border-dynamic-primary/30'; } else { pill.className += ' bg-dynamic-primary/0 text-dynamic-foreground border border-dynamic-primary/20'; } if (context.startsWith('@')) { // @mentions - just show the text content = '' + context + ''; } else if (context.startsWith('http://') || context.startsWith('https://')) { // Web URLs show world icon icon = ''; content = icon + '' + context + ''; } else if (context.startsWith('#')) { // Any hashtag context shows image icon icon = ''; // Remove hash from display content = icon + '' + context.substring(1) + ''; } else if (context.endsWith('/')) { // Folder context (ends with /) icon = ''; // Keep trailing slash for display content = icon + '' + context + ''; } else if (context.includes('.') || isRemovable) { // File context (contains a dot) or removable file from sidebar icon = ''; content = icon + '' + context + ''; } else { // Generic context content = '' + context + ''; } pill.innerHTML = content; // Add remove button for removable pills if (isRemovable && fullPath) { const removeBtn = document.createElement('button'); removeBtn.className = 'ml-0.5 text-dynamic-muted-foreground hover:text-dynamic-foreground transition-colors'; removeBtn.innerHTML = ''; removeBtn.addEventListener('click', (e) => { e.stopPropagation(); this.removeFileFromContext(fullPath); }); pill.appendChild(removeBtn); } return pill; } renderPromptText() { const promptText = document.getElementById('prompt-text'); const placeholder = document.getElementById('prompt-placeholder'); if (!promptText || !placeholder) return; if (this.config.prompt) { promptText.innerHTML = this.highlightMentions(this.config.prompt); placeholder.style.display = 'none'; } else { promptText.innerHTML = ''; placeholder.style.display = 'block'; } } renderSettingsPills() { const container = document.getElementById('settings-pills'); if (!container) return; container.innerHTML = ''; // Mode pill (first) const modePill = this.createSettingPill(this.capitalizeFirst(this.config.mode), 'mode'); container.appendChild(modePill); // Model pill (second) const modelPill = this.createSettingPill(this.config.model, 'model'); container.appendChild(modelPill); // Thinking pill (if enabled) if (this.config.thinking) { const thinkingPill = this.createSettingPill('Thinking', 'thinking'); container.appendChild(thinkingPill); } // MAX pill (if enabled) if (this.config.max) { const maxPill = this.createSettingPill('MAX', 'max'); container.appendChild(maxPill); } } createSettingPill(text, type) { const pill = document.createElement('div'); pill.className = 'rounded-full text-[10px] font-medium flex items-center gap-1'; let icon = ''; // Use different styling based on pill type if (type === 'mode') { // Mode pill keeps the background with reduced padding if (this.isDarkMode) { pill.className += ' px-2 py-1 bg-dynamic-primary/20 text-dynamic-foreground border border-dynamic-primary/30'; } else { pill.className += ' px-2 py-1 bg-dynamic-primary/10 text-dynamic-foreground border border-dynamic-primary/20'; } } else { // Model, thinking, and max pills only have text color pill.className += ' pl-0.5 text-dynamic-primary'; } switch (type) { case 'model': pill.innerHTML = '' + text + ''; break; case 'mode': // Add icon based on mode type const modeType = text.toLowerCase(); switch (modeType) { case 'agent': icon = ''; break; case 'chat': icon = ''; break; case 'manual': icon = ''; break; case 'cloud': icon = ''; break; default: icon = ''; } pill.innerHTML = icon + '' + text + ''; break; case 'thinking': // Brain icon for thinking mode icon = ''; pill.innerHTML = icon + '' + text + ''; break; case 'max': // Lightning bolt icon for MAX mode icon = ''; pill.innerHTML = icon + '' + text + ''; break; } return pill; } setupResizeListener() { let resizeTimeout; window.addEventListener('resize', () => { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { this.renderDiffView(); }, 100); }); } renderDiffView() { const diffView = document.getElementById('diff-view'); const diffViewInline = document.getElementById('diff-view-inline'); const promptText = document.getElementById('prompt-text'); if (!diffView || !diffViewInline) return; const viewportHeight = window.innerHeight; const isCompact = viewportHeight < 400; if (this.config.showDiff && (this.config.diffOldText || this.config.diffNewText)) { // Determine which view to show based on viewport height if (isCompact) { // Show floating diff view diffView.classList.remove('hidden'); diffView.classList.add('diff-enter'); diffViewInline.classList.add('hidden'); this.setupDiffContent('diff-view'); } else { // Show inline diff view diffViewInline.classList.remove('hidden'); diffView.classList.add('hidden'); this.setupDiffContent('diff-view-inline'); // Reset margin when switching to inline view if (promptText) { promptText.style.marginTop = '0'; } } } else { diffView.classList.add('hidden'); diffViewInline.classList.add('hidden'); // Reset margin when diff is hidden if (promptText) { promptText.style.marginTop = '0'; } } } setupDiffContent(containerId) { const container = document.getElementById(containerId); if (!container) return; // Count lines in old and new content const oldLines = this.config.diffOldText ? this.config.diffOldText.split('\n').length : 0; const newLines = this.config.diffNewText ? this.config.diffNewText.split('\n').length : 0; // Set filename with line counts const filenameElement = container.querySelector('[id$="-filename"]'); if (filenameElement) { const filename = this.config.diffFilename || 'untitled'; filenameElement.innerHTML = ` ${filename} +${newLines} -${oldLines} `; } // Set diff content const oldContent = container.querySelector('[id$="-old-content"]'); const newContent = container.querySelector('[id$="-new-content"]'); if (oldContent) { oldContent.textContent = this.config.diffOldText || '(empty)'; // Update colors based on dark mode if (this.isDarkMode) { oldContent.style.backgroundColor = 'rgba(127, 29, 29, 0.15)'; oldContent.style.color = '#fca5a5'; oldContent.style.borderColor = 'rgba(127, 29, 29, 0.3)'; } else { oldContent.style.backgroundColor = 'rgba(254, 226, 226, 0.7)'; oldContent.style.color = '#b91c1c'; oldContent.style.borderColor = 'rgba(252, 165, 165, 0.5)'; } } if (newContent) { newContent.textContent = this.config.diffNewText || '(empty)'; // Update colors based on dark mode if (this.isDarkMode) { newContent.style.backgroundColor = 'rgba(20, 83, 45, 0.15)'; newContent.style.color = '#86efac'; newContent.style.borderColor = 'rgba(20, 83, 45, 0.3)'; } else { newContent.style.backgroundColor = 'rgba(220, 252, 231, 0.7)'; newContent.style.color = '#15803d'; newContent.style.borderColor = 'rgba(134, 239, 172, 0.5)'; } } // Apply flash animation to buttons if configured const acceptButton = container.querySelector('[id$="-accept-diff"]'); const rejectButton = container.querySelector('[id$="-reject-diff"]'); if (this.config.flashButton === 'accept' && acceptButton) { acceptButton.classList.add('flash-animation'); // Remove from reject button if it exists if (rejectButton) rejectButton.classList.remove('flash-animation'); } else if (this.config.flashButton === 'reject' && rejectButton) { rejectButton.classList.add('flash-animation'); // Remove from accept button if it exists if (acceptButton) acceptButton.classList.remove('flash-animation'); } else { // Remove animation from both if 'none' if (acceptButton) acceptButton.classList.remove('flash-animation'); if (rejectButton) rejectButton.classList.remove('flash-animation'); } // Setup toggle button for floating view only if (containerId === 'diff-view') { const toggleButton = container.querySelector('#toggle-diff'); const diffContent = container.querySelector('#diff-content'); const promptText = document.getElementById('prompt-text'); if (toggleButton && diffContent && promptText) { // Remove existing listeners const newToggleButton = toggleButton.cloneNode(true); toggleButton.parentNode.replaceChild(newToggleButton, toggleButton); // Set initial state - collapsed when compact let isExpanded = false; diffContent.style.maxHeight = '0'; diffContent.style.overflow = 'hidden'; newToggleButton.querySelector('svg').style.transform = 'rotate(-90deg)'; // Function to update prompt margin based on diff view height const updatePromptMargin = () => { const diffHeight = container.offsetHeight; promptText.parentElement.style.paddingTop = `${diffHeight + 16}px`; // 16px for some breathing room }; // Set initial margin setTimeout(updatePromptMargin, 0); newToggleButton.addEventListener('click', () => { isExpanded = !isExpanded; if (isExpanded) { diffContent.style.maxHeight = diffContent.scrollHeight + 'px'; newToggleButton.querySelector('svg').style.transform = 'rotate(0deg)'; // Update margin after animation setTimeout(updatePromptMargin, 300); } else { diffContent.style.maxHeight = '0'; diffContent.style.overflow = 'hidden'; newToggleButton.querySelector('svg').style.transform = 'rotate(-90deg)'; // Update margin after animation setTimeout(updatePromptMargin, 300); } }); } } } highlightMentions(text) { return text.replaceAll(/@(https?:\/\/[^\s]+.*?|\w+\.\w+|\w+)/g, '@$1'); } capitalizeFirst(str) { return str.charAt(0).toUpperCase() + str.slice(1); } async handleCopy() { if (!this.config.prompt) { this.showNotification('No prompt to copy'); return; } await this.copyToClipboard(this.config.prompt); this.showNotification('Prompt copied to clipboard!'); } handleEdit() { // Get current query string const queryString = window.location.search; // Get current URL path parts const pathParts = window.location.pathname.split('/'); // Find index of 'embed-preview' and replace with 'embed' const embedPreviewIndex = pathParts.findIndex(part => part === 'embed-preview'); if (embedPreviewIndex !== -1) { pathParts[embedPreviewIndex] = 'embed'; } else { // Fallback: just append /embed/ if embed-preview not found pathParts.push('embed'); } // Construct new URL const newPath = pathParts.join('/'); const newUrl = window.location.origin + newPath + queryString; // Open in new tab window.open(newUrl, '_blank'); } async copyToClipboard(text) { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); } else { // Fallback const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); document.execCommand('copy'); textArea.remove(); } } showNotification(message) { const notification = document.getElementById('notification'); if (!notification) return; notification.textContent = message; notification.classList.remove('opacity-0'); notification.classList.add('opacity-100'); setTimeout(() => { notification.classList.remove('opacity-100'); notification.classList.add('opacity-0'); }, 2000); } addFileToContext(filePath) { if (!this.selectedFiles.has(filePath)) { this.selectedFiles.add(filePath); this.renderContextPills(); this.renderFileTree(); // Re-render to show selection } } getFileName(filePath) { // Extract just the filename or folder name from the path const cleanPath = filePath.endsWith('/') ? filePath.slice(0, -1) : filePath; const parts = cleanPath.split('/'); const name = parts[parts.length - 1]; // Add trailing slash back for folders to maintain context return filePath.endsWith('/') ? name + '/' : name; } removeFileFromContext(filePath) { if (this.selectedFiles.has(filePath)) { this.selectedFiles.delete(filePath); this.renderContextPills(); this.renderFileTree(); // Re-render to update selection } } getFullPath(node, currentPath = '') { // Helper to get the full path from a node return currentPath ? `${currentPath}/${node.name}` : node.name; } 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; // Check if it's a folder (ends with /) const isFolder = cleanPath.endsWith('/'); // Remove trailing slash for processing const processPath = isFolder ? cleanPath.slice(0, -1) : cleanPath; const parts = processPath.split('/').filter(p => p !== ''); // Filter out empty strings let current = tree; parts.forEach((part, index) => { if (!current[part]) { const isLastPart = index === parts.length - 1; current[part] = { name: part, isFile: isLastPart && !isFolder, isHighlighted: isLastPart && isHighlighted, children: {} }; } if (index < parts.length - 1 || (index === parts.length - 1 && !current[part].isFile)) { 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, parentPath) { 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 fullPath = parentPath ? `${parentPath}/${item.name}` : item.name; const itemElement = document.createElement('div'); // Check if file or folder is selected const contextPath = item.isFile ? fullPath : fullPath + '/'; const isSelected = this.selectedFiles.has(contextPath); // Add highlighting class if the file is marked or selected if (item.isHighlighted) { itemElement.className = 'flex items-center gap-1 py-0.5 px-1.5 bg-dynamic-primary/20 rounded text-xs text-dynamic-foreground font-medium transition-all'; } else if (isSelected) { 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 click handler for files and folders (but not starred items) if (!item.isHighlighted) { itemElement.addEventListener('click', () => { const contextPath = item.isFile ? fullPath : fullPath + '/'; if (isSelected) { this.removeFileFromContext(contextPath); } else { this.addFileToContext(contextPath); } }); itemElement.title = isSelected ? 'Click to remove from context' : 'Click to add to context'; itemElement.style.cursor = 'pointer'; } else { itemElement.style.cursor = 'default'; itemElement.title = 'Starred items cannot be selected'; } // 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 or selected, use primary color for icon if (item.isHighlighted || isSelected) { 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 = ``; } else { // Folder icon icon.innerHTML = ''; } // Add name const nameSpan = document.createElement('span'); nameSpan.className = 'truncate flex-1'; nameSpan.textContent = item.name; itemElement.appendChild(icon); itemElement.appendChild(nameSpan); // Add indicators for highlighted or selected items if (item.isHighlighted || isSelected) { const indicatorContainer = document.createElement('span'); indicatorContainer.className = 'ml-auto flex items-center gap-1'; // Add star for highlighted items if (item.isHighlighted) { const starIcon = document.createElement('span'); starIcon.className = 'text-dynamic-primary'; starIcon.innerHTML = ''; indicatorContainer.appendChild(starIcon); } // Add checkmark for selected items (not highlighted) if (isSelected && !item.isHighlighted) { const checkIcon = document.createElement('span'); checkIcon.className = 'text-dynamic-primary'; checkIcon.innerHTML = ''; indicatorContainer.appendChild(checkIcon); } itemElement.appendChild(indicatorContainer); } container.appendChild(itemElement); // Recursively render children if (!item.isFile && Object.keys(item.children).length > 0) { this.renderTreeNode(item.children, container, level + 1, fullPath); } }); } } // Initialize when DOM is ready document.addEventListener('DOMContentLoaded', () => { window.embedPreview = new EmbedPreview(); }); // Expose API for external usage window.EmbedPreviewAPI = { getPrompt: () => window.embedPreview?.config.prompt, setPrompt: (prompt) => { if (window.embedPreview) { window.embedPreview.config.prompt = prompt; window.embedPreview.renderPromptText(); } }, updateConfig: (config) => { if (window.embedPreview) { Object.assign(window.embedPreview.config, config); window.embedPreview.render(); } } };