Spaces:
Configuration error
Configuration error
| class LoraSelector extends HTMLElement { | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: block; | |
| } | |
| .lora-container { | |
| position: relative; | |
| } | |
| .lora-select { | |
| width: 100%; | |
| background: #374151; | |
| border: 1px solid #4b5563; | |
| border-radius: 0.5rem; | |
| padding: 0.75rem; | |
| color: white; | |
| font-size: 1rem; | |
| cursor: pointer; | |
| appearance: none; | |
| background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); | |
| background-repeat: no-repeat; | |
| background-position: right 0.75rem center; | |
| background-size: 1rem; | |
| } | |
| .lora-select:focus { | |
| outline: none; | |
| border-color: #10b981; | |
| box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.3); | |
| } | |
| .loading { | |
| position: absolute; | |
| right: 10px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| display: none; | |
| } | |
| .spinner { | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid rgba(255, 255, 255, 0.3); | |
| border-radius: 50%; | |
| border-top-color: #10b981; | |
| animation: spin 1s ease-in-out infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| .error { | |
| color: #ef4444; | |
| font-size: 0.875rem; | |
| margin-top: 0.5rem; | |
| display: none; | |
| } | |
| </style> | |
| <div class="lora-container"> | |
| <select class="lora-select" id="loraSelect"> | |
| <option value="">Loading LoRA models...</option> | |
| </select> | |
| <div class="loading" id="loadingIndicator"> | |
| <div class="spinner"></div> | |
| </div> | |
| </div> | |
| <div class="error" id="errorMessage"></div> | |
| `; | |
| this.loadLoraModels(); | |
| } | |
| async loadLoraModels() { | |
| const selectElement = this.shadowRoot.getElementById('loraSelect'); | |
| const loadingIndicator = this.shadowRoot.getElementById('loadingIndicator'); | |
| const errorMessage = this.shadowRoot.getElementById('errorMessage'); | |
| try { | |
| loadingIndicator.style.display = 'block'; | |
| // Fetch LoRA models from Hugging Face API | |
| const response = await fetch('https://huggingface.co/api/models/Playtime-AI/Wan2.2-Loras'); | |
| const data = await response.json(); | |
| // Get safetensors files | |
| const safetensorsFiles = data.siblings.filter(file => file.rfilename.endsWith('.safetensors')); | |
| // Clear loading option | |
| selectElement.innerHTML = '<option value="">Select a LoRA model</option>'; | |
| // Add each LoRA model as an option | |
| safetensorsFiles.forEach(file => { | |
| const option = document.createElement('option'); | |
| const modelName = file.rfilename.replace('.safetensors', ''); | |
| option.value = JSON.stringify({ | |
| filename: file.rfilename, | |
| trigger: this.getTriggerWord(modelName) | |
| }); | |
| option.textContent = modelName; | |
| selectElement.appendChild(option); | |
| }); | |
| // Dispatch event when models are loaded | |
| this.dispatchEvent(new CustomEvent('loras-loaded', { | |
| detail: { models: safetensorsFiles }, | |
| bubbles: true | |
| })); | |
| } catch (error) { | |
| console.error('Error loading LoRA models:', error); | |
| selectElement.innerHTML = '<option value="">Failed to load models</option>'; | |
| errorMessage.textContent = 'Failed to load LoRA models. Please try again later.'; | |
| errorMessage.style.display = 'block'; | |
| } finally { | |
| loadingIndicator.style.display = 'none'; | |
| } | |
| } | |
| getTriggerWord(modelName) { | |
| const triggers = { | |
| 'Wan2.2-Disco-Diffusion': 'disco style', | |
| 'Wan2.2-Futuristic-City': 'futuristic cityscape', | |
| 'Wan2.2-Anime-Art': 'anime art style', | |
| 'Wan2.2-Cyberpunk': 'cyberpunk aesthetic', | |
| 'Wan2.2-Fantasy': 'fantasy illustration', | |
| 'Wan2.2-Photorealistic': 'photorealistic', | |
| 'Wan2.2-Watercolor': 'watercolor painting', | |
| 'Wan2.2-Oil-Painting': 'oil painting', | |
| 'Wan2.2-Sketch': 'pencil sketch', | |
| 'Wan2.2-Impressionist': 'impressionist painting' | |
| }; | |
| return triggers[modelName] || 'enhanced style'; | |
| } | |
| get selectedValue() { | |
| const selected = this.shadowRoot.getElementById('loraSelect').value; | |
| try { | |
| return JSON.parse(selected); | |
| } catch { | |
| return { filename: '', trigger: '' }; | |
| } | |
| } | |
| set selectedValue(value) { | |
| this.shadowRoot.getElementById('loraSelect').value = JSON.stringify(value); | |
| } | |
| } | |
| customElements.define('lora-selector', LoraSelector); | |