layout store (auto/custom + pending), marked custom on manual move (onDidMovePanel outside arrange), sash drag, stream/group rename. Tray chips: HTML5 drag accepted via onUnhandledDragOver/onDidDrop (position -> direction), click = new column. WebAudio two-note chime. Gateway page instructions updated. Verified headless (custom vs auto). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
26 lines
1.4 KiB
JavaScript
26 lines
1.4 KiB
JavaScript
import React, { useEffect, useRef, useState } from 'react'
|
|
import { names } from './names.js'
|
|
import { layout } from './layout.js'
|
|
|
|
// Tab for a stream panel: double-click to rename (viewer-side label, kept in this browser). Empty = back to user · title.
|
|
export default function Tab({ api, params }) {
|
|
const id = api.id
|
|
const custom = names.use(s => s.streams[id])
|
|
const base = `${params.user} · ${params.title}`
|
|
const [editing, setEditing] = useState(false)
|
|
const [draft, setDraft] = useState('')
|
|
const ref = useRef(null)
|
|
useEffect(() => { if (editing) { ref.current?.focus(); ref.current?.select() } }, [editing])
|
|
const start = e => { e.stopPropagation(); setDraft(custom || ''); setEditing(true) }
|
|
const commit = () => { if (!editing) return; const v = draft.trim(); names.rename(id, v); api.setTitle(v || base); layout.markCustom(); setEditing(false) }
|
|
return (
|
|
<div className="vtab" title={`${base}\ndouble-click to rename`} onDoubleClick={start}>
|
|
{editing
|
|
? <input ref={ref} value={draft} placeholder={base} onChange={e => setDraft(e.target.value)}
|
|
onPointerDown={e => e.stopPropagation()} onBlur={commit}
|
|
onKeyDown={e => { e.stopPropagation(); if (e.key === 'Enter') commit(); if (e.key === 'Escape') setEditing(false) }} />
|
|
: <span className="vtab-text">{custom || base}</span>}
|
|
</div>
|
|
)
|
|
}
|