// Screen-share quality presets modelled on Discord's tiers. Values are the // upper bounds — LiveKit + WebRTC's congestion control dynamically drop to // lower spatial/temporal layers (SVC with VP9) when the uplink degrades, so // these numbers behave as "up to" caps, not constant bitrates. const STORAGE_KEY = 'chatapp.screenshare'; export type ScreenSharePreset = | 'auto' | '720p30' | '720p60' | '1080p30' | '1080p60' | '1440p60' | '4k60'; export interface ScreenShareSettings { preset: ScreenSharePreset; } const DEFAULTS: ScreenShareSettings = { preset: 'auto', }; export interface PresetParams { dims: { width: number; height: number } | null; // null = browser picks native framerate: number; bitrateKbps: number; label: string; } const PRESET_PARAMS: Record = { auto: { dims: null, framerate: 60, bitrateKbps: 8000, label: 'Auto (Original)' }, '720p30': { dims: { width: 1280, height: 720 }, framerate: 30, bitrateKbps: 2500, label: '720p · 30 fps', }, '720p60': { dims: { width: 1280, height: 720 }, framerate: 60, bitrateKbps: 3500, label: '720p · 60 fps', }, '1080p30': { dims: { width: 1920, height: 1080 }, framerate: 30, bitrateKbps: 4000, label: '1080p · 30 fps', }, '1080p60': { dims: { width: 1920, height: 1080 }, framerate: 60, bitrateKbps: 6000, label: '1080p · 60 fps', }, '1440p60': { dims: { width: 2560, height: 1440 }, framerate: 60, bitrateKbps: 8000, label: '1440p · 60 fps', }, '4k60': { dims: { width: 3840, height: 2160 }, framerate: 60, bitrateKbps: 10_000, label: '4K · 60 fps', }, }; export const PRESET_ORDER: ReadonlyArray = [ 'auto', '720p30', '720p60', '1080p30', '1080p60', '1440p60', '4k60', ]; export function getPresetParams(p: ScreenSharePreset): PresetParams { return PRESET_PARAMS[p]; } type Listener = (s: ScreenShareSettings) => void; const listeners = new Set(); let cached: ScreenShareSettings | null = null; function isPreset(v: unknown): v is ScreenSharePreset { return typeof v === 'string' && v in PRESET_PARAMS; } function read(): ScreenShareSettings { if (cached) return cached; try { const raw = window.localStorage.getItem(STORAGE_KEY); if (!raw) { cached = DEFAULTS; return cached; } const parsed = JSON.parse(raw) as Partial; cached = { preset: isPreset(parsed.preset) ? parsed.preset : DEFAULTS.preset, }; return cached; } catch { cached = DEFAULTS; return cached; } } function write(s: ScreenShareSettings): void { cached = s; try { window.localStorage.setItem(STORAGE_KEY, JSON.stringify(s)); } catch { /* quota / private mode */ } for (const l of listeners) l(s); } export function getScreenShareSettings(): ScreenShareSettings { return read(); } export function updateScreenShareSettings( patch: Partial, ): ScreenShareSettings { const next = { ...read(), ...patch }; write(next); return next; } export function subscribeScreenShareSettings(listener: Listener): () => void { listeners.add(listener); return () => listeners.delete(listener); }