Live portal: custom layouts are left alone — new streams chime and wait in a draggable tray; Arrange re-grids and returns to auto
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>
This commit is contained in:
30
live/src/layout.js
Normal file
30
live/src/layout.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { create } from './tinyStore.js'
|
||||
|
||||
// Layout mode. "auto" = untouched grid: streams joining/leaving re-grid everything. The first manual move, resize
|
||||
// or rename makes it "custom": the grid is left alone, leavers just close, joiners wait in the tray (with a ding)
|
||||
// until dragged in. "Arrange" re-grids everything and goes back to auto.
|
||||
export const layout = create({
|
||||
custom: false,
|
||||
pending: [], // streams live but not placed yet (custom mode)
|
||||
markCustom() { return this.custom ? null : { custom: true } },
|
||||
reset() { return { custom: false, pending: [] } },
|
||||
addPending(s) { return this.pending.some(p => p.id === s.id) ? null : { pending: [...this.pending, s] } },
|
||||
dropPending(id) { return this.pending.some(p => p.id === id) ? { pending: this.pending.filter(p => p.id !== id) } : null },
|
||||
keepPending(ids) { const pending = this.pending.filter(p => ids.has(p.id)); return pending.length === this.pending.length ? null : { pending } },
|
||||
})
|
||||
|
||||
// short two-note chime (WebAudio, no asset). Browsers need one prior click on the page to allow sound.
|
||||
let ctx
|
||||
export function ding() {
|
||||
try {
|
||||
ctx = ctx || new (window.AudioContext || window.webkitAudioContext)()
|
||||
if (ctx.state === 'suspended') ctx.resume().catch(() => {})
|
||||
const t0 = ctx.currentTime
|
||||
for (const [i, f] of [[0, 880], [0.16, 1320]]) {
|
||||
const o = ctx.createOscillator(), g = ctx.createGain()
|
||||
o.type = 'sine'; o.frequency.value = f
|
||||
g.gain.setValueAtTime(0, t0 + i); g.gain.linearRampToValueAtTime(0.25, t0 + i + 0.02); g.gain.exponentialRampToValueAtTime(0.001, t0 + i + 0.35)
|
||||
o.connect(g).connect(ctx.destination); o.start(t0 + i); o.stop(t0 + i + 0.4)
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
Reference in New Issue
Block a user