Commit Graph

1432 Commits

Author SHA1 Message Date
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
c73b5a09e4 feat: add element_access_denied.svelte; use in badge review page
- New reusable element_access_denied.svelte with title, message, action props
- Badge review page: swap inline 'Access Denied' card with the component
- Project doc: all 6 steps complete, status → Complete
2026-03-11 17:06:11 -04:00
Scott Idem
0c11cfb3e2 fix: replace alert() access guard in event settings with proper UX
- Remove blocking alert() + module-level browser guard
- Move access check to onMount with 500ms grace delay (matches /core pattern)
- Add {:else} block: Lock icon + 'Access Restricted' message + redirect link
- Remove now-unused 'browser' import; add Lock from lucide
2026-03-11 16:59:26 -04:00
Scott Idem
53c517ec30 feat: session-expired banner via ae_auth_error store
- Add ae_auth_error writable store to ae_stores.ts
- Wire api_get_object, api_post_object, api_patch_object to set
  ae_auth_error on 401/403 (browser-only guard, never fires SSR)
- Root layout watches ae_auth_error; only raises flag_expired when
  a JWT is present (prevents false trigger on unauthenticated loads)
- Dismissible amber banner added to root layout (non-blocking, above content)
- Tested via debug menu trigger; banner fires and clears correctly
2026-03-11 16:56:07 -04:00
Scott Idem
60ca3b2f6c fix: update docs/todos after v1 edit form retirement + v2 rename
- Update CLIENT__IDAA_and_customized_mods.md: remove v1 entry and v2 suffix
- Update tests/README.md: rename _v2 reference to canonical filename
- Update TODO__Agents.md: mark all state_referenced_locally warnings resolved;
  document remaining 23 CSS @apply warnings as harmless language-service noise
2026-03-11 15:42:58 -04:00
Scott Idem
9c291cf286 fix: move IDAA recovery meeting v2 lookup calls into onMount; remove unused CSS
state_referenced_locally warnings in ae_idaa_comp__event_obj_id_edit_v2.svelte:
- lu_country_list and lu_country_subdivision_list $state runes were read in
  top-level synchronous if/else blocks; moved into onMount
- Add onMount to Svelte imports
- Remove unused .field-richtext CSS selector

Remaining 32 warnings in 2 files are either:
- CSS @apply / @reference warnings from the CSS language service not understanding
  Tailwind v4 at-rules (harmless, build works fine)
- Warnings in the legacy v1 edit form (no code references it)
2026-03-11 15:34:41 -04:00
Scott Idem
5c09730991 fix: move IDAA recovery meeting page browser block into onMount
Two state_referenced_locally warnings on data prop in
recovery_meetings/[event_id]/+page.svelte: reading a $props()
rune synchronously in a top-level if (browser) block only captures
the initial value.

Move the postMessage block into onMount (browser-only by nature);
remove the now-redundant 'browser' import.
2026-03-11 15:23:59 -04:00
Scott Idem
a878e4a05b fix: clean up launcher tmp/bindable items; update TODO
- Update stale comment in menu_location_list.svelte: prop is already
  $bindable(null), comment incorrectly said it was not
- Confirm cleanup_tmp_files is wired in launcher_background_sync.svelte
- Mark both items done in TODO__Agents.md
2026-03-11 15:17:32 -04:00
Scott Idem
bf94e0dee9 fix: extend poster session type context to all file list wrapper contexts
element_manage_event_file_li_all.svelte — also derives context_session_type_code
via Dexie chain (event_presentation → session, or event_presenter → presentation →
session) and passes it to element_manage_event_file_li. Fixes the button not showing
when viewing a presenter's files from the session view.

element_manage_event_file_li_direct.svelte — extends the Dexie chain to also handle
event_session (direct lookup) and event_presentation, not just event_presenter.

