Commit Graph

63 Commits

Author SHA1 Message Date
Scott Idem
456674cc3e fix(launcher): remove ghost Flowbite close btn; add working Close btn in footer
The Flowbite Dialog component (which Drawer wraps internally) renders a
<CloseButton type='submit'> by default when dismissable=true. Since the
Drawer does not use the form/dialog mechanism, that button appeared at the
bottom of the drawer but did nothing. Fix: dismissable={false} on the cfg
Drawer suppresses it.

launcher_cfg.svelte footer redesigned:
- Lower-left: Close button (always visible) — mirrors top-right X, useful
  when the user has scrolled down through long config sections
- Lower-right: Reload (always visible, shorter label)
- Full-width Debug Panel button (edit_mode only, below the row)

Click-outside behavior unchanged — Flowbite Dialog outsideclose defaults
to true, so tapping outside the drawer still closes it.
2026-03-13 15:07:46 -04:00
Scott Idem
ce0c8b03c9 feat(launcher): Oral/Poster Kiosk mode preset toggle + ae_mode WS command
Adds a two-button Session Mode Preset toggle in Display & App Modes cfg:
- 'Oral / Default' restores all menus/headers/iframe off
- 'Poster Kiosk'   sets iframe=true + hides menu, header, footer

When WS is connected (local_push or remote controller), tapping a preset
sends ae_mode:poster / ae_mode:oral to all connected devices so an operator
can reconfigure the whole room from one device.

ae_mode:{poster|oral} command handler added to handle_ws_recv() in
+layout.svelte — receives and applies the same preset on remote devices.
2026-03-13 13:58:37 -04:00
Scott Idem
a5a5022143 fix(launcher): remove wasted top space in iframe/menu-hidden mode; vh bottom buffer
events/+layout.svelte:
  the events nav is actually rendered. Adds pt-0 for the launcher case
  (nav never rendered there; launcher manages its own header offset).
- pb-48 (fixed 12rem) → pb-[25vh] — scrolls ~25% of viewport below
  last card on any screen size, scales properly on phone/tablet/desktop.

launcher/+layout.svelte outer wrapper:
- Static mt-4 replaced with conditional:
    mt-12 when launcher header is visible (matches h-12 header height,
           keeps content clear of the absolute overlay)
    mt-2  when launcher header is hidden (minimal breathing room only)
- Result: iframe mode + launcher_header=hide → near-zero top dead space
2026-03-13 13:24:03 -04:00
Scott Idem
e3e81226a0 feat(launcher): URL params to control iframe/menu/header/footer visibility
Allows sharing a clean link that auto-configures display state on any
device — no manual setup required, ideal for tablet PWA kiosk deployment.

All four params are optional and independent. Values persist in
localStorage so they survive reloads; the URL param always wins when
present. Read inside the existing reactive URL-sync $effect, which
fires on every SPA navigation (unlike the root onMount which only
fires once on initial load).

Supported params:
  ?iframe=true/false          — hide/show global sys & debug menus
  ?launcher_menu=hide/show    — hide/show left session/location panel
  ?launcher_header=hide/show  — hide/show 'Æ Launcher v3' header bar
  ?launcher_footer=hide/show  — hide/show the status footer

Example clean poster-kiosk link:
  /events/{id}/launcher/{loc_id}?session_id={sess_id}
    &iframe=true&launcher_menu=hide&launcher_header=hide
2026-03-13 13:16:26 -04:00
Scott Idem
b18cda98b7 feat(launcher): poster modal zoom sync, clean close buttons
- Add modal_zoom_fit state (default: fit); resets on every new poster open
- Zoom/Fit toggle button + image double-tap on controller tablet
- Both zoom triggers send ae_zoom:fit/zoom over WS to remote display (local_push)
- ae_zoom: handler added to handle_ws_recv() for remote device
- Replace 3 scattered close buttons with single bottom control bar:
    - [Zoom/Fit] always visible on controller
    - [Close Both] sends ae_close WS + clears local modal (local_push only)
    - [Back to List] clears local modal only; remote keeps showing current poster
