diff --git a/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte b/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte index 43a02b0f..f7875893 100644 --- a/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte +++ b/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte @@ -78,6 +78,15 @@ }; } + // Generate a stable per-device client ID on first load and persist it. + // events_loc is backed by svelte-persisted-store (localStorage) so this + // survives page reloads. Without this, client_id falls back to Date.now() + // inside element_websocket_v3 — a new ID on every reload, which breaks + // direct-target WS messages and doesn't match V3 Vision ID expectations. + if (!$events_loc.launcher.controller_client_id) { + $events_loc.launcher.controller_client_id = crypto.randomUUID(); + } + // Unified Selection Sync (Refactored 2026-02-11) // WHY: We track URL params directly to ensure the UI reacts instantly to navigation. // We use untrack for store writes to prevent circular dependency loops. @@ -342,10 +351,16 @@ $events_sess.launcher.modal__open_event_file_id = obj_id; // Look up the file object from Dexie so the modal can render the image. // The remote device has no event_file_obj in scope — only the ID was sent over WS. - db_events.file.get(obj_id).then((file_obj: any) => { + // Also look up the parent presentation to use its name as the modal title + // (cleaner for the LCD display than a raw filename). + db_events.file.get(obj_id).then(async (file_obj: any) => { if (file_obj) { $events_sess.launcher.modal__event_file_obj = file_obj; - $events_sess.launcher.modal__title = file_obj.filename ?? ''; + const presentation = file_obj.for_id + ? await db_events.presentation.get(file_obj.for_id) + : null; + $events_sess.launcher.modal__title = + presentation?.name ?? file_obj.filename ?? ''; } }); }