Both: correct API URL to /v3/hosted_file/ per backend agent's examples.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 14:35:24 -04:00
Scott Idem
f6a6534380 fix: restore /v3/action/hosted_file path now that endpoint exists in v3 router
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 14:26:29 -04:00
Scott Idem
8cba7899db fix: correct convert_file API path from /v3/action/hosted_file to /hosted_file
The endpoint is registered under the older hosted_file router at /hosted_file/{id}/convert_file,
not under the v3 actions router. Both list and table convert buttons were sending to the wrong path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 14:12:53 -04:00
Scott Idem
2e08f5ff15 feat(events): restore PDF→webp convert button in event file table
Adds a per-row "Convert PDF → Image" button in ae_comp__event_file_obj_tbl.
Only shown when edit_mode is on, the file is a PDF, and the session
type_code is 'poster' — poster sessions need images in the Launcher modal
(which uses <img>, not a PDF viewer).

Calls GET /v3/action/hosted_file/{id}/convert_file (pdf2image, 3840px wide,
first page, saves as a new hosted_file linked to the same parent object).
Per-row status tracking: idle → converting → done | error with retry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 13:01:26 -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
556a395a3e fix: always default hash_prefix_length to 2, never preserve stale localStorage value
The event_device table has no hash_prefix_length column, so incoming_dev.hash_prefix_length
is always undefined from the API. The old merge logic preserved whatever was in localStorage,
which had a stale value of 1 from earlier testing — causing the background file sync to create
1-char cache subdirectories instead of the correct 2-char SHA-256 prefix dirs.

Background sync now consistently creates 2-char dirs matching launch_from_cache behavior.
If the API ever returns a hash_prefix_length, it will be used; otherwise the floor is 2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 11:41:42 -04:00
Scott Idem
319b935cc5 feat(launcher): configurable .tmp cache cleanup in cfg panel
- launcher_cfg_local_actions.svelte: add "Cache Maintenance" block
  (native-only, gated by is_native && cache_root); number input for
  max age in hours, "Clean Now" button with status feedback
- launcher_background_sync.svelte: read cleanup_tmp_max_age_hours
  from events_loc.launcher store (default 24h) instead of hardcoded
  1440 minutes; setting persists across sessions via persisted store

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 10:56:39 -04:00
Scott Idem
f6344008ea security: use bootstrap key in manifest, add .tmp cache cleanup
- manifest.webmanifest/+server.ts: swap PUBLIC_AE_API_SECRET_KEY →
  PUBLIC_AE_BOOTSTRAP_KEY (least privilege; endpoint only needs a
  site-domain lookup, same as the bootstrap use case)
- electron_relay.ts: add cleanup_tmp_files() — runs `find ... -name
  "*.tmp" -mmin +N -delete` via native run_cmd bridge
- launcher_background_sync.svelte: call cleanup_tmp_files() on mount
  when is_native && cache_root are present (once per startup)
- AE__Permissions_and_Security.md: close Sev-1 audit language
- TODO__Agents.md: mark PUBLIC_AE_API_SECRET_KEY audit as complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 10:54:17 -04:00
Scott Idem
b479dea33e [Launcher] Add accessibility controls + doc comments to sidebar menu
- New: menu_launcher_controls.svelte — bottom control bar for the launcher
  sidebar with font size cycler (A / A+ / A−) and light/dark mode toggle,
  always visible regardless of access level; visibility toggles (All Files /
  All Sessions) moved here from launcher_menu.svelte and restyled to
  preset-tonal-tertiary with descriptive title attributes
- launcher_menu.svelte — replace inline visibility-toggle block with
  <Menu_launcher_controls />; add full header doc-comment describing
  component structure and data flow
- menu_location_list.svelte — add header doc-comment covering purpose,
  visibility rules, data flow, and the intentional non-bindable prop pattern
- documentation/TODO__Agents.md — mark font size cycler task complete
2026-03-11 10:18:13 -04:00
Scott Idem
f03627ef3c security: move hardcoded bootstrap API key to env var
PUBLIC_AE_BOOTSTRAP_KEY replaces the hardcoded 'IDF68Em5X4HTZlswRNgepQ' in:
- src/routes/+layout.ts (site-domain bootstrap request)
- src/routes/testing/+page.svelte (trace agent key)

