Spaces:
Running
Running
| /** | |
| * Image Manager Web Component | |
| * Centralized management of all image prompts and configurations | |
| * Provides a single source of truth for all image URLs in the application | |
| */ | |
| class ImageManager extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.attachShadow({ mode: 'open' }); | |
| this.config = null; | |
| this.imageCache = new Map(); | |
| } | |
| connectedCallback() { | |
| this.loadConfig(); | |
| this.render(); | |
| } | |
| loadConfig() { | |
| // Try to load from JSON config element | |
| const configElement = document.getElementById('imagePromptsConfig'); | |
| if (configElement) { | |
| try { | |
| this.config = JSON.parse(configElement.textContent); | |
| } catch (e) { | |
| console.error('Failed to parse image prompts config:', e); | |
| this.config = this.getDefaultConfig(); | |
| } | |
| } else { | |
| this.config = this.getDefaultConfig(); | |
| } | |
| // Store in global window object for easy access | |
| window.imagePrompts = this.config; | |
| } | |
| getDefaultConfig() { | |
| return { | |
| hero: "https://static.photos/medical/800x600/1", | |
| productImages: [ | |
| "https://static.photos/medical/400x300/101", | |
| "https://static.photos/medical/400x300/102", | |
| "https://static.photos/medical/400x300/103", | |
| "https://static.photos/medical/400x300/104" | |
| ], | |
| about: "https://static.photos/workspace/800x600/201", | |
| testimonials: [ | |
| "https://static.photos/people/48x48/301", | |
| "https://static.photos/people/48x48/302", | |
| "https://static.photos/people/48x48/303" | |
| ], | |
| categories: { | |
| medical: "medical", | |
| workspace: "workspace", | |
| people: "people", | |
| abstract: "abstract" | |
| }, | |
| placeholder: "https://static.photos/abstract/100x100", | |
| fallback: "https://static.photos/abstract/400x300" | |
| }; | |
| } | |
| render() { | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: none; | |
| } | |
| .image-manager-panel { | |
| position: fixed; | |
| top: 100px; | |
| right: 20px; | |
| width: 300px; | |
| background: white; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.15); | |
| padding: 20px; | |
| z-index: 9999; | |
| display: none; | |
| font-family: -apple-system, BlinkMacSystemFont, sans-serif; | |
| } | |
| .image-manager-panel.active { | |
| display: block; | |
| } | |
| .image-manager-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 15px; | |
| padding-bottom: 10px; | |
| border-bottom: 1px solid #e5e5e5; | |
| } | |
| .image-manager-title { | |
| font-weight: bold; | |
| font-size: 16px; | |
| color: #333; | |
| } | |
| .image-manager-close { | |
| background: none; | |
| border: none; | |
| font-size: 20px; | |
| cursor: pointer; | |
| color: #666; | |
| } | |
| .image-manager-section { | |
| margin-bottom: 15px; | |
| } | |
| .image-manager-section-title { | |
| font-size: 14px; | |
| font-weight: 600; | |
| color: #555; | |
| margin-bottom: 8px; | |
| } | |
| .image-manager-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 5px; | |
| } | |
| .image-manager-item { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 8px; | |
| background: #f8f9fa; | |
| border-radius: 4px; | |
| font-size: 12px; | |
| } | |
| .image-manager-item:hover { | |
| background: #e9ecef; | |
| } | |
| .image-manager-url { | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| max-width: 180px; | |
| } | |
| .image-manager-copy { | |
| background: #5d9245; | |
| color: white; | |
| border: none; | |
| border-radius: 3px; | |
| padding: 3px 8px; | |
| font-size: 11px; | |
| cursor: pointer; | |
| } | |
| .image-manager-copy:hover { | |
| background: #3f7344; | |
| } | |
| .image-manager-toggle { | |
| position: fixed; | |
| top: 150px; | |
| right: 20px; | |
| background: #5d9245; | |
| color: white; | |
| border: none; | |
| border-radius: 50%; | |
| width: 40px; | |
| height: 40px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| z-index: 9998; | |
| box-shadow: 0 2px 10px rgba(0,0,0,0.2); | |
| } | |
| </style> | |
| <button class="image-manager-toggle"> | |
| <i class="fas fa-images"></i> | |
| </button> | |
| <div class="image-manager-panel"> | |
| <div class="image-manager-header"> | |
| <div class="image-manager-title">Image Prompts Manager</div> | |
| <button class="image-manager-close">×</button> | |
| </div> | |
| <div class="image-manager-section"> | |
| <div class="image-manager-section-title">Hero Image</div> | |
| <div class="image-manager-list"> | |
| <div class="image-manager-item"> | |
| <div class="image-manager-url" title="${this.config?.hero || ''}"> | |
| ${this.config?.hero ? this.config.hero.substring(0, 30) + '...' : ''} | |
| </div> | |
| <button class="image-manager-copy" data-url="${this.config?.hero || ''}">Copy</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="image-manager-section"> | |
| <div class="image-manager-section-title">Product Images (${this.config?.productImages?.length || 0})</div> | |
| <div class="image-manager-list"> | |
| ${this.config?.productImages?.map((url, index) => ` | |
| <div class="image-manager-item"> | |
| <div class="image-manager-url" title="${url}"> | |
| Product ${index + 1}: ${url.substring(0, 25)}... | |
| </div> | |
| <button class="image-manager-copy" data-url="${url}">Copy</button> | |
| </div> | |
| `).join('') || '<div style="padding: 8px; color: #666; font-size: 12px;">No product images configured</div>'} | |
| </div> | |
| </div> | |
| <div class="image-manager-section"> | |
| <div class="image-manager-section-title">Testimonial Images (${this.config?.testimonials?.length || 0})</div> | |
| <div class="image-manager-list"> | |
| ${this.config?.testimonials?.map((url, index) => ` | |
| <div class="image-manager-item"> | |
| <div class="image-manager-url" title="${url}"> | |
| Testimonial ${index + 1}: ${url.substring(0, 25)}... | |
| </div> | |
| <button class="image-manager-copy" data-url="${url}">Copy</button> | |
| </div> | |
| `).join('') || '<div style="padding: 8px; color: #666; font-size: 12px;">No testimonial images configured</div>'} | |
| </div> | |
| </div> | |
| <div class="image-manager-section"> | |
| <div class="image-manager-section-title">Configuration</div> | |
| <div class="image-manager-list"> | |
| <div class="image-manager-item"> | |
| <div>Categories: ${this.config?.categories ? Object.keys(this.config.categories).join(', ') : ''}</div> | |
| <button class="image-manager-copy" data-url="${JSON.stringify(this.config)}">Copy Config</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| this.addEventListeners(); | |
| } | |
| addEventListeners() { | |
| const toggleBtn = this.shadowRoot.querySelector('.image-manager-toggle'); | |
| const closeBtn = this.shadowRoot.querySelector('.image-manager-close'); | |
| const panel = this.shadowRoot.querySelector('.image-manager-panel'); | |
| const copyButtons = this.shadowRoot.querySelectorAll('.image-manager-copy'); | |
| if (toggleBtn && panel) { | |
| toggleBtn.addEventListener('click', () => { | |
| panel.classList.toggle('active'); | |
| }); | |
| } | |
| if (closeBtn && panel) { | |
| closeBtn.addEventListener('click', () => { | |
| panel.classList.remove('active'); | |
| }); | |
| } | |
| if (copyButtons) { | |
| copyButtons.forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| const url = e.currentTarget.getAttribute('data-url'); | |
| this.copyToClipboard(url); | |
| // Show feedback | |
| const originalText = e.currentTarget.textContent; | |
| e.currentTarget.textContent = 'Copied!'; | |
| e.currentTarget.style.background = '#28a745'; | |
| setTimeout(() => { | |
| e.currentTarget.textContent = originalText; | |
| e.currentTarget.style.background = '#5d9245'; | |
| }, 1500); | |
| }); | |
| }); | |
| } | |
| } | |
| copyToClipboard(text) { | |
| navigator.clipboard.writeText(text).then(() => { | |
| console.log('Image URL copied to clipboard:', text); | |
| }).catch(err => { | |
| console.error('Failed to copy:', err); | |
| // Fallback method | |
| const textArea = document.createElement('textarea'); | |
| textArea.value = text; | |
| document.body.appendChild(textArea); | |
| textArea.select(); | |
| document.execCommand('copy'); | |
| document.body.removeChild(textArea); | |
| }); | |
| } | |
| // Public API methods | |
| getImage(category, index = 0) { | |
| if (!this.config) return this.config?.fallback || ''; | |
| switch(category) { | |
| case 'hero': | |
| return this.config.hero; | |
| case 'product': | |
| return this.config.productImages[index] || this.config.fallback; | |
| case 'testimonial': | |
| return this.config.testimonials[index] || this.config.fallback; | |
| case 'about': | |
| return this.config.about; | |
| case 'placeholder': | |
| return this.config.placeholder; | |
| default: | |
| return this.config.fallback; | |
| } | |
| } | |
| updateImage(category, value, index = 0) { | |
| if (!this.config) return false; | |
| switch(category) { | |
| case 'hero': | |
| this.config.hero = value; | |
| break; | |
| case 'product': | |
| if (this.config.productImages && index < this.config.productImages.length) { | |
| this.config.productImages[index] = value; | |
| } | |
| break; | |
| case 'testimonial': | |
| if (this.config.testimonials && index < this.config.testimonials.length) { | |
| this.config.testimonials[index] = value; | |
| } | |
| break; | |
| case 'about': | |
| this.config.about = value; | |
| break; | |
| default: | |
| return false; | |
| } | |
| // Update the JSON config element | |
| const configElement = document.getElementById('imagePromptsConfig'); | |
| if (configElement) { | |
| configElement.textContent = JSON.stringify(this.config, null, 2); | |
| } | |
| return true; | |
| } | |
| static get observedAttributes() { | |
| return ['config']; | |
| } | |
| attributeChangedCallback(name, oldValue, newValue) { | |
| if (name === 'config' && oldValue !== newValue) { | |
| this.loadConfig(); | |
| this.render(); | |
| } | |
| } | |
| } | |
| customElements.define('image-manager', ImageManager); |