- Bottom bar hidden on controller=remote (kiosk display-screen mode)
- native pinch-to-zoom via touch-action: pinch-zoom on img (no JS library needed)
- pb-14 on modal bodyClass prevents buttons from overlapping poster content
2026-03-12 19:57:33 -04:00
Scott Idem
0da5894529 fix: stable WS client ID + presentation name for poster modal title
- Generate and persist crypto.randomUUID() as controller_client_id on first
  launcher load (events_loc is persisted so it survives page reloads).
  Previously fell back to Date.now() on every reload.
- ae_open: handler now resolves presentation name via Dexie lookup chain:
  file.for_id -> presentation.name, falling back to file.filename.
  Remote modal now shows the presentation title instead of raw filename.
2026-03-11 19:01:21 -04:00
Scott Idem
3df09713d3 fix: poster WS remote control - push open and resolve file obj
- launcher_file_cont: poster 'Open Poster' click now sends ae_open:event_file
  command to remote display when controller=local_push and WS is connected
- +layout.svelte handle_ws_recv: ae_open: handler now looks up the file obj
  from Dexie after setting modal__open_event_file_id, so the remote modal
  has the hosted_file_id needed to render the poster image (was showing
  'No image selected' on remote device)
2026-03-11 18:54:40 -04:00
Scott Idem
5db66bc888 feat: upgrade launcher WebSocket to V3 protocol
- New element_websocket_v3.svelte:
  - URL path /v3/ws/group/{id}/client/{id}
  - Auth via ?api_key=...&jwt=... query params (browsers can't set WS headers)
  - Strict WS_Message_V3 schema: msg_type + target fields
  - 45s heartbeat to maintain Redis presence TTL
  - Self-echo detection via from_id (server-stamped)
  - Target 'group' (was 'all'), 'direct' (was 'dm')
- launcher/+layout.svelte:
  - Swap Element_websocket_v2 -> Element_websocket_v3
  - Add api_key and jwt props for V3 auth
  - Fix handle_ws_recv: type -> msg_type (V3 schema)
  - Remove unused 'type' prop passed to element
2026-03-11 17:50:08 -04:00
Scott Idem
56fd1d1760 fix: Launcher dead-code cleanup — dead else-if branch and unused handle_get_device_info
- launcher/+layout.svelte: dead {:else if $events_slct.event_session_id} branch
  (condition identical to preceding {#if}) replaced with correct
  {:else if $events_slct.event_location_id} — shows "Select a session"
  prompt when a room is chosen but no session is yet selected.

- launcher/[event_location_id]/+page.svelte: removed unreachable
  handle_get_device_info() function (never called; pre-relay pattern
  superseded by launcher_background_sync.svelte's run_device_heartbeat()).
  Cleaned up now-unused imports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 12:30:41 -04:00
Scott Idem
04a8edc6d1 [Perf] Fix liveQuery reactivity, silence debug logs, add performance guidelines
- launcher/+layout.svelte: convert lq__event_session_obj from $derived to
  $derived.by() so Svelte tracks event_session_id as a dependency; the old
  pattern read the store inside the Dexie async callback where Svelte's
  tracking is off, so the liveQuery never updated on session change
- ae_events__event_file.ts: fix hardcoded log_lvl: 2 in SWR fire-and-forget
  background refresh (always-on debug logging on every cache hit) → 0
- e_app_sign_in_out.svelte: lower 6 call-site log levels (1×log_lvl:2,
  5×log_lvl:1) to 0; sign-in runs on every page load
- element_manage_hosted_file_li.svelte: log_lvl:2 → 0 in refresh call;
  remove log_lvl=1 assignment + debug block inside click handler; log_lvl:1
  → 0 in delete call
- AE__Performance_Guidelines.md: add 5 Svelte 5 runes rules covering
  $derived.by() for reactive liveQuery, liveQuery purity, cheap equality
  guards ($id+updated_on, ID-join, shallow_equal), untrack() requirement,
  and log_lvl discipline

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 15:01:42 -04:00
Scott Idem
ffc430a727 Perf: replace JSON.stringify comparisons with efficient identity checks
JSON.stringify on large store objects (ae_loc, ae_api, slct, event lists)
was running on every navigation, comparing serialized strings of potentially
deep objects. Replace with targeted comparators:

- Root +layout.svelte: add shallow_equal() helper — O(n keys) key-by-key
  identity check instead of O(serialized bytes). Used for ae_api, ae_loc,
  and slct sync guards.

- Launcher +layout.svelte: ID-list join-compare for event_location_obj_li
  and id_li__event_location (O(n) string vs O(n*m) stringify). Refactor
  liveQuery closures to be pure (data-only, no store reads/writes inside
  the async Dexie context where Svelte reactivity tracking is undefined).
  Move store sync into separate $effects that compare updated_on + id
  (O(1)) or ID-join (O(n)) rather than full object serialization.
2026-03-10 14:20:57 -04:00
Scott Idem
3447c4d4a4 launcher: fix liveQuery reactivity for location-dependent queries
The three liveQueries that depend on $events_slct.event_location_id were
plain liveQuery() calls, not $derived.by(() => liveQuery(...)). This meant
Svelte store changes did NOT cause them to re-run.

Root cause of the 'hung' bug:
- On initial load at /launcher (no location in URL), id = null
- Dexie watches the event_location_id = null index range
- User selects a location (or navigates to /launcher/{id}): store updates
- Sessions for the real location are in a DIFFERENT Dexie range
- Dexie never fires because the null range was never touched
- If sessions are already in IndexedDB cache (no new DB write), the list
  stays permanently frozen at []

Fix: convert lq__event_session_obj_li, lq__location_event_file_obj_li, and
lq__event_location_obj to $derived.by(() => liveQuery(...)). When
event_location_id changes, $derived.by creates a new Observable targeting
the correct location, which immediately queries Dexie for existing cached
data and then watches that range for further changes.

Also: remove the .reverse() before .sortBy('name') on the session query —
.sortBy() always re-sorts so .reverse() before it was a no-op.
2026-03-06 21:51:31 -05:00
Scott Idem
589ab9b652 launcher: add font size cycler to footer bar
Onsite operators may have the sys menu locked/unavailable. This button
in the always-visible launcher footer gives them direct A / A+ / A−
control that cycles $ae_loc.font_size_mode, which the root layout DOM
effect picks up and applies as html.font-size-* class.
2026-03-06 21:40:39 -05:00
Scott Idem
c0729ab4f3 a11y(launcher): dark mode, touch targets, overflow, transition speed
Shell (+layout.svelte):
- fix: header bg-slate-200 -> dark:bg-slate-800 (projector/night use)
- fix: main content bg-gray-100 -> dark:bg-gray-900
- fix: config drawer bg-orange-100 -> dark:bg-slate-800
- fix: debug drawer bg-red-100 -> dark:bg-slate-900
- fix: footer border-gray-200 -> border-gray-300 (invisible border fixed)
- fix: remove header hover:text-base (caused layout shift on hover)
- fix: remove footer hover:sm:text-sm etc. (layout shift + broken TW4 syntax)
- fix: header/footer/status spans duration-1000 -> duration-300 (too slow)
- fix: footer opacity-50 -> opacity-70 (was too dim for projector/status reading)
- fix: nav section add overflow-y-auto (sessions overflowed on short screens)
- fix: nav section remove hover:bg-surface-100-900 (confusing whole-panel hover)
- a11y: header menu toggle button class=''->'px-2 py-1 rounded...' (touch target)
- a11y: footer edit_mode button class=''->'px-1.5 py-1 rounded...' (touch target)
- a11y: footer status spans px-1 -> px-2 py-0.5 (slightly larger tap area)
- a11y: footer datetime add dark:hover:bg-slate-700
2026-03-06 20:32:57 -05:00
Scott Idem
b2fa6228a6 fix(launcher): poster session display — metadata, image modal, file paths
1. launcher_presentation_view: accept session_type prop from parent
   instead of relying on event_session_type_code on file objects (which
   is not reliably populated in Dexie). Use session_type to correctly
   set hide_meta, session_type, and open_method on the file container.

2. launcher_session_view: pass session_type={type_code} to
   Launcher_presentation_view; restore Launcher_presenter_view_posters
   in the presenter list for poster mode (hide_name=true). Some events
   store poster files at the PRESENTER level (for_id=event_presenter_id)
   — particularly group/company presenters — so both paths must render.

3. launcher/+layout.svelte: fix poster modal image 403. The img src was
   using the event_file download endpoint which requires auth headers a
   plain img tag cannot send. Switched to the hosted_file endpoint with
   key=account_id, which is browser-compatible. Also guarded on
   modal__event_file_obj.hosted_file_id for safer access.

4. launcher_cfg_screen_saver: rename section title to 'Poster Screen Saver'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 19:15:32 -05:00
Scott Idem
7549749d14 Launcher: Resolved session selection hang and improved reactivity pattern. 2026-02-11 17:18:55 -05:00
Scott Idem
c5bfc140af fix(launcher): resolve 'download' filename bug and refactor data_url to global page state
- Updated 'handle_open_file' in launcher_file_cont.svelte to correctly pass filename to API.
- Fixed WebSocket ae_download command in launcher layout to include filename.
- Implemented a safety net in api_get_object.ts to extract filename from params if missing.
- Added 'download' attribute to Hosted Files download button for direct links.
- Refactored launcher menu components to use Svelte 5 global 'page' state instead of obsolete 'data_url' prop.
2026-02-10 19:12:55 -05:00
Scott Idem
72abd8034e fix(launcher): resolve session selection hang and reactive header sync
- Optimized session navigation by switching from static data_url to reactive $page.url.\n- Fixed selection hang when entering the Launcher without an initial session ID.\n- Synchronized global header and idle logic with derived layout observables.\n- Streamlined prop passing to ensure clean state transitions during session switching.
2026-02-10 18:49:31 -05:00
Scott Idem
1ab13eaf96 fix(launcher): optimize layout reactivity and implement staggered data pipeline
- Moved session observable to child component to ensure reactive header updates.\n- Implemented deferred loading for presenters and files to prevent request storms.\n- Fixed WebSocket navigation paths to use store-backed IDs.\n- Streamlined Svelte 5 component lifecycle for better performance and SWR responsiveness.
2026-02-10 17:57:28 -05:00
Scott Idem
718de1457d Fix infinite hydration loop and stabilize global store synchronization
- Refactored layouts to derive account data from stable props instead of reactive stores.
- Wrapped store updates in untrack() with deep equality guards to prevent infinite re-renders.
- Resolved duplicate untrack declarations and missing imports across the project.
- Added fetch safeguards to Element_data_store to prevent redundant API calls.
- Standardized hydration patterns to break circular dependencies during initial load.
2026-02-08 17:15:20 -05:00
Scott Idem
88bc18cf15 fix(core): resolve 68 compiler errors and stabilize Svelte 5 reactivity
- Fixed 'Captured initial value' warnings in 65+ components by implementing
  proper sync effects with 'untrack' and derived states.
- Hardened Event Settings JSON editors using a temporary string-buffer pattern
  to safely decouple object-based data from CodeMirror's string requirements.
- Resolved strict TypeScript mismatches across core routes (Accounts, Sites, etc.)
  and improved property indexing safety in views.
- Patched Flowbite-Svelte Drawer transitions for Svelte 5 compatibility using
  prop spreading.
- Added comprehensive safety comments to high-risk reactivity blocks.
- Synchronized 'ae_types.ts' with V3 backend models.
2026-02-08 16:05:35 -05:00
Scott Idem
969e5610bb refactor(launcher): standardize helper names and apply batch formatting
- Renamed internal 'preventDefault' to 'prevent_default' in launcher files.
- Fixed native 'event.preventDefault()' call in launcher_file_cont.svelte.
- Applied batch formatting (printWidth: 80) across the (launcher) module.
2026-02-06 14:48:44 -05:00
Scott Idem
49f0a888b0 refactor(ui): standardize button types and migrate file operations to V3 Action API 2026-02-03 22:54:22 -05:00
Scott Idem
0809ad3eac feat(v3-auth): modernize hosted file access with simplified bypass pattern
- Roll out platform-wide standard for unauthenticated binary access using '?key=[account_id]' query parameter.
- Update API helpers (get, post, patch) to recognize 'key' bypass and strip account context headers accordingly.
- Refactor IDAA Bulletin Board to restore inline image rendering and edit-mode previews.
- Modernize Events Launcher (Layout, Sync, Session View) to use V3 Action URLs with verified auth.
- Update HTML generators in 'ae_utils.ts' to support the new authenticated URL structure.
- Harden 'ae_comp__event_file_obj_tbl' CSV export and clipboard links with V3 standard patterns.
2026-02-03 18:37:55 -05:00
Scott Idem
cb46eec440 perf(launcher): minimize session switch latency with non-blocking SWR and stable observables 2026-01-30 16:26:38 -05:00
Scott Idem
1dc0f0d77c perf(launcher): instrument SWR tracing and optimize session loading flow 2026-01-30 16:21:46 -05:00
Scott Idem
66affb2a0f perf(events): implement non-blocking SWR pattern and optimize library loaders
- Refactored 'event_session', 'event_presentation', 'event_file', 'event_location', and 'event_presenter' libraries to follow a Stale-While-Revalidate pattern.
- Implemented non-blocking background refreshes to allow instant UI rendering from IndexedDB cache.
- Parallelized nested collection loads (files, presentations) during background tasks to eliminate UI stalls.
- Standardized ID lookups using specific indices (event_location_id, event_id) for reliable local data retrieval.
- Resolved regression where aggressive ID overwriting caused relationship inconsistencies in background loads.
- Fixed reactive access to live queries in 'session_view.svelte' using proper Svelte 5 prefixing.
2026-01-30 16:04:28 -05:00
Scott Idem
3148375eb3 feat(launcher): implement auto-collapse coordinator and overhauled configuration UI
- Introduced 'Launcher_Cfg_Section' with 3-way state support (collapsed, auto, pinned).
- Implemented 'handle_section_expand' coordinator in 'launcher_cfg.svelte' for single-active-section behavior.
- Overhauled all configuration sub-components to participate in the auto-collapse logic.
- Updated 'ae_events_stores.ts' with new persistent section states.
- Synchronized 'launcher_cfg_template.svelte' with the new pattern for future extensions.
2026-01-30 14:11:52 -05:00
Scott Idem
1d5401bbe5 First round of work on the Launcher Config. 2026-01-30 13:22:01 -05:00
Scott Idem
5a2eaa8fac feat(frontend): implement string-only ID standardization and native data cleaning
Update layout.ts to clean raw data from the native bridge. Initialize events_slct.event_device_id in launcher layout. Resort device_id prioritization in launcher_background_sync.svelte.
2026-01-30 12:38:13 -05:00
Scott Idem
79917edffc feat(launcher): implement device heartbeat and background sync engine
- Hardened 'find_object_id' in Dexie to support 'event_' prefixed IDs.
- Implemented singleton 'LauncherBackgroundSync' in root launcher layout.
- Added V3-compliant heartbeat and room refresh cycles.
- Fixed redundant component imports and Skeleton UI prop errors.
- Added 'inc_file_li' support to event API loader.
2026-01-26 11:23:12 -05:00
Scott Idem
2db2aba6f9 feat(launcher): improve Events Presentation Launcher stability and data flow
- Standardized ID usage: Migrated multiple components from '_random' variants to standard 'id' properties to align with V3 backend and Dexie patterns.
- Electron Integration: Added global 'native_app' declaration and Window interface augmentation in app.d.ts to resolve TypeScript errors.
- Type Safety: Enhanced ae_EventSession interface with missing 'event_file_li' and added runtime null checks in session loading logic.
- UI/UX: Restored missing launcher components in location-specific pages and fixed LiveQuery filter logic for session lists.
- Bug Fixes: Resolved indexing errors in location list and corrected store update patterns in layout.
2026-01-16 13:59:51 -05:00
Scott Idem
10cc435146 There have been a lot of changes. For some reason the commit is not working? Trying again. 2025-11-19 18:56:58 -05:00
Scott Idem
0987cd6ad9 style: Apply Prettier formatting with 4-space indentation
Applied consistent code formatting across the project using Prettier, now configured to use 4-space indentation instead of tabs.
2025-11-18 18:40:50 -05:00
Scott Idem
7e1eaba3bc feat: Migrate ESLint to flat config and resolve initial linting errors
Migrated the ESLint configuration to the new flat config format ()
and addressed several initial linting errors.

Key changes include:
- Updated ESLint configuration to treat  as warnings instead of errors.
- Fixed  errors in  by declaring  and .
- Corrected  error in  by using  instead of an out-of-scope .
- Resolved  error in  by replacing the undefined  directive with the  component.
- Addressed  errors in  by replacing  with  and  with .
- Fixed  errors in  by importing necessary modules (, , ) and adding missing props (, , , , ).
2025-11-17 18:46:54 -05:00
Scott Idem
e9a8f7df00 This was a lot... things are mostly working again. The changing of id_random properties caused some problems. The hosted_file_hash_sha256 is not working? There are other issues as well... This will take some time. 2025-11-13 18:38:00 -05:00
Scott Idem
de7e02b9ef Refactor: Update import paths for api.ts after moving it to src/lib/api/api.ts. 2025-11-13 16:16:18 -05:00
Scott Idem
e912c4a48a Better text sizing and flex. 2025-10-16 16:39:30 -04:00
Scott Idem
8d15a5ba0b More work on the WebSockets. Improved status and WS triggers. Better online/offline status. 2025-10-16 14:48:10 -04:00
Scott Idem
3d6b7c412c Work on tablet layout for posters 2025-10-16 11:45:33 -04:00
Scott Idem
77b250b4f3 Better handling of idle with WS messages. 2025-10-16 10:58:11 -04:00
Scott Idem
9678c5620d Lots of work on the Launcher and configuration. Looks pretty good and useful. 2025-10-15 19:01:30 -04:00
Scott Idem
b2154273e0 Reset looks better now. 2025-10-15 13:46:08 -04:00
Scott Idem
1f79ae4e19 More work on versioning for Events 2025-10-15 13:17:32 -04:00
Scott Idem
6b8f4d54ed The posters look pretty good now. A lot more is configurable. 2025-10-15 12:52:27 -04:00
Scott Idem
f459c09fbc Getting the posters ready for LCI. 2025-10-15 10:37:46 -04:00
Scott Idem
0bc04bf899 Things are looking and working better. Note the Modal size is set to "". This allows it to stretch to the full width of the screen. 2025-10-13 18:17:32 -04:00
Scott Idem
8f7c1bf428 More work on the posters and Websockets 2025-10-13 17:19:32 -04:00
Scott Idem
de071dae49 Work on the Launder and Websockets. It needs work. 2025-10-13 15:41:55 -04:00
Scott Idem
41d20a3bcf Minor clean up getting ready for LCI and demo for AAPOR. 2025-10-08 16:41:57 -04:00