Files
web-terminal/public/history.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

113 lines
3.2 KiB
TypeScript

/**
* public/history.ts — resume a past Claude Code session (O2).
*
* A 🕘 toolbar button opens a list of past sessions (GET /sessions); clicking
* "Resume" opens a new tab in that project's directory and runs
* `claude --resume <id>`.
*/
import { ICON_HISTORY } from './icons.js'
export interface HistoryHooks {
resume: (cwd: string, id: string) => void
}
interface HistorySession {
id: string
cwd: string
project: string
mtimeMs: number
preview: string
}
function relTime(ms: number): string {
const s = Math.max(0, (Date.now() - ms) / 1000)
if (s < 60) return 'just now'
if (s < 3600) return `${Math.floor(s / 60)}m ago`
if (s < 86400) return `${Math.floor(s / 3600)}h ago`
return `${Math.floor(s / 86400)}d ago`
}
export function mountHistory(toolbar: HTMLElement, hooks: HistoryHooks): void {
const overlay = document.createElement('div')
overlay.id = 'historymodal'
overlay.style.display = 'none'
const card = document.createElement('div')
card.className = 'hist-card'
overlay.appendChild(card)
document.body.appendChild(overlay)
const hide = (): void => {
overlay.style.display = 'none'
}
overlay.addEventListener('click', (e) => {
if (e.target === overlay) hide()
})
const open = async (): Promise<void> => {
card.replaceChildren()
const title = document.createElement('div')
title.className = 'hist-title'
title.textContent = 'Resume a Claude session'
card.appendChild(title)
const status = document.createElement('div')
status.className = 'hist-empty'
status.textContent = 'Loading…'
card.appendChild(status)
overlay.style.display = 'flex'
let sessions: HistorySession[]
try {
const res = await fetch('/sessions')
sessions = (await res.json()) as HistorySession[]
} catch {
status.textContent = 'Failed to load sessions.'
return
}
if (sessions.length === 0) {
status.textContent = 'No past Claude sessions found.'
return
}
status.remove()
for (const s of sessions) {
const row = document.createElement('div')
row.className = 'hist-row'
const main = document.createElement('div')
main.className = 'hist-main'
const proj = document.createElement('div')
proj.className = 'hist-proj'
proj.textContent = `${s.project} · ${relTime(s.mtimeMs)}`
const prev = document.createElement('div')
prev.className = 'hist-prev'
prev.textContent = s.preview || '(no preview)'
prev.title = s.cwd
main.append(proj, prev)
const btn = document.createElement('button')
btn.className = 'hist-resume'
btn.textContent = 'Resume'
btn.addEventListener('click', () => {
hooks.resume(s.cwd, s.id)
hide()
})
row.append(main, btn)
card.appendChild(row)
}
}
const toggle = document.createElement('button')
toggle.className = 'toolbtn'
toggle.innerHTML = ICON_HISTORY
toggle.dataset.tip = 'Session history'
toggle.setAttribute('aria-label', 'Session history')
toggle.setAttribute('aria-label', 'Session history')
toggle.addEventListener('click', () => {
if (overlay.style.display === 'none') void open()
else hide()
})
toolbar.appendChild(toggle)
}