Added to .env.staging, .env.prod, .env.local (gitignored), and updated
.env.staging.default / .env.prod.default with XXXX placeholders.
Key can now be rotated independently from the main API secret key.
2026-03-10 16:30:11 -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
517c40bb11 Chore: silence debug logging across all pages (log_lvl → 0)
Set log_lvl to 0 in all pages and layouts that had it left at 1 or 2
from development. Also remove two hardcoded `log_lvl = 2` overrides
inside function bodies in reports_files.svelte and
reports_presenters.svelte that were forcing verbose output regardless
of the module-level setting.

Affected: launcher location page, leads pages (2), pres_mgmt reports (2),
presenter +page.ts, IDAA layouts (2), IDAA archives, IDAA recovery
meetings page, journals pages (2).
2026-03-10 14:23:28 -04:00
Scott Idem
44d4b8e04f IDAA: guard attend_json.zoom against SWR IDB re-fire crash
The Zoom button onclick initialises attend_json.zoom. However the
background SWR list fetch (load_ae_obj_li__event in +layout.ts) can
overwrite $idaa_slct.event_obj with a fresh IDB record where
attend_json = {} (no zoom key), even if the Zoom button was already
clicked.

Without the guard, the $effect that rebuilds the Zoom full URL and the
template bindings below the Zoom fields access attend_json.zoom.passcode_enc
on an undefined object, throwing a TypeError and crashing the component.

