import { BundleType, getBundleType, getVersion } from '@tauri-apps/api/app' import { invoke } from '@tauri-apps/api/core' import { listen } from '@tauri-apps/api/event' import { openUrl } from '@tauri-apps/plugin-opener' import { relaunch } from '@tauri-apps/plugin-process' import { check, type Update } from '@tauri-apps/plugin-updater' import { initOcrRefinement } from './ocrRefinement' import './styles.css' type CaptureMode = 'raw-proxy' | 'spool-folder' | 'both' type AppConfig = { mode: CaptureMode listenAddr: string forwardAddr: string forwardPrinter: string spoolDir: string apiBaseUrl: string issuer: string clientId: string stationId: string installationId: string deviceId?: string | null } type SystemPrinter = { name: string driver: string port: string address?: string | null isVirtual: boolean } type VirtualPrinterStatus = { installed: boolean name: string detail: string } type Barcode = { format: string; value: string } type PalletPrintStatus = 'new-pallet' | 'reprint-same' | 'reprint-changed' | 'reprint-unreadable' type PalletFieldChange = { field: string previous: string current: string } type PalletPrintComparison = { palletId: string status: PalletPrintStatus occurrence: number previousCapturedAt?: string | null changes: PalletFieldChange[] } type Job = { id: string source: string label: string bytes: number barcodes: Barcode[] palletPrint?: PalletPrintComparison | null capturedAt: string forwarded: boolean error?: string | null } type Status = { running: boolean mode: CaptureMode pending: number jobs: number } type LoginStart = { userCode: string verificationUri: string verificationUriComplete?: string | null expiresIn: number interval: number } type LoginPoll = { status: string accessStatus?: string | null role?: string | null } type AuthStatus = { signedIn: boolean accessStatus: string role?: string | null } type DebugCheck = { name: string ok: boolean detail: string } type DebugSnapshot = { platform: string arch: string appVersion: string dataDir: string captureStorage: string captureRunning: boolean captureMode: CaptureMode pending: number jobs: number signedIn: boolean forwardPrinter: string listenAddr: string forwardAddr: string clientId: string virtualPrinter: string installedPrinters: number recentErrors: string[] checks: DebugCheck[] } type ManualReleaseDownload = { url: string sha256?: string } type ManualRelease = { version: string notes?: string downloads: Record } type ManualUpdate = { version: string url: string } const app = document.querySelector('#app')! let config: AppConfig let jobs: Job[] = [] let status: Status = { running: false, mode: 'raw-proxy', pending: 0, jobs: 0 } let loginTimer: number | undefined let currentVersion = '0.1.24' let bundleType: BundleType | null = null let availableUpdate: Update | null = null let availableManualUpdate: ManualUpdate | null = null let updateBusy = false let captureError: string | null = null let systemPrinters: SystemPrinter[] = [] let virtualPrinterStatus: VirtualPrinterStatus = { installed: false, name: 'FieldLens Print', detail: 'Checking FieldLens queues…', } let authStatus: AuthStatus = { signedIn: false, accessStatus: 'signed-out', role: null } let debugEnabled = false let debugSnapshot: DebugSnapshot | null = null app.innerHTML = `
FL

FieldLens Print

Capture and inspect print jobs before the printer driver renders them.

Loading
PRINTER ROUTING

Destination

P
FieldLens Print Capture + physical print
T
FieldLens Test Capture Full pipeline, no paper
System printer Checking FieldLens queues…
SETTINGS

FieldLens Print

Local configuration
Capture settings
Connection & updates
App updates Version … Checking for updates shortly after startup.
` function el(id: string) { return document.getElementById(id) as T } function setView(view: 'setup' | 'running' | 'ocr') { el('setup-view').hidden = view !== 'setup' el('running-view').hidden = view !== 'running' el('ocr-view').hidden = view !== 'ocr' el('setup-tab').classList.toggle('active', view === 'setup') el('running-tab').classList.toggle('active', view === 'running') el('ocr-tab').classList.toggle('active', view === 'ocr') } function formatDebugReport(snapshot: DebugSnapshot) { const checks = snapshot.checks.length ? snapshot.checks.map((check) => ` ${check.ok ? 'OK ' : 'ERR'} ${check.name}: ${check.detail}`).join('\n') : ' (no platform command checks)' const errors = snapshot.recentErrors.length ? snapshot.recentErrors.map((error) => ` - ${error}`).join('\n') : ' none' return [ `FieldLens Print ${snapshot.appVersion}`, `Platform: ${snapshot.platform} / ${snapshot.arch}`, `Bundle: ${bundleType ?? 'unknown'}`, '', `Capture: ${snapshot.captureRunning ? 'running' : 'stopped'} (${snapshot.captureMode})`, `Pending scans: ${snapshot.pending}`, `Recent jobs: ${snapshot.jobs}`, `Capture storage: ${snapshot.captureStorage}`, `Data dir: ${snapshot.dataDir}`, '', `Physical printer: ${snapshot.forwardPrinter || '(not selected)'}`, `Installed printers: ${snapshot.installedPrinters}`, `Virtual printer: ${snapshot.virtualPrinter}`, `Listen: ${snapshot.listenAddr || '(unset)'}`, `Forward: ${snapshot.forwardAddr || '(unset)'}`, '', `Auth session: ${snapshot.signedIn ? 'present' : 'none'}`, `Access: ${authStatus.accessStatus}${authStatus.role ? ` (${authStatus.role})` : ''}`, `OIDC client: ${snapshot.clientId}`, '', 'Platform checks:', checks, '', 'Recent job errors:', errors, ].join('\n') } async function refreshDebugSnapshot() { if (!debugEnabled) return const output = el('debug-output') output.textContent = 'Loading diagnostics…' try { debugSnapshot = await invoke('get_debug_snapshot') output.textContent = formatDebugReport(debugSnapshot) } catch (error) { output.textContent = `Debug snapshot failed: ${String(error)}` } } function setDebugEnabled(enabled: boolean) { debugEnabled = enabled el('debug-panel').hidden = !enabled if (enabled) { setView('setup') void refreshDebugSnapshot() } } function readForm(): AppConfig { return { ...config, mode: el('mode').value as CaptureMode, listenAddr: el('listenAddr').value.trim(), forwardAddr: el('forwardAddr').value.trim(), forwardPrinter: el('forwardPrinter').value, spoolDir: el('spoolDir').value.trim(), apiBaseUrl: el('apiBaseUrl').value.trim(), issuer: el('issuer').value.trim(), clientId: el('clientId').value.trim(), stationId: el('stationId').value.trim(), } } function writeForm(next: AppConfig) { el('mode').value = next.mode el('listenAddr').value = next.listenAddr el('forwardAddr').value = next.forwardAddr el('forwardPrinter').value = next.forwardPrinter || '' el('spoolDir').value = next.spoolDir el('apiBaseUrl').value = next.apiBaseUrl el('issuer').value = next.issuer el('clientId').value = next.clientId el('stationId').value = next.stationId } function renderPrinters() { const select = el('forwardPrinter') const current = select.value || config?.forwardPrinter || '' const options = systemPrinters.filter((printer) => !printer.isVirtual) const emptyLabel = options.length ? 'Choose a printer…' : 'No installed printers found' select.innerHTML = `` + options.map((printer) => { const suffix = printer.address ? ` · ${printer.address}` : printer.port ? ` · ${printer.port}` : '' return `` }).join('') if (options.some((printer) => printer.name === current)) select.value = current const selected = options.find((printer) => printer.name === select.value) el('running-printer').textContent = selected?.name || config?.forwardPrinter || 'No physical printer selected' const help = el('forward-printer-help') if (selected?.address) { help.textContent = `Detected RAW network endpoint: ${selected.address}` } else if (selected) { help.textContent = 'No RAW/TCP address was exposed for this queue. Enter the printer IP:9100 manually.' } else if (config?.forwardPrinter && !options.some((printer) => printer.name === config.forwardPrinter)) { help.textContent = `Previously selected printer "${config.forwardPrinter}" is not installed or available. Add it in system printer settings, then Refresh printers.` } else if (!options.length) { help.textContent = 'No installed CUPS printers were found. Add the physical printer in system settings, then Refresh printers.' } else { help.textContent = 'Select the physical printer that should receive the original print bytes.' } el('install-printer').disabled = !options.length const virtualState = el('virtual-printer-state') virtualState.textContent = virtualPrinterStatus?.detail ?? 'Printer status unavailable.' virtualState.classList.toggle('ready', Boolean(virtualPrinterStatus?.installed)) el('remove-printer').disabled = !virtualPrinterStatus?.installed } async function refreshPrinters() { const state = el('virtual-printer-state') state.textContent = 'Refreshing installed printers…' try { const [printers, printerStatus] = await Promise.all([ invoke('list_system_printers'), invoke('get_virtual_printer_status'), ]) systemPrinters = printers virtualPrinterStatus = printerStatus renderPrinters() } catch (error) { state.textContent = `Printer discovery failed: ${String(error)}` } } function renderAuthStatus() { const state = el('login-state') const login = el('login') const sync = el('sync') if (!authStatus.signedIn) { state.textContent = 'Not signed in.' login.textContent = 'Sign in' sync.disabled = true return } login.textContent = 'Sign out' if (authStatus.accessStatus === 'approved') { state.textContent = authStatus.role ? `Signed in · ${authStatus.role}` : 'Signed in · access approved' sync.disabled = false } else if (authStatus.accessStatus === 'pending') { state.textContent = 'Signed in · access pending approval' sync.disabled = true } else { state.textContent = `Signed in · access ${authStatus.accessStatus}` sync.disabled = true } } async function refreshAuthStatus() { try { authStatus = await invoke('get_auth_status') } catch (error) { authStatus = { signedIn: false, accessStatus: 'unavailable', role: null } el('login-state').textContent = `Sign-in status unavailable: ${String(error)}` el('sync').disabled = true el('login').textContent = 'Sign in' return } renderAuthStatus() if (debugEnabled) void refreshDebugSnapshot() } function formatBytes(bytes: number) { if (bytes < 1024) return `${bytes} B` if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` return `${(bytes / (1024 * 1024)).toFixed(1)} MB` } function friendlySource(source: string) { if (source === 'cups-original') return 'Live print' if (source === 'cups-test') return 'Test capture' if (source === 'raw-proxy') return 'RAW proxy' if (source === 'spool-folder') return 'Spool' return source } function fieldLabel(field: string) { const labels: Record = { origin: 'Origin', variety: 'Variety', brand: 'Brand', class: 'Class', base: 'Base', pack: 'Pack', grower: 'Grower', packrun: 'Packrun', size: 'Size', qty: 'Qty', pickDate: 'Pick date', packDate: 'Pack date', total: 'Total', } return labels[field] ?? field } function palletStatusLabel(status: PalletPrintStatus) { if (status === 'new-pallet') return 'First print' if (status === 'reprint-same') return 'Reprint · same' if (status === 'reprint-changed') return 'Reprint · changed' return 'Reprint · diff unavailable' } function render() { const pill = el('status-pill') const captureLive = (status?.running ?? false) && !captureError pill.classList.toggle('live', captureLive) pill.classList.toggle('error-state', Boolean(captureError)) pill.querySelector('span:last-child')!.textContent = captureError ? 'Capture error' : captureLive ? 'Capture active' : 'Capture stopped' el('job-count').textContent = String(status?.jobs ?? jobs.length) el('code-count').textContent = String(jobs.reduce((sum, job) => sum + job.barcodes.length, 0)) el('pending-count').textContent = String(status?.pending ?? 0) el('job-hint').textContent = captureError ? 'Capture needs attention' : status?.running ? 'Ready' : 'Stopped' el('jobs').innerHTML = jobs.length ? jobs.map((job) => { const source = friendlySource(job.source) const isTest = job.source === 'cups-test' const routeText = isTest ? 'No paper' : job.forwarded ? 'Printed' : 'Captured' const routeClass = isTest ? 'test' : job.forwarded ? 'success' : 'neutral' const decodeText = job.palletPrint ? `Pallet ${job.palletPrint.palletId}` : job.barcodes.length ? `${job.barcodes.length} code${job.barcodes.length === 1 ? '' : 's'} detected` : 'Captured · no barcode decoded yet' const palletPrint = job.palletPrint const palletBadge = palletPrint ? `${escapeHtml(palletStatusLabel(palletPrint.status))}` : '' const palletDetail = palletPrint ? `
Print #${palletPrint.occurrence} ${palletPrint.previousCapturedAt ? `Previous ${new Date(palletPrint.previousCapturedAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}` : ''}
${palletPrint.changes.length ? `
${palletPrint.changes.map((change) => ` ${escapeHtml(fieldLabel(change.field))} ${escapeHtml(change.previous)} → ${escapeHtml(change.current)} `).join('')}
` : ''} ` : '' return `
${escapeHtml(source)} ${routeText} ${palletBadge}
${decodeText} ${palletDetail}
${job.barcodes.map((code) => `${escapeHtml(code.format)}${escapeHtml(code.value)}`).join('')}
${job.error ? `
${escapeHtml(job.error)}
` : ''}
${formatBytes(job.bytes)} ${escapeHtml(job.label)}
` }).join('') : `
⇢
No print jobs yet Send a job to FieldLens Print or FieldLens Test Capture.
` } function escapeHtml(value: string) { return value.replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char]!)) } async function refreshStatus() { status = await invoke('get_status') jobs = await invoke('recent_jobs') render() if (debugEnabled) void refreshDebugSnapshot() } async function ensureCaptureStarted() { try { await invoke('start_capture') captureError = null } catch (error) { captureError = String(error) } await refreshStatus() return !captureError } async function restartCapture() { await invoke('stop_capture').catch(() => undefined) return ensureCaptureStarted() } el('forwardPrinter').addEventListener('change', () => { const selected = systemPrinters.find((printer) => printer.name === el('forwardPrinter').value) if (selected?.address) { el('forwardAddr').value = selected.address } else if (selected) { el('forwardAddr').value = '' } renderPrinters() if (debugEnabled) void refreshDebugSnapshot() }) el('refresh-printers').addEventListener('click', () => { void refreshPrinters() }) el('install-printer').addEventListener('click', async () => { const button = el('install-printer') const state = el('virtual-printer-state') const targetPrinter = el('forwardPrinter').value if (!targetPrinter) { state.textContent = 'Choose the physical destination printer first.' return } button.disabled = true let stoppedForRepair = false try { state.textContent = 'Stopping capture before printer repair…' await invoke('stop_capture') stoppedForRepair = true await refreshStatus() state.textContent = 'Repairing printers and migrating captured jobs… administrator approval may appear.' virtualPrinterStatus = await invoke('install_virtual_printer', { targetPrinter }) config = await invoke('save_config', { config: readForm() }) state.textContent = 'Restarting capture and processing migrated backlog…' const restarted = await ensureCaptureStarted() if (!restarted) { throw new Error(captureError || 'capture did not restart') } stoppedForRepair = false await refreshPrinters() setView('running') } catch (error) { state.textContent = `Printer setup failed: ${String(error)}` if (stoppedForRepair) { await ensureCaptureStarted().catch(() => undefined) } } finally { button.disabled = false } }) async function submitCaptureSelfTest(button: HTMLButtonElement, state: HTMLElement) { button.disabled = true state.textContent = 'Submitting a no-paper capture self-test…' try { const result = await invoke('run_capture_self_test') state.textContent = result window.setTimeout(() => { void refreshStatus() }, 1200) } catch (error) { state.textContent = `Self-test failed: ${String(error)}` } finally { button.disabled = false } } el('self-test').addEventListener('click', () => { void submitCaptureSelfTest( el('self-test'), el('virtual-printer-state'), ) }) el('running-self-test').addEventListener('click', () => { void submitCaptureSelfTest( el('running-self-test'), el('running-action-state'), ) }) el('remove-printer').addEventListener('click', async () => { const button = el('remove-printer') const state = el('virtual-printer-state') button.disabled = true state.textContent = 'Removing FieldLens Print…' try { virtualPrinterStatus = await invoke('remove_virtual_printer') await refreshPrinters() } catch (error) { state.textContent = `Printer removal failed: ${String(error)}` } finally { button.disabled = false } }) el('save').addEventListener('click', async () => { config = await invoke('save_config', { config: readForm() }) writeForm(config) if (status?.running) await restartCapture() else await refreshStatus() }) el('start').addEventListener('click', async () => { config = await invoke('save_config', { config: readForm() }) const started = await restartCapture() if (started) setView('running') }) el('stop').addEventListener('click', async () => { captureError = null await invoke('stop_capture') await refreshStatus() }) window.addEventListener('keydown', (event) => { if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key.toLowerCase() === 'd') { event.preventDefault() setDebugEnabled(!debugEnabled) } }) el('debug-refresh').addEventListener('click', () => { void refreshDebugSnapshot() }) el('debug-copy').addEventListener('click', async () => { const state = el('debug-action-state') if (!debugSnapshot) await refreshDebugSnapshot() if (!debugSnapshot) { state.textContent = 'No debug report available.' return } try { await navigator.clipboard.writeText(formatDebugReport(debugSnapshot)) state.textContent = 'Diagnostic report copied. Secrets and tokens are excluded.' } catch (error) { state.textContent = `Could not copy report: ${String(error)}` } }) el('setup-tab').addEventListener('click', () => setView('setup')) el('running-tab').addEventListener('click', () => setView('running')) el('ocr-tab').addEventListener('click', () => setView('ocr')) el('running-stop').addEventListener('click', async () => { const state = el('running-action-state') state.textContent = 'Stopping capture…' captureError = null await invoke('stop_capture') await refreshStatus() state.textContent = 'Capture stopped. Open Setup to restart or change configuration.' }) el('sync').addEventListener('click', async () => { const state = el('login-state') try { const result = await invoke<{ synced: number; pending: number }>('sync_pending') state.textContent = `Synced ${result.synced}. ${result.pending} pending.` await refreshStatus() } catch (error) { state.textContent = `Sync failed: ${String(error)}` await refreshAuthStatus() } }) el('login').addEventListener('click', async () => { const login = el('login') const state = el('login-state') if (authStatus.signedIn) { try { await invoke('logout') authStatus = { signedIn: false, accessStatus: 'signed-out', role: null } el('login-code').textContent = '' renderAuthStatus() } catch (error) { state.textContent = `Sign out failed: ${String(error)}` } return } login.disabled = true try { const session = await invoke('begin_login') el('login-code').textContent = session.userCode state.textContent = 'Authorize this device in your browser…' const target = session.verificationUriComplete || session.verificationUri await openUrl(target) if (loginTimer) window.clearInterval(loginTimer) loginTimer = window.setInterval(async () => { try { const result = await invoke('poll_login') if (result.status === 'authorized') { window.clearInterval(loginTimer) loginTimer = undefined el('login-code').textContent = '' authStatus = { signedIn: true, accessStatus: result.accessStatus || 'unknown', role: result.role ?? null, } renderAuthStatus() } else if (result.status === 'expired' || result.status === 'denied') { window.clearInterval(loginTimer) loginTimer = undefined state.textContent = `Login ${result.status}.` } } catch (error) { if (loginTimer) window.clearInterval(loginTimer) loginTimer = undefined state.textContent = String(error) } }, Math.max(3, session.interval) * 1000) } catch (error) { state.textContent = `Sign in failed: ${String(error)}` } finally { login.disabled = false } }) async function checkForUpdates(manual = false) { if (updateBusy) return const state = el('update-state') const installButton = el('install-update') const checkButton = el('check-update') try { checkButton.disabled = true state.textContent = manual ? 'Checking for updates…' : 'Checking quietly for updates…' availableUpdate = null availableManualUpdate = null if (bundleType !== BundleType.Deb) { try { const update = await check() availableUpdate = update if (update) { state.textContent = `Version ${update.version} is available.` installButton.hidden = false installButton.textContent = 'Download & restart' return } } catch { // The signed updater feed may not exist yet on a freshly bootstrapped // install. Fall back to the stable installer feed below. } } const manualUpdate = await checkManualRelease() availableManualUpdate = manualUpdate if (manualUpdate) { state.textContent = `Version ${manualUpdate.version} is available.` installButton.hidden = false installButton.textContent = bundleType === BundleType.Deb ? 'Install & restart' : 'Open installer' return } installButton.hidden = true state.textContent = `FieldLens Print ${currentVersion} is up to date.` } catch (error) { if (manual) state.textContent = `Update check failed: ${String(error)}` else state.textContent = 'Automatic update check unavailable. You can retry manually.' } finally { checkButton.disabled = false } } async function installAvailableUpdate() { if ((!availableUpdate && !availableManualUpdate) || updateBusy) return const state = el('update-state') const button = el('install-update') const progress = el('update-progress') const bar = progress.querySelector('span') as HTMLSpanElement const wasRunning = Boolean(status?.running) updateBusy = true button.disabled = true try { if (availableManualUpdate && bundleType === BundleType.Deb) { progress.hidden = false bar.style.width = '20%' state.textContent = 'Stopping capture before update…' if (wasRunning) { await invoke('stop_capture') await refreshStatus() } state.textContent = `Downloading and verifying FieldLens Print ${availableManualUpdate.version}…` bar.style.width = '45%' const result = await invoke<{ status: string; version: string }>('install_linux_update') if (result.status === 'up-to-date') { state.textContent = `FieldLens Print ${result.version} is already installed.` progress.hidden = true if (wasRunning) await ensureCaptureStarted() return } bar.style.width = '100%' state.textContent = `FieldLens Print ${result.version} installed. Restarting…` await relaunch() return } if (availableManualUpdate) { if (wasRunning) { state.textContent = 'Stopping capture before opening the installer…' await invoke('stop_capture') await refreshStatus() } state.textContent = 'Opening the current FieldLens Print installer…' try { await openUrl(availableManualUpdate.url) state.textContent = 'Installer opened. Complete the update, then restart FieldLens Print.' } catch (error) { if (wasRunning) await ensureCaptureStarted().catch(() => undefined) throw error } return } progress.hidden = false bar.style.width = '8%' let downloaded = 0 let total = 0 const signedUpdate = availableUpdate if (!signedUpdate) return if (wasRunning) { state.textContent = 'Stopping capture before update…' await invoke('stop_capture') await refreshStatus() } state.textContent = `Downloading FieldLens Print ${signedUpdate.version}…` await signedUpdate.downloadAndInstall((event) => { if (event.event === 'Started') { total = event.data.contentLength ?? 0 downloaded = 0 bar.style.width = '10%' } else if (event.event === 'Progress') { downloaded += event.data.chunkLength if (total > 0) { const percent = Math.min(92, Math.max(10, Math.round((downloaded / total) * 100))) bar.style.width = `${percent}%` } } else if (event.event === 'Finished') { bar.style.width = '100%' state.textContent = 'Update installed. Restarting…' } }) await relaunch() } catch (error) { state.textContent = `Update failed: ${String(error)}` progress.hidden = true if (wasRunning) await ensureCaptureStarted().catch(() => undefined) } finally { updateBusy = false button.disabled = false } } function compareVersions(left: string, right: string) { const a = left.split('.').map((part) => Number.parseInt(part, 10) || 0) const b = right.split('.').map((part) => Number.parseInt(part, 10) || 0) const length = Math.max(a.length, b.length) for (let index = 0; index < length; index += 1) { const diff = (a[index] ?? 0) - (b[index] ?? 0) if (diff !== 0) return diff } return 0 } async function checkManualRelease(): Promise { const response = await fetch( 'https://fieldlens.arrowstack.dev/downloads/release.json', { cache: 'no-store' }, ) if (!response.ok) throw new Error(`release feed returned HTTP ${response.status}`) const release = await response.json() as ManualRelease if (!release.version || compareVersions(release.version, currentVersion) <= 0) return null const key = bundleType === BundleType.Deb ? 'linux-x86_64-deb' : navigator.userAgent.includes('Windows') ? 'windows-x86_64' : null if (!key) return null const download = release.downloads?.[key] if (!download?.url) return null return { version: release.version, url: download.url } } el('check-update').addEventListener('click', () => { void checkForUpdates(true) }) el('install-update').addEventListener('click', () => { void installAvailableUpdate() }) await listen('capture-job', (event) => { jobs = [event.payload, ...jobs.filter((job) => job.id !== event.payload.id)].slice(0, 200) void refreshStatus() }) initOcrRefinement(el('ocr-refinement-root')) config = await invoke('get_config') writeForm(config) await refreshPrinters() await ensureCaptureStarted() void refreshAuthStatus() const readyForRunning = Boolean( virtualPrinterStatus?.installed && config.forwardPrinter && status?.running && !captureError ) setView(readyForRunning ? 'running' : 'setup') currentVersion = await getVersion() bundleType = await getBundleType().catch(() => null) el('version-label').textContent = `Version ${currentVersion}${bundleType ? ` · ${bundleType}` : ''}` window.setTimeout(() => { void checkForUpdates(false) }, 3000) window.setInterval(() => { void refreshStatus() }, 5000)