/** * public/main.ts — esbuild entry point for the browser frontend. * * Bootstraps the multi-tab app: a tab bar (#tabs) + a utility toolbar (#toolbar) * over a stack of terminal panes (#term), each tab an independent TerminalSession. * The mobile key bar routes input to the active tab. * * Per-session terminal/WS/reconnect logic lives in terminal-session.ts; * tab management + persistence in tabs.ts. */ // CSS side-effect import (esbuild → public/build/main.css; typed via css.d.ts). import '@xterm/xterm/css/xterm.css' import { mountKeybar } from './keybar.js' import { TabApp } from './tabs.js' import { mountSearch } from './search.js' import { mountQrConnect } from './qr.js' import { mountSettings, loadSettings, type Settings } from './settings.js' import { mountDashboard } from './dashboard.js' import { mountHistory } from './history.js' const paneHost = document.getElementById('term') const tabs = document.getElementById('tabs') const toolbar = document.getElementById('toolbar') if (!paneHost) throw new Error('#term element not found in DOM') if (!tabs) throw new Error('#tabs element not found in DOM') if (!toolbar) throw new Error('#toolbar element not found in DOM') const app = new TabApp(paneHost, tabs) // Apply saved theme/font (M3) to the restored tabs. let settings: Settings = loadSettings() app.applySettings(settings) // Key bar (mobile + desktop) sends to whichever tab is active. mountKeybar((data) => app.sendToActive(data)) // Toolbar utilities. mountSearch(toolbar, { find: (query, dir) => app.findInActive(query, dir), clear: () => app.clearActiveSearch(), }) mountSettings( toolbar, () => settings, (s) => { settings = s app.applySettings(s) }, ) mountDashboard(toolbar, { snapshot: () => app.snapshot(), focus: (idx) => app.focusTab(idx), }) mountHistory(toolbar, { resume: (cwd, id) => app.newTabForResume(cwd, id), }) mountQrConnect(toolbar) // PWA: register the service worker (installable + offline shell, M4). if ('serviceWorker' in navigator) { window.addEventListener('load', () => { void navigator.serviceWorker.register('./sw.js').catch(() => { // SW registration is best-effort; the app works without it. }) }) }