feat(v0.3): keybar Claude shortcuts + scrollback search + clickable links

- keybar: add Esc·Esc / ^O / ^T / ^B + per-button tooltips (KEY_MAP 14 keys)
- M1 search: @xterm/addon-search per terminal; 🔍 toolbar button + search box
  (Enter=next, Shift+Enter=prev, Esc=close)
- M2 links: @xterm/addon-web-links per terminal (tap URLs Claude prints)
- tab bar split into #tabs (scrollable) + #toolbar (utility cluster) for future
  utilities (QR/settings/dashboard)
- Browser-verified: 14 keybar buttons, search highlights matches, no console errors.
  199 tests green, typechecks + build ok.
This commit is contained in:
Yaojia Wang
2026-06-17 18:28:40 +02:00
parent 110c1752d4
commit eaeacf3a78
10 changed files with 576 additions and 42 deletions

View File

@@ -1,27 +1,34 @@
/**
* public/main.ts — esbuild entry point for the browser frontend.
*
* Bootstraps the multi-tab app: a tab bar (#tabbar) over a stack of terminal
* panes (#term), each tab an independent TerminalSession (own WS + server
* session). The mobile key bar routes input to the active tab.
* 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 processed by esbuild (→ public/build/main.css).
// tsc cannot resolve CSS paths; @ts-ignore suppresses the single diagnostic.
// @ts-ignore
// @ts-ignore CSS side-effect import processed by esbuild (→ public/build/main.css).
import '@xterm/xterm/css/xterm.css'
import { mountKeybar } from './keybar.js'
import { TabApp } from './tabs.js'
import { mountSearch } from './search.js'
const paneHost = document.getElementById('term')
const tabBar = document.getElementById('tabbar')
const tabs = document.getElementById('tabs')
const toolbar = document.getElementById('toolbar')
if (!paneHost) throw new Error('#term element not found in DOM')
if (!tabBar) throw new Error('#tabbar 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, tabBar)
const app = new TabApp(paneHost, tabs)
// Key bar (mobile) sends to whichever tab is active.
// 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(),
})