Fix: add `&& $idaa_slct.event_obj.attend_json?.zoom` guard to both the
$effect condition and the {#if} block that renders the Zoom input fields.
2026-03-10 14:22:23 -04:00
Scott Idem
3f57077a8b Pres mgmt: migrate session liveQueries to $derived.by() pattern
Replace $derived(liveQuery(...)) with $derived.by(() => { const id = ...; return liveQuery(...) })
for lq__event_presenter_obj and lq__auth__event_presenter_obj in the
session page.

$derived.by() captures only the specific ID at derivation time, so
unrelated store changes do not recreate the observable. Plain $derived
closes over the entire $events_slct store object, causing unnecessary
observable recreation on any store mutation.
2026-03-10 14:21:51 -04:00
Scott Idem
46c2d2da12 Launcher: preserve legacy address fields in IDB; add file_sync to loop_info
ae_events__event.ts: Add legacy flat address fields (address_name,
address_line_1, address_city, etc.) to properties_to_save. Events
predating location_address_json still return these flat fields from
the API; they must survive an IDB round-trip without being stripped
by _process_generic_props, or the edit form's fallback reads return
undefined.

launcher_background_sync.svelte:
- Move file_sync interval into loop_info $state alongside other
  intervals; load from cfg/device config with fallback (30 s). Keeps
  all intervals in one place for UI display consistency.
- Align onMount fallback values to match loop_info defaults.
- Add sync_paused mid-loop break: long sync cycles now honour a pause
  request per-iteration rather than waiting for the entire batch.
2026-03-10 14:21:37 -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
b6c55a5042 [Launcher] Fix $bindable warning on slct_event_location_id; audit TODO items
- menu_location_list.svelte: mark slct_event_location_id as $bindable(null) to
  resolve Svelte 5 compiler warning (bind:value used on non-bindable prop)
- TODO__Agents.md: audit and close resolved launcher items:
  - Location select auto-load bug: fixed via $derived.by() liveQuery pattern
  - Session Search button visibility: was never a real bug, hardcoded false
  - Dark mode select fix: already applied via app.css color-scheme rules
2026-03-10 11:30:26 -04:00
Scott Idem
d4f63138ad [DevOps] Add /health endpoint and Docker HEALTHCHECK
- src/routes/health/+server.ts: lightweight health endpoint returning 200 OK
- Dockerfile: HEALTHCHECK hitting /health every 30s, 5s timeout, 10s start period
  Enables Docker/Nginx zero-downtime routing and auto-recovery.
2026-03-10 11:29:02 -04:00
Scott Idem
9b285d7fec feat: implement automated Docker deployment and update README
- Replaced manual rsync/npm_deploy workflow with multi-stage Docker builds.
- Added Dockerfile and .dockerignore for staging and production environments.
- Added 'deploy:staging' and 'deploy:prod' scripts to package.json.
- Updated README.md with new deployment instructions.
2026-03-09 22:17:54 -04:00
Scott Idem
206faf0c71 fix: resolve TS errors and Svelte 5 state_referenced_locally warnings
- e_app_sign_in_out: type user_id/person_id as string|null (TS errors)
- archives/[archive_id]/+page.svelte: move if(browser) block to onMount
- ae_idaa_comp__archive_obj_id_edit: wrap timezone loader in onMount
- ae_idaa_comp__archive_content_obj_id_edit: wrap timezone loader in onMount
- bb/[post_id]/+page.svelte: move if(browser) block to onMount
- TODO: add completed entries, note remaining recovery_meetings warnings
2026-03-09 19:40:36 -04:00
Scott Idem
9b7832ee55 feat(themes): Add AE_Firefly variant themes — SteelBlue, Indigo, Rainbow
Three new Firefly-family themes following the AE_Firefly design system:
- AE_Firefly_SteelBlue: metallic steel blue primary (~214°), burnished gold
  secondary, cobalt navy tertiary, chrome silver surfaces
- AE_Firefly_Indigo: deep indigo primary (~266°), violet secondary, dusty
  rose tertiary, velvet slate surfaces
- AE_Firefly_Rainbow: coral-red primary (~15°), emerald green secondary,
  rich violet tertiary, sunrise cream surfaces (spans the visible spectrum)

All variants share consistent semantic colors (success/warning/error) with
AE_Firefly for cross-theme recognizability. All WCAG 2.1 AA compliant.

Also adds URL param support for theme switching:
- ?theme=AE_Firefly_SteelBlue&theme_mode=dark
- Params applied to ae_loc (persisted), then silently removed via replaceState
2026-03-09 19:22:17 -04:00
Scott Idem
2c21117a3f style(idaa): unify Admin Options toggle style across all IDAA edit forms
Replace the old float-right Show/Hide toggle button + separate collapsible
div with an inline caret-button heading inside a consistent section card
(bg-surface-100-900, border-error-400) — matching the recovery meetings v2
form style. All four forms updated: Post, Post Comment, Archive, Archive
Content. Existing class:hidden behaviour preserved so FormData is unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:29:39 -04:00
Scott Idem
3a1ec9a861 feat(idaa/recovery_meetings): collapse Admin Options section by default
Admin Options are rarely changed; collapsing by default reduces visual
noise on the long edit form. Hidden inputs preserve status/enable/hide/
priority/sort/group values when the section is collapsed so a save never
silently resets admin fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:15:46 -04:00
Scott Idem
b7352c080a feat(idaa/recovery_meetings): UX improvements to long edit form v2
- Add Save Changes button at top of form so users don't scroll to the
  bottom to save; existing meetings only (mirrors v1 behaviour)
- Collapse Contact 2 subsection behind a toggle; auto-expands when the
  meeting already has Contact 2 data saved
- Add hidden inputs when Contact 2 is collapsed so FormData preserves
  existing contact info rather than overwriting with nulls on save

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:13:36 -04:00
Scott Idem
173dc3c84c fix(idaa/bb): fire-and-forget page load + UX fixes for post/comment editing
- [post_id]/+page.ts: remove await on post load (same pattern as archives);
  liveQuery renders immediately, replace error(404) with console.warn.
- ae_idaa_comp__post_options: add in-flight creating guard + spinner on Create
  New Post button to prevent double-submit; remove ~15 lines of dead commented
  code left over from previous refactors.
- ae_idaa_comp__post_obj_id_view: remove confirm() dialog before opening the
  Add Comment form — no need to confirm an intent to type a comment.
- ae_idaa_comp__post_comment_obj_id_edit: remove redundant block that manually
  injected post_id into the payload for new comments; post_id is already
  handled correctly by the form payload builder.
2026-03-09 17:53:35 -04:00
Scott Idem
dac7fc99da feat(idaa/archives): fire-and-forget page load + Create New Archive/Content buttons
- +page.ts: remove await on archive load so navigation is not blocked;
  liveQuery renders from Dexie cache immediately, API result updates reactively.
  Replace error(404) with console.warn — soft failure is correct for IDAA.
- ae_idaa_comp__archive_obj_li: add Create New Archive button (trusted+edit_mode
  only) with in-flight spinner and creating guard to prevent double-submit.
  Layout adjusted to justify-between to accommodate the new button.
- ae_idaa_comp__archive_content_obj_li: add Create New Archive Content button
  with same spinner/guard pattern; pre-populates original_timezone from parent
  archive so staff do not need to re-select it for every content item.
2026-03-09 17:53:24 -04:00
Scott Idem
89119191b1 fix(idaa): fix iframe scroll and parent URL not updating on navigation
Two bugs fixed:

1. scroll_to handler scrolled to page top (0,0) instead of the iframe's
   position in the Novi page. The iframe sits below Novi's own header/nav,
   so the user ended up looking at the Novi header instead of the iframe
   content after navigation. Fixed to use getBoundingClientRect() to scroll
   to 20px above the iframe's actual document position. Also added the
   missing scroll_to handler to idaa_novi_iframe_archives.html (it had none).

2. Parent URL not updating with event_id/post_id/archive_id on navigation.
   Detail pages sent postMessage using $idaa_slct.<id> (the store), which
   is still null at synchronous init time — the $effect that populates it
   runs later. Fixed to read from data[data.account_id].slct.<id> directly
   (set by the +page.ts load function from URL params before render).

Also added afterNavigate to idaa/+layout.svelte to send scroll_to on all
client-side navigations, covering cases the per-page blocks miss (e.g.
navigating back to the list view).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 14:49:06 -04:00
Scott Idem
eb0dcb17f8 fix(idaa): upgrade Novi UUID verification to server-side API call
Previously, IDAA iframe access relied on trusting URL params (uuid, email,
full_name) passed from Novi — any 36-char string granted authenticated access
with no actual verification.

The (idaa)/+layout.svelte now performs an async Novi API call on every UUID
load to verify the UUID exists, fetches name/email directly from Novi (cannot
be spoofed via URL), and sets $idaa_loc.novi_verified on success.
All-or-nothing: if novi_idaa_api_key is absent or the call fails, access denied.

- ae_idaa_stores.ts: add novi_verified boolean field to idaa_loc
- (idaa)/+layout.svelte: async UUID verification with spinner to prevent
  Access Denied flash; permission upgrade-only strategy preserved
- video_conferences/+page.svelte: skip duplicate Novi member details call if
  layout already verified ($idaa_loc.novi_verified check)
- iframe HTML files: remove browser-side Novi API fetch and email/full_name
  params; pass only uuid; add README/START/STOP/WARNING comments for client
  staff; fix iframe-before-script DOM ordering bug
- documentation: CLIENT__IDAA_and_customized_mods.md updated with full
  verification flow, site_cfg_json fields, permission table, access gate

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 14:48:49 -04:00
Scott Idem
7df887fabd Typo 2026-03-09 11:10:07 -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
95508458f6 ui: hide Session Search nav on its own page, dark mode file list, font size cycler
- event_page_menu: set events__session_search=false — the Session Search nav
  link was redundantly appearing on the Session Search page itself

- element_manage_event_file_li: replace hardcoded gray hover colors with
  theme-aware surface tokens (hover:bg-surface-100-900, border-surface-200-800)
  and add transition-colors; fixes light-on-light in dark mode for the file
  list table rows and Event File Purpose select element

- font size cycler (default → larger → smaller → default):
  - ae_stores: add font_size_mode: 'default' to ae_loc defaults
  - app.css: html.font-size-larger (112.5%) and html.font-size-smaller (87.5%)
  - +layout.svelte: DOM effect applies/removes font-size-* class on <html>
  - e_app_sys_menu: compact A / A+ / A− button cycles the mode
2026-03-06 21:35:06 -05:00
Scott Idem
9f228a35fa docs: add UI style guidelines + component patterns reference
- GUIDE__AE_UI_Style_Guidelines.md: overall design system
  - Firefly theme semantics (Primary/Secondary/Tertiary/Surface)
  - Forbidden class list (bg-gray-*, variant-soft-*, rounded-container-token, etc.)
  - Skeleton v3 -> v4 migration table
  - Transition, a11y, dark mode, QR type-guard rules
  - Debug code to remove checklist
- AE__UI_Component_Patterns.md: per-component class recipes
  - Hero card, standard card, table row, list item, info chips
  - Empty state, upload zone, section wrapper, agree forms
  - Modal, muted text, QR code, icon usage

pres_mgmt: complete styling cleanup (second pass)
- location/+page.svelte: rounded-container-token -> rounded-xl
- location_view.svelte: code chip yellow -> preset-tonal-warning;
  description pre bg-gray-100 -> bg-surface-100-900
- ae_comp__event_presenter_obj_li.svelte: remove debug borders,
  overflow-x-scroll -> overflow-x-auto, ul bg-gray-100 removed
- presenter/[presenter_id]/+page.svelte:
  - Both upload zones: bg-gray-100 -> surface tokens with transition
  - Label text: text-gray-600 dark:text-gray-400 -> opacity-60
  - Modal: remove bg-white dark:bg-gray-800 theme bypass
- pres_mgmt/+page.svelte: event upload zone same surface token fix
- ae_comp__event_session_poc_form_agree.svelte:
  - bg-gray-100 container -> bg-surface-100-900
  - bg-yellow-100 identity line -> preset-tonal-warning
- ae_comp__event_presenter_form_agree.svelte: same as above
2026-03-06 21:25:28 -05:00
Scott Idem
6cc595ee9c Saving more notes. 2026-03-06 21:24:16 -05:00
Scott Idem
1dd8e35720 pres_mgmt: redesign Session View, clean Presentation list, fix transitions
- session_view.svelte: replace flat <ul> with hero card layout
  - Name as <h1>, date/time chip (primary teal), room chip (tertiary indigo)
  - QR only rendered when URL is string (not true loading placeholder)
  - Skeleton pulse placeholders while LiveQuery resolves
  - Description in surface card with uppercase label
  - Accessible: aria labels, focus rings, aria-live on no-results section
- ae_comp__event_session_obj_li.svelte:
  - variant-soft-warning (Skeleton v3) -> preset-tonal-warning (v4)
  - Add transition-colors duration-200 to <tr> rows and session <a> links
- ae_comp__event_presentation_obj_li.svelte:
  - Remove debug breakpoint border colors (red/yellow/gray)
  - overflow-x-scroll -> overflow-x-auto
  - Remove heavy preset-filled-surface-400-600 from <ul> container
  - <li> cards: surface tokens, rounded-xl, shadow-sm, transition
  - <h4> title bar: bg-surface-100-900 with flex wrap layout
  - Code badge: hardcoded yellow -> preset-tonal-warning
  - Description <pre>: hardcoded bg-gray-100 -> bg-surface-100-900
- pres_mgmt/+page.svelte: 'no results' section
  - bg-yellow-100 + text-yellow-500 -> preset-tonal-warning
  - Search icon, aria-live, cleaner list in surface card
- [session_id]/+page.svelte: rounded-container-token (v3) -> rounded-xl
2026-03-06 21:15:27 -05:00
Scott Idem
5f57d81ead fix(launcher): stabilize session header height to prevent bouncing
Root cause: flex-row flex-wrap on the session header caused the datetime
and name to compete for the same row. Long session names (up to 300 chars)
wrapped onto 2-3 lines while short names stayed 1 line, making the header
jump in height every time the operator switched sessions.

Fix:
- header: flex-row flex-wrap -> flex-col; datetime and name are now
  always on separate rows, header height is predictable in both cases
- h2 name: shrink -> grow line-clamp-2 min-w-0; height is always exactly
  2 lines, never less, never more; full text accessible via title attribute
- code badge: added shrink-0 so it is never squeezed by a long name
- removed justify-between/justify-end conditional classes (no longer relevant)
- Section 508: title attribute on h2 provides full text for screen readers
2026-03-06 20:37:23 -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
4cecc7a860 style(launcher): accessibility, session list UX, and preset-* token fixes
Style token fixes:
- launcher_cfg.svelte: tab buttons preset-filled-primary-500 -> preset-filled-primary;
  opacity-50 inactive -> preset-tonal-surface
- launcher_cfg_app_modes.svelte: same fix for app mode buttons (opacity-40)
- launcher_cfg_controller.svelte: variant-filled-success/error -> preset-filled-*
- launcher_cfg_template.svelte: variant-filled-success -> preset-filled-success
- launcher_session_view.svelte: add dark:border-gray-600/700 to bare border-gray-*

Session list (menu_session_list.svelte) -- full accessibility + UX pass:
- fix: background sync fetches hidden:all so All Sessions toggle works
- fix: hide_event_launcher respected in class:hidden and class:opacity-40
- fix: overlay uses explicit opaque backgrounds (slate-100/slate-800) to prevent
  preset-tonal-secondary transparency bleed-through in light and dark mode
- feat: compact fixed 2rem row height; position:absolute overlay on hover/focus
  reveals full session name (300-char) without any layout shift (no sibling movement)
- feat: active session always fully visible in flow (height:auto, no clipping)
- a11y: hover_timer_wait 750->1200ms (motor accessibility)
- a11y: removed hover:scale which caused cursor drift and timer jitter
- a11y: px-1.5 py-1 touch targets, focus-visible ring for keyboard/switch users
- a11y: fa-eye-slash icons distinguish hide vs hide_event_launcher states
- docs: comprehensive OSIT/Aether-specific comments throughout
2026-03-06 20:25:31 -05:00
Scott Idem
cc6f73ca04 style(journals): standardize Skeleton v4 preset-* classes across all journal components
- Replace all Skeleton v2 variant-* classes with v4 preset-* equivalents
  - variant-filled-* → preset-filled-*
  - variant-soft-* / variant-ghost-* → preset-tonal-*
  - variant-outline-* → preset-outlined-*
  - variant-form-material removed from inputs/selects/textareas
  - input-bordered removed

- Fix dark mode: journal entry content hover (dark:hover:bg-blue-950)
- Fix dark mode: journal obj view section/description bg and text colors
- Fix modal headers: add dismissable=false + explicit X close button (all 3 journals modals)
- Fix DaisyUI wrappers removed from modal_journal_entry_append
- app.css: add global select padding-inline to fix text-against-border issue
2026-03-06 19:15:51 -05:00
Scott Idem
3ca9503b88 fix: resolve svelte-check warnings (non-IDAA batch)
- Remove dead .field_editing_wrapper CSS rules from element_ae_crud_v2.svelte
  (template migrated to field_editing_wrapper_v2 with Tailwind)
- Fix TS error: use optional chaining on person_obj key in ae_comp__person_obj_tbl.svelte
- Fix state_referenced_locally: wrap data.user init with untrack() in users/[user_id]/+page.svelte
- Replace misused <label> with <span> for visual section headings (a11y) in:
  launcher_cfg_native_os.svelte, launcher_cfg_health.svelte,
  launcher_cfg_local_actions.svelte, launcher_cfg_template.svelte
2026-03-06 18:15:31 -05:00
Scott Idem
48d5fe8995 fix: add missing each-block keys (svelte/require-each-key)
Fixed all 27 remaining instances across 19 files. Keys used:
- Object ID fields where available (e.g. account_id_random, event_file_id)
- index for logger lists with no reliable unique key
- Property name for Object.entries() loops
2026-03-06 17:54:50 -05:00