/** * UI rendering and DOM manipulation */ // WebGPU/Model control getters function getModelSelect() { return document.getElementById('model-select'); } function getLoadModelBtn() { return document.getElementById('load-model-btn'); } function getLoadingProgress() { return document.getElementById('loading-progress'); } function getProgressFill() { return document.getElementById('progress-fill'); } function getProgressText() { return document.getElementById('progress-text'); } function getModelStatus() { return document.getElementById('model-status'); } function getWebGPUStatus() { return document.getElementById('webgpu-status'); } function getModelSection() { return document.getElementById('model-section'); } function getReloadModelBtn() { return document.getElementById('reload-model-btn'); } function getClearCacheBtn() { return document.getElementById('clear-cache-btn'); } function getCacheInfo() { return document.getElementById('cache-info'); } /** * Populate model selector with available models * @param {Array} models - Array of model configurations */ export function populateModelSelector(models) { const modelSelect = getModelSelect(); if (!modelSelect) return; modelSelect.innerHTML = ''; models.forEach(model => { const option = document.createElement('option'); option.value = model.id; const noteText = model.note ? ` - ${model.note}` : ''; option.textContent = `${model.label} (${model.size}${noteText})`; modelSelect.appendChild(option); }); } /** * Show/hide model section based on inference mode * @param {boolean} show - Whether to show the model section */ export function toggleModelSection(show) { const modelSection = getModelSection(); if (modelSection) { if (show) { modelSection.classList.remove('hidden'); } else { modelSection.classList.add('hidden'); } } } /** * Update loading progress * @param {number} percent - Progress percentage (0-100), or -1 for indeterminate */ export function updateLoadingProgress(percent) { const progressFill = getProgressFill(); const progressBar = getLoadingProgress(); // Handle indeterminate state (percent < 0) if (progressBar) { if (percent < 0) { progressBar.classList.add('indeterminate'); } else { progressBar.classList.remove('indeterminate'); } } if (progressFill) { progressFill.style.width = percent < 0 ? '30%' : `${percent}%`; } } /** * Show/hide loading progress * @param {boolean} show - Whether to show the progress bar */ export function showLoadingProgress(show) { const loadingProgress = getLoadingProgress(); if (loadingProgress) { loadingProgress.style.display = show ? 'block' : 'none'; } } /** * Show/hide the model input wrapper (dropdown + load button) * @param {boolean} show - Whether to show the input wrapper */ export function showModelInputWrapper(show) { const wrapper = document.querySelector('.model-input-wrapper'); if (wrapper) { wrapper.style.display = show ? 'flex' : 'none'; } } /** * Update model status display * @param {string} message - Status message * @param {string} type - Status type ('success', 'error', 'loading', or '') */ export function updateModelStatus(message, type = '') { const modelStatus = getModelStatus(); if (modelStatus) { modelStatus.textContent = message; modelStatus.className = 'model-status'; if (type) { modelStatus.classList.add(type); } modelStatus.style.display = message ? 'block' : 'none'; } } /** * Update WebGPU status display * @param {string} message - Status message * @param {boolean} available - Whether WebGPU is available */ export function updateWebGPUStatus(message, available) { const webgpuStatus = getWebGPUStatus(); if (webgpuStatus) { webgpuStatus.textContent = message; webgpuStatus.className = 'webgpu-status'; if (available) { webgpuStatus.classList.add('available'); } else { webgpuStatus.classList.add('unavailable'); } } } /** * Enable/disable model loading button * @param {boolean} enabled - Whether to enable the button */ export function setLoadModelButtonEnabled(enabled) { const loadModelBtn = getLoadModelBtn(); if (loadModelBtn) { loadModelBtn.disabled = !enabled; } } /** * Get selected model ID * @returns {string|null} */ export function getSelectedModelId() { const modelSelect = getModelSelect(); return modelSelect ? modelSelect.value : null; } /** * Update button states based on model load status * @param {boolean} modelLoaded - Whether the model is loaded */ export function updateButtonStates(modelLoaded) { const tooltipText = modelLoaded ? '' : 'Load a model first'; // Update live caption button const liveCaptionBtn = document.getElementById('start-live-caption-btn'); if (liveCaptionBtn) { liveCaptionBtn.disabled = !modelLoaded; liveCaptionBtn.title = tooltipText; // Update button text based on model state if (!modelLoaded) { liveCaptionBtn.textContent = 'Load Model Below'; } else { liveCaptionBtn.textContent = 'Start'; } } } /** * Set up event listeners (simplified - only for model loading) * @param {Function} onSend - Not used (kept for compatibility) * @param {Function} onLoadModel - Callback for load model button * @param {Function} onReloadModel - Callback for reload model button */ export function setupEventListeners(onSend, onLoadModel, onReloadModel) { // Load model button const loadModelBtn = getLoadModelBtn(); if (loadModelBtn && onLoadModel) { loadModelBtn.addEventListener('click', onLoadModel); } // Reload model button const reloadModelBtn = getReloadModelBtn(); if (reloadModelBtn && onReloadModel) { reloadModelBtn.addEventListener('click', onReloadModel); } } /** * Update cache info display * @param {number} usedBytes - Bytes used by cache */ export function updateCacheInfo(usedBytes) { const cacheInfoEl = getCacheInfo(); const clearCacheBtn = getClearCacheBtn(); if (!cacheInfoEl) return; if (usedBytes > 0) { const usedMB = usedBytes / 1024 / 1024; if (usedMB >= 1000) { cacheInfoEl.textContent = `${(usedMB / 1024).toFixed(1)} GB cached`; } else if (usedMB >= 1) { cacheInfoEl.textContent = `${usedMB.toFixed(0)} MB cached`; } else { cacheInfoEl.textContent = 'No models cached'; } if (clearCacheBtn) { clearCacheBtn.disabled = usedMB < 1; } } else { cacheInfoEl.textContent = 'No models cached'; if (clearCacheBtn) { clearCacheBtn.disabled = true; } } } /** * Set up clear cache button handler * @param {Function} onClearCache - Callback for clear cache button */ export function setupClearCacheHandler(onClearCache) { const clearCacheBtn = getClearCacheBtn(); if (clearCacheBtn && onClearCache) { clearCacheBtn.addEventListener('click', onClearCache); } } /** * Set clear cache button text * @param {string} text - Button text */ export function setClearCacheButtonText(text) { const clearCacheBtn = getClearCacheBtn(); if (clearCacheBtn) { clearCacheBtn.textContent = text; } }