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.
75 lines
2.6 KiB
TypeScript
75 lines
2.6 KiB
TypeScript
/**
|
|
* public/share.ts — "share this session" (v0.4 multi-device mirror).
|
|
*
|
|
* A 🔗 toolbar button shows a QR + URL of the form `<origin>/?join=<sessionId>`
|
|
* for the ACTIVE tab. Opening that link on another device on the same network
|
|
* joins the same live terminal (mirror: both see output, either can type).
|
|
* Reuses the .qr-* card styles.
|
|
*/
|
|
|
|
import QRCode from 'qrcode'
|
|
import { ICON_SHARE } from './icons.js'
|
|
|
|
export function mountShareSession(toolbar: HTMLElement, getActiveId: () => string | null): void {
|
|
const modal = document.createElement('div')
|
|
modal.id = 'sharemodal'
|
|
modal.className = 'qrmodal'
|
|
modal.style.display = 'none'
|
|
|
|
const card = document.createElement('div')
|
|
card.className = 'qr-card'
|
|
const title = document.createElement('div')
|
|
title.className = 'qr-title'
|
|
title.textContent = 'Share this session'
|
|
const canvas = document.createElement('canvas')
|
|
const urlEl = document.createElement('div')
|
|
urlEl.className = 'qr-url'
|
|
const note = document.createElement('div')
|
|
note.className = 'qr-note'
|
|
const close = document.createElement('button')
|
|
close.className = 'qr-close'
|
|
close.textContent = 'Close'
|
|
card.append(title, canvas, urlEl, note, close)
|
|
modal.appendChild(card)
|
|
document.body.appendChild(modal)
|
|
|
|
const hide = (): void => {
|
|
modal.style.display = 'none'
|
|
}
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal) hide()
|
|
})
|
|
close.addEventListener('click', hide)
|
|
|
|
const open = (): void => {
|
|
const id = getActiveId()
|
|
if (!id) {
|
|
// The active tab hasn't attached yet — no id to share.
|
|
urlEl.textContent = ''
|
|
note.textContent = 'This tab is still connecting — try again in a second.'
|
|
const ctx = canvas.getContext('2d')
|
|
if (ctx) ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
modal.style.display = 'flex'
|
|
return
|
|
}
|
|
const url = `${location.origin}/?join=${id}`
|
|
urlEl.textContent = url
|
|
void QRCode.toCanvas(canvas, url, { width: 220, margin: 1 })
|
|
const host = location.hostname
|
|
note.textContent =
|
|
host === 'localhost' || host === '127.0.0.1'
|
|
? 'Tip: open this page via your LAN IP (not localhost) so the code works on other devices.'
|
|
: 'Scan on a device on the same network to mirror this session.'
|
|
modal.style.display = 'flex'
|
|
}
|
|
|
|
const toggle = document.createElement('button')
|
|
toggle.className = 'toolbtn'
|
|
toggle.innerHTML = ICON_SHARE
|
|
toggle.dataset.tip = 'Share session'
|
|
toggle.setAttribute('aria-label', 'Share session')
|
|
toggle.setAttribute('aria-label', 'Share this session')
|
|
toggle.addEventListener('click', open)
|
|
toolbar.appendChild(toggle)
|
|
}
|