Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Amuse Model Alchemist</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.waves.min.js"></script> | |
| <style> | |
| .dropzone { | |
| border: 2px dashed #4b5563; | |
| transition: all 0.3s ease; | |
| } | |
| .dropzone.active { | |
| border-color: #3b82f6; | |
| background-color: rgba(59, 130, 246, 0.05); | |
| } | |
| .progress-bar { | |
| transition: width 0.3s ease; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-gray-100 min-h-screen"> | |
| <div id="vanta-bg" class="fixed inset-0 -z-10"></div> | |
| <div class="container mx-auto px-4 py-12"> | |
| <!-- Header --> | |
| <header class="text-center mb-12"> | |
| <div class="inline-block bg-gradient-to-r from-blue-500 to-purple-600 p-2 rounded-lg mb-4"> | |
| <i data-feather="cpu" class="w-12 h-12 text-white"></i> | |
| </div> | |
| <h1 class="text-4xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-500"> | |
| Amuse Model Alchemist | |
| </h1> | |
| <p class="text-xl text-gray-300 max-w-2xl mx-auto"> | |
| Transform your Stable Diffusion models into ONNX/Olive artifacts with alchemical precision | |
| </p> | |
| </header> | |
| <!-- Main Card --> | |
| <div class="bg-gray-800/80 backdrop-blur-lg rounded-xl shadow-2xl overflow-hidden max-w-4xl mx-auto"> | |
| <!-- Tabs --> | |
| <div class="flex border-b border-gray-700"> | |
| <button class="tab-btn active px-6 py-4 font-medium text-blue-400 border-b-2 border-blue-400"> | |
| <i data-feather="upload" class="mr-2"></i> Convert Model | |
| </button> | |
| <button class="tab-btn px-6 py-4 font-medium text-gray-400 hover:text-white"> | |
| <i data-feather="terminal" class="mr-2"></i> CLI Mode | |
| </button> | |
| <button class="tab-btn px-6 py-4 font-medium text-gray-400 hover:text-white" data-tab="settings"> | |
| <i data-feather="settings" class="mr-2"></i> Settings | |
| </button> | |
| </div> | |
| <!-- Content --> | |
| <div class="p-8" id="tabContent"> | |
| <!-- Settings Tab Content --> | |
| <div id="settingsTab" class="hidden"> | |
| <div class="space-y-6"> | |
| <h2 class="text-2xl font-bold mb-6">Application Settings</h2> | |
| <div class="bg-gray-700/50 rounded-lg p-6"> | |
| <h3 class="text-lg font-semibold mb-4 flex items-center"> | |
| <i data-feather="save" class="mr-2"></i> Storage Settings | |
| </h3> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">Default Output Directory</label> | |
| <div class="flex"> | |
| <input type="text" id="outputDir" class="flex-1 bg-gray-700 border border-gray-600 rounded-l-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Path to output directory"> | |
| <button id="browseDirBtn" class="px-4 py-2 bg-gray-600 hover:bg-gray-500 rounded-r-lg"> | |
| <i data-feather="folder" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label class="flex items-center"> | |
| <input type="checkbox" id="createSubdir" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2" checked> | |
| <span>Create timestamped subdirectory for each conversion</span> | |
| </label> | |
| </div> | |
| <div> | |
| <label class="flex items-center"> | |
| <input type="checkbox" id="overwriteFiles" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2"> | |
| <span>Overwrite existing files</span> | |
| </label> | |
| </div> | |
| <div> | |
| <label class="flex items-center"> | |
| <input type="checkbox" id="saveSettings" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2" checked> | |
| <span>Save settings on exit</span> | |
| </label> | |
| </div> | |
| <div> | |
| <label class="flex items-center"> | |
| <input type="checkbox" id="autoClose" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2" checked> | |
| <span>Auto-close after successful conversion</span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex justify-end"> | |
| <button id="saveSettingsBtn" class="px-6 py-2.5 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 rounded-lg font-medium flex items-center justify-center"> | |
| <i data-feather="save" class="mr-2"></i> Save Settings | |
| </button> | |
| </div> | |
| <!-- Current Settings Summary --> | |
| <div class="bg-gray-700/50 rounded-lg p-4"> | |
| <button id="settingsSummaryToggle" class="flex items-center text-blue-400 hover:text-blue-300 w-full justify-between"> | |
| <span class="font-medium">Current Settings Summary</span> | |
| <i data-feather="chevron-down" class="w-4 h-4"></i> | |
| </button> | |
| <div id="settingsSummary" class="hidden mt-4 text-sm space-y-2"> | |
| <div><span class="text-gray-400">Output Directory:</span> <span id="currentOutputDir"></span></div> | |
| <div><span class="text-gray-400">Create Subdirectories:</span> <span id="currentSubdir"></span></div> | |
| <div><span class="text-gray-400">Overwrite Files:</span> <span id="currentOverwrite"></span></div> | |
| <div><span class="text-gray-400">Save Settings:</span> <span id="currentSaveSettings"></span></div> | |
| <div><span class="text-gray-400">Auto-Close:</span> <span id="currentAutoClose"></span></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Convert Model Tab Content --> | |
| <div id="convertTab"> | |
| <!-- Drop Zone --> | |
| <div id="dropzone" class="dropzone rounded-lg p-12 text-center mb-8 cursor-pointer"> | |
| <div class="flex flex-col items-center justify-center space-y-4"> | |
| <i data-feather="upload-cloud" class="w-12 h-12 text-gray-400"></i> | |
| <h3 class="text-xl font-semibold">Drag & Drop Model Files Here</h3> | |
| <p class="text-gray-400">or click to browse (.ckpt, .safetensors, or Diffusers folder)</p> | |
| <input type="file" id="fileInput" class="hidden" webkitdirectory mozdirectory msdirectory odirectory directory multiple> | |
| <button id="browseBtn" class="mt-4 px-6 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg font-medium"> | |
| Select Files | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Selected Files --> | |
| <div id="selectedFiles" class="hidden mb-8"> | |
| <h3 class="text-lg font-semibold mb-3 flex items-center"> | |
| <i data-feather="file" class="mr-2"></i> Selected Files | |
| </h3> | |
| <div class="bg-gray-700/50 rounded-lg p-4 max-h-48 overflow-y-auto"> | |
| <div id="fileList" class="space-y-2"></div> | |
| </div> | |
| </div> | |
| <!-- Conversion Options --> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8"> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">Target Hardware</label> | |
| <select class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="directml">DirectML (Windows Default)</option> | |
| <option value="cuda">NVIDIA CUDA</option> | |
| <option value="cpu">CPU (OpenVINO)</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">Precision</label> | |
| <select class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="fp16">FP16 (Recommended)</option> | |
| <option value="int8">INT8 (Quantized)</option> | |
| <option value="fp32">FP32 (Full Precision)</option> | |
| </select> | |
| </div> | |
| </div> | |
| <!-- Advanced Options --> | |
| <div class="mb-6"> | |
| <button id="advancedToggle" class="flex items-center text-blue-400 hover:text-blue-300"> | |
| <i data-feather="chevron-down" class="mr-2 w-4 h-4"></i> Advanced Options | |
| </button> | |
| <div id="advancedOptions" class="hidden mt-4 space-y-4"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">Model Type</label> | |
| <select class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="auto">Auto-detect</option> | |
| <option value="sd1">SD v1.x</option> | |
| <option value="sd2">SD v2.x</option> | |
| <option value="sdxl">SDXL</option> | |
| <option value="sd3">SD3</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">Output Format</label> | |
| <select class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="olive">Olive Optimized</option> | |
| <option value="onnx">Raw ONNX</option> | |
| <option value="amuse">Amuse Ready</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="flex items-center"> | |
| <input type="checkbox" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2"> | |
| <span>Run Smoke Test After Conversion</span> | |
| </label> | |
| </div> | |
| <div> | |
| <label class="flex items-center"> | |
| <input type="checkbox" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-blue-500 mr-2"> | |
| <span>Generate Amuse Import Package</span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Legal Warning --> | |
| <div class="bg-yellow-900/30 border border-yellow-800 rounded-lg p-4 mb-8"> | |
| <div class="flex items-start"> | |
| <i data-feather="alert-triangle" class="text-yellow-400 mr-3 mt-1"></i> | |
| <div> | |
| <h4 class="font-medium text-yellow-300 mb-1">Legal Notice</h4> | |
| <p class="text-sm text-yellow-200"> | |
| You must have the necessary rights to convert any model. By proceeding, you acknowledge that you have verified the model's license terms. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Action Buttons --> | |
| <div class="flex flex-col sm:flex-row justify-end space-y-3 sm:space-y-0 sm:space-x-4"> | |
| <button class="px-6 py-2.5 bg-gray-700 hover:bg-gray-600 rounded-lg font-medium flex items-center justify-center"> | |
| <i data-feather="save" class="mr-2"></i> Save Settings | |
| </button> | |
| <button id="convertBtn" class="px-6 py-2.5 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 rounded-lg font-medium flex items-center justify-center"> | |
| <i data-feather="zap" class="mr-2"></i> Convert Model | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Progress Modal --> | |
| <div id="progressModal" class="hidden fixed inset-0 bg-black/70 z-50 flex items-center justify-center p-4"> | |
| <div class="bg-gray-800 rounded-xl shadow-2xl w-full max-w-2xl"> | |
| <div class="p-6 border-b border-gray-700"> | |
| <h3 class="text-xl font-semibold flex items-center"> | |
| <i data-feather="rotate-cw" class="mr-2 animate-spin"></i> Converting Model | |
| </h3> | |
| </div> | |
| <div class="p-6"> | |
| <div class="mb-4"> | |
| <div class="flex justify-between text-sm mb-1"> | |
| <span>Progress</span> | |
| <span id="progressPercent">0%</span> | |
| </div> | |
| <div class="w-full bg-gray-700 rounded-full h-2.5"> | |
| <div id="progressBar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-700/50 rounded-lg p-4 h-48 overflow-y-auto"> | |
| <div id="progressLog" class="font-mono text-sm text-gray-300 space-y-1"> | |
| <div>> Initializing model conversion...</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="p-4 border-t border-gray-700 flex justify-end"> | |
| <button id="cancelBtn" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg mr-3"> | |
| Cancel | |
| </button> | |
| <button id="hideModalBtn" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg disabled:opacity-50" disabled> | |
| Close | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Settings management | |
| function loadSettings() { | |
| try { | |
| // In a real app, this would load from settings.json file | |
| // const settingsData = fs.readFileSync(path.join(app.getPath('userData'), 'settings.json'), 'utf8'); | |
| // const settings = JSON.parse(settingsData); | |
| const settings = JSON.parse(localStorage.getItem('ama_settings')) || { | |
| outputDir: '', | |
| createSubdir: true, | |
| overwriteFiles: false, | |
| saveSettings: true, | |
| autoClose: true | |
| }; | |
| document.getElementById('outputDir').value = settings.outputDir; | |
| document.getElementById('createSubdir').checked = settings.createSubdir; | |
| document.getElementById('overwriteFiles').checked = settings.overwriteFiles; | |
| document.getElementById('saveSettings').checked = settings.saveSettings; | |
| document.getElementById('autoClose').checked = settings.autoClose; | |
| updateSettingsSummary(); | |
| return settings; | |
| } catch (e) { | |
| console.error('Error loading settings:', e); | |
| return { | |
| outputDir: '', | |
| createSubdir: true, | |
| overwriteFiles: false, | |
| saveSettings: true, | |
| autoClose: true | |
| }; | |
| } | |
| } | |
| function saveSettings() { | |
| const settings = { | |
| outputDir: document.getElementById('outputDir').value, | |
| createSubdir: document.getElementById('createSubdir').checked, | |
| overwriteFiles: document.getElementById('overwriteFiles').checked, | |
| saveSettings: document.getElementById('saveSettings').checked, | |
| autoClose: document.getElementById('autoClose').checked | |
| }; | |
| localStorage.setItem('ama_settings', JSON.stringify(settings)); | |
| // In a real app, this would save to settings.json file | |
| // const settingsJSON = JSON.stringify(settings, null, 2); | |
| // fs.writeFileSync(path.join(app.getPath('userData'), 'settings.json'), settingsJSON); | |
| updateSettingsSummary(); | |
| return settings; | |
| } | |
| // Error handling for settings loading | |
| function handleSettingsError(error) { | |
| console.error('Settings error:', error); | |
| const defaultSettings = { | |
| outputDir: '', | |
| createSubdir: true, | |
| overwriteFiles: false, | |
| saveSettings: true, | |
| autoClose: true | |
| }; | |
| localStorage.setItem('ama_settings', JSON.stringify(defaultSettings)); | |
| return defaultSettings; | |
| } | |
| function updateSettingsSummary() { | |
| const settings = JSON.parse(localStorage.getItem('ama_settings')) || {}; | |
| document.getElementById('currentOutputDir').textContent = settings.outputDir || 'Not set'; | |
| document.getElementById('currentSubdir').textContent = settings.createSubdir ? 'Yes' : 'No'; | |
| document.getElementById('currentOverwrite').textContent = settings.overwriteFiles ? 'Yes' : 'No'; | |
| document.getElementById('currentSaveSettings').textContent = settings.saveSettings ? 'Yes' : 'No'; | |
| document.getElementById('currentAutoClose').textContent = settings.autoClose ? 'Yes' : 'No'; | |
| } | |
| // Initialize Vanta.js background | |
| VANTA.WAVES({ | |
| el: "#vanta-bg", | |
| mouseControls: true, | |
| touchControls: true, | |
| gyroControls: false, | |
| minHeight: 200.00, | |
| minWidth: 200.00, | |
| scale: 1.00, | |
| scaleMobile: 1.00, | |
| color: 0x1e293b, | |
| shininess: 50.00, | |
| waveHeight: 15.00, | |
| waveSpeed: 0.75, | |
| zoom: 0.85 | |
| }); | |
| // Initialize Feather Icons | |
| document.addEventListener('DOMContentLoaded', function() { | |
| feather.replace(); | |
| // Tab switching | |
| const tabBtns = document.querySelectorAll('.tab-btn'); | |
| const tabContent = document.getElementById('tabContent'); | |
| const convertTab = document.getElementById('convertTab'); | |
| const settingsTab = document.getElementById('settingsTab'); | |
| tabBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| tabBtns.forEach(b => b.classList.remove('active', 'text-blue-400', 'border-blue-400')); | |
| tabBtns.forEach(b => b.classList.add('text-gray-400')); | |
| btn.classList.add('active', 'text-blue-400', 'border-blue-400'); | |
| btn.classList.remove('text-gray-400'); | |
| if (btn.dataset.tab === 'settings') { | |
| convertTab.style.display = 'none'; | |
| settingsTab.style.display = 'block'; | |
| } else { | |
| settingsTab.style.display = 'none'; | |
| convertTab.style.display = 'block'; | |
| } | |
| }); | |
| }); | |
| // Settings summary toggle | |
| document.getElementById('settingsSummaryToggle').addEventListener('click', () => { | |
| const summary = document.getElementById('settingsSummary'); | |
| const icon = document.getElementById('settingsSummaryToggle').querySelector('i'); | |
| if (summary.classList.contains('hidden')) { | |
| summary.classList.remove('hidden'); | |
| icon.setAttribute('data-feather', 'chevron-up'); | |
| } else { | |
| summary.classList.add('hidden'); | |
| icon.setAttribute('data-feather', 'chevron-down'); | |
| } | |
| feather.replace(); | |
| }); | |
| // Load settings on startup | |
| loadSettings(); | |
| // Save settings button | |
| document.getElementById('saveSettingsBtn').addEventListener('click', saveSettings); | |
| // Advanced options toggle | |
| const advancedToggle = document.getElementById('advancedToggle'); | |
| const advancedOptions = document.getElementById('advancedOptions'); | |
| advancedToggle.addEventListener('click', () => { | |
| const icon = advancedToggle.querySelector('i'); | |
| if (advancedOptions.classList.contains('hidden')) { | |
| advancedOptions.classList.remove('hidden'); | |
| icon.setAttribute('data-feather', 'chevron-up'); | |
| } else { | |
| advancedOptions.classList.add('hidden'); | |
| icon.setAttribute('data-feather', 'chevron-down'); | |
| } | |
| feather.replace(); | |
| }); | |
| // File dropzone functionality | |
| const dropzone = document.getElementById('dropzone'); | |
| const fileInput = document.getElementById('fileInput'); | |
| const browseBtn = document.getElementById('browseBtn'); | |
| const selectedFiles = document.getElementById('selectedFiles'); | |
| const fileList = document.getElementById('fileList'); | |
| // Handle drag and drop | |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
| dropzone.addEventListener(eventName, preventDefaults, false); | |
| }); | |
| function preventDefaults(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| } | |
| ['dragenter', 'dragover'].forEach(eventName => { | |
| dropzone.addEventListener(eventName, highlight, false); | |
| }); | |
| ['dragleave', 'drop'].forEach(eventName => { | |
| dropzone.addEventListener(eventName, unhighlight, false); | |
| }); | |
| function highlight() { | |
| dropzone.classList.add('active'); | |
| } | |
| function unhighlight() { | |
| dropzone.classList.remove('active'); | |
| } | |
| dropzone.addEventListener('drop', handleDrop, false); | |
| browseBtn.addEventListener('click', () => fileInput.click()); | |
| fileInput.addEventListener('change', function() { | |
| handleFiles(this.files); | |
| }); | |
| function handleDrop(e) { | |
| const dt = e.dataTransfer; | |
| const files = dt.files; | |
| handleFiles(files); | |
| } | |
| function handleFiles(files) { | |
| if (files.length > 0) { | |
| selectedFiles.classList.remove('hidden'); | |
| fileList.innerHTML = ''; | |
| for (let i = 0; i < Math.min(files.length, 5); i++) { | |
| const file = files[i]; | |
| const fileItem = document.createElement('div'); | |
| fileItem.className = 'flex items-center text-sm'; | |
| fileItem.innerHTML = ` | |
| <i data-feather="file" class="mr-2 text-gray-400"></i> | |
| <span class="truncate flex-1">${file.name}</span> | |
| <span class="text-gray-400 ml-2">${formatFileSize(file.size)}</span> | |
| `; | |
| fileList.appendChild(fileItem); | |
| } | |
| if (files.length > 5) { | |
| const moreFiles = document.createElement('div'); | |
| moreFiles.className = 'text-sm text-gray-400'; | |
| moreFiles.textContent = `+ ${files.length - 5} more files`; | |
| fileList.appendChild(moreFiles); | |
| } | |
| feather.replace(); | |
| } | |
| } | |
| function formatFileSize(bytes) { | |
| if (bytes === 0) return '0 Bytes'; | |
| const k = 1024; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
| } | |
| // Conversion simulation | |
| const convertBtn = document.getElementById('convertBtn'); | |
| const progressModal = document.getElementById('progressModal'); | |
| const progressBar = document.getElementById('progressBar'); | |
| const progressPercent = document.getElementById('progressPercent'); | |
| const progressLog = document.getElementById('progressLog'); | |
| const cancelBtn = document.getElementById('cancelBtn'); | |
| const hideModalBtn = document.getElementById('hideModalBtn'); | |
| convertBtn.addEventListener('click', () => { | |
| progressModal.classList.remove('hidden'); | |
| simulateConversion(); | |
| }); | |
| cancelBtn.addEventListener('click', () => { | |
| progressModal.classList.add('hidden'); | |
| resetProgress(); | |
| }); | |
| hideModalBtn.addEventListener('click', () => { | |
| progressModal.classList.add('hidden'); | |
| }); | |
| function resetProgress() { | |
| progressBar.style.width = '0%'; | |
| progressPercent.textContent = '0%'; | |
| progressLog.innerHTML = '<div>> Initializing model conversion...</div>'; | |
| hideModalBtn.disabled = true; | |
| } | |
| function simulateConversion() { | |
| let progress = 0; | |
| const steps = [ | |
| {percent: 10, message: "> Detecting model type and components..."}, | |
| {percent: 25, message: "> Preparing model for ONNX export..."}, | |
| {percent: 40, message: "> Exporting text encoder to ONNX format..."}, | |
| {percent: 55, message: "> Exporting UNet to ONNX format..."}, | |
| {percent: 70, message: "> Exporting VAE to ONNX format..."}, | |
| {percent: 85, message: "> Running Olive optimization passes..."}, | |
| {percent: 95, message: "> Performing smoke test validation..."}, | |
| {percent: 100, message: "> Conversion complete! Output saved to models/exported_model"} | |
| ]; | |
| // Create logs directory if it doesn't exist | |
| if (!localStorage.getItem('ama_logs')) { | |
| localStorage.setItem('ama_logs', JSON.stringify([])); | |
| } | |
| const interval = setInterval(() => { | |
| if (progress >= steps.length) { | |
| clearInterval(interval); | |
| hideModalBtn.disabled = false; | |
| return; | |
| } | |
| const step = steps[progress]; | |
| progressBar.style.width = `${step.percent}%`; | |
| progressPercent.textContent = `${step.percent}%`; | |
| const logEntry = document.createElement('div'); | |
| logEntry.textContent = step.message; | |
| progressLog.appendChild(logEntry); | |
| progressLog.scrollTop = progressLog.scrollHeight; | |
| // If this is the last step | |
| if (progress === steps.length - 1) { | |
| const settings = JSON.parse(localStorage.getItem('ama_settings')) || {}; | |
| // Save log | |
| const logContent = Array.from(progressLog.children).map(el => el.textContent).join('\n'); | |
| const logs = JSON.parse(localStorage.getItem('ama_logs') || '[]'); | |
| logs.push({ | |
| timestamp: new Date().toISOString(), | |
| content: logContent | |
| }); | |
| localStorage.setItem('ama_logs', JSON.stringify(logs)); | |
| console.log('Log content saved:\n', logContent); | |
| if (settings.autoClose) { | |
| const completeMsg = document.createElement('div'); | |
| completeMsg.className = 'text-green-400'; | |
| completeMsg.textContent = '> Conversie voltooid — programma sluit nu automatisch.'; | |
| progressLog.appendChild(completeMsg); | |
| setTimeout(() => { | |
| progressModal.classList.add('hidden'); | |
| resetProgress(); | |
| // In a real app, this would close the window | |
| // window.close(); | |
| }, 3000); | |
| } else { | |
| hideModalBtn.disabled = false; | |
| } | |
| } | |
| progress++; | |
| }, 1500); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |