Spaces:
Running
Running
| // Main application script | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initializeApp(); | |
| }); | |
| function initializeApp() { | |
| // Feather icons - initialize after components load | |
| setTimeout(() => { | |
| if (typeof feather !== 'undefined') { | |
| feather.replace(); | |
| } | |
| }, 100); | |
| // API Type switcher | |
| const apiTypeSelect = document.getElementById('apiType'); | |
| if (apiTypeSelect) { | |
| const apiKeyField = document.getElementById('apiKeyField'); | |
| const apiUrlField = document.getElementById('apiUrlField'); | |
| const modelField = document.getElementById('modelField'); | |
| apiTypeSelect.addEventListener('change', function() { | |
| // Reset all fields to hidden first | |
| apiKeyField.classList.add('hidden'); | |
| apiUrlField.classList.add('hidden'); | |
| modelField.classList.add('hidden'); | |
| // Show fields based on selection | |
| if (this.value === 'CUSTOM') { | |
| // For custom API, show all fields | |
| apiKeyField.classList.remove('hidden'); | |
| apiUrlField.classList.remove('hidden'); | |
| modelField.classList.remove('hidden'); | |
| } else if (this.value === 'MODELAPI_KEY') { | |
| apiKeyField.classList.remove('hidden'); | |
| modelField.classList.remove('hidden'); | |
| } else if (this.value === 'CUSTOM_API_URL') { | |
| apiUrlField.classList.remove('hidden'); | |
| modelField.classList.remove('hidden'); | |
| } | |
| }); | |
| } | |
| // Character upload preview | |
| const charUpload = document.getElementById('charUpload'); | |
| if (charUpload) { | |
| const charPreview = document.getElementById('charPreview'); | |
| const charAvatar = document.getElementById('charAvatar'); | |
| const charName = document.getElementById('charName'); | |
| const charVersion = document.getElementById('charVersion'); | |
| charUpload.addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| const fileName = file.name; | |
| const isPNG = fileName.endsWith('.png'); | |
| const isJSON = fileName.endsWith('.json'); | |
| if (!isPNG && !isJSON) { | |
| alert('Please upload a PNG or JSON character card.'); | |
| return; | |
| } | |
| if (isPNG) { | |
| const version = fileName.includes('v3') ? 'v3' : 'v2'; | |
| charName.textContent = fileName.replace('.png', '').replace('_v2', '').replace('_v3', ''); | |
| charVersion.textContent = `Chara Card ${version}`; | |
| } else if (isJSON) { | |
| const reader = new FileReader(); | |
| reader.onload = function(event) { | |
| try { | |
| const jsonData = JSON.parse(event.target.result); | |
| charName.textContent = jsonData.data?.name || jsonData.name || 'Unnamed Character'; | |
| charVersion.textContent = `JSON Character`; | |
| } catch (error) { | |
| console.error('Error parsing JSON:', error); | |
| charName.textContent = 'Invalid JSON'; | |
| charVersion.textContent = 'Error'; | |
| } | |
| }; | |
| reader.readAsText(file); | |
| } | |
| // Set random avatar for preview | |
| const seed = Math.floor(Math.random() * 100) + 1; | |
| charAvatar.src = `http://static.photos/abstract/200x200/${seed}`; | |
| charPreview.classList.remove('hidden'); | |
| }); | |
| } | |
| // Save system prompt | |
| const savePromptBtn = document.getElementById('savePrompt'); | |
| if (savePromptBtn) { | |
| const systemPromptText = document.getElementById('systemPrompt'); | |
| savePromptBtn.addEventListener('click', function() { | |
| const prompt = systemPromptText.value.trim(); | |
| if (prompt) { | |
| localStorage.setItem('systemPrompt', prompt); | |
| showToast('System prompt saved!', 'success'); | |
| } else { | |
| localStorage.removeItem('systemPrompt'); | |
| showToast('System prompt cleared.', 'info'); | |
| } | |
| }); | |
| // Load saved prompt | |
| const savedPrompt = localStorage.getItem('systemPrompt'); | |
| if (savedPrompt) { | |
| systemPromptText.value = savedPrompt; | |
| } | |
| } | |
| // Save persona | |
| const savePersonaBtn = document.getElementById('savePersona'); | |
| if (savePersonaBtn) { | |
| const personaName = document.getElementById('personaName'); | |
| const personaDesc = document.getElementById('personaDesc'); | |
| const personaAvatar = document.getElementById('personaAvatar'); | |
| savePersonaBtn.addEventListener('click', function() { | |
| const persona = { | |
| name: personaName.value.trim(), | |
| description: personaDesc.value.trim(), | |
| avatar: null | |
| }; | |
| if (personaAvatar.files[0]) { | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| persona.avatar = e.target.result; | |
| savePersonaData(persona); | |
| }; | |
| reader.readAsDataURL(personaAvatar.files[0]); | |
| } else { | |
| savePersonaData(persona); | |
| } | |
| }); | |
| function savePersonaData(persona) { | |
| localStorage.setItem('userPersona', JSON.stringify(persona)); | |
| showToast('Persona saved successfully!', 'success'); | |
| } | |
| // Load saved persona | |
| const savedPersona = localStorage.getItem('userPersona'); | |
| if (savedPersona) { | |
| try { | |
| const persona = JSON.parse(savedPersona); | |
| personaName.value = persona.name || ''; | |
| personaDesc.value = persona.description || ''; | |
| } catch (e) { | |
| console.error('Error loading persona:', e); | |
| } | |
| } | |
| } | |
| // Save LLM config | |
| const saveConfigBtn = document.getElementById('saveConfig'); | |
| if (saveConfigBtn) { | |
| const topPInput = document.getElementById('topP'); | |
| const topKInput = document.getElementById('topK'); | |
| const maxTokensInput = document.getElementById('maxTokens'); | |
| const contextSizeInput = document.getElementById('contextSize'); | |
| const apiKeyInput = document.getElementById('apiKey'); | |
| const apiUrlInput = document.getElementById('apiUrl'); | |
| const modelInput = document.getElementById('model'); | |
| saveConfigBtn.addEventListener('click', function() { | |
| const config = { | |
| apiType: apiTypeSelect.value, | |
| apiKey: apiKeyInput.value, | |
| apiUrl: apiUrlInput.value, | |
| model: modelInput.value, | |
| topP: parseFloat(topPInput.value), | |
| topK: parseInt(topKInput.value), | |
| maxTokens: parseInt(maxTokensInput.value), | |
| contextSize: parseInt(contextSizeInput.value), | |
| timestamp: new Date().toISOString() | |
| }; | |
| localStorage.setItem('llmConfig', JSON.stringify(config)); | |
| showToast('LLM configuration applied!', 'success'); | |
| }); | |
| // Load saved config | |
| const savedConfig = localStorage.getItem('llmConfig'); | |
| if (savedConfig) { | |
| try { | |
| const config = JSON.parse(savedConfig); | |
| apiTypeSelect.value = config.apiType || 'CUSTOM'; | |
| apiKeyInput.value = config.apiKey || ''; | |
| apiUrlInput.value = config.apiUrl || ''; | |
| modelInput.value = config.model || ''; | |
| topPInput.value = config.topP || 0.9; | |
| topKInput.value = config.topK || 40; | |
| maxTokensInput.value = config.maxTokens || 2048; | |
| contextSizeInput.value = config.contextSize || 12000; | |
| // Trigger change to show correct fields | |
| apiTypeSelect.dispatchEvent(new Event('change')); | |
| } catch (e) { | |
| console.error('Error loading config:', e); | |
| } | |
| } | |
| // Initialize API type display | |
| apiTypeSelect.dispatchEvent(new Event('change')); | |
| } | |
| } | |
| // Toast notification | |
| function showToast(message, type = 'info') { | |
| const toast = document.createElement('div'); | |
| toast.className = `fixed top-6 right-6 px-6 py-3 rounded-lg shadow-2xl z-50 transform transition-transform translate-x-full ${type === 'success' ? 'bg-green-800/90' : 'bg-blue-800/90'}`; | |
| toast.textContent = message; | |
| const icon = document.createElement('i'); | |
| icon.setAttribute('data-feather', type === 'success' ? 'check-circle' : 'info'); | |
| icon.className = 'inline mr-2'; | |
| toast.prepend(icon); | |
| document.body.appendChild(toast); | |
| feather.replace(); | |
| setTimeout(() => { | |
| toast.classList.remove('translate-x-full'); | |
| }, 10); | |
| setTimeout(() => { | |
| toast.classList.add('translate-x-full'); | |
| setTimeout(() => toast.remove(), 300); | |
| }, 3000); | |
| } | |
| // Export config for use in chat page | |
| window.getLLMConfig = function() { | |
| const config = localStorage.getItem('llmConfig'); | |
| return config ? JSON.parse(config) : null; | |
| }; | |
| window.getSystemPrompt = function() { | |
| return localStorage.getItem('systemPrompt') || ''; | |
| }; | |
| window.getUserPersona = function() { | |
| const persona = localStorage.getItem('userPersona'); | |
| return persona ? JSON.parse(persona) : null; | |
| }; |