Files
web-terminal/public/dashboard.ts
Yaojia Wang 67b0a43b39 polish(v0.6): replace toolbar emoji with themed line icons + hover tooltips
The glossy emoji (🔍⚙▦🕘🔗📱) clashed with the Amber dark theme. Swap them
for clean lucide line icons (MIT, stroke=currentColor) so they take the theme
colour — muted by default, Amber on hover — and add a styled CSS hover tooltip
(data-tip, right-anchored so it never clips the edge) showing what each does.

- public/icons.ts: 7 line icons (search/settings/dashboard/history/keyboard/
  share/device), script-generated from lucide
- 7 toolbar modules: innerHTML = ICON_* + data-tip + aria-label
- style.css .toolbtn: 18px svg, --text-dim → --accent on hover, [data-tip] tooltip

Frontend-only. web typecheck + build clean; bundle has 0 toolbar emoji; verified
in-browser: line icons in theme colour, amber + tooltip on hover.
2026-06-30 14:36:26 +02:00

98 lines
2.8 KiB
TypeScript

/**
* public/dashboard.ts — all-sessions overview (M7).
*
* A ▦ toolbar button opens an overlay listing every tab with its connection
* dot, folder name, and Claude status; click a row to focus that tab. While
* open it re-renders every second so statuses stay live.
*/
import { ICON_DASHBOARD } from './icons.js'
export interface TabInfo {
idx: number
title: string
conn: string // SessionStatus
claude: string // ClaudeStatus
active: boolean
pending: boolean
}
export interface DashboardHooks {
snapshot: () => TabInfo[]
focus: (idx: number) => void
}
function claudeText(claude: string): string {
if (claude === 'working') return '⚙ working'
if (claude === 'waiting') return '⏳ needs approval'
if (claude === 'idle') return '✓ idle'
return ''
}
export function mountDashboard(toolbar: HTMLElement, hooks: DashboardHooks): void {
const overlay = document.createElement('div')
overlay.id = 'dashboard'
overlay.style.display = 'none'
const card = document.createElement('div')
card.className = 'dash-card'
overlay.appendChild(card)
document.body.appendChild(overlay)
let timer: ReturnType<typeof setInterval> | null = null
const render = (): void => {
card.replaceChildren()
const title = document.createElement('div')
title.className = 'dash-title'
title.textContent = 'Sessions'
card.appendChild(title)
for (const t of hooks.snapshot()) {
const row = document.createElement('div')
row.className = t.active ? 'dash-row active' : 'dash-row'
const dot = document.createElement('span')
dot.className = `tab-dot dot-${t.conn}`
const name = document.createElement('span')
name.className = 'dash-name'
name.textContent = t.title
const claude = document.createElement('span')
claude.className = t.pending ? 'dash-claude pending' : 'dash-claude'
claude.textContent = claudeText(t.claude)
row.append(dot, name, claude)
row.addEventListener('click', () => {
hooks.focus(t.idx)
hide()
})
card.appendChild(row)
}
}
const open = (): void => {
render()
overlay.style.display = 'flex'
timer = setInterval(render, 1000)
}
const hide = (): void => {
overlay.style.display = 'none'
if (timer !== null) {
clearInterval(timer)
timer = null
}
}
overlay.addEventListener('click', (e) => {
if (e.target === overlay) hide()
})
const btn = document.createElement('button')
btn.className = 'toolbtn'
btn.innerHTML = ICON_DASHBOARD
btn.dataset.tip = 'All sessions'
btn.setAttribute('aria-label', 'All sessions')
btn.setAttribute('aria-label', 'All sessions')
btn.addEventListener('click', () => (overlay.style.display === 'none' ? open() : hide()))
toolbar.appendChild(btn)
}