shubeydoo's picture
Liquid AI LFM2-VL-450M-WebGPU Demo
accf76b
/**
* 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;
}
}