2433 Commits

Author SHA1 Message Date
Scott Idem
d32355a1a2 docs(launcher): add cfg menu inventory and v3.1 design docs
MODULE__AE_Events_Launcher_Config_Menu.md — v3.0 inventory of the
3-tab drawer layout (now superseded but kept as reference baseline).
MODULE__AE_Events_Launcher_Config_Menu_new.md — v3.1 unified design
spec that drove the sidebar tab migration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:17:07 -04:00
Scott Idem
213eabd8c1 feat(launcher): migrate cfg menu to 7-tab sidebar layout (v3.1)
Replaces the 3-tab horizontal bar (Setup / Device / Dev) with a vertical
sidebar navigation matching the v3.1 design spec. New tab structure:

  General       — App Modes, Screen Saver (operator-facing)
  Connectivity  — Remote Controller & WebSocket
  Sync & Health — Sync Timers, System Health
  Native OS     — OS controls (native or edit_mode preview)
  Wallpaper     — Desktop wallpaper settings
  Advanced      — Launch Timing, Updates (edit_mode only)
  Maintenance   — Local Resets, Debug Panel (edit_mode only)

Layout changes:
- Sidebar nav (w-48) + scrollable main content area replace inline tab bar
- Tab header shows label + description subtitle
- Technical Mode toggle is now a labeled button (not hidden icon)
- Footer shows Account/Device context; Reload moved to header
- {#key active_tab} wrapper ensures clean component remount on tab switch
- Remove unused icons (SlidersHorizontal, HeartPulse, Timer, CloudDownload)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:14:52 -04:00
Scott Idem
872291b0a0 fix(launcher): replace Flowbite Modal with custom overlay for cfg panel
Two problems with the Flowbite <Modal> approach:
1. Built-in dismissable CloseButton rendered with no functional dismiss path
   (no title/form), appearing centered in the panel.
2. size="xl" (max-w-7xl) left no backdrop area on typical laptop screens,
   making outsideclose impossible to trigger.

Replace with a simple custom overlay: full-screen backdrop div that closes
on click, inner panel with stopPropagation. Matches the original Drawer
pattern. close_cfg() writes to store immediately on backdrop click for
reliable persistence independent of effect timing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:04:34 -04:00
Scott Idem
25d17841e4 fix(launcher): fix cfg modal default-open and outside-click persistence
Two bugs in the Launcher Config Modal after the Drawer→Modal migration:
1. Pre-existing persisted configs (missing hide_drawer__cfg field) caused
   !undefined = true, opening the modal on every fresh load. Fixed by adding
   a field-level initialization guard after the full-object guard.
2. $-syntax writes inside untrack() were suppressed by svelte-persisted-store,
   so outside-click closure was never persisted. Fixed by using events_loc.update()
   directly to ensure the write reaches localStorage serialization. Added equality
   guard to effect 1 to prevent spurious modal flicker from whole-store re-fires.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 21:36:01 -04:00
Scott Idem
6282fb167f fix(launcher): use $derived.by for session liveQueries to fix stale presentation/presenter data
When switching sessions within the same location, presentations and presenters
were not updating. The root cause: plain $derived(liveQuery(...)) never recreates
the Observable when slct__event_session_id changes, because liveQuery's async
callback runs in Dexie's zone where Svelte tracking is off. Dexie's range-level
change detection then ignores new session data (it arrives under a different
event_session_id index value, outside the originally observed range).

Replaced all four liveQuery declarations with $derived.by(() => { const id = ...;
return liveQuery(...id...); }) — the same pattern already used in +layout.svelte
for location-dependent queries. Svelte tracks the id read in the outer closure
and recreates the Observable on every session change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 19:55:17 -04:00
Scott Idem
a90572bcb8 fix(idaa): ensure breakout links preserve site access key and uuid
Proactively re-injects 'key' (site access key) and 'uuid' (Novi token)
into 'Open Externally' and 'Copy Link' URLs on the Video Conferences
page. This prevents authentication failures when members open meetings
in a new browser tab after SvelteKit internal navigation has dropped
the bootstrap parameters.

Updated CLIENT__IDAA_and_customized_mods.md to document the requirement
for these keys in breakout URLs.
2026-05-23 11:31:10 -04:00
Scott Idem
194c89f6d1 style(launcher): layout and Tailwind class adjustments
+layout.svelte: add lg:min-h-8/12 and max-h-screen to main content area.
launcher_background_sync.svelte: reposition sync monitor panel (bottom-15,
left-2, z-10 — was bottom-20, left-4, z-9999).
launcher_menu.svelte: reorder Tailwind classes for readability, no change
to applied styles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 19:09:08 -04:00
Scott Idem
469729ce22 revert(help_tech): restore ae_loc preservation in Clear & Reload button
Reverts the change from d1f5d0e2f that removed ae_loc preservation.
The tech help component is used across non-Launcher contexts where
users are authenticated normally and should not be signed out.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 19:06:56 -04:00
Scott Idem
d1f5d0e2fd fix(launcher): clear ae_loc in cache cleanup; align tech help Clear & Reload
menu_launcher_controls: handle_cache_cleanup now removes both ae_events_loc
and ae_loc from localStorage, giving a true clean slate on reload.

e_app_help_tech: Clear & Reload button no longer silently re-saves ae_loc
after clearing — if edit mode wipes localStorage, ae_loc goes with it.
Updated confirm message and title tooltip to say "you will be signed out"
instead of the previous misleading "sign-in will be preserved."

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 19:05:16 -04:00
Scott Idem
9c83567430 feat(launcher): add Clear Cache and Reload Launcher buttons to controls bar
Fills in two new buttons added to menu_launcher_controls.svelte:
- Clear Cache: removes 'ae_events_loc' from localStorage and deletes the
  ae_events_db IndexedDB database, then reloads — clears stale launch state
  without touching downloaded file cache or user prefs (theme/font size).
- Reload Launcher: calls native.window_control({ action: 'reload' }) in
  Electron, falls back to window.location.reload() in browser mode.

Also fixes a stray 'lucide-svelte' import (merged Recycle into '@lucide/svelte')
and separates cache_status from reset_apps_status so button labels stay correct
when multiple actions fire.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 19:02:26 -04:00
Scott Idem
b4d0d82141 fix(launcher): fix VLC stopping 10-15 seconds after open on macOS
Root cause: run_cmd uses exec() which blocks until the child exits. The
direct VLC binary forks its GUI process and exits — exec returns and the
post_script begins. The old post_script polled for VLC focus (up to 10s)
then sent Cmd+F, which fired mid-playback and stopped the video.

Fix 1 — nohup + &: detaches VLC from exec immediately so run_cmd returns
in ~0ms. This decouples the launcher flow from VLC's lifecycle.

Fix 2 — --fullscreen flag: VLC opens fullscreen directly via CLI option.
Eliminates the Cmd+F keystroke that was the proximate cause of the stop.

Fix 3 — > /dev/null 2>&1: silences VLC's verbose logging to prevent
exec's 1MB stdout buffer from overflowing and killing the process.

post_script simplified to a single `tell application "VLC" activate`
to bring VLC to the foreground after the 3s startup delay.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 18:10:37 -04:00
Scott Idem
15bfe6d5d6 feat(launcher): move Reset Apps to always-visible controls bar
Adds a presenter-accessible "Reset Apps" button to menu_launcher_controls
that is always visible (no edit mode required). Kills presentation apps
(PowerPoint, Keynote, Acrobat, VLC, soffice) — critical recovery path for
presenters stuck on stage with a frozen app.

Also: warning colors on All Files / All Sessions when showing non-default
(hidden) content, and state-aware tooltips on the Display Mode toggle that
describe current state and what pressing will do.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 18:02:22 -04:00
Scott Idem
dddf4b6170 feat(launcher): restore Kill Apps button in Native OS config
Kills the standard conference presentation app set between sessions:
Microsoft PowerPoint, Keynote, Adobe Acrobat Reader DC, VLC, soffice.

- Calls native.kill_processes({ process_name_li }) via existing relay
- Process list overridable per device via event_device.other_json.launcher.kill_process_li
- Button lives in Native OS config > System Actions (edit mode only)
- Reuses system_status for feedback — shows which apps are being killed
- Original list recovered from git history of legacy architecture docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 16:43:42 -04:00
Scott Idem
587b815446 docs(todo): mark composable flow + slide scripts done; add event_file cfg_json backend task
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 15:58:38 -04:00
Scott Idem
ca51a82dae feat(launcher): richer tooltip on file download button
Tooltip now shows file size, created/updated timestamps, and open_in_os
setting alongside the existing SHA256 and hosted ID info.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 15:58:35 -04:00
Scott Idem
a38320c7f5 fix(launcher): monospace font for session list date/time column
Datetime values align cleanly across rows when rendered in font-mono.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 15:58:31 -04:00
Scott Idem
c76fb8f2b5 fix(launcher): open_in_os win routing, display override, and onsite ext fix
- open_in_os='win' now routes to Windows launch profiles (pptxwin/pptwin/odpwin/pdfwin)
  via WIN_EXTENSION_MAP in get_launch_profile() — was silently ignored before
- Display override migrated from non-existent cfg_json backend field to localStorage
  ($events_loc.launcher.file_display_overrides) — only visible in edit mode; TODO added
  for proper backend column when event_file gains cfg_json
- Onsite mode WIN extension rename now covers all 4 types (pptx, ppt, odp, pdf)
  instead of only pptx/ppt
- open_in_os button shows LoaderCircle spinner during API call
- Remove cfg_json from properties_to_save (column does not exist on event_file table)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 15:58:27 -04:00
Scott Idem
a26ea8b49c fix(launcher): optimistic update for display override button
Without this, the button depended on the liveQuery round-trip to show
the new state — invisible on stale IDB caches that predate the cfg_json
properties_to_save fix. Now mutates event_file_obj locally on click so
the button reflects the new state immediately, with the background
refresh as confirmation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 14:18:58 -04:00
Scott Idem
21fad1a698 fix(launcher): restore open_in_os win routing, fix cfg_json in IDB, fix display state
open_in_os = 'win': get_launch_profile() now maps pptx→pptxwin, ppt→pptwin,
odp→odpwin, pdf→pdfwin when open_in_os is 'win', routing to the Windows-variant
launch profiles (Parallels/CrossOver). Was never wired in native mode — feature
was silently lost in the MasterKey→Launcher port.

cfg_json missing from properties_to_save: the per-file display override was
always read as undefined from Dexie because cfg_json was never saved. Added
cfg_json to properties_to_save so display_override and any other cfg fields
persist correctly. NOTE: IDB_CONTENT_VERSIONS for event_file is not yet wired;
existing devices need a manual cache clear to pick up the new field.

Display override button: removed $ae_loc.is_native gate — must be configurable
from any device ahead of the event, not only on the podium Mac.

Display toggle persistence: quick_display_mode now reads from and writes to
$events_loc.launcher.display_mode so the last-set state survives page reloads
instead of always defaulting to 'extend'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 14:12:12 -04:00
Scott Idem
33e9eeef78 fix(launcher): retry activate in post-script loop to beat macOS focus-stealing
All app post-scripts called activate once before the poll loop, then only
checked `frontmost` passively. When Electron retains focus (common when the
app is spawned via run_cmd), macOS focus-stealing prevention blocks the one-
shot activate and VLC/PowerPoint/etc. never come to front. The poll loop
times out and fires the keystroke at Electron instead.

Fix: move activate inside the repeat loop so it re-fires every 0.5s until
macOS yields focus. Also bumped VLC post_delay_ms 1000→2000 and iterations
15→20 for slow conference machines.

Affected profiles: VLC (mac), PowerPoint (mac), LibreOffice (mac+win),
Acrobat (mac+win).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 13:51:53 -04:00
Scott Idem
172ea994c7 refactor(launcher): consolidate menu controls and anchor to bottom
- Combine Extend/Mirror into a single toggle button, moved behind edit_mode
- All edit-mode controls (All Files, All Sessions, Display) now share consistent preset-tonal-tertiary styling
- Remove the always-visible display row and its non-native-mode disclaimer
- Wrap Menu_launcher_controls in mt-auto to keep it pinned to the bottom of the sidebar regardless of session count
- Add min-w-20 to file size chip to prevent collapse on narrow sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 13:48:53 -04:00
Scott Idem
17b549a75c docs/refactor: finalize V3 cleanup and archive badge config project
- Moved PROJECT__AE_Events_Badges_Config_Cleanup.md to archive.
- Updated PROJECT__Use_AE_API_V3_CRUD_upgrade.md with latest audit and migration status.
- Migrated ae_comp__event_presenter_form_agree.svelte to modern V3 update_ae_obj helper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 23:04:07 -04:00
Scott Idem
3de01af1a1 docs: cleanup and archive agent TODO list
Archived completed May 2026 tasks and streamlined the active list to
focus on upcoming events and the final V3 API surgical cleanup.

- Created TODO__Agents__ARCHIVE_2026-05.md with completed items.
- Streamlined TODO__Agents.md for active show support (CMSC, Axonius).
- Added V3 CRUD migration tracking for core site and utility helpers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 23:02:25 -04:00
Scott Idem
518a450b91 docs(events): reorganize badges and leads documentation
Standardized documentation structure for Badges and Leads modules into
focused technical references and practical onsite guides.

- Refined MODULE__AE_Events_Badges.md (Core data integrity & sync logic)
- Renamed MODULE__AE_Events_Exhibitor_Leads.md to MODULE__AE_Events_Leads.md
- Renamed MODULE__AE_Events_Badges_Onsite.md to GUIDE__AE_Events_Badges_Onsite.md
- Expanded GUIDE__AE_Events_Onsite_Runbook.md with Badge and Leads sections.
- Maintained all critical business logic, including the 'Override Fields' pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 22:40:06 -04:00
Scott Idem
cb767ed115 docs(events): reorganize presentation and launcher documentation
Split the monolithic MODULE__AE_Events_PressMgmt_Launcher.md into focused,
granular modules to improve maintainability and onsite utility.

- Created MODULE__AE_Events_Presentation_Management.md (Back-office focus)
- Created MODULE__AE_Events_Launcher.md (Podium display focus)
- Created GUIDE__AE_Events_Onsite_Runbook.md (SRR and onsite workflows)
- Promoted PROJECT__AE_Events_Launcher_Native_integration.md to
  MODULE__AE_Events_Launcher_Native.md (Permanent technical reference)
- Renamed 'Press Mgmt' references to 'Presentation Management' for clarity.
- Removed redundant README.md in launcher route.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 22:36:00 -04:00
Scott Idem
86201f0fc1 feat(launcher): implement force-sync and chronological download priority
Onsite operators can now manually trigger a full pre-sync of all location
materials via a "Force Sync Location" button in the Launcher config.
This ensures podium Macs have all content cached before an event starts.

- Added trigger__force_location_sync to launcher session store.
- Implemented force_location_sync() in background sync engine to perform
  recursive metadata fetches for all sessions in a location.
- Optimized download queue with a 4-tier chronological sort:
  1. Event/Location assets (Global)
  2. Sessions by start time (Earliest first)
  3. Presentations by start time (Sequential order)
  4. File creation date (First-in fairness for on-time uploads)
- Updated module and native integration documentation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 22:29:13 -04:00
Scott Idem
60e3fc539e docs(devops): add Nginx caching investigation task for app version pickup issue
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 18:31:51 -04:00
Scott Idem
b3029a4d27 docs: update TODO and add BOOTSTRAP mistake #13 for API retry regression
TODO__Agents.md:
- Added the two additional fixes from the review pass to the PATCH/DELETE
  retry hardening entry: default timeout 60s→20s, and DELETE missing
  ae_auth_error banner on 401/403.

BOOTSTRAP__AI_Agent_Quickstart.md:
- Added mistake #13: breaking the API retry loop by returning errors from
  the TypeError/AbortError block instead of throwing them. Documents the
  Jan 2026 regression (commit a10accfaa), the three retry classes that must
  be preserved, and a quick verification method.
- Filled the gap at item #7 (was missing, causing off-by-one numbering
  from item 8 onward). Items renumbered 8-14 → 7-13.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 18:21:01 -04:00
Scott Idem
ea765d8ad2 fix(api): lower patch/delete timeout to 20s and add delete auth error banner
Two gaps found during review of the recent retry-hardening commits:

1. api_patch_object.ts and api_delete_object.ts still defaulted to 60s
   timeout while GET/POST were lowered to 20s. No callers set an explicit
   timeout, so the default was the only value used. With retry_count=5 and
   the new backoff policy, 60s per attempt = 5+ minutes worst-case wait.
   Lowered to 20s to match GET/POST and keep worst-case under ~2 minutes.

2. api_delete_object.ts had no ae_auth_error import and no session-expired
   banner on 401/403. A stale-session DELETE would silently return false
   with no user feedback. Added browser + ae_auth_error imports and the
   ae_auth_error.set() call matching the pattern in GET/POST/PATCH.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 18:11:32 -04:00
Scott Idem
db5acdd30a docs: align API retry hardening status with implemented helpers 2026-05-21 18:04:06 -04:00
Scott Idem
a000e07647 api: harden delete retry classification and backoff 2026-05-21 17:58:59 -04:00
Scott Idem
7f9368589a api: harden patch retry classification and backoff 2026-05-21 17:53:30 -04:00
Scott Idem
55d3d49595 test: add v3 latency probe and modernize api coverage 2026-05-21 17:48:00 -04:00
Scott Idem
f5cf1ef398 api: separate timeout abort retries from intentional aborts 2026-05-21 15:46:30 -04:00
Scott Idem
d5d552a029 Badge layout fix for Axonius 2026-05-21 15:19:48 -04:00
Scott Idem
689bb326cb fix(api): restore network-error retry and add backoff in get/post_object
The Jan 2026 "offline-first fast-paths" commit (a10accfaa) inadvertently
broke retries for transient network failures (ERR_NETWORK_CHANGED, WiFi
roam events, etc.). The original code's .catch() returned undefined, which
fell through to the `if (!response) throw` path and correctly entered the
retry loop. After a10accfaa, .catch() returned the error as a value, and
the subsequent `instanceof Error` check returned false immediately —
bypassing all retries for the most common failure mode in
hotel/conference environments.

Changes:
- TypeError now throws into the retry loop instead of returning false
- AbortError still returns false immediately (intentional cancel, no retry)
- Per-attempt AbortController: moved inside the loop in both files so each
  retry gets its own independent timeout (previously GET retries had no
  timeout at all after the first attempt's clearTimeout ran)
- clearTimeout() added to catch block so timer is always cancelled on error
- Exponential backoff added: 2s→4s→6s→8s (capped) between attempts;
  rapid retries on a flaky network accomplish nothing without a delay
- Default timeout lowered: 90s → 20s (generous for search/GET but avoids
  the 90s worst-case hang that amplified ERR_NETWORK_CHANGED exposure)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 13:44:12 -04:00
Scott Idem
e6db2b4d6a fix(idaa): add Clear Cache & Reload escape hatch to recovery meetings server error state
"Try Again" resets auto_retry_count but reuses the same localStorage state — if
ae_loc or ae_idaa_loc holds a stale account_id or api_secret_key, every retry
fails identically and the user is stuck in an infinite error loop.

New button clears ae_loc + ae_idaa_loc from localStorage and db_events.event
from IDB, then reloads via the sessionStorage-preserved UUID URL (same logic as
the IDAA layout's Clear Cache & Reload). Forces a fresh FQDN handshake and
re-derives correct auth state. Guidance text shown so users know to try it when
Try Again keeps failing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 12:30:53 -04:00
Scott Idem
cfc5d237c7 docs(todo): add launcher wallpaper drift hotplug follow-up
Track projector/display unplug-replug wallpaper reset behavior and evaluate loop/check/event-driven strategies.
2026-05-20 19:30:37 -04:00
Scott Idem
a56f520d4e feat(launcher): add quick mirror/extend toggle in left menu
Always visible in launcher menu, disabled outside native mode, with title text and preview-note messaging.
2026-05-20 19:28:46 -04:00
Scott Idem
c0f828ec2c feat: Add platform-specific VLC launch profiles (macOS working; Linux deferred)
- macOS: Uses direct binary path (/Applications/VLC.app/Contents/MacOS/VLC)
  with --no-play-and-exit --play-and-pause flags and AppleScript fullscreen toggle.
  Confirmed working: pause on last frame, stays in window.

- Linux: Uses shell command (vlc --no-play-and-exit --play-and-pause).
  Known issue: not going fullscreen, not pausing on end. Deferred for later
  investigation of flag interpretation vs launcher execution layer.

- Created separate profile constants: VLC_MIRROR_MAC_PROFILE and
  VLC_MIRROR_LINUX_PROFILE in ae_launcher__default_launch_profiles.ts

- Updated TODO__Agents.md with Linux VLC issue for future debugging

Files: ae_launcher__default_launch_profiles.ts, documentation/TODO__Agents.md
2026-05-20 19:09:15 -04:00
Scott Idem
5bb06c4904 feat(display): expose list_display_modes and set_display_mode in relay
Svelte callers can now enumerate all display modes and set resolution/
refresh/HiDPI per display through the native bridge.
2026-05-20 18:16:43 -04:00
Scott Idem
7621e044b4 docs(launcher): update display layout doc — display_control replaces displayplacer as primary
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:48:25 -04:00
Scott Idem
76569a872f feat(launcher): add display mode toggle; fix silent display layout failures
- Add Extend/Mirror toggle to Native OS config section (always visible,
  no Technical Mode required). Default: Extend. State updates on success.
- Replace .catch(() => {}) swallowing with console.warn logging on both
  set_display_layout call sites so failures appear in the Electron console
- Remove old edit-mode-only Extend button (replaced by new toggle)
- Update PROJECT doc: displayplacer install note, binary lookup order, GitHub link
- Clean up TODO__Agents.md: resolve stale items, add new low-priority Electron items

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:33:33 -04:00
Scott Idem
d9726d062e docs(launcher): sync docs to current code state
set_wallpaper: update signature in PROJECT doc (§5.3 and §7) — actual params
are {path?, url?, url_external?, display?, api_key?, account_id?}, not just {path}.

set_display_layout: clarify in PROJECT doc (§5.3 and §7) — now auto-detects
displays via displayplacer list; configStr is optional manual override, not required.

MODULE profiles table: correct post_delay_ms values (Mac 2000→1000ms,
Win 3000→1500ms) and add polling-loop note explaining the new timing behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:39:16 -04:00
Scott Idem
91f40c4a89 fix(launcher): border-2 on wallpaper chips/buttons for VNC readability
ring-1 uses CSS box-shadow which nearly disappears when the UI is scaled
down through VNC. Switched all preset chips and action buttons to border-2
so they remain clearly distinct at reduced resolution.
preset-tonal-surface + border-2 border-surface-400 for unselected chips;
disabled:opacity-60 (vs default 50) on Save & Apply so it is still
identifiable as a button when no URL is entered yet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:22:22 -04:00
Scott Idem
e63a17865c fix(launcher): replace fixed post_delay with frontmost poll across all launch profiles
`open -a App` returns immediately; a fixed delay is unreliable for cold starts.
Each post_script now polls every 500 ms (up to 7.5 s) until the target process is
frontmost before firing the automation keystroke. Keynote polls document count
instead of frontmost since start(front document) requires the file to be loaded.
Mac profiles: post_delay_ms 2000 → 1000. Win/Parallels profiles: 3000 → 1500.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:16:01 -04:00
Scott Idem
a59e53aec5 fix(launcher): larger, ring-bordered preset chips for VNC readability; fix lint errors
Bumps chip buttons h-6→h-8 with text-xs and ring-1 borders so active/inactive
states are clearly distinct at VNC/remote scale. Save & Apply bumped to h-10
text-sm. Fixes: /50 opacity modifier in class: directives (uses class expression
instead), stale svelte-ignore comments replaced with onkeydown handlers, each
block key added. Documents wallpaper repeat-apply macOS caching bug in TODO with
workaround and fix location (Electron temp filename).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 14:29:34 -04:00
Scott Idem
6042095147 feat(launcher): preset chips + simplified buttons for wallpaper config
Replaces free-text-only inputs with quick-select preset chips (1 primary,
4 client external/projector) that populate the URL field on click.
Consolidates Save/Apply/Save&Apply/Restore into a single Save & Apply
primary button plus an icon-only Restore button. All status messages
merged into one shared state variable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 14:22:01 -04:00
Scott Idem
932deced12 docs: mark IDAA server-side Novi verification resolved in TODO__Agents.md
All Access Denied root causes fixed and deployed 2026-05-19. 503 auto-retry
regression also documented and fixed same session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 19:47:46 -04:00
Scott Idem
861385b4ff docs(idaa): update Novi verification docs to reflect server-side proxy (complete)
CLIENT__IDAA_and_customized_mods.md:
- Verification Flow: describe Aether proxy call, not direct browser-to-Novi fetch
- Replace old fetch() code snippet with new Aether endpoint call
- Update novi_idaa_api_key / novi_api_root_url field descriptions (server-side only now)
- Security notes: key never sent to browser; shape changes go in backend method
- Rate limit note: 12h TTL (was 5-min), add 503 auto-retry behavior
- Fix Redis cache key: idaa:novi_member:{uuid} (account_id was dropped from key)

GUIDE__AE_API_V3_for_Frontend.md §12:
- 503 frontend action: auto-retry once after 3s before api_error
- Mark migration section complete (2026-05-19); update table to show retry behavior

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 19:46:51 -04:00
Scott Idem
42f40e990e fix(idaa): auto-retry 503 from Aether Novi proxy (mirrors network-error path)
Before: a 503 response from /v3/action/idaa/novi_member/{uuid} hit !response.ok
immediately, set verify_failed_for_uuid, and showed "Verification Unavailable" —
no automatic retry. In the old client-to-Novi flow an unreachable server threw
TypeError (auto-retried); the new server-side path returns a clean HTTP 503
(plain Error), bypassing the is_network_or_timeout branch.

After: 503 gets the same 3s wait + one retry that network/timeout errors get.
Only if the retry also 503s does it fall through to the error UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 19:41:01 -04:00
Scott Idem
3ea362c166 fix(idaa): restore site_cfg guard to prevent API call on non-IDAA domains
The server-side migration removed the old novi_idaa_api_key check, which was
also acting as an implicit 'is IDAA configured here?' guard. Without it, any
domain that resolves (including ghost/domain-not-found with account_id='ghost')
would fire the Aether endpoint and get an error response, showing 'Verification
Unavailable' over the root layout's 'Domain Not Found' message.

Restore the site_cfg.novi_idaa_api_key presence check as the first guard:
- key absent → site_cfg_json still loading OR this is not an IDAA site → skip
- account_id='ghost' → domain lookup failed → added explicit ghost guard too

The key itself is unused for auth (server holds it); we only test its presence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 18:57:26 -04:00
Scott Idem
400312456b feat(idaa): replace client-side Novi call with server-side Aether proxy endpoint
verify_novi_uuid() now calls GET /v3/action/idaa/novi_member/{uuid} instead
of fetching Novi directly from the browser. The Aether backend handles the
Novi call server-to-server, eliminating false Access Denied failures caused
by hotel/conference WiFi, VPNs, and Cloudflare IP filtering.

Response parsing simplified — full_name and email are normalized server-side.
Empty-200 anti-pattern handling, email space→+ normalization, and display-name
formatting moved to the backend (confirmed working per API agent).

Retry logic and error classification unchanged (429→rate_limited, network
error→retry once, all others→api_error).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 18:49:18 -04:00
Scott Idem
6755a68b13 fix(idaa): add VPN/network hint, bump TTL to 12h, document server-side verify plan
- Classify persistent network/timeout failures as 'network_error' (separate from
  generic 'api_error') so the UI can show a targeted message
- Add actionable hint for members on hotel WiFi, VPN, or corporate networks:
  turn off VPN, switch to cellular, try a different network
- Extend VERIFIED_TTL_MS_DEFAULT from 45 min to 12 hours — covers a full workday
  so members at conferences do not need to re-verify mid-day
- Document planned server-side Novi verification FastAPI endpoint in
  CLIENT__IDAA_and_customized_mods.md (once implemented, eliminates client-side
  Cloudflare/IP-reputation exposure entirely)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 18:23:45 -04:00
Scott Idem
71e79f032d docs(idaa): mark Access Denied root cause investigation as resolved
All 10 fixes applied and verified as of 2026-05-19. Collapsed the three
open issues into the completed checklist with commit references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 16:06:45 -04:00
Scott Idem
53fd5e7de4 fix(idaa): increase spinner timeout to 35s, guard sessionStorage with try-catch
VERIFY_TIMEOUT_MS 8s → 35s: worst-case auto-retry cycle is 27s (12s abort +
3s wait + 12s abort). At 8s the "Reset & Retry" banner fired while the second
retry was still in flight; members who clicked it cleared their stores and
reloaded mid-attempt, landing on Access Denied. At 35s the escape hatch only
appears if verification is genuinely stuck (slow Novi server or missing api_key).

sessionStorage try-catch: iOS Safari Private Browsing and certain iframe sandbox
configs throw on sessionStorage access. Wrap setItem (onMount) and getItem
(reload_with_uuid) in try-catch so the component degrades gracefully to
location.reload() rather than crashing silently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 16:04:28 -04:00
Scott Idem
14e84884cd refactor(idaa): rename reload_to_origin → reload_with_uuid (clearer intent)
'origin' is a reserved web term (protocol+domain); the function restores the
initial Novi-provided iframe URL (which includes the ?uuid= param), not the
site root. Renamed to reload_with_uuid to make that clear.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 15:53:18 -04:00
Scott Idem
e921ca973f fix(idaa): add Novi fetch timeout and auto-retry on network errors
- Wrap Novi API fetch in AbortController with 12s hard timeout — prevents
  verify_in_flight from getting stuck if Novi's server hangs with no response
- Auto-retry once (after 3s) on network errors (TypeError: Failed to fetch)
  and timeouts (AbortError) — these are almost always transient cellular/WiFi
  blips and previously hard-failed with no second chance
- Rate-limit retries (429) already had a 10s wait; network retry is separate
- Update status message to "Connection issue — retrying..." during network retry
- Update error panel hint to suggest closing/reopening the Novi page as last resort
- Update Access Denied hint with same guidance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 15:29:14 -04:00
Scott Idem
2855e091f7 fix(idaa): fix Access Denied on reload in iframe and extend Novi TTL to 25 min
- Add reload_to_origin(): saves initial iframe URL (with ?uuid=) to sessionStorage
  on mount; all reload buttons use it instead of bare location.reload() so the UUID
  is preserved after internal SvelteKit navigation strips it from the URL
- Fix TTL short-circuit to also check $ae_loc permissions — without this, a store
  reset (browser restart, stale localStorage) while the TTL was still valid would
  skip re-verification and fall straight to Access Denied
- Extend Novi verification TTL from 5 to 25 minutes
- Add Clear Cache & Reload option to the Access Denied state (iframe mode)
- Move Novi UUID debug info on Access Denied page to edit_mode only; UUID line
  at bottom of auth'd pages stays always visible for troubleshooting
- Remove expired temporary tech-notice variables (template block was already commented out)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 15:22:20 -04:00
Scott Idem
f37c64c68b perf(app): remove unused Google Fonts requests, add pre-JS loading spinner
- app.html: comment out 3 Google Fonts <link> tags (Noto Sans, Noto Serif,
  Roboto Mono) — no theme or component applies these families; all themes use
  system-ui. Saves 3 blocking network requests on every page load.
- app.html: add subtle CSS-only #ae_loader spinner (1.75rem ring, pointer-events:none)
  that shows during JS download + root load function, before Svelte mounts.
- +layout.svelte: add onMount to remove #ae_loader as soon as Svelte bootstraps;
  the existing is_hydrating frosted-glass overlay takes over from there.
- app.css: comment out orphaned Quicksand @font-face — font-family was never
  applied to any element so browser never fetched it anyway.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 15:02:19 -04:00
Scott Idem
615af58a11 docs(idaa): update CLIENT doc for error handling and contact search status
- Access Gate: document new verify_error_type states (rate_limited/api_error),
  retry/reset UI buttons added in the previous session
- Search Architecture: correct 'contacts not searchable' — default_qry_str already
  includes contact data; two bugs fixed 2026-05-19 (stale STORED GENERATED columns,
  frontend secondary filter dropping API-matched results). IDB fast-path gap remains.
- TODO__Agents.md: update contact search task to reflect API path now working;
  narrow remaining work to IDB fast-path only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 15:02:09 -04:00
Scott Idem
76e21b08ff fix(idaa): remove over-filtering of API text search results in recovery meetings
The secondary client-side filter was re-checking qry_str against name, description,
location_text, and default_qry_str on the API response. This silently dropped meetings
that the API correctly matched via default_qry_str (a backend-generated combined index
containing contact name/email) — because that field is not always present in the
response body.

The API's LIKE search on default_qry_str is already exact. The secondary filter should
only correct structured dimensions (type, physical/virtual OR-logic) where the backend
uses AND logic that the frontend must compensate for. Text search is left to the API.

Root cause confirmed: STORED GENERATED columns in the event table needed a rebuild;
frontend filtering was masking results that the API returned correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 12:49:52 -04:00
Scott Idem
4ada5c4a8f Lowered the timeout from 8 to 5 seconds 2026-05-19 11:33:24 -04:00
Scott Idem
8850db89c6 fix(layouts): guard appshell header/footer data stores behind account_id
element_data_store fires its load trigger as soon as api_ready is true,
with no check for account_id. In the IDAA iframe flow, the outer layout
mounts before Novi UUID verification completes, so the footer fetch fires
with no x-account-id header and gets a 403.

Wrap the IDAA outer layout footer in {#if $ae_loc.account_id} so it only
loads once the member's identity is established. Apply the same guard to
the events layout header and footer for consistency.

Journals was already safe (data stores are inside the trusted_access gate).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 11:17:24 -04:00
Scott Idem
ccacdc3f4b Add comments and less debug 2026-05-19 10:55:32 -04:00
Scott Idem
128944c7ab feat(idaa): improve error handling for Novi verification failures and network errors
Distinguish transient API failures (rate limits, server errors, network drops) from
real membership denials, so members see actionable messages instead of 'Access Denied.'

Layout: new verify_error_type state ('rate_limited' | 'api_error') surfaces a
yellow 'Identity Verification Unavailable' banner with three recovery options --
Try Again (no reload, clears latch), Clear Cache and Reload, and Full Reset.
Spinner now shows live status messages (e.g. 'High traffic - retrying in 10 seconds...').

Recovery meetings page: qry_error_detail distinguishes network drops (TypeError /
ERR_NETWORK_CHANGED) from server errors, showing specific guidance in the error UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 10:46:28 -04:00
Scott Idem
c0386f27bc fix(idaa): fix name A→Z / Z→A sort not applying in API revalidation path
The RE-SORT block after API revalidation only checked for 'name' (a legacy
sort mode removed when sort_modes was introduced). 'name_asc' and 'name_desc'
fell through to the else branch and were silently re-sorted chronologically
by tmp_sort_1, overriding the user's selection. Updated to match the fast-path
IDB sort logic which already handled all three modes correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 09:49:40 -04:00
Scott Idem
ee506832e7 fix(idaa): make novi_btn buttons visible in Bootstrap iframe context
Bootstrap v3 (.btn) sets border:1px solid transparent which overrides
Skeleton/Tailwind preset-outlined border classes when loaded last in the
Novi iframe. Replacing the dead border-color comments with a box-shadow
ring — Bootstrap does not reset box-shadow on .btn so it survives without
!important. Adds a darker ring + faint bg tint on hover.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:41:05 -04:00
Scott Idem
bbab9e7c8c feat(idaa): update default recovery meeting limit to 100 and add 75 to stepper
- Update default qry__limit to 100 in idaa_loc
- Add 75 to limit_steps in recovery meetings query component
- Bump AE_IDAA_LOC_VERSION to 2 to apply changes to existing users
- Update IDAA documentation and TODO__Agents.md with SQL optimization task
- Mark implemented UI/UX ideas as done in documentation
2026-05-18 21:25:09 -04:00
Scott Idem
daf1570781 Updating docs 2026-05-18 18:51:53 -04:00
Scott Idem
c69e40829f feat(idaa): collapsible Meeting Info panel on recovery meetings list
Wrap the data store element in an accordion-style toggle. State persists
in idaa_loc (localStorage) so the user's preference survives page reloads.
Added ds_info_collapsed field to idaa_local_data_struct.recovery_meetings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:59:02 -04:00
Scott Idem
429f38996a refactor(idaa): filter bar polish — cycling sort, centered layout, move into form
- Replace three sort chips with single cycling button (Last Updated → Name A→Z → Name Z→A → repeat)
- Add min-w-36 to sort button to prevent layout bounce between labels
- Move My Meetings chip to filter row (first position) alongside Virtual/In-Person
- Widen filter chips row from max-w-xl to max-w-2xl to match search bar
- Move sort/max/actions section inside <form> so all rows share the same width constraint
- Flatten span wrappers in bottom row — all buttons are direct flex children of justify-center container, fixing left-drift when conditional buttons are hidden
- Add keyed {#each (val)} to Type chip loop

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:50:43 -04:00
Scott Idem
6857f1226c refactor(idaa): compact filter bar — sr-only labels, +/- stepper for max results
- Group labels (Location, Type, Sort, Max) moved to sr-only — visually hidden,
  accessible to screen readers. Chips are self-descriptive without them.
- Max results chips replaced with a [−] 150 [+] stepper that steps through
  predefined values [25, 50, 100, 150] (+ 200/500 for trusted_access). Minus/plus
  buttons disable at the ends of the list. limit_steps and limit_idx computed as
  $derived in script so onclick closures have access without @const gymnastics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:18:59 -04:00
Scott Idem
bb84117991 feat(idaa): replace Max Results and Sort By selects with pill chips
- Max: 25 / 50 / 100 / 150 chips for all users; 200 / 500 visible to trusted_access
  staff only (consistent with previous select behavior).
- Sort: Last Updated / Name A-Z / Name Z-A chips; clicking triggers the same
  qry__order_by_li update and search_version bump as the old select onchange.
- Sort logic extracted into set_sort_mode() helper to keep onclick clean.
- Active state: preset-filled-tertiary-400-600; inactive: outlined + opacity-60.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:11:46 -04:00
Scott Idem
7a1099bbbe feat(idaa): replace filter checkboxes/radios with toggle pill chips + update docs
- ae_idaa_comp__event_obj_qry.svelte: replace Location checkboxes and Type radio
  inputs with styled pill-chip buttons. Location chips (Virtual / In-Person) are
  independent toggles; Type chips (All / IDAA / Caduceus / Family Recovery) are
  mutually exclusive — clicking the active chip deselects back to All. Chips fire
  the reactive search $effect directly via store updates; no explicit trigger needed.
  Remote First dev toggle preserved in edit mode, now inline with filter chips.
- CLIENT__IDAA_and_customized_mods.md: update Recovery Meetings filter/sort docs,
  add My Meetings / favorites section, correct idaa_loc and idaa_sess store schemas,
  bump Last Verified date.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:08:01 -04:00
Scott Idem
3a81887c56 feat(idaa): add guided empty state for filtered results + star button on meeting detail
- +page.svelte: when search returns zero results and filters are active, show
  "No meetings found for these filters" with a one-click "Clear all filters" button
  instead of the bare no-results message. The 8s cache-reset escape hatch is
  unchanged and still fires only when zero results appear with no filters set.
- [event_id]/+page.svelte: add star/favorites button to the detail page nav bar
  alongside Back/Edit. Loads the same idaa_meetings_favorites data_store record
  on mount; PATCHes the shared record on toggle. State is optimistic with rollback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 17:00:51 -04:00
Scott Idem
730fb19d60 refactor(idaa): migrate favorites storage from mod_meetings_json to data_store
Favorites are now stored in a dedicated data_store record (code:
idaa_meetings_favorites, scoped to the IDAA account_id) so toggling
never touches ae_event rows or their updated_on timestamps. Structure:
{ [novi_uuid]: [event_id, ...] }. Pre-created DB records for dev (id 150)
and live IDAA (id 151) accounts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:38:05 -04:00
Scott Idem
b32fb05138 chore(idaa): note updated_on side effect on favorites toggle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:16:22 -04:00
Scott Idem
12429ccf2e feat(idaa): add My Meetings favorites for recovery meetings
Store Novi UUID list in event.mod_meetings_json.favorite so favorites
persist cross-browser without requiring Novi API write access. Optimistic
IDB update with API rollback on failure. Star button uses inline styles
to override Bootstrap v3 iframe CSS conflicts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:09:05 -04:00
Scott Idem
2d552b36fd Made the IDAA ideas more staff friendly.
The IDAA Posts now show the person's name even if anonymous.
2026-05-18 15:01:52 -04:00
Scott Idem
3ed1a2a6c4 Documentation updates with IDAA ideas. 2026-05-18 08:56:53 -04:00
Scott Idem
ab9e54d768 fix(idaa): resolve ~1-year 'no meetings found' bug on recovery meetings page
Root cause: stale IDB records from prior deploys persisted indefinitely.
Fast path returned 0 (account_id mismatch), API errored silently, and the
error state showed the same message as a genuinely empty result — making
the failure indistinguishable from real data.

Fix is layered defense:
- Bump IDB_CONTENT_VERSIONS.events.event to 2 (one-time force-clear for all users)
- Add check_and_clear_idb_table() helper to store_versions.ts; wire it in
  (idaa)/+layout.svelte to catch future version mismatches on session start
- One silent auto-retry (3s) on API failure before surfacing error UI
- Distinct error state (Unable to load meetings) separate from empty state
- Escape-hatch cache-reset button after 8s when zero results + no active filters
- Document root cause and fix in README.md and BOOTSTRAP__AI_Agent_Quickstart.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 22:52:29 -04:00
Scott Idem
5bb2df1bd9 Quick adjustments to badge layout. Hopefully this does not break the other Axonius layout that uses the full background image. 2026-05-15 15:59:08 -04:00
Scott Idem
50c484a4cc fix(badges): fix 4x6 fanfold header image display and body gap
Two layout fixes for the badge_4x6_fanfold layout (no background_image_path):

1. badge_header max-height: 1.5in — the Axonius logo (624×232px) renders at
   ~1.49in tall at full badge width. The inherited max-h-[1.00in] was cropping
   the bottom half of the image.

2. badge_body margin-top: 0 — overrides the component-level mt-54 (≈2.25in).
   That margin was needed for the PVC layout where a full-bleed background image
   covered the badge and body text needed to start in the image's designated zone.
   For fanfold badges with a standalone header_path, mt-54 created a 2.25in blank
   gap between the header and the attendee name.

Also updates default fit_heights for badge_4x6_fanfold to match the 4.0in body
height (was sized for 4.5in before the header zone was properly accounted for).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 15:48:37 -04:00
Scott Idem
26fde2a566 feat(badges): add 4×6 fanfold layout CSS for Epson single-sided stock
Introduces badge_layout_epson_4x6_fanfold.css (layout code badge_4x6_fanfold)
for the Axonius Adapt 2026 June show. Wires @page size to 4in×6in in the print
page and cleans up the stale 4in×12in default. Imports new CSS in badge component.
Measured stock: 4in × 6in portrait with 5/8in lanyard hole 1/4in from top.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 15:26:13 -04:00
Scott Idem
21a44f96fa Cleaning up the styles for the Session related content. 2026-05-15 14:44:57 -04:00
Scott Idem
631a77158c feat(pres_mgmt): replace time_hours/time_format/datetime_format with single use_12h toggle
Three redundant store fields encoding the same AM/PM choice replaced with a single
`use_12h: boolean` in PresMgmtLocState. iso_datetime_formatter gains a third param
(use_12h: boolean | null = null) that auto-resolves 24h↔12h format name variants via
a symmetric FORMAT_PAIRS lookup — null default leaves all ~100 existing call sites intact.

Toggle surfaces in three places: Clock icon in session time chip (hidden button, same
visual), event Options modal Display section, and session Options modal Display section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 14:29:57 -04:00
Scott Idem
1296b1077e Style fixes 2026-05-15 13:56:23 -04:00
Scott Idem
1fe2f6f4d2 fix: correct idaa_trig usage in recovery_meetings Zoom URL editor
$idaa_trig is a key_val object (dict of boolean flags), not a string signal.
Adds update_zoom_full_url key to the store template and converts all 7
string comparisons/assignments to property access on the object.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 13:13:00 -04:00
Scott Idem
73f97ee17b General code cleanup. Mainly updating Lucide icons. 2026-05-15 13:04:50 -04:00
Scott Idem
ad6b390fd9 fix: pres_mgmt session search now includes presenter and presentation names
Previously, searching by presenter name in pres_mgmt returned no results
because event_presenter_li_qry_str / event_presentation_li_qry_str were
never requested or stored.

Changes:
- ae_types.ts: add event_presentation_li_qry_str + event_presenter_li_qry_str
  to ae_EventSession interface
- db_events.ts: same two fields added to Session Dexie interface
- ae_events__event_session.ts:
  * add ft_presentation_search_qry_str param
  * auto-upgrade view to 'alt' when either ft_presenter or ft_presentation
    search is used (backend requires v_event_session_w_file_count for these)
  * add both fields to properties_to_save so they persist to Dexie cache
- +page.svelte (pres_mgmt):
  * pass ft_presenter_search_qry_str + ft_presentation_search_qry_str in API call
  * local IDB fast path now checks both new fields
  * client-side filter guard also checks both new fields
2026-05-15 12:39:37 -04:00
Scott Idem
f297c7c018 fix: account_name not showing on events page — stale Dexie cache + duplicate assignment
Two Svelte-side bugs causing account_name to always show 'Account Name Not Set':

1. ae_core__site.ts: background site_domain refresh only pushed cfg_json back
   into $ae_loc after a stale cache hit. Now also pushes account_name and
   account_code when the store holds a placeholder value.

2. +layout.ts: duplicate ae_loc_init['account_name'] assignment at line ~475
   was overwriting the correct one at line ~385 with a different fallback string
   ('Account Name Not Set' vs 'Ghost Account'). Removed the duplicate.

Also includes user-intentional changes during testing:
- events/+page.svelte: typo fix ('You access' -> 'Your access'); Pres Mgmt /
  Launcher / Badges / Leads buttons now gated on trusted_access && edit_mode
- events/+page.ts: event list limit 25 -> 7
- events/[event_id]/+page.svelte: user edit
2026-05-15 11:46:10 -04:00
Scott Idem
48a748d314 refactor(storage): replace hardcoded IDB lists with indexedDB.databases() in all clear buttons
Consistent with the tech help panel update — all Clear Storage / Clear & Reload
buttons now enumerate IDB databases dynamically so new modules are included
automatically without needing to update these lists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 18:23:29 -04:00
Scott Idem
c4e2e64a7e Style work 2026-05-14 17:55:05 -04:00
Scott Idem
76b4adef74 feat(help): improve Clear & Reload + add Full Reset button in tech help panel
Reworks Clear & Reload to use indexedDB.databases() (dynamic enumeration)
instead of a hardcoded DB list. Edit mode clears localStorage/sessionStorage
while preserving ae_loc (sign-in + permissions). Normal mode clears IDB only.

Adds new Full Reset button — wipes all IDB, localStorage, and sessionStorage
for the origin with no preservation. Useful for diagnosing quota/storage issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 17:44:23 -04:00
Scott Idem
95a86b16fa feat(idb): add IDB content version check system, wire to journals
Adds check_and_clear_idb_tables() helper in core__idb_dexie.ts that clears
Dexie tables when their content version changes. Version numbers live in
IDB_CONTENT_VERSIONS (store_versions.ts); state tracked in localStorage
(ae_idb_ver__{module}__{table}) so each table clears exactly once per bump.

Wired into db_journals.ts (pilot): journal_entry at v3 clears stale
content_md_html/history_md_html data cached before the properties_to_save fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 16:53:55 -04:00
Scott Idem
042265008c fix(journals): remove description_md_html from IDB properties_to_save
Mirrors the content_md_html/history_md_html fix on journal_entry.
description_md_html is computed from description via marked.parse() on
every background refresh and does not need to be persisted to IDB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 16:19:25 -04:00
Scott Idem
a5f2ae3835 fix(journals): decouple IDB cache writes from API data return
QuotaExceededError on db_save was propagating through .then() into the
outer .catch(), which returned undefined and discarded the successfully-
fetched API data. Wrapped all db_save calls in try/catch so IDB failures
log a warning but never kill the load/create/update return value.

Also removed content_md_html and history_md_html from properties_to_save
— these are computed from content/history on every background refresh via
marked.parse(), so storing the rendered HTML alongside the source was
doubling storage cost per entry and the primary cause of quota exhaustion.

Affects: load, list, create, update, and qry functions in both journal
and journal_entry modules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 12:43:18 -04:00
Scott Idem
b3ce65f7f6 docs: update TODO — mark error handling, PWA SW, launch_profiles editor as done; annotate slide control + kill_processes status
- Error handling + fallback: confirmed done (launcher_file_cont.svelte)
- PWA service worker chrome-extension guard: confirmed done (service-worker.js)
- Launcher config UI / launch_profiles editor: confirmed done (launcher_cfg_launch_timing.svelte)
- Slide control scripts: annotated partial state — Svelte buttons wired, Electron scripts still hardcoded, deferred post June 10
- kill_processes: documented as not started on Svelte side; noted Device tab length concern

Also bundle two prior-session Launcher fixes:
- VLC post_delay_ms: 2000 → 1500ms
- launcher_cfg_launch_timing: add min-w-22 to built-in/current delay display
2026-05-14 12:24:36 -04:00
Scott Idem
054775b0f8 feat(launcher): skip wallpaper gsettings on Linux, show dev popup instead
Running gsettings on the dev workstation resets monitors on every test cycle.
Linux Electron handler now returns linux_test_mode:true with would_run details
instead of applying. Svelte cfg component shows a debug popup (mirrors Native
Test Mode style). Background sync logs to console and leaves applied-URL unset.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:54:56 -04:00
Scott Idem
af28fba263 feat(launcher): restore macOS default wallpaper + external-only apply fix
- electron_relay: add restore_macos_default_wallpaper() — uses run_osascript
  to find first .heic in /System/Library/Desktop Pictures/ (version-agnostic)
- wallpaper cfg: Restore macOS Default button (native or edit_mode); clears
  applied-URL tracking so next configured URL re-applies correctly
- wallpaper cfg: fix Apply Now / Save & Apply enabled when only external URL
  is filled; display target becomes 'external' to leave built-in unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:37:15 -04:00
Scott Idem
17e522f826 feat(launcher): wallpaper auto-apply from device config
Stores wallpaper URL(s) in event_device.other_json.launcher.wallpaper and
auto-applies on all native Launcher instances within one heartbeat cycle (~60s),
eliminating manual per-Mac setup at events.

- electron_relay: typed set_wallpaper wrapper (url, url_external, display, auth headers)
- launcher_defaults: wallpaper_applied_url tracking + section_state__wallpaper
- launcher_cfg_wallpaper: new config section — save to device config + apply now
- launcher_cfg: add wallpaper section to device tab
- launcher_background_sync: auto-apply if config URL changed since last apply;
  external-only config targets only the secondary display, leaving built-in unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:31:39 -04:00
Scott Idem
324f3a97ac Doc update 2026-05-13 17:47:49 -04:00
Scott Idem
530853a78d Version bump because I think things are in a pretty good state. 2026-05-13 17:29:59 -04:00
Scott Idem
453fcf581d Remove dead recovery meetings search prefill 2026-05-13 17:24:08 -04:00
Scott Idem
530b53aa6d Fix IDAA recovery meetings auto search 2026-05-13 17:00:36 -04:00
Scott Idem
cc990084fb Updated version number... 2026-05-13 16:06:40 -04:00
Scott Idem
5fdb0d1d87 Add service worker self heal on version mismatch 2026-05-13 15:46:26 -04:00
Scott Idem
44cc538ce0 Disable service worker on localhost 2026-05-13 15:30:08 -04:00
Scott Idem
978a9a6960 Clarify service worker cross-origin guard 2026-05-13 15:13:27 -04:00
Scott Idem
82430649db Fix iframe reload and service worker caching 2026-05-13 15:08:17 -04:00
Scott Idem
cdcec259f7 fix(launcher): bypass downloads for url files 2026-05-13 13:59:43 -04:00
Scott Idem
e5883cd53c fix(launcher): skip url files in sync 2026-05-13 13:36:57 -04:00
Scott Idem
8d5c5e39c9 fix(launcher): guard health device metadata 2026-05-13 12:50:25 -04:00
Scott Idem
39749c608a fix(launcher): use per-profile timing overrides 2026-05-13 12:48:43 -04:00
Scott Idem
4923099cfb feat(launcher): add device launch timing override 2026-05-13 12:34:36 -04:00
Scott Idem
36bd32f172 docs(launcher): document post delay override 2026-05-13 12:22:11 -04:00
Scott Idem
1374f0728e refactor(launcher): canonicalize default profiles 2026-05-13 12:15:13 -04:00
Scott Idem
c79ae92be0 docs(launcher): align launch profile terminology 2026-05-13 11:50:13 -04:00
Scott Idem
49c6a2351e refactor(launcher): use launch_profiles only\n\nRemove the temporary launch_scripts compatibility alias and keep the launcher
configuration surface focused on launch_profiles everywhere in the Svelte app and
docs.
2026-05-13 10:30:10 -04:00
Scott Idem
b697126495 refactor(launcher): prefer launch_profiles naming\n\nRename the public launcher override concept to launch_profiles across the task list\nand docs, while keeping launch_scripts as a compatibility alias for older device
records. Update the Svelte resolver to read both keys so per-device tweaks remain
backward compatible during the transition.
2026-05-13 10:26:01 -04:00
Scott Idem
8dd22912c3 fix(launcher): keep native open state off download spinner\n\nAdd an opt-out on the shared hosted-file button so promise-returning native opens\ncan use Launcher status text without being treated like active downloads. Apply it\nto the Electron launcher open path so cache hits no longer show 'Downloading...'\nwhen the row status already reports the correct cache/open state. 2026-05-12 13:49:55 -04:00
Scott Idem
f8fe4ac5a2 fix(launcher): button state hygiene across all 3 modes
Fix 1 (stale error_detail):
  open_file_error_detail is now cleared at the start of every mode's
  branch (native, onsite, default) alongside open_file_clicked=true.
  Previously it was cleared mid-way through the native flow (Step 1),
  so a stale error from a previous failed run could flash briefly.

Fix 2 (stuck 'Opening...' during post_script sleep):
  After the Step 4 open call returns (run_cmd or open_local_file_v2),
  update the status message immediately to 'App opened — running setup...'
  so the button doesn't appear frozen for the full post_delay_ms wait.

Fix 3 (safety valve for hanging native calls):
  A 60s setTimeout is registered at the start of the native branch.
  If any native IPC call hangs indefinitely and the existing per-path
  reset timeouts never fire, this force-resets open_file_clicked after
  60s. All normal paths complete within ~8s so this only triggers on
  true hangs. ERR_NETWORK_CHANGED cannot re-enter the download path
  because the open_file_clicked guard blocks re-entry.
2026-05-12 13:37:11 -04:00
Scott Idem
2c1e9d294e fix(launcher): swallow orphaned IPC reply on open_local_file_v2
shell.openPath always resolves on the Electron side, but if the Svelte
renderer navigates before the IPC reply arrives, the promise rejects
with 'reply was never sent'. The file is already open at that point.

Catch the rejection and treat it as success on both call sites (Step 4
primary path and Step 7 fallback). This eliminates the unhandled
promise error without masking real failures.
2026-05-12 13:18:53 -04:00
Scott Idem
768fdbfb21 feat(launcher): URL-type event_file support + displayplacer Electron task
URL files: event_file.filename = 'https://...' is now a first-class
file type in the launcher.

- is_url derived rune detects https/http filename prefix
- URL branch in handle_open_file() runs before cache/native branches
  (no download, no temp copy, no hash)
- Offline guard: warns and blocks click if navigator.onLine is false;
  online/offline listeners registered only for URL rows (no-op on files)
- Native mode: opens via native.open_external({ url, app: 'chrome' })
  with silent fallback to default browser
- Browser mode: window.open() with noopener
- display_mode default: 'mirror' (URLs typically just need the screen
  mirrored, not extended presenter view)
- Button badge shows Link2 icon + WifiOff warning when offline
- Button text uses event_file.title as label (falls back to URL)
- Test mode popup: skips Steps 1-2 (N/A), shows open_external call

DEFAULT_LAUNCH_PROFILES: add 'url' key (display_mode: 'mirror')

Electron TODO: added set_display_layout / displayplacer per-device
config task to aether_app_native_electron/documentation/TODO_AGENTS.md
with full contract details and resources
2026-05-12 13:14:58 -04:00
Scott Idem
e74dc7a388 fix(launcher): skip post_delay sleep when no post_script
The sleep step was running unconditionally, meaning files that open
with the 'default' catch-all profile (zip, unknown ext, etc.) would
wait 2 seconds for no reason — there's no AppleScript to prepare for.

Gate the sleep inside the post_script check so it only delays when
there's actually automation to run after app launch.

Also update the test mode popup to show '0ms — skipped (no post_script)'
and display post_delay_ms as '(default: 2000ms)' when unset.
2026-05-12 12:49:50 -04:00
Scott Idem
a3f2f17480 Allow npx svelte check 2026-05-12 12:41:14 -04:00
Scott Idem
6c73812187 fix(launcher): improve test mode popup contrast for light/dark mode
- Warning text in cfg_app_modes: replace text-yellow-400/70 with
  preset-tonal-warning badge (theme-aware, readable in both modes).
- Popup dialog: bg-surface-900 → bg-surface-50/95 dark:bg-surface-900/95
  so the card adapts to light/dark instead of being locked dark.
- Code blocks: bg-black/30 → bg-surface-500/10 (neutral opacity).
- Header title: text-warning-400 → text-warning-600 dark:text-warning-400.
- Semantic text colors: all -300 variants → 500-level equivalents:
  text-green-300/400 → text-success-600 dark:text-success-400
  text-primary-300 → text-primary-500
  text-yellow-300/400 → text-warning-500
  text-purple-300 → text-purple-500
2026-05-12 12:40:39 -04:00
Scott Idem
ff824ebbe5 feat(launcher): add Native Test Mode for profile/command preview
Enables testing the LaunchProfile system from any device (no Mac/Electron
needed). When active, the Open button simulates the full native flow and
shows a debug popup with everything that WOULD be sent to Electron.

- ae_events_stores__launcher_defaults.ts: add native_test_mode boolean
  (persisted, default false) to LauncherLocState and launcher_loc_defaults.

- launcher_cfg_app_modes.svelte: add Native Test Mode checkbox toggle in
  the Advanced Toggles (Edit Mode Only) section with active-state warning.

- launcher_file_cont.svelte:
  - Add test_mode_popup_open/test_mode_popup_data state vars.
  - Add branch 0 in handle_open_file(): when native_test_mode + app_mode=native,
    skip all Electron calls; resolve the real LaunchProfile, build a data
    snapshot, open the debug popup.
  - Debug popup shows: file info, simulated temp path, cache/copy pass,
    resolved LaunchProfile fields, set_display_layout call, open command
    (run_cmd or open_local_file_v2 fallback), sleep delay, post-script
    (AppleScript or shell: prefix). Click backdrop or Close to dismiss.
2026-05-12 12:33:24 -04:00
Scott Idem
422c9c341c feat(launcher): implement LaunchProfile system — MasterKey replacement
- Add ae_launcher__default_launch_profiles.ts with LaunchProfile interface,
  DEFAULT_LAUNCH_PROFILES constant, and resolve_launch_profile() helper.
  Covers pptx/ppt/key/odp/pdf, all VLC media formats, Windows/Parallels
  variants (pptxwin/pptwin/odpwin/pdfwin), and a catch-all 'default'.

- Replace get_launch_script_template() with get_launch_profile() in
  launcher_file_cont.svelte. Override priority: device API config >
  local persistent config > built-in defaults > 'default' catch-all.

- Rewrite handle_open_file() native branch with 9-step profile-driven flow:
  copy_from_cache_to_temp → resolve profile → set_display_layout (silent fail)
  → open (run_cmd or OS default) → sleep(post_delay_ms) → run post_script
  → fallback to OS default on open failure → surface status/error detail.

- Add open_file_error_detail state var; show error pre block in status
  alert for native error state, show fallback note for fallback state.

- Add display override toggle button in event_file_meta (visible when
  trusted_access + is_native): cycles null → extend → mirror → null,
  PATCHes event_file.cfg_json.display_override via V3 CRUD.
2026-05-12 12:17:43 -04:00
Scott Idem
a3d229c803 docs(launcher): CMSC Charlotte task breakdown + doc date fix
- TODO__Agents: expand CMSC Charlotte launcher task into done/remaining
  sections; enumerate Svelte-side migration items (default templates,
  composable open flow, error fallback, slide control config, kill list
  config, Launcher config UI editor, end-to-end Mac test gate)
- PROJECT__AE_Events_Launcher_Native_integration: update Last Updated to 2026-05-11
2026-05-11 17:34:48 -04:00
Scott Idem
f72454f379 docs(launcher): sync native integration doc with current Electron implementation
- Bootstrap lifecycle now reflects actual two-step flow (event_device → site_domain/search).
  Removes stale /v3/data_store/code reference and corrects the JWT claim — auth is
  x-aether-api-key + x-account-id throughout; no JWT is issued or used.
- check_hash_file_cache corrected to check_cache (actual method name in preload bridge).
- cleanup_tmp_files removed from relay list — stale .tmp cleanup is handled inline inside
  download_to_cache, not via a standalone IPC channel.
- Known Issue section replaced: all AppleScript handlers are now hardened (launch_presentation
  converted to temp-.scpt approach in the companion Electron commit ca4fddd).
- Markdown lint fixes: blank lines around tables, table pipe spacing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 17:16:01 -04:00
Scott Idem
c5c5292715 feat(launcher): configurable launch scripts + composable native primitives
- electron_relay: type launch_from_cache with script_template param;
  add copy_from_cache_to_temp export; add JSDoc for run_osascript hardening
- launcher_file_cont: add get_launch_script_template() helper reading from
  device-level (event_device.data_json.launch_scripts) and event-level
  (events_loc.launcher.launch_scripts) config; wire into handle_open_file()
- PROJECT__AE_Events_Launcher_Native_integration.md: add Section 8
  (Configurable Launch Scripts); update IPC reference for new/changed handlers
- MODULE__AE_Events_PressMgmt_Launcher.md: add configurable launch behavior
  note to Native Mode Safe Handover section
2026-05-11 13:48:54 -04:00
Scott Idem
8ed7e0f8d7 Remove _random field handling from event_session and event_file modules
Drop the _random key copy loop from _process_generic_props in both files —
V3 API returns {obj_type}_id directly as the random string ID, so copying
from {obj_type}_id_random is a no-op. Simplify .id assignment to use
baseIdKey only. Remove commented-out _random entries from properties_to_save.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 12:31:27 -04:00
Scott Idem
68e5e01df1 A quick backup of the todo before removing things. 2026-05-11 12:26:51 -04:00
Scott Idem
611b1e6b51 Remove _random ID references and fix hosted file download ID
- Fix download button to use hosted_file_id instead of id (which resolved
  to event_file_id via _process_generic_props, hitting the wrong endpoint)
- Fix Dexie file table query in event_file_obj_tbl_wrapper to use _id
  fields (the indexed ones) instead of _id_random variants
- Remove _random fields from properties_to_save in event, event_session
- Drop _id_random fallbacks from launcher device ID resolution and
  background sync heartbeat
- Clean up dead comments and old FA anchor in post edit component
- Update TODO__Agents.md: BGH section removed, CMSC/Axonius shows added,
  download button fix marked complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 12:26:21 -04:00
Scott Idem
1ef9080cda Hide journal entry AI tools unless admin edit mode is active
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 17:24:10 -04:00
Scott Idem
66c0be65c4 Show AI tools only in edit mode; open existing summary without regenerating
- Hide AI tools panel when entry is not in edit mode
- Clicking AI button when a summary already exists opens it directly
  instead of triggering a new API call; Re-run still available in modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 17:22:16 -04:00
Scott Idem
bdba092de0 Polish journal entry save buttons and header controls
- Add hover titles to all save buttons
- Match warning color scheme across floating, inline, and header save buttons
- Fix floating save button visibility (Tailwind v4 hidden/md:inline-flex conflict)
- Hide floating save when no unsaved changes using {#if}
- Hide Config button when not in admin edit mode
- Remove the mobile/backup explicit Save button from header (redundant)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 17:15:52 -04:00
Scott Idem
0fa93d7ee5 Fix auto-save stopping after the first save in journal entry editor
The auto-save $effect wrapped has_unsaved_changes in untrack(), which meant it
was never tracked when save_status was 'saved' (JS short-circuit in the else-if
branch). After every successful save the effect lost its reactive dependency on
user edits and never re-fired until something else changed save_status first.

Fix: track tmp_entry_obj.content and tmp_entry_obj.name directly (void reads)
so the effect re-runs on every keystroke and the debounce timer resets correctly
(fires 2 s after the last change, not the first). has_unsaved_changes is also
tracked directly so the status indicator resets cleanly when changes are cleared.
All side-effects remain in untrack() to prevent reactive loops.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 16:21:22 -04:00
Scott Idem
847d653b5e Truncate journal and entry names in browser tab titles
Entry: 50 chars for entry name, 30 for journal name
Journal: 60 chars for journal name
Appends ellipsis (…) when truncated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 16:08:41 -04:00
Scott Idem
cd01a87143 Fix browser tab titles for journal and journal entry pages
Journal: [Journal Name] - OSIT's AE Journals
Entry:   [Entry Name] - [Journal Name] - OSIT's AE Journals

Entry page title was previously commented out; now active with correct format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 16:00:31 -04:00
Scott Idem
60ecd221b4 Add tab indentation and line number toggle to CodeMirror editor
- Wire indentWithTab into keymap (Tab=indent, Shift-Tab=dedent, 4 spaces)
- Set indentUnit to 4 spaces
- Wrap lineNumbers() in a Compartment for live toggle without editor rebuild
- Add Hash toolbar button to toggle line numbers; respects show_line_numbers prop as initial value

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 15:55:42 -04:00
Scott Idem
e3a3ab7de8 Fix audio player controls not rendering in Chromium
Chromium's native audio player shadow DOM collapses when max-height is
applied to the <audio> element — audio plays but controls are invisible.
Remove the inline style (carried over from video context) and use w-full
max-w-lg instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 19:19:30 -04:00
Scott Idem
3553809f27 Add code field to archive content edit form and IDB
- Expose archive_content.code in edit form (trusted + edit_mode only)
- Add code to properties_to_save so it persists on every API load/save
- Add code field + index to Archive_Content Dexie interface (schema v2)
- Minor: center "Add" button rows in archive and content list components

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 18:42:15 -04:00
Scott Idem
6e700e7b4d Remove redundant saving status from IDAA archive edit forms
XHR upload % in the button + disabled states now communicate
upload/save progress; the top Saving.../Finished saving block
is no longer needed (and its out:fade was broken on re-entry).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 18:13:23 -04:00
Scott Idem
d7b4d8c37c Hide drop zone container during upload to fix empty border flash
The outer bordered div was always in the DOM; only the label and input
inside were hidden during upload, leaving a visible empty dashed box.
Apply the same hidden guard to the container div.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:53:14 -04:00
Scott Idem
d9f704fe25 Enable upload progress tracking in both file upload components
Pass track_progress: true to post_object so the XHR path fires live
percent_completed events, making the upload % display functional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:46:42 -04:00
Scott Idem
af74b52481 Add XHR upload path with real-time progress tracking to post_object
New track_progress param (default false) switches to XMLHttpRequest for
form_data uploads so xhr.upload.onprogress can fire percent_completed
postMessages into api_upload_kv. fetch() has no upload progress events.
No retry loop on XHR path — silently retrying a large video upload is
bad UX; caller re-submits on failure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:46:38 -04:00
Scott Idem
730eea4ce7 Limit archive content upload to single file; improve file section UX
Restrict upload to one file (each archive content item maps to one file);
contextual toggle button text (Switch to Select / Switch to Upload);
swap FontAwesome upload icon for Lucide.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:33:19 -04:00
Scott Idem
09f1a6ee57 Fix .ttf accept typo and remove $bindable from log_lvl in event file upload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:33:15 -04:00
Scott Idem
64c3afe564 Clean up hosted files upload component
Guard task_id effect against resetting mid-upload; add prevent_default
wrapper; add 20-min timeout for large video/audio files; add null result
guard before result[0]; fix for= attribute to use variable; console.error
on failure; remove dead params/comments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 17:33:12 -04:00
Scott Idem
54dfd734e6 Replace _random archive ID variants with V3 canonical field names
archive_obj.archive_id_random → .archive_id in load function and post-create
assignment; remove archive_id_random and hosted_file_id_random from editable
fields list — V3 returns the random string as the primary ID field directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 16:58:03 -04:00
Scott Idem
c7ebeebe29 Add dirty-tracking to Archive Content edit: disable Save, hide Cancel when unchanged
- ArchiveContentForm interface + factory for controlled input bindings
- obj_changed bindable prop wired to Cancel button visibility in parent page
- Split Save button: edit mode disables when clean, create mode always enabled
- Post-upload/select/remove syncs orig snapshot so file ops do not dirty the form
- Fix archive_content_id_random / archive_id_random → V3 field names in edit component
- Add missing file_extension field to ae_ArchiveContent type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 16:57:58 -04:00
Scott Idem
c71fc65be9 Fix archive content upload not patching record after file upload
Svelte 4 store nested property mutations don't call set()/update(), so
$effect on $idaa_slct never fired after upload. Replaced store property
binds with local $state variables that Svelte 5 proxies track natively.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 16:26:01 -04:00
Scott Idem
8b7597906f Tighten Jitsi report table padding
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 15:05:05 -04:00
Scott Idem
c289268550 Fix Jitsi report dark surfaces
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 14:53:41 -04:00
Scott Idem
09a5178b89 Add Jitsi reports staff link
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 14:44:00 -04:00
Scott Idem
e64252b839 Refine Jitsi participant copy
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 14:39:45 -04:00
Scott Idem
25e35f6f96 Add Jitsi participant copy actions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 14:29:27 -04:00
Scott Idem
74bc3b3625 Use 1000-row Jitsi pages
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 14:21:08 -04:00
Scott Idem
cd868460fe Fetch all Jitsi report rows
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 14:03:47 -04:00
Scott Idem
6ebf4f125d Better styling for toggle
Co-authored-by: Copilot <copilot@github.com>
2026-05-06 12:52:57 -04:00
Scott Idem
0ae8cf63d7 Improve Jitsi iframe toggle contrast
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 12:49:55 -04:00
Scott Idem
d32312653d Fix Jitsi report iframe title contrast
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 12:26:32 -04:00
Scott Idem
f5155eba50 New template page for IDAA and their Jitsi reports page. 2026-05-06 12:23:39 -04:00
Scott Idem
392217e66c Refine Jitsi report edit-mode controls 2026-05-06 12:10:41 -04:00
Scott Idem
7497bfb9f8 Tighten Jitsi report exclusions
Use Jitsi url_params.uuid for exclusion where available, preserve url_params in cached activity logs, and add the temporary staff-name fallback behind the same edit-mode toggle.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 11:47:43 -04:00
Scott Idem
3ae9d0a884 Refine IDAA Jitsi reports UX
Add Novi UUID exclusion and known-meeting filtering, default the report date range to the last 60 days, and hide Room Name unless global edit mode is enabled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 10:39:42 -04:00
Scott Idem
409308d2be Refine Jitsi docs and bootstrap notes
Keep the bootstrap quickstart focused on general platform knowledge, while preserving the Jitsi Reports reminder in the project docs and todo list.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-05 17:43:31 -04:00
Scott Idem
62cc26d1f9 Making things prettier:
npx prettier --write src/routes/journals/
2026-05-05 17:27:48 -04:00
Scott Idem
8b087edeb9 Add journal entry follow-up notes
Document the remaining Journal Entry Config follow-ups: toggle contrast, footer button styling, passcode auth behavior, AI summary shortcut, Archive On sizing, and Archive On behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-05 17:26:28 -04:00
Scott Idem
54707a00e3 Refine journal entry config
Polish the Journal Entry Config modal to match the desired section outline, hide alert messaging unless enabled, update the shared draft typing for entry flows, and replace deprecated privacy icons.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-05 17:14:20 -04:00
Scott Idem
e5c8500bc1 Advance journal config modal parity 2026-05-05 14:56:10 -04:00
Scott Idem
07dd213cfd Refine journal description editor layout 2026-05-05 14:36:01 -04:00
Scott Idem
1c20038a55 Align AI modal with journals config style 2026-05-05 14:12:55 -04:00
Scott Idem
d8616ea5fd Normalize Journals config tabs 2026-05-05 14:10:12 -04:00
Scott Idem
0b04ce7c0c Add Jitsi reports to IDAA 2026-05-05 14:02:52 -04:00
Scott Idem
146682a30b Modernize AI tools token input 2026-05-05 13:33:40 -04:00
Scott Idem
20d8a6975d Align journal docs with current model 2026-05-05 13:31:19 -04:00
Scott Idem
80957316f2 Normalize journal entry config actions 2026-05-05 12:59:30 -04:00
Scott Idem
0d0cec9819 Tighten AI config autofill handling 2026-05-05 10:35:35 -04:00
Scott Idem
0705fa8de4 Tweak the wrapping for small width. 2026-05-04 19:05:04 -04:00
Scott Idem
5846981c48 Refine journal entry AI tool layout 2026-05-04 19:00:57 -04:00
Scott Idem
3ca0f0bad9 Wire journal AI tools into entry view 2026-05-04 18:41:03 -04:00
Scott Idem
7486150aab Fix journal entry layout scrolling 2026-05-04 18:32:38 -04:00
Scott Idem
c3a346cc9a Add responsive journal sidebar 2026-05-04 17:42:13 -04:00
Scott Idem
7fd8c976bf Hide empty journal attachments 2026-05-04 17:35:36 -04:00
Scott Idem
9ed2d21757 Stabilize journal entry width 2026-05-04 17:25:08 -04:00
Scott Idem
38a752fbae Gate journal filters by access level 2026-05-04 16:59:33 -04:00
Scott Idem
285ef84b7e Refine journal search filtering 2026-05-04 16:58:48 -04:00
Scott Idem
5cbdec3b5c Reset BB dirty state after save 2026-05-01 18:53:28 -04:00
Scott Idem
8a23e7b7b3 Clean BB detail view wiring 2026-05-01 18:43:19 -04:00
Scott Idem
cc5a6887c0 Stabilize BB iframe width 2026-05-01 18:41:13 -04:00
Scott Idem
89c05cc323 Show Novi IDs in BB read views 2026-05-01 18:31:36 -04:00
Scott Idem
0631937e18 Dim locked Novi identity fields 2026-05-01 18:15:20 -04:00
Scott Idem
20bf1d94eb Improve IDAA BB post editing 2026-05-01 17:34:18 -04:00
Scott Idem
878ff91c30 feat(api): migrate send_email to v3 action endpoint 2026-05-01 15:53:05 -04:00
Scott Idem
7cef6be54c docs(core): mark data store fallback temporary and list special cases 2026-05-01 14:31:19 -04:00
Scott Idem
19822c4eaf docs(security): narrow x-no-account-id guidance and JWT notes 2026-05-01 13:59:07 -04:00
Scott Idem
d5e5cb7ada fix(idaa): gate jitsi report load and restore data store fallback 2026-05-01 13:45:24 -04:00
Scott Idem
e7b6045580 Updates to the documentation.
Co-authored-by: Copilot <copilot@github.com>
2026-04-30 17:13:11 -04:00
Scott Idem
a1ebeddf9d fix(core): clarify account fallback source and pretty-print _json payloads 2026-04-30 17:00:53 -04:00
Scott Idem
2f5ad8ccc0 fix(core): preserve account context on key params and harden account detail fallback
- api_get/post/patch_object: stop treating params.key as account-bypass trigger\n- account detail: remove forced key usage, add list/cache fallback path\n- account detail: fix fallback bug that set load_error even when fallback record existed\n- sites detail: pretty-print cfg_json before save\n- docs: clarify key != bypass and add 403 troubleshooting notes
2026-04-30 16:37:54 -04:00
Scott Idem
90adb19f5d fix(core): modern Svelte 5 cleanup — Dexie .get() bug, typed API calls, inline confirms
- person_view.svelte: fix liveQuery using .get() (primary key, never set by V3)
  → .where('person_id').equals().first()
- people/[person_id]: same Dexie .get() fix for lq__person_obj
- person_view.svelte: replace 4x generic api.update_ae_obj → core_func.update_ae_obj__person
  (removes unused api import)
- Replace all browser confirm()/alert() dialogs (9 occurrences, 6 files) with
  inline two-click confirm state pattern (confirm_action = $state<string|null>)
  Affected: users, accounts, contacts, addresses, people, sites
- Bootstrap doc: add Dexie .get() trap to Section 5 and Mistake #8
2026-04-30 16:00:20 -04:00
Scott Idem
7be60c2b8b fix(core): replace legacy *_id_random with V3 short-form IDs across all core pages
- sites, accounts, addresses, contacts, users list/detail pages
- ae_comp__person_obj_tbl: fix bulkGet→where/anyOf, rename prop person_id_random_li→person_id_li
- person_view: ~20 person_id_random refs in API calls/props
- people page + search + form components
- activity_logs: intentionally unchanged (person_id_random is a real field there)
2026-04-30 15:41:28 -04:00
Scott Idem
bb6782cc32 Clarifying the message about the UUID missing from the URL param. 2026-04-30 15:17:25 -04:00
Scott Idem
51b7f267e9 fix(auth): guard passcode check against missing site_access_code_kv
When the site domain resolves to ghost (not found or missing access key),
$ae_loc.site_access_code_kv is undefined, causing a TypeError on .super.length.

Add early return if kv is absent and use optional chaining on each access
level so the function gracefully returns "no match" on unregistered domains.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:33:27 -04:00
Scott Idem
de07fa0e0e docs: capture IDAA IDB audit results and layout security model
- TODO__Agents.md: mark IDAA IDB caching item complete (audited 2026-04-28);
  all protection layers confirmed in place, no code changes needed
- GUIDE__SvelteKit2_Svelte5_DexieJS.md: add "SvelteKit Layout Hierarchy:
  Security and Execution Order" section explaining execution order, auth-gate
  consequences, pre-gate risks in +page.ts/+layout.ts, and the reactivity-guard
  vs auth-guard distinction for IDAA $effect blocks
- BOOTSTRAP__AI_Agent_Quickstart.md: add Mistake #7 — treating $effect blocks
  as auth bypass risks vs understanding the real layout hierarchy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:10:17 -04:00
Scott Idem
b4f0ca3e64 fix(auth): re-enable ?key= access gate with persistent-state fix
The key gate was disabled 2026-04-01 after a page-refresh lockout bug.
Root cause: +layout.ts unconditionally wrote ae_loc_init['allow_access'],
which the +layout.svelte merge spread clobbered the persisted key string
on every navigation/refresh without ?key= in the URL, causing the gate
comparison to fail and showing "Access Denied".

Fix: only write allow_access to ae_loc_init when access_key is present
in the URL. On refresh/navigation without the key param, the persisted
value survives the spread unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:10:10 -04:00
Scott Idem
6507fb82c0 Bug fix for showing and hiding location filter part.
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 14:13:06 -04:00
Scott Idem
d692d7cfde Minor code clean up, style improvements, and bug fixes.
Co-authored-by: Copilot <copilot@github.com>
2026-04-27 13:53:12 -04:00
Scott Idem
fdee7c16ca fix(auth): harden magic-link root_url and clean up stale array-response code
- Defensive fallback for root_url: $ae_loc.base_url || window.location.origin
  so the backend email builder always gets a valid URL (guide warns that a null
  root_url produces a broken magic link "None?user_id=...")
- handle_lookup_user_email: drop stale array-response branch; use user_id (V3
  primary field) instead of user_id_random (legacy alias, same value)
- handle_change_password: same cleanup — user_id preferred over user_id_random,
  dead array-response else-if removed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 12:40:59 -04:00
Scott Idem
4d08994e79 docs: sync updated frontend API guide for user auth endpoints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 12:34:55 -04:00
Scott Idem
bbdfe75866 fix(auth): migrate sign-in from legacy /user/* to V3 action endpoints
Legacy GET /user/authenticate and GET /user/lookup_email were returning 404
because the backend has removed those routes. Updated all 5 auth functions in
ae_core__user.ts to use V3 equivalents:

- auth_ae_obj__username_password: GET /user/authenticate → POST /v3/action/user/authenticate (body)
- auth_ae_obj__user_id_user_auth_key: GET /user/authenticate → POST /v3/action/user/authenticate (body)
- send_email_auth_ae_obj__user_id: GET /user/{id}/email_auth_key_url → GET /v3/action/user/{id}/email_auth_key_url
- qry_ae_obj_li__user_email: GET /user/lookup_email → POST /v3/crud/user/search
- auth_ae_obj__user_id_change_password: PATCH /user/{id}/change_password → POST /v3/action/user/{id}/change_password

Credentials are now in the POST body (not query params) for authenticate calls.
Updated two call sites in e_app_sign_in_out.svelte to drop removed null_account_id param.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 12:12:52 -04:00
Scott Idem
56e23f3da0 fix(files): normalize file extension to lowercase before legacy/untrusted checks
Filenames like .PPT or .Ppt bypassed the extension checks entirely because the
comparison was case-sensitive. Lowercasing guessed_extension at the point of
computation fixes this for all checks (legacy, untrusted, block_upload).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 11:39:57 -04:00
Scott Idem
4ae9ecc381 fix(files): show legacy file warning banner for trusted-access users
Trusted-access users (Pres Mgmt admins) were getting file_list_status='ready'
when selecting .ppt/.doc/.xls files, so the prominent warning banner never
rendered — only the small per-row warning in the file table was visible.

- element_input_files_tbl: introduce 'warn_legacy' status for trusted users;
  show a yellow warning banner (vs red blocked banner for non-trusted users)
- ae_comp__event_files_upload: change button disabled check from != 'ready'
  to === 'blocked_legacy' so 'warn_legacy' does not accidentally block upload

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 23:24:55 -04:00
Scott Idem
3fd6b33d6f fix(events): prune stale event_file records from Dexie after list refresh
bulkPut only upserts — files deleted on the server stayed in Dexie forever,
showing in the Launcher and Manage Files UI until the browser cache was cleared.

After each _refresh_file_li_background call, deleted records are now pruned
from Dexie. Scope-guarded so we only remove records that would have appeared
in the query (e.g. hidden files are not pruned after a hidden='not_hidden' fetch).
Also covers the disable (enable=false) case the same way.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 10:02:40 -04:00
Scott Idem
e15a26f6c6 fix(launcher): load location files into Dexie on location select/refresh
refresh_location_config() was missing inc_file_li:true, so location-level
files were never fetched from the API and lq__location_event_file_obj_li
always returned empty from Dexie. Files only appeared when Pres Mgmt had
previously loaded them on the same device.

Also added a reactive $effect so files load immediately when the operator
switches rooms, rather than waiting up to 60s for the next timer tick.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 09:22:11 -04:00
Scott Idem
f8e34b10b8 docs(todo): document download button ID resolution bug and file.clear() scope issue
Both found during 2026-04-22 late-night review of Manage Files upload/download flow.
Downloads confirmed working despite wrong ID (backend silently accepts event_file_id
at hosted_file endpoint). Needs proper fix before backend tightens validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 02:23:08 -04:00
Scott Idem
29c5a9fa82 fix(pres_mgmt): hidden files now visible in Manage Files without manual refresh
Background file loads for session, presentation, and presenter were using the
default hidden='not_hidden', so hidden files never reached Dexie. The Manage
Files liveQuery reads straight from Dexie, making hidden files completely
invisible until the Refresh button was clicked (which already used hidden='all').

The Launcher is unaffected — it has always had a render-time guard that hides
files with event_file_obj.hide unless show_content__hidden_files is enabled.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 01:54:54 -04:00
Scott Idem
18cbe256de fix(pres_mgmt): increase file upload timeout to 20 min, guard null result
- Set post_object timeout to 1200000ms (20 min) for hosted file uploads;
  the 90s default was killing large presentation file uploads
- Guard result[0] access in .then() to prevent crash when upload
  times out or is aborted (TypeError: can't access property "hosted_file_id")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 00:36:14 -04:00
Scott Idem
2b2324ee8a Updated to do list 2026-04-20 15:31:29 -04:00
Scott Idem
6c6fccdfb4 Tweaking the colors and timing for the Session Menu in the Launcher 2026-04-20 13:33:54 -04:00
Scott Idem
ef5188aa6d refactor(launcher): remove duplicate session load from menu_session_list
On session click/hover, the menu was calling load_ae_obj_id__event_session
directly AND then navigating via goto(), which re-runs +page.ts and calls
it again. Both fired concurrently on cold cache, causing two identical API
requests for the same session.

Fix: remove the direct load call entirely. The goto() promise is assigned
to ae_promises.slct__event_session_id so the existing #await spinner still
works — it now reflects actual navigation + page.ts load time rather than
a redundant parallel fetch. Remove events_func and ae_api imports (unused).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 13:01:16 -04:00
Scott Idem
c4fdc8efa4 fix(launcher): hidden sessions collapse space, sort by datetime, rename internal-file flag
- menu_session_list: move class:hidden to <li> so fixed-height rows fully collapse
- launcher/+layout.svelte: sort sessions by start_datetime (ascending) instead of name
- Rename hide_content__draft_files → show_content__internal_files (default false);
  remove redundant show_content__draft_files; rename prop hide_draft →
  show_internal_purpose_files in launcher_file_cont; update all 7 call sites and
  the menu_launcher_controls toggle. Now hides admin/draft/outline purpose files
  by default with consistent naming across the flag, prop, and toggle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 12:49:39 -04:00
Scott Idem
66310adb22 More to do things. 2026-04-19 19:32:43 -04:00
Scott Idem
b94516ce26 fix(idaa): purge IDB when has_cached_session but $ae_loc has no auth
Closes a gap where $ae_loc could be reset externally (sign-out) while
$idaa_loc retained novi_verified within TTL, causing Case 2 to return
early and skip the IDB purge even though the render gate shows Access Denied.
Now Case 2 only preserves the session when $ae_loc also reflects active auth;
inconsistent state falls through to Case 1 (purge).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:53:26 -04:00
Scott Idem
b8e6bcaf03 fix(idaa): strip API calls from all +page.ts/+layout.ts, gate loading in $effect
SvelteKit load functions fire during link prefetch before Novi auth completes;
`if (browser)` guards do not prevent this. Moving all IDAA data fetching into
$effect hooks gated on `novi_verified || trusted_access` closes the IDB
pre-population race across archives, bb/[post_id], and recovery_meetings/[event_id].

Also documents the Auth-Before-Cache rule and per-route status in
AE__Permissions_and_Security.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:49:47 -04:00
Scott Idem
dea599bd9c fix(idaa): move Recovery Meetings load out of +layout.ts, gate $effect on auth
+layout.ts was firing on SvelteKit link prefetch, writing events to IDB
before Novi auth ran. Stripped to thin shell; the existing search $effect
in +page.svelte already handles SWR load+revalidation — just needed an
auth gate (novi_verified || trusted_access) at the top.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:15:41 -04:00
Scott Idem
4d5081582f fix(idaa): exempt trusted_access users from IDB purge and BB load gate
Case 1 purge in the layout was firing for manager/trusted users (no UUID),
causing a loop: db_events.event cleared → liveQuery updates → refetch →
store write → Effect 2 re-runs → clear again.

BB $effect was also blocking managers since novi_verified is always false
for non-Novi auth paths.

Both now check trusted_access before gating/purging.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:12:02 -04:00
Scott Idem
1381b81bf0 fix(idaa): move BB post loading from +page.ts to $effect in +page.svelte
+page.ts runs before layout effects and fires on SvelteKit link prefetch,
causing private IDAA posts to be written to IDB before Novi auth runs.
Moving to $effect gated on novi_verified eliminates the race entirely —
$effect only runs post-mount, after the layout has verified the user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:06:04 -04:00
Scott Idem
686b289bdb fix(idaa): gate BB +page.ts fetch on novi_verified
Without this, +page.ts fires the API call before +layout.svelte
effects run, causing posts to be written to IDB after the purge.
Anonymous users (novi_verified=false) now return early with no fetch.
Cached verified sessions (within TTL) continue to load normally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:48:03 -04:00
Scott Idem
6d8f767e45 fix(idaa): add console logs to all IDAA IDB purge paths
Three distinct log messages for each trigger:
- No UUID / no session path
- Novi auth failure (catch block)
- Reset & Retry button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:44:49 -04:00
Scott Idem
61c9a6766d fix(idaa): purge IDAA IDB on no-UUID unauthenticated path
The previous purge only fired inside verify_novi_uuid() catch,
which requires a UUID in the URL. Unauthenticated visits without
a UUID (Case 1 in Effect 2) now also clear posts, comments,
archives, and events from IDB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:43:22 -04:00
Scott Idem
ff4295b24c fix(idaa): also purge db_events on Novi auth failure
Extends the IDB purge from the previous commit to include
db_events.event — covers cached IDAA recovery meeting records.
No module overlap in current client deployments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:37:50 -04:00
Scott Idem
9d8c0e5dd4 Updated to do list for bug fixes related to IDAA. And possibly other areas. 2026-04-19 17:27:28 -04:00
Scott Idem
236a5513ee fix(idaa): purge posts and archives IDB on Novi auth failure
When Novi UUID verification fails (or the manual Reset & Retry is
triggered), clear db_posts.post, db_posts.comment, db_archives.archive,
and db_archives.content from IndexedDB. Prevents private IDAA data
from persisting in the browser after a session ends or auth is denied.

db_events.event intentionally excluded — shared with conference modules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:26:34 -04:00
Scott Idem
868f4b3390 Updated he directory path for general agents trash. 2026-04-19 16:55:10 -04:00
Scott Idem
aebbcf5b47 docs: add AI agent bootstrap / quickstart document
Concise onboarding doc covering: project overview, critical rules (IDAA
privacy, no-rm, svelte-check), env/deploy cheat sheet, Svelte 5 runes
patterns, V3 API patterns, naming conventions, real past mistakes, source
layout, and reading order for deeper dives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:52:25 -04:00
Scott Idem
9baffc4407 chore(devops): clean up TODO and remove dead package.json scripts
- TODO: mark BGH file-warning and hide-draft items complete; add detailed
  Dockerfile env-file simplification task (deferred post-April 21 show);
  strip stale completed DevOps entries from the active list
- package.json: remove build:docker:test/prod (never used locally; deploys
  go through remote deploy.sh on Linode)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:35:31 -04:00
Scott Idem
898afd9775 fix(files): refine legacy file upload warnings and trusted-access block bypass
- element_input_files_tbl: only block upload for non-trusted users; trusted_access
  users see the same warnings but can still proceed
- element_input_files_tbl: improved warning message wording for .ppt and .doc
- element_manage_event_file_li: minor tweaks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 13:56:19 -04:00
Scott Idem
74e65ea892 feat(files): block upload and show warning for legacy .ppt/.doc file formats
- Set file_list_status to 'blocked_legacy' when any selected file is .ppt or .doc,
  disabling the Upload button until the file is removed
- Show a red banner at the top when upload is blocked
- Add a per-file warning message row in the file table for all legacy/untrusted
  extensions (previously computed but never rendered — only a pink cell highlight)
- Red styling for blocking extensions (.ppt/.doc), yellow for warn-only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 13:07:45 -04:00
Scott Idem
1ad3d2030d fix(launcher/files): hide admin-purpose files and fix event_file_id in PATCH body
- launcher_file_cont: add 'admin' file_purpose to hide_draft filter (alongside outline/draft)
- element_manage_event_file_li: remove event_file_id from data_kv passed to update_ae_obj;
  it was being sent in the PATCH body causing 'Unknown column event_file_id in SET' (400)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 13:02:56 -04:00
Scott Idem
721facf7ba fix(locations): auto-load locations on page open; fix session query and POC visibility
- Add +page.ts to trigger load_ae_obj_li__event_location on page load (locations
  were never fetched without a manual trigger)
- Fix ae_comp__event_session_obj_li_wrapper: query used event_location_id_random
  (deprecated index) instead of event_location_id, causing empty session lists
  under each location
- Wire hide__session_poc to pres_mgmt_loc.current.show__session_li_poc_field so
  the Options toggle actually takes effect in the per-location session list
- Also set hide__session_location=true since location is implicit in that context

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 11:54:22 -04:00
Scott Idem
a42b49dd50 fix(launcher): auto-set app_mode to native when running in Electron
On a fresh Electron install the events_loc persisted store has no
app_mode value set, causing the native file launch path to fall through
to a browser save dialog. Auto-initialise app_mode='native' in the
launcher layout when is_native is detected so all three modes (default,
onsite, native) continue to work correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:51:51 -04:00
Scott Idem
278a40c981 Updated to do list 2026-04-18 18:16:35 -04:00
Scott Idem
5fcf2e86f1 Making things look nicer 2026-04-16 19:48:09 -04:00
Scott Idem
7543bf6ae5 Renamed a directory to be more consistent 2026-04-16 19:15:18 -04:00
Scott Idem
9af5a292b6 Updating to do lists. 2026-04-16 19:11:25 -04:00
Scott Idem
2595664dd1 feat(pres_mgmt): extract session search component + time window filter
- Extract session search form into ae_comp__pres_mgmt_session_search.svelte
  (parallels ae_comp__badge_search.svelte); removes ~145 lines from +page.svelte
- Add time window filter: Clock icon toggle button reveals compact before/after
  selects; trusted users get 3d/7d options; active state highlighted in amber
- Add passes_hide_filter to IDB fast path to mirror API qry_hidden logic and
  eliminate the hidden-session blink on revalidation
- Add passes_time_window applied to both IDB fast path and API results
- Add time window state fields to PresMgmtLocState + pres_mgmt_loc_defaults
- Add contextual warning in "No sessions found" when time filter is active
- badges: hide "Start Here" button for trusted_access users; tweak button shade
- badges: scope placeholder CSS fix to input only (not textarea)
- Add MODULE__AE_Events_PressMgmt_Launcher.md doc

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 19:01:35 -04:00
Scott Idem
e4265f69af fix(badges): fix stale-Dexie race in font size initialization
The old guard locked on badge ID after the first liveQuery tick. If
Dexie had a cached badge without cfg_json.font_sizes, the guard fired
with no sizes to apply, then blocked the SWR background refresh that
delivered the real saved sizes. Result: font sizes appeared unsaved on
any browser that had visited the badge before sizes were set.

Fix: track the cfg_json string last applied (_font_sizes_applied_cfg)
instead of just the badge ID. Re-applies whenever cfg_json changes on
a background refresh, but skips if local sizes have drifted from the
last apply (user is mid-adjustment — auto-save will sync shortly).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 21:51:18 -04:00
Scott Idem
1df17e68bb fix(badges): lighten placeholder text in create form
Tailwind v4 renders placeholder text too dark on light backgrounds,
making it indistinguishable from real input values. Same scoped CSS
fix already applied to ae_comp__badge_print_controls.svelte.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 20:29:43 -04:00
Scott Idem
deea250a85 fix(badges): add fallback badge_type_code_li when template has no badge_type_list
badge_type_code_li was returning [] when the template had no badge_type_list,
causing the Badge Type field to be hidden entirely in the Staff section.
Add same fallback default as create form and search filter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 18:07:24 -04:00
Scott Idem
4d43fd8a67 fix(badges): move Badge Type field to top of Staff section
Was at the bottom after print position controls — now first item in
Staff Adjustments, before Hide/Unhide Badge, where staff expect it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 18:03:50 -04:00
Scott Idem
9180384ec1 temp(badges): restrict Copy Link to Administrator + Edit Mode only
Was: Trusted + Edit Mode. Now: Administrator + Edit Mode.
Temporarily restricted for Axonius 2026.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:46:50 -04:00
Scott Idem
75f755c660 temp(badges): restrict Email Link to Administrator + Edit Mode only
Was: visible to everyone pre-print, Trusted+Edit for reprints.
Now: Administrator + Edit Mode only (all three locations).
Temporarily restricted for Axonius 2026 — restore broader access after event.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:41:00 -04:00
Scott Idem
4780be7a00 fix(badges): sync create form badge types with search filter; default to attendee
- default_badge_type_code_li now matches ae_comp__badge_search.svelte list
  (attendee, sponsor, staff, guest, volunteer, member, nonmember, test)
- badge_type_code initializes to 'attendee' (In-Person Attendee)
- $effect re-applies preferred default when template badge_type_list loads async,
  falling back to first item if 'attendee' isn't in the template's list
- Remove the blank '-- Select Badge Type --' placeholder option

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:32:36 -04:00
Scott Idem
5d6cc4ca35 fix(badges): shorten pending_close Save button label to prevent wrapping
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:28:54 -04:00
Scott Idem
a68b85e1f1 fix(badges): use direct token classes for field_actions buttons
btn + preset-filled-* renders transparent on gray/surface backgrounds
(Skeleton v4 CSS variable specificity issue — documented in
GUIDE__AE_UI_Style_Guidelines.md §12).

Replace all three buttons in field_actions (Save, Revert, Cancel) with
direct Tailwind token classes: bg-warning-500, bg-error-500,
bg-success-500, bg-surface-200-800 etc. Save button now visibly renders
in amber (dirty), red + pulse (pending_close), green (saved).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:19:03 -04:00
Scott Idem
0199c2e2c9 fix(badges): guard unsaved edits — warn on close, error on second close
When a field accordion has unsaved changes and the user tries to close
(X button, same-header click, or switching to another field), we now set
pending_close = true instead of silently discarding.

- Save button turns bright red + animate-pulse with label "Save first (or × to discard)"
- X button turns red with "Discard changes" tooltip
- Field stays open — no data is lost
- Second close attempt (pending_close already true) actually discards
- Saving normally clears pending_close and closes the accordion

WHY: kiosk attendees at a live event were silently losing typed overrides
(professional title, affiliations, etc.) when switching fields mid-queue.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:07:26 -04:00
Scott Idem
126eb77be2 fix(badges): cancel edit state on field switch, not just on explicit cancel
toggle_field only changed active_field — it never called cancel_field for the
previously open field. Unsaved typed values stayed in edit_full_name_override etc.,
so reopening a field would show the stale typed value and re-apply it to the badge
preview, even though the user had already moved on.

New logic: capture was_open, always call cancel_field for the current field (resets
edit vars + sets active_field = null), then open the new field if it wasn't the one
being closed. Closing a field by re-clicking its pencil now also discards unsaved state,
consistent with the explicit [X] button behavior.

Also: add global placeholder CSS fix to TODO__Agents.md (scoped workaround already
in ae_comp__badge_print_controls; long-term fix belongs in app.css or theme file).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 16:55:22 -04:00
Scott Idem
7733ef8708 fix(badges): auto-focus input after accordion animation + mute placeholder text
Auto-focus: requestAnimationFrame (~16ms) fired before the 185ms accordion
animation ended — input was still 0px/clipped so focus() silently failed.
Changed to setTimeout(210ms) so focus lands after the animation completes.

Placeholder color: placeholders show the current badge value (e.g. 'John Smith')
so without explicit styling they look identical to filled text. Added scoped
CSS rules setting placeholder to gray-400 (light) / gray-500 (dark) so it reads
clearly as a hint rather than existing content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 16:42:58 -04:00
Scott Idem
a81d65ce7e fix(badges): detect and surface PATCH failure in handle_print_badge
update_ae_obj__event_badge returns false on API failure without throwing,
so the old code always treated a failed PATCH as success — badge printed,
count not saved, page navigated away silently.

Now: check the return value explicitly. On failure — still fire window.print()
(physical print must never be blocked) and still navigate back, but show a
visible red error state ('Printed — count NOT saved (see staff)') and hold
for 4s so a kiosk operator can see it before the loop resets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 16:36:56 -04:00
Scott Idem
3d81cb5a83 fix(badges): strip milliseconds/Z from datetime before PATCH
MariaDB rejects ISO 8601 with milliseconds ('2026-04-14T20:29:15.784Z').
print_last_datetime and print_first_datetime must be 'YYYY-MM-DDTHH:MM:SS'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 16:32:07 -04:00
Scott Idem
345641e4c4 fix(badges): show 'already printed' notice to public users on print page
Without this, a public user who navigates to a printed badge's print page
sees a blank controls panel with no explanation. Now shows an amber notice
directing them to event staff. Gated on !can_print && is_printed && !is_trusted
so it never shows to staff.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 23:51:25 -04:00
Scott Idem
71c615bf4a fix(badges): hide template debug info bar from non-staff users
The name|layout|v3 info row was always visible. Gate it on
trusted_access && edit_mode — attendees and volunteers should
not see internal template metadata above their badge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 23:44:44 -04:00
Scott Idem
81aa0eefcd fix(badges): properly suppress pronouns and lead scanning with {#if false}
HTML comments don't suppress Svelte {#if} blocks — the content was rendering
unconditionally. Switch to {#if false} so the blocks are genuinely hidden.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 23:29:36 -04:00
Scott Idem
430d39231d temp(badges): comment out pronouns and lead scanning for Axonius 2026
Lead scanning was canceled last-minute; pronouns not on this badge template.
Both blocks left in source with AXONIUS 2026 markers for easy restoration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 23:14:35 -04:00
Scott Idem
5203104fef refactor(badges): move hide toggle + print count editor to individual badge view
Hide/Unhide and print count edit belong on the per-badge page (print controls
staff section), not the search list — the list was getting too crowded.

- ae_comp__badge_obj_li: removed hide toggle, print count input, and the
  ae_api/events_func imports that were only there to support them
- ae_comp__badge_print_controls: added Hide Badge button (Trusted, top of staff
  section) and Print Count editor (Admin+, below hide); both reuse the existing
  save_field/field_save_status pattern for consistent spinner/done/error feedback

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 22:44:58 -04:00
Scott Idem
bf31f13650 fix(badges): gate Show Hidden filter on trusted_access + edit_mode
Lower access levels (authenticated, public) can have edit_mode active.
Show Hidden must be trusted+ only — split it out of the generic edit_mode block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 22:38:58 -04:00
Scott Idem
7bc7bf5554 feat(badges): hide toggle, print count editor, show hidden filter
- Hide/Unhide toggle button (Trusted + Edit Mode) on each badge row in the list; badge disappears immediately when hidden unless Show Hidden is active
- Print count inline editor in debug row (Admin + Edit Mode); updates count only, no timestamp changes
- "Show Hidden" checkbox in search filters (Trusted + Edit Mode); wires through IDB fast-path, API hidden param, and visible_badge_obj_li filter
- show_hidden requires edit_mode to be active — reverts to hiding hidden badges when edit mode is off

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 22:21:08 -04:00
Scott Idem
6aeaef6f1d fix(badges): trusted staff visibility driven by filter, not edit mode
Edit mode should not override the filter state — staff set their
filters and turn off edit mode all the time. The real split is
trusted staff vs kiosk/public, not edit mode on/off.

Trusted and above: qry_printed_status is the sole control over
printed badge visibility, regardless of edit mode state.

Public (kiosk) / authenticated / anonymous: always unprinted only.
Badge kiosks run at public_access and should never expose a list
of already-printed badges to attendees.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 19:44:30 -04:00
Scott Idem
ae00ddffb0 fix(badges): fix Printed/Not Printed filter visibility and API query
Two bugs:

1. visible_badge_obj_li gated on trusted+edit_mode, but the filter
   dropdown is also accessible to manager+ without edit_mode. Changed
   gate to (trusted+edit) || manager_access to match the filter's own
   access condition.

2. not_printed API query used print_count eq 0, which does not match
   NULL in SQL. Unprinted badges have print_count = NULL, so the API
   was returning 0 results and overwriting the correct IDB fast-path
   results. Removed the not_printed condition from the API query —
   IDB fast path (print_count ?? 0) < 1 and visible_badge_obj_li
   both handle NULL correctly and are the authoritative filter for
   that case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 19:39:58 -04:00
Scott Idem
8d430a9c31 fix(badges): drive printed badge visibility from status filter not edit_mode
Previously edit_mode was a blunt override: trusted+edit showed all
badges regardless of the filter setting. This meant the Printed Status
dropdown had no effect on what was visible in the list.

Now trusted+edit mode respects qry_printed_status as the single source
of truth: 'all' shows everything, 'printed' shows only printed, and
'not_printed' shows only unprinted. The filter dropdown is only
accessible to trusted+edit users so it is safe to use as the control.

Kiosk/attendee behavior (trusted no edit, public, anonymous) unchanged:
only unprinted badges are shown regardless of filter state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 19:24:24 -04:00
Scott Idem
f6051156cf fix(badges): include sort selection in active-filters check
Sort changes without a text query were falling through to the fallback
liveQuery (50 badges sorted by given_name), ignoring the selected sort
entirely. Added params.sort to has_active_filters so any explicit sort
selection triggers the full search path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 19:15:11 -04:00
Scott Idem
d64222ca91 fix(badges): implement missing sort cases for all sort options
All four sort options in the dropdown were falling through to the
default (given_name ASC) because their cases were missing from both
the IDB fast-path sort and the API order_by_li mapping:

- Affiliations ASC: IDB sorts by affiliations_override → affiliations;
  API sorts by affiliations column
- Badge Type ASC: badge_type_code ASC
- First Printed DESC: print_first_datetime DESC
- Last Printed DESC: print_last_datetime DESC

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:57:03 -04:00
Scott Idem
acf0a13955 fix(badges): update badge type list and fix filter-only search
Update badge type codes for Axonius 2026 (replaces ISHLT 2024 list).
Added TODO to drive this from event templates in the future.

Fix printed status and badge type filters not working without a text
query. The min_chars guard was blocking all filter-only searches,
causing "Printed" and "Not Printed" to always return empty results.
Now bypasses min_chars when any non-default filter is active (printed
status, type code, or affiliations), since selecting a filter is
explicit user intent regardless of the text query.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:42:04 -04:00
Scott Idem
5826b21821 feat(badges): allow public_access to print first-print badges
Badge print kiosks authenticate at the public_access level (site-wide
passcode). Previously the print gate was trusted_access, meaning kiosk
operators had to sign in at the trusted level just to print.

Changed in both the list view and the badge detail controls panel:
- First print: public_access and above (kiosk use case)
- Reprint: still requires trusted_access + edit_mode

ae_comp__badge_obj_li.svelte: added is_public derived; updated
can_print and the print button #if condition.

ae_comp__badge_print_controls.svelte: added is_public derived; updated
can_print comment and logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:01:47 -04:00
Scott Idem
ad3b27b747 fix(badges): auto-save font sizes on adjustment
Font size changes now persist automatically (600ms debounce) without
requiring the user to find and click Lock Sizes in the collapsed Staff
section. reset_font_sizes_to_auto continues to handle its own save.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 19:37:31 -04:00
Scott Idem
15566efec1 revert(badges): remove _random workaround on badge create template ID
Per V3 convention, {obj_type}_id IS the random string — send
event_badge_template_id (not _random). The backend not saving it is
a backend bug, not a frontend concern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 18:37:16 -04:00
Scott Idem
5e07f2822c fix(badges): send event_badge_template_id_random on badge create
The IDB stores the random string in event_badge_template_id (overwritten
by _process_generic_props). Sending this as event_badge_template_id
passed a string to an int(11) FK column — backend silently ignored it.
Using event_badge_template_id_random lets the V3 CRUD handler resolve
it to the correct integer FK.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 18:34:54 -04:00
Scott Idem
d35a28f912 ui(badges): show template name on create form when only one exists
Single-template events auto-select silently but gave no visual
confirmation. Added a read-only display of the template name so staff
can verify the correct template is in use before submitting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 18:06:26 -04:00
Scott Idem
2e01e7f115 fix(badges): always pre-select first template on badge create
Every badged event must have a template — without one the badge cannot
render. Changed auto-select from === 1 to >= 1 so multi-template events
also get a default (first template). Added an error message and disabled
submit when no templates are configured at all. Removed blank
"-- Select Template --" option so the form never submits with null.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 18:03:15 -04:00
Scott Idem
f7ddcaa448 fix(badges): use base fields instead of _override on badge create
Professional title, organization, and location entered during manual
badge creation were being stored in the *_override fields. Override
fields are intended for overriding imported/AMS person data — for new
manually created badges, the base fields (professional_title,
affiliations, location) are correct.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 17:53:48 -04:00
Scott Idem
940e25d549 fix(theme): add missing color ramps to AE_Firefly_Axonius
Each data-theme selector is fully self-contained — CSS custom properties
do not inherit across theme selectors. The Axonius file only defined the
primary ramp, leaving surface/secondary/tertiary/success/warning/error
undefined, causing the UI to render in grey/black/white.

Added full color ramps and dark-mode contrast tokens (matching base
AE_Firefly) to both light and dark blocks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 20:52:58 -04:00
Scott Idem
22d62ba3b1 Adjusting styles Axonius 2026-04-10 19:42:41 -04:00
Scott Idem
c7fa75afc7 ui(badges): add background image bleed support (cfg_json, PVC layout)
- Add `bleed` field to BadgeTemplateCfg (CSS length, e.g. "0.125in")
- Badge view: derive bleed_offset, move bg-image to absolute positioned div
  that extends past card edges; add isolation:isolate to badge_front stacking context
- Template form: add bleed input in Advanced > Appearance; wire to cfg_json save/load
- PVC layout CSS: change overflow:hidden → overflow:visible in print rule to allow
  bleed div to render at physical card boundary (Zebra driver clips at card edge)
- Prevents white borders on PVC cards when printer has slight alignment variance.
  Screen preview shows bleed visually extending past the card outline.
2026-04-10 14:25:08 -04:00
Scott Idem
cfdec1e305 Forgot to include this update 2026-04-10 11:53:38 -04:00
Scott Idem
bfe02727bf docs(passcode): note backend fixes implemented and tested; phase 2 pending 2026-04-10 11:53:00 -04:00
Scott Idem
e542c55500 ui(badges): layout & fit-text tweaks; improve template form controls; remove badge modals from event settings; add documentation for passcode security 2026-04-10 11:44:22 -04:00
Scott Idem
c9e2284758 Badges: per-badge locked font sizes via cfg_json
Allows coordinators to pre-tune font sizes for attendees with long names
and have those sizes apply automatically on every kiosk, not just one machine.

- ae_types.ts: add cfg_json to ae_EventBadge interface
- db_events.ts: add cfg_json to Badge Dexie interface
- ae_events__event_badge.ts: add cfg_json to properties_to_save so it is
  persisted to IndexedDB on load and returned by the API
- print/+page.svelte: on first load per badge, read cfg_json.font_sizes and
  initialize font_size_name/title/affiliations/location state from saved values
  (guarded by _font_sizes_loaded_for to avoid clobbering user adjustments on
  background liveQuery refreshes)
- ae_comp__badge_print_controls.svelte: add lock_font_sizes() and
  reset_font_sizes_to_auto() functions; add Lock Sizes / Auto reset UI in the
  Staff adjustments section (trusted-only); button shows warning style when
  sizes are unsaved vs success when locked; status indicator shows what is
  currently locked

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 21:47:34 -04:00
Scott Idem
941ad6ae88 Badges: template controls cfg, collapsible form sections, navigation polish
- badge_template_form: fix default field visibility (location off render, pronouns/leads excluded from controls); fix duplicate QR checkboxes by removing orphan show_qr_front/back state vars; reorganize Advanced cfg_json into labeled sub-groups; make all 5 non-Advanced sections collapsible (general starts open, rest collapsed)
- print_controls: add DEFAULT_SHOWN constant so field_shown() uses explicit whitelist fallback instead of showing all fields when no controls_cfg is set
- badges config +page: add Templates navigation button in header (FileText icon)
- templates +page: add back-nav header with ArrowLeft to badges/config, Settings icon, page title

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 20:31:38 -04:00
Scott Idem
d05420d9c1 Badges: persist template controls_cfg; fix onchange syntax in template form 2026-04-09 15:10:03 -04:00
Scott Idem
76c28a7e22 Updated to do list. Cleaned up the badge search area at the top of the badge. Also tested the upload option 2026-04-09 14:31:29 -04:00
Scott Idem
a84ea4cbcb Cleaned up the Badges main search area and the top in general. 2026-04-08 17:34:05 -04:00
Scott Idem
dd4c558d1b feat(badges): add Axonius Zoom CSV server import option to upload form 2026-04-08 17:10:55 -04:00
Scott Idem
d7b49efdde Apply site cfg_json theme when persisted ae_loc shows no explicit user-theme; parse persisted ae_loc to detect prior user choice 2026-04-08 16:43:47 -04:00
Scott Idem
fec08fdfbf Respect site cfg_json theme on first-run; add user_theme_selected flag; set flag when user selects theme or URL param 2026-04-08 16:38:12 -04:00
Scott Idem
32ed4e47a8 Remove outline... 2026-04-08 16:13:36 -04:00
Scott Idem
534bda9203 Apply site.cfg_json theme defaults only on first-run (no persisted ae_loc); preserve manual/URL overrides 2026-04-08 16:07:20 -04:00
Scott Idem
8aef519aa6 Apply site.cfg_json theme defaults to ae_loc (theme_name, theme_mode); allow URL param to override 2026-04-08 15:43:15 -04:00
Scott Idem
dd339a7280 Better for the small screens now. 2026-04-08 15:19:07 -04:00
Scott Idem
3659fef17c chore(badges): add 'Start Here' helper button to focus fulltext search input 2026-04-08 15:14:18 -04:00
Scott Idem
d5b2b557f3 chore(badges): hex-only body_text_color + form color picker; renderer default black 2026-04-08 14:19:48 -04:00
Scott Idem
0aa32a5293 chore(tailwind): safelist common text-* color utilities for dynamic classes 2026-04-08 13:42:47 -04:00
Scott Idem
ef5c807c27 chore(badges): tidy badge template form grouping and advanced cfg_json 2026-04-08 13:29:11 -04:00
Scott Idem
b02843e467 feat(badges): cfg_json body_text_color applied in renderer 2026-04-08 12:32:13 -04:00
Scott Idem
56b4e5c627 Slight change to header and footer background colors. 2026-04-08 11:56:02 -04:00
Scott Idem
b64db756ad Add AE_Firefly_BGH theme; align typography tokens for Axonius/BGH; register themes in UI 2026-04-08 11:42:34 -04:00
Scott Idem
590139e63a New style option for Axonius 2026. Set as default for them as well in their site config.
Also general style clean ups
2026-04-08 10:21:08 -04:00
Scott Idem
372d79df2b docs(idaa): track contact_li_json_ext search gap + message sent to backend
- TODO__Agents.md: added task for contact search — backend to whitelist
  contact_li_json_ext in event search, frontend to add OR condition in
  search__event() and update local IDB fast-path filter. Blocked on backend.

- CLIENT__IDAA_and_customized_mods.md: documented the search architecture
  gap under Recovery Meetings — what default_qry_str contains, why
  contact_li_json is unsearchable as raw JSON, what contact_li_json_ext is
  and what needs to happen to enable contact name/email search.

Backend agent notified via ae_send_message 2026-04-08.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 00:20:59 -04:00
Scott Idem
c979454d84 docs(idaa): update IDAA doc with staff editing rules, Contact 1 convention, test coverage
- Added Section 4 'Staff Editing Rules': documents per-object behavior when
  trusted/admin staff edit member content. BB Post external_person_id is readonly
  for non-admin staff; Post Comment preserves existing record identity; Recovery
  Meeting external_person_id is intentionally editable for ownership reassignment.
  Clarifies that staff identity only fills when the record has no existing linkage.

- Added Section 5 'Recovery Meetings — Contact 1 Convention': states the business
  rule that Contact 1 is nearly always the same person as external_person_id (the
  meeting owner). Documents the distinction between ownership link vs. display contact.

- Added race condition defense note to Section 3 Implementation Patterns: creation
  buttons and edit submit handlers must scavenge from localStorage when the Svelte
  store is briefly null on mount.

- Updated test coverage table: Recovery Meetings now has substantial Playwright
  coverage (idaa_recovery_meeting_edit.test.ts). Noted pending BB Post/Comment tests.

- Updated Last Verified date to 2026-04-07.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:41:49 -04:00
Scott Idem
8d30e01ad4 fix(idaa): harden identity linkage in BB post and comment edit handlers
Three targeted fixes following code review of the Novi UUID linkage commit:

1. ae_idaa_comp__post_obj_id_edit.svelte — Add localStorage scavenge fallback
   in handle_submit_form() for external_person_id / full_name / email.
   WHY: The form input falls back to $idaa_loc.novi_uuid at render time only.
   On a race-condition mount where the store was null, the input captures an
   empty string.  Without this, a subsequent PATCH on a legacy post (no
   external_person_id) would overwrite the field with an empty string, permanently
   breaking the Novi linkage for that record.  The scavenge re-checks the live
   store and then localStorage before submitting.

2. ae_idaa_comp__post_options.svelte — Fix double alert() on creation failure.
   WHY: The .catch() handler alerted the user and reset 'creating'.  The
   .finally() block then ran unconditionally and fired a second alert when
   final_id was null (which it always is on failure).  User saw two dialogs.
   Fixed by removing the duplicate alert from .finally() — it now only resets
   the 'creating' flag, which .catch() may have already done (harmless reset).

3. ae_idaa_comp__post_comment_obj_id_edit.svelte — Remove 'log_lvl = 1' mutation.
   WHY: log_lvl is a $bindable prop.  Assigning to it inside handle_submit_form()
   unconditionally mutated the parent binding on every single form submission,
   overriding the caller's logging preference.  This was debug code accidentally
   left in.  Removed; the existing 'if (log_lvl)' guard is sufficient.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:23:33 -04:00
Scott Idem
f2765d6a5e feat(idaa): enforce mandatory Novi UUID linkage for member content
CRITICAL IDENTITY FIX:
Ensures all member-generated content (Meetings, Posts, Comments) is explicitly linked to the creator's Novi UUID via 'external_person_id' at the moment of creation.

Changes:
- Added 'external_person_id' to creation payloads in Recovery Meetings and BB Posts.
- Implemented 'identity scavenging' from localStorage in submit handlers to prevent race conditions where Svelte stores are briefly null.
- Refactored Post Comment edit component to robustly initialize and save creator identity.
- Added 'The Novi UUID Rule' to IDAA documentation to mandate this pattern for future development.
- Added Playwright test to verify creation linkage and fixed a version-mismatch bug in the test auth helper.

Note: Archives and Archive Content are excluded as they do not require member ownership.
2026-04-07 22:07:53 -04:00
Scott Idem
ef45a0ca0f feat(badges): TC modal centering, positioning, and allow-tracking toggle
- Center modal horizontally; position 10vh from top instead of centered vertically
- Add Allow/Do-not-allow toggle buttons inside the TC modal so attendees
  can set their lead scanning preference while reading the terms
- Buttons reflect current DB value on open and use solid color fills
  (green/red) so selection state is unambiguous in light and dark mode
- Save & Close calls existing save_field('allow_tracking') then closes;
  Cancel closes without saving

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 20:29:41 -04:00
Scott Idem
b01478a87f More layout and style clean up and related. 2026-04-07 19:04:27 -04:00
Scott Idem
f34074cdd6 Cleaning up the styles and some permissions 2026-04-07 18:53:22 -04:00
Scott Idem
ae9cdaf9f1 badges: per-tier search limits — result cap + min chars, config UI
Add anonymous/auth/trusted search constraints to BadgesRemoteCfg with
conservative defaults (anon: 15 results / 3 chars, auth: 25 / 2,
trusted+: 150 / 1). Configurable per event via mod_badges_json.

- BadgesRemoteCfg + BadgesLocState: 6 new fields with defaults
- sync_config__event_badges: mirrors new fields from mod_badges_json
- +page.svelte: effective_search_limits derived by tier using $ae_loc
  cumulative flags; enforces min_chars guard and result cap on both
  local IDB path and API call
- ae_comp__badge_search: effective_min_chars derived same way; blocks
  search trigger below threshold; shows dynamic hint text
- Fallback broad search (SCENARIO 2) suppressed for non-trusted users
  so no results show on page load without a query
- config/+page.svelte: Search Limits section with 3-column number
  inputs (Anonymous / Auth / Trusted+) for result limit and min chars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:08:10 -04:00
Scott Idem
be0b8baf62 Re-organizing things. 2026-04-07 15:54:01 -04:00
Scott Idem
93fea0d165 Making custom changes just for Axonius badge printing next week 2026-04-07 14:59:23 -04:00
Scott Idem
988ba75df3 feat(badges): cfg_json hide toggles for title/affiliations/location; wire renderer 2026-04-07 14:28:10 -04:00
Scott Idem
34bf823987 chore(badges): save in-progress changes — background_image_path, cfg_json support, template form TS fix, view boolean fixes 2026-04-07 13:57:02 -04:00
Scott Idem
1e178c14e7 leads: lead detail UX overhaul — notes editor, priority star, profile card cleanup
- Replace admin field editor with direct TipTap + Save Notes button for exhibitor notes;
  show Add Notes button when notes are empty (no dead placeholder)
- Add one-click priority star toggle in header (always visible, no edit mode required)
- Remove Exhibit Context card (exhibitors don't need to see their own booth name)
- Move Captured By into profile card with human-readable labels
  (shared_passcode → "Booth (Shared)", access type codes → Staff/Admin)
- Add location row (city/state + country) to profile card
- Gate Remove button to edit mode only to prevent accidental taps
- Fix button position stability: Edit/View always rightmost (same screen position),
  Remove grows in from left — prevents double-tap accidents
- Add unsaved-changes guard (beforeNavigate) covering both notes and custom question form
- Custom questions form: hide Save when no questions configured, show
  "Configure in Manage Tab" link instead

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 20:42:55 -04:00
Scott Idem
50e83502ff leads: UX improvements — manage tab, sign-in flow, notes editor, filter
- leads_api_access toggle in Admin Tools (manager only)
- Account Status section for end users (payment/licenses/API badges + CSV export button)
- Sign-out fix: use Object.fromEntries instead of delete on PersistedState proxy
- Shared passcode sign-in redirects directly to Manage tab (their role is config, not capture)
- Manage tab section reorder: Account Status → Lead Retrieval Config → Booth Profile → Access & Security → App Settings
- Filter dropdown: replace abstract "My Leads" with direct identity options (All / Booth (Shared) / per-licensee); auto-resolves and migrates stale 'my' values
- Lead detail: replace Element_ae_obj_field_editor notes with direct TipTap editor + Save Notes button; Add Notes button on empty state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 19:25:38 -04:00
Scott Idem
10e9206ca4 fix(leads): persist licensed auth across reloads; manage tab UX fixes
**Session persistence bug** — leads_loc_defaults was missing __version: 1.
store_versions.ts wipes ae_leads_loc when parsed.__version !== 1 (always true
when the field is absent), so every page reload cleared auth_exhibit_kv and
forced re-login. Adding __version: 1 to both the interface and defaults fixes
this for all auth types.

**Manage tab fixes:**
- Description: collapsed by default with ChevronDown/Up toggle — same pattern
  as session_view.svelte. Avoids long promo copy dominating the manage screen.
- Staff Passcode: removed duplicate green plain-text display for admins; the
  Element_ae_obj_field_editor already shows the value (was showing twice).
- Booth Identifier: replaced static read-only display with Element_ae_obj_field_editor
  so the booth code (exhibit.code) is editable inline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 17:19:35 -04:00
Scott Idem
f95243a9c7 fix(leads): disable sign-in submit until exhibit loads; add licensed-user auth tests
Prevents silent no-op when user clicks submit before lq__exhibit_obj is ready
(exhibit not yet written to Dexie). Button now shows 'Loading...' spinner while
the exhibit record is resolving, eliminating the two-tap workaround needed on
first page load.

Also adds 7 Playwright tests for licensed user sign-in (leads_licensed_signin.test.ts)
covering success path, wrong credentials, email/identity tagging on captured leads,
identity isolation between staff members, and returning-session bypass.

Helpers: attach_leads_routes/setup_leads_test_page now accept exhibit_overrides
(e.g. license_li_json) to inject licensed users into mocked API responses.
seed_leads_loc import added to leads_auth.test.ts multi-exhibit test.

Total leads test coverage: 29 tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 17:04:57 -04:00
Scott Idem
d340bbbe94 test: seed ae_leads_loc; update badge tests and helpers; docs: note Leads migration 2026-04-03 17:47:10 -04:00
Scott Idem
a952c5ddbe docs(leads): document Leads store migration and payment UI fix; note tests update 2026-04-03 17:33:23 -04:00
Scott Idem
7f79c1857a leads: event-level payment config + Stripe key migration
- New /events/[event_id]/leads/config page: administrator UI for
  mod_exhibits_json. Controls leads_require_payment toggle and Stripe
  keys (publishable key + buy button IDs per license tier).

- leads_require_payment (mod_exhibits_json) now gates all billing UI:
  header CreditCard button in exhibit +page.svelte and Licenses & Billing
  accordion in ae_tab__manage.svelte. Default false (client covers costs).

- Stripe keys migrated from site_cfg_json to mod_exhibits_json (per-event).
  ae_comp__exhibit_payment accepts them as optional props; falls back to
  site_cfg_json for events not yet migrated.

- Fixed "My Leads" bug for shared-passcode users: search_params now maps
  licensee_email 'my' → 'shared_passcode' literal (not kv.key passcode
  string) so filters correctly match stored external_person_id values.

- Event settings: Exhibits section replaced with config link + raw JSON
  fallback, matching pres_mgmt/badges pattern.

- Docs updated: README.md, MODULE__AE_Events_Exhibitor_Leads.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 12:36:36 -04:00
Scott Idem
48c5515131 Updated to do list 2026-04-03 01:39:55 -04:00
Scott Idem
c8eb904eb0 Minor style fix and char change 2026-04-02 21:34:49 -04:00
Scott Idem
d80202e35b Style clean up. Making things nicer again. 2026-04-02 21:31:58 -04:00
Scott Idem
055bbd9ffd events(settings): add modules config page and settings link 2026-04-02 20:01:15 -04:00
Scott Idem
0e0fc071c7 events: center module hub cards (flex-wrap + fixed card width) 2026-04-02 19:58:25 -04:00
Scott Idem
5971ca6143 fix: use file_count_all + is_null for sessions-without-files query
Two corrections to the qry_files filter:
1. Switch from file_count to file_count_all — covers files on presentations
   and presenters under the session, not just direct session files.
2. Switch "without files" from eq:0 to is_null — the view uses a LEFT JOIN
   so sessions with no files get NULL, never 0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 19:08:43 -04:00
Scott Idem
cf7203daaf fix: implement qry_files filter in search__event_session (sessions with/without files)
qry_files was accepted as a parameter but never applied to the search query,
causing the "Sessions With/Without Files" report toggle to always return all
sessions regardless of the setting.

When qry_files !== null, automatically switch to the 'alt' view
(v_event_session_w_file_count) which exposes file_count, then add:
  true  → file_count > 0  (sessions with files)
  false → file_count = 0  (sessions without files)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 18:58:39 -04:00
Scott Idem
0ca2408111 Updated to do list again 2026-04-02 18:44:56 -04:00
Scott Idem
62ae376e67 chore(pkg): add runed (PersistedState) to fix svelte-check 2026-04-02 18:41:18 -04:00
Scott Idem
034e25d6c4 chore(pkg): remove orphaned shadcn-svelte and bits-ui 2026-04-02 18:38:32 -04:00
Scott Idem
08fdb2bddf chore(docs): prune fully-completed subsections from TODO__Agents.md 2026-04-02 18:26:44 -04:00
Scott Idem
84875d1daa Moving older files around. 2026-04-02 18:21:39 -04:00
Scott Idem
09757d249c chore(docs): archive completed TODO items to TODO__Agents__ARCHIVE_2026-03.md and tidy main TODO 2026-04-02 18:20:03 -04:00
Scott Idem
fae4bba037 Commenting out alert/notice for now. 2026-04-02 18:19:13 -04:00
Scott Idem
7b2694e9b7 fix(lead): remove invalid 'fill' prop from Star icon 2026-04-02 18:15:52 -04:00
Scott Idem
e27ff2c67f Limit who can create a new badge. This may need to change later. 2026-04-02 18:11:54 -04:00
Scott Idem
c198ca2454 chore(badges): remove legacy badge_id_only_search; sync remote badges config into badges_loc; docs update 2026-04-02 18:03:23 -04:00
Scott Idem
0ab8b936ce badges(runtime): honor mod_badges_json flags (badge_id_only search, QR toggle, add/upload/mass-print gating) 2026-04-02 17:23:35 -04:00
Scott Idem
4a5b4bf7cd badges(config): fix duplicate keys and initialize draft when mod_badges_json missing; update settings button style 2026-04-02 17:06:23 -04:00
Scott Idem
1935564645 Quick version bump again. 2026-04-02 16:30:34 -04:00
Scott Idem
fface58751 fix: add default_qry_str to db_events Event interface, remove incorrect global augment
The field exists on the DB object but was missing from the TypeScript interface,
causing a false error in recovery_meetings search. Added it to db_events.ts where
it belongs. Removed the incorrect global DOM Event augment from the temp augments
file (was patching the wrong interface).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 16:04:49 -04:00
Scott Idem
4a1b0dac86 pres_mgmt config: remove legacy launcher option, add back button + dirty state to config UI
- Remove show__launcher_link_legacy from PressMgmtRemoteCfg, PresMgmtLocState, and
  pres_mgmt_loc_defaults — the Flask/legacy launcher is retired
- Sync function now hardcodes hide__launcher_link_legacy=true (always hidden)
- Config page: back button to pres_mgmt, save buttons disabled until changes made
- Fix {#each} key expressions in config page
- Migrate e_app_access_type and element_manage_event_file_li to pres_mgmt_loc store
- Add temporary svelte type augments file (src/types/)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 15:53:55 -04:00
Scott Idem
fd9e5f6dc0 pres_mgmt: migrate to typed PersistedState store, canonical config schema
Replaces untyped $events_loc.pres_mgmt (svelte-persisted-store) with a
dedicated pres_mgmt_loc (runed PersistedState) backed by a fully typed
PresMgmtLocState interface and PressMgmtRemoteCfg for the server-side JSON.

Key changes:
- ae_events_stores__pres_mgmt_defaults.ts: canonical interfaces + defaults
  covering all hide__/show__ fields, labels, report prefs, query filters,
  and lock_config sync fields; qry_enabled uses 'not_enabled' (matches API)
- ae_events_stores__pres_mgmt.svelte.ts: new PersistedState store
- ae_events__event.ts: sync_config__event_pres_mgmt() rewired to write
  directly to pres_mgmt_loc.current; launcher link inversion preserved
- All 26+ pres_mgmt templates migrated from $events_loc.pres_mgmt.* to
  pres_mgmt_loc.current.*
- New config UI at (pres_mgmt)/pres_mgmt/config/ — manager + edit mode only
- Event settings page: removed embedded pres_mgmt form, links to config page
- event_page_menu: Config button visible only when manager_access + edit_mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 15:27:12 -04:00
Scott Idem
21f0fe69af Quick version bump 2026-04-02 15:03:01 -04:00
Scott Idem
01c895f7ba feat(pres_mgmt): make session start/end datetime editable in edit mode
start_datetime and end_datetime were visible as chips but had no edit control.
Added two datetime-local field editors shown in edit_mode below the display chip:

- Converts stored "YYYY-MM-DD HH:mm:ss" → "YYYY-MM-DDTHH:MM" for the input
  (safe because dayjs has no timezone plugin — times are stored as local time)
- Falls back to event start date + 08:00/09:00 when session datetime is null,
  so staff only need to adjust the time rather than retype the full date
- Editors are side-by-side in a flex-wrap row with min-width so they wrap on mobile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 13:56:06 -04:00
Scott Idem
3a4c4a1e64 feat(pres_mgmt): make session code editable in edit mode
The code badge was display-only — replaced with a field editor so staff
can correct session codes without going to a separate admin view.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 13:46:39 -04:00
Scott Idem
75664ad2e1 feat(pres_mgmt): restore location and description editing in session view
Event location (FK lookup) and description were both visible in the session
view but had no edit controls — lost during V3 migration. Restored both:

- event_location_id: select dropdown populated from this event's location list
  (liveQuery on db_events.location filtered by event_id from the session object)
- description: textarea editor shown directly in edit_mode (no collapse needed
  when actively editing)

Also added event_location_id to editable_fields__event_session, which was
missing and would have caused backend rejections on PATCH.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 13:44:00 -04:00
Scott Idem
2a5adda6cb idaa/video_conferences: restrict invite button to trusted_access staff only
The Jitsi invite dialog can expose backend room URLs and paths.
Previously invite was gated on is_moderator (any Novi group moderator).

Now restricted to $ae_loc.trusted_access (IDAA staff in Aether) so
regular member moderators cannot send invites. All other toolbar
buttons are unchanged.
2026-04-02 13:27:05 -04:00
Scott Idem
be3634d750 No longer allow regular attendees to send an invite. The moderators may be next. 2026-04-02 13:11:23 -04:00
Scott Idem
fd5d5e371b idaa/video_conferences: issue JWT to all verified Novi users
Previously only moderators received a JWT; non-moderators joined
anonymously. Now all verified Novi users get a JWT with the
is_moderator flag set appropriately, allowing the Jitsi server to
enforce authentication and respect context.user.moderator for
all participants.

Also adds JWT payload decode logging (client-side, signature not
verified) so the moderator flag and user identity can be confirmed
in the browser console during testing.
2026-04-02 12:51:05 -04:00
Scott Idem
75d85bf904 Working through bugs... Related to data stores and not using the for type and for id... They were locked. 2026-04-01 19:39:26 -04:00
Scott Idem
5e0f35d3df Working on security defaults and layout of menus. 2026-04-01 19:18:38 -04:00
Scott Idem
0767e2ff82 More cosmetic and permissions review 2026-04-01 18:41:23 -04:00
Scott Idem
38c5345060 Making things look nicer. 2026-04-01 18:09:17 -04:00
Scott Idem
601bcf94b0 Added an extra backup just in case Edit Mode toggle. 2026-04-01 17:43:36 -04:00
Scott Idem
197d136c59 Fixing stuff that was lost with the upgrade to AE v3. 2026-04-01 17:11:21 -04:00
Scott Idem
7d8981bcb5 Version bump just because. I think things are working well from a technical standpoint. 2026-04-01 16:52:10 -04:00
Scott Idem
828a2a0b10 Making things work and look a little nicer. Missing business logic and functions. 2026-04-01 16:50:37 -04:00
Scott Idem
665eb48280 fix(events): show session codes by default
hide__session_code was defaulting to true, suppressing the code badge
in the session list on fresh sessions. Flip to false so codes are
visible out of the box — users can still hide via the menu toggle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 16:43:27 -04:00
Scott Idem
d12a4bf71f feat(events): restore inc_file_counts opt-in, session list layout + button polish
- Add `inc_file_counts` flag to `load_ae_obj_id__event_session` — maps to
  backend alt view (v_event_session_w_file_count) when true; default stays
  lightweight. Callers never pass raw view names.
- Preserve-on-write fallback in `_refresh_session_id_background` keeps
  cached file_count/file_count_all if API response omits them.
- Session detail +page.ts uses `inc_file_counts: true` so SvelteKit prefetch
  no longer clobbers counts via bulkPut on hover.
- Remove explicit `view: 'alt'` from launcher +page.ts (now invalid param).
- Session list link: flex-1 + min-w-0 for full-row width; name flex-1 pushes
  badge group right; code + file_count stacked in flex-col items-end.
- Hover styling: button-like appearance with slow fade-out (duration-500) /
  fast snap-in (hover:duration-150).
- Session +page.svelte: use url_session_id (string) for link_to_id props and
  auth__kv.session[] index — fixes TS type error from number|undefined.
- IDAA layout: dormant tech notice banner (guarded by 1==3, remove when ready).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 16:38:13 -04:00
Scott Idem
214fca3713 fix(auth): disable access_key check — always grant access
Access keys cleared from all site_domain records. Bypassing the entire
key verification block to unblock IDAA. TODO: restore when keys are re-added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:59:24 -04:00
Scott Idem
802d0ec368 fix(idaa): preserve Novi session on internal iframe navigation
When navigating within the iframe (e.g. meeting list → meeting detail),
the UUID is only present on the initial iframe src URL — internal SvelteKit
<a href> links don't carry it forward. The layout effect was unconditionally
clearing novi_verified on every navigation that lacked a UUID, causing
"Access Denied" on every internal link click.

Fix: if a valid TTL-cached Novi session exists when no UUID is in the URL,
treat it as internal navigation and preserve the session rather than wiping it.
Non-Novi paths (no session, no UUID) still clear and deny as before.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:55:36 -04:00
Scott Idem
113aae23a7 fix(auth): preserve key string in key_checked to prevent access denied on navigation
key_checked was set to boolean true in Case 3, which +layout.svelte then
persisted back to localStorage. On the next keyless navigation, the check
true === 'actual-key-string' always failed, causing Access Denied after
just one internal page navigation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:44:34 -04:00
Scott Idem
62e1115b05 style(layout): add RefreshCw icon to offline/retry buttons, adjust error banner color
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:33:26 -04:00
Scott Idem
63ec7f4cc2 feat(auth): persist verified access key to allow keyless internal navigation
Sites requiring a ?key= param (e.g. IDAA Novi iframe pages) no longer need
the key appended to every internal link after the first successful verification.
Stored key is always validated against the current site config from the API —
stale or rotated keys are denied immediately. Key present in URL always takes
the strict live-validation path with no cache shortcut.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:33:21 -04:00
Scott Idem
8fabaf28f7 fix(idaa): preserve default sound mute settings when URL params absent
Unconditional assignment was overwriting $state defaults (incoming msg,
reactions, raise hand all muted) with false whenever the iframe template
didn't pass the sound URL params — which it never does.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 19:04:11 -04:00
Scott Idem
f1bce485ab fix(idaa): revert JWT to moderators-only pending Jitsi server config
Temporary rollback — non-moderators rejoin anonymously until Prosody is
configured with allow_empty_token=false to enforce JWT moderator claims.
TODO comment left in place to track the follow-up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:52:00 -04:00
Scott Idem
58dbb68601 Minor documentation update 2026-03-31 17:53:31 -04:00
Scott Idem
9b0c05b80c fix(idaa): require JWT for all Novi users, remove embed meeting button
- Issue JWT to all verified Novi users, not just moderators; unauthenticated
  URL access no longer sufficient to join an IDAA video conference
- Remove 'embedmeeting' from Jitsi toolbar via explicit toolbarButtons whitelist;
  the embed dialog exposed the Jitsi host/room URL violating IDAA privacy rules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 17:53:09 -04:00
Scott Idem
ae4b94f1b2 fix(idaa): expand recovery_meetings search to use default_qry_str from API
Backend updated (2026-03-31) to return default_qry_str in event API responses.
Frontend now stores it via properties_to_save and searches it in both the local
Dexie fast-path filter and the secondary post-API client filter. Previously, the
server searched default_qry_str (e.g. day-of-week, recurring_text) while the
client only checked name/description/location_text -- causing local results to
drop valid matches on revalidation (e.g. searching 'Thursday').

Also adds TODO note to audit other event search pages for the same mismatch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 16:16:43 -04:00
Scott Idem
e6daf6b503 fix(bootstrap): validate access_key server-side, prevent stale cache bypass
When a URL access_key is present, skip the Dexie cache fast-path in
lookup_site_domain entirely — the key must be validated against the API.
Previously, a stale cached entry with a previously-valid key would be
returned immediately, allowing access even after the key changed or
was revoked in the URL.

Also: add site_domain_access_key to properties_to_save__site_domain
so domain-level keys are persisted to Dexie for cache validation;
remove shadow access_key re-declaration in +layout.ts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 15:07:41 -04:00
Scott Idem
84dc3dd158 feat(site): forward optional access_key from URL into site_domain search 2026-03-31 13:35:09 -04:00
Scott Idem
aa5ba8c9c6 docs: clarify access_key guidance; mark prod deploy completed in TODO__Agents.md 2026-03-31 12:57:43 -04:00
Scott Idem
c53a993bab Improved the open meeting externally buttons and style. 2026-03-30 20:06:23 -04:00
Scott Idem
d8ce04304b fix(idaa): re-verify UUID on SvelteKit navigation, not just full reloads
Root cause: url_uuid was read once from window.location.search (const),
assuming UUID changes always cause a full iframe reload (Novi impersonation).
Manual URL edits within the same SvelteKit session keep the layout mounted,
leaving url_uuid stale — the TTL cache then hit for the OLD valid UUID,
granting access under the wrong identity without re-verifying.

Fix:
- url_uuid is now $derived from $page.url.searchParams, updated on every
  SvelteKit navigation
- url_uuid is read outside untrack() in Effect 2 so UUID changes trigger
  a fresh verification run
- verify_failed (boolean) replaced with verify_failed_for_uuid (string|null)
  so the retry-loop latch is keyed to the specific failed UUID — a different
  UUID in the URL is always a clean slate that gets verified fresh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 19:43:12 -04:00
Scott Idem
525ce1db79 feat(idaa): add manual-copy fallback textarea to breakout modal
Clipboard API is blocked by default in many browsers when running inside
an iframe (requires explicit permission grant). IDAA members shouldn't need
to navigate browser settings to get a meeting link.

Added a readonly textarea below the two action buttons — click it to
select all, then Ctrl+C/Cmd+C. Works in every browser without any
permissions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 19:16:16 -04:00
Scott Idem
6559e3393c fix(idaa): close Jitsi fake-UUID access hole + add breakout modal
Security fixes (3 layers):
1. layout: verify_novi_uuid now rejects Novi 200 responses with no member
   data — prevents non-existent UUIDs from passing as verified members
2. layout: access gate now requires $idaa_loc.novi_verified in addition to
   novi_uuid (stale UUID alone was insufficient)
3. video_conferences: onMount guard aborts Jitsi init if the layout-verified
   UUID doesn't match the URL UUID (defense-in-depth)

Also fixes an infinite verification loop: when verification fails, writes to
$idaa_loc trigger storage events that cause $ae_loc to re-notify subscribers,
re-running Effect 2 indefinitely. Added verify_failed latch to stop retries —
the UUID is fixed for the page lifetime, retrying always produces the same result.

Feature: "Open Externally" button + modal (iframe mode only) lets IDAA members
escape the Novi iframe when scrolling/layout is broken. Options: copy link to
clipboard or open in new tab. Accessible to all users without edit-mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 19:15:21 -04:00
Scott Idem
702a7a73de docs: update architecture notes and TODO with Svelte 5 store migration plan
- AE__Architecture.md: minor wording fix
- TODO__Agents.md: add Svelte 4→5 store migration task (root cause of IDAA
  Novi re-auth bug; prerequisite for Phase 2c store refactor)
- PROJECT__Stores_Svelte5_Migration.md: new migration planning doc

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:49:29 -04:00
Scott Idem
847d89054d feat(idaa): show reset button if Novi verification stalls after 8s
If the "Verifying identity..." spinner is still visible after 8 seconds,
show an escape-hatch button that clears ae_loc + ae_idaa_loc from
localStorage and reloads — forcing a fresh site config fetch which
re-populates novi_idaa_api_key so verification can actually run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:45:17 -04:00
Scott Idem
0d49ff3b8d fix(stores): bump AE_LOC_VERSION to 2; add ae_idaa_loc version wipe
AE_LOC_VERSION 1→2: force-clears stale ae_loc localStorage on next page
load for all users. Fixes users stuck on "Verifying identity..." in the
IDAA iframe — their cached site_cfg_json predated novi_idaa_api_key being
added to the site record, leaving api_key null so verification never ran.

AE_IDAA_LOC_VERSION 1: ae_idaa_loc (Novi auth state) was never included in
store_versions.ts — no wipe mechanism existed for it. Added now so future
schema changes can be handled cleanly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:41:13 -04:00
Scott Idem
0e9a26cdca Another bug fix for IDAA and Novi verification. 2026-03-30 13:10:55 -04:00
Scott Idem
83e271a323 Version updates 2026-03-27 19:37:40 -04:00
Scott Idem
ace90ad043 docs(todo): document flowbite-svelte ModalProps errors and orphaned ShadCN packages
Records the root cause of the 2026-03-27 hidden-error discovery (broken ambient
declaration masking 31 pre-existing svelte-check errors), the lesson learned, and
two follow-up tasks: fix ModalProps.children across 26 files, remove shadcn-svelte
and bits-ui from package.json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:35:44 -04:00
Scott Idem
d139ed1bd0 fix(types): add aria-hidden to IconProps augment; remove orphaned ShadCN components
- lucide-augment.d.ts: add `aria-hidden?: string | boolean` to IconProps
  (SVGAttributes drops this too in @lucide/svelte ≥ 0.577.0)
- Remove src/lib/components/ui/ — ShadCN primitives with zero importers;
  bits-ui API drift was generating ~20 type errors for dead code

svelte-check: 31 errors remaining (all ModalProps.children — flowbite-svelte
API change, deferred to next session), 0 warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:32:24 -04:00
Scott Idem
3d988222a1 fix(types): move @lucide/svelte augmentation to module-context file
app.d.ts is a script-context declaration file. A `declare module 'x' {}`
in a script file is an ambient module declaration that completely replaces
the package's types — not an augmentation. This caused svelte-check to see
@lucide/svelte as exporting only IconProps, producing 1131 "class" errors
and 237 "no exported member" errors for every icon import.

Moving the augmentation to src/lucide-augment.d.ts with `export {}` makes
it a module file, so `declare module` becomes a proper augmentation that
merges with the package types. Result: Lucide errors drop from 1368 to 0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:15:08 -04:00
Scott Idem
5433a906bb fix(types): restore class prop on Lucide IconProps after 0.577.0 breakage
@lucide/svelte >=0.577.0 dropped `class` from IconProps — it now derives
props purely from SVGAttributes<SVGSVGElement>, which TS types without
`class`. Every <SomeIcon class="..." /> in the codebase errored (1131
errors). Augment IconProps in app.d.ts to re-add `class?: string`.
Root cause: 0.561.0 → 0.577.0 bump in commit 366c6629 (2026-03-10).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 18:35:52 -04:00
Scott Idem
d89218be15 feat(leads): implement Stripe payment component for exhibit licenses
Full implementation of ae_comp__exhibit_payment.svelte (was a 9-line stub).
Reads Stripe config from $ae_loc.site_cfg_json per-event. License tier
selector (1/3/6/10 users) uses {#key} remount pattern to work around
stripe-buy-button web component ignoring attribute changes after mount.
Three states: paid confirmation (priority=true), not-configured hint, payment
form. client_reference_id=exhibit_id ties payments to booth records.
TypeScript declaration for stripe-buy-button added to app.d.ts via
svelte/elements augmentation. exhibit_id prop wired in +page.svelte and
ae_tab__manage.svelte.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 18:29:12 -04:00
Scott Idem
a8e9bd6694 Updated to do 2026-03-27 17:04:56 -04:00
Scott Idem
6cd3b5f8f9 More notes and comments updates 2026-03-27 16:21:51 -04:00
Scott Idem
b33c1b16f6 fix(idaa): check UUID against trusted/admin lists directly for Jitsi moderator
$ae_loc.trusted_access is only ever upgraded, never downgraded — it sticks
across Novi impersonation even though a different UUID is in the URL. Instead,
check user_id directly against $idaa_loc.novi_admin_li / novi_trusted_li so
the moderator grant is tied to the specific UUID being used, not the inherited
session access level.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:17:38 -04:00
Scott Idem
d7a0857bed fix(idaa): load Jitsi external API script dynamically to eliminate race condition
<svelte:head> scripts load asynchronously with no lifecycle hook to await
completion, so onMount could call init_jitsi() before JitsiMeetExternalAPI
was defined. Replace with a dynamic script loader that is awaited between
fetch_novi_data() and init_jitsi(). Also uses the domain from URL params
rather than the hardcoded jitsi.dgrzone.com hostname.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:09:19 -04:00
Scott Idem
6939c058d8 Documentation updates 2026-03-27 14:53:28 -04:00
Scott Idem
b88a7de358 feat(idaa): trusted/admin users always get Jitsi moderator role
Rather than hardcoding the IDAA admins group UUID or making an extra
API call, re-use the access level already established by the IDAA layout.
If $ae_loc.trusted_access is set (verified against novi_trusted_li /
novi_admin_li), the user is a moderator immediately. Only regular
authenticated members fall through to the group membership check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 14:52:31 -04:00
Scott Idem
27f0bd21fb fix(idaa): fall back to site config group list when g_uuid not in URL
Older Novi pages that haven't been updated to pass g_uuid still need
the moderator check to work. Use [g_uuid] when present, otherwise fall
back to novi_idaa_group_guid_li from site config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 14:31:34 -04:00
Scott Idem
f111670f60 feat(idaa): use URL g_uuid for Jitsi moderator group check
Instead of checking membership across all groups in novi_idaa_group_guid_li
(site config), pass the single g_uuid from the URL param. Each Novi iframe
page supplies the group relevant to that specific meeting, so checking just
that one group is both more precise and avoids unnecessary Novi API calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 14:27:06 -04:00
Scott Idem
045efa71e1 fix(layout): show sys bar in iframe when show_menu=true for trusted users
The {#if} gate only allowed the sys bar to mount for admins or
trusted+edit_mode users in an iframe. Trusted staff using show_menu=true
had sys_menu.hide set correctly but the component never mounted. Add
!sys_menu.hide as an escape hatch so the URL override actually works.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 14:25:18 -04:00
Scott Idem
1e2c9d9b74 docs(idaa): document Novi API rate limits and backoff behavior
20 calls/sec, 600/min, 100k/day. Notes the 10s flat backoff + single retry
and the 5-min TTL cache that prevents normal-use rate limiting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 14:02:43 -04:00
Scott Idem
e64001cf63 fix(idaa): add 10s backoff retry on Novi API 429 rate-limit
On a 429 response, waits 10 seconds then retries once. If the retry also
returns 429, throws and denies access (Reload/Retry button covers that case).
verify_in_flight and novi_verifying stay true during the wait so the spinner
remains visible and no concurrent calls can sneak in.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 13:59:50 -04:00
Scott Idem
4137d8677d refactor(idaa): simplify Novi verification — remove reactive UUID, dedupe, rate-limit
UUID is set by Novi via iframe src at page load and never changes within a
session (impersonation = full iframe reload). Reading it once from
window.location.search eliminates reactive noise from SvelteKit client-side
navigation causing spurious re-verification runs.

Removed:
- verify_dep $derived.by (reactive UUID + site_cfg narrowing)
- dedupe snapshot + last_effect_* tracking variables
- verify_backoff_attempts and exponential backoff retry logic
- novi_rate_limited_until writes and UUID-change guards
- ~80 lines of complexity

Kept:
- site_cfg_json read outside untrack (effect still re-runs when API key loads async)
- verify_in_flight concurrency guard
- TTL cache (prevents duplicate calls on SWR site_cfg updates)
- All permission upgrade and store write logic

NOTE: If Novi adds dynamic impersonation (no full reload), see comment at
url_uuid declaration for what to restore.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 13:45:38 -04:00
Scott Idem
19d0145d00 fix(idaa): fix Novi UUID verification — stuck spinner, repeat calls, impersonation
Critical bugs fixed:
- $derived(() => {}) stored the function itself; uuid/api_key were always
  undefined so verification never fired. Fixed to $derived.by(() => {}).
- novi_verifying pre-initialized to true (flash prevention) was also used as
  the concurrency guard — guard saw it as in-flight and exited immediately,
  leaving the spinner stuck forever. Split into separate verify_in_flight flag.
- $idaa_loc reads in dedupe snapshot (outside untrack) subscribed the effect
  to idaa_loc writes, causing needless re-runs post-verification.
- Rate limit was not UUID-aware: 429 on one UUID blocked impersonation
  (new UUID). TTL and rate-limit guards now both bypass when UUID changes.

Also includes: store defaults for novi_verified_ts + novi_rate_limited_until,
docs update, iframe template g_uuid param (prior agent changes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 13:38:42 -04:00
Scott Idem
9d44b9341c Now with the ability to actually create a badge. We still need to make this look nicer. Buttons should look more like button and consistent with the other areas of AE Events Badges. Also take a look at the trigger updated fields. 2026-03-27 11:51:42 -04:00
Scott Idem
bc67ff5798 docs(todo): mark Zebra driver install and test data setup complete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 09:59:49 -04:00
Scott Idem
f87ab10251 feat(badges): add manual one-off badge create modal
Two-step creation: POST event_person first, then event_badge linked to it.
Badge create route (event_person parent) pending backend fix — frontend is
ready and passing event_person_id + event_badge_template_id in payload.

- ae_events__event_person.ts: new create function (nested under event)
- ae_events_functions.ts: export create_ae_obj__event_person
- ae_comp__badge_create_form.svelte: modal form with live name preview,
  conditional display-name override, template selector (auto-selects when
  only one template), badge_type_code_li derived from selected template's
  badge_type_list JSON, two-step submit status labels
- +page.svelte: load template list via liveQuery, wire Create Badge button
  (edit_mode only), native <dialog> modal with backdrop, remote-first
  refresh on success

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 09:59:46 -04:00
Scott Idem
35c4341c34 docs(todo): add DevOps items — prod deploy, Bitbucket token migration, branch strategy, Gitea webhook
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 19:21:03 -04:00
Scott Idem
bd5759f037 docs(readme): update build/deploy section for new script names and env tiers
Replace stale deploy:staging/deploy:prod references with current
build:docker:*, deploy:remote:*, and .env.dev/test/prod file names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:05:18 -04:00
Scott Idem
872e381c00 Added missing required var for API path 2026-03-26 17:54:00 -04:00
Scott Idem
64402e8e2a chore(scripts): rename deploy:* → build:docker:*, add deploy:remote:*
- deploy:dev/test/prod → build:docker:dev/test/prod to distinguish
  local Docker builds from remote server deploys
- Add deploy:remote:test and deploy:remote:prod — SSH to linode.oneskyit.com
  and run deploy.sh on the server
- Trailing whitespace cleanup in .env.*.default files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 17:18:12 -04:00
Scott Idem
88b11b8318 Renaming files 2026-03-26 16:09:53 -04:00
Scott Idem
65e0477761 refactor(build): replace staging/cp env hack with vite --mode per-environment
- Rename .env.staging → .env.dev (and .default template)
- Add .env.test.default for the test tier (test-api.oneskyit.com)
- build:staging → build:dev/test/prod using vite --mode <name>
- deploy:staging → deploy:dev; add deploy:test
- Dockerfile: ARG BUILD_MODE=dev; explicit .env.runtime copy per mode
- .dockerignore: rewritten (deduped); allow .env.dev/.env.test/.env.prod
- .gitignore: track .env.dev.default and .env.test.default
- Remove dead PUBLIC_AE_* imports from ae_stores.ts (ACCOUNT_ID, EVENT_ID,
  NO_ACCOUNT_ID_TOKEN, SPONSORSHIP_CFG_ID); sponsorship_cfg_id defaults to null
- Strip dead vars from .env.prod.default template (AE_CFG_ID, AE_APP_NODE_PORT,
  ACCOUNT_ID, EVENT_ID, SPONSORSHIP_CFG_ID, NO_ACCOUNT_ID_TOKEN)
- GUIDE__Development.md: build:staging → build:dev

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 16:07:31 -04:00
Scott Idem
98736ae1bc chore(env): scrub real account IDs from .env.staging.default comments
The staging default template had real OSIT account_id and event_id values
in inline comments. These are not secrets but shouldn't be in a committed
template — they'd be misleading on any non-OSIT deployment.

Replaced with plain XXXX placeholders.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:08:18 -04:00
Scott Idem
7308a4773d docs(api): add V3 user actions section and clarify response shape
Added section 7 covering /v3/action/user/ endpoints: authenticate, verify_password,
change_password, new_auth_key, email_auth_key_url — including the legacy→V3
migration table and auth key one-time-use behavior.

Also clarified the response shape note to explicitly list all response types
(GET single, GET list, POST create, PATCH, search) that use the V3 envelope.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:05:39 -04:00
Scott Idem
99541f0f9d fix(api): add explicit fetch CORS options and response header debug logging
Added mode, credentials, redirect, and cache options to the GET fetchOptions
object. These were previously left to browser defaults, which vary by environment
and can produce opaque CORS failures that are hard to diagnose. Being explicit
avoids environment-dependent surprises.

Also added a try/catch around response.headers logging (log_lvl >= 1) so header
dumps don't throw in environments that restrict header access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:05:31 -04:00
Scott Idem
f950c22a59 fix(clip-video): correct false 'Clipped' state on network failure + error UI
get_object() returns false on network failure; the .then() handler was
running with result=false and accessing result.hosted_file_id (evaluates
to undefined, valid JS key, no throw) so all success state was set even
though the request failed.

- Guard result in .then(): if !result.hosted_file_id → set status='error'
- Add 'Failed — Retry?' button state in error branch
- Raise client-side AbortController timeout 300s → 1800s (30 min)
- Add comment explaining root cause (get_object returns false, not throw)

Root cause of the connection drop is proxy_send_timeout or NAT hairpin
timeout (both default 60s) — not a frontend issue; tracked separately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:16:07 -04:00
Scott Idem
b63f8eed0c Work on IDAA and Novi auth 2026-03-25 21:13:27 -04:00
Scott Idem
929f08b656 docs: add IDAA auth test lessons and untrack() reactive tracking guide
tests/README.md — new "IDAA Auth Tests" section with three lessons:
  1. ae_idaa_loc seed must include full bb/archives structure or
     verify_novi_uuid() throws silently and resets novi_uuid to null
  2. StorageEvent pattern for testing reactive persisted-store updates
     without pre-seeding Dexie or navigating twice
  3. getByText { exact: false } for UUID in multi-field spans

GUIDE__SvelteKit2_Svelte5_DexieJS.md — new "untrack() reactive tracking
trap" section: reading a store value inside untrack() makes it a one-shot
dependency; fix is to hoist the read outside untrack() and add a guard
to avoid redundant work on unrelated store updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 19:07:07 -04:00
Scott Idem
48a39b16d5 test(idaa): add Playwright auth tests for Novi UUID verification
Covers 5 scenarios with extensive inline comments explaining business
context and the 2026-03-25 stale-cache root-cause fix:

1. Auth gate (Sev-1 regression guard) — no UUID → Access Denied
2. Happy path — valid UUID + fresh cfg → access granted
3. Invalid UUID — Novi 404 → Access Denied
4. Stale cache — StorageEvent delivers fresh site_cfg_json →
   Effect 2 retries verification without reload (tests the reactive
   tracking fix in (idaa)/+layout.svelte)
5. iframe mode — Reload/Retry button visible on Access Denied

Key lesson found while writing: ae_idaa_loc seed must include the full
bb object or verify_novi_uuid() throws on bb.qry__hidden assignment,
caught silently, resetting novi_uuid to null even after a successful
Novi API call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 19:00:03 -04:00
Scott Idem
ab294c2a0b Sorry. Quick save to make something live before deadline. 2026-03-25 18:31:39 -04:00
Scott Idem
1de563203d fix(idaa): add reload button to Access Denied screen in iframe mode
WHY: Novi UUID verification is async — on first iframe load the API call
may not complete before the access gate renders, leaving the user stuck on
Access Denied with no way to retry without manually reloading the host page.
The Reload/Retry button calls location.reload() to re-trigger verification.
Only shown in iframe mode where the timing race is the known failure path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 18:04:53 -04:00
Scott Idem
0091fe3ff6 Updates to the documentation about the id_random legacy. 2026-03-25 17:43:15 -04:00
Scott Idem
0ad36a74b2 Fix: system bar hide logic for iframe and menu param overrides (IDAA embed reliability) 2026-03-25 15:49:41 -04:00
Scott Idem
fd244720a7 Update to AE API v3 for the hosted file hash check. 2026-03-25 13:17:25 -04:00
Scott Idem
362136e677 fix(upload): update clip_video endpoint to V3 action path
The legacy /hosted_file/{id}/clip_video route was decommissioned with the
rest of the hosted_file router. Updated to /v3/action/hosted_file/{id}/clip_video.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:30:23 -04:00
Scott Idem
a5a806e256 fix(upload): update hosted file upload endpoint to V3 action path
The legacy /hosted_file/upload_files router was decommissioned (commented
out in registry.py). Both upload components now point to the active V3
endpoint at /v3/action/hosted_file/upload. Response shape is identical
so no consumer-side changes needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 12:11:00 -04:00
Scott Idem
613e43114c fix(idaa): correct reactive loop fix + hide clutter in iframe sys bar
1. Replace incorrect untrack() with idempotent write guard in the
   sys_menu trusted-access effect. untrack() prevents new dep reads but
   ae_loc was already tracked from the outer condition reads, so the write
   still re-notified the effect every run. The guard (only write if value
   != false) breaks the cycle: run 2 finds value already false, skips the
   write, effect stops. Max 2 runs vs the previous infinite loop.

2. Hide auth shield, font-size cycler, and dark/light toggle in the sys
   bar when in iframe mode — host page owns those concerns. Edit mode
   toggle and the main expand button remain visible for staff.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 11:39:24 -04:00
Scott Idem
1c818e648b fix(idaa): break sys_menu reactive loop + restore menu on iframe=false
Two fixes in the IDAA root layout:

1. Add missing `untrack` import and wrap `$ae_loc.sys_menu.hide = false`
   in `untrack()` inside the trusted-access effect. Without this, reading
   $ae_loc.iframe/$ae_loc.trusted_access and then writing back to $ae_loc
   caused an infinite reactive loop → effect_update_depth_exceeded error.
   Only hit by trusted/admin users in iframe mode (regular Novi members
   at authenticated_access were unaffected).

2. When iframe URL param is explicitly set to 'false', restore
   $ae_loc.sys_menu.hide = false. The root layout sets it to true on
   iframe=true but never resets it, leaving the system bar permanently
   hidden after leaving iframe mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 11:20:44 -04:00
Scott Idem
5cd1d3b7ad feat(idaa): auto-show sys menu for trusted users in iframe mode
Trusted admins embedded in the Novi iframe can't append show_menu=true
to the src URL, so watch trusted_access reactively and unhide the sys
bar automatically when they authenticate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 22:29:01 -04:00
Scott Idem
66f0efb507 fix(store): guard localStorage calls for Node/SSR builds 2026-03-24 16:46:52 -04:00
Scott Idem
a637343544 chore(ci): add Docker BuildKit examples, .dockerignore, CI cache docs; tune vite config 2026-03-24 16:32:45 -04:00
Scott Idem
a8f3c29b9f Last round of prettier: npx prettier --write src/ 2026-03-24 13:27:40 -04:00
Scott Idem
23d25bf65a Prettier for everything else left 2026-03-24 12:28:28 -04:00
Scott Idem
12a9472064 Prettier for IDAA pages only 2026-03-24 12:28:07 -04:00
Scott Idem
b74c6d0e9c Prettier for Journals 2026-03-24 12:25:22 -04:00
Scott Idem
e1338b1a72 Other areas of the AE SvelteKit primary routes. 2026-03-24 12:18:27 -04:00
Scott Idem
6018a94499 Prettier for Events as a whole. Everything else under that primary directory. 2026-03-24 12:16:44 -04:00
Scott Idem
6e67534454 Prettier for Event ID 2026-03-24 12:16:11 -04:00
Scott Idem
693486bac9 Prettier for Event Pres Mgmt 2026-03-24 12:15:01 -04:00
Scott Idem
6d1d1e2658 Prettier for Event Exhibitor Leads 2026-03-24 12:14:30 -04:00
Scott Idem
7f6e286b73 Prettier for Event Launcher 2026-03-24 12:13:59 -04:00
Scott Idem
a3ed379b17 Prettier for Event Badges 2026-03-24 12:13:37 -04:00
Scott Idem
e9379be5a1 Now even prettier with the new Tailwind CSS plugin. Probably should have done this long ago... 2026-03-24 12:11:25 -04:00
Scott Idem
9a75243d9c Making the code easier to read and more consistent. 2026-03-24 12:05:22 -04:00
Scott Idem
94849137f0 I think pretty much all references to v1 and v2 have been removed. All files have been renamed from _v3 to just the function/var name with out the appended version. Assume no _vX is the current version. 2026-03-24 11:32:06 -04:00
Scott Idem
512e5ef87c Saving more code clean up and removal 2026-03-24 11:15:01 -04:00
Scott Idem
d27ec58fe9 More code clean up 2026-03-24 10:56:31 -04:00
Scott Idem
42358efe7d More code clean up 2026-03-24 10:54:40 -04:00
Scott Idem
8e61bd0ba1 More and more code removal and clean up 2026-03-24 10:42:40 -04:00
Scott Idem
0bc71391fc Cleaning up and removing old legacy code and files 2026-03-24 10:28:54 -04:00
Scott Idem
6e22639e6e fix(api): pass real account_id for lookup requests instead of bypass header
The x-no-account-id bypass was hardcoded to resolve account_id=1 on the
backend, causing account-scoped lookup overrides (e.g. custom country names)
to leak to all callers regardless of their account.

Removing the bypass lets get_object auto-promote the real account_id from
api_cfg, so the backend's existing account filter works correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 20:00:28 -04:00
Scott Idem
a6f8ff709e fix(idaa): fix country/subdivision/timezone dropdowns — switch to in-memory sort
- Country and state/province fields were showing as plain text inputs because
  liveQuery used orderBy() on non-indexed columns, causing silent Dexie errors
  that left the store as undefined indefinitely.
- Fix: replaced orderBy() with toArray() + in-memory sort across all three
  lookup types (country, country_subdivision, time_zone).
- Sort convention matches Aether backend: sort DESC (higher = first, NULL=0
  last), then name ASC — puts priority entries at the top.
- Added db_lookups.ts (IDB schema for lookup tables) and updated core__countries,
  core__country_subdivisions, core__time_zones to IDB-backed SWR pattern.
- Affected: archive edit, archive content edit, recovery meeting edit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 18:44:24 -04:00
Scott Idem
dafe79b3c6 ui(idaa): keep required asterisk inline with label text (embed in inline flex) 2026-03-23 18:23:24 -04:00
Scott Idem
a4927d37bd Updated documentation 2026-03-23 18:01:34 -04:00
Scott Idem
f3ab1c1050 fix(idaa/recovery_meetings): fix weekday chips, recurring fields, and timezone lookup
- Weekday chips: replace bind:checked (unreliable with dynamic bracket notation in
  {#each}) with explicit onchange handlers + class: directives; read weekdays from
  state in submit handler instead of FormData
- Recurring pattern/times: bind select and time inputs to working copy
  so values display and edit correctly
- Times clearing: map empty string to null so times can be cleared once set
- liveQuery guard: skip event_obj sync while edit form is open to prevent
  background refresh from overwriting in-progress user changes
- Timezone lookup: forward order_by_li, limit, offset through the full call chain
  so priority sort and result count params are actually sent to the API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 16:05:16 -04:00
Scott Idem
5bed167829 Bug fix for saving Zoom info. Removed more old commented out references to on: Svelte 4 code. 2026-03-23 14:27:46 -04:00
Scott Idem
a14320d9ed idaa(recovery_meetings): sanitize Zoom encrypted passcode to avoid saving literal 'null' and normalize related fields 2026-03-23 14:23:29 -04:00
Scott Idem
de8a016bda Minor bug fix to render some icons in HTML text. :-) 2026-03-20 19:25:03 -04:00
Scott Idem
1c8997bd4f docs: update Exhibitor Leads module doc — confirm modes, re-enable, capture identity
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 19:16:43 -04:00
Scott Idem
fe23899479 feat: leads QR UX — merged confirm modes, faster scanning, correct capture identity
- Merge Rapid + Qualify scan modes into single Confirm mode with two-button card:
  "Add & Scan Next" (resets) and "Add & View Lead" (navigates to detail). Same
  two-button pattern on the reenable card: "Restore & Scan Next" / "Restore & View Lead".
  Stale 'qualify' localStorage values normalized to 'rapid' via $derived.by().
- QR scanner speed: fps 10→25, qrbox 82%→88%, useBarCodeDetectorIfSupported (native
  BarcodeDetector API on Chrome/Edge — significantly faster than ZXing JS fallback)
- Fix capture identity stored in external_person_id / group:
  licensed exhibit user → their email; shared passcode → 'shared_passcode' label
  (not the raw passcode); Aether user bypassing exhibit sign-in → access_type string
  ('trusted', 'manager', 'super', etc.). Consistent across all three lead capture
  components (single scanner, multi scanner, manual search).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 19:15:35 -04:00
Scott Idem
6662e82f40 feat: leads re-enable flow — detect removed leads on scan + Remove/Restore buttons
- QR scanner (single + multi): detect previously-removed leads via IDB enable flag;
  route to 'reenable' state instead of duplicate error; offer Re-activate button
- API fallback: if create fails and no IDB record, search API for disabled tracking
  record by event_exhibit_id + event_badge_id (adds qry_badge_id param to
  search__exhibit_tracking)
- Lead detail page: Replace raw enable checkbox with Remove Lead (two-click confirm,
  navigates back after) and Restore Lead card (shown when enable is falsy)
- Fix flash of disabled records in leads list: filter !enable in both filtered_lead_li
  derived and local IDB fast-path in handle_search_refresh
- eslint.config.js: disable svelte/no-navigation-without-resolve (no base path configured)
- Also includes _random field annotation cleanup (db_events, ae_types), iframe layout
  fixes, badge view tweaks, test updates, and doc updates from prior session

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 18:18:10 -04:00
Scott Idem
4586e809d7 fix: leads scanner — handle falsy API result to prevent frozen 'adding' state
When create_ae_obj__exhibit_tracking returns false/null (API down, network
error, auth failure), the scanner was left frozen at 'adding' indefinitely.
Added else branch to surface an error state in both single and multi scanners.

Also fixes multi scanner which wasn't capturing the API return value at all.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 16:36:59 -04:00
Scott Idem
334c3a21bc feat: leads QR scanner — Auto/Multi modes, 4-mode fancy selector, UX polish
Scanner modes (now 4, persisted per exhibit):
- Rapid:   confirm tap → auto-reset (existing, fixed)
- Qualify: confirm tap → navigate to lead detail (existing, fixed)
- Auto:    badge found → auto-add immediately, no confirmation tap needed
- Multi:   BarcodeDetector batch scan → responsive grid of confirm cards

Multi scanner (new ae_comp__lead_qr_scanner_multi.svelte):
- Native BarcodeDetector API (Chrome/Edge/Safari 17+); Firefox fallback message
- 16:9 viewfinder with corner guides + "Align up to 4 badges flat" overlay
- Capture Batch tap → up to 8 QR codes detected in one frame
- Per-card states: loading skeleton, ready (Add/Skip), blocked (opt-out),
  already-captured (View/OK), adding spinner, success (auto-fade), error
- Add All (N) bulk action; cards fade+scale out smoothly on dismiss

Mode selector (ae_tab__add.svelte):
- Replaces Rapid/Qualify toggle with collapsible 4-mode fancy select
- Trigger shows active mode icon (color-coded) + name + description
- 2×2 options grid expands on tap, closes on selection

QR scanner element (element_qr_scanner_v3.svelte):
- object-fit: cover eliminates 4:3 camera letterbox dead zone
- 7-second start timeout with actionable error message
- Starting/error overlays with high-contrast styling
- Try Again button with RefreshCw icon

Style guide updated: icon+text button rule (§8), btn/preset-filled workaround (§12)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 16:30:38 -04:00
Scott Idem
14c2635df4 docs + fix: Leads module doc, badges onsite doc, license access fix, QR log cleanup
- Add MODULE__AE_Events_Exhibitor_Leads.md — full module reference (auth model, tabs,
  data model, routes, offline/PWA notes, OSIT admin notes)
- Add MODULE__AE_Events_Badges_Onsite.md — onsite printing guide (browser settings,
  CUPS/Linux setup, Zebra ZC10L section with test results, Epson stub, troubleshooting)
- Archive PROJECT__AE_Events_Exhibitor_Leads_v3*.md + Zebra test day doc → history/
- Fix leads license management access: ae_tab__manage.svelte license section now visible
  to administrator_access OR shared-passcode sign-in (type === 'shared'); matches spec
- Add type field to LeadsLocState.auth_exhibit_kv interface (was set at runtime but missing from type)
- Silence QR code console noise: entry log gated at log_lvl >= 2, data URL log removed
- vite.config.ts: exclude documentation/ and tests/ from Vite HMR file watcher

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 12:58:57 -04:00
Scott Idem
9673cbefe3 fix: PWA install prompt — capture beforeinstallprompt at module load time
The browser fires beforeinstallprompt very early (~1s after page load),
before Svelte's $effects run. Moving the event listener registration to
module level ensures we never miss the event regardless of when init()
is called from the root layout.

init() now only handles dismiss state (localStorage) and standalone
detection (DOM) — both safe to defer until after component mount.

Platforms:
- Chrome / Chromium / Android: native install button via captured prompt
- iOS Safari: manual Share → Add to Home Screen instructions (unchanged)
- Firefox desktop: no beforeinstallprompt support (browser-level limitation);
  Firefox shows its own install button in the address bar automatically

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:14:17 -04:00
Scott Idem
9a43879535 feat: leads — add show/hide hidden records toggle + update stale README gaps
- Add Eye/EyeOff toggle to exhibit tracking search bar; wired to
  existing $events_loc.leads.show_hidden store field
- Guard + init the show_hidden field in +page.svelte
- Add show_hidden to search_params so toggling triggers a refresh
- Apply hide filter in local IDB search path (skip hidden unless toggled)
- Pass hidden: 'not_hidden' | 'all' to API search__exhibit_tracking call
- Apply hide filter in filtered_lead_li for the broad liveQuery fallback path
- README: remove stale allow_tracking/PWA/export gaps; add Implemented section;
  fix ae_events_functions import path (moved to ae_events/ subdir)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:09:16 -04:00
Scott Idem
2c570c82dc docs: archive 4 completed project docs, update stale references
Archived to documentation/history/:
- PROJECT__AE_Firefly_Theme_Repair_SUMMARY.md (complete)
- PROJECT__AE_Pres_Mgmt_Session_view_refactor_2026-02.md (resolved 2026-02-26)
- PROJECT__AE_Access_Control_UX.md (all steps done 2026-03-11)
- PROJECT__AE_combined_front_back_Docker.md (complete 2026-03-10)

Pre-archive housekeeping:
- CLAUDE.md: removed 3 resolved active issues; replaced stale session bug doc link with Badges Task 4.0 doc
- AE__Architecture.md: corrected stale "TipTap marked for removal" note — both editors are active
- PROJECT__AE_Firefly_Theme_Repair_SUMMARY.md: added archival note re element_modal_v1 retirement

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:21:35 -04:00
Scott Idem
bf9aa9710c docs: add V3 field editor usage section to GUIDE__Development.md
Fulfills Phase 4 open item from PROJECT__AE_Object_Field_Editor_V3_upgrade.md.
Covers import, basic text usage, all field types, select with nullable FK,
key props table, and behavior notes (optimistic display, edit_mode visibility).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:09:19 -04:00
Scott Idem
942b3ddf5f docs: update component docs and check off completed Phase 3 datetime item
- PROJECT__AE_Object_Field_Editor_V3_upgrade.md: mark datetime support complete (was already implemented, just not checked off)
- AE__Components.md: replace stale ae_crud entry with element_ae_obj_field_editor_v3 description; correct TipTap "marked for removal" note (TipTap is actively used)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:06:24 -04:00
Scott Idem
bb0365f1e8 chore: move tiptap scss to styles/, trash orphaned codemirror files, update project doc
- element_tiptap_editor.scss → elements/styles/ (single importer path updated)
- Trashed element_codemirror_editor.svelte and element_codemirror_editor_wrapper.svelte (zero importers — active component is element_editor_codemirror.svelte)
- PROJECT__AE_Object_Field_Editor_V3_upgrade.md: mark v1/v2 removal complete, update status to 🟡 Mostly Complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:58:46 -04:00
Scott Idem
2b747de9bd chore: retire CRUD v1/v2 components — both were already unused
element_ae_crud.svelte and element_ae_crud_v2.svelte had zero active
importers; only a commented-out reference remained. Moved both to trash
and removed the dead comment from ae_comp__event_presentation_obj_li.svelte.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:54:33 -04:00
Scott Idem
519f5b949c chore: move ae_events_functions.ts into ae_events/ module
Relocates the functions file from lib root into its module directory,
matching the pattern used by all other modules (ae_journals, ae_archives, etc.).
Updated all 85 import paths from \$lib/ae_events_functions → \$lib/ae_events/ae_events_functions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:52:13 -04:00
Scott Idem
bf834aa165 chore: rename editor components and analytics to follow element_* convention
- AE_Comp_Editor_CodeMirror.svelte → element_editor_codemirror.svelte
- AE_Comp_Editor_TipTap.svelte → element_editor_tiptap.svelte
- analytics.svelte → e_app_analytics.svelte (matches e_app_* prefix of siblings)
- Updated all import paths; import variable names unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:49:57 -04:00
Scott Idem
0d960435f8 chore: remove orphaned legacy files and move QR scanner to elements/
- Trashed 10 unreferenced files: core .legacy types, bak files, element_modal_v1, element_websocket_v2, AE_MetadataFooter_not_ref, element_qr_scanner_v2
- Removed empty placeholder dirs: ae_db/, hooks/
- Moved element_qr_scanner_v3.svelte from lib root into elements/
- Updated 2 import paths for QR scanner (badges + leads)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:45:57 -04:00
Scott Idem
60461de9d9 fix: badge view debug bar — center text and reserve fixed height to prevent layout bounce
Add justify-center + h-6 to the debug info row above the badge so it stays centered
and doesn't cause vertical shift when conditional elements show/hide on edit mode toggle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 19:18:38 -04:00
Scott Idem
da3b8dcf46 feat: badge print controls — per-template field visibility and edit access via other_json.controls_cfg
Add field_shown() and field_editable() functions driven by event_badge_template.other_json:
  controls_cfg: { shown?: string[], auth_editable?: string[] }

Access rules:
  - No authenticated_access → display-only, no edit buttons shown
  - authenticated only → can edit fields in auth_editable (default: title/affiliations/location/allow_tracking/pronouns)
  - trusted + edit_mode → always sees and edits all fields, ignores config

Each attendee field card (name, title, affiliations, location, allow_tracking, pronouns)
is now wrapped in {#if field_shown()} and its edit button/accordion gated by field_editable().
No backend changes needed — other_json is an existing longtext JSON column.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 19:04:40 -04:00
Scott Idem
f628e7e3fc feat: badge print controls — quick print btn, compacted spacing, collapsible sections, overflow fix
- Add Quick Print button (30%) alongside canonical Print Badge (70%): calls window.print()
  only — no count increment, no navigation back to search
- Compact panel spacing: reduce space-y, pt/pb on card headers, standalone row py, font_ctrl py
- Add collapsible Attendee/Staff section groups reusing ctrl-accordion CSS pattern;
  attendee starts open, staff starts collapsed — auto-collapses the other on expand
- Add overflow-x-hidden to print page panel container to kill horizontal scrollbar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 18:47:35 -04:00
Scott Idem
fdd8691e2e feat: badge print — two-line name toggle + leading-none tightening + calibration SVG
- Add name_two_lines toggle (default: true) — uses CSS horizontal padding scaled by
  character count to coax short names (e.g. "Scott Idem") into a natural two-line wrap
  without a hard <br>; three tiers: ≤12 chars (18%), ≤20 (8%), ≤28 (2%), >28 no pad
- Inner <div> (block element) used inside Element_fit_text for class: directives —
  Svelte scoped CSS requires static class names in the template; dynamic strings and
  class: on component elements both fail to match scoped CSS rules
- Add leading-none to all four Element_fit_text fields (name, title, affiliations,
  location) — line-height must be set at the wrapper div level where fit_text measures
  scrollHeight, otherwise the binary-search scaler returns inflated sizes
- name_two_lines state persisted to localStorage (ae_badge_print_tweaks key) alongside
  existing print_offset, hide_chrome, and banner_full_width tweaks
- Rewrite badge_header_calibration.svg as a precise SVG ruler with labeled tick marks
  (major at 1in intervals, minor at 0.25in) for accurate physical print calibration
- Gate debug outline CSS on html.debug_outlines class (set by controls panel) so
  outlines never appear in normal print mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 17:55:49 -04:00
Scott Idem
621a637b85 feat: badge print UX improvements — chrome toggle, banner width, overlap fix, header centering
- Replace ae_comp__badge_obj_view_v2 with ae_comp__badge_obj_view (consolidated component)
- Add hide-chrome toggle ([H] shortcut + button) to hide site nav/footer/sys bar for clean print workspace
  — syncs $ae_loc.sys_menu.hide + $ae_sess.disable_sys_nav/footer with restore-on-unmount
- Add banner_full_width toggle (default true=100% width, false=natural pixel size for calibration)
- Center badge header image (display:block; margin:0 auto) — was left-aligned when narrower than badge
- Fix controls panel overlap: move from bottom-0 to bottom-24 to clear sys bar (84px tall)
- Add [H] keyboard shortcut for chrome toggle (guards against focus in inputs)
- Persist hide_chrome and banner_full_width in ae_badge_print_tweaks localStorage key
- Add sample header image assets (calibration SVG/PNG, hex blue SVG/PNG, demo PNG)
- Update badge PVC CSS layout and module docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:42:22 -04:00
Scott Idem
639e436854 docs: update tests/README and GUIDE__Development with current test patterns
tests/README.md:
- Add shared helpers table (_helpers/ files and purpose)
- Update "Writing / modifying tests" to reference setup_badge_test_page;
  badge tests are now the canonical template
- Add "Hard-Won Lessons — Badge Print / IDB Tests" section covering:
  - __version guard: why ae_defaults.ts must include __version: 1 or
    store_versions.ts silently wipes ae_loc and loses trusted_access
  - IDB inject-then-reload pattern: why reload is required (liveQuery
    won't fire on raw IDB writes that bypass Dexie's notification system)
- Fix pre-existing lint warnings: bare URLs → code spans, trailing newline

GUIDE__Development.md:
- Add Required Check #5: run Playwright integration tests for auth/store
  or badge print changes; badge tests are the canonical template
- Add tests/README.md to Key Documentation table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 17:12:36 -04:00
Scott Idem
81741919a8 refactor: extract seed_trusted_session + setup_badge_test_page into shared test helpers
All 4 badge test files had identical ~35-line beforeEach blocks (pageerror listener,
inline V3 route mocks, addInitScript localStorage seed). Replaced with two helpers
in minimal_v3_mocks.ts:

  seed_trusted_session(page, event_id, account_id?)
    — seeds ae_loc localStorage with trusted/manager auth via addInitScript;
      account_id defaults to testing_account_id

  setup_badge_test_page(page, event_id)
    — one-call beforeEach: pageerror listener + attach_minimal_v3_routes +
      seed_trusted_session

Each test file's beforeEach is now 1-3 lines. All 12 tests still pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 17:05:22 -04:00
Scott Idem
7f17b3b9a1 docs: update badge module docs — verify completed items, close stale TODOs
- MODULE__AE_Events_Badge_Templates.md: mark style_href, duplex, and
  badge_back suppression as done; correct v1→v2 component references
- PROJECT__AE_Events_Badges_Review_Print.md: update project status
- TODO__Agents.md: close debug-outlines, style_href, duplex TODOs;
  mark window.print() wiring as done

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 16:59:05 -04:00
Scott Idem
ca29e9c1b8 fix: gate debug outlines on html.debug_outlines class in print CSS
Debug outlines were applying to all print jobs. Now scoped to
html.debug_outlines so they only appear when the "Show debug outlines"
checkbox is active in the controls panel (trusted users only).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 16:59:00 -04:00
Scott Idem
af02e38528 test: badge E2E tests — fix __version wipe, extract idb_helpers, add render + workflow tests
Root cause fix: tests/_helpers/ae_defaults.ts was missing __version: 1, causing
store_versions.ts to wipe ae_loc from localStorage on every test page load. This
made trusted_access fall back to false, hiding the print button (can_print guard)
and failing all attendee workflow tests.

Changes:
- ae_defaults.ts: add __version: 1 with comment explaining the store_versions guard
- idb_helpers.ts: extract inject_badge_and_template() from print layout test into
  shared helper; now used by all three print/render test files
- event_badge_render.test.ts: new — 4 tests covering full_name_override priority,
  full_name fallback, duplex=0 hides badge back, duplex=1 shows badge back
- event_badge_attendee_workflow.test.ts: cleaned up (diagnostic code removed);
  all 3 tests now pass
- event_badge_print_layout.test.ts: renamed from badge_print_layout.test.ts;
  inline inject_idb() replaced with shared idb_helpers import
- event_badge_smoke.test.ts: renamed from event_badge.test.ts
- playwright.config.ts: use system /usr/bin/chromium on Arch Linux (avoids
  Playwright's bundled Chromium which requires Ubuntu libs not present on Arch)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 16:58:55 -04:00
Scott Idem
07d21c29c8 feat: add trusted-only debug outlines toggle to badge print controls panel 2026-03-18 13:05:17 -04:00
Scott Idem
ec5b09dfaa feat: hide AE menu by default in iframe mode; add show_menu override
iframe=true now hides the sys bar for all users (previously trusted_access
users still saw it). Admins can pass show_menu=true to re-enable it while
testing an embedded page like video_conferences.

hide_menu=true remains for non-iframe hide use cases (kiosk, etc).

Updated URL builder: hide_menu checkbox → show_menu checkbox.
Updated GUIDE__Development.md URL params table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 20:18:09 -04:00
Scott Idem
c1f96ba94e docs: update GUIDE__Development.md — add URL params, refresh stale content
- Add Section 6: full URL parameters reference (global + per-module)
- Update coordination section: message tool → ae_send_message MCP
- Expand Section 5 into a proper key documentation table
- Fix section numbering (Continuity was unnumbered)
- Bump version to 1.2 (2026-03-17)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 20:09:05 -04:00
Scott Idem
931df5581f refactor: rename ae_hide_menu URL param to hide_menu
ae_ prefix belongs on Svelte component/variable names, not URL params.
Updated both the consumer (+layout.svelte) and the builder (jitsi_url_builder).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 20:03:28 -04:00
Scott Idem
5978c5e341 fix: read ae_hide_menu URL param and apply to sys_menu.hide in layout
The URL builder generates ae_hide_menu=true but nothing consumed it.
Now layout.svelte reads the param on mount and sets $ae_loc.sys_menu.hide,
which flows through bind:hide into E_app_sys_bar's class:hidden.

Applies even for trusted_access users who bypass the iframe guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 19:55:36 -04:00
Scott Idem
93bd8ba962 feat: add hide_ae_menu toggle to Jitsi URL builder advanced panel
Adds ae_hide_menu=true query param option to suppress the AE navigation
chrome when embedding the Jitsi video conference page in Novi or other
host pages that provide their own navigation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 19:51:16 -04:00
Scott Idem
543dc3c300 feat(jitsi): add 'Hide AE system menu' embed toggle and URL param 2026-03-17 19:31:34 -04:00
Scott Idem
dcfeb99024 feat(idaa): add Jitsi URL Builder tool to reports page
New component ae_idaa_comp__jitsi_url_builder.svelte builds and previews
Jitsi iframe URLs for testing and Novi page configuration. Features:
- Environment selector (prod / dev / local / custom)
- Room name, Novi UUID, site key inputs
- Moderator toggle (explains JWT + logging implication)
- Advanced: domain, start muted/hidden, all 5 sound settings
- Output in URL or iframe HTML snippet mode with copy button
- "Open in new tab" for quick testing

Embedded on jitsi_reports page as a collapsible panel, gated to
trusted_access users only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 19:24:38 -04:00
Scott Idem
8693989a69 security: move jitsi_reports inside (idaa) auth gate
jitsi_reports was previously at src/routes/idaa/jitsi_reports/ and
was not protected by the (idaa) layout auth gate. Moved to
src/routes/idaa/(idaa)/jitsi_reports/ — same URL, now requires
trusted_access or Novi-verified authenticated access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 19:15:47 -04:00
Scott Idem
9fc3ee0198 fix(imports): point to element_data_store_v3 and restore Data Store v3; commit workspace updates 2026-03-17 18:57:27 -04:00
Scott Idem
3038be0686 chore: add IDAA guardrail to migrate_fa_to_lucide.py (abort if run on src/routes/idaa) 2026-03-17 13:40:14 -04:00
Scott Idem
ce09dcd09b Removing old code. Updated dev/test doc. 2026-03-17 13:20:26 -04:00
Scott Idem
adef935188 chore: aggressive cleanup: remove legacy element_data_store.svelte (v1) after v3 migration 2026-03-17 12:22:00 -04:00
Scott Idem
01fef4f113 chore: final recovery and integration of WIP improvements into ae_app_3x_llm 2026-03-17 11:12:22 -04:00
Scott Idem
e085a1d513 chore: remove broken/redundant components imported from WIP branch 2026-03-17 10:51:58 -04:00
Scott Idem
47f0ee0e81 chore: remove unused element_data_store_v2.svelte 2026-03-17 10:45:34 -04:00
Scott Idem
df34a85a89 cherry: import ae_comp__badge_obj_view.svelte (v1 with edit) from wip-modal-fix-attempt 2026-03-17 10:35:00 -04:00
Scott Idem
df8189cd66 cherry: import element_input_v2.svelte from wip-modal-fix-attempt 2026-03-17 10:32:59 -04:00
Scott Idem
cedf76344b cherry: import element_data_store_v2.svelte from wip-modal-fix-attempt 2026-03-17 10:32:39 -04:00
Scott Idem
1156e02e48 cherry: import element_modal_v1.svelte from wip-modal-fix-attempt 2026-03-17 10:29:37 -04:00
Scott Idem
6b49e2f036 wip(theme): repair AE Firefly theme vars (ensure light+dark ramps, !important background) 2026-03-17 10:29:25 -04:00
Scott Idem
0ffc72980c docs: add/update Firefly theme repair summary 2026-03-17 10:11:38 -04:00
Scott Idem
f92058d394 refactor: Remove all legacy variant-* class usage from Svelte components, enforce preset-* (Skeleton v4) everywhere 2026-03-16 19:01:46 -04:00
Scott Idem
0cebd3868b docs+utils: Remove all legacy variant-* class references, update all docs and snippets to use only preset-* (Skeleton v4) 2026-03-16 18:59:21 -04:00
Scott Idem
b543c8a930 chore: migrate all FA icons to Lucide (@lucide/svelte)
- Replaced all active FontAwesome <span class="fas fa-*"> icons with
  Lucide components across 145 files (excluding /idaa/ which is intentional)
- Fixed merge script bug: consolidated lucide-svelte imports into @lucide/svelte
- Replaced dynamic toggle patterns (fa-toggle-on/off) with ToggleRight/ToggleLeft
- Replaced fa-eye/fa-eye-slash with Eye/EyeOff
- Replaced fa-bug/fa-bug-slash with Bug/BugOff
- Replaced fa-sync fa-spin with RefreshCw + animate-spin
- Replaced fa-microchip with Cpu
- Fixed {@const} placement in element_manage_event_file_li.svelte
- Removed obsolete CSS hover rules for .unlock_icon/.lock_icon
- svelte-check: 0 errors, 0 warnings
2026-03-16 18:07:43 -04:00
Scott Idem
c9050264a5 fix: hide passcode input after successful validation
When a passcode matched, entered_passcode was cleared and the trigger
was set, but show_passcode_input was never set to false. This left the
input visible so users could keep typing after access was granted.

Set show_passcode_input = false immediately after clearing entered_passcode
on a successful match, consistent with the intent described in the
handle_clear_access() function which resets it to true on clear.
2026-03-16 17:54:52 -04:00
Scott Idem
8f88e043b3 Saving updated guide 2026-03-16 16:52:10 -04:00
Scott Idem
cf6aa1301b feat(leads): gate export button on leads_api_access flag
Export button now only renders when event_exhibit.leads_api_access === true,
preventing a 403 that would always fire otherwise. Endpoint confirmed live on
backend. TODO updated to reflect export + allow_tracking gate both resolved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 16:43:37 -04:00
Scott Idem
464b63c1a3 refactor(leads): export function → V3 action endpoint
Updated download_export__event_exhibit_tracking to call
/v3/action/event_exhibit/{exhibit_id}/tracking_export instead of the
legacy /event/exhibit/{exhibit_id}/tracking/export (V1 path).

Added doc comment listing the expected export columns so the backend
agent has the full field spec when implementing the endpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 16:05:18 -04:00
Scott Idem
be24cfdeb5 feat(leads): allow_tracking gate + icon bug fix + docs update
- QR scanner: after badge loads, blocks add with 'Tracking Opt-Out' warning
  card if allow_tracking !== true; replaced deprecated CheckCircle → CircleCheck
- Manual search: shows ShieldOff 'Opt-Out' label per row for blocked badges;
  add_as_lead() also guards against programmatic bypass
- Fix: ae_comp__exhibit_tracking_obj_li — Loader2 from wrong package
  @lucide/svelte → LoaderCircle from lucide-svelte
- ae_types.ts: added allow_tracking and agree_to_tc to ae_EventBadge interface
- README.md (leads): full rewrite reflecting actual current state and known gaps
- TODO__Agents.md: updated Leads entry from stale 'NEXT MAJOR FEATURE' to
  accurate in-progress status with remaining checklist

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 15:59:26 -04:00
Scott Idem
b8104b3ff2 feat(stores): Phase 2b — TypeScript interfaces for store defaults sub-files
ae_stores__auth_loc_defaults.ts
  SiteCfgJson, AePerson, AeUser, AccessType, AuthLocState

ae_events_stores__badges_defaults.ts
  BadgesLocState, BadgesSessState

ae_events_stores__launcher_defaults.ts
  SectionState, LauncherLocState, LauncherSessState

ae_events_stores__leads_defaults.ts
  LeadsLocState, TmpLicense, LeadsSessState

ae_events_stores__pres_mgmt_defaults.ts
  PresMgmtLocState, PresMgmtSessState

All exported consts annotated with their interface type.
svelte-check: 0 errors, 0 warnings
2026-03-16 15:52:19 -04:00
Scott Idem
41ed6a8adc refactor(stores): Phase 2a — split store defaults into domain sub-files
ae_stores.ts
- Extract auth/identity section (~80 lines) into ae_stores__auth_loc_defaults.ts
- Spread auth_loc_defaults into ae_app_local_data_defaults (zero consumer changes)

ae_events_stores.ts (both loc and sess structs)
- badges   → ae_events_stores__badges_defaults.ts
- launcher → ae_events_stores__launcher_defaults.ts
- leads    → ae_events_stores__leads_defaults.ts
- pres_mgmt → ae_events_stores__pres_mgmt_defaults.ts

Each new file exports *_loc_defaults and *_sess_defaults. The store files
now reference these by name instead of embedding inline objects. All
$ae_loc.* and $events_loc.* consumer paths are unchanged.

svelte-check: 0 errors, 0 warnings
2026-03-16 15:42:21 -04:00
Scott Idem
f4020e7834 refactor(stores): phase 1 cleanup — ae_idaa_stores.ts
- Remove unused `import { offset } from '@floating-ui/dom'`
- Remove ver_idb constant and field (same as ae_stores / ae_events_stores)
- Remove commented-out personal Novi UUIDs (security hygiene — these belong
  in site_cfg_json on the server, not in source; idaa layout already reads
  them from $ae_loc.site_cfg_json and writes to idaa_loc at mount)
- Add comment explaining novi_admin/trusted/jitsi_mod_li are server-driven
- Remove dead writable/persisted alternatives and console.log lines
- Remove stale 'Updated 20xx-xx-xx' date comments
- Condense redundant tracking comments to single-line form
2026-03-16 15:23:22 -04:00
Scott Idem
2a414e0401 docs: mark Launcher style migration as complete
Verified all FA spans and variant-* patterns in launcher files are inside
HTML comment blocks — no live FA or legacy variant classes remain.
The freeze was precautionary; the actual migration was already done.
Phase 3 for Launcher is now limited to UX/card styling polish only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 15:14:52 -04:00
Scott Idem
2a1c687d40 docs: update style review to reflect Phase 1 & 2 completion
Marked all non-frozen, non-IDAA style migration work as done:
- FA→Lucide across events, pres_mgmt, core, badges, leads, hosted_files
- variant-* → preset-* across all modules
- badge code_to_icon refactor, FA CDN scoped to IDAA layout
- global svg.lucide inline fix in app.css

Phase 3 deferred: Launcher (post-April 2026) and IDAA (last priority).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 15:09:54 -04:00
Scott Idem
e06833065e style: global Lucide SVG inline flow fix in app.css
Lucide renders <svg> elements which default to inline-block in browsers,
causing icons to break onto their own line when mixed with text — unlike
FA spans which were display:inline.

Added svg.lucide { display: inline; vertical-align: middle; } to app.css
so all Lucide icons flow inline with adjacent text globally, matching the
FA icon behavior without needing class="inline" on every icon instance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 15:08:24 -04:00
Scott Idem
998b10c5de style: FA→Lucide migration in remaining core admin files
- person_view.svelte: converted all FA spans including complex {#if} refactor
  of {@html} ternary string patterns (hide/enable/priority/auth_key buttons)
- people/[person_id]/+page.svelte: ArrowLeft, HelpCircle, LoaderCircle
- ae_comp__person_obj_tbl.svelte: Building2, ListOrdered, Mail, Unlink, User, UserCheck
- migrate script: added fa-archive, fa-link-slash, fa-question-circle mappings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 15:05:34 -04:00
Scott Idem
65291aa5b9 refactor(stores): phase 1 cleanup — ae_stores.ts and ae_events_stores.ts
- Remove ~200 lines of dead commented-out code (old test blocks, stale
  import alternatives, unused console.log lines)
- Remove ver_idb fields — superseded by store_versions.ts __version mechanism
- Remove Stripe button IDs / publishable key from events_sess (no consumers)
- Remove stale event-specific comments (CHOW 2024 sponsor tiers, ISHLT note)
- Remove example: true test field from session leads struct
- Improve comments: ver stamp rationale, auth__kv expiry intent, api_*_kv
  shape doc, slct/events_slct tab-isolation rationale, level_guest_max_li
  fallback note, events_trig_kv purpose
- Keep all active business logic and exports unchanged; zero consumer impact
2026-03-16 14:50:53 -04:00
Scott Idem
04aee814e1 style: scope FontAwesome CDN to IDAA layout only
Move the FA 5.15.4 CDN <link> from app.html (global) into
src/routes/idaa/+layout.svelte <svelte:head> so it only loads
on /idaa/* routes. All other modules now use Lucide exclusively.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 14:49:15 -04:00
Scott Idem
80baaa9d91 style: badge code_to_icon refactor + core variant-* → preset-* migration
badge ae_comp__badge_obj_view_v2.svelte:
- Replace FA HTML string dict (code_to_html) with Lucide component map
  (code_to_icon) — no FontAwesome dependency for dietary/option icons
- option_1 maps: Biohazard (generic/allergy), Utensils (dietary), Bone,
  Fish, Carrot for specific diets
- option_2 maps: Asterisk (generic flag), Hand (first-time attendee)
- Template: replace {@html option_other_*} with {@const Icon}<Icon /> pattern
- Back-of-badge: shows text label + inline icon

core/ (21 files):
- variant-soft-* → preset-tonal-* (6 variants)
- variant-filled-* → preset-filled-* (6 variants)
- variant-glass-surface → preset-tonal-surface (Skeleton v4 has no glass)
- bare variant-soft → preset-tonal-surface

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 14:42:28 -04:00
Scott Idem
37c9d830f6 style(badges): FA→Lucide migration in ae_comp__badge_obj_view_v2.svelte
Convert 6 template-level FA spans to Lucide components (Star, Biohazard,
Asterisk, Wifi). The code_to_html JS string dict (dietary symbols used
with {@html}) retains FA spans since they are raw HTML strings, not
Svelte template markup — FontAwesome CSS (app.html CDN) renders them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 14:30:30 -04:00
Scott Idem
478dedb898 style: FA→Lucide migration — events misc, badges, leads, hosted_files; variant-* fixes
- Batch-migrated 10 files via migrate_fa_to_lucide.py (53+18+10+1+4+2+1+1+6+4 FA instances)
  - events/ae_comp__events_menu_opts.svelte (53)
  - events/ae_comp__event_file_obj_tbl.svelte (18)
  - events/ae_comp__event_presentation_obj_li.svelte (10)
  - events/ae_comp__event_session_obj_tbl.svelte (1)
  - badges/print_list/+page.svelte (2), badges/templates/+page.svelte (1)
  - leads/ae_tab__manage.svelte (4)
  - hosted_files/+page.svelte (1), hold_video_util.svelte (6), video_util/+page.svelte (4)
- events/[event_id]/+page.svelte: converted JS icon strings to Lucide component refs
  (Presentation, Plane, IdCard, Contact) — rendered via <mod.icon size="2rem" />
- +page.svelte: hover:variant-outline-warning → hover:preset-outlined-warning (×2)
- migrate_fa_to_lucide.py: added 18 new icon mappings
  (ArrowLeft/Right, Ban, Broom→Trash2, calendar-alt, Database, DoorOpen, Download,
   exchange-alt, file-image, lock, magic→Sparkles, print, sticky-note, sync, tag,
   trash, user-ninja/tie→UserRound, video)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 14:25:18 -04:00
Scott Idem
31fe26df9c Saving project documentation. 2026-03-16 14:04:22 -04:00
Scott Idem
8db806c6ab style(pres_mgmt): Phase 3 — FA→Lucide icon migration across all 24 pres_mgmt files
Used scripts/migrate_fa_to_lucide.py to batch-replace all FontAwesome
<span class="fas fa-*"> icons with Lucide SVG components across the
entire presentation management module (273 icon instances, 69 icon types).

Manual fixes applied post-script:
- presenter_view.svelte: remove duplicate @lucide/svelte Pencil import
- presenter_page_menu.svelte: remove duplicate X from @lucide/svelte import
- ae_comp__event_presenter_obj_tbl.svelte: fix Lucide import inserted
  inside multiline import block (script bug with multiline imports)
- ae_comp__event_session_alert.svelte: same multiline import fix
Script updated with fix: use complete-statement matching for import
insertion instead of single-line matching.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 14:01:56 -04:00
Scott Idem
b44e77ad62 IDAA: inline Tailwind utilities — remove @apply style block (23 svelte-check warnings)
ae_idaa_comp__event_obj_id_edit.svelte: the component <style> block used
@reference + @apply for ~10 local classes (.section-card, .field-label,
.toggle-chip, .day-chip, etc.). svelte-check's CSS language service does
not understand Tailwind v4 @reference/@apply directives and emitted 23
'Unknown at rule' warnings.

Fix: all local class usages inlined as Tailwind utility strings directly
on each element (~80 template sites). The <style> block is removed.
Conditional classes on toggle-chip/day-chip converted to ternary expressions.

svelte-check now reports 0 errors and 0 warnings across all files.
2026-03-16 13:52:26 -04:00
Scott Idem
1291b225c6 Svelte: fix 3 svelte-check warnings (non-IDAA) + Playwright test type error
- ae_comp__badge_print_controls: select_ref_badge_type declared as $state()
  so Svelte 5 tracks DOM ref assignment correctly (was plain let).
- launcher_cfg_section: <svelte:component this={icon}> replaced with
  let Icon = $derived(icon) + <Icon /> — svelte_component_deprecated fix.
- launcher_file_cont: same svelte_component_deprecated fix for FileIcon.
- badge_print_layout.test.ts: inject_idb signature changed from (badge, template)
  to ({ badge, template }) — page.evaluate() passes exactly one argument;
  all three call sites updated to pass { badge: mock_badge, template: mock_template }.
2026-03-16 13:51:01 -04:00
Scott Idem
6ca2314472 Badges: fix print page svelte-check error — extract print CSS to static file
Svelte 5 does not support <style> or conditional {#if} blocks wrapping
<style> tags inside <svelte:head>. The parser treats them as raw-text
elements and reports '<script> was left open' at EOF.

Fix:
- Print media CSS moved to static/ae-print-badge.css (plain static file,
  no framework magic needed — all selectors target global elements).
- svelte:head now uses a simple <link> to that file.
- $effect injects the @page size dynamically per template layout field,
  avoiding the Svelte 5 parser limitation for conditional style injection.
- Badge_template interface in db_events.ts: added cfg_json / data_json
  (standard Aether object fields that were missing from the type).
2026-03-16 13:50:28 -04:00
Scott Idem
338cfd4ec0 style: fix missed Phase 1/2 items — FA→Lucide, a11y, variant-* cleanup
- +layout.svelte: replace fa-cog/fa-spinner spinners with LoaderCircle;
  variant-filled-primary → preset-filled-primary on reload button
- events/+page.svelte: fa-calendar-alt → CalendarDays, fa-exclamation-triangle
  → TriangleAlert (+ text-red-500 → text-error-500), fa-spinner → LoaderCircle
- sign_in_out.svelte: fa-times → X, fa-sign-in-alt → LogIn
- journals/+layout.svelte: fa-arrow-right → ArrowRight
- journal_entry_header.svelte: fix focus:ring-0 → focus:ring-2 focus:ring-primary-500
  (WCAG 2.1 AA — removes keyboard focus indicator violation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 13:31:21 -04:00
Scott Idem
efc0f46079 style(launcher): Phase 3 — FA→Lucide icon migration across all launcher files
Replaces all FontAwesome <span class="fas/fab fa-*"> with Lucide Svelte
components across 20 launcher files. launcher_cfg_section.svelte icon prop
changed from FA string to AnyComponent (svelte:component for dynamic render).
Dynamic file-extension icon now uses ae_util.file_extension_icon_lucide().
Fixes class: directives on components (invalid in Svelte 5) → ternary class.
Removes title prop from Lucide components → wrapping <span title="...">.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:33:37 -04:00
Scott Idem
99df204763 style: Phase 1+2 — FA→Lucide, variant-* → preset-*, dark mode forms
Phase 1 (global quick wins):
- app.css: add global dark mode utility for .input/.select/.textarea
- events menu nav + layout: replace all FontAwesome icons with Lucide
- events settings: replace FA icons, standardize variant-* → preset-*

Phase 2 (module-by-module migration):
- root +layout.svelte: fix hardcoded banner colors → preset-filled-error/warning
- journals entry list: replace slate-* with gray-*, HSL eye colors → CSS tokens
- pres mgmt presenter view: variant-soft-warning → preset-tonal-warning, FA edit → Lucide
- badges (4 files): variant-* → preset-*, FA spinner → Lucide Loader2, dynamic alert fix
- events session list + event hub: variant-soft/filled → preset-tonal/filled
- leads module (12 files): complete variant-* → preset-* migration across all
  exhibitor sign-in, QR scanner, manual search, tracking list, manage tab,
  custom questions, license list, exhibit page, lead detail page + form

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 11:53:07 -04:00
Scott Idem
a79be722ae feat: add require_auth prop to hosted file download button
- Defaults to true (authenticated_access required) — no change to existing consumers
- Launcher file buttons set require_auth=false to allow unauthenticated downloads
2026-03-16 11:00:45 -04:00
Scott Idem
45fbf35c6b fix(sys-bar): full dark mode fix — base text color, select/input global CSS, color-scheme 2026-03-16 10:40:04 -04:00
Scott Idem
0d554e434d fix(launcher): sync monitor native-only, repositioned, always-visible chip, light/dark responsive; add mode button tooltips 2026-03-16 10:39:56 -04:00
Scott Idem
75e8a87713 feat(launcher): add launcher_menu/header/footer URL params to URL builder; strip after apply 2026-03-16 10:39:23 -04:00
Scott Idem
9b8bc7cb73 feat(journals): redesign list + entry views with glow, dark mode, compact mobile-first layout
- ae_comp__journal_obj_li: compact tap-target rows, glow wrapper, plain gray dark mode (no Skeleton paired utils)
- +page.svelte (journals index): fix 'Managed by Account Name Not Set' bug, responsive spacing, glow on quick-add
- ae_comp__journal_obj_id_view: glow wrapper, explicit bg-white/dark:bg-gray-900, remove broken dynamic color class
- ae_comp__journal_entry_obj_li: glow wrapper, left-accent border on cards, fix dark mode hover
- ae_comp__journal_entry_obj_id_view: glow wrapper, consistent gray dark mode, edit-mode ring indicator
- ae_comp__journal_entry_header: replace Skeleton surface vars with plain grays (dark mode fix)
- e_app_theme: fix theme selector panel readability in dark mode
2026-03-13 19:48:23 -04:00
Scott Idem
ea87befc0d Version bump. 2026-03-13 17:59:24 -04:00
Scott Idem
11d7632571 feat(events): collapse session description by default with toggle 2026-03-13 17:44:21 -04:00
Scott Idem
e5b4805916 feat(sign-in): inline status feedback on username/password sign-in button
Replaces all alert() calls in the user/pass auth flow with reactive state.
Button shows: Verifying… (disabled) → Failed — retry? (red) →
Enter credentials first (amber) → Username/User ID Sign In (default).
Error messages (wrong password, no person record, no server response)
appear as small text below the button on failure.
Clicking the button resets to default so retry is clean.
Also removes dead commented-out alert and cleans up the promise chains.

No type="button" issues found — all non-submit buttons were already typed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 17:09:09 -04:00
Scott Idem
9dde412781 feat(sign-in): replace alert() with inline status on email sign-in button
Removes the debug alert() calls from the email magic-link flow.
Button now shows live feedback inline:
- 'Sending…' while the lookup is in flight (disabled + cursor-wait)
- 'Email sent ✓' on success (green fill)
- 'No account found' if no user matches the email
- 'Error — retry?' on network/API failure
- 'Enter an email first' if submitted empty
Clicking the button while showing a result resets it to the default label.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 17:01:57 -04:00
Scott Idem
0b9272e9f9 feat(sys-bar): polish sign-in forms, fix passcode flow, dynamic section headers
Sign In/Out (e_app_sign_in_out.svelte):
- Remove redundant internal header (sr-only was broken by :global CSS override)
- Full-width form inputs and buttons with 'or' divider between the two methods
- Signed-in state shows centered username and full-width action buttons

Access/Passcode (e_app_access_type.svelte):
- Fix 'Locked' button: was running trigger=true (no-op permission reprocess);
  now correctly toggles show_passcode_input so the input shows/hides on click

System bar (e_app_sys_bar.svelte):
- Dynamic section headers: Sign In/Out shows username when signed in;
  Access/Passcode shows ShieldEllipsis/ShieldMinus/ShieldUser based on state
- Fix passcode input not showing on re-open via menu button:
  onDestroy resets show_element__passcode_input=false; toggle_expand now
  restores it to true for anonymous/no-access state (matches handle_shield_click)
- Broaden anonymous check from === 'anonymous' to !access_type || === 'anonymous'
- Remove dead getElementById focus call (DOM not ready at that point;
  focus_input binding in Element_access_type handles it correctly)
- Appearance section: mode/font buttons at top, dark mode gets amber tint
  in light mode for visual context, theme select uses text-sm

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:59:04 -04:00
Scott Idem
50bfe2f64b fix(journals): remove duplicate Journal_obj_id_edit modal and fix bind:show
- [journal_id]/+page.svelte was rendering a second Journal_obj_id_edit
  alongside the one already in ae_comp__journal_obj_id_view.svelte,
  causing the modal to open twice simultaneously.
- ae_comp__journal_obj_id_view: changed show={} to bind:show={} so that
  closing the modal properly writes back to journals_sess, preventing the
  store from fighting the close and re-opening it.
2026-03-13 16:25:00 -04:00
Scott Idem
61c020b0b2 feat(sys-bar): polish — frosted glass panel, smooth label transitions, footer clearance
- Panel and sticky header now use bg-white/85 + backdrop-blur-md (frosted glass)
  matching the compact bar's existing semi-transparent treatment
- Button hover labels switched from hidden/inline (instant snap) to max-w/opacity
  transitions (duration-300 ease-in-out) — smooth expand-in, graceful fade-out
- Inlined Appearance controls (theme select + mode/font buttons) replacing Element_theme
  sub-component to avoid w-72 / bg-blue conflicts inside the panel
- Added Dev/Tools section (edit mode only): iframe toggle, reload, clear storage+DB,
  URL builder, debug overlay — separated from user-facing config
- :global CSS in style block strips blue bg/border from Element_access_type and
  Element_sign_in_out when rendered inside .ae_sys_panel
- bottom-4 → bottom-12 to clear route-level footers (absolute bottom-0, text-xs
  hover:text-base — expands to ~44px on hover)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:24:50 -04:00
Scott Idem
e4cb968659 fix(launcher): implement reliable click-outside-to-close for cfg drawer
Flowbite's built-in outsideclose was unreliable for this side-drawer setup.
Root cause: Flowbite's _onclick handler uses dlg.getBoundingClientRect() to
detect backdrop clicks. This works in theory for showModal() dialogs, but
something in the Svelte transition / stacking context prevented it firing.

Fix: bypass Flowbite's detection entirely.
- onclick prop on <Drawer> is spread through Flowbite's restProps chain
  to the native <dialog> element, overriding the broken _onclick handler.
  Handler: just closes the drawer unconditionally.
- A stopPropagation wrapper div around all visual panel content ensures
  that clicks INSIDE the panel never bubble up to the dialog element.
  Only genuine backdrop clicks (outside the visual panel area) reach
  the dialog and trigger the close handler.

ESC key is unaffected — it fires oncancel not onclick.
2026-03-13 15:53:01 -04:00
Scott Idem
84fb39d85b fix(sys-bar): prevent bar height shift on hover
- Hover info strip is always in DOM (opacity-only toggle, no {#if} mount)
  so first hover no longer triggers a layout recalc/flash
- Bar strip gets fixed h-9 height so inline label text appearing on
  group-hover can never shift the bar vertically

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:51:27 -04:00
Scott Idem
8ed766bbca fix(sys-bar): fix hover bounce, horizontal overflow, add collapsible sections
- Hover info strip is now absolute-positioned above the bar (opacity fade
  with delay-500) so it never shifts the bar layout — fixes the bounce
- Panel widened to w-80 with overflow-x-hidden — fixes horizontal scroll
  caused by sub-components hardcoding w-72 inside the padded panel
- All panel sections are now collapsible (Access open by default, others
  closed) — reduces vertical crowding; matches launcher_cfg pattern
- Section headers show current state inline (access level, theme name/mode)
- Admin section groups cfg + debug trigger together cleanly
- Bar transitions use duration-200 for snappier feel without bounce

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:45:35 -04:00
Scott Idem
c3c3e1cbcb feat(sys-menu): replace sys_menu with new e_app_sys_bar component
New compact bar + expandable panel design:
- Compact strip (bottom-right): auth shield, font cycler, dark/light toggle,
  edit mode toggle (authenticated+), menu expand — icon-only by default,
  labels reveal on hover
- Hover info strip shows person name + access level when logged in
- Expanded panel: sign in/out, access/passcode, appearance (theme), admin
  (config + URL builder + debug trigger) — all gated same as before
- Debug overlay trigger moved into admin section (edit mode only)
- All business logic preserved via existing sub-components (unchanged)
- e_app_sys_menu.svelte retained but no longer mounted (import commented out)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:31:21 -04:00
Scott Idem
a3de95629a feat(sys-menu): add URL param builder to app config panel
New e_app_url_builder.svelte component lets admins construct and copy
shareable URLs with any combination of core global params (iframe, theme,
theme_mode, key). Outputs full URL by default; toggleable to params-only
string for pasting onto existing links. Integrated into e_app_cfg Utilities
section (visible in edit mode).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:27:19 -04:00
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
bf22b4a512 fix(launcher): fix cfg drawer medium-width wrapping; rename Hardware→Device tab; native OS in edit_mode
- launcher_cfg_section.svelte: Remove md:grid-cols-2 from content wrapper.
  Root cause of the middle-width layout issue: at md breakpoint the drawer
  is only 384px wide but the section body switched to 2-column, cramming
  full-width content blocks into ~170px each. Always grid-cols-1 now.

- launcher_cfg.svelte: Rename Hardware tab to Device (neutral — applies
  even in browser). Reorder Device tab content: Sync Timers first (relevant
  to all devices), then native sections behind $ae_loc.is_native || edit_mode.
  Updates still hidden behind is_native only (no useful preview).

- launcher_cfg_native_os.svelte: Add dev-preview banner when edit_mode is
  on but not running in Electron. electron_relay.ts guards all calls with
  'if (!native) return null' so there are no import errors or crashes —
  controls simply show with a warning indicator. Removes stale placeholder
  comment left by a previous agent.
2026-03-13 14:54:28 -04:00
Scott Idem
71795461dd feat(launcher): reorganize cfg drawer into Setup/Hardware/Dev tabs
- Setup (default): Oral/Poster preset, WS Controller, Screen Saver
- Hardware: Electron health/OS/updates (native only) + Sync control
- Dev: Local reset/debug tools — tab hidden until Edit Mode is on

Edit Mode toggle added as subtle pencil icon in the cfg drawer header
(low opacity when off, primary-colored when active). This is intentional
— onsite kiosk operators should not stumble on it, but admins doing
setup can find and toggle it without leaving the drawer.

Dev tab visibility and the Debug Panel button both gate on edit_mode,
keeping the default view clean for non-technical operators.
2026-03-13 14:28:23 -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
1417fafcd3 feat(launcher): auto-apply kiosk URL params on Launcher link for poster sessions
When on a Poster Session in Pres Mgmt, the Launcher nav link now appends
?iframe=true&launcher_menu=hide so the recipient opens a clean kiosk view
without the site header or left session panel.

- ae_comp__events_menu_nav: add events__launcher_extra_params prop; update
  launcher_sess_qry to merge extra params after session_id
- session_page_menu: derive is_poster from type_code and pass the kiosk
  params into the Launcher link
2026-03-13 13:41:49 -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
0c18eea31c refactor(pres_mgmt): consistent layout, menu structure, and centering
- Add (pres_mgmt)/+layout.svelte with shared section wrapper (max-w-7xl mx-auto)
  so all child pages center correctly on wide viewports
- Strip per-page outer <section> tags from session, presenter, location pages;
  replace with inner <div max-w-7xl mx-auto> for detail constraint
- Restructure all page menus from flex-row to flex-col so nav bar occupies its
  own row and options/actions sit in a separate justified row below — prevents
  unwanted wrapping when nav is w-full
- Standardize Æ Core button visibility to edit_mode && manager_access across all
  menus; update button style to ae_btn_warning for visual distinction
- Add w-full + justify-between to ae_comp__events_menu_nav outer div

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 13:11:23 -04:00
Scott Idem
752dca290a feat(launcher): dedicated Digital Poster session card-grid view
Create launcher_session_view_posters.svelte — a touch-first card-grid
layout for Digital Poster sessions, designed for tablet/phone PWA use.

Layout:
- 1 column on mobile, 2 on sm, 3 on xl
- Each poster card: title (line-clamp-3) + presenter name/affiliation +
  'Open Poster' action button at card bottom
- Poster code (or 1-based index fallback) badge in top-right corner
- Card active:scale-[0.98] for tactile touch press feedback
- Sticky compact session header strip with name, code, and poster count
- Optional 'Session Resources' strip for rare session-level files
- overflow-y-auto + grow so the grid scrolls; header strip stays fixed

Integration:
- launcher_session_view.svelte: import + delegate when type_code==='poster'
- launcher_file_cont.svelte: min-w-96 → w-full on poster button so it
  fills its container (card or list row) without overflow on small screens
- WS open/close/zoom command pipeline unchanged (all in launcher_file_cont
  and +layout.svelte which were not modified for the WS paths)
2026-03-13 12:42:12 -04:00
Scott Idem
5f1169bb4c fix(launcher): use parent type_code for session_type, not per-file field
The event_session_type_code field on file objects is often null, causing
poster-mode files to fall back to 'oral' and render as download buttons
instead of modal openers.

- launcher_session_view: session-level files now use derived type_code
- launcher_presenter_view: add session_type prop (default 'oral'); parent passes type_code
- launcher_presenter_view_posters: hardcode poster/modal (only ever rendered in poster context)
- launcher_menu event/location files unchanged: no session context, field is the only signal
2026-03-13 11:46:17 -04:00
Scott Idem
c29ac9f9f5 fix(pres_mgmt): use w-full on Session and Presenter page sections
Session ID page: replace `container mx-auto` with `w-full` — removes the
Tailwind breakpoint max-width cap that was narrower than the outer layout's
max-w-7xl on smaller desktop viewports, and eliminates the double-padding
from nested container classes.

Presenter ID page: replace the conflicting `md:container items-center mx-auto
h-full min-w-full max-w-max` with a clean `w-full`. The `items-center` was
the main bug — in flex-col context it horizontally centered children (presenter
details, file upload form, file list) at their content width instead of
stretching them to fill the available space. Removes all of the conflicting
min/max width overrides.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 11:05:55 -04:00
Scott Idem
633930f65a refactor(pres_mgmt): menu consistency — modal pattern, prop cleanup, styling
All 6 *_page_menu.svelte files now consistently use:
- Flowbite Modal (not expandable panels) with matching header/close pattern
- interface Props (no export keyword) with only props actually used
- flex flex-row flex-wrap gap-0.5 on ae_menu__object_options span

Removed unused props and their call-site bindings:
- location_page_menu: removed data prop; +page.svelte drops data={data}
- locations_page_menu: removed unused log_lvl prop
- presenter_page_menu: removed data prop; +page.svelte drops {data}
- event_reports_page_menu: removed data prop; +page.svelte drops {lq__event_obj} data pass

event_page_menu: fixed interface export keyword (was export interface)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 11:05:02 -04:00
Scott Idem
26dbe57b22 fix(pres_mgmt): migrate QR generation from API to js_generate_qr_code
The API-based generate_qr_code was returning 404 (backend endpoint broken),
causing TypeError crashes on the Presenter, Session, and Location views.
Switch all three to the pure-JS js_generate_qr_code('str', { str: url_str })
which uses the qrcode npm library client-side — consistent with badge views.

- presenter_view.svelte: replace generate_qr_code, remove unused qr_id_url var
- session_view.svelte: replace generate_qr_code, remove unused slct import
- location_view.svelte: replace generate_qr_code, remove unused slct/slct_trigger imports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 11:02:04 -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
90615ad5cc docs(badges): update print behavior docs + Zebra test day plan
- MODULE__AE_Events_Badge_Templates: rewrite Print Layout Architecture
  section with accurate cross-browser behavior table and Chrome margin
  guidance
- PROJECT__AE_Events_Badges_Review_Print: add TASK 4.1 print CSS
  centering history; correct Chrome squish root cause explanation
- PROJECT__AE_Events_Zebra_Hardware_Test_Day: new — comprehensive
  pre-test and day-of checklists for ZC10L hardware test ~2026-03-16
- TODO__Agents: add [Badges] Zebra ZC10L Hardware Testing section
2026-03-12 19:23:47 -04:00
Scott Idem
cfa9e85d05 feat(badges): add demo background pattern toggle in edit mode
Adds a 🎨 button to the badge debug bar (edit-mode only) that cycles
through 5 SVG background patterns keyed by badge type color palette:
stripes, dots, diamonds, grid, swirls.

Swirl tile is 64×64 with corner-to-corner cubic bezier S-curves so
edges align seamlessly when tiled. Off by default; never prints.
2026-03-12 19:23:21 -04:00
Scott Idem
c867795f4d Badges: use position:fixed for print centering — bypasses all ancestor layout issues 2026-03-12 18:25:31 -04:00
Scott Idem
5410bb4a8c Badges: switch to transform-based print centering — fixes Firefox physical printer shift 2026-03-12 18:08:27 -04:00
Scott Idem
bc23e8a399 Badges: fix Firefox print centering (overflow:auto+display:contents); add print layout Playwright tests 2026-03-12 17:58:33 -04:00
Scott Idem
5ef66af98d Badges: fix print centering — override overflow:hidden on body/html in print 2026-03-12 17:46:50 -04:00
Scott Idem
1e70271002 Badges: add temp print debug outlines — diagnose centering issue 2026-03-12 17:32:31 -04:00
Scott Idem
05aa0288cb Badges: fix print centering — dissolve .main_content; hide footer/scroll buttons from print; document print layout architecture 2026-03-12 17:28:25 -04:00
Scott Idem
f26416de22 Badges: print centering via display:contents — collapse wrappers, body as flex center 2026-03-12 17:21:56 -04:00
Scott Idem
11a6d5d35c Badges: center badge vertically + horizontally in print preview; scaffold print_margin_cfg 2026-03-12 17:05:41 -04:00
Scott Idem
c3a4ff45c7 Badges: clean print output — suppress outlines, hide app chrome, reset layout container 2026-03-12 16:52:17 -04:00
Scott Idem
25359f12b8 Badges: soft feathered glow around badge wrapper in dark mode 2026-03-12 16:43:11 -04:00
Scott Idem
dd2deb1ab1 Badges: fix debug pre dark mode colors; white ring shadow around badge in dark mode 2026-03-12 16:31:26 -04:00
Scott Idem
745067b228 Badges: force light-mode rendering on badge print area — ignore dark theme 2026-03-12 16:26:21 -04:00
Scott Idem
6e8f44b009 Badges: center badge wrapper via mx-auto so Edit Mode debug div doesn't shift layout 2026-03-12 16:15:55 -04:00
Scott Idem
b4ab60ebba Badges: PVC wrapper hugs card tightly; hide 'Front of badge' label when single-sided 2026-03-12 16:05:08 -04:00
Scott Idem
7b340de139 Badges: fix button layout \u2014 Revert/Save/X always in fixed positions 2026-03-12 15:54:06 -04:00
Scott Idem
23422c8f27 Badges: hide Save until field is dirty; warning style when unsaved changes 2026-03-12 15:48:26 -04:00
Scott Idem
f74fc593b0 Badges: allow_tracking auto-expands when true; TC modal on info button 2026-03-12 15:41:32 -04:00
Scott Idem
9888374d86 Badges: auto-focus input when field accordion opens; add Revert button to field forms 2026-03-12 15:28:37 -04:00
Scott Idem
9e64815d62 Badges: panel widens to w-80 while editing, font scaling on active card 2026-03-12 15:21:13 -04:00
Scott Idem
5f6f1b408b Badges: live preview while typing, accordion entrance animation, register tailwindcss-animate 2026-03-12 14:54:26 -04:00
Scott Idem
961b05c5e4 Badges: controls panel UX polish — remove identity card, elevate print btn, style buttons 2026-03-12 14:46:08 -04:00
Scott Idem
b92c0bdcf1 Badges: retire v1 render, badge print page kiosk UX improvements
- ae_comp__badge_obj_view.svelte (v1) removed — replaced by v2 everywhere
- print/+page.svelte: always uses v2, removed v1/v2 toggle
  Header redesigned for kiosk: name + Ready/Printed×N status chip,
  event title subtitle. Re-print shortcut only in trusted+edit_mode.
  Duplicate 'Print Now' header button removed (canonical is in right panel).
- ae_comp__badge_print_controls.svelte:
  Identity card added at top (name, badge type, badge ID).
  Pronouns moved to attendee-level section (was staff-only).
  'Staff adjustments' divider added before badge type section.
  Attendee info section header label added.
- ae_comp__badge_obj_view_v2.svelte: debug JSON blocks gated behind edit_mode.
- print_list/+page.svelte: updated import to v2.
2026-03-12 14:19:58 -04:00
Scott Idem
c7063806b7 refactor: v2 badge render display-only + print button to controls panel
- ae_comp__badge_obj_view_v2.svelte: removed all inline edit-mode logic
  (floating Edit/Save/Cancel panel, placeholder list, input fields, save/cancel
  functions). V2 is now purely a display component — editing happens in the right
  panel (ae_comp__badge_print_controls.svelte) via liveQuery reactivity.
  display_* values are now $derived directly from lq__event_badge_obj.
  Fixes effective_badge_type_code CSS class (was always empty in previous v2).

- ae_comp__badge_print_controls.svelte: added "Print Badge" button at the top of
  the panel. Increments print_count, fires window.print(), then navigates to badge
  search. This is now the canonical print action for v2; the header "Print Now"
  button is a shortcut that calls window.print() only (no count tracking).
  Clarified $ae_loc.edit_mode comment — global AE Edit Mode is not a badge-specific
  edit toggle; used here only to gate reprints.

- print/+page.svelte: added clarifying comments on is_edit_mode (global vs local)
  and the header "Print Now" button (shortcut only, no count tracking).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 13:48:08 -04:00
Scott Idem
2198c55e27 feat: badge v2 auto-scaling text with Element_fit_text
Adds binary-search font auto-scaling for badge text fields, replacing
the character-count heuristic in v1. New files:

- action_fit_text.ts: Svelte action using binary search + MutationObserver
  + ResizeObserver. Pass null to disable (manual override mode).
- element_fit_text.svelte: Component wrapper with min/max/manual_size/
  height/width props. height prop required for overflow detection to work.
- ae_comp__badge_obj_view_v2.svelte: Badge render using Element_fit_text
  for name/title/affiliations/location in display mode. font_size_* props
  default to undefined (auto-scale) instead of numeric defaults.
  fit_heights derived object provides layout-aware section heights for
  badge_3.5x5.5_pvc, badge_4x5_fanfold, and badge_4x6_fanfold layouts.
  flex_justify() maps shorthand ('around','between','even') to CSS values.
  Edit mode uses plain divs — inputs are never auto-scaled.

print/+page.svelte: Added v1/v2 toggle button in header. V1 preserved
as fallback. font_size_* passed as null (not ?? undefined) to v2 so
auto-scaling is active by default; manual override from print controls
still disables it per-field.

Docs: PROJECT__AE_Events_Badges_Review_Print.md updated with kiosk
workflow design intent, email address rule (always event_badge.email),
permission model alignment gap (TASK 4.0), and v2 implementation status.
TODO__Agents.md: completed items removed, badge polish tasks updated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 13:23:47 -04:00
Scott Idem
dfad2831d6 Updated documentation. 2026-03-11 19:06:37 -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
3f5f554595 Now with websocket use instructions. :-) 2026-03-11 17:16:44 -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
496afcb813 fix: suppress svelte chunk circular dep warnings via manualChunks
Rollup was splitting svelte/src/internal/client/runtime.js into a
different chunk from svelte/src/index-client.js, producing ~35 warnings
about untrack/tick re-exports leading to broken execution order.

Add manualChunks function to vite.config.ts to colocate all svelte
node_modules into a single 'svelte-vendor' chunk, keeping runtime.js
and index-client.js together.
2026-03-11 15:02:02 -04:00
Scott Idem
fde3801ea6 refactor: clean up try_cache defaults and add SWR to sponsorship loaders
- Remove vestigial try_cache param from generate_qr_code (never used in body)
- Remove vestigial try_cache from ae_core_functions: load_ae_obj_id__site_domain,
  update_ae_obj_id_crud, update_ae_obj_id_crud_v2 (none referenced it in body)
- Add proper SWR pattern to load_ae_obj_id__sponsorship_cfg and
  load_ae_obj_id__sponsorship; change defaults from false to true
- Change load_ae_obj_id__event_file default try_cache from false to true
  (consistent with load_ae_obj_li__event_file)
- Change load_ae_obj_id__hosted_file default try_cache from false to true
  (consistent with load_ae_obj_li__hosted_file)
- Remove stale try_cache arg from element_ae_crud.svelte caller
2026-03-11 14:57:23 -04:00
Scott Idem
50a20ebc44 chore: add cSpell words; fix deploy scripts to target ae_app service only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 14:36:17 -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
477fc16f16 fix: derive poster session type from Dexie chain for presenter-linked files
v_event_file joins event_session only via event_file.event_session_id.
Files with for_type='event_presenter' have event_session_id=NULL on the
file record itself, so event_session_type_code is structurally always NULL
from the API for these files — no amount of refreshing can fix it.

Instead of relying on the file's event_session_type_code, derive the session
type in element_manage_event_file_li_direct via the Dexie chain:
  presenter.event_presentation_id → presentation.event_session_id → session.type_code

Pass the result as context_session_type_code to element_manage_event_file_li,
which now checks EITHER the file's own event_session_type_code OR the context
prop against 'poster' to show the PDF→Image convert button.

Sessions are guaranteed in Dexie because the pres_mgmt layout loads
inc_session_li:true on every navigation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 14:06:33 -04:00
Scott Idem
4690548946 fix(events): switch onMount→\$effect for file list Dexie refresh
onMount fired before the parent presenter liveQuery resolved, so
link_to_id was undefined and the refresh was silently skipped.

Using \$effect makes the background refresh re-run once link_to_id
becomes available (after the presenter Dexie lookup completes),
ensuring event_session_type_code is written to Dexie and the
PDF→image convert button renders correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 13:43:33 -04:00
Scott Idem
de8b85bb05 fix(events): refresh file list on mount to populate event_session_type_code in Dexie
The presenter detail page loads files with try_cache:false, which fetches
fresh data from the API but does NOT write it to Dexie (by design in the
SWR implementation). The file list's liveQuery then reads stale Dexie
records that lack event_session_type_code, causing the PDF→image convert
button condition to silently fail for presenter files in poster sessions.

Fix: trigger a try_cache:true background refresh on mount in the direct
wrapper so fresh API data (with event_session_type_code='poster') is
persisted to Dexie and the liveQuery re-renders with the correct field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 13:33:31 -04:00
Scott Idem
b2fa1a59dc feat(events): add PDF→webp convert button to event file list view
Mirrors the convert button added to the table view (ae_comp__event_file_obj_tbl).
The list view (element_manage_event_file_li) is the primary Pres Mgmt UI
for managing event files per object (session, presenter, location, etc.).

Same conditions: edit_mode on, extension=pdf, event_session_type_code=poster.
Per-row status: idle → converting → done | error with retry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 13:24:41 -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
241e05bc79 feat: add store_versions.ts — localStorage schema versioning for ae_loc and ae_events_loc
Persistent stores grow and change over time. svelte-persisted-store deep-merges
old localStorage values with new defaults, so stale values (e.g. hash_prefix_length: 1)
silently survive schema changes and cause subtle bugs.

- src/lib/stores/store_versions.ts:
  Single source of truth for AE_LOC_VERSION / AE_EVENTS_LOC_VERSION.
  Side-effect on import: reads raw localStorage and wipes if __version mismatches.
  Must be imported first in ae_stores.ts and ae_events_stores.ts so the wipe
  happens before persisted() hydrates from localStorage.

- ae_stores.ts + ae_events_stores.ts:
  Import store_versions as first import; add __version to persisted store defaults.

- documentation/TODO__Agents.md:
  Added stores refactor task — both store files need a cleanup pass.

Bump AE_LOC_VERSION or AE_EVENTS_LOC_VERSION by 1 on breaking schema changes.
Non-breaking changes (new optional fields, default value tweaks) do not need a bump.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 12:43:05 -04:00
Scott Idem
ca91afbd9d chore: Optimized Dockerfile and .dockerignore for faster deployments. 2026-03-11 12:36:34 -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
93efabdf00 docs(electron): fix cross-repo confusion, remove misplaced file, update integration docs
- Remove tmp_shell_handlers.ts from SvelteKit repo — this was a draft of the
  Electron main-process shell handler that was placed in the wrong repo. It used
  ipcMain (Electron main-process only) and could never run in SvelteKit. Was not
  imported anywhere but was misleading.

- Fix src/lib/electron/README.md:
  - Correct broken doc link (was AE_EVENTS_LAUNCHER_NATIVE_INTEGRATION.md,
    actual filename is PROJECT__AE_Events_Launcher_Native_integration.md)
  - Mark electron_native.js as DEPRECATED — do not import (was described as
    active "Bridge Logic", which was incorrect and confusion-causing)
  - Add clear bridge architecture diagram showing the three-layer flow
  - Note that aether_app_native_electron/ is the active Electron repo

- Update PROJECT__AE_Events_Launcher_Native_integration.md:
  - Fix Layer 1 file path: aether_app_native/ → aether_app_native_electron/
  - Section 5.3: Phase 5 actuators are all implemented, not "planned" — update
    recording, display layout, power control, window control, wallpaper to reflect
    actual implementation status; note update_app is a stub
  - Section 7: Expand IPC whitelist to cover all relay functions including new
    cleanup_tmp_files, system handlers, and full parameter signatures
  - Document get_seed_config / get_jwt as intentionally not relayed to UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 11:59:29 -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
a34f70d3dd Removed and or moved old documentation. 2026-03-11 10:18:44 -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
6a98b5801b Version bump to 3.00.01 2026-03-11 09:37:47 -04:00
Scott Idem
366c66293c chore: npm update - minor/patch dependency updates
Updated 170 packages within semver ranges. Notable updates:
- svelte: 5.45.10 -> 5.53.10
- @sveltejs/kit: 2.49.2 -> 2.53.4
- tailwindcss: 4.1.18 -> 4.2.1
- dexie: 4.2.1 -> 4.3.0
- openai: 6.10.0 -> 6.27.0
- vite: 7.2.7 -> 7.3.1
- @skeletonlabs/skeleton: 4.8.0 -> 4.12.1

Skipped: eslint 9->10, @eslint/js 9->10, globals 16->17 (major bumps, need separate review)
svelte-check: 0 errors, build: success
2026-03-10 19:13:36 -04:00
Scott Idem
5eb3374ac0 chore: upgrade node:21-alpine to node:22-alpine (Active LTS)
Node 21 was a non-LTS release, EOL June 2024. Node 22 is the current
Active LTS, supported through April 2027.
2026-03-10 18:58:23 -04:00
Scott Idem
ec00f75df6 fix: revert BuildKit cache mounts (BuildKit not default on this host) 2026-03-10 17:10:19 -04:00
Scott Idem
5e0cc73084 perf: add BuildKit npm cache mounts, add compose:down script
- RUN --mount=type=cache,target=/root/.npm for both npm install steps
  Persists npm cache across builds so unchanged deps aren't re-downloaded
- deploy:staging now targets ae_app only (skip api/nginx rebuild)
- compose:down includes --profile database to remove mariadb/pma containers
  and cleanly release ae_dev_net (fixes 'Resource is still in use' warning)
2026-03-10 16:56:41 -04:00
Scott Idem
9646f7fec2 fix: remove dead PUBLIC_TESTING import from ae_stores.ts 2026-03-10 16:40:21 -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
b7d9e9669d chore: clean up env templates, docs, and TODO
- .env.staging.default / .env.prod.default: remove dead Red/Green/Blue container
  vars, OSIT_WEB_PORT vars, DOCKER_AE_EXTRA_HOST entries, AE_APP_CFG_ID,
  AE_APP_NODE_PORT_RED/GREEN/BLUE, PUBLIC_AE_API_SERVER_INTERNAL; replace real
  staging key with XXXX placeholder (default files are committed to git);
  fix .prod PUBLIC_AE_API_PORT 5005 -> 443 (external rev proxy, not gunicorn)
- TODO__Agents.md: clean up completed items, condense history, update DevOps section
- PROJECT__AE_combined_front_back_Docker.md: mark unified Docker architecture complete
2026-03-10 16:19:23 -04:00
Scott Idem
a3d7ea7a78 [Cleanup] Guard remaining unguarded console.log calls
- core__crud_generic.ts: guard patch result logs (lines 246/252) with
  if (log_lvl) — these fired on every successful patch call
- e_app_sign_in_out.svelte: already committed in previous round
- element_manage_hosted_file_li.svelte: already committed in previous round

All other console.log calls in launcher/lib files confirmed already guarded
via $B2 context check. Remaining unguarded logs are in event handlers
(fire on user action only, not hot render paths) or testing/admin pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 15:07:01 -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
7139753c79 Docs: update TODO and add combined Docker architecture reference
TODO__Agents.md: Mark QR code on badge front as done — ae_comp__badge_obj_view.svelte
already generates the QR via core_func.js_generate_qr_code() and renders it
inside a {#await qr_data_url} block on the badge face.

PROJECT__AE_combined_front_back_Docker.md: New reference document covering
the combined front+back Docker orchestration architecture (consolidated
notes from the session).
2026-03-10 14:24:09 -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
283ccb3ce4 Tests: fix IDAA recovery meeting test suite
Multiple failures caused by the SWR background fetch pattern and
collapsed UI sections:

1. IDB settle wait: add waitForFunction that polls IndexedDB for
   tmp_sort_1 on the event record. tmp_sort_1 is only written by
   _process_generic_props, so its presence signals the background
   API fetch has completed and no further liveQuery re-fires will
   overwrite form inputs the test is about to fill.

2. JSON.parse for _json fields: update_ae_obj_v3 auto-serializes any
   key ending in _json to a JSON string before sending the PATCH.
   Tests now parse location_address_json, attend_json, and
   contact_li_json from the captured body before asserting field values.

3. Contact 2 section: collapsed by default when contact_2.full_name
   and email are null. Collapsed state renders type='hidden' inputs
   which Playwright's fill() rejects. Add click on the 'Contact 2
   (Optional)' toggle before filling those fields.

4. Admin Options section: same collapse pattern. Add click on the
   'Admin Options' toggle before filling status/sort/group/hide fields.

5. Increase suite timeout to 60 s: open_edit_form awaits real lookup
   API responses (pass_through_lookups=true) which can take 20-25 s
   on slow network, leaving no margin at the default 30 s limit.
2026-03-10 14:23:01 -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
2c0eba4130 DevOps: add svelte-kit sync step to Dockerfile before build
Fixes a build warning about missing .svelte-kit/tsconfig.json that
appears when building inside Docker. Running npx svelte-kit sync
pre-generates the required SvelteKit scaffolding before the main
build step.

Also update README title to reflect Svelte v5 and trim a trailing
whitespace in the environment handling section.
2026-03-10 14:20:40 -04:00
Scott Idem
18462249b3 feat: Integrated SvelteKit frontend into unified Aether orchestration. Updated deploy scripts and documentation. 2026-03-10 13:33:39 -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
01316789c6 [UI] Fix native browser controls dark mode (color-scheme sync)
Add html.dark/html.light color-scheme rules to app.css so native controls
(select dropdowns, scrollbars, date pickers) follow the app's class-based
dark mode rather than the OS theme.
2026-03-10 11:28:17 -04:00
Scott Idem
a955fad891 docs: add deployment optimization tasks to TODO list 2026-03-09 22:49:35 -04:00
Scott Idem
38b3164a0c docs: finalize README with updated deployment instructions 2026-03-09 22:27:56 -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
8247c62d0e test(idaa): IDAA recovery meeting edit form Playwright test suite + README docs
Full payload-verification test suite for ae_idaa_comp__event_obj_id_edit_v2.

Root cause fixed: $ae_loc.lu_time_zone_list empty at mount caused Svelte 5 to
render <input type=text name=timezone required value=''> instead of the <select>
branch. HTML5 required validation silently cancelled onsubmit with no JS error
and zero network activity — waitForRequest timed out with no obvious cause.
Fix: pre-seed lu_time_zone_list in addInitScript so the <select> branch renders
on first mount with a valid value already set.

Key patterns established:
- setup_idaa_auth(): pre-seeds ae_loc + ae_idaa_loc in localStorage via
  addInitScript; includes lu_time_zone_list and window.__ae_test_mode = true
- setup_api_mocks(): selective pass-through flags for lookups and site_domain
- open_edit_form(): waitForFunction guards for name field, country lists, and
  the timezone required field before any interaction
- capture_patch_body(): registers waitForRequest before click, awaits after

README.md updated with deep-dive section covering:
- HTML5 form validation silent block and how to diagnose it
- Svelte 5 one-time value= bind trap
- addInitScript store pre-seeding pattern
- __ae_test_mode email suppression
- waitForFunction patterns for reactive state
- Route mock strategy (pass-through vs fixture)
2026-03-09 17:54:01 -04:00
Scott Idem
37e7a93617 fix(api): suppress send_email() in test/Playwright environments
Add a guard at the top of send_email() that checks globalThis.__ae_test_mode.
If truthy, logs a suppression message and returns null immediately so no HTTP
request is made. This prevents real emails being sent when Playwright tests
exercise components that call send_staff_notification_email() after a successful
save. Activate by setting window.__ae_test_mode = true in addInitScript.
2026-03-09 17:53:45 -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
e7ac5168f6 fix: person lookup failing due to legacy person_id_random field name
V3 CRUD returns 'id' as the random identifier, not 'person_id_random'.
The person check and assignment were using the old field name, causing
the 'no person record' alert even when the lookup returned valid data.

Now checks person_rec.id ?? person_rec.person_id_random as a fallback
for backwards compatibility.
2026-03-06 22:51:29 -05:00
Scott Idem
0dab64a8d6 fix: improve sign-in error messages for both auth flows
- Check user_response?.detail (FastAPI standard) before user_response?.error
- Distinguish null response (network/server error) from bad credentials
- Remove silent console.error-only path; user now always sees a message
- Fix misleading 'auth_ae_obj__username_password' label in user_id+key flow
- Clarify 'no person record' message to suggest contacting administrator
- Simplify success log messages (remove dead commented-out code)
2026-03-06 22:42:15 -05:00
Scott Idem
79457103de fix: sign-in broken due to wrong field name on auth response
The /user/authenticate endpoint returns 'user_id' not 'user_id_random'.
Both auth flows (user_id+auth_key and username+password) were checking
user_response?.user_id_random, which was always undefined, causing the
user_id to never be set and falling through to the email lookup fallback.

Fixed both .then() handlers to check user_response?.user_id and assign
user_obj.user_id.
2026-03-06 22:39:16 -05:00
Scott Idem
e9527cdcd5 Saving wrap up notes for the night. 2026-03-06 22:00:12 -05: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
195a4f2174 Saving these notes. 2026-03-06 21:23:00 -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
b39ce19fdc feat(theme): add AE Firefly theme — 'Shiny serenity, like a firefly.'
New custom Skeleton v4 theme for One Sky IT, LLC.
Design vision: Scott Idem, 2026-03-06.

Color system:
  Primary   — Luminescent teal (bioluminescent shimmer, hue ~184°)
  Secondary — Warm amber-gold (firefly body glow, hue ~90°)
  Tertiary  — Night-sky indigo (depth + serenity, hue ~277°)
  Surface   — Moonlit slate (barely-cool neutral; crisp in light,
              deep midnight in dark)

Section 508 / WCAG 2.1 AA compliance embedded in contrast assignments.
Set as new app default (replacing nouveau).

Files:
  src/ae-firefly.css                        — theme definition
  src/app.css                               — @import registered
  src/lib/app_components/e_app_theme.svelte — 'Firefly ✦' in selector
  src/lib/stores/ae_stores.ts               — default theme_name updated
2026-03-06 21:05:08 -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
Scott Idem
dd5cf9b63b chore: minor updates across events, journals, elements, and shared components
Miscellaneous small changes to events (badges, launcher, leads, pres_mgmt,
settings), journals, reusable elements (crud, field editor), app components,
core components, and test README. Mostly 1-2 line changes per file.
2026-03-06 17:32:53 -05:00
Scott Idem
0c9b6a9f5b fix: IDAA auth — harden novi_admin_li/trusted_li and remove iframe gate
- ae_idaa_stores.ts: update default novi_admin_li UUID; add staff UUID to
  novi_trusted_li hardcoded defaults.
- +layout.svelte (idaa): only overwrite admin/trusted lists from site_cfg_json
  when the list is non-empty, so hardcoded defaults are never silently cleared.
  Remove $ae_loc.iframe requirement for 'authenticated' access level — the
  presence of a valid Novi UUID in the URL is sufficient proof regardless of
  whether the iframe flag is set yet.
2026-03-06 17:32:47 -05:00
Scott Idem
9fc72b4671 feat: wire up class-based dark mode for Tailwind v4
- app.css: add @custom-variant dark so Tailwind v4 respects .dark class
  on <html> instead of always following OS prefers-color-scheme.
- app.html: remove hardcoded class="light" (now set dynamically).
- +layout.svelte: toggle .dark/.light on <html> when ae_loc.theme_mode changes.
- e_app_theme.svelte: related theme toggle changes.
2026-03-06 17:32:30 -05:00
Scott Idem
bdf3260c74 test: fix IDAA recovery meetings test — real backend save works
- Fix site_domain mock to return array { data: [mock_site_domain] } so
  ae_api.headers['x-account-id'] gets a valid 11-char account_id.
  Previously returned { data: {} } causing layout to fall back to 'ghost'
  (5 chars) and the real API PATCH rejected the request with 422.
- Add integration test describe block (Real Backend Save) with
  pass_through_event_patch option to let PATCH reach the real API.
- Extract localStorage injection into setup_idaa_auth() helper.
- Fix test name: 'sends PUT' → 'sends PATCH' (the API uses PATCH).
- All 14 tests pass.
2026-03-06 16:59:39 -05:00
Scott Idem
94c974d7fb test: add Playwright tests for IDAA Recovery Meetings edit form
13 tests covering: form render, form sections, field names/types,
weekday checkboxes, timing inputs, contact fields, address fieldset
visibility, virtual checkbox, text input, and PATCH API submission.
All tests pass (13/13). Fully mocked — no real backend required.
2026-03-05 22:01:30 -05:00
Scott Idem
609818c361 a11y + CSS cleanup: fix label associations, remove orphaned style blocks, suppress autofocus
- Add for/id to all form label+input pairs: person, address, contact, users pages
- Convert decorative section-header <label> elements to <p> in launcher cfg files
- Add for/id to dynamic labels in leads custom questions, badges review form
- Change non-form <label> wrappers for custom editors to <div>/<p>
- Remove orphaned <style> blocks (ae_quick_modal_container/ae_quick_popover) from
  people/[person_id]/+page.svelte and location_view.svelte

Result: 95 warnings -> 26 warnings (remaining are lu_* false positives + 1 legacy CSS)
2026-03-05 21:28:16 -05:00
Scott Idem
b766942373 Less _random 2026-03-05 21:01:07 -05:00
Scott Idem
fdd4020267 fix: reduce svelte-check warnings from 175 to 95 (80 eliminated)
Svelte 5 reactivity pattern fixes:
- Convert prop/data captures to $derived where used in reactive contexts
- Wrap store assignments in $effect + untrack for ae_acct pattern
- Move sign_in_out URL param processing to onMount (from top-level if(browser))
- Wrap debug console.log blocks in $effect instead of top-level if(log_lvl)
- Fix $state initializers reading props directly ($state(link_to_id) → $state(''))
- Fix box = $state(null) in journals layout

CSS fixes:
- TipTap scss: change :global(.tiptap){nested} to :global{.tiptap{nested}} so
  Svelte does not scope-hash dynamic content selectors (latent CSS bug fixed)
- element_manage_hosted/event: dq__where vars → $derived for reactive liveQuery

Config:
- svelte.config.js: add onwarn (suppresses a11y/CSS in Vite pipeline; note:
  svelte-check 4.x does not read onwarn so CLI count unchanged)

Remaining 95 warnings (acceptable baseline):
- 70x a11y_label: form labels need for/id attributes (proper a11y fix deferred)
- 12x lu_* false positives in IDAA async callbacks (correct code)
- 8x CSS dynamic selectors Svelte cannot detect at compile time
- 5x other intentional patterns (autofocus, form state, log_lvl callbacks)
2026-03-05 20:50:39 -05:00
Scott Idem
73597cb8b4 chore: svelte-check cleanup — fix Svelte 5 patterns in events/pres_mgmt, badges, launcher, and tests
Source changes (0 errors, 175 warnings after):
- api_post__crud_obj_v3: add backward-compat migration aliases (for_obj_type/id, obj_type/id) to nested CRUD funcs
- ae_events__event_device/presenter/session: make event_id/presentation_id optional; fall back to store value
- element_ae_obj_field_editor_v3: import type Snippet properly; mark current_value as $bindable()
- ae_comp__badge_obj_view: fix $derived(() => false) → $derived(false) for show_receipt/show_tickets
- badge templates: pass explicit event_id param to delete/update calls
- launcher/+page: capture URL params as stable consts; pass event_id to update_ae_obj__event_device
- ae_comp__event_device_obj_li: wrap setInterval in $effect; onDestroy cleanup always registered
- ae_comp__event_device_obj_li_wrapper: move console.log to $effect; fix self-closing tag
- presenter form/menu/view/list: add missing event_presentation_id to all update/delete calls
- reports/locations/presenter/+page: move store assignments into $effect + untrack; ae_acct → $derived
- session/+page: add Comp_event_presenter_form_agree import; cast for type compat
- session_view: wrap <img onclick> in <button> for accessibility/validity
- ae_comp__event_presentation_obj_li: remove unneeded event_id/session_id from create_ae_obj__event_presenter
- ae_comp__event_session_obj_li: make lq prop optional; add plain-array fallback prop
- location/+page: refactor to $derived ae_acct, $effect+untrack for stores, simplified session/file sections
- location_page_menu: add optional data prop; export interface

Tests:
- Rename ae_events__event_badge.spec.ts → ae_events__event_badge.test.ts (extended coverage)
- All test files: 'warn' → 'warning' (Playwright API), addInitScript array-destructure pattern, import type fixes
- ae_defaults: remove duplicate hide_app_cfg key; meaningful sponsorship cfg_id placeholder
- create_event_badge.spec: fix import path to use $lib alias
- event_presenter.test: fix test URL to use /presenter/:id route

NOTE: location/+page.svelte — Element_manage_event_file_li_wrap no longer receives
allow_basic/allow_moderator (now default false); file list shows but management
actions may be restricted. Follow-up needed to restore auth__kv-based access.
2026-03-05 20:05:35 -05:00
Scott Idem
56419a097f Restrict access to Mangers and above. 2026-03-05 18:21:32 -05:00
Scott Idem
761fa69824 fix(badges): render QR code on badge front when show_qr_front is set
Fixed three issues in ae_comp__badge_obj_view.svelte:
1. Bug: {#await} block used src={qr_data_url} (the Promise) instead of src={result}
   (the resolved data URL) — QR image never displayed correctly on badge front.
2. Layout: .special div had no flex context; added flex-row + items-end so the QR
   sits at the bottom-right of the body section via ml-auto.
3. Cleanup: removed dead second QR block that awaited event_badge_qr_id_get_promise
   (always null), which could never render.

Also marks CRUD v2 refactor as complete in TODO__Agents.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 18:03:32 -05:00
Scott Idem
fdd1c88b35 fix(field-editor-v3): show edit buttons dimmed in edit mode, bright on hover
Previously buttons were opacity-0 until hover — invisible even in edit mode.
Changed to opacity-20 base so users can see which fields are editable, opacity-100 on hover.
Matches the behavior in element_data_store_v3.svelte.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 18:00:56 -05:00
Scott Idem
d846a39677 refactor: migrate Element_ae_crud v1/v2 usages to element_ae_obj_field_editor_v3
Replace all active Element_ae_crud (v1) and Element_ae_crud_v2 usages across
22 files with Element_ae_obj_field_editor_v3. Also remove 9 commented-out v1
imports that were dead code.

Key changes:
- Remove trigger_patch pattern; replace with direct api.update_ae_obj_v3() calls
- Replace field_value prop with current_value, on:ae_crud_updated with on_success
- Remove legacy props: api_cfg, hide_edit_btn, outline_element, show_crud,
  display_inline, display_block_edit (→ display_block), class_li
- field_type 'boolean' → 'checkbox', 'email' → 'text' (v3 has no email type)
- Replace core_func.update_ae_obj_id_crud_v2() with api.update_ae_obj_v3()
- Keep core_func where still used (QR code generation, person create)

Files: presenter_view, person_view, location_view, device_obj_li,
presentation_obj_li, session_view, launcher_file_cont, session_alert,
event/session/location/presenter page menus, leads exhibit tracking page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 17:38:02 -05:00
Scott Idem
cdf56aadcd refactor(crud): migrate v2 component usages to field-editor-v3
Replaces all active Element_ae_crud_v2 usages with Element_ae_obj_field_editor_v3,
and direct core_func.update_ae_obj_id_crud_v2 calls with api.update_ae_obj_v3.
Adds 'number' field_type to v3 editor. All on_success callbacks trigger SWR
refresh via events_func load functions so liveQuery updates Dexie correctly.

- element_ae_obj_field_editor_v3: add 'number' input type
- ae_comp__event_session_obj_li: replace core_func v2 API calls + dead import
- ae_comp__event_location_obj_li: migrate 2x Element_ae_crud_v2 (name, description)
- ae_tab__manage: migrate 7x Element_ae_crud_v2 (priority/checkbox, numbers, text, tiptap)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 16:39:29 -05:00
Scott Idem
c3ec0f88ee fix(field-editor-v3): layout shift, bindable crash, and optimistic display
- Fix layout shift on edit_mode toggle: always render the edit button
  (using invisible/pointer-events-none) so the flex container doesn't
  reflow when edit_mode is toggled on/off.

- Fix 'store.set is not a function' crash: remove $bindable() from
  current_value. The component is SWR-first; after a successful PATCH
  liveQuery updates the prop from Dexie. Trying to write back to a
  readonly liveQuery-derived prop caused the crash.

- Fix stale display after save: add has_optimistic flag + display_value
  derived. After a successful PATCH, display_value shows draft_value
  immediately without waiting for liveQuery. Cleared automatically when
  current_value catches up, or on cancel/re-open of edit mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 20:05:43 -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
795c12c1db fix(launcher): clean up poster session file display
Two issues in poster session view:

1. 'No files for this presenter' message always showed because
   file_count on the presenter is 0 — poster files are attached to
   the presentation, not the presenter. Removed the misleading message
   from launcher_presenter_view_posters.svelte with an explanation.

2. launcher_presentation_view.svelte was not passing hide_meta, so
   the OS toggle, date badge, and file size always rendered below the
   poster button. Added hide_meta={true} for poster files to match the
   clean display already used in launcher_presenter_view_posters.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 18:46:22 -05:00
Scott Idem
4aa1c2485d fix(launcher): guard hash_prefix_length select on native_device existence
native_device can be undefined during initial load (before device config
is fetched). The select bind:value was doing a hard property access that
threw a TypeError crash when native_device was null/undefined. Wrapped
in {#if $ae_loc.native_device} to defer rendering until it's available.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 18:34:19 -05:00
Scott Idem
0179471113 fix(launcher): break reactive loop causing tab crash on Event Files
The $effect in launcher_presentation_view.svelte was calling
load_ae_obj_id__event_presentation() on every prop update. The SWR
pattern in that function always fires a background Dexie write, which
triggered the upstream liveQuery, which updated the prop, which
re-ran the $effect — creating an infinite loop that saturated the API
and crashed the browser tab within 30-60 seconds.

Fix: guard on last_loaded_id so the API call only fires when the
presentation ID actually changes, not on every downstream re-render.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 18:30:49 -05:00
Scott Idem
c8c66a3514 fix(launcher): add sync pause toggle and reduce default polling intervals
The SWR pattern always fires a background API call on cache hits, so the
15s session interval created a continuous API stream even when data was
fresh. Increased all defaults: session 15s->60s, location/device 30s->60s,
presentation/presenter 45-60s->120s.

Added sync_paused guard to all six refresh functions and a Pause/Resume
toggle in the Sync Timers config section (visible without edit mode) so
testing and troubleshooting don't require a full reload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 18:19:51 -05:00
Scott Idem
9d2bab420b fix(launcher): restore digital poster session support
type_code was a prop in launcher_session_view that was never passed by
the parent layout, silently killing all poster-specific code paths
(sort order, section labels, poster presenter component, inline
presenter display). Fixed by deriving type_code directly from the
session LiveQuery object already present in the component.

Also adds a poster icon badge to the session list so poster sessions
are visually distinct from oral sessions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 17:39:41 -05:00
Scott Idem
eb35cd023a fix(idaa): restrict Show/Hide Disabled buttons to manager_access + edit_mode
Disabled items are treated as functionally deleted for all end clients
(including Trusted Access staff). Only Manager + Edit Mode should see
Show/Hide Disabled controls — previously using administrator_access and
missing edit_mode gate in BB and archives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 16:50:35 -05:00
Scott Idem
5687f247d3 fix(idaa, leads): add each-block keys; gate clipboard buttons to manager_access
- Add keyed {#each} to recovery meeting list and exhibit tracking list
  to satisfy Svelte's each-block-key lint rule and ensure correct DOM
  reconciliation on list updates
- Gate Zoom/Jitsi copy-to-clipboard buttons behind manager_access in
  both the recovery meeting list view and single meeting detail view

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 14:38:02 -05:00
Scott Idem
29093c45df fix(auth+ds): passcode re-entry bug and Data Store v3 business logic
- e_app_access_type: reset checked_passcode on clear so same passcode
  can be re-entered without a page refresh (guard was blocking re-entry)
- element_data_store_v3: wire display prop to wrapper CSS style;
  gate "not found" diagnostic to administrator+ or trusted+edit_mode;
  public/anonymous visitors no longer see missing block warnings
- +page.svelte: add manager_access exception to header/content class_li
  so managers can see "not found" diagnostics (matches footer pattern)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 14:31:29 -05:00
Scott Idem
fb78293fdf Bug fix for signing out with a passcode and then trying to sign back in with the same passcode without refreshing the page. 2026-03-04 13:31:43 -05:00
Scott Idem
b064d8c235 feat(leads): V3 API migration, QR Scanner v3, and Exhibitor Leads UI overhaul
- Migrate event_exhibit and event_exhibit_tracking CRUD to V3 API (parent_type/child_type params).
- Implement Element_qr_scanner_v3.svelte: A Svelte 5 / Runes component using html5-qrcode with auto-start and unique viewfinder IDs.
- Integrate QR Scanner v3 into ae_comp__badge_search.svelte and lead capture.
- Refactor Exhibitor Leads UI:
  - Add 'Rapid Scan' vs 'Qualify Mode' toggles for efficient lead capture.
  - Upgrade ae_comp__lead_detail_form.svelte to support new question/response schema with backward compatibility.
  - Implement 'Sign Out of Booth' functionality in exhibit management.
  - Optimize lead detail layout for mobile readability and high information density.
  - Fix component prop sync for event_id and exhibit_id.
- UI/UX refinements: standardizing icons (SquarePen), cleaning up unused imports, and improving responsive states.
2026-03-03 18:49:57 -05:00
Scott Idem
5c3823f41a feat(badges): implement badge print controls panel and refine badge overrides
- Create ae_comp__badge_print_controls.svelte: A fixed-right-edge panel for per-field accordion controls, font size adjustments, and inline editing.
- Refactor print/+page.svelte to integrate the new controls panel and standardize font size state management via $bindable() props.
- Update ae_comp__badge_obj_view.svelte and ae_comp__badge_review_form.svelte to correctly sync badge_type_code_override and badge_type_override.
- Improve badge_type_name derivation logic to prioritize staff overrides.
- Hide unused receipt/ticket sections in badge view pending future redesign.
- Update documentation (PROJECT and TODO) to reflect completion of Task 3.
2026-03-02 19:47:11 -05:00
Scott Idem
32e9550ca2 feat(badges): layout CSS system — data-layout attribute, @page injection, style_href
Two compiled layout CSS files in src/lib/ae_events/badges/css/:
  - badge_layout_epson_4x5_fanfold.css — 4"×5" per side, Epson ColorWorks C3500
    fanfold duplex; preferred for general conference use (ISHLT, demos)
  - badge_layout_zebra_zc10l_pvc.css — 3.5"×5.5" PVC card, Zebra ZC10L,
    single-sided (pair with duplex=0 on template)

Rules scoped under [data-layout="..."] on the wrapper — beats Tailwind utility
class specificity without !important. Vite hot-reloads these in dev.

Badge component: add data-layout attribute from template.layout field; import
both layout CSS files (falls back to Tailwind 4"×6" defaults if layout unset).

Print page svelte:head: inject @page paper-size rule per layout code
(@page cannot use attribute selectors so it must be dynamic). Also wire
style_href as a <link> for per-event external client branding CSS.

db_events.ts Badge_template interface: add style_href and duplex fields.
MODULE doc: update layout codes table with badge_4x5_fanfold entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 16:53:32 -05:00
Scott Idem
51cfcbf2d6 feat(badges): wire duplex field — hide badge back for single-sided templates
- Add duplex + style_href to properties_to_save so they're cached in IDB
- Derive show_badge_back: true when duplex is null/unset (safe default for
  existing templates) or 1; false when duplex=0 (single-sided, e.g. Axonius
  NYC using Zebra ZC10L PVC cards)
- Wrap entire badge_back section in {#if show_badge_back} so the DOM node
  is fully removed rather than just hidden — cleaner than a CSS class approach

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 16:06:51 -05:00
Scott Idem
827c7ac62e fix(badges): wire badge_type_list from template instead of hardcoded ISHLT list
Replace static `badge_type_code_li` array (hardcoded ISHLT 2024 types) with
`$derived.by()` that parses `$lq__event_badge_template_obj.badge_type_list` JSON.
Each event's template defines its own badge type set — the component now reflects
that correctly. Falls back to `[]` on missing/invalid JSON (hides the type select).

Also add MODULE__AE_Events_Badge_Templates.md documenting template field reference,
external CSS approach, layout codes, duplex field plan, and standard template setup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 15:43:49 -05:00
Scott Idem
9939d94970 feat(badges): badge review form + expand properties_to_save
ae_comp__badge_review_form.svelte:
- Full implementation of the badge review form (Task 1)
- Editable fields gated by access level (attendee / trusted / admin)
- Save/cancel with change detection, override revert buttons
- QR code display (hover zoom + click expand)
- Print status section, options/tickets, T&C block
- HTML rendering for name/title/affiliations/location fields
- Accessibility font-size toggle (text-2xl ↔ text-4xl)
- Help modal (Flowbite) with 6 sections
- Local edit mode — never writes to $ae_loc.edit_mode

ae_events__event_badge.ts:
- Add missing fields to properties_to_save so they are persisted to IDB:
  pronouns_override, phone, phone_override, registration_type(_code/_override),
  allow_tracking, agree_to_tc, other_1-8_code, ticket_1-8_code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 19:02:56 -05:00
Scott Idem
4b17ca9f59 docs(badges): mark Task 2 complete, document font controls and bug fix
- PROJECT doc: Task 2 status → complete (v1); added implementation
  details, default px values table, and note on future mm/inch iteration
- PROJECT doc: noted the review page field list bug fix (commit 011fc19a)
- MODULE doc: added "Recently Completed" section above Still Needed;
  cleared Badge Review Form and Print Font Controls from HIGH PRIORITY list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:58:59 -05:00
Scott Idem
3d7279da4c feat(badges): add print font size controls
- Add optional font_size_{name,title,affiliations,location} px props to
  ae_comp__badge_obj_view; when set, replaces auto inch-based sizing with
  an inline style so the caller controls the value
- Add screen-only (print:hidden) font size control panel to print page:
  four [−] [value] [+] [↺] rows, step 2px, null = auto (default)
  First + click activates at a sensible default that approximates the
  auto-sized inch values; ↺ button resets back to auto mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:49:58 -05:00
Scott Idem
011fc19a77 fix(badges): correct default field lists in review page
default_authenticated_fields was missing: pronouns_override, phone_override,
allow_tracking, agree_to_tc — so attendees couldn't edit those fields even though
the form rendered inputs for them.

default_trusted_fields had 'email' and 'badge_type_code' instead of 'email_override'
and 'badge_type_code_override', causing the can_edit() check in handle_save() to
silently drop those changes. Also added the full trusted field set: registration_type,
other_1-8_code, ticket_1-8_code, hide, priority, notes.

Also removed unused lucide imports (ShieldCheck, User, UserCheck) left over from
the removed access level indicator block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:37:25 -05:00
Scott Idem
c4e85b1fe3 feat(badges): print/review pages, 4-button list, Lucide icons, permissions doc
Badge search results list (ae_comp__badge_obj_li):
- 4 action buttons per row: Print, Review (nav link), Copy Link (clipboard), Email Link
- Visibility rules: unprinted-only for non-edit mode; all non-hidden for trusted+edit
- Plain name display (User/EyeOff icon) — name is no longer a print link
- Obscured email for non-trusted users
- Debug row (ID, CR, UP, PC, FP, LP) in edit mode
- All icons converted to Lucide (Font Awesome removed)

Badge print page (/print):
- 3 header action buttons: Print Now, Review (nav), Email Link
- Removed old [badge_id]/+page.svelte placeholder (moved to trash)
- Added is_trusted, is_edit_mode, print state derived vars
- "Already printed Nx — last [timestamp]" warning inline with name
- Removed unused imports (browser, onMount, events_slct)

Badge review page (/review):
- 3 header action buttons: Print (nav), Copy Link (clipboard), Email Link
- Added events_loc for email placeholder + title event name
- Added is_edit_mode, print_count, is_printed, copy_status
- FA icons replaced with Lucide (ShieldCheck, UserCheck, User)
- Title now includes event name (was missing)

Infrastructure:
- print/+page.ts and review/+page.ts added (non-blocking badge loaders)
- ae_comp__badge_review_form.svelte stub created (fields pending)
- Fixed: components no longer write to $ae_loc.edit_mode (critical bug)

Docs:
- NEW: AE__Permissions_and_Security.md — full permissions hierarchy reference
- NEW: PROJECT__AE_Events_Badges_Review_Print.md — agent task brief for review form + print font controls
- UPDATED: MODULE__AE_Events_Badges.md rev 5 — field permissions spec, header buttons, still-needed list by priority

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 15:12:22 -05:00
Scott Idem
ee500a9ad5 Saving notes and things 2026-02-26 18:52:15 -05:00
Scott Idem
911a427757 docs: add IDAA client module doc, minor whitespace cleanup
- CLIENT__IDAA_and_customized_mods.md: New comprehensive doc covering IDAA
  architecture, all 4 submodules (Archives, BB, Recovery Meetings, Jitsi),
  Novi UUID auth system, permission levels, state stores, iframe integration,
  and testing requirements. Reverse-engineered from source 2026-02-26.
- MODULE__AE_Events_Badges.md: trailing whitespace only
- tests/README.md: blank line only
2026-02-26 18:50:20 -05:00
Scott Idem
8cb8195ecd docs(api-guide): document flat vs nested URL path rules (section 4)
Describes which object types are always-flat (never nested in URL) for
ALL operations, vs event sub-objects which use nested paths for mutations
but flat paths for all reads (GET, list, search, delete).

Always-flat objects:
- Core: account, activity_log, address, contact, hosted_file, organization,
  page, person, site, user
- Other: archive, event, journal, post

Event sub-objects (event_badge, event_session, etc.) use nested
create_nested_obj_v3 / update_nested_obj_v3 for POST/PATCH, but flat
paths for everything else.

Includes Playwright mock URL patterns for each operation type.
2026-02-26 18:47:50 -05:00
Scott Idem
7c6f264266 docs: clarify Electron scope, update badge test lessons, all badge tests passing
- AE__Architecture.md: Add section 7 -- Runtime Environment: Browser vs Electron.
  Electron is ONLY for Events Pres Mgmt Launcher. Badge printing (and everything
  else) works via standard browser window.print(), no Electron needed.
- MODULE__AE_Events_Badges.md rev 3: Update print section to browser-first approach,
  remove Electron from print workflow, add two new test lessons (flat API URLs,
  CSS attribute vs DOM property), mark all tests passing.
- TODO__Agents.md: Add completed data integrity test fixes item, add window.print()
  wiring to upcoming tasks, expand input field audit note.
2026-02-26 18:10:08 -05:00
Scott Idem
f5e98b8c0d fix(tests): fix all 4 failing badge data integrity tests
- Badge search mock: was checking nested URL /v3/crud/event/{id}/event_badge/search
  but search_ae_obj_v3 uses flat path /v3/crud/event_badge/search
- Template list mock: was checking nested path, fixed to flat /v3/crud/event_badge_template/
  with for_obj_id query param (matches get_ae_obj_li_v3 behavior)
- Badge objects: add _random ID fields (event_badge_id_random, id_random, event_id_random)
  required for Dexie IDB processing
- Template edit assertion: input[value*=...] CSS checks HTML attribute not DOM property;
  Svelte bind:value sets DOM property only — fix to use getByLabel('Template Name')
- Relax null body check: the debug panel footer can contain 'null' legitimately
2026-02-26 18:08:25 -05:00
Scott Idem
5a16772639 feat(badges): auto-navigate to badge search after print 2026-02-26 17:36:45 -05:00
Scott Idem
d1ded2d45e test(badges): pass attendee workflow test (edit → print → return)
- Add data-testid to badge edit/save/cancel/print buttons and professional title input
- Fix API mock routes (badge GET uses /v3/crud/event_badge/{id}, not nested)
- Add badge_template mock so badge view renders
- Add event_badge_id_random and id_random fields to all mock responses
- Split workflow test into its own test() instead of being inline in beforeEach
- Use data-testid selectors throughout for stability
2026-02-26 17:28:08 -05:00
Scott Idem
657a7122b8 docs(test): update workflow test comments for multi-word search
Test continues to use 'scott idem' (full name) now that backend
supports multi-word search with AND logic. Updated status notes.
2026-02-26 17:08:58 -05:00
Scott Idem
dc0f3066b3 fix(badges): support multi-word fulltext search
- Split search query by spaces
- Apply AND logic: all words must match
- Single word: LIKE '%word%'
- Multi-word: LIKE '%word1%' AND LIKE '%word2%'

Example: 'scott idem' now searches for both 'scott' AND 'idem'
Previously searched for literal 'scott idem' phrase which failed.

Fixes search bug discovered during Playwright test development.
2026-02-26 17:08:27 -05:00
Scott Idem
ba10b8f996 test: add attendee badge workflow test (WIP)
- Simulates complete check-in: navigate → search → view → edit → print → return
- Mocks V3 API for event, badge search, and PATCH operations
- Tests override field editing (professional_title_override)
- Documents future attendee review feature (email link workflow)

**STATUS: WIP** - Search results not displaying in test environment
- API mocks configured correctly
- IDB cleared properly, schema managed by app
- Store initialization includes qry__remote_first flag
- Issue: Badge list not rendering after search (timing or store issue)
-  Screenshot saved to test-results/ for debugging
2026-02-26 17:01:10 -05:00
Scott Idem
5514d579d0 test: centralize demo IDs and site_domain mock in env.ts
- Added testing_site_id, testing_site_domain_id, testing_fqdn, testing_person_id
- Created mock_site_domain object matching +layout.ts expectations
- Updated minimal_v3_mocks.ts to use mock_site_domain
- Fixes 'Domain Not Registered' overlay in Playwright tests
- All test IDs now documented with comments from tests/README.md
2026-02-26 16:43:36 -05:00
Scott Idem
908b4dbef9 docs(badges): clarify badge types vary by Event/Template 2026-02-26 16:26:44 -05:00
Scott Idem
ed7542b5dc docs(badges): comprehensive module documentation
COMPLETE DOCUMENTATION:
- Override fields pattern (*_override) and sync behavior
- External system integration (iMIS, Zoom, Novi, Impexium, Confex, Cvent)
- Cron sync safety rules (override fields NEVER overwritten)
- Access level permissions (Authenticated → Super)
- Search/filter capabilities (fulltext, badge type, print status, affiliations)
- Quick edit implementation details
- Database schema and API functions
- Component architecture
- Testing status and known issues
- Development guidelines

KEY CONCEPTS DOCUMENTED:
1. Override fields protect staff/attendee edits from automated syncs
2. Data flow is pull-only (read from external systems, never push back)
3. Display priority: override field → regular field → fallback
4. Access-based editing (currently placeholder, future JSON config)
5. All 7 override fields explained with examples

SYNC BEHAVIOR CLARIFIED:
- Cron jobs CAN update regular fields
- Cron jobs CANNOT touch override fields
- Manual staff edits CAN update any field
- Attendee self-service limited to specific overrides per event config
2026-02-26 16:17:39 -05:00
Scott Idem
b28595dad8 fix(badges): return processed data from all badge API functions
CRITICAL FIX (same pattern as event_file fix):
- search__event_badge(): Now returns processed_obj_li instead of unprocessed result_li
- load_ae_obj_id__event_badge(): Returns processed object after IDB save
- load_ae_obj_li__event_badge(): Returns processed list after IDB save
- create_ae_obj__event_badge(): Returns processed object after IDB save
- update_ae_obj__event_badge(): Returns processed object after IDB save

Pattern: All functions now return what was actually saved to IDB (processed data)
This ensures consistency between API return values and IDB cached data.

TEST IMPROVEMENTS:
- Add full_name field to badge mock data (not just full_name_override)
- Ensures mock data matches real API structure
2026-02-26 15:59:25 -05:00
Scott Idem
96cfb8c1b0 Quick save of test page 2026-02-26 15:49:05 -05:00
Scott Idem
a91c648c61 test: standardize naming conventions to snake_case
- Rename demo_event_id → testing_event_id (more explicit)
- Rename demo_account_id → testing_account_id (matches convention)
- Rename demo_badge_id → event_badge_id (descriptive)
- Rename demo_template_id → event_badge_template_id (explicit)
- Update all test files for consistency (15 files)
- Enhance README with organized test data sections
- Update person IDs to match README test data
- No regression: 15 tests passing, 7 pre-existing failures unchanged
2026-02-26 15:43:31 -05:00
Scott Idem
2c289e39de test(badges): Add badge cold-start and data integrity tests
- Add coldstart_event_badges_list.test.ts: badge list on empty IDB
- Add event_badge_data_integrity.test.ts: field mapping, templates, Electron compatibility
- 6 of 12 badge tests passing (3 original + 3 new)
- Tests cover: CRUD, interactions, cold-start, Electron bridge graceful degradation
- Ready for client demo
2026-02-26 14:57:08 -05:00
Scott Idem
9547da6da6 fix(events): Fix pres mgmt reports data loading and field mapping
- Fix session detail page params not being passed to component (causing undefined session_id)
- Fix wrapper components discarding API enriched data by re-fetching from IDB
- Fix event_file, event_session, event_presenter wrappers to preserve API data
- Add optional chaining for hash_sha256 field to prevent undefined crashes
- Fix search__event_file to always process API results before returning
- Ensures hosted_file_size -> file_size field mapping for reports
- All pres mgmt reports (files, sessions, presenters) now work on cold-start
2026-02-26 14:36:46 -05:00
Scott Idem
61025ea0d5 Docs: Add CLAUDE.md project context file for AI agent coordination
Project-specific context file for Claude Code containing:
- Critical privacy/business rules (IDAA, Journals)
- Mandatory workflows and tech stack
- API patterns and key documentation references
- Active issues tracking

Complements global ~/.claude/CLAUDE.md with SvelteKit-specific guidance.
2026-02-26 13:48:01 -05:00
Scott Idem
e34dd83436 Docs: Update README to mention DexieJS 4.x in tech stack 2026-02-26 13:47:43 -05:00
Scott Idem
fd5c36f836 Test: Add Playwright test for session cold-start bug fix
New test validates that presentations AND presenters render on first
navigation when IndexedDB is empty (cold-start scenario).

Test Configuration:
- Demo Session: DOW3h7v6H42 (703) 'How To Do Things'
- Demo Presentation: 7U2eXSjR6H4 (1670) 'Build a House'
- Demo Presenter: gT-hxnifb-0 (2202) 'Bob The Builder'

Tests:
1. UI visibility: Session name, presentation, and presenter all visible
2. IDB integrity: Verifies all nested data written to IndexedDB

Both tests passing - confirms fix works correctly.
2026-02-26 13:47:07 -05:00
Scott Idem
3c8a6feda0 Docs: Document session cold-start bug fix and mark project resolved
- Updated Dexie/liveQuery guide with detailed explanation of the
  try_cache + microtask yield bug pattern
- Marked session view refactor project as RESOLVED (2026-02-26)
- Added inline code comments to all three fixed loader functions
  explaining the critical fixes

Documents the 'refresh twice' bug resolution for future reference.
2026-02-26 13:43:34 -05:00
Scott Idem
32f8a75373 Refactor: Add microtask yields to journals module for consistency
Added await Promise.resolve() yields after IndexedDB writes to ensure
Dexie observers fire before function returns. Aligns with pattern
established in event loaders fix.

Journals module was already working correctly (preserved try_cache),
but adding yields ensures consistent timing behavior across all
nested data loading patterns.
2026-02-26 13:42:27 -05:00
Scott Idem
3849118fec Fix: Event session cold-start bug - presentations/presenters now load on first render
CRITICAL BUG FIX: Session view required 1-2 manual refreshes to display
presentations and presenters when IndexedDB was empty.

Root Cause:
- Nested loaders passed try_cache: false, preventing IDB writes
- Missing microtask yields caused race conditions between IDB writes
  and liveQuery subscriptions

Changes:
- ae_events__event_session.ts: Preserve try_cache in nested loads
- ae_events__event_presentation.ts: Block on presenter loads with await
  Promise.all() + preserve try_cache
- ae_events__event_presenter.ts: Add microtask yield after IDB write

Result: Presentations AND presenters now render correctly on first
navigation without requiring manual page refresh.
2026-02-26 13:40:36 -05:00
Scott Idem
9da3e5326b docs: reorganize and rename documentation files for consistency
- Apply consistent prefix naming: AE__, GUIDE__, PROJECT__, MODULE__, TODO__
- Move superseded/session docs to documentation/history/
- Migrate old/ directory contents to history/ with updated naming
- README.md: replace stale Modules section with accurate current routes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 10:49:23 -05:00
Scott Idem
b1162b9f08 I am done. Just saving things for the night. Not a good day. 2026-02-25 20:17:03 -05:00
Scott Idem
95a56d25bf test: stabilize cold-start Playwright tests; fix editor bind:new_content fallback 2026-02-25 20:15:09 -05:00
Scott Idem
7b5c7528b6 All of the changes from today (Wednesday February 25, 2026) need to be reviewed. We spent over 6 hours trying to fix the page loading when viewing a fresh Session ID and Presentation ID. OpenAI and Gemini failed hard!!! I am at a lost and frustrated. I will probably need to deal with this myself. :-/ 2026-02-25 18:34:21 -05:00
Scott Idem
17620b6fbc refactor(events): enforce hierarchy consistency and id standardization 2026-02-25 17:53:20 -05:00
Scott Idem
197adff33b tests: add Playwright event badge CRUD + interaction tests; add minimal V3 mocks and env helper; update presenter/badge smoke tests to use helpers; convert test locals to snake_case 2026-02-24 17:49:43 -05:00
Scott Idem
39614c9cc2 test: remove stale tests/disabled/example.test.ts 2026-02-24 16:45:45 -05:00
Scott Idem
a7f6101cce test: move and rename private-network check to tests/private_network.test.ts; make diagnostic 2026-02-24 16:42:10 -05:00
Scott Idem
f2c426b595 test: combine and modernize private-network checks; remove legacy disabled variants 2026-02-24 16:38:36 -05:00
Scott Idem
7f9f93765d test: modernize v3 API security tests (mock /v3/, deterministic headers); add runnable modern copy 2026-02-24 16:36:27 -05:00
Scott Idem
c70e7a3bf9 test: add badge interaction test + README; ignore disabled tests in Playwright config 2026-02-24 16:32:50 -05:00
Scott Idem
e57bbca33e Work on CORS testing and fixes. Chrome and pfSense with the DNS resolution found a problem. Should be fixed now. 2026-02-23 18:18:58 -05:00
Scott Idem
30e44a0af1 Testing tests with Playwright 2026-02-21 10:05:21 -05:00
Scott Idem
7724cac492 Saving notes 2026-02-20 19:45:54 -05:00
Scott Idem
399455b67a feat(testing): Add foundational Playwright security test
Adds the first successful, stable Playwright integration test for the SvelteKit frontend. This test serves as a template for future UI/UX and integration tests.

Key outcomes and learnings captured in this test:
- Establishes a pattern for running tests against the high-speed 'npm run dev' server.
- Verifies critical API security header logic, including the unauthenticated bypass for lookups and account ID scavenging from localStorage.
- Solves application boot crashes in a test environment by injecting a complete, default 'ae_loc' state object into localStorage before the app hydrates.
- Demonstrates the correct, race-condition-free pattern for waiting on network requests that are handled by API mocks.

This commit also removes the old, unreliable Node.js-based scripts ('verify_jwt_logic.js', 'verify_jwt_sync.js') that these new tests replace.
2026-02-20 19:43:01 -05:00
Scott Idem
06dbccaaa4 Reorganized System Lookups page and added Time Zone priority filtering.
- Sequenced sections as Country, Subdivisions, then Time Zones.
- Implemented a reactive toggle for 'only_priority' in the Time Zone section to support high-priority filtering.
- Switched to a full-width card layout for improved readability of large datasets.
2026-02-20 18:03:42 -05:00
Scott Idem
2dfc8a5034 Implemented 'only_priority' filtering for V3 Lookups.
- Updated V3 lookup API and core timezone loader to support the 'only_priority' flag.
- Enabled high-priority timezone filtering in IDAA Recovery Meeting and Archive editors to streamline selection lists.
2026-02-20 17:17:09 -05:00
Scott Idem
52c9e765a0 Implemented Aether API V3 Lookup integration for standardized tables.
- Created 'src/lib/ae_api/api_get__lookup_v3.ts' to handle the new '/v3/lookup/{lu_type}/list' endpoint.
- Refactored 'get_ae_obj_li_for_lu' in 'api.ts' to prioritize the V3 system for countries, subdivisions, and time zones, with V2 fallback for legacy types.
- Ensured lookups use the 'x-no-account-id' bypass for unauthenticated site bootstrapping.
- Updated core country, subdivision, and time zone loaders to use the refined API interface.
2026-02-20 16:12:06 -05:00
Scott Idem
b4d85f4618 Saving notes 2026-02-19 19:20:16 -05:00
Scott Idem
e2e120456e Enhanced Events Launcher Location loading and instructions.
Implemented explicit 'enabled' and 'hidden' parameter support in the Event data layer ('load_ae_obj_id__event').

Updated the Events Launcher layout and background sync engine to proactively fetch all enabled locations (including hidden ones), ensuring the room select list is complete and stays updated.

Refined 'launcher_file_cont.svelte' to only display native-specific file opening instructions when 'app_mode' is 'native'.

Updated AGENT_TODO.md to reflect task completion and new priorities.
2026-02-19 17:54:21 -05:00
Scott Idem
0a689be25d Implemented 'inc_hosted_file' support and expanded data mapping for Event Files.
Updated the Event File data layer to support the 'inc_hosted_file' flag in load and search functions, enabling on-demand retrieval of joined Hosted File metadata.

Refined the data mapping in 'process_ae_obj__event_file_props' to include 'content_type' and strictly controlled which properties are persisted to IndexedDB, adhering to the 'Bite-Sized Data' principle by excluding unneeded backend fields like subdirectory paths.

Enhanced 'get_ae_obj_li_v3' to support generic parameter pass-through for V3 CRUD operations.
2026-02-19 17:15:12 -05:00
Scott Idem
e5b94123a5 Fixed Event File data mapping for V3 API and updated AGENT_TODO.
Mapped prefixed backend fields 'hosted_file_hash_sha256' and 'hosted_file_size' to flat 'hash_sha256' and 'file_size' properties in the Event File data layer. This resolves component crashes (TypeError: slice on null) by ensuring IndexedDB is populated with valid hash strings.

Updated AGENT_TODO.md to reflect recently completed tasks.
2026-02-19 15:19:19 -05:00
Scott Idem
4a7ac31197 Resolved Svelte check errors and modernized component event handling.
Implemented Svelte 5 callback props (onsuccess, oncancel) for Badge create and upload forms, replacing legacy dispatchers.

Updated the AE Field Editor to accept an optional 'id' prop, resolving property mismatch errors.

Updated the Event Settings page to use the new callback prop interface, clearing type assignment errors reported by 'npm run check'.
2026-02-16 19:11:59 -05:00
Scott Idem
de7e8443c8 Making the Debug Menu not always shown as easily. 2026-02-16 17:22:36 -05:00
Scott Idem
f96f7069a4 Stabilized hierarchical permissions and implemented strict visibility gating.
Standardized access level hierarchy (super > manager > administrator > trusted) and added hierarchical comparison utilities to 'ae_util'.

Refactored IDAA layout to use an 'Upgrade-Only' permission strategy, preventing context-specific identifications from downgrading global Manager privileges.

Implemented strict gated filtering in the Journal Entry list: hidden and disabled items now correctly require both the appropriate hierarchical role (Trusted/Admin) AND active Edit Mode.
2026-02-16 17:12:24 -05:00
Scott Idem
fb724411d3 Initialized Activity Log IndexedDB infrastructure and refactored Jitsi reports.
Added 'activity_log' table to Dexie 'ae_core_db' (v4) to support local caching of tracking data.

Implemented 'process_ae_obj__activity_log_props' with robust timestamp fallbacks.

Refactored 'qry__jitsi_report' to follow the Frontier module pattern, ensuring consistent V3 search and local cache synchronization.
2026-02-16 16:13:04 -05:00
Scott Idem
4711f41c34 Expanded Recovery Meeting sort options and refined development SOP.
Implemented 'Meeting Name (A-Z)' and 'Meeting Name (Z-A)' sorting for Recovery Meetings, including UI dropdown updates and client-side re-sorting logic.

Updated documentation/GUIDE__DEVELOPMENT.md to v1.1 with clarified verification steps and inter-agent coordination protocols.

Minor label cleanup in Journal editor.
2026-02-16 15:58:17 -05:00
Scott Idem
f34e24aa02 Standardized robust chronological sorting across modules.
Updated '_process_generic_props' in multiple libraries to ensure 'updated' timestamp always falls back to 'created_on' or epoch start, preventing null values from breaking newest-first ordering in IndexedDB.

Aligned Recovery Meetings and Archives list views to use these pre-computed sort keys for consistent UI behavior even when 'updated_on' is null.
2026-02-16 15:40:00 -05:00
Scott Idem
fdfba0f752 Clean up of the email notifications for Post, Post Comment, and Event create and update. 2026-02-16 13:34:38 -05:00
Scott Idem
6c4cdb4ed0 Notes 2026-02-13 19:52:45 -05:00
Scott Idem
b03888d37f Serious notes about security updates. 2026-02-13 19:21:51 -05:00
Scott Idem
f62bd9fb79 security(api): harden V3 authentication and unify CRUD endpoint patterns
Implemented critical security and architectural fixes to align the frontend with the Aether API V3 standard and resolve 403 Forbidden race conditions.

- Unified CRUD Helpers: Updated get, create, update, and delete helpers to use the standard /v3/crud/{obj_type}/{id} paths, ensuring correct backend isolation context.
- Auth Scavenging: Implemented direct localStorage scavenging for 'x-account-id' in core fetch helpers to prevent hydration race conditions in Svelte 5.
- Header Cleanup: Purged redundant 'x-aether-api-token' and fixed misplaced protocol headers in global stores.
- Reliability: Fixed 'Content-Type' typos and standardized kebab-case header normalization.
2026-02-13 19:10:32 -05:00
Scott Idem
3e83890932 feat(framework): implement AE Obj Field Editor v3 and test playground
- Created consolidated AE_Obj_Field_Editor_V3 component using Svelte 5 Runes.
- Standardized on V3 CRUD API (PATCH /v3/crud/{obj_type}/{obj_id}).
- Implemented local state guards to prevent reactivity loops.
- Added support for multiple field types (text, textarea, select, checkbox, tiptap).
- Created a dedicated testing playground with real Demo account data.
- Updated the PROJECT_AE_OBJECT_FIELD_EDITOR_V3_UPGRADE.md plan.
2026-02-13 16:07:21 -05:00
Scott Idem
6bfd13f52c refactor(events, idaa): standardize on String-only IDs and remove '_random' suffix
Aligned the Events Badges, Templates, and IDAA Bulletin Board modules with
Aether UI/UX v3 standards by prioritizing semantic String IDs (e.g., event_id,
badge_id) over legacy '_random' variants in API helpers, Dexie processors,
and UI components.

- Refactored badge and template loaders to use clean ID field names.
- Updated search and LiveQuery logic in badge management views.
- Cleaned up unused imports and legacy code in +layout and +page components.
- Standardized ID mapping in generic object processors.
- Improved IDAA post creation UX with autofocus and empty title defaults.
2026-02-13 13:51:55 -05:00
Scott Idem
de947c827a ui(hosted_files): update clipping components and fix count reference bug 2026-02-12 19:57:42 -05:00
Scott Idem
5998389e3a ui(hosted_files): improve upload component accessibility and dark mode 2026-02-12 19:57:25 -05:00
Scott Idem
52ff50457d ui(hosted_files): update video util page for dark mode consistency 2026-02-12 19:57:10 -05:00
Scott Idem
4f4f16478d refactor(core): adjust root layout data structure and clean up site config 2026-02-12 15:29:17 -05:00
Scott Idem
a7e17283e4 fix(events): robust date sorting in core event processors 2026-02-12 15:24:18 -05:00
Scott Idem
60771c5ba1 fix(journals): robust date sorting in journal and entry processors 2026-02-12 15:23:22 -05:00
Scott Idem
c4009391c0 fix(posts): robust date sorting in post and comment processors 2026-02-12 15:22:28 -05:00
Scott Idem
5e9457580c fix(bb): robust date sorting in Bulletin Board list 2026-02-12 15:21:44 -05:00
Scott Idem
64be14249c ui(recovery_meetings): update meeting editor and search components 2026-02-12 15:07:37 -05:00
Scott Idem
4266bef352 ui(bb): update post list surface colors 2026-02-12 15:07:05 -05:00
Scott Idem
6977867ed8 feat(idaa): add IDAA module dashboard 2026-02-12 15:06:10 -05:00
Scott Idem
299367ae62 Updating notes 2026-02-11 19:29:26 -05:00
Scott Idem
f785907792 Updating the documentation for things. Hopefully improvements to Gemini CLI. 2026-02-11 17:59:09 -05:00
Scott Idem
7549749d14 Launcher: Resolved session selection hang and improved reactivity pattern. 2026-02-11 17:18:55 -05:00
Scott Idem
cda7a5421c General code clean up. Removing old stuff. 2026-02-11 13:41:41 -05:00
Scott Idem
3ffe9cbcc9 More general code clean up. Updating Lucide icons. 2026-02-11 13:21:38 -05:00
Scott Idem
3ca041383f More general code clean up. More removing of the _random bits. 2026-02-11 13:00:42 -05:00
Scott Idem
87d74e5b4b Just general code clean up. Also removing more of the extra _random bits. 2026-02-11 12:51:55 -05:00
Scott Idem
5bea72f02e Doing general clean up to remove old code and keep removing the _random bits that I find. Bug fix for new hosted files not showing immediately for BB Posts. Just directly updating the selected Post and its linked_li_json. 2026-02-11 12:39:20 -05:00
Scott Idem
ae03cbb27f Saving notes 2026-02-10 19:45:45 -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
d300825312 fix(sync): restrict room refreshes to selected session to prevent flickering
- Optimized background API refreshes to focus on the selected session instead of the entire room.\n- Eliminated redundant bulk database writes that were causing UI flickering during refresh cycles.\n- Staggered initial data fetches to prevent API request storms.\n- Added detailed logging for easier tracing of background sync operations.
2026-02-10 18:38:39 -05:00
Scott Idem
ab2b984c25 fix(launcher): resolve metadata flickering and harden session reactivity
- Standardized default view to 'alt' for session loading to ensure persistent file counts.\n- Hardened 'No files' warning logic to prevent false positives during background refreshes.\n- Restored reactive session observable in layout for global header and idle logic.\n- Documented overwrite pitfalls in SVELTE_DEXIE_GUIDE.md.
2026-02-10 18:31:59 -05:00
Scott Idem
475954b791 feat(sync): persist polling intervals across all Launcher variants
- Migrated background timers to persistent 'sync_intervals' store.\n- Updated Sync Monitor and Config UI to display and edit all six polling loops.\n- Ensured timer settings are applied and persisted regardless of native mode status.\n- Refined initialization logic to prioritize user configuration with robust fallbacks.
2026-02-10 18:28:13 -05:00
Scott Idem
b91adfd8cf feat(sync): implement dedicated presentation and presenter refresh loops
- Added background API refresh loops for room-level presentation and presenter metadata.\n- Updated Sync Monitor UI to display all six loop intervals.\n- Added UI controls in Launcher Config for fine-tuning polling periods.\n- Staggered initial background fetches to prevent API connection flooding.\n- Corrected timer assignments to separate structural metadata from room content.
2026-02-10 18:15:34 -05:00
Scott Idem
ddefd94f03 feat(sync): implement separate API refresh loops for presentations and presenters
- Added dedicated background timers for room-level presentation and presenter metadata.\n- Staggered initial data fetches to prevent request storms during room entry.\n- Cleaned up redundant timer assignments and separated API refresh from native file sync.\n- Optimized loop frequencies for better balance between freshness and performance.
2026-02-10 18:13:16 -05:00
Scott Idem
cf18257fb4 fix(sync): refine heartbeat logic and silence non-native warnings
- Silenced 'Heartbeat skipped' warnings when not running in native Electron mode.\n- Hardened device ID retrieval using String-Only ID prioritization logic.\n- Improved reliability of background sync cycles during session transitions.
2026-02-10 18:04:19 -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
be53e12d63 perf(launcher): implement staggered data loading for sessions and presentations
- Optimized session list load to be shell-only, preventing initial request storms.\n- Moved deep data fetching (presenters/files) into the Presentation component level using Svelte effects.\n- Deferred child collection lookups until components are rendered in the DOM.\n- Fixed ae_api import in launcher_presentation_view.\n- Hardened background refresh logic to respect requested views.
2026-02-10 17:30:30 -05:00
Scott Idem
8a05e48514 fix(launcher): resolve hydration errors and missing file counts
- Initialized event_session_id to null in store template to prevent Svelte 5 binding errors.\n- Fixed invalid bind expressions in Launcher layout.\n- Corrected view: alt propagation in session list background refresh to ensure file counts are retrieved.\n- Hardened duplicate launch protection with verify_hash enabled by default.\n- Updated SVELTE_DEXIE_GUIDE.md with binding pitfall documentation.
2026-02-10 16:00:13 -05:00
Scott Idem
900105913b Documentation clean up. 2026-02-10 14:08:35 -05:00
Scott Idem
b5b44e4016 feat(launcher): harden native caching and telemetry; consolidate documentation
- Implemented SHA-256 integrity checks and stale temp purge in native download logic.\n- Enabled strict hash verification in background sync cycle.\n- Made CPU load gauge dynamic using real-time loadavg metadata.\n- Consolidated native app documentation into single master manual.\n- Marked legacy electron_native.js as deprecated.\n- Updated roadmap with temp cleanup and launch protection tasks.
2026-02-10 14:07:45 -05:00
Scott Idem
6abfff293c refactor: bulk convert decorative labels to spans across core modules
Addresses 'a11y-label-has-associated-control' warnings by converting
non-functional styling labels into spans with 'block' layout.
2026-02-09 22:00:49 -05:00
Scott Idem
f24449457f refactor: address Svelte compiler warnings and improve a11y/security
- Convert decorative labels to spans to fix a11y warnings in Accounts and Sites pages
- Add rel="noopener noreferrer" to external links for security
- Use untrack for Svelte 5 state initialization from props in e_app_sign_in_out.svelte
- Make TipTap editor styles global to fix scoping warnings for dynamic content
2026-02-09 20:22:51 -05:00
Scott Idem
c1750dd04e fix(data_store): implement hierarchical priority logic in liveQuery; update project plans 2026-02-09 19:03:06 -05:00
Scott Idem
fe4380f819 Standardize Exhibit Leads module: Rich text support and naming alignment
- Integrated TipTap rich text editor for Booth Descriptions and Exhibitor Notes.
- Implemented strip_html utility for clean search/preview of rich text fields.
- Renamed exhibit loading functions to follow load_ae_obj_id__event_exhibit* pattern.
- Hardened property processors with 'undefined' string guards and automatic reload triggers.
- Resolved type mismatches and naming inconsistencies across the Leads module.
- Verified zero-error state via svelte-check.
2026-02-09 15:30:00 -05:00
Scott Idem
e7895cee07 Fix Exhibit Licensee list parsing and standardize terminology
- Hardened 'Comp_exhibit_license_list' to handle both raw JSON and IDB object formats.
- Updated all labels to 'Licensed Leads User' or 'Licensee' for consistency.
- Verified Admin-only restriction for the licensee management section.
- Fixed silent parsing failures that caused empty staff lists.
2026-02-08 23:17:47 -05:00
Scott Idem
c88904beb1 Implement 'Already Added' detection for Lead Capture
- Added real-time checking for existing leads in manual search.
- Implemented 'Already Captured' status for QR scanner with direct 'View Lead' link.
- Resolved all remaining TypeScript typing issues in capture components.
- Optimized Dexie lookups for existing lead detection.
2026-02-08 23:08:36 -05:00
Scott Idem
1dd80cc974 Implement dynamic Custom Questions editor in Lead Detail view
- Added 'Comp_lead_detail_form' for editing licensee responses.
- Implemented reactive form generation based on Exhibit question definitions.
- Wired up 'Edit Mode' toggle in Lead Detail page.
- Added CRUD editors for lead notes, priority, and visibility status.
2026-02-08 23:06:09 -05:00
Scott Idem
111ef76d14 Fix Lead List filtering and resolve Svelte 5 subscription crash
- Refactored Lead List to use direct observable subscription for better reactivity.
- Implemented in-memory 'Hard Guard' filter to ensure licensee selection is strictly enforced.
- Fixed 'TypeError: s.subscribe is not a function' by removing legacy $ prefix from resolved props.
- Resolved TypeScript typing errors in Lead Detail and Search components.
- Migrated Badge Search icons to Lucide.
2026-02-08 23:03:35 -05:00
Scott Idem
8787f8c2ff Harden Licensed User dropdown logic and resolve data parsing errors
- Implemented dual-format parsing for 'license_li_json' (handles raw string and IDB objects).
- Added fallback lookup by 'event_exhibit_id_random' for robust record resolution.
- Fixed reactivity for licensee dropdown population.
- Hardened default selection logic based on Aether access levels.
2026-02-08 22:25:56 -05:00
Scott Idem
68075d37a1 Standardize on Licensed Exhibit Leads User (licensee) terminology
- Renamed all staff-related fields and variables to 'licensee'.
- Implemented correct filtering logic for Aether Admins (default All, hide My).
- Implemented correct filtering for booth users (default My, show colleagues).
- Populated dropdown labels with Full Names from license_li_json.
- Removed 'Shared Passcode' from the Lead List filter.
2026-02-08 21:28:16 -05:00
Scott Idem
224e03405c Remove redundant 'View Leads List' button from Add Lead tab 2026-02-08 20:00:57 -05:00
Scott Idem
f2e5fccc8d Implement auto-prefill for Exhibitor shared passcode for trusted users 2026-02-08 19:55:27 -05:00
Scott Idem
16acae03d0 Implement exhibitor search constraints and sync telemetry
- Restricted public exhibitor search to Priority (paid) items only.
- Enforced 3-character search minimum for non-trusted users on landing page.
- Implemented real-time sync telemetry (last refresh and countdown) in Manage tab.
- Added auto-refresh logic with configurable intervals.
2026-02-08 19:46:53 -05:00
Scott Idem
b3114c619a Complete Exhibitor Leads Authentication and Portal Structure
- Implemented dual-mode Sign-In (Shared Passcode / Licensed User) with persistent state.
- Added Manager Bypass logic for seamless admin access.
- Implemented Welcome/Start tab with redirection to Lead List upon authentication.
- Added Sign-Out functionality and Header navigation improvements.
- Integrated all Manage tab sub-sections (Licenses, Questions, Payment).
2026-02-08 19:23:26 -05:00
Scott Idem
72b0086efa Complete Exhibitor Leads Manage tab functionality
- Implemented dynamic Custom Questions editor for qualifiers.
- Wired up Staff License management and Billing stubs with expanding rows.
- Finalized Admin Tools and App Settings sections.
- Verified zero errors project-wide.
2026-02-08 19:18:01 -05:00
Scott Idem
7963314377 Overhaul Exhibitor Leads Manage tab and resolve all TypeScript errors
- Implemented full Staff License management (CRUD for license_li_json).
- Added Admin Tools section for managers (Payment status, Max licenses, Device counts).
- Implemented App Settings (Refresh interval, navigation preferences, cache management).
- Fixed all remaining TypeScript errors in Badge and Presentation modules.
- Integrated Payment tab conditional visibility logic.
2026-02-08 18:46:32 -05:00
Scott Idem
d6480bd0dc Optimize IDAA Bulletin Board performance and finalize hydration stability
- Disabled redundant comment loading in BB Post list to eliminate '1+N' API request overhead.
- Fixed missing 'untrack' import in BB Post page.
- Refactored IDAA Archive and BB pages to use 'page.params' for robust Svelte 5 reactivity.
- Finalized root layout hydration guards to prevent infinite store synchronization loops.
2026-02-08 18:29:31 -05:00
Scott Idem
f84d6b638d Implement reactive sorting for IDAA Archives
- Forced priority archives to the top of the list.
- Implemented descending (DESC) sort by the 'sort' field within groups.
- Added a sort selector to the archive list component.
- Centralized sorting logic in-memory within LiveQuery for immediate reactivity.
2026-02-08 18:12:54 -05:00
Scott Idem
84cfed97ca Fix badge loading race conditions and standardize Svelte 5 param resolution
- Resolved 'untrack is not defined' ReferenceError in Badge Detail page.
- Transitioned Badge, IDAA Archive, and Journal Entry pages to use page.params for robust reactivity.
- Implemented fallback semantic ID lookup for Badges to handle mixed ID formats in URLs.
- Added proper loading states to detail views to prevent 'No IDB record' flashes during hydration.
- Systematically audited and cleaned up duplicate 'untrack' imports across all Svelte files.
2026-02-08 17:48:20 -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
356eda5ab4 Saving notes and things. Done for the night. Tomorrow need to work on the Leads - Manage/Config and then Leads - Sign In/Licenses/Payment 2026-02-07 19:16:41 -05:00
Scott Idem
acf69c3deb feat(leads): standardize exhibit and lead tracking on V3 API and base ID keys 2026-02-07 18:58:20 -05:00
Scott Idem
aa5c795287 refactor(leads): combine QR and Search buttons into a single toggle
- Simplified Tab 2 interface by replacing segmented control with a single toggle button.
- Updated button labels and icons to indicate the available switch action.
2026-02-07 18:09:29 -05:00
Scott Idem
677571dde2 feat(leads): persistent tab states and unified search UI
- Implemented sticky tab persistence using local storage via events_loc store.
- Aligned Manual Search form styling with Lead List search for UI consistency.
- Updated tab switching logic to support historical navigation within exhibits.
- Center-aligned and stabilized Add Lead content area.
2026-02-07 18:08:23 -05:00
Scott Idem
f4a34e4ad7 feat(leads): stabilize v3 layout and unify tab width logic
- Removed max-width constraints across leads modules for full-width stability.
- Fixed duplicate Download icon import causing build errors.
- Improved header responsiveness for mobile-first experience.
- Refined tab switching logic with state persistence placeholders.
2026-02-07 18:00:12 -05:00
Scott Idem
699a1dd584 refactor(leads): modularize exhibitor leads logic and stub v3 UI 2026-02-07 16:43:33 -05:00
Scott Idem
f8f65139a7 Saving initial changes to the Leads v3 project files and directories. 2026-02-07 14:54:31 -05:00
Scott Idem
ea661f5893 Saving notes 2026-02-06 18:18:47 -05:00
Scott Idem
a6d9060422 debug: enable verbose logging in presenter load function
- Increased 'log_lvl' to 2 in the presenter's SvelteKit load function to track data revalidation and ID mapping.
2026-02-06 18:12:08 -05:00
Scott Idem
136afdd8f8 debug: implement surgical tracking and refined ID safety net for event files
- Added surgical console logging in '_refresh_file_li_background' to track raw API data vs processed records.
- Refined the ID safety net to only inject missing keys, preventing accidental overwrites of existing relationships.
- Hardened '_process_generic_props' to prevent 'null' random IDs from clobbering clean V3 IDs.
- Restored specific object ID fields to 'properties_to_save' for full IndexedDB synchronization.
2026-02-06 18:06:56 -05:00
Scott Idem
d53a2789ae refactor: improve event file loader stability and ID integrity
- Implemented a safety net in '_refresh_file_li_background' to inject missing 'for_id' and 'for_type' from query context.
- Fixed a bug in '_process_generic_props' where 'null' random IDs could overwrite clean IDs.
- Enabled initial debugging logs for event file processing.
2026-02-06 17:25:42 -05:00
Scott Idem
4eabc7eeba refactor: implement frontend safety net for missing event file foreign keys
- Updated '_refresh_file_li_background' to manually inject 'for_id', 'for_type', and specific object IDs if they are missing from the API response.
- This ensures robust indexing and retrieval from Dexie even when the V3 backend fails to populate linking fields.
- Verified that these 'fixed' objects are correctly processed and saved to the local cache.
2026-02-06 16:55:00 -05:00
Scott Idem
7b61145a66 refactor: ensure robust event file retrieval from Dexie cache
- Reverted 'element_manage_event_file_li_direct.svelte' to use 'for_type' and 'for_id' for cache-aware filtering.
- Enhanced 'element_manage_event_file_li_all.svelte' with a combined filter for specific and generic IDs.
- Verified that ID synchronization in 'process_ae_obj__event_file_props' supports these retrieval patterns.
2026-02-06 16:45:10 -05:00
Scott Idem
df9d3ad3d2 refactor: stabilize event file management and ID synchronization
- Updated 'process_ae_obj__event_file_props' to synchronize generic 'for_id' with specific object IDs (e.g., 'event_presenter_id').
- Standardized 'element_manage_event_file_li_direct.svelte' to use specific ID filtering.
- Fixed missing 'prevent_default' in 'ae_comp__hosted_files_clip_video.svelte'.
- Resolved miscellaneous type and syntax errors identified by svelte-check.
2026-02-06 16:41:44 -05:00
Scott Idem
2e8e4c7a7b refactor(archives): apply batch formatting to the module
- Finalized batch formatting (printWidth: 80) across all components in the archives module.
2026-02-06 16:19:07 -05:00
Scott Idem
d21e2f8e6f refactor: standardize event file actions and apply batch formatting
- Updated 'create_event_file_obj_from_hosted_file_async' to use the modern V3 action endpoint.
- Standardized 'prevent_default' helper names in root Event and Archive components.
- Applied batch formatting (printWidth: 80) across the settings and events modules.
2026-02-06 16:17:31 -05:00
Scott Idem
433862ad00 refactor(events): modernize event files upload component
- Standardized props and UI using Lucide icons and Element_input_files_tbl.
- Migrated state to Svelte 5 runes ($state, $bindable).
- Updated upload logic to handle sequential processing and event_file creation.
- Improved revalidation logic by clearing Dexie cache before refreshing.
2026-02-06 15:43:03 -05:00
Scott Idem
ec363f16fc fix: correct remaining native 'event.preventDefault()' calls
- Reverted incorrect snake_case 'event.prevent_default()' to native CamelCase 'event.preventDefault()' across multiple components.
- Finalized formatting for event files upload and archive content edit components.
2026-02-06 15:10:10 -05:00
Scott Idem
f8476e1133 refactor(events): finalize formatting and fix remaining native calls
- Corrected missed native 'event.preventDefault()' calls in badge form components.
- Applied batch formatting (printWidth: 80) to top-level event route files.
2026-02-06 15:04:55 -05:00
Scott Idem
bb94fd38aa refactor(pres_mgmt): fix native calls and apply batch formatting
- Corrected native 'event.preventDefault()' calls in form agreement components.
- Applied batch formatting (printWidth: 80) across the pres_mgmt module.
2026-02-06 15:02:26 -05:00
Scott Idem
9ea94c0727 refactor(leads): fix native calls and apply batch formatting
- Corrected native 'event.preventDefault()' calls in exhibit search components.
- Applied batch formatting (printWidth: 80) across the leads module.
2026-02-06 14:59:09 -05:00
Scott Idem
5385eacc0f refactor(badges): standardize helpers and apply batch formatting
- Standardized 'prevent_default' helper names across badges module.
- Corrected native 'event.preventDefault()' calls in view and template components.
- Applied batch formatting (printWidth: 80) to all badges files.
2026-02-06 14:55:43 -05:00
Scott Idem
67752ccdfe refactor(lib): standardize helpers and format shared components
- Applied snake_case helper standardization and formatting to element_input_file, hosted_files_clip_video, and websocket_v2.
- Cleaned up deprecated legacy imports.
2026-02-06 14:49:36 -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
7ce5e1f825 refactor: standardize 'prevent_default' helpers and batch format modules
- Renamed internal 'preventDefault' helpers to 'prevent_default' for project-wide snake_case consistency.
- Corrected native event method calls from 'prevent_default()' to 'preventDefault()' to resolve runtime TypeErrors.
- Applied batch formatting (printWidth: 80) to Journals and IDAA Recovery Meetings modules.
- Removed deprecated 'preventDefault' imports from svelte/legacy.
- Fixed Journal Entry 404 by migrating loader to V3 API helper.
2026-02-06 14:45:53 -05:00
Scott Idem
5bd4f7454d style(idaa): apply expanded 80-width formatting and snake_case to Recovery Meetings
- Batch formatted all Recovery Meetings module files using Prettier with printWidth: 80.
- Refactored preventDefault to prevent_default in editor and search components.
- Standardized line breaks for long attribute lists and Svelte tags for better readability.
- Ensured consistent snake_case naming for internal identifiers.
2026-02-06 14:21:24 -05:00
Scott Idem
f4b49f41eb fix(journals): resolve 404 error by switching to V3 entry loader
- Replaced legacy generic load_ae_obj_id with V3-compliant journals_func.load_ae_obj_id__journal_entry.
- Corrected API endpoint targeting from /crud/ to /v3/crud/.
- Hardened journal entry loading for Svelte 5 stability.
2026-02-06 14:18:24 -05:00
Scott Idem
2f3125c64b style(journals): apply expanded 80-width formatting and snake_case
- Batch formatted all Journals module files using Prettier with printWidth: 80.
- Refactored preventDefault to prevent_default across all Svelte components.
- Standardized line breaks for imports and long attribute lists for better readability.
- Ensured consistent snake_case naming for internal identifiers.
2026-02-06 14:15:43 -05:00
Scott Idem
07dd6c18a1 Making things look nicer. Better formatting. 2026-02-06 13:49:11 -05:00
Scott Idem
49e1d57f8f feat(v3): harden clean ID pattern and standardize snake_case platform-wide
- Systematically migrated from *_id_random to clean *_id fields in BB, People, and Posts modules.
- Synchronized Post_Comment interface with account_id for multi-tenant isolation.
- Applied optional chaining and local reactive state to harden async UI initialization.
- Refactored common helpers to follow snake_case naming conventions.
- Resolved various minor stability and logic issues across IDAA Bulletin Board.
2026-02-06 10:44:26 -05:00
Scott Idem
7401003614 feat(idaa): sort BB posts by most recent activity
- Refactored lq__post_obj_li to perform in-memory descending sort by updated_on/created_on.
- Ensured newest and recently modified posts appear at the top of the list.
- Hardened reactivity for Svelte 5 stability.
2026-02-06 10:06:30 -05:00
Scott Idem
85c92b2736 Saving notes 2026-02-05 20:42:54 -05:00
Scott Idem
4e523b9bd8 feat(idaa): stabilize Bulletin Board module and resolve creation crashes
- Fixed 'post_id' missing error in comment creation by mapping to 'post_id_random'.
- Resolved infinite request loop in post view via untrack() optimization.
- Hardened all property accesses with optional chaining to prevent TypeErrors.
- Migrated comment editor to local reactive state for Svelte 5 stability.
- Refactored post visibility layer to use derived reactive filtering.
- Standardized ID mapping patterns across all BB components.
2026-02-05 20:38:09 -05:00
Scott Idem
73c687ac5a fix(idaa): resolve BB comment IntegrityError and infinite loop
- Explicitly mapped 'post_id' in create_nested_obj_v3 payload.
- Removed redundant background load trigger in view component.
- Stabilized parent-child relationship for V3 API mapping.
- Resolved persistent 400 error during comment creation.
2026-02-05 20:25:23 -05:00
Scott Idem
d6b6e988eb fix(idaa): resolve BB comment loop and parent mapping issues
- Switched to create_nested_obj_v3 for establishing parent-child links.
- Optimized background load trigger with untrack to prevent infinite loops.
- Resolved MySQL IntegrityError (missing post_id) during comment creation.
- Hardened view component reactivity for Svelte 5.
2026-02-05 20:10:46 -05:00
Scott Idem
a3ec7f8c42 fix(idaa): synchronize Dexie schema with renamed post_comment table
- Renamed 'comment' table to 'post_comment' in db_posts.ts.
- Bumped Dexie database version to 2.
- Resolved InvalidTableError: Table post_comment does not exist.
- Standardized ID mapping for local storage consistency.
2026-02-05 19:57:22 -05:00
Scott Idem
2d4ddd335b fix(idaa): finalize null-hardening for BB comment editor buttons
- Added optional chaining to 'Cancel Edit' button title attributes.
- Resolved persistent TypeError: can't access property 'full_name' on null.
- Ensured total UI stability during new comment initialization.
2026-02-05 19:26:39 -05:00
Scott Idem
962cc00570 fix(idaa): finalize null-safety for BB comment editor
- Added optional chaining to all post_comment_obj property accesses.
- Resolved TypeError when clicking 'New Comment' button.
- Hardened UI stability for asynchronous data loading.
2026-02-05 19:22:04 -05:00
Scott Idem
13cb13c470 fix(idaa): complete null-hardening for BB comment editor
- Added optional chaining to all post_comment_obj property accesses.
- Resolved TypeError when clicking 'New Comment' button.
- Hardened UI stability for asynchronous data loading.
2026-02-05 19:16:02 -05:00
Scott Idem
81b2ce6f06 fix(idaa): resolve infinite loop and ReferenceError in BB post view
- Deleted redundant reactive trigger in post view component.
- Removed state-syncing side-effects from liveQuery derivation.
- Standardized log_lvl prop to resolve initialization crash.
- Optimized performance for single post loading.
2026-02-05 19:13:01 -05:00
Scott Idem
6d98758f3c fix(idaa): modernize Bulletin Board reactivity and implement SWR for single posts 2026-02-05 18:07:18 -05:00
Scott Idem
6c2c37ff06 fix(standardization): enforce V3 String-Only ID mapping and harden account isolation
- Added 'account_id_random' to persistent property list to fix local search isolation.
- Standardized search body and helpers to support mandatory x-account-id headers.
- Refactored LiveQuery to use synchronous dependency tracking ($derived.by) for reliable search updates.
- Broadened server-side search to handle inclusive OR logic on the client, preventing disappearing results.
- Updated task list with IDAA Recovery Meeting testing status.
2026-02-05 17:56:13 -05:00
Scott Idem
f4f3f99927 fix(idaa): stabilize Recovery Meetings search and broaden visibility logic
- Resolved 400 Bad Request by whitelisting 'physical', 'virtual', and 'external_person_id' fields in backend searchable_fields.
- Broadened server-side search by removing restrictive AND filters for location types, moving the inclusive OR logic to the client-side filter layer.
- Hardened handle_search_refresh error handling to clear results on category filter changes (Type, Physical, Virtual) while maintaining flicker protection strictly for text search typing.
- Restored permissive visibility for Trusted users, allowing them to see hidden/disabled items regardless of Edit Mode.
- Optimized plural object loading in Archives and Events modules using concurrent Promise.all.
- Standardized ID handling across the search flow to prevent Dexie lookup failures.
- Finalized well-commented code across IDAA modules to document search strategies and workarounds.
2026-02-05 16:33:32 -05:00
Scott Idem
0a94afe691 chore(archives): add trace logging for content load and finalize well-commented code 2026-02-05 14:32:32 -05:00
Scott Idem
ee79f08e69 fix(idaa): resolve broken reactivity and sorting in Archive modules
- Fixed ReferenceError in Archive Content liveQuery (variable name mismatch).
    - Corrected main Archive list sorting by removing incorrect .reverse() call.
    - Implemented robust in-memory sorting for Archive Content to handle mixed directions (Group DESC + Sort ASC).
    - Simplified dependency tracking in Svelte 5 derivations to prevent empty list flickers.
2026-02-05 13:51:17 -05:00
Scott Idem
22e05ec095 fix(standardization): apply safe sort padding across all remaining modules
- Hardened 'JournalEntry' and 'Sponsorship' object processors to handle null sort values.
- Disabled aggressive pre-loading of archive content in main archives list to improve performance.
2026-02-05 12:33:35 -05:00
Scott Idem
2306f2d0c4 fix(idaa): harden data sync against padStart crashes and fix Archive Content sort
- Hardened object processors in Archives and Posts modules to safely handle null 'sort' values, preventing runtime TypeErrors during data synchronization.
- Fixed inconsistent sorting in Archive Content list by correctly implementing descending order (sort then reverse) and adding a configuration loading guard to the liveQuery.
- Standardized safe data processing patterns in SVELTE_DEXIE_GUIDE.md.
- Performed minor cleanup and visibility logic hardening in Recovery Meetings module.
2026-02-05 12:27:26 -05:00
Scott Idem
bd39fd3061 Restore Event Session search stability and advance platform-wide string ID standardization
- Restored Event Session search by standardizing on 'event_id' for Dexie queries and implementing dual-layer filtering (local + API guard) to prevent broad results from clobbering filtered views.
- Advanced String-Only ID Standardization (Phase 2) by updating generic object processors across all event library modules to support both base IDs and legacy '_random' variants.
- Refactored Event Presenter and Presentation components to support standardized '_id_li' props while maintaining backward compatibility.
- Standardized common helper identifiers to snake_case (e.g., 'prevent_default') in the Events module.
- Verified Staff and Poster email notification logic in the Bulletin Board module.
- Updated .gitignore and cleaned up test artifacts.
2026-02-04 19:32:17 -05:00
Scott Idem
46c30590ed Standardize DB interfaces with dependency tracking comments and clean up legacy location.reload() arguments 2026-02-04 18:05:34 -05:00
Scott Idem
db34da66dc Standardize Event DB/Types and fix critical UI type mismatches across Badges, Journals, and Locations modules 2026-02-04 17:57:42 -05:00
Scott Idem
2e804ae01b fix(idaa): resolve type mismatches in delete methods and liveQuery results
- Updated 'method' parameter types in IDAA edit components to match the expected union types in the V3 API helpers.
- Applied type casting to 'liveQuery' results in the Archives page to ensure 'topic_name' and other convenience fields are recognized by the compiler.
- Fixed casting for 'method' variable in delete operations across Archives, Bulletin Board, and Recovery Meetings.
2026-02-04 15:05:53 -05:00
Scott Idem
bc30724628 feat(events): reorganize badge admin tools and enhance dependency tracking
- Migrated 'Add New Badge' and 'Upload Badge List' to centralized Event Settings hub.
- Secured Admin Tools visibility with Administrator access and Edit Mode requirements.
- Restored and modernized Badge Template management route with Svelte 5 runes.
- Patched 'Archive' and 'Session' database interfaces to resolve compiler errors.
- Added project-wide dependency comments to key interfaces (Archive, Badge, Session) to prevent cascading change regressions.
- Fixed duplicate import errors in the Settings page.
2026-02-04 14:04: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
e8f92b818a fix(journals): resolve delete_method type mismatch and test safe workflow 2026-02-03 22:28:30 -05:00
Scott Idem
77a5ab840a fix(journals): final cleanup of duplicate button attributes and refined types 2026-02-03 22:19:38 -05:00
Scott Idem
281972cb5d fix(journals): add type='button' to prevent form submission and migrate to V3 Action API for file downloads 2026-02-03 22:14:22 -05:00
Scott Idem
6abe4c897e fix(events): ensure presenters and presentation-level files show correctly
- Harden 'ae_events__event_presenter.ts' to ensure both ID and ID_random fields are synced during refreshes.
- Update presenter list wrapper to use '_id_random' for reliable Dexie queries.
- Enable 'inc_all_file_li' and 'inc_file_li' in session loaders to pre-cache nested files.
- Introduce 'Launcher_presentation_view.svelte' to explicitly display files linked to presentations.
- Refactor presenter views to focus exclusively on speaker-specific files, avoiding UI duplication.
2026-02-03 19:19:11 -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
6634c9aef0 feat(hosted-files): introduce direct download mode and automate filename shortening
- Add 'show_direct_download' prop to support native browser downloads via V3 Action paths (/v3/action/...).
- Integrate 'shorten_filename' logic directly into the component via 'max_filename' prop.
- Refactor internal UI to snippets for consistent rendering across button and anchor modes.
- Update 'ae_HostedFile' type with new SQL view fields: 'filename_no_ext' and 'filename_w_ext'.
- Add direct download toggle and edit mode indicator to testing dashboard.
- Simplify 'ae_comp__event_file_obj_tbl' by removing manual snippets in favor of standardized component logic.
2026-02-03 15:20:32 -05:00
Scott Idem
671247e783 feat(hosted-files): finalize download button UI with fixed spinner, divider, and alternating status view 2026-02-03 14:35:31 -05:00
Scott Idem
76b9e97894 feat(hosted-files): enhance download component with style variants and robust progress states
- refactor(ae_comp__hosted_files_download_button): add 'variant' (tonal, filled, outline, ghost) and 'color' props.
- fix(ae_comp__hosted_files_download_button): ensure Tailwind bundles variant classes using a literal lookup map.
- fix(ae_comp__hosted_files_download_button): prevent premature "Failed to download" message during initialization.
- feat(testing): add comprehensive Hosted Files testing dashboard at /testing/hosted_files with large file progress trials.
- chore: remove legacy v1 download button component.
2026-02-03 13:19:04 -05:00
Scott Idem
aaead82c1a feat(hosted-files): introduce standardized download component and V3 action support
- Created AE_Comp_Hosted_Files_Download_Button using Svelte 5 and Lucide icons.
- Added file_extension_icon_lucide utility for direct Lucide icon mapping.
- Refactored download logic to core__hosted_files.ts using V3 Action endpoint (/v3/action/hosted_file/.../download).
- Integrated new component into Event File Object Table.
- Cleaned up legacy window.postMessage calls in several file management views.

NOTE: The new download component is currently in development and may not be fully functional.
2026-02-03 11:19:23 -05:00
Scott Idem
1ae7b5642d General clean up the _random appends. About to work on a new Hosted File Download Svelte component. 2026-02-03 10:27:01 -05:00
Scott Idem
d753c8e47a Saving notes 2026-01-30 18:56:44 -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
bf357e5244 style(launcher): rename configuration components to use snake_case naming convention 2026-01-30 13:06:01 -05:00
Scott Idem
8c7784802a feat(launcher): implement Phase 5 system management UI and relay
- Expose new native handlers in electron_relay.ts (Wallpaper, Updates, Window Control, Power).
- Overhaul Native OS Management UI with controls for window states, display layouts, and power.
- Add Application Updates component with support for local and web sources.
- Include confirmation modal for dangerous system actions (shutdown/reboot).
- Update TODO.md to mark Phase 5 integration as completed.
2026-01-30 12:49:09 -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
7a8871c51f docs: update native app specifications and TODO for Phase 5 completion
- Synchronize Functional Spec with new system handlers (Wallpaper, Power, Recording, Displays).
- Update Automation Scripts with finalized AppleScript handlers for PowerPoint and Keynote.
- Mark IDAA and Phase 5 automation tasks as completed in TODO.md.
2026-01-30 11:35:01 -05:00
Scott Idem
8c99f5abed feat(idaa): implement jitsi report streaming and conference lifecycle improvements
- Refactor Jitsi reports to use SvelteKit streaming with a skeleton loader.
- Add conference lifecycle event listeners (left, close) to video conference page.
- Implement manual Novi data re-sync and improve initialization robustness.
- Fix skeleton visibility by using standard Tailwind colors.
2026-01-30 10:16:37 -05:00
Scott Idem
c626a0f9df Saving notes 2026-01-29 18:12:38 -05:00
Scott Idem
35114e5e58 feat(v3-api): rollout 'view' parameter across event library modules 2026-01-29 18:00:15 -05:00
Scott Idem
9551825b3f fix: adjust event report filters to use 'all' status and event_id field resolution 2026-01-29 16:21:25 -05:00
Scott Idem
99345c93a9 feat: update event file and session search to support V3 API filters 2026-01-29 15:51:41 -05:00
Scott Idem
6c8118fc82 feat: add AE_Comp_Site_Config_Editor for managed site configuration 2026-01-29 15:09:51 -05:00
Scott Idem
ce9e06eb31 refactor: migrate IDAA Archive, BB, and Recovery Meetings to AE_Comp_Editor_TipTap 2026-01-29 14:53:52 -05:00
Scott Idem
7ec3bae343 feat: add Zero-Dependency AE_Comp_Editor_TipTap with auto-formatting support 2026-01-29 14:30:28 -05:00
Scott Idem
363d94a36b refactor: consolidate CodeMirror editors into unified AE_Comp_Editor_CodeMirror component 2026-01-29 14:16:35 -05:00
Scott Idem
3d7b68e7ce Good by Data Store v2 2026-01-29 13:15:24 -05:00
Scott Idem
1a462c06c5 Date update 2026-01-29 12:43:40 -05:00
Scott Idem
0c8a96dd0c This should be the last round of Data Store changes to upgrade them to v3. 2026-01-29 12:35:57 -05:00
Scott Idem
2975835d96 Continuing to make updates to the Data Stores to use v3. 2026-01-29 12:29:14 -05:00
Scott Idem
9bf2373a82 Slowly updating things to Data Store v3. Also doing some safe code clean up. Mainly removing commented out code. 2026-01-29 12:21:47 -05:00
Scott Idem
917c8f3e37 Slowly implementing the new Data Store element v3. It should be a drop in replacement for v2. 2026-01-29 12:12:33 -05:00
Scott Idem
ab3aa52e07 A working Data Store element/component again!!! Saving! 2026-01-29 11:51:12 -05:00
Scott Idem
6a132af2ae Saving changes that seem to be safe and working. 2026-01-29 11:08:34 -05:00
Scott Idem
20f1f5ad27 This is a mostly working state again. Some files were backed up to ~/tmp/Aether_UI_UX_app. Things are slowly being merged back in. Not easy. 2026-01-29 10:57:59 -05:00
Scott Idem
b25d13297e fix: stabilize root layout reactivity and badge template form
- Fix infinite loops in '+layout.svelte' by using 'untrack' for store synchronization effects.
- Correctly define 'ae_acct' as a derived rune to resolve ReferenceErrors and ensure site styles hydrate properly.
- Modernize 'ae_comp__badge_template_form.svelte' with Svelte 5 Runes and callback props (onsuccess, onerror, oncancel).
- Fix illegal async  in badge form by moving logic to a dedicated load function untracked by the effect.
- Add Presentation Management Reports to TODO list for tracking.
2026-01-28 15:46:29 -05:00
Scott Idem
55773a332d refactor: harden type safety and modernize core forms for Svelte 5
- Standardize 'ae_BaseObj' and event types in 'ae_types.ts' to handle nullable fields from V3 API/Dexie.
- Modernize Person, Address, and Contact forms with Svelte 5 Runes and reactive synchronization.
- Refactor Event Settings and its sub-components to use the 'onsave' callback pattern, removing deprecated dispatchers.
- Hardened 'element_data_store_v2' with safe initialization and localStorage caching logic.
- Clean up unused 'element_data_store.svelte' (V1) and suppress Electron environment type errors in 'tmp_shell_handlers.ts'.
- Update documentation and workspace settings to reflect Phase 5 reactive patterns.
2026-01-28 14:40:20 -05:00
Scott Idem
bc1d74f817 docs: update task lists and migration audit for Exhibit Search standardization 2026-01-28 12:07:00 -05:00
Scott Idem
cc3a6b0f59 feat(leads): implement reactive search for exhibitors and lead tracking
- Implemented V3-style reactive search (Local Cache -> Remote Revalidation) for exhibitors.
- Standardized search fields to 'name' for Exhibits and 'event_badge_full_name' for Lead Tracking.
- Refactored Leads UI with standardized search components and grid layout.
- Updated event routing to exclusively use string-based IDs (Triple-ID pattern).
- Hardened 'ae_EventSession' type definitions to handle null values from V3 API/Dexie.
2026-01-28 12:05:42 -05:00
Scott Idem
f145b4dcac Saving notes. 2026-01-27 18:46:58 -05:00
Scott Idem
811afb2fd7 refactor(sessions): standardize reactive search and fix list layout
- Migrated Session search to the debounced  pattern with Search Guards and shared observables.
- Implemented 'Remote First' toggle support (Edit Mode only) for background-only revalidation.
- Resolved 'each_key_duplicate' crash and fixed icon scaling issues in the session list.
- Restored missing session alert visibility for managers.
- Standardized store initialization to prevent reactivity loops.
2026-01-27 18:41:19 -05:00
Scott Idem
8680ad89c1 refactor(badges): complete field normalization and search mapping
- Finalized the rename of 'default_qry_string' to 'default_qry_str' across the Badge library, Dexie schema, and API documentation.
- Synchronized all reactive filtering in '+page.svelte' with the standardized field names to restore full search functionality.
- Verified that text search, affiliations, and types now correctly map to whitelisted backend fields.
2026-01-27 18:04:08 -05:00
Scott Idem
5219bd0c9c refactor(badges): fix text search and standardize field mapping
- Renamed 'default_qry_string' to 'default_qry_str' across the Badge interface, Dexie schema, and API logic to resolve 400 'Unauthorized search field' errors.
- Synchronized local Fast Path filtering with the correct database field names.
- Hardened the reactive search pattern in '+page.svelte' to ensure end-to-sync consistency between local and background results.
2026-01-27 17:53:05 -05:00
Scott Idem
bf93539880 refactor(badges): stabilize search components and reactivity
- Implemented Search Guard and store initialization in '+page.svelte' to fix loops and filtering.
- Updated 'ae_comp__badge_search.svelte' to act as a pure UI component triggering versioned searches.
- Refactored 'ae_comp__badge_obj_li.svelte' to use shared data streams and fixed icon build errors.
2026-01-27 17:47:08 -05:00
Scott Idem
22ede45bd6 refactor(badges): standardize reactive search and fix text filtering
- Corrected 'search__event_badge' to use the valid 'default_qry_str' field name, resolving 400 errors during text search.
- Re-implemented the standardized debounced search pattern in '+page.svelte' with a robust Search Guard to eliminate loops.
- Hardened Fast Path local filtering to match the 'Badge' schema and synchronized result sorting with API revalidation.
- Updated 'ae_comp__badge_search.svelte' and 'ae_comp__badge_obj_li.svelte' to align with the shared data pattern and fixed Lucide icon imports.
- Ensured 'events_loc' store initialization for new search-related fields.
2026-01-27 17:46:55 -05:00
Scott Idem
23ecb21fe1 fix(journals): eliminate infinite search loop with criteria-based guard
- Implemented a 'Search Guard' pattern in '+page.svelte' that snapshots search criteria and bails out of redundant executions.
- Stabilized reactivity by removing immediate list clearing in Remote First mode, ensuring a consistent data stream.
- Isolated all search-driven state updates with 'untrack' to prevent circular dependency triggers.
- Hardened the 'lq__journal_entry_obj_li' observable to ensure stable result emission.
2026-01-27 16:50:18 -05:00
Scott Idem
4f8c482cf3 refactor(journals): stabilize reactive search and synchronize result counts
- Refactored 'src/routes/journals/[journal_id]/+page.svelte' to use a singleton 'liveQuery' observable, eliminating the search subscription loop.
- Synchronized entry counts in 'ae_comp__journal_obj_id_view.svelte' with the shared entries observable, ensuring accurate header feedback.
- Cleaned up 'ae_comp__journal_entry_obj_li.svelte' by removing redundant script blocks and hardening visibility filters.
- Restricted the result count badge to Edit Mode for a cleaner standard user interface.
- Improved initial load UX with localized spinners and proper undefined-state guards.
2026-01-27 16:17:46 -05:00
Scott Idem
6055fc3408 refactor(journals): standardize high-performance reactive search and stabilize results stream
- Implemented the loop-proof search pattern in 'src/routes/journals/[journal_id]/+page.svelte' using versioned triggers and untracked dependency isolation.
- Stabilized data loading by introducing 'ae_comp__journal_entry_obj_li_wrapper.svelte', ensuring smooth SWR transitions between local and API results.
- Synchronized result counts with the rendered list in 'ae_comp__journal_entry_obj_li.svelte' and implemented permissive visibility defaults.
- Added 'Remote First' toggle support (Edit Mode only) and laid groundwork for Global Search in 'ae_comp__journal_entry_obj_qry.svelte'.
- Updated Dexie schema and API helpers to support future person-level cross-journal searching.
2026-01-27 15:20:17 -05:00
Scott Idem
07e13ea5f2 perf(idaa): optimize search responsiveness and stabilize reactive data stream
- Stabilized the 'liveQuery' observable in 'ae_idaa_comp__event_obj_li_wrapper.svelte' by re-wrapping it in '', ensuring smooth UI updates when switching between local and API results.
- Reduced search debounce time to 250ms in '+page.svelte' for a more instantaneous user experience.
- Hardened 'Fast Path' local filtering to be more permissive and added debug logging to help diagnose IndexedDB sync issues.
- Restricted the visibility of the 'Remote First' toggle to Edit Mode (.edit_mode) for a cleaner standard user interface.
2026-01-27 14:48:08 -05:00
Scott Idem
3b6b32ce1c refactor(idaa): harden reactive search logic and resolve infinite loops
- Replaced boolean search triggers with a versioned integer pattern to break reactivity loops in '+page.svelte'.
- Resolved 'behind by one' search lag by ensuring the UI immediately reflects local results, even if empty.
- Corrected subscription syntax for 'liveQuery' observables in 'ae_idaa_comp__event_obj_li.svelte'.
- Implemented a robust 'Remote First' toggle support in search logic while maintaining fast-path local feedback.
- Hardened Fast Path filtering to match server-side logic more accurately for improved cache reliability.
2026-01-27 14:26:47 -05:00
Scott Idem
055d8e7816 refactor(search): standardize debounced reactive search across modules
- Standardized the search pattern using Svelte 5 debounced $effects in Recovery Meetings, Badge Search, and Journals to eliminate manual triggers and stuttering.
- Fixed a bug in 'ae_idaa_comp__event_obj_li.svelte' where the Results count was inconsistent with the displayed list by implementing a derived 'visible_event_obj_li' state.
- Hardened 'ae_idaa_comp__event_obj_li_wrapper.svelte' to filter out 'undefined' entries from database 'bulkGet' calls.
- Cleaned up legacy search handling code in 'ae_idaa_comp__event_obj_qry.svelte'.
- Updated 'TODO.md' and 'GEMINI.md' to reflect search logic hardening accomplishments.
2026-01-27 13:08:11 -05:00
Scott Idem
b837e6d0f8 perf(core): standardize non-blocking load pattern and add performance guidelines
- Documented the 'Non-Blocking Load Pattern' (SWR) in 'documentation/PERFORMANCE_GUIDELINES.md' to prevent future performance regressions.
- Refactored 'src/routes/core/people/[person_id]/+page.ts' to be non-blocking, improving perceived speed for person details.
- Updated 'GEMINI.md' standards and 'TODO.md' tasks to reflect system-wide performance hardening.
2026-01-27 12:32:26 -05:00
Scott Idem
9655604d86 perf(events): refactor load functions to be non-blocking
- Eliminated blocking 'await' calls in '+page.ts' for main Events, Session, Location, and Presenter views.
- Implemented Stale-While-Revalidate (SWR) pattern: pages now render instantly using cached IndexedDB data while API refreshes run in the background.
- Removed expensive sequential loops in load functions, allowing the reactive UI (via LiveQuery) to handle data arrival as background tasks complete.
2026-01-27 12:25:35 -05:00
Scott Idem
30413e32d2 refactor(events): harden V3 String-Only ID vision and fix Session Alert store error
- Standardized Event module logic files to strictly use random string IDs, ensuring frontend consistency and avoiding 'Integer Trap' 404s.
- Refactored 'ae_comp__event_session_alert.svelte' to accept a plain object instead of a store/observable, resolving the 'store_invalid_shape' error in list loops.
- Updated all callers of the alert component to pass unwrapped session objects.
- Cleaned up event-related UI components to remove redundant '_random' field lookups and align with V3 CRUD patterns.
- Updated project metadata (GEMINI.md, TODO.md) to reflect the new memory structure and latest hardened state.
2026-01-27 12:18:12 -05:00
Scott Idem
a704779d1b fix(ux): optimize event lists and fix editor line wrapping
- Implemented 'Details' toggle in Session lists to defer expensive presenter/file sub-component renders, improving initial hydration speed.
- Fixed line-wrapping for long lines in Journals Editor (both plain textarea and CodeMirror).
- Updated TODO.md and GEMINI.md to reflect Journals standardization and performance optimizations.
2026-01-26 20:25:56 -05:00
Scott Idem
ae86d0aede fix(journals): standardize component naming, props, and libraries
- Renamed all Journal components to follow the ae_comp__* snake_case convention.
- Normalized all custom event handler props from PascalCase (onSave) to snake_case (on_save) across the module.
- Migrated all icon imports from @lucide/svelte to lucide-svelte for consistency.
- Resolved ReferenceErrors and file corruption issues in Journals config and entry views.
- Updated qry__journal_entry logic to support category filtering.
- Verified module integrity and component interop.
2026-01-26 20:18:39 -05:00
Scott Idem
6858052e7d fix(idaa): correct svelte-check errors and refine IDAA components
- Fixed invalid JavaScript-style comments inside HTML tags.
- Restored intentional '1 == 3' business logic pattern.
- Switched to 'event.currentTarget' for FormData instantiation.
- Resolved type errors in delete method calls via explicit casting.
- Cleaned up unused props and resolved property access issues in Archives and Bulletin Board components.
- Verified final state with npm run check.
2026-01-26 18:09:18 -05:00
Scott Idem
ac14721bd0 fix(idaa): audit and harden IDAA module components and types
- Updated ae_types.ts with missing IDAA-specific fields for Archives and Events (topic_name, archive_on, contact_li_json, etc.) using snake_case.
- Refactored bulletin board post filter to safely handle null archive_on dates.
- Fixed missing 'data' prop assignment in bulletin board list component to resolve type error.
- Corrected core_func.download_export__obj_type method name in recovery meetings export.
- Hardened safety checks for contact_li_json in recovery meetings view logic to prevent null property access.
- Mapped Jitsi meeting event data to internal snake_case variables and fixed input type assignments.
- Updated project documentation (TODO, GEMINI.md, .ae_brief) to reflect IDAA hardening progress.
2026-01-26 17:50:27 -05:00
Scott Idem
0a390c9505 feat(sw): implement and enable robust service worker caching
- Re-enabled automatic service worker registration in svelte.config.js.
- Implemented a production-ready service-worker.js using SvelteKit metadata (-worker).
- Added intelligent caching for build artifacts, static files, and network fallbacks.
- This ensures the UI shell and assets load instantly from disk, completing the hydration performance workstream.
2026-01-26 17:19:30 -05:00
Scott Idem
ef597bc058 feat(launcher): implement visual telemetry dashboard in health config
- Added visual progress bars for RAM usage based on native device metadata.
- Implemented color-coded status indicators (success/warning/error) for memory usage.
- Enhanced layout with animated pulses for heartbeat and sync status.
- Added detailed device info display including hostname and IP addresses.
2026-01-26 17:14:14 -05:00
Scott Idem
476ab7ede6 perf(hydration): resolve white page delay by removing blocking awaits from layouts
- Refactored Events root layout to fire load_ae_obj_id__event in the background.
- Refactored Journals list page to fire load_ae_obj_li__journal in the background.
- Combined with SWR module logic, this ensures near-instant UI rendering from Dexie cache while refreshes occur asynchronously.
2026-01-26 17:13:40 -05:00
Scott Idem
c7a517a6e1 perf(hydration): optimize page loads and silence background fetch noise
- Implemented SWR pattern for Journal and Site Domain lookups.
- Refactored +layout.ts across modules to fire background refreshes instead of blocking.
- Updated +layout.svelte to render the layout shell immediately while hydrating.
- Silenced 'AbortError' and 'NetworkError' in api_get_object.ts and api_post_object.ts for log_lvl 0.
- Resolved duplicate export errors in ae_journals__journal.ts.
2026-01-26 17:06:22 -05:00
Scott Idem
715cc7cb65 perf(hydration): implement non-blocking load path for Launcher and Sessions
- Implemented SWR pattern for session loading in ae_events__event_session.ts.
- Refactored Launcher layouts (+layout.ts and +page.ts) to fire background refreshes instead of awaiting API calls.
- Removed redundant blocking logic to ensure instant UI rendering from Dexie cache.
2026-01-26 16:50:40 -05:00
Scott Idem
359eb9cf3f perf(hydration): implement cache-first site discovery and SWR event loading
- Refactored lookup_site_domain_v3 to be cache-first, unblocking root layout.
- Implemented Stale-While-Revalidate (SWR) pattern for load_ae_obj_id__event.
- Added global hydration overlay and loading spinner to +layout.svelte.
- Updated site domain whitelist to persist critical account/styling metadata in Dexie.
- Refactored root load function to return immediately if cached site data is found.
2026-01-26 16:41:14 -05:00
Scott Idem
c95b866969 Saving settings. 2026-01-26 16:19:05 -05:00
Scott Idem
5f2ccf8823 refactor(launcher): modularize launcher config and implement Phase 5 actuators
- Broke down the massive launcher_cfg.svelte into 7 modular sub-components.
- Updated electron_relay.ts with Phase 5 presentation controls and manifest tools.
- Updated architecture documentation to reflect the new TypeScript-based native bridge.
2026-01-26 16:18:00 -05:00
Scott Idem
7c14b1e3a2 chore: disable noisy markdown linting rules (MD007, MD033) in VS Code settings 2026-01-26 15:19:27 -05:00
Scott Idem
b072857d68 feat(native): harden launcher bridge and implement presentation-aware handover
- Upgraded LauncherBackgroundSync to force-hydrate OS metadata (home/tmp) on mount.
- Hardened electron_relay.ts with robust placeholder resolution and global regex.
- Restored safe handover by making native.launch_from_cache presentation-aware.
- Integrated heartbeat and sync status into the formal Launcher Config UI.
- Added comprehensive technical documentation for the 2026 native architecture.
2026-01-26 15:12:03 -05:00
Scott Idem
f0c4022675 fix(native): restore safe handover by making launch_from_cache presentation-aware
- Reverted handle_open_file to use native.launch_from_cache for atomic copy support.
- Upgraded Electron main process to handle specialized launcher logic (LibreOffice/AppleScript) internally after the copy operation.
- Ensures absolute paths and file existence before triggering OS apps.
2026-01-26 14:59:10 -05:00
Scott Idem
1a9630903d refactor(native): localize OS hydration to Launcher module
- Removed detailed OS path hydration from root +layout.ts to keep it lean.
- Moved home/tmp directory hydration to LauncherBackgroundSync onMount.
- Fixed regex pattern bug in electron_relay.ts (/\[home\]/g).
- Ensures absolute paths are correctly generated within the Launcher module.
2026-01-26 14:34:15 -05:00
Scott Idem
32866f7911 docs: finalize Phase 4 and update session context
- Officially marked Phase 4 (Heartbeat/Sync) as complete.
- Updated GEMINI.md development history and session context.
- Set Phase 5 (AppleScript/Office) as the next active workstream.
2026-01-26 13:28:34 -05:00
Scott Idem
71297af15c fix(api): implement auto-serialization for _json fields in V3 create/update
- Added logic to automatically JSON.stringify any field ending in '_json' in V3 API helpers.
- Added final payload logging to create_ae_obj_v3 for better debugging.
- Resolves 'str type expected' validation errors (HTTP 400) when sending objects to V3 CRUD endpoints.
2026-01-26 13:02:11 -05:00
Scott Idem
13bc903ad9 fix(launcher): resolve heartbeat timezone shift and enable browser testing
- Removed manual 'Z' suffix addition in device processing to fix incorrect offsets.
- Switched heartbeat payload to standard UTC ISO strings.
- Enabled background timers in non-native environments for easier verification.
- Hardened telemetry gathering to skip Electron-only APIs when running in a browser.
2026-01-26 12:57:59 -05:00
Scott Idem
af1dc18b57 Bug fix for Activity Log creation. Related to the date and auth. 2026-01-26 12:36:19 -05:00
Scott Idem
17a64ed320 fix(reports): add x-account-id header to Jitsi report query
- Explicitly added the 'x-account-id' header to resolve HTTP 403 error when fetching Jitsi activity logs.
- Ensures the server-side scope check passes for these report requests.
2026-01-26 12:23:26 -05:00
Scott Idem
72464d1a31 fix(reports): resolve TypeError in Jitsi reports and restore filtering
- Added Array.isArray check to prevent 'not iterable' error on API failure responses.
- Restored params_json to narrow the activity_log query and prevent potential timeouts.
- Cleaned up minor whitespace in sort logic.
2026-01-26 12:12:11 -05:00
Scott Idem
766225f5b9 fix(launcher): add missing ae_util import and format heartbeat datetime
- Resolved ReferenceError: ae_util is not defined.
- Formatted heartbeat to SQL-compatible datetime string.
2026-01-26 12:03:05 -05:00
Scott Idem
0e4802f7de Saving minor fix for IDAA and JItsi. Saving code clean up for the Launcher. Related to the device heartbeat loop. 2026-01-26 11:43:47 -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
54a2cfa59f Enhance: Implement Phase 4 Device Heartbeat and Room Refresh
- Fully implemented run_device_heartbeat with OS/Network telemetry.
- Added refresh_location_config to keep room metadata synchronized.
- Finalized background loop initialization in onMount.
- Integrated telemetry gathered via the Electron get_device_info bridge.
2026-01-23 17:40:39 -05:00
Scott Idem
10f7c1b776 Fix: Finalize manual command interface and structured output handling
- Improved manual OS command input with better binding and extraction logic.
- Resolved '[object Object]' issue by correctly displaying stdout/stderr/error from structured return.
- Polished terminal output UI with better styling and clear functionality.
- Renamed section to 'Native OS Handlers & Folders' for better clarity.
2026-01-23 17:32:15 -05:00
Scott Idem
f9f6d7f4f0 Fix: Synchronize SvelteKit relay and enhance native command testing
- Updated electron_relay.ts with full Phase 3 command set (run_cmd_sync, kill_processes, get_device_info).
- Fixed 500 error in Event Location by standardizing enabled/hidden flags.
- Hardened native environment detection in root layout.
- Added Manual OS Command input to Launcher Configuration for Phase 3 testing.
2026-01-23 17:23:35 -05:00
Scott Idem
efdf1188a6 Fix: Persist native sync timers and implement monitor UI
- Implemented safe merge in root layout to preserve local timer overrides.
- Added Native Sync Monitor UI to background sync component.
- Exposed loop periods and hash prefix length in Launcher Config.
- Ensured manual adjustments survive page refreshes.
2026-01-23 16:56:02 -05:00
Scott Idem
ce11138f20 Fix: Fully implement recursive data fetching in Event Location
- Updated load functions to pass inc_session_li and inc_all_file_li.
- Enhanced _handle_nested_loads to recursively fetch sessions and files.
- Ensured consistency with Triple-ID pattern and V3 API standards.
2026-01-23 16:37:39 -05:00
Scott Idem
dc38c2c10c Enhance: Implement exhaustive background caching and recursive data loading
- Implemented aggressive room-wide background caching engine in LauncherBackgroundSync.svelte.
- Added inc_file_li and inc_all_file_li support to Event and Event Location object loaders for total room coverage.
- Switched to proven V1 download path (/hosted_file/) to resolve V3 CRUD binary delivery issues.
- Optimized Electron bridge with download locking and flat-hash storage matching legacy 'perfect' logic.
- Standardized all Electron IPC methods and parameters to snake_case.
- Added visual sync progress indicator for room caching status.
2026-01-23 16:30:03 -05:00
Scott Idem
683ea0394d Fix: Restore Native caching business logic and implement 3-mode launcher
- Implemented Default, Onsite, and Native launcher modes in launcher_file_cont.svelte.
- Restored background pre-caching logic with configurable timers in new LauncherBackgroundSync component.
- Fixed standard browser download regression for regular website mode.
- Modernized electron_relay to TypeScript and standardized bridge detection in layout.
- Detailed startup and background sync technical flow in documentation.
2026-01-23 15:17:50 -05:00
Scott Idem
20b41ebaef Enhance: Integrate Aether Native Bridge and update V3 deployment plan
- Updated +layout.ts to detect window.aetherNative and prioritize hydrated config.
- Mapped native operational paths (cache, recordings) to global location store.
- Refined NATIVE_APP_V3_REWRITE_PLAN.md with Two-Step Bootstrap strategy.
- Updated TODO.md with project progress and next-gen launcher priorities.
2026-01-23 14:08:37 -05:00
Scott Idem
4d932fcd2c Enhance: Journal config options and UI result messaging
- Added Query Limit configuration for Journals and Entries in modal settings.
- Refactored +page.svelte to delegate empty-state handling to the entry list component.
- Improved "no results" messaging in ae_comp__journal_entry_obj_li.svelte.
2026-01-23 11:26:36 -05:00
Scott Idem
b026a00af5 Fix: Stabilize Journal Entries query logic and UI search behavior
- Consolidated LiveQuery logic into [journal_id]/+page.svelte and removed redundancy from layout.
- Added automatic load trigger on journal_id change to ensure data freshness.
- Hardened Enabled/Hidden filters to correctly include NULL/undefined values in default views.
- Refined search behavior to distinguish between "default view" (null) and "no results found" ([]).
- Updated modal_journals_config.svelte with standardized module-level settings.
- Robust ID handling in bulk retrieval to handle varying property names (id, random_id).
2026-01-23 11:08:59 -05:00
Scott Idem
6925229d56 Saving notes 2026-01-22 20:25:33 -05:00
Scott Idem
a8e9c39ac6 Fix: Resolve type errors and cleanup window.location.reload calls 2026-01-22 20:18:29 -05:00
Scott Idem
c81ce50a00 Fix: Hardened types in analytics, hosted file upload, and QR scanner components 2026-01-22 19:47:16 -05:00
Scott Idem
32002fe6bd Fix: Resolve implicit any and property access errors in core components 2026-01-22 19:44:10 -05:00
Scott Idem
94a387ca6d Fix: Resolve UI component type mismatches and QR scanner property errors 2026-01-22 19:36:44 -05:00
Scott Idem
d7dd9afaf3 Cleanup: Remove unused legacy files prefixed with not_used 2026-01-22 19:35:29 -05:00
Scott Idem
555ecb043d Fix: Resolve implicit any errors and missing properties in QR scanner and layout loads 2026-01-22 19:29:45 -05:00
Scott Idem
b432a33cee Cleanup: Remove unused element_input_v2.svelte 2026-01-22 19:23:39 -05:00
Scott Idem
4976f2d897 Fix: System-wide type hardening and V3 API alignment 2026-01-22 19:22:16 -05:00
Scott Idem
9de1d4b23e Docs: Mark IDAA Result Limit task as completed in TODO.md 2026-01-22 18:12:47 -05:00
Scott Idem
02132fc5e8 Fix: Ensure IDAA Recovery Meetings respect the user-defined limit
- Simplified limit logic in search__event and qry_ae_obj_li__event_v2 to use the passed value directly.
- Added .slice(0, limit) to filtered results in Event module to handle over-fetching correctly.
- Explicitly passed the qry__limit from idaa_loc to the meeting list components.
- Applied .limit() to the Dexie liveQuery in the meeting list wrapper.
- Updated the UI result count and loop to strictly adhere to the requested limit.
2026-01-22 18:10:42 -05:00
Scott Idem
0ab6d2b3e3 Hardening: Restore V3 Search legacy compatibility and fix Hosted File specialized delete logic
- Restored legacy query aliases (qry_ae_obj_li__event, qry__event_file, etc.) across Event modules to fix build errors.
- Fixed Hosted File deletion logic in core__hosted_files.ts to use specialized /hosted_file/{id} endpoint with fake_delete support.
- Standardized ID field usage (hosted_file_id vs hosted_file_id_random) in Archive Content and Event File upload components.
- Updated TODO.md to reflect completed search restoration tasks.
2026-01-22 17:30:20 -05:00
Scott Idem
f190beb691 Working on making the Journal Entry search work correctly. There were/are some difference between the front and backend. Mainly it was not expecting not_enabled. It was expecting disabled. That has been changed on the backend. There is still some weirdness with the "all" options for either the enable and or hide fields. 2026-01-21 20:29:40 -05:00
Scott Idem
6e6e8d80cc Fix: Restore Journal Entry text search compatibility
- Backend: Updated `qry__journal_entry` in `ae_journals__journal_entry.ts`.
- Logic: Switched `default_qry_str` operator to `like` (from `match`) for V3 API compatibility.
- Fix: Corrected filter field names from `enabled`/`hidden` to `enable`/`hide`.
- Resilience: Added robust unwrapping of V3 API response envelopes (handling `{ data: [] }` vs array) to prevent iteration errors.
2026-01-21 19:17:10 -05:00
Scott Idem
f5adc4ac72 Docs: Mark IDAA Recovery Meetings Search as restored in TODO.md 2026-01-21 18:47:03 -05:00
Scott Idem
6624773d43 Fix: Align IDAA Event Search with proven Session Search logic
- Backend: Updated `search__event` in `ae_events__event.ts` to populate `params['lk_qry']`.
- Logic: Used `like` operator with wildcards for `default_qry_str` in `search_query` to match `event_session` pattern.
- Context: This fixes the text search in IDAA Recovery Meetings by ensuring the backend receives the expected V3 search parameters.
2026-01-21 18:45:47 -05:00
Scott Idem
c782d2273a Fix: Restore IDAA Recovery Meetings search functionality
- Backend: Added V3-compliant `search__event` to `ae_events__event.ts` with support for `default_qry_str`.
- Backend: Removed accidental duplicate `qry_ae_obj_li__event_v2` implementation.
- API: Exported `search__event` from `ae_events_functions.ts`.
- Frontend: Updated `recovery_meetings/+page.svelte` to use the new `search__event` function.
2026-01-21 17:29:52 -05:00
Scott Idem
af35124a8b Fix: Finalize Event Session Search with default_qry_str support
- Explicitly added `default_qry_str` to the V3 search body for "like" searches.
- Updated `TODO.md` to reflect completion of the task.
2026-01-21 17:07:06 -05:00
Scott Idem
8fae3aa89a Fix(ServiceWorker): Mitigate persistent TypeError by disabling auto-registration
- Mitigation: Disabled `serviceWorker.register` in `svelte.config.js` to stop the loop of `TypeError` during script evaluation.
- Debug Tool: Preserved the `fix-sw` cache clearing utility by moving it to `src/routes/testing/fix-sw/+page.svelte` for future investigation.
- Service Worker: Simplified `src/service-worker.js` to a minimal "Hello World" state and removed the problematic `.ts` version.
- Cleanup: Minor formatting adjustment in `ae_events_functions.ts`.
- Docs: Updated `TODO.md` and `GEMINI.md` to reflect the mitigation and planned follow-up.
2026-01-21 16:49:33 -05:00
Scott Idem
09c7d2440a Fix: Harden V3 search logic and restore specialized business mapping
- API: Updated `search_ae_obj_v3` to correctly serialize complex URL parameters (JSON).
- Events: Restored "sacred" business logic for Event Badge and Session searches using `ft_qry` and `lk_qry`.
- PWA: Fixed manifest path in `app.html` to resolve 404 errors.
- Documentation: Updated `GEMINI.md` and `TODO.md` with recent search hardening accomplishments.
2026-01-21 15:27:53 -05:00
Scott Idem
95f2ab79be Fix: Restore specialized search logic for Event Sessions and Presenters 2026-01-21 14:05:17 -05:00
Scott Idem
d705058175 Refactor: Remove redundant db_save exports and complete V3 cleanup 2026-01-21 13:28:30 -05:00
Scott Idem
be69ef1afd Modernize(Files): Full V3 API Migration for Hosted and Event Files
- API Migration: Refactored core__hosted_files.ts and ae_events__event_file.ts to use AE API CRUD V3.
- Logic Hardening: Unified property processing via '_process_generic_props' and ensured 'id_random' string identifier consistency.
- Reliability: Hardened orphan cleanup logic in delete_ae_obj_id__hosted_file and aligned with V3 generic delete patterns.
2026-01-21 12:47:34 -05:00
Scott Idem
2f2a1a87dc Modernize(Events/Sponsorships): Full V3 API Migration and ID Hardening
- API Migration: Refactored Event (Device, Location, Session, Presentation, Presenter, File, Exhibit, BadgeTemplate) and Sponsorship modules to use AE API CRUD V3.
- ID Hardening: Ensured all nested collection loads and searches use 'id_random' string identifiers to resolve 404 errors.
- Logic Consolidation: Unified property processing via '_process_generic_props'.
- Cleanup: Removed redundant '/admin' routes.
- Stability: Maintained V2 isolation for IDAA search.
2026-01-21 12:44:13 -05:00
Scott Idem
cd06fb79e8 Saving the new documentation for the Launcher project plans. Finally moving this to a new version of Electron and pretty much starting from scratch. 2026-01-21 12:11:47 -05:00
Scott Idem
3221e5830e Fix(Core): Restore system lookups and enhance reference data view
- API: Correctly exported 'get_ae_obj_li_for_lu' and reverted to stable V2 endpoints (/v2/crud/lu/.../list) to resolve V3 500 errors.
- Auth: Injected 'x-no-account-id' bypass header for unauthenticated global lookup access.
- UI: Updated lookups page to support 'english_short_name' fallback for countries and added Country Subdivisions card.
- Debug: Added transient console logging for verification.
2026-01-20 19:13:14 -05:00
Scott Idem
f244526538 Fix(IDAA): Harden Recovery Meetings search and fix detail page crash
- Search Hardening: Implemented 'Inclusive OR' logic for physical/virtual filters and restored robust full-text search using 'default_qry_str' with wildcards.
- Crash Fix: Added null checks for 'contact_li_json' in meeting detail view to prevent TypeError.
- ID Stability: Enhanced 'core__idb_dexie.ts' to support 'id_random' mapping and fixed misleading save logging.
- Reliability: Both V2 and V3 search paths now consistently return processed objects with standardized IDs.
2026-01-20 18:26:08 -05:00
Scott Idem
07d7b4ec6d Fix(Events): Isolate IDAA Search to V2 and Refine V3 Search Pattern
- IDAA Isolation: Created  using legacy V2 endpoints and  for Recovery Meetings stability.
- V3 Refinement: Implemented 'Body + Header' injection in  to fix 'Integer Trap' while maintaining Auth scope.
- API Upgrade: Enhanced  to support custom headers.
- Docs: Updated migration guide and development history with final isolation strategy.
2026-01-20 18:25:14 -05:00
Scott Idem
6707b6826b Fix(Events): Revert to URL-based account context for V3 Search 2026-01-20 18:24:39 -05:00
Scott Idem
ace707bb7d Fix(IDAA): Revert account_id body injection for Events V3 search
Restored the use of 'for_obj_type' and 'for_obj_id' URL parameters for the 'event/search' endpoint. The backend permission middleware requires these URL parameters to validate the API Key scope for the Event object, whereas body-only injection (used in Archives/Posts) was causing a 403 Forbidden error for Events.
2026-01-20 18:23:55 -05:00
Scott Idem
6380effa90 Quickly saving in progress changes. We are working on why the IDAA Recovery Meetings are not loading. 403 errors. 2026-01-20 18:23:40 -05:00
Scott Idem
0e411531eb V3 Hardening & Fixes: Structured Errors, JWT Fallbacks, and Module Stability
- Implemented Structured Error Handling across GET/POST/PATCH helpers to extract rich V3 error metadata.
- Added direct localStorage fallback for JWT detection to resolve race conditions during initial page load.
- Fixed async race condition in Archives leading to 'archive_content_li is undefined' crash.
- Hardened generic object processor to handle non-array API responses gracefully.
- Resolved zero-result bug in Event Search by using raw 'account_id_random' to bypass backend mapping conflicts.
- Isolated bootstrap headers in +layout.ts and removed invalid response headers from request config.
- Enhanced /testing dashboard with live header inspection and V3 hardening audits.
2026-01-20 18:23:40 -05:00
Scott Idem
c40a296a77 API V3: Implement Structured Error Handling and Validation Tests
- Updated api_get_object and api_post_object to extract rich metadata (meta.details) from 400/500 responses.
- Enables frontend to bubble up specific DB schema, validation, and constraint errors for better debugging.
- Added 'V3 Hardening' section to /testing dashboard with automated tests for Permissive Mode and Structured Error extraction.
2026-01-20 18:22:14 -05:00
Scott Idem
8566917be1 API Hardening: Refine Bypass Logic and Enable Permissive Mode
- Hardened 'Bootstrap Paradox' bypass logic in GET/POST helpers to only strip account ID if an intentional bypass value is provided.
- Enabled 'Permissive Update Mode' (x-ae-ignore-extra-fields: true) by default to improve frontend state synchronization.
- Fixed loader hydration bug where isolated API headers were being overwritten by stale global defaults.
- Ensured correctly resolved account names persist in local state instead of defaulting to 'Ghost Account'.
- Added Environment & Bridge diagnostics section to the testing dashboard for easier runtime verification.
2026-01-20 18:22:14 -05:00
Scott Idem
25d6503afe Environment & Bootstrap Stability: Fix Ghost Account and Modernize PWA Manifest
- Resolved 'Ghost Account' warning by updating layout hydration to align with V3 ID Vision (account_id vs account_id_random).
- Improved site lookup reliability using Agent API Key and structured EQ filters for exact FQDN matching (including ports).
- Modernized PWA manifest with maskable icons (PNG/WebP), app shortcuts, and unique installation IDs.
- Implemented automatic Electron 'Native' mode detection in root layout.
- Fixed stale API URLs in Launcher native file download logic.
- Added V3 migration documentation and JWT verification test scripts.
2026-01-20 18:22:14 -05:00
Scott Idem
08d0d9ca45 feat: Refine dynamic manifest with OSIT branding and full icon set
- Incorporated comprehensive icon list from dgr.oneskyit.com reference.
- Updated dynamic naming logic to 'One Sky IT - {Account} Aether PWA'.
- Set display mode to 'fullscreen' per reference standard.
- Added verification tool to the Testing Dashboard.
2026-01-20 18:15:28 -05:00
Scott Idem
79e8411842 feat: Implement dynamic domain-based PWA manifest
- Added /manifest.webmanifest server-side route.
- Implemented hostname-based branding lookup using Agent API Key.
- Updated app.html to use the dynamic manifest route.
- Added manifest verification tool to the System Testing dashboard.
2026-01-20 18:10:40 -05:00
Scott Idem
f565857e20 feat: Implement API V3 Testing Dashboard and Security Hardening
- Added comprehensive System Testing dashboard with live V3 trace tool.
- Implemented Section 2D 'Fail-Fast' protocol in get_object helper.
- Added reactive JWT synchronization in root layout to ensure V3 consistency.
- Resolved Tailwind 4 @apply compilation errors in testing page.
2026-01-20 17:54:15 -05:00
Scott Idem
0b2ed6ce08 feat(pwa): implement dynamic manifest.webmanifest and service worker for multi-tenant support 2026-01-16 19:09:03 -05:00
Scott Idem
ecb6ba5250 refactor: improve type safety, Svelte 5 reactivity, and API resilience 2026-01-16 17:29:33 -05:00
Scott Idem
09d1aa6720 Hardened Journals and IDAA module load functions for offline resilience and ghost account support. 2026-01-16 17:04:56 -05:00
Scott Idem
f1f905c626 Refined offline banner UI with semi-transparency and collapse/expand toggle. 2026-01-16 16:54:33 -05:00
Scott Idem
24ccf38412 Hardened root layout initialization and ghost fallback logic to resolve 500 errors during API downtime. 2026-01-16 16:51:11 -05:00
Scott Idem
a10accfaaf Implemented offline-first fast-paths and hardened API/Layout resilience. Added reactive offline banner, root error page, and ghost site fallbacks to handle server downtime and connection loss without crashing. 2026-01-16 16:41:32 -05:00
Scott Idem
8b611e7875 feat(offline): implement Dexie fallbacks and fix type stability in Events module
- Offline Resilience: Added local Dexie fallbacks to load_ae_obj_id__* and load_ae_obj_li__* functions for Events, Sessions, Locations, Exhibits, and Badges.
- Type Safety: Standardized ae_Event and ae_EventSession interfaces in ae_types.ts; resolved Promise assignment errors in load functions (+page.ts).
- Bug Fixes: Corrected duplication in ae_events__event_location.ts and ae_events__event_badge_template.ts; fixed missing try_cache parameters.
- UI Stability: Fixed Dexie LiveQuery subscription pattern in bulk print view and ensured proper property access in layout load functions.
2026-01-16 15:09:10 -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
5ee17c2925 The badge view ID now correctly renders a badge! This took way too long. So many $ were removed and things missed. 2026-01-16 13:07:28 -05:00
Scott Idem
07572e0f5c The badge does not yet render and are not fully working on the badge view ID page, but I am saving anyways. It at least works and seems to finally be moving in the right direction. It is partially related to the badge_template records not being saved to the badge_template. 2026-01-16 12:44:30 -05:00
Scott Idem
c7d63da8a1 fix: Consolidate type and parameter fixes across multiple modules
- API: Standardized 'order_by_li' types in event and archive modules.
- API: Corrected 'enabled'/'hidden' parameter types in event exhibit and device search/list functions.
- Type Safety: Addressed generic type casting issues in _process_generic_props across event modules.
- Data Handling: Resolved return type consistency in journal creation and DB save operations.
- Parameter Management: Fixed missing 'try_cache' parameters in event exhibit functions.
- Core Logic: Ensured correct object properties in DB put operations for hosted files.
2026-01-15 19:56:37 -05:00
Scott Idem
ee1ca93894 fix: resolve TypeErrors in core, events, and journals modules
- Core: Use FormData in video clip components to fix EventTarget errors.
- DB: Rename 'File' to 'ae_LocalFile' in db_core to prevent DOM type shadowing.
- API: Strictly type 'order_by_li' across archives, events, and journals to match API definition.
- API: Fix 'enabled'/'hidden' parameter types in search functions.
- Generics: Add 'any' cast to 'processed_obj' in generic processors to fix indexing errors.
- Journals: Update journal_entry creation to return null on failure, fix missing ID fields in DB save.
2026-01-15 19:27:53 -05:00
Scott Idem
ab1c207c86 feat: standardize OrderBy types and fix data model mismatches
- Update ae_types.ts with joined fields for deep layout loading
- Fix OrderBy vs OrderBy[] type mismatch in API v2/v3 and generic CRUD
- Apply 'as const' to order_by_li defaults in core/event libraries
- Resolve type errors in reports_presenters and reports_files Svelte components
2026-01-15 19:03:26 -05:00
Scott Idem
31f18b8723 fix(types): systemic cleanup of high-impact svelte-check errors
- Resolved Svelte 5 $bindable() prop errors in Analytics and Person Table components.
- Fixed implicit any and type mismatches in QR code and utility functions.
- Corrected OrderBy type mismatch pattern in systemic loaders.
- Improved type safety for BlobPart and ArrayBuffer handling.
- Down to 700 errors from ~731.
2026-01-15 18:29:27 -05:00
Scott Idem
d4753de9e2 fix(core): add missing links and modernize list pages
- Added missing Addresses and Contacts links to Core Management main page.
- Modernized list pages for Accounts, Sites, Activity Logs, and Lookups.
- Standardized headers, iconography, and search layouts across all core list views.
- Improved layout responsiveness and visual hierarchy.
2026-01-15 18:24:42 -05:00
Scott Idem
156cb79a24 fix(core): comprehensive search UI redesign and module modernization
- Rebuilt Users and People search from scratch using robust flexbox (grow, min-w-100px, whitespace-nowrap).
- Added search functionality and card-grid layout to Address and Contact management lists.
- Modernized detail pages for Addresses and Contacts with standardized headers and iconography.
- Unified form padding (p-3 for inputs, p-2 for selects) across all Core modules.
- Fixed variable hoisting and missing icon imports in Address components.
2026-01-15 18:17:44 -05:00
Scott Idem
b2f695c846 feat(core): standardized search UI and improved form padding
- Standardized User Management search to match People Management style.
- Added 'p-3' padding to main search inputs and 'p-2' to selects for better visual clarity.
- Improved search icon spacing with enhanced shim padding (!px-4).
- Applied consistent padding enhancements to Account, Site, and User detail pages.
- Standardized 'Go' button prominence across search sections.
2026-01-15 18:01:52 -05:00
Scott Idem
39d0707968 Lots of style clean up 2026-01-15 17:51:48 -05:00
Scott Idem
572ce1841b feat(core): standardized form inputs and buttons across core modules
- Updated Person, Address, Contact forms with consistent Skeleton UI styling.
- Standardized Account, Site, and User detail pages.
- Modernized People, Address, and Contact list pages with improved headers and action buttons.
- Applied 'variant-filled-surface' and 'rounded-lg' patterns from Journals module for UI consistency.
2026-01-15 17:40:46 -05:00
Scott Idem
96adf1fa26 Better looking testing dashboard. Still needs work though. It does not scroll correctly. 2026-01-15 17:28:26 -05:00
Scott Idem
52f5d0bab6 fix(core): use multi-call strategy for inclusive User Management scope
- Updated load_ae_obj_li__user to handle 'All' scope by fetching account and global users separately.
- Ensured Search API uses authorized account_id_random field with op: 'eq' for null.
- Fixed 404 for global lookups by avoiding [NULL] in standard List API.
2026-01-15 16:17:25 -05:00
Scott Idem
c219c78e8f fix(core): use supported NULL comparison operator in User Management search
- Switched from 'op: is' to 'op: eq' for NULL values, as 'is' was returning 400.
- Refined search query structure to ensure compatibility with user search endpoint.
2026-01-15 15:16:40 -05:00
Scott Idem
f23b515f11 fix(core): resolve zero-results and incorrect scoping in User Management
- Refined load_ae_obj_li__user to use List API for simple account filtering and Search API for complex cases.
- Fixed 'Global Only' scope returning all users by ensuring correct NULL filtering.
- Improved search query structure with top-level 'or' for inclusive global support.
- Added detailed logging to load_ae_obj_li__user for easier debugging.
2026-01-15 14:58:52 -05:00
Scott Idem
227c806318 fix(core): improve User Management filtering and scope support
- Updated load_ae_obj_li__user to use search_ae_obj_v3 for complex account OR global filtering.
- Fixed zero-results issue by defaulting to 'All' scope (Current Account + Global).
- Added visual 'Account' vs 'Global' badges to user cards.
- Added 'Scope' selector to User Management page.
2026-01-15 14:45:34 -05:00
Scott Idem
111c828a04 feat(core): default User Management to current account context
- Updated load_ae_obj_li__user to support for_obj_id filtering.
- Updated create_ae_obj__user to support account_id for new users.
- Updated User Management page to default to current account_id for listing and creation.
2026-01-15 14:40:09 -05:00
Scott Idem
df9e14d896 feat(core): enhance Person management and modernize Address/Contact lists
- Improved Person table with 'Not Linked' badge for better visibility.
- Updated Person form with 'prefix' field and direct User account linking.
- Standardized Address and Contact list views to show all records (enabled/hidden).
- Amending previous refactor to include all verified import and type fixes.
2026-01-15 14:36:25 -05:00
Scott Idem
74d107f157 refactor(core): consolidate logic and modernize imports
- Unified Person and User logic into ae_core__* counterparts and marked legacy files.
- Renamed Activity Log to ae_core__activity_log.ts for naming consistency.
- Updated all core function imports across the identity, logs, and video conference modules.
- Fixed missing 'prefix' field in Person form payload and corrected return types in Activity Log.
- Updated project TODO to reflect completed core module refinements.
2026-01-15 14:29:35 -05:00
Scott Idem
7ce9c5e093 fix(auth): resolve sign-in crashes and email authentication 500 error
- Refactored authentication calls in core__user.ts to explicitly set x-account-id and remove x-no-account-id, ensuring correct account context for legacy endpoints.
- Updated emailed sign-in link logic to use the correct /user/{user_id}/email_auth_key_url endpoint and avoid 500 crashes caused by extraneous URL parameters.
- Fixed person search query field names (enable/hide) and broadened search scope to 'all' to ensure records are found regardless of status.
- Added safety checks and documentation to prevent UI crashes when API responses are empty or NULL.
2026-01-15 14:03:49 -05:00
Scott Idem
b88d5fbabf Fixes for username and password sign in! Quick save while Gemini still working. 2026-01-15 11:28:57 -05:00
Scott Idem
380e80ed7d Saving notes 2026-01-14 19:14:03 -05:00
Scott Idem
98cd35078d feat(journals): implement background auto-save for status and security fields
- Refactored handle_update_journal and handle_update_entry to support non-closing background persistence.
- Implemented strict payload whitelisting for journal and journal_entry updates to resolve HTTP 400 schema errors.
- Added onchange triggers to enable, hide, priority, and sort fields for real-time background syncing.
- Standardized 'Status & Security' tab labeling across configuration components.
2026-01-14 19:08:37 -05:00
Scott Idem
dbb45e10ae style(journals): standardize and stabilize 'Config' interfaces across all levels 2026-01-14 18:52:45 -05:00
Scott Idem
0108e28f59 More clean up. Saving notes. 2026-01-14 18:30:36 -05:00
Scott Idem
9caeb91394 Quickly saving good looking and mostly working config menus. 2026-01-14 18:27:46 -05:00
Scott Idem
cdf318e744 style(journals): stabilize and standardize all configuration interfaces
- Implemented 'untrack' safeguards in all modal effect blocks to prevent reactivity loops.
- Standardized Module, Journal, and Entry configuration with unified tabbed UI and Aether Orange theme.
- Fixed critical 'journal is undefined' and ReferenceErrors in configuration modals.
- Restored missing button toggles and visibility options in journal-level settings.
- Documented Dexie liveQuery '$' prefix mandate in GEMINI.md and project documentation.
- Eliminated legacy JournalEntry_SettingsMenu in favor of ModalJournalEntryConfig.
2026-01-14 17:50:48 -05:00
Scott Idem
8ae23cdcd9 style(journals): standardize configuration interfaces across module
- Refactored Module, Journal, and Entry configs to use consistent tabbed layouts.
- Adopted unified 'Aether Orange' aesthetic with Skeleton UI and Lucide icons.
- Replaced overcrowded inline settings menu with 'ModalJournalEntryConfig'.
- Cleaned up Header component logic and improved visual consistency.
2026-01-14 17:17:56 -05:00
Scott Idem
04e4350fbf feat(journals): implement force reset for lost passcodes and harden sync
- Added 'Force Reset to Plain Text' button in the editor for decryption failure states.
- Guarded reset functionality with '.edit_mode'.
- Refactored background sync logic to properly isolate 'untrack' calls and prevent content resets.
2026-01-14 17:06:00 -05:00
Scott Idem
cd0702b8cc fix(journals): resolve AI tools ReferenceError and finalize modularization
- Corrected template usage to 'JournalEntry_AITools'.
- Finalized Chunk 3: Modularized AI tools wrapper.
2026-01-14 16:52:00 -05:00
Scott Idem
bb3e4f4095 fix(journals): resolve browser hang during privacy toggle
- Refactored effects to use selective tracking and 'untrack' for all store reads.
- Implemented manual 'deep_copy' to handle Svelte 5 reactive proxies safely.
- Added concurrency locks ('is_processing') to decryption and save workflows.
- Stabilized state synchronization to prevent circular reactivity loops.
2026-01-14 16:48:50 -05:00
Scott Idem
11e022d21e Lots of updates to the notes. Style/layout fix for main Journals page. 2026-01-14 16:48:00 -05:00
Scott Idem
c907a95c57 fix(journals): eliminate reactivity loops and browser hangs during privacy toggle
- Refactored all effects to use 'untrack' for rigorous dependency isolation.
- Implemented 'deep_copy' helper to safely handle Svelte 5 reactive proxies.
- Added concurrency locks to 'update_journal_entry' and decryption workflows.
- Completed integration of 'JournalEntry_Metadata' component.
2026-01-14 16:35:25 -05:00
Scott Idem
b9fc4addfc fix(journals): resolve browser hang and integrate Metadata component
- Implemented manual 'deep_copy' to handle reactive proxies safely.
- Added concurrency locks ('is_processing') to prevent PATCH loops.
- Standardized metadata display by switching to 'JournalEntry_Metadata'.
2026-01-14 16:31:27 -05:00
Scott Idem
58a41e093b fix(journals): resolve reactivity loop and hang during privacy toggle
- Refactored effects to use 'untrack' for store isolation.
- Implemented content normalization in 'has_unsaved_changes' to avoid circular triggers.
- Ensured atomic state sync after successful PATCH updates.
- Added comprehensive flow diagnostics to track effect execution.
2026-01-14 16:20:44 -05:00
Scott Idem
574403244b fix(journals): harden privacy toggle and add effect diagnostics
- Ensured content is decrypted before allowing conversion to plain text.
- Added detailed diagnostic logging to reactive effects to debug loop issues.
- Integrated background sync skip logic to preserve decrypted state.
2026-01-14 16:10:30 -05:00
Scott Idem
693a2e42c4 fix(journals): resolve decryption UI update and background sync issues
- Cleared 'content_encrypted' from local state upon successful decryption to update UI.
- Modified background sync effect to preserve decrypted content during active sessions.
- Enhanced decryption logging for better diagnostics.
2026-01-14 16:04:15 -05:00
Scott Idem
05bf348e0f fix(journals): resolve decryption toggle issues and component errors
- Added diagnostic logging to decryption helper to track passcode source.
- Refactored 'handle_content_decryption' to prioritize unlocking content.
- Fixed 'updated_obj' reference error in main view.
2026-01-14 16:00:05 -05:00
Scott Idem
6562d4ba04 feat(journals): extract decryption logic to helper and improve Quick Add behavior
- Extracted journal entry decryption workflow to 'ae_journals_decryption.ts' to decouple it from Svelte reactivity and address loop issues.
- Updated 'ae_comp__journal_entry_obj_id_view.svelte' to use the new decryption helper.
- Refactored 'Quick Add' to use the first line as the title and remove it from the content before saving.
2026-01-14 15:52:02 -05:00
Scott Idem
8c9788bd41 feat(journals): modernize UI with responsive grid and fix Quick Add integration 2026-01-14 15:20:05 -05:00
Scott Idem
283053e4a4 feat(journals): stabilize V3 API integration and restore cryptographic toggles 2026-01-14 14:35:55 -05:00
Scott Idem
17870bc0a2 Bug fix for the auto save saving over and over and over. 2026-01-14 13:18:35 -05:00
Scott Idem
56fa003382 feat(journals): implement centralized export templates and bulk interop
- Added 'ae_journals_export_templates.ts' with Markdown, HTML, and JSON support
- Refactored 'ae_comp__modal_journal_export.svelte' to use the new template system
- Optimized bulk export with automated template selection based on Journal type
- Integrated 'Import Entries' action directly into the Journal menu
- Updated project documentation to reflect portability features and implementation status
2026-01-14 12:55:45 -05:00
Scott Idem
b80cbb7c2b A quick save while the new exports are fully working. The prepend and append also work now. 2026-01-14 12:29:47 -05:00
Scott Idem
228f0ecf06 docs(journals): update project status tracking
- Marked Phase 2 (Rapid Entry) and Phase 3 (Content Portability) as complete
- Logged new Auto-Save functionality in Phase 4
- Updated TODO.md and module documentation to reflect current state
2026-01-13 23:59:35 -05:00
Scott Idem
0143f18ebb feat(journals): implement auto-save toggle and visual status
- Added 'auto_save' preference to journals store
- Added toggle and status indicators (Check/X/Loader) to JournalEntry_Header
- Implemented auto-save logic with debounce in Journal Entry view
2026-01-13 23:52:42 -05:00
Scott Idem
b1e9902285 feat(journals): expose export functionality in UI
- Added 'Export Entries' button to Journal Index and Journal Detail views
- Wired buttons to trigger the new export modal
2026-01-13 23:43:50 -05:00
Scott Idem
b3ac910832 fix(journals): resolve Svelte parser error in export modal
- Replaced  directive with string interpolation for Tailwind classes containing colons and slashes (e.g. ), which caused build failures.
2026-01-13 23:42:41 -05:00
Scott Idem
07bb31f412 feat(journals): add drag-and-drop zone to import modal 2026-01-13 23:30:30 -05:00
Scott Idem
d691fa8cb3 feat(journals): implement bulk Markdown import with multiple parsing strategies
- Added 'ae_journals_parsers.ts' with Standard, Personal Log, and Amazon Vine parsers (ported from Python)
- Created 'AeCompModalJournalImport' for file selection, preview, and API submission
- Integrated Import button into Journals list view
2026-01-13 23:22:46 -05:00
Scott Idem
8fd11d7224 feat(journals): implement Quick Add and unified Append/Prepend shared component
- Created AeCompJournalEntryQuickAdd for high-velocity note creation
- Extracted robust append/prepend logic from List View into AeCompModalJournalEntryAppend
- Unified List and Detail views to use the shared modal for content manipulation
- Added explicit Append/Prepend actions to Journal Entry settings menu
- Updated TODO.md and Journals module documentation
2026-01-13 22:59:08 -05:00
Scott Idem
80bc5e453a Changes related to the directory name change. 2026-01-13 14:42:02 -05:00
Scott Idem
93dd8271eb fix: resolve Svelte 5 'props_invalid_value' error by removing defaults from $bindable props
Binding to an undefined value when a $bindable prop has a default fallback causes a runtime error in Svelte 5 runes mode. Defaults are now applied within the component logic.
2026-01-12 12:50:35 -05:00
Scott Idem
bb964edfc5 Saving notes 2026-01-09 16:51:11 -05:00
Scott Idem
5056d5d8f0 Standardize Core UI forms and unify schemas for V3 API compatibility
- Implement new Svelte 5 Person, Address, and Contact form components with surgical payload logic.
- Refactor core routes (People, Addresses, Contacts) to support unified Create/Edit workflows.
- Update ae_types.ts, db_core.ts, and db_journals.ts to align with V3 backend object models.
- Fix type safety issues in Journal history views and refine metadata display.
- Migrate person core functions to the newer ae_core__person module.
2026-01-09 15:14:36 -05:00
Scott Idem
8e205b2db9 Saving notes 2026-01-08 19:27:32 -05:00
Scott Idem
09995eb0d7 Fix type safety in Journal Entry Settings Menu 2026-01-08 19:08:06 -05:00
Scott Idem
5947459330 Remove unused local state from Journal Entry view 2026-01-08 19:06:47 -05:00
Scott Idem
d1853f51f4 Refactor Journal Entry view: Extract Header and clean up Editor bindings 2026-01-08 19:05:33 -05:00
Scott Idem
4fa59c3c0f Saving these files that should be pretty good. 2026-01-08 18:56:44 -05:00
Scott Idem
ca73a5376a Finalize Journal Entry view cleanup and modularization
- Synchronized Lucide icon imports with template usage.
- Removed all redundant local state and legacy crypto comments.
- Verified stable integration of modular AITools, ObjectFlags, and MetadataFooter.
- Standardized Svelte 5 logic for better maintainability and token efficiency.
2026-01-08 18:31:16 -05:00
Scott Idem
8927128561 Modularize Journal Entry Editor and continue God Component decomposition
- Extracted complex View/Edit logic into standalone JournalEntry_Editor.svelte.
- Integrated modular editor with bindable state and editorView.
- Maintained support for CodeMirror, Plain Text, and Rendered HTML modes.
- Simplified main view by removing another 300+ lines of template logic.
2026-01-08 18:22:20 -05:00
Scott Idem
86608f66fa This file was moved I think/hope 2026-01-08 18:18:59 -05:00
Scott Idem
5ffab36ef7 Add independent settings access to AI Tools component
- Added a dedicated gear button to open the AI modal directly to the Settings tab.
- Resolved the 'chicken and egg' problem where settings were inaccessible if the AI process failed.
- Improved layout consistency with inline-flex wrapper.
2026-01-08 18:16:45 -05:00
Scott Idem
b912350829 Add Demo Mode and error handling to AE_AITools component
- Implemented 'Demo Mode' fallback when no API token is provided.
- Ensured the AI modal opens even on connection errors to display troubleshooting info.
- Improved user feedback with simulated loading states for demo data.
2026-01-08 18:15:16 -05:00
Scott Idem
51bdad9485 Centralize AI configuration into generic AE_AITools component
- Enhanced AE_AITools with a Settings tab for model, prompt, and parameter configuration.
- Connected AE_AITools to Journals state via two-way bindings for persistent configuration.
- Removed redundant AI settings from Journals config modal.
- Standardized Svelte 5 patterns for cross-module component configuration.
2026-01-08 18:12:15 -05:00
Scott Idem
c884eed52c Implement generic AE_ObjectFlags and AE_MetadataFooter components
- Created AE_ObjectFlags.svelte for standardized flag management across all modules.
- Created AE_MetadataFooter.svelte for unified creation/update/original timestamp display.
- Modularized Journal Entry view by replacing manual flag and footer logic with generic elements.
- Improved code reusability and reduced main component complexity.
2026-01-08 18:08:47 -05:00
Scott Idem
efe8677ab6 Implement generic AE_AITools component and integrate into Journals
- Created reusable AE_AITools.svelte in src/lib/ae_elements for system-wide AI features.
- Refactored Journal Entry view to utilize the generic AI toolset.
- Cleaned up redundant module-specific AI logic and modal code.
- Standardized Svelte 5  patterns for AI summary results.
2026-01-08 18:02:05 -05:00
Scott Idem
0c16649cb4 Modularize AI Tools and perform third cleanup pass on Journal Entry view
- Extracted AI summarization logic and Modal into standalone JournalEntry_AITools component.
- Applied Prettier formatting to main component for consistent structure.
- Removed further blocks of legacy script and template code.
- Re-verified stability of core features (decryption, saving, metadata).
2026-01-08 17:51:36 -05:00
Scott Idem
28a4e9457e Working on cleaning this up and breaking it into smaller chunks. 2026-01-08 17:49:25 -05:00
Scott Idem
467a49e86b Stabilize Journal Entry view and complete second comment audit pass
- Restored accidentally removed article tag and corrected syntax errors.
- Cleaned up extensive blocks of redundant comments and legacy code in the script and template.
- Refined component structure to serve as a lean template for other Aether modules.
- Re-verified all core UI logic (edit mode, decryption, data sync).
2026-01-08 17:42:41 -05:00
Scott Idem
3d98233c84 Saving my changes. Cleaning up the comments. 2026-01-08 17:37:40 -05:00
Scott Idem
f35a1a10d0 Stabilize Journal Entry view and perform initial comment audit
- Cleaned up redundant imports, legacy script stubs, and dead code.
- Removed extensive legacy comments from history and AI summary template sections.
- Verified stability of core decryption and data sync logic.
- Standardized file structure to serve as a better template for other Aether modules.
2026-01-08 17:31:08 -05:00
Scott Idem
bd184d5440 Modularize Journal Entry view and refine UI logic
- Extracted AI Tools and Metadata into dedicated Svelte 5 components (JournalEntry_AITools, JournalEntry_Metadata).
- Standardized iconography to Lucide across list and detail views.
- Refined sort order controls with improved styling and Lucide icons.
- Fixed 'OpenAI is not defined' ReferenceError by restoring necessary imports.
- Corrected component naming discrepancy for Journal_entry_obj_file_li.
- Hardened change detection using Svelte 5 .by for reliable Save button behavior.
2026-01-08 16:59:12 -05:00
Scott Idem
b50cbe7972 Fix ReferenceError: CalendarClock and Bot not defined in Journals Config modal 2026-01-08 16:40:50 -05:00
Scott Idem
6146cebaa4 Fix build warnings and restore missing event sync function
- Restored sync_config__event_pres_mgmt in ae_events__event.ts to resolve import errors.
- Removed unused OpenAI import in journal entry view.
- Removed unused Html5QrcodeScannerState import in QR scanner component.
- Improved build consistency for staging and production environments.
2026-01-08 16:17:57 -05:00
Scott Idem
dd0390a5dd Refine Journals UI and harden entry toggle logic
- Implemented 3-state (View/Eye/Save) toggle in journal entry header with Lucide icons.
- Hardened change detection logic using Svelte 5 .by for reliable UI updates.
- Improved header responsiveness and scaling across device sizes.
- Commented out experimental CodeMirror toolbar to maintain stability while keeping the helper code for reference.
2026-01-08 15:38:28 -05:00
Scott Idem
4c8f09e588 Finalize Unified Type Migration and scaffold core logistics logic
- Completed the unified type system in ae_types.ts covering all 42 active Aether objects.
- Scaffolded missing core logic modules for Organization, EventTrack, and Sponsorship with standardized return types.
- Updated project roadmap in TODO.md to reflect mission completion on foundational type parity.
2026-01-08 15:11:37 -05:00
Scott Idem
2b21031beb Fix CodeMirror stability and dispatch errors
- Replaced dangerous custom dispatch override with EditorView.updateListener.
- Resolved 'Uncaught TypeError: child is undefined' by allowing CodeMirror to manage its own update cycle.
- Improved Svelte state syncing for new_content.
2026-01-08 14:56:19 -05:00
Scott Idem
66ddc9ace4 Fix duplicate declaration of editorView in CodeMirror component 2026-01-08 14:43:10 -05:00
Scott Idem
01ced00f78 Refactor Journals UI and enhance CodeMirror editor
- Added formatting toolbar to journal entry editor with support for bold, italic, headers, and lists.
- Standardized iconography to Lucide across Journals module, removing legacy FontAwesome.
- Improved responsiveness and dark mode compatibility in layout and list views.
- Refactored CodeMirror component to support external control via editorView binding.
- Hardened security by removing unnecessary @html tags in journal names.
2026-01-08 14:39:26 -05:00
Scott Idem
480094305c Update TODO.md: Journal module migrated, 7 active interfaces remain 2026-01-08 14:21:08 -05:00
Scott Idem
9d6b0ef568 Migrate Journal module to unified type system
- Added detailed fields for ae_EventPerson, ae_EventRegistration, ae_AccountCfg, and others to ae_types.ts.
- Replaced local interfaces in ae_journals__journal.ts with unified imports.
- Standardized Promise return types for all core data loading, creation, search, and update functions in Journal module.
- Finalized migration for primary Identity and Journal objects.
2026-01-08 14:19:22 -05:00
Scott Idem
e138f12c6d Migrate Event Exhibit and Tracking modules to unified type system
- Added ae_EventExhibit and ae_EventExhibitTracking interfaces to ae_types.ts.
- Replaced local interfaces in ae_events__exhibit.ts with unified imports.
- Standardized Promise return types for all core data loading, creation, and update functions in Exhibit and Tracking logic.
- Integrated triple-ID patterns for lead retrieval and booth management components.
2026-01-08 14:15:45 -05:00
Scott Idem
0749e899b2 Update TODO.md with latest unified type migration progress 2026-01-08 14:10:26 -05:00
Scott Idem
8656fa0c42 Migrate Posts and Event Presentations to unified type system
- Integrated ae_Post, ae_PostComment, ae_Page, and ae_Archive types into logic files.
- Replaced local interfaces in ae_posts__post.ts and ae_posts__post_comment.ts.
- Standardized Promise return types for all CRUD and loading functions in Post and Presentation modules.
- Synchronized event presentation logic with V3 triple-ID patterns.
2026-01-08 14:06:31 -05:00
Scott Idem
541a06135c Migrate Event child modules to unified type system
- Added ae_EventFile, ae_EventDevice, ae_EventAbstract, and ae_Organization to ae_types.ts.
- Replaced local interfaces in EventFile and EventDevice logic files with unified imports.
- Standardized Promise return types for all core data loading, creation, search, and update functions.
- Integrated triple-ID patterns for Dexie and V3 API parity across files and devices.
2026-01-08 13:59:03 -05:00
Scott Idem
c46485a954 Migrate Content and Storage modules to unified type system
- Added ae_HostedFile, ae_HostedFileLink, ae_DataStore, ae_Post, ae_PostComment, ae_Page, ae_Archive, and ae_ArchiveContent to ae_types.ts.
- Replaced local interfaces in core__hosted_files.ts and core__data_store.ts with unified imports.
- Replaced local interfaces in ae_archives/*.ts with unified imports.
- Standardized Promise return types for all core data loading, creation, search, and update functions across migrated modules.
- Synchronized storage and archival modules with Backend V3 field naming and Triple-ID patterns.
2026-01-08 13:44:07 -05:00
Scott Idem
113905f731 Migrate Event logistics modules to unified type system
- Added ae_EventLocation, ae_EventSession, ae_EventPresenter, and ae_EventTrack to ae_types.ts.
- Replaced local interfaces in logistics logic files with unified imports.
- Standardized Promise return types for all core data loading, creation, search, and update functions.
- Integrated triple-ID patterns for Dexie and V3 API parity across locations, sessions, and presenters.
2026-01-08 12:49:31 -05:00
Scott Idem
777f453894 Migrate Address and Contact modules to unified type system
- Added ae_Address and ae_Contact interfaces to ae_types.ts.
- Replaced local interfaces in Address and Contact logic files with unified imports.
- Standardized Promise return types for all core data loading, creation, and update functions in migrated modules.
- Refined field optionality to align with frontend CRUD requirements.
2026-01-08 12:22:47 -05:00
Scott Idem
324b65394a Migrate User and ActivityLog modules to unified type system
- Added ae_User and ae_ActivityLog interfaces to ae_types.ts.
- Replaced local interfaces in User and ActivityLog logic files with unified imports.
- Standardized Promise return types for all core data loading, creation, and update functions.
- Verified permission field alignment for User type to support hierarchical access logic.
2026-01-08 12:20:21 -05:00
Scott Idem
25b51e1081 Migrate Event and EventBadge modules to unified type system
- Updated ae_types.ts with ae_Event, ae_EventBadge, and ae_EventBadgeTemplate interfaces.
- Standardized return types (Promise<ae_Event[]>, etc.) for all core loading and CRUD functions in Events module.
- Integrated triple-ID patterns for Dexie and V3 API parity in event-related objects.
- Cleaned up local interface redundancies in favor of unified imports.
2026-01-08 12:10:31 -05:00
Scott Idem
78bbba5999 Migrate Person and Site modules to unified type system
- Updated ae_types.ts with detailed fields for ae_Person, ae_Site, and ae_SiteDomain based on V3 backend exports.
- Replaced local interfaces in ae_core__person.ts and ae_core__site.ts with unified imports.
- Added explicit Promise return types to all core data loading and CRUD functions in Person and Site modules.
- Standardized triple-ID pattern and return signatures across core identity and configuration modules.
2026-01-08 11:37:32 -05:00
Scott Idem
d9848234a4 Implement unified frontend type system and migrate core modules
- Created src/lib/types/ae_types.ts to serve as the central repository for Aether object interfaces.
- Standardized interfaces to include the triple-ID pattern (id, [type]_id, [type]_id_random) for Dexie and V3 API compatibility.
- Migrated Account and JournalEntry modules to use ae_Account and ae_JournalEntry types.
- Added explicit Promise return types to all core data loading, creation, and update functions in migrated modules.
2026-01-08 11:35:06 -05:00
Scott Idem
e355b7649d Refactor core API helpers and implement System Testing Dashboard
- Unified and hardened get, post, patch, and delete helpers with standardized retry logic, kebab-case headers, and V3 response envelope handling.
- Implemented robust 'Bootstrap Paradox' resolution logic across the API stack to handle unauthenticated site domain lookups safely.
- Enhanced API helpers to support custom fetch injection, enabling reliable server-side execution in SvelteKit.
- Upgraded /testing page into a comprehensive System Testing Dashboard for core helper and V3 search verification.
- Updated TODO.md and GEMINI.md with 2026-01-08 session learnings and 'Frontier Journals' vision.
2026-01-08 11:30:05 -05:00
Scott Idem
bc56b38ec1 Wrapping up for the night. Saving notes and things. 2026-01-07 19:34:30 -05:00
Scott Idem
e20898e513 Implement Bootstrap Paradox resolution for V3 site domain lookup
- Modified lookup_site_domain_v3 to strictly strip auth headers for guest lookup
- Enhanced /testing page with FQDN input and improved error visibility
- Updated TODO.md with Technical Debt refactoring roadmap
- Documented Unified Aether AI Agent (UE-AE-01) transition progress in GEMINI.md
2026-01-07 19:28:09 -05:00
Scott Idem
ea0d57658f Standardize JWT authentication and finalize Activity Log V3 migration 2026-01-07 17:43:23 -05:00
Scott Idem
87023e7483 Add Activity Logs link to Core Management layout menu 2026-01-07 12:45:16 -05:00
Scott Idem
3ed3f9ed90 Fix Activity Log visibility and search query construction
- Changed default 'enabled' and 'hidden' filters to 'all' for activity logs
- Refactored qry__activity_log to avoid sending empty 'and' arrays
- Ensured consistent filtering between API function and UI
2026-01-07 12:41:18 -05:00
Scott Idem
07479f17a8 Implement Activity Log management and Person activity integration
- Updated qry__activity_log to support filtering by person_id
- Created /core/activity_logs standalone page for monitoring system actions
- Enhanced Person detail page with 'Recent Activity' column showing real data
- Added 'Activity Logs' card to the Core Management dashboard
2026-01-07 12:20:52 -05:00
Scott Idem
5bd7c4756c Complete editable fields whitelists for all remaining AE objects
- Created .editable_fields.ts files for Journals, Events, and Sponsorships modules
- Standardized the whitelist pattern across the entire codebase
- Updated TODO.md and GEMINI.md with recent accomplishments and learnings
2026-01-07 11:56:28 -05:00
Scott Idem
9c6df5c7f9 Finalize IDAA Bulletin Board V3 migration and fix UI filtering issues
- Ensured 'account_id' is injected into post objects during processing to maintain IndexedDB filter consistency
- Resolved race condition by awaiting database clearing before refreshing posts
- Corrected 'archive_on' date comparison logic in BB component
- Exported 'qry__post' and enabled comment fetching during post search
- Updated GEMINI.md and TODO.md with project progress
2026-01-07 11:37:36 -05:00
Scott Idem
c0fc5052ab Fix SSR errors, enhance Person activity views, and expand Core CRUD
- Resolved Svelte 5 / SvelteKit SSR errors by adding browser checks for window.postMessage and Dexie database operations
- Prevented side effects on global state during detail page preloading by refactoring people/[person_id]/+page.ts to use shallow copies
- Implemented full V3 CRUD support, detail pages, and editable_fields for Address and Contact modules
- Enhanced Event and Post search to support filtering by person_id, enabling real related data in the Person detail view
- Fixed missing onMount import in Person detail component
2026-01-06 19:20:27 -05:00
Scott Idem
6d0531e227 Restore server-side 'conference' filter and finalize IDAA migration
- Restored 'conference' search filter in ae_events__event.ts (now supported by backend)
- Maintained local filtering as a safety fallback
- Finalized Bulletin Board (Posts) V3 migration
- Added editable_fields for all remaining IDAA-related core objects
2026-01-06 18:06:32 -05:00
Scott Idem
43d32f2e3e Migrate Bulletin Board (Posts) to V3 and finish IDAA V3 migration
- Migrated Post and Post Comment modules to Aether API CRUD V3
- Added editable_fields for Posts and Post Comments
- Implemented local filtering for 'archive_on' in BB route as V3 workaround
- Verified IDAA Archives and Recovery Meetings are functional on V3
- Ensured all IDAA logic follows the API -> Processor -> DB Save pattern
2026-01-06 17:47:17 -05:00
Scott Idem
bfa1943889 Fix IDAA Recovery Meetings loading and optimize API retry logic
- Implemented workaround for 'conference' field search restriction in V3 by using local filtering
- Optimized post_object to stop retrying on 4xx client errors (400, 401, 403)
- Migrated Archives and Event/Recovery Meeting modules to Aether API CRUD V3
- Added editable_fields definitions for Archive and Archive Content objects
2026-01-06 17:22:19 -05:00
Scott Idem
e1f97d5154 Migrate initial site domain lookup to Aether API CRUD V3
- Implemented lookup_site_domain_v3 in ae_core__site.ts using Search API
- Refactored root +layout.ts to use the new V3 lookup logic
- Standardized top-level data initialization with V3 response metadata
- Improved security by transitioning to standardized V3 API patterns
2026-01-06 16:41:06 -05:00
Scott Idem
d2084de4d8 Fix 500 error, add Address/Contact placeholders, and enhance Person activity view
- Fixed broken import path in /core dashboard
- Simplified core layout data requirements to prevent crashes
- Implemented V3 CRUD and Dexie tables for Addresses and Contacts
- Created placeholder management pages for /core/addresses and /core/contacts
- Added 'Linked Activity & Content' section to Person detail page to show related events and posts
2026-01-06 16:16:31 -05:00
Scott Idem
076ac1e662 Saving notes and todo updates 2026-01-06 15:51:19 -05:00
Scott Idem
0e93514262 Implement User Details page at /core/users/[user_id]
- Added dynamic route for user details with manager-level access
- Implemented permission management UI (Super, Manager, Admin, Verified)
- Added account status toggles and timestamp visibility
- Integrated with V3 User CRUD logic and editable field whitelist
2026-01-06 15:46:13 -05:00
Scott Idem
a0f04726e0 Restructure Core management into linkable routes and add common layout navigation
- Renamed person route to people for consistency
- Refactored /core dashboard to use standard <a> links
- Added common management <nav> to core layout
- Replaced goto() with standard links across all list and detail pages
- Fixed 500 error caused by broken import path and strict layout data
2026-01-06 15:33:08 -05:00
Scott Idem
5d2186e8ca Improve Person management and implement User linking
- Created Comp_person_search component for dashboard integration
- Improved reactivity in person results table using derived liveQuery
- Implemented User Account Linking UI in Person detail page
- Added logic to filter and link unlinked users to person records
- Modernized Core Dashboard with person search integration
2026-01-06 14:59:31 -05:00
Scott Idem
c77a9ef3dd Update project documentation and TODO list for 2026-01-06 progress 2026-01-06 13:50:16 -05:00
Scott Idem
00e80af3a1 Migrate Event Badges to V3 and implement Core Management pages
- Completed V3 migration for Event Badge CRUD operations
- Implemented User module V3 logic and editable fields
- Created management routes for Accounts, Sites, Users, and Lookups
- Updated Site Domain logic to use 'fqdn' and show 'access_key'
- Modernized Core Dashboard with navigation cards
- Restored Dexie User table definition
2026-01-06 13:38:47 -05:00
Scott Idem
1b318eeb8b Migrate Core modules (Account, Site, Person, Activity Log) to Aether API CRUD V3 pattern
- Created V3 logic files: ae_core__account.ts, ae_core__site.ts, ae_core__person.ts
- Standardized on API -> Processor -> DB Save pattern
- Added editable_fields definitions for Account, Site, Person, and Activity Log
- Updated Activity Log module to use V3 wrappers
- Updated Dexie schema with account, site, and site_domain tables
- Added V3 tests to testing page
2026-01-06 12:55:50 -05:00
Scott Idem
f737713740 Saving notes 2026-01-05 19:40:56 -05:00
Scott Idem
d4134359b7 Finalize Journals V3 migration and implement soft delete testing.
- Added 'Test Journal Module Soft Deletes' to the testing page.
- Verified 'disable' and 'hide' methods for journal entries.
- Ensured all Journals CRUD operations utilize the standardized V3 wrappers.
- Documented successful end-to-end testing of the new API patterns.
2026-01-05 19:35:21 -05:00
Scott Idem
c6476cd767 Implement V3 PATCH/DELETE wrappers and migrate Journals module to full V3 CRUD.
- Added update_ae_obj_v3, update_nested_obj_v3, delete_ae_obj_v3, and delete_nested_ae_obj_v3.
- Refactored Journals and Journal Entries modules to utilize the new V3 API wrappers.
- Standardized data processing and IDB caching for all CRUD operations in Journals.
- Updated testing page with comprehensive V3 CUD test buttons.
2026-01-05 19:30:12 -05:00
Scott Idem
d066da9047 The ability to use V3 Create Nested works. 2026-01-05 19:10:07 -05:00
Scott Idem
45f7393ee3 Now with a working V# GET ID and GET nested ID. 2026-01-05 18:52:11 -05:00
Scott Idem
bb6b3b7352 The sorting now works and some missing fields were added. 2026-01-05 18:29:41 -05:00
Scott Idem
17589596bc Improved sorting for badges. Waiting on an API fix to include the missing badge fields. I think only the minimum is being returned. We need things like the print_count, print_first_datetime, and other fields. 2026-01-05 18:02:09 -05:00
Scott Idem
cdca5d64be Work on cleaning up the order by for badges. 2026-01-05 17:00:00 -05:00
Scott Idem
e06b407567 Complete initial V3 CRUD migration for Journals and Badges, fix Journals New Entry button, and update TODO integration roadmap. 2026-01-02 21:07:27 -05:00
Scott Idem
7edbb8000a Migrate Journals and Badges modules to Aether API V3 CRUD endpoints and fix Svelte 5 / Dexie integration issues. 2026-01-02 20:38:04 -05:00
Scott Idem
60057814c7 Work on switching to the new CRUD V3. 2026-01-02 20:29:43 -05:00
Scott Idem
6eb601f56d Saving changes to the Journals and API CRUD V3 fixes. 2026-01-02 19:30:33 -05:00
Scott Idem
33d42ed85b Saving first versions to use the new CRUD V3 endpoints. 2026-01-02 18:27:12 -05:00
Scott Idem
2c7ed476af Adding new API CRUD v3 functionality. 2026-01-02 18:11:07 -05:00
Scott Idem
9111e14961 Fixing a bug when editing the linked hosted file for Archive Content. 2026-01-02 13:29:16 -05:00
Scott Idem
a8314a5da0 Wrapping up for the night. This can now copy the iframe src link. 2025-12-16 21:51:29 -05:00
Scott Idem
0b64d33a72 Saving Gemini notes. 2025-12-16 15:26:04 -05:00
Scott Idem
a9d1e30fc4 This seems like a good pause point. The new Jitsi reports page is functional. The meetings log various activities and events. 2025-12-16 15:07:27 -05:00
Scott Idem
ae49fa7b39 Activity logging is working well enough for now. We need to add a reports page for the video conferences next. 2025-12-16 13:55:20 -05:00
Scott Idem
f1645fe6f4 Snapshot before Gemini gets to work. 2025-12-16 13:09:45 -05:00
Scott Idem
710d6e10c0 Fixed the activity logs!!! The ae_api was not imported or used. The account_id was also referenced incorrectly. 2025-12-16 12:38:11 -05:00
Scott Idem
a7ad5ff7fa Trying to get the activity logs to work... Create! 2025-12-16 12:02:18 -05:00
Scott Idem
f403a3dd59 Saving the final changes for the night. 2025-12-15 19:07:06 -05:00
Scott Idem
0169cd5fcd The activity log is almost working. 2025-12-15 18:59:26 -05:00
Scott Idem
f529f4baac Starting to wrap up for the night. Gemini cleaned up the tools and settings section. 2025-12-15 18:30:57 -05:00
Scott Idem
a7bf03e449 Things are looking good. Now have a tools and setting pop up thing. 2025-12-15 18:07:41 -05:00
Scott Idem
e16a28cc29 Work on checking or guessing permissions based on Novi URL params. 2025-12-15 16:07:26 -05:00
Scott Idem
825f5c4829 Saving my work while we working on making the Jitsi integration better. 2025-12-15 15:27:34 -05:00
Scott Idem
44364186ed Cleaning things up a little. 2025-12-15 14:47:38 -05:00
Scott Idem
e09805ec50 Another snapshot 2025-12-15 14:11:49 -05:00
Scott Idem
43fb316fd4 Cleaning up the code for this page. Separate out the Novi calls. 2025-12-15 14:04:54 -05:00
Scott Idem
122965f083 Saving changes as we keep cleaning up the Novi specific code. 2025-12-15 13:25:39 -05:00
Scott Idem
368ed89173 Saving a snapshot before Gemini works on things. 2025-12-15 13:16:22 -05:00
Scott Idem
8b60a09021 Saving Gemini's notes 2025-12-12 17:24:51 -05:00
Scott Idem
1d99cc325f Things are in a pretty good working state. Ideally the Novi page should be a little more dynamic with changing the Jitsi settings. Or maybe the controls should be moved to the idaa/video_conferences page. Wrapping up for the day/week. 2025-12-12 17:17:11 -05:00
Scott Idem
fcc2811dc7 Saving a mostly working Jitsi update 2025-12-12 16:03:30 -05:00
Scott Idem
2a5b46883f Did some package updates, Svelte build changes (fewer warnings), work on IDAA and Jitsi service. 2025-12-12 14:57:10 -05:00
Scott Idem
1611ac0183 Removing old unused references to skeletonlabs. 2025-12-08 18:14:57 -05:00
Scott Idem
dc4364dd27 This file was not needed and was causing Svelte syntax highlighting issues. 2025-12-08 18:00:48 -05:00
Scott Idem
6c0f65fdf5 docs: Update GEMINI.md and TODO.md with UI library update status 2025-12-08 17:47:32 -05:00
Scott Idem
3388f11514 Just grabbed the Svelte updates from apx 2 minutes ago. Because why not. 2025-12-08 17:35:49 -05:00
Scott Idem
9d5c079935 Double checking things and letting Gemini make some notes. 2025-12-08 17:29:59 -05:00
Scott Idem
098fc94a5a GOOD: This is the most up to date version that seems to work after all the AI updates and changes and the package upgrades. This was a lot!!! This needs more testing... A *lot* was changed over the last 2 weeks. An oldish version from 2 weeks ago that I know works is commit b3c04464. I also found a commit (1fc58eb1) from 3 days ago that mostly works as well. 2025-12-08 17:20:34 -05:00
Scott Idem
4cf9f75259 This is just a quick extra snapshot.... I think things are still working here. 2025-12-08 15:48:59 -05:00
Scott Idem
90ee6bb156 All packages have now been updated. This includes the skeletonlabs packages. Everything seems to be working. (Except for some unrelated known bugs. Why did the badge search stop working (AGAIN)?) 2025-12-05 17:00:54 -05:00
Scott Idem
83cd8d851d The tailwind-variants is now updated to 3.x and seems to be working....The last things are the skeletonlabs. They keep breaking things.... 2025-12-05 16:33:44 -05:00
Scott Idem
50936f56d7 Vite updated to 7.x...!? Seems to be working still. 2025-12-05 16:30:26 -05:00
Scott Idem
fd73ad2e0f More package updates 2025-12-05 16:28:40 -05:00
Scott Idem
d447de4073 Another another snapshot... 2025-12-05 16:19:50 -05:00
Scott Idem
9d95b5eac9 Another snapshot. 2025-12-05 16:18:18 -05:00
Scott Idem
3a544a2086 Taking a snapshot while I update packages in batches.... At least things are working again. 2025-12-05 16:04:05 -05:00
Scott Idem
1fc58eb18f Seems to be working still... Trying to update packages. 2025-12-05 14:50:06 -05:00
Scott Idem
b3bad26d8f Things seems to be working. It pulls the example moderator group list from Novi using their API. The moderator check is working. It still needs to handle more than one Novi Group. 2025-12-05 12:56:08 -05:00
Scott Idem
3f0ff46f51 First version of moving the Jitsi code to a new IDAA module under video_conferences. 2025-12-05 12:15:24 -05:00
Scott Idem
2403b41529 More work on Jitsi for IDAA 2025-12-04 19:57:35 -05:00
Scott Idem
484ea8c36c The Jitsi meetings and most settings are working well enough for IDAA members to use for now. 2025-12-04 15:37:04 -05:00
Scott Idem
c1a440df6c Quick save of changes for IDAA 2025-12-04 11:24:39 -05:00
Scott Idem
0fcd4e4cdf Work on Jitsi settings 2025-12-02 19:07:55 -05:00
Scott Idem
b91d79c175 fix: Resolve props_invalid_value error and add each block keys\n\n- Initialized .files.upload_complete in src/routes/hosted_files/video_util/+page.svelte to prevent props_invalid_value error.\n- Removed commented-out, unused each blocks from several components.\n- Added each keys to improve reactivity and resolve svelte/require-each-key errors in several components. 2025-11-20 20:21:06 -05:00
Scott Idem
a68d5439bd feat: Remove legacy files and fix first svelte/no-at-html-tags error\n\n- Moved legacy files from src/routes/legacy to backups/legacy/src/routes/legacy.\n- Removed the empty src/routes/legacy directory.\n- Fixed a svelte/no-at-html-tags error in src/routes/idaa/(idaa)/archives/[archive_id]/+page.svelte by replacing '{@html ?.name ?? 'Archive'}' with '{?.name ?? 'Archive'}'.\n- Addressed a misidentified '{@html}' tag in src/lib/ae_core/ae_comp__hosted_files_clip_video.svelte by removing commented-out code that might have caused false positives. 2025-11-20 19:46:17 -05:00
Scott Idem
266363b85f feat(event_badge): Improve event_id handling in badge data processing
Refactor how  is ensured in badge objects when fetched from the API and cached locally.

Previously,  directly injected  into results, creating an inconsistency with other data fetching functions. This change centralizes the  injection logic within .

- Updated  to accept an optional  parameter.
- Added logic within the processor to set  and  on badge objects if they are missing, using the provided  context.
- Modified call sites (, , ) to pass the  to the  function.

This ensures consistent and robust handling of the  across badge-related data operations, aligning with the project's data processing patterns.
2025-11-20 17:44:21 -05:00
Scott Idem
359d5d47f9 Working on event_id and event_id_random are both showing as undefined. badge_id and badge_id_random work in this file though. 2025-11-20 17:11:02 -05:00
Scott Idem
a1a33f794d More fixes related to id_random vs id. 2025-11-20 16:56:13 -05:00
Scott Idem
f164caf65f Another id_random bug fix. 2025-11-20 16:40:44 -05:00
Scott Idem
0da470e7dc More bug fixes related to incorrect paths. I am sure there are mote around. 2025-11-20 16:31:01 -05:00
Scott Idem
ce5a3535cc Restoring old files just in case. Style fix. 2025-11-20 16:13:20 -05:00
Scott Idem
ee9d9a0aca Trying to commit these changes so they are not lost. This is right after Gemini tried to move the Pres Mgmt files into a new sub directory. Everything seems to be working but I am sure something was missed. 2025-11-20 16:13:20 -05:00
Scott Idem
8223b964e1 Quickly saving lots of changes before they might be lost. 2025-11-20 16:13:20 -05:00
Scott Idem
064942c647 docs: Reorganize documentation files
- Centralize project-wide documentation into a new /documentation directory.
- Remove old, deprecated README guideline files.
- Create a comprehensive AETHER_API_OBJECTS.md file detailing all API data models.
2025-11-20 14:26:30 -05:00
Scott Idem
65b793f663 Fixed bug where trusted_access value changes were not working. 2025-11-20 12:43:30 -05:00
Scott Idem
d24e6c0400 docs: Update README.md with AE Events - Exhibit Leads module 2025-11-19 23:02:44 -05:00
Scott Idem
4b1db3afff docs: Add and update READMEs for new v3 event modules
- Added new README.md for the v3 Event Leads module at `src/routes/events/[event_id]/(leads)/README.md`, adapting content from its legacy counterpart.
- Created placeholder README.md files for the main v3 Events module (`src/routes/events/[event_id]/README.md`), the v3 Launcher module (`src/routes/events/[event_id]/(launcher)/README.md`), and the v3 Badges module (`src/routes/events/[event_id]/(badges)/README.md`).
2025-11-19 22:47:28 -05:00
Scott Idem
d1021e2822 feat: Refactor event modules for Svelte 5 and new routing structure
- Moved legacy event modules (`events_badges`, `events_leads`) to `src/routes/legacy/` and renamed them to `events_badges_v2` and `events_leads_v2` respectively.
- Created new directory structures for Svelte 5 and SvelteKit-based event modules under `src/routes/events/[event_id]/`:
    - `(launcher)`
    - `(badges)`
    - `(leads)`
- Updated `src/routes/events_leads/README.md` (now `src/routes/legacy/events_leads_v2/README.md`) with comprehensive documentation for the legacy lead retrieval module, reflecting its features, data model, routing, components, and technical implementation details, incorporating project conventions.
2025-11-19 22:45:50 -05:00
Scott Idem
bd4bd360f7 docs: Update project documentation and session notes
- Updates `GEMINI.md` with a detailed summary of the days debugging session for the v3 Badges page.
- Updates `TODO.md` to mark recent bug fixes as complete and adds new tasks for outstanding issues (e.g., `order_by_li` fix).
- Updates `ARCHITECTURE.md` to reflect the current state of the tech stack (Tailwind v4, component library status).
- Creates `README.md` files for the new v3 Badges module and the legacy Lead Retrieval module to clarify their purpose and status.
2025-11-19 19:05:06 -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
b3c0446440 fix(badges): Resolve badge search API error and UI bugs
This commit addresses several critical issues preventing the new v3 badge search page from functioning correctly.

- **Fixes API Fetch Error:** Resolves a `TypeError: NetworkError` that occurred during badge searches. The error was caused by an incorrect `order_by_li` parameter being sent to the API. The parameter is now temporarily commented out in `ae_events__event_badge.ts` to allow searches to succeed.

- **Fixes Svelte 5 Binding Error:** Implements a defensive initialization for badge search filter properties directly in the `(badges)/badges/+page.svelte` script. This prevents a `props_invalid_value` runtime error by ensuring that bound store values are not `undefined` when the child search component is rendered.

- **Fixes Invalid Attribute Name Error:** Corrects the `onsubmit|preventDefault` syntax in the new badge creation and upload forms. The `preventDefault` logic is now handled inside the respective submit handler functions.

- **Restores Site-wide Styles:** Re-adds the global Skeleton UI CSS imports to `app.css` to fix numerous "unknown utility class" errors (e.g., `preset-tonal-*`) and restores button styles in the legacy leads list.

- **Refactors Badge Search Component:** Updates the `ae_comp__badge_search.svelte` component to use its own reactive props for state management instead of relying on global stores, and removes obsolete/conflicting logic.
2025-11-19 18:54:46 -05:00
Scott Idem
79da9acd2f fix(badges): Resolve build errors and restore site styles
This commit addresses several critical issues that were preventing the application from building and rendering correctly.

- **Restores Skeleton UI CSS:** Re-adds the global Skeleton UI CSS imports to `app.css`. This fixes numerous "unknown utility class" errors (e.g., `preset-tonal-secondary`) across the site, allowing components to render with their intended styles again.

- **Fixes Invalid Attribute Name Error:** Corrects the `onsubmit|preventDefault` syntax in the new badge creation and upload forms (`ae_comp__badge_create_form.svelte`, `ae_comp__badge_upload_form.svelte`). The `preventDefault` logic is now handled inside the respective submit handler functions, resolving the Svelte v5 parsing error.

- **Fixes Svelte 5 Binding Error:** Implements a defensive initialization for badge search filter properties directly in the `(badges)/badges/+page.svelte` script. This prevents a `props_invalid_value` runtime error by ensuring that bound store values are not `undefined` when the child search component is rendered.

- **Fixes Invalid CSS Classes:** Replaces incorrect `preset-tonal-*` classes in the legacy `leads_list.svelte` component with standard Tailwind CSS utility classes to prevent further styling-related errors.
2025-11-19 18:21:40 -05:00
Scott Idem
8029034e37 style: Apply Prettier formatting to codebase 2025-11-19 13:38:45 -05:00
Scott Idem
8f49edc1b3 Various fixes. One being yet another id_random issue. 2025-11-19 13:34:15 -05:00
Scott Idem
b16413c3c0 fix(svelte): resolve props_invalid_value and dexie DataError
- Initialize ds_loaded properties in ae_stores.ts to fix svelte binding error.
- Make db_save_ae_obj_li__ae_obj in core__idb_dexie.ts schema-aware to fix Dexie DataError when saving to tables with non-'id' primary keys.
2025-11-19 13:29:47 -05:00
Scott Idem
f25b9ccd8f feat: migration to Svelte 5 2025-11-19 12:38:03 -05:00
Scott Idem
d99e9ee1b0 refactor(events): Centralize editable fields for event objects
- Implemented a whitelist for editable fields for the 'event' object type to prevent sending read-only fields in POST/PATCH requests.
- Created a new file  to define the editable fields.
- Modified  and  to use this whitelist.
- Removed the temporary cleaning logic from the event settings page.
- Corrected Svelte 5  to  in event settings components.
- Updated Dexie interfaces for badge, badge_template, and device to use string IDs.
2025-11-19 12:12:29 -05:00
Scott Idem
2e70ce312b docs: Update GEMINI.md with summary of event settings page
Added a summary of the new event settings page to GEMINI.md, documenting the form-based UI, collapsible sections, view toggles, and Svelte 5 reactivity improvements.
2025-11-18 20:13:32 -05:00
Scott Idem
9691ceca43 feat: Add form-based UIs and view toggles for event settings
This commit introduces form-based UI components for 'mod_badges_json' and 'mod_abstracts_json', and expands the form for 'mod_pres_mgmt_json'. It also adds view toggles to switch between form and raw JSON editing for each of these sections, providing greater flexibility for the user.
2025-11-18 20:10:01 -05:00
Scott Idem
c06f50c21e feat: Add basic event fields and collapsible sections to settings page
This commit introduces a new component for editing basic event fields and adds collapsible sections to the event settings page for better organization. The new fields include code, conference, name, summary, description, timezone, start/end datetimes, and notes.
2025-11-18 19:42:15 -05:00
Scott Idem
1d0620085f docs: Update GEMINI.md with Svelte 5 and ID guidelines
Added a new 'Development Guidelines' section to GEMINI.md to provide clear instructions on Svelte v5 conventions and the project's 'id' vs 'id_random' usage. This will serve as a reminder to avoid common pitfalls and ensure code consistency.
2025-11-18 19:32:14 -05:00
Scott Idem
74140f41db fix: Update event settings forms for Svelte 5 reactivity
Corrected event settings components to use Svelte 5 bindable props for two-way data binding. This ensures that changes in child form components (ae_comp__event_settings_form.svelte, ae_comp__event_settings_pres_mgmt_form.svelte) are reactively reflected in the parent page (settings/+page.svelte) and properly handled during save operations.
2025-11-18 19:23:33 -05:00
Scott Idem
f7d1f304fe feat: Add form-based UI for event settings
This commit introduces form-based UI components for editing the 'cfg_json' and 'mod_pres_mgmt_json' fields on the event settings page. This provides a more user-friendly experience than editing the raw JSON directly. It also fixes an issue where the JSON objects were being displayed as '[object Object]'.
2025-11-18 19:17:27 -05:00
Scott Idem
0be878c8c1 feat: Add basic event settings management page
This commit introduces a new page at /events/[event_id]/settings that allows users to view and edit the JSON configuration fields for an event. The page provides textareas for each config field and a 'Save' button to persist the changes.
2025-11-18 18:59:49 -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
6d1f9989d0 config: Configure Prettier to use 4 spaces for indentation
Updated .prettierrc to set 'useTabs' to false and 'tabWidth' to 4, ensuring that Prettier formats code with 4-space indentation instead of tabs, as per user preference.
2025-11-18 18:39:35 -05:00
Scott Idem
6aa97a9f6b This is now actually fixed. Gemini did not fix the links correctly. 2025-11-18 18:34:59 -05:00
Scott Idem
9ea6b17617 feat: Fix event list and add module links
This commit fixes the event list on the /events page by correcting the data fetching logic. It also adds links to the 'Pres Mgmt', 'Badges', 'Leads', and 'Launcher' modules for each event, and updates the main event link to use the random string ID.
2025-11-18 18:18:25 -05:00
Scott Idem
c8c19a35a6 refactor: Use random IDs as primary keys in IDB
This commit refactors the IndexedDB schema to use the string-based random IDs as primary keys instead of the numeric auto-incrementing IDs. This change affects the 'badge' table and all related components that interact with it. The badge detail page and list component have been updated to use the new primary key for more efficient and consistent data retrieval.
2025-11-18 17:49:16 -05:00
Scott Idem
97d41adb12 fix: Use random IDs for badge links and detail page
This commit updates the badge list to use the string-based random IDs for badge links. It also updates the badge detail page to correctly fetch the badge data using the random ID from the URL.
2025-11-18 17:37:48 -05:00
Scott Idem
69c34fa4bc fix: Resolve badge search reactivity and SSR issues
This commit fixes several issues with the new badge search functionality. It resolves a 500 Internal Error by moving the Dexie liveQuery into an onMount block to prevent server-side execution. It also fixes a Svelte 5 reactivity issue by using a more explicit subscription pattern for the liveQuery and by correctly accessing state variables in the template. The badge search results are now correctly displayed.
2025-11-18 17:30:57 -05:00
Scott Idem
d678f97324 style: Apply Prettier formatting
Applied consistent code formatting across the project using Prettier, addressing minor style inconsistencies introduced in recent changes.
2025-11-18 14:25:21 -05:00
Scott Idem
691b20fd54 refactor: Rename CodeMirror wrapper and fix editor buttons
Renamed Tiptap_editor to CodeMirror_wrapper and updated all usages. Renamed the wrapper file to element_codemirror_editor_wrapper.svelte. Fixed a TypeError in the CodeMirror editor buttons by using EditorSelection.range() to correctly create selection ranges.
2025-11-18 14:14:24 -05:00
Scott Idem
95412dd0ad feat: CodeMirror integration and bug fixes
This commit addresses several issues related to the migration from TipTap to CodeMirror:

- **CodeMirror Initialization Fixes:**
  - Resolved 'Unrecognized extension value' errors by refactoring  to explicitly import individual CodeMirror extensions instead of relying on . This ensures proper singleton usage and prevents module duplication issues.
  - Updated  and  to utilize these individual extensions.

- **Text Wrapping Enabled:**
  - Added  to the extensions in  and  to enable text wrapping in the CodeMirror editors.

- **Content Saving Fixes:**
  - Corrected content binding for CodeMirror editor instances in various IDAA components:
    -  (description, location_text, attend_text)
    -  (content, notes)
    -  (content)
    -  (description, notes)
    -  (description, notes)
  - Ensured that the  prop of  is correctly bound to the respective state variables in the parent components, and these state variables are initialized with existing content.

- **Save Button Enablement:**
  - Fixed an issue in  where the Save button was not enabling on content changes. The  logic now directly compares the  and  with the original object's content, ensuring reactivity.
2025-11-18 13:27:42 -05:00
Scott Idem
e521bea448 Taking some notes since I can't sleep. 2025-11-18 03:44:36 -05:00
Scott Idem
d627e59bf6 Updated packages and fixed CodeMirror import error. 2025-11-17 22:30:32 -05:00
Scott Idem
390bbcb6e8 DOCS: Update GEMINI.md with Tiptap to CodeMirror migration details 2025-11-17 21:26:51 -05:00
Scott Idem
4f262149cd FEAT: Replace Tiptap editor with CodeMirror
Replaced the Tiptap-based rich text editor with CodeMirror for basic markdown formatting.

- Removed  directory.
- Removed all  and  dependencies from .
- Renamed  to .
- Updated  to use  and removed Tiptap-specific logic.
- Updated all Svelte components that were importing the old Tiptap wrapper to import the new CodeMirror wrapper and removed unsupported props (, , , ).
- Ran
up to date, audited 492 packages in 1s

92 packages are looking for funding
  run `npm fund` for details

8 low severity vulnerabilities

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details. and
> osit-aether-app-svelte@3.9.6 format
> prettier --write .

.eslintrc.cjs 18ms (unchanged)
.prettierrc 4ms (unchanged)
.vscode/settings.json 2ms (unchanged)
ae_app_svelte_tailwind_skeleton.code-workspace 1ms (unchanged)
ARCHITECTURE.md 22ms (unchanged)
components.json 1ms (unchanged)
COMPONENTS.md 9ms (unchanged)
DATA_STRUCTURES.md 7ms (unchanged)
eslint.config.js 4ms (unchanged)
GEMINI.md 5ms (unchanged)
jsconfig.json 1ms (unchanged)
NAMING_CONVENTIONS.md 8ms (unchanged)
OLD_README_guidelines_ui_ux.md 6ms (unchanged)
OLD_README_guidelines_v1.md 8ms (unchanged)
OLD_README_guidelines_v2.md 26ms (unchanged)
package.json 1ms (unchanged)
playwright.config.ts 3ms (unchanged)
README.md 6ms (unchanged)
src/ae-c-idaa-light.css 14ms (unchanged)
src/ae-c-lci.css 9ms (unchanged)
src/ae-osit-default.css 9ms (unchanged)
src/aeclci_v1.css 7ms (unchanged)
src/app.css 16ms (unchanged)
src/app.d.ts 1ms (unchanged)
src/app.html 11ms (unchanged)
src/index.test.ts 1ms (unchanged)
src/lib/ae_api/api_delete_object.ts 10ms (unchanged)
src/lib/ae_api/api_get__crud_obj_id.ts 9ms (unchanged)
src/lib/ae_api/api_get__crud_obj_li_v1.ts 7ms (unchanged)
src/lib/ae_api/api_get__crud_obj_li_v2.ts 6ms (unchanged)
src/lib/ae_api/api_get_object_v1.ts 19ms (unchanged)
src/lib/ae_api/api_get_object.ts 9ms (unchanged)
src/lib/ae_api/api_patch_object.ts 5ms (unchanged)
src/lib/ae_api/api_post_object.ts 10ms (unchanged)
src/lib/ae_archives/ae_archives__archive_content.ts 12ms (unchanged)
src/lib/ae_archives/ae_archives__archive.ts 14ms (unchanged)
src/lib/ae_archives/ae_archives_functions.ts 1ms (unchanged)
src/lib/ae_archives/db_archives.ts 4ms (unchanged)
src/lib/ae_archives/README.md 2ms (unchanged)
src/lib/ae_core/ae_comp__hosted_files_clip_video_li.svelte 33ms (unchanged)
src/lib/ae_core/ae_comp__hosted_files_clip_video_v1.svelte 25ms (unchanged)
src/lib/ae_core/ae_comp__hosted_files_clip_video.svelte 21ms (unchanged)
src/lib/ae_core/ae_comp__hosted_files_download_button.svelte 10ms (unchanged)
src/lib/ae_core/ae_comp__hosted_files_upload.svelte 13ms (unchanged)
src/lib/ae_core/ae_core_functions.ts 10ms (unchanged)
src/lib/ae_core/core__account.ts 1ms (unchanged)
src/lib/ae_core/core__activity_log.ts 7ms (unchanged)
src/lib/ae_core/core__api_helpers.ts 2ms (unchanged)
src/lib/ae_core/core__check_hosted_file_obj_w_hash.ts 1ms (unchanged)
src/lib/ae_core/core__countries.ts 2ms (unchanged)
src/lib/ae_core/core__country_subdivisions.ts 2ms (unchanged)
src/lib/ae_core/core__crud_generic.ts 6ms (unchanged)
src/lib/ae_core/core__data_store.ts 2ms (unchanged)
src/lib/ae_core/core__hosted_files.ts 6ms (unchanged)
src/lib/ae_core/core__idb_dexie.ts 4ms (unchanged)
src/lib/ae_core/core__person.ts 16ms (unchanged)
src/lib/ae_core/core__qr_code.ts 6ms (unchanged)
src/lib/ae_core/core__site_domain.ts 2ms (unchanged)
src/lib/ae_core/core__site.ts 1ms (unchanged)
src/lib/ae_core/core__time_zones.ts 2ms (unchanged)
src/lib/ae_core/core__user.ts 6ms (unchanged)
src/lib/ae_core/db_core.ts 3ms (unchanged)
src/lib/ae_events_functions.ts 2ms (unchanged)
src/lib/ae_events/ae_events__event_badge_template.ts 9ms (unchanged)
src/lib/ae_events/ae_events__event_badge.ts 13ms (unchanged)
src/lib/ae_events/ae_events__event_device.ts 16ms (unchanged)
src/lib/ae_events/ae_events__event_file.ts 14ms (unchanged)
src/lib/ae_events/ae_events__event_location.ts 12ms (unchanged)
src/lib/ae_events/ae_events__event_presentation.ts 10ms (unchanged)
src/lib/ae_events/ae_events__event_presenter.ts 11ms (unchanged)
src/lib/ae_events/ae_events__event_session.ts 18ms (unchanged)
src/lib/ae_events/ae_events__event.ts 17ms (unchanged)
src/lib/ae_events/ae_events__exhibit.ts 10ms (unchanged)
src/lib/ae_events/db_events.ts 10ms (unchanged)
src/lib/ae_journals/ae_journals__journal_entry.ts 13ms (unchanged)
src/lib/ae_journals/ae_journals__journal.ts 15ms (unchanged)
src/lib/ae_journals/ae_journals_functions.ts 1ms (unchanged)
src/lib/ae_journals/ae_journals_stores.ts 3ms (unchanged)
src/lib/ae_journals/db_journals.ts 6ms (unchanged)
src/lib/ae_posts/ae_posts__post_comment.ts 8ms (unchanged)
src/lib/ae_posts/ae_posts__post.ts 12ms (unchanged)
src/lib/ae_posts/ae_posts_functions.ts 1ms (unchanged)
src/lib/ae_posts/db_posts.ts 2ms (unchanged)
src/lib/ae_posts/README.md 2ms (unchanged)
src/lib/ae_sponsorships/ae_sponsorships_functions.ts 7ms (unchanged)
src/lib/ae_sponsorships/db_sponsorships.ts 2ms (unchanged)
src/lib/ae_sponsorships/README.md 2ms (unchanged)
src/lib/ae_utils/ae_utils__crypto.ts 5ms (unchanged)
src/lib/ae_utils/ae_utils__datetime_format.ts 3ms (unchanged)
src/lib/ae_utils/ae_utils__extract_prefixed_form_data.ts 3ms (unchanged)
src/lib/ae_utils/ae_utils__file_extension_icon.ts 1ms (unchanged)
src/lib/ae_utils/ae_utils__files.ts 2ms (unchanged)
src/lib/ae_utils/ae_utils__get_obj_li_w_match_prop.ts 1ms (unchanged)
src/lib/ae_utils/ae_utils__is_datetime_recent.ts 1ms (unchanged)
src/lib/ae_utils/ae_utils__perm_checks.ts 3ms (unchanged)
src/lib/ae_utils/ae_utils__process_data_string.ts 2ms (unchanged)
src/lib/ae_utils/ae_utils__return_obj_type_path.ts 2ms (unchanged)
src/lib/ae_utils/ae_utils__set_obj_prop_display_name.ts 2ms (unchanged)
src/lib/ae_utils/ae_utils__to_title_case.ts 2ms (unchanged)
src/lib/ae_utils/ae_utils.ts 5ms (unchanged)
src/lib/api/api.ts 14ms (unchanged)
src/lib/app_components/analytics.svelte 3ms (unchanged)
src/lib/app_components/e_app_access_type.svelte 26ms (unchanged)
src/lib/app_components/e_app_cfg.svelte 12ms (unchanged)
src/lib/app_components/e_app_clipboard.svelte 6ms (unchanged)
src/lib/app_components/e_app_codemirror_v5.svelte 8ms (unchanged)
src/lib/app_components/e_app_debug_menu.svelte 7ms (unchanged)
src/lib/app_components/e_app_help_tech.svelte 26ms (unchanged)
src/lib/app_components/e_app_sign_in_out.svelte 32ms (unchanged)
src/lib/app_components/e_app_sys_menu.svelte 25ms (unchanged)
src/lib/app_components/e_app_theme.svelte 9ms (unchanged)
src/lib/components/ui/button/button.svelte 6ms (unchanged)
src/lib/components/ui/button/index.js 1ms (unchanged)
src/lib/components/ui/button/index.ts 1ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte 5ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte 2ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte 2ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte 2ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte 2ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte 3ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte 2ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte 2ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte 2ms (unchanged)
src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte 2ms (unchanged)
src/lib/components/ui/dropdown-menu/index.js 1ms (unchanged)
src/lib/components/ui/dropdown-menu/index.ts 1ms (unchanged)
src/lib/components/ui/input/index.js 0ms (unchanged)
src/lib/components/ui/input/index.ts 0ms (unchanged)
src/lib/components/ui/input/input.svelte 2ms (unchanged)
src/lib/components/ui/popover/index.js 0ms (unchanged)
src/lib/components/ui/popover/index.ts 1ms (unchanged)
src/lib/components/ui/popover/popover-content.svelte 2ms (unchanged)
src/lib/components/ui/separator/index.js 0ms (unchanged)
src/lib/components/ui/separator/index.ts 1ms (unchanged)
src/lib/components/ui/separator/separator.svelte 2ms (unchanged)
src/lib/components/ui/tooltip/index.js 0ms (unchanged)
src/lib/components/ui/tooltip/index.ts 1ms (unchanged)
src/lib/components/ui/tooltip/tooltip-content.svelte 2ms (unchanged)
src/lib/electron/electron_native.js 33ms (unchanged)
src/lib/electron/electron_relay.js 6ms (unchanged)
src/lib/electron/README.md 4ms (unchanged)
src/lib/element_qr_scanner_v2.svelte 15ms (unchanged)
src/lib/elements/element_ae_crud_v2.svelte 22ms (unchanged)
src/lib/elements/element_ae_crud.svelte 20ms (unchanged)
src/lib/elements/element_codemirror_editor.svelte 4ms (unchanged)
src/lib/elements/element_codemirror_wrapper.svelte 1ms (unchanged)
src/lib/elements/element_data_store_v2.svelte 38ms (unchanged)
src/lib/elements/element_data_store.svelte 29ms (unchanged)
src/lib/elements/element_input_file.svelte 13ms (unchanged)
src/lib/elements/element_input_files_tbl.svelte 13ms (unchanged)
src/lib/elements/element_input_v2.svelte 59ms (unchanged)
src/lib/elements/element_manage_event_file_li_all.svelte 2ms (unchanged)
src/lib/elements/element_manage_event_file_li_direct.svelte 2ms (unchanged)
src/lib/elements/element_manage_event_file_li.svelte 45ms (unchanged)
src/lib/elements/element_manage_hosted_file_li_all.svelte 8ms (unchanged)
src/lib/elements/element_manage_hosted_file_li.svelte 15ms (unchanged)
src/lib/elements/element_obj_tbl_row.svelte 11ms (unchanged)
src/lib/elements/element_sql_qry.svelte 6ms (unchanged)
src/lib/elements/element_tiptap_editor.scss 2ms (unchanged)
src/lib/elements/element_websocket_v2.svelte 16ms (unchanged)
src/lib/stores/ae_events_stores.ts 6ms (unchanged)
src/lib/stores/ae_idaa_stores.ts 2ms (unchanged)
src/lib/stores/ae_stores.ts 6ms (unchanged)
src/lib/utils/ae_string_snippets.ts 1ms (unchanged)
src/lib/utils/index.ts 0ms (unchanged)
src/lib/utils/utils.ts 1ms (unchanged)
src/parent_iframe.html 5ms (unchanged)
src/routes/+layout.svelte 31ms (unchanged)
src/routes/+layout.ts 5ms (unchanged)
src/routes/+page.svelte 7ms (unchanged)
src/routes/admin/+layout.svelte 6ms (unchanged)
src/routes/admin/+page.svelte 8ms (unchanged)
src/routes/core/+layout.svelte 3ms (unchanged)
src/routes/core/+page.svelte 15ms (unchanged)
src/routes/core/+page.ts 0ms (unchanged)
src/routes/core/ae_comp__person_obj_tbl.svelte 9ms (unchanged)
src/routes/core/not_used+layout.ts 1ms (unchanged)
src/routes/core/person_view.svelte 55ms (unchanged)
src/routes/core/person/[person_id]/+page.svelte 13ms (unchanged)
src/routes/core/person/[person_id]/+page.ts 2ms (unchanged)
src/routes/core/README.md 2ms (unchanged)
src/routes/events_badges/+layout.svelte 7ms (unchanged)
src/routes/events_badges/+layout.ts 1ms (unchanged)
src/routes/events_badges/+page.svelte 2ms (unchanged)
src/routes/events_badges/+page.ts 0ms (unchanged)
src/routes/events_badges/README.md 3ms (unchanged)
src/routes/events_badges/review/+layout.ts 1ms (unchanged)
src/routes/events_badges/review/+page.svelte 35ms (unchanged)
src/routes/events_badges/stats/+layout.ts 1ms (unchanged)
src/routes/events_badges/stats/+page.svelte 33ms (unchanged)
src/routes/events_leads/+layout.svelte 2ms (unchanged)
src/routes/events_leads/+layout.ts 1ms (unchanged)
src/routes/events_leads/+page.svelte 8ms (unchanged)
src/routes/events_leads/+page.ts 1ms (unchanged)
src/routes/events_leads/exhibit/[slug]/+page.svelte 36ms (unchanged)
src/routes/events_leads/exhibit/[slug]/+page.ts 2ms (unchanged)
src/routes/events_leads/exhibit/[slug]/leads_add_scan.svelte 37ms (unchanged)
src/routes/events_leads/exhibit/[slug]/leads_list.svelte 25ms (unchanged)
src/routes/events_leads/exhibit/[slug]/leads_manage.svelte 40ms (unchanged)
src/routes/events_leads/exhibit/[slug]/leads_payment.svelte 10ms (unchanged)
src/routes/events_leads/exhibit/[slug]/leads_view_lead.svelte 42ms (unchanged)
src/routes/events_leads/README.md 4ms (unchanged)
src/routes/events/[event_id]/(badges)/badges/[badge_id]/+page.svelte 5ms (unchanged)
src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_obj_view.svelte 41ms (unchanged)
src/routes/events/[event_id]/(badges)/badges/+layout.svelte 3ms (unchanged)
src/routes/events/[event_id]/(badges)/badges/+page.svelte 5ms (unchanged)
src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_obj_li.svelte 9ms (unchanged)
src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_search.svelte 15ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher_cfg.svelte 26ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher_file_cont.svelte 32ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher_menu.svelte 12ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher_presenter_view_posters.svelte 6ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher_presenter_view.svelte 6ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher_session_view.svelte 17ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher/[event_location_id]/+page.svelte 4ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher/[event_location_id]/+page.ts 2ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte 45ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher/+layout.ts 2ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher/+page.svelte 0ms (unchanged)
src/routes/events/[event_id]/(launcher)/launcher/+page.ts 0ms (unchanged)
src/routes/events/[event_id]/(launcher)/menu_location_list.svelte 6ms (unchanged)
src/routes/events/[event_id]/(launcher)/menu_session_list.svelte 10ms (unchanged)
src/routes/events/[event_id]/+layout.svelte 2ms (unchanged)
src/routes/events/[event_id]/+layout.ts 1ms (unchanged)
src/routes/events/[event_id]/+page.svelte 20ms (unchanged)
src/routes/events/[event_id]/+page.ts 1ms (unchanged)
src/routes/events/[event_id]/device/ae_comp__event_device_obj_li_wrapper.svelte 2ms (unchanged)
src/routes/events/[event_id]/device/ae_comp__event_device_obj_li.svelte 28ms (unchanged)
src/routes/events/[event_id]/event_page_menu.svelte 37ms (unchanged)
src/routes/events/[event_id]/location/[event_location_id]/+page.svelte 11ms (unchanged)
src/routes/events/[event_id]/location/[event_location_id]/+page.ts 2ms (unchanged)
src/routes/events/[event_id]/location/[event_location_id]/location_page_menu.svelte 26ms (unchanged)
src/routes/events/[event_id]/location/[event_location_id]/location_view.svelte 27ms (unchanged)
src/routes/events/[event_id]/locations/+page.svelte 5ms (unchanged)
src/routes/events/[event_id]/locations/ae_comp__event_location_obj_li.svelte 18ms (unchanged)
src/routes/events/[event_id]/locations/locations_page_menu.svelte 11ms (unchanged)
src/routes/events/[event_id]/presenter/[presenter_id]/+page.svelte 17ms (unchanged)
src/routes/events/[event_id]/presenter/[presenter_id]/+page.ts 1ms (unchanged)
src/routes/events/[event_id]/presenter/[presenter_id]/ae_comp__event_presenter_form_agree.svelte 13ms (unchanged)
src/routes/events/[event_id]/presenter/[presenter_id]/presenter_page_menu.svelte 30ms (unchanged)
src/routes/events/[event_id]/presenter/[presenter_id]/presenter_view.svelte 108ms (unchanged)
src/routes/events/[event_id]/presenter/ae_comp__event_presenter_obj_li_wrapper.svelte 3ms (unchanged)
src/routes/events/[event_id]/presenter/ae_comp__event_presenter_obj_li.svelte 22ms (unchanged)
src/routes/events/[event_id]/presenter/ae_comp__event_presenter_obj_tbl_wrapper.svelte 4ms (unchanged)
src/routes/events/[event_id]/presenter/ae_comp__event_presenter_obj_tbl.svelte 15ms (unchanged)
src/routes/events/[event_id]/reports/+page.svelte 66ms (unchanged)
src/routes/events/[event_id]/reports/event_reports_page_menu.svelte 16ms (unchanged)
src/routes/events/[event_id]/reports/reports_files.svelte 14ms (unchanged)
src/routes/events/[event_id]/reports/reports_presenters.svelte 9ms (unchanged)
src/routes/events/[event_id]/reports/reports_sessions.svelte 10ms (unchanged)
src/routes/events/[event_id]/session/[session_id]/+page.svelte 16ms (unchanged)
src/routes/events/[event_id]/session/[session_id]/+page.ts 2ms (unchanged)
src/routes/events/[event_id]/session/[session_id]/ae_comp__event_session_poc_form_agree.svelte 17ms (unchanged)
src/routes/events/[event_id]/session/[session_id]/ae_comp__event_session_poc_profile.svelte 9ms (unchanged)
src/routes/events/[event_id]/session/[session_id]/session_page_menu.svelte 30ms (unchanged)
src/routes/events/[event_id]/session/[session_id]/session_view.svelte 59ms (unchanged)
src/routes/events/[event_id]/session/ae_comp__event_session_alert.svelte 9ms (unchanged)
src/routes/events/[event_id]/sign_in_out.svelte 7ms (unchanged)
src/routes/events/+layout.svelte 11ms (unchanged)
src/routes/events/+layout.ts 1ms (unchanged)
src/routes/events/+page.svelte 7ms (unchanged)
src/routes/events/+page.ts 1ms (unchanged)
src/routes/events/ae_comp__event_file_obj_tbl_wrapper.svelte 3ms (unchanged)
src/routes/events/ae_comp__event_file_obj_tbl.svelte 31ms (unchanged)
src/routes/events/ae_comp__event_files_upload.svelte 10ms (unchanged)
src/routes/events/ae_comp__event_presentation_obj_li.svelte 27ms (unchanged)
src/routes/events/ae_comp__event_session_obj_li_wrapper.svelte 2ms (unchanged)
src/routes/events/ae_comp__event_session_obj_li.svelte 31ms (unchanged)
src/routes/events/ae_comp__event_session_obj_tbl_wrapper.svelte 3ms (unchanged)
src/routes/events/ae_comp__event_session_obj_tbl.svelte 5ms (unchanged)
src/routes/events/ae_comp__events_menu_nav.svelte 5ms (unchanged)
src/routes/events/ae_comp__events_menu_opts.svelte 25ms (unchanged)
src/routes/events/README.md 3ms (unchanged)
src/routes/hosted_files/+layout.svelte 2ms (unchanged)
src/routes/hosted_files/+layout.ts 1ms (unchanged)
src/routes/hosted_files/+page.svelte 3ms (unchanged)
src/routes/hosted_files/video_util/+page.svelte 8ms (unchanged)
src/routes/hosted_files/video_util/hold_video_util.svelte 7ms (unchanged)
src/routes/idaa/(idaa)/+layout.svelte 6ms (unchanged)
src/routes/idaa/(idaa)/archives/[archive_id]/+page.svelte 12ms (unchanged)
src/routes/idaa/(idaa)/archives/[archive_id]/+page.ts 2ms (unchanged)
src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_content_obj_id_edit.svelte 32ms (unchanged)
src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_content_obj_li.svelte 15ms (unchanged)
src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_obj_id_edit.svelte 19ms (unchanged)
src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_obj_id_view.svelte 10ms (unchanged)
src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__modal_media_player.svelte 4ms (unchanged)
src/routes/idaa/(idaa)/archives/[archive_id]/not_used+layout.ts 0ms (unchanged)
src/routes/idaa/(idaa)/archives/+layout.svelte 1ms (unchanged)
src/routes/idaa/(idaa)/archives/+layout.ts 1ms (unchanged)
src/routes/idaa/(idaa)/archives/+page.svelte 5ms (unchanged)
src/routes/idaa/(idaa)/archives/ae_idaa_comp__archive_obj_li.svelte 5ms (unchanged)
src/routes/idaa/(idaa)/archives/ae_idaa_comp__media_player.svelte 6ms (unchanged)
src/routes/idaa/(idaa)/bb/[post_id]/+page.svelte 7ms (unchanged)
src/routes/idaa/(idaa)/bb/[post_id]/+page.ts 1ms (unchanged)
src/routes/idaa/(idaa)/bb/+layout.svelte 2ms (unchanged)
src/routes/idaa/(idaa)/bb/+layout.ts 1ms (unchanged)
src/routes/idaa/(idaa)/bb/+page.svelte 7ms (unchanged)
src/routes/idaa/(idaa)/bb/+page.ts 2ms (unchanged)
src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_comment_obj_id_edit.svelte 26ms (unchanged)
src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_id_edit.svelte 38ms (unchanged)
src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_id_view.svelte 17ms (unchanged)
src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_obj_li.svelte 9ms (unchanged)
src/routes/idaa/(idaa)/bb/ae_idaa_comp__post_options.svelte 8ms (unchanged)
src/routes/idaa/(idaa)/hold_+page.svelte 0ms (unchanged)
src/routes/idaa/(idaa)/hold_app.pcss 7ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/[event_id]/+page.svelte 8ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/[event_id]/+page.ts 1ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/+layout.svelte 2ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/+layout.ts 1ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/+page.svelte 9ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_edit.svelte 80ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_view.svelte 34ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li_wrapper.svelte 3ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_li.svelte 24ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte 27ms (unchanged)
src/routes/idaa/(idaa)/recovery_meetings/not_used+page.ts 0ms (unchanged)
src/routes/idaa/+layout.svelte 9ms (unchanged)
src/routes/idaa/README.md 3ms (unchanged)
src/routes/journals/[journal_id]/+layout.svelte 12ms (unchanged)
src/routes/journals/[journal_id]/+layout.ts 2ms (unchanged)
src/routes/journals/[journal_id]/+page.svelte 8ms (unchanged)
src/routes/journals/[journal_id]/+page.ts 1ms (unchanged)
src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.svelte 6ms (unchanged)
src/routes/journals/[journal_id]/entry/[journal_entry_id]/+page.ts 1ms (unchanged)
src/routes/journals/+layout.svelte 14ms (unchanged)
src/routes/journals/+layout.ts 1ms (unchanged)
src/routes/journals/+page.svelte 8ms (unchanged)
src/routes/journals/+page.ts 1ms (unchanged)
src/routes/journals/ae_comp__journal_entry_obj_file_li.svelte 12ms (unchanged)
src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte 102ms (unchanged)
src/routes/journals/ae_comp__journal_entry_obj_li.svelte 34ms (unchanged)
src/routes/journals/ae_comp__journal_entry_obj_qry.svelte 7ms (unchanged)
src/routes/journals/ae_comp__journal_obj_id_edit.svelte 39ms (unchanged)
src/routes/journals/ae_comp__journal_obj_id_view.svelte 9ms (unchanged)
src/routes/journals/ae_comp__journal_obj_li.svelte 6ms (unchanged)
src/routes/journals/ae_comp__obj_core_props.svelte 12ms (unchanged)
src/routes/journals/modal_journals_config.svelte 12ms (unchanged)
src/routes/journals/README.md 2ms (unchanged)
src/routes/testing/+layout.ts 0ms (unchanged)
src/routes/testing/+page.svelte 4ms (unchanged)
static/idaa_novi_iframe_archives.html 4ms (unchanged)
static/idaa_novi_iframe_bulletin_board.html 3ms (unchanged)
static/idaa_novi_iframe_jitsi_meeting.html 3ms (unchanged)
static/idaa_novi_iframe_recovery_meetings.html 3ms (unchanged)
static/jitsi_iframe_api.html 4ms (unchanged)
static/manifest.json 1ms (unchanged)
SVELTE_DEXIE_GUIDE.md 8ms (unchanged)
svelte.config.js 1ms (unchanged)
test-results/.last-run.json 0ms (unchanged)
tests/example.test.ts 1ms (unchanged)
TODO.md 18ms (unchanged)
tsconfig.json 1ms (unchanged)
vite.config.ts 1ms (unchanged)
vitest.config.ts 0ms (unchanged) to clean up dependencies and fix formatting.
2025-11-17 21:24:57 -05:00
Scott Idem
5c67421d7e feat: Add README files for various modules and routes 2025-11-17 20:28:34 -05:00
Scott Idem
3a0d901a05 Fix: Resolve parsing error and svelte/no-at-html-tags linting issues
Resolved a critical parsing error in leads_view_lead.svelte due to incorrect Svelte class directive syntax.

Addressed multiple svelte/no-at-html-tags linting errors across the following files to mitigate potential XSS vulnerabilities and improve code safety:
- src/routes/events_leads/exhibit/[slug]/leads_add_scan.svelte
- src/routes/events_leads/exhibit/[slug]/leads_manage.svelte
- src/routes/events_leads/exhibit/[slug]/leads_view_lead.svelte

Replaced {@html} blocks with safer Svelte conditional rendering ({#if}) and direct interpolation ({value}) for static and dynamic content where appropriate. Removed commented-out {@html} tags that were still triggering linting errors.
2025-11-17 19:11:01 -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
b99e85f1db fix(events): Correct data fetching for session child objects
This commit fixes a bug where presentations, presenters, and files related to a session were not being displayed. The issues were caused by incorrect property names ( suffix) and improper use of Svelte 5 features after a recent refactoring.
2025-11-17 17:47:24 -05:00
Scott Idem
a3b37a5df4 refactor: Standardize data processing and update to Svelte 5 runes
This commit introduces a major refactoring of the data processing logic across multiple modules (events, archives, posts, sponsorships) to use a standardized pattern with . This improves consistency and maintainability.

Key changes:
- Replaced module-specific data processing with a generic helper.
- Removed deprecated  functions.
- Updated Svelte components to leverage the new Svelte 5 runes, simplifying state management.
- Fixed linting errors and updated test configurations.
- Added .
2025-11-17 16:38:54 -05:00
Scott Idem
c4fa35e86e Saving the new documentation for Svelte and Dexie made by Gemini. Saving changes made by Gemini to fix various things. 2025-11-17 14:09:33 -05:00
Scott Idem
02169f1cb9 fix(events): correct presentation query 2025-11-13 21:43:20 -05:00
Scott Idem
92ef08ce1f fix(events): correct presentation query and props 2025-11-13 21:38:47 -05:00
Scott Idem
d554696c8e fix(events): correct presentation query and props 2025-11-13 21:33:00 -05:00
Scott Idem
66c42a52f9 Reapply "fix(events): correct reactive props in session view"
This reverts commit 97e9f35ea1.
2025-11-13 21:28:12 -05:00
Scott Idem
97e9f35ea1 Revert "fix(events): correct reactive props in session view"
This reverts commit b03376e136.
2025-11-13 21:28:07 -05:00
Scott Idem
b03376e136 fix(events): correct reactive props in session view
This commit fixes a bug where the session view was not displaying correctly due to incorrect usage of reactive props.

- In , the  prefix was added to the props passed to the  component to make them reactive.
- In , the  prefix was removed from the  variables in the template, as they are already reactive when using Svelte 5 runes.
- The  block for  was corrected to use the resolved value.
2025-11-13 21:17:49 -05:00
Scott Idem
7ded0fd9a6 fix(events): correct presentation query in session view
This commit fixes a bug where presentations were not being displayed correctly within the event session view. The issue was caused by an incorrect query that was using the standard session ID to filter presentations, instead of the random session ID.

The liveQuery in  has been updated to use the  from the  derived variable. This ensures that the query correctly fetches all presentations associated with the session.
2025-11-13 20:38:21 -05:00
Scott Idem
e19b448238 Updates to fixed processed_obj. Fixed other things with find and replace and regular expressions. 2025-11-13 20:29:30 -05:00
Scott Idem
3104304fdb Updates to common enabled and hidden properties. 2025-11-13 20:16:22 -05:00
Scott Idem
88d36d512d Update to all catch functions. 2025-11-13 20:15:09 -05:00
Scott Idem
7cd59bfaac Another round of lots of updates to fix little bugs here there and everywhere. 2025-11-13 20:03:51 -05:00
Scott Idem
13869b40bd Trying to remove some more _random suffixes. Ideally they will all eventually go away and we will just use .id or example.example_id or example.linked_obj_name_id 2025-11-13 19:28:52 -05:00
Scott Idem
a87fa02bc6 Revert: Legacy launcher link changes
Reverted changes to legacy launcher links in various components. These links are intentionally structured to work with nginx location aliases and should not be modified to conform to SvelteKit routing.
2025-11-13 19:15:55 -05:00
Scott Idem
aef34dc79b Fix: Correctly load session content in new launcher
The session content was not loading in the new Events Launcher because the loaded session data was not being correctly assigned to the events store.

This commit fixes the issue by correctly assigning the loaded session object and its nested properties to the events store.
2025-11-13 19:03:43 -05:00
Scott Idem
0e960ba955 Fixing id_random vs id for links and other things. Fixed the hash SHA256 error. 2025-11-13 18:59:27 -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
a84d06a28d Refactor: Update import paths for elements after moving them to src/lib/elements/. 2025-11-13 16:37:24 -05:00
Scott Idem
6e00502d3d Refactor: Update import paths for app_components after moving them to src/lib/app_components/. 2025-11-13 16:19:09 -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
94832c2471 Refactor: Organize src/lib into subdirectories and move files accordingly. 2025-11-13 16:09:03 -05:00
Scott Idem
6d8089bdbd Revert: Remove remaining codemirror-toolbar imports and extensions from e_app_codemirror_v5.svelte. 2025-11-13 16:03:44 -05:00
Scott Idem
14a69ca433 Revert: Rollback codemirror-toolbar integration and uninstall packages due to module resolution error. 2025-11-13 15:59:25 -05:00
Scott Idem
2f83aefd22 Feat: Integrate codemirror-toolbar with markdownItems into e_app_codemirror_v5.svelte for basic rich text editing. 2025-11-13 15:44:05 -05:00
Scott Idem
81e4d83c2d Docs: Update ARCHITECTURE.md and COMPONENTS.md to reflect ShadCN removal and CodeMirror emphasis. 2025-11-13 15:40:56 -05:00
Scott Idem
03a6661360 Cleaned up MarkDown again 2025-11-13 15:36:45 -05:00
Scott Idem
882b9b5bc7 Docs: Create ARCHITECTURE.md, COMPONENTS.md, and DATA_STRUCTURES.md for comprehensive project documentation. 2025-11-13 15:33:39 -05:00
Scott Idem
cfed92e8a5 Formatting clean up 2025-11-13 15:23:49 -05:00
Scott Idem
853219ce16 Docs: Rename old README_guidelines files to OLD_README_guidelines for historical purposes. 2025-11-13 15:23:12 -05:00
Scott Idem
514dfe913c Docs: Create NAMING_CONVENTIONS.md to standardize naming across the Aether project. 2025-11-13 15:20:19 -05:00
Scott Idem
fd083faac5 More documentation for the AIs to read. 2025-11-13 15:19:05 -05:00
Scott Idem
4093afe4fc Missed one 2025-11-13 15:12:17 -05:00
Scott Idem
fb634268c1 Refactor: Standardize naming conventions in ae_core module to snake_case and remove deprecated functions. 2025-11-13 15:11:25 -05:00
Scott Idem
d4ead978f9 Remove redundant/backup element_qr_scanner and old_e_app_codemirror files. 2025-11-13 14:50:15 -05:00
Scott Idem
4b4c3c5552 Fixed MarkDown formatting 2025-11-13 14:38:30 -05:00
Scott Idem
4d6c10cbad Quick save before more big changes. 2025-11-13 14:26:04 -05:00
Scott Idem
a995711335 Update TODO.md and GEMINI.md to reflect temporary rollback of ae_core_functions.ts and module-by-module refactoring plan. 2025-11-13 14:24:05 -05:00
Scott Idem
bc10075314 I think we are done with the first big rounds of changes by Gemini. 2025-11-13 12:52:46 -05:00
Scott Idem
389f0c855f Starting another round of changes by Gemini. Saving things in a working state. 2025-11-13 12:47:57 -05:00
Scott Idem
db8c0e0d05 Quick snapshot again as Gemini is working. 2025-11-13 12:44:28 -05:00
Scott Idem
dfaa27384b Saving a quick snapshot after Gemini changes to Dexie function and processing functions. 2025-11-13 12:12:58 -05:00
Scott Idem
848a3e9a4b First realish round of updates by Gemini... 2025-11-13 11:12:06 -05:00
Scott Idem
e793db8d3b feat: Initialize branch for AI-driven development
This commit introduces the initial setup for the `ae_app_3x_llm` branch.
The purpose of this branch is to explore and implement more direct
collaboration with AI Large Language Models (LLMs) for complex code
changes, including the creation of new function libraries and the
modification of existing code.

This initial commit includes:
- GEMINI.md: A file to provide context to the AI about the project.
- TODO.md: A to-do list for upcoming development tasks, created in
  collaboration with the AI.
2025-11-04 21:59:33 -05:00
Scott Idem
35031d3193 Added new topic for IDAA BB 2025-11-03 18:04:47 -05:00
Scott Idem
616d6a4404 Another moderator 2025-11-03 17:51:48 -05:00
Scott Idem
d9887aba93 Making things look nicer 2025-10-17 18:14:09 -04:00
Scott Idem
fab1a4916d Clean up of the event search page options menu and related. 2025-10-17 17:27:18 -04:00
Scott Idem
bb993a1028 Last minute clean up before LCI. 2025-10-17 16:22:23 -04:00
Scott Idem
d1c0148c75 Sort of bug fix for version reload. Does it need the alert() to work correctly??? 2025-10-16 21:43:08 -04:00
Scott Idem
e912c4a48a Better text sizing and flex. 2025-10-16 16:39:30 -04:00
Scott Idem
8b22c0fc34 The Launcher works pretty well. Except for the native app... 2025-10-16 16:07:37 -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
c2ccf0c06d Fix for disabling and hiding event file purpose options. 2025-10-16 10:04:12 -04:00
Scott Idem
c170358681 Quick icon change. 2025-10-15 19:04:56 -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
a6058efaf0 Good saving point. Version reset looks good now. 2025-10-15 14:12:04 -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
5b9dacd291 Better logic for lengths. Keep them more equal. 2025-10-10 17:54:49 -04:00
Scott Idem
8c2241e610 Work on badge rendering. 2025-10-10 17:40:35 -04:00
Scott Idem
74cc5c5d0d Work on badge printing and the actual badge layout. 2025-10-09 19:26:35 -04:00
Scott Idem
0f05fd708f Minor bug fix. 2025-10-08 19:19:35 -04:00
Scott Idem
41d20a3bcf Minor clean up getting ready for LCI and demo for AAPOR. 2025-10-08 16:41:57 -04:00
Scott Idem
bad5da3ddc More serious work on badge printing. Working on the templates and actually implementing the printable badge. 2025-10-07 19:39:53 -04:00
Scott Idem
28cb8e2bf2 Testing 2025-10-07 03:35:45 -04:00
Scott Idem
11334a17b1 Minor 2025-10-07 03:32:20 -04:00
Scott Idem
b24262b4ab Could not sleep. Working on the search for badges. 2025-10-07 03:25:02 -04:00
Scott Idem
e1a04d5eda Not sure 2025-10-06 18:56:16 -04:00
Scott Idem
00ff9554d8 Trying to simplify things 2025-10-06 18:54:11 -04:00
Scott Idem
d8b7ad61b3 Significant work on getting badges ready again. 2025-10-06 18:43:33 -04:00
Scott Idem
88e4ab119d More updates related to the IDB table name changes for events. 2025-10-06 14:25:52 -04:00
Scott Idem
c3992d8711 Removing old code. Standardizing the event related IDB table names. Getting badge printing ready again.... 2025-10-06 14:04:46 -04:00
Scott Idem
cd03d9e488 Updating the badge printing module. It needs a bit of work. Removing the old versions of functions. 2025-10-06 13:54:20 -04:00
Scott Idem
9fa7247204 Note on changes to QR code function. 2025-10-03 17:44:43 -04:00
Scott Idem
c4115adfd6 Wrapping up for the week. Version bump. 2025-10-03 17:42:11 -04:00
Scott Idem
af9c4a82f0 Making the QR codes work again. Also making things look better. 2025-10-03 17:41:35 -04:00
Scott Idem
c949581429 Moving older event_speakers and sponsorships modules to backups. 2025-10-03 14:49:17 -04:00
Scott Idem
a2f2742f30 More clean up. Making this live now. 2025-10-03 14:44:18 -04:00
Scott Idem
bcee6c5281 More work on the alerts for sessions. I think they are pretty good now. 2025-10-03 14:29:24 -04:00
Scott Idem
75f7ed54a5 Cleaning up the new session alerts 2025-10-03 14:01:25 -04:00
Scott Idem
c70abc46f3 Finally using the alerts for sessions!! 2025-10-03 13:27:12 -04:00
Scott Idem
b85d2ee98c The new event files reports has been cleaned up and more configurable. 2025-10-02 12:57:37 -04:00
Scott Idem
03f831de8a Quick bug fix for sorting 2025-10-02 11:07:25 -04:00
Scott Idem
b5a6c15059 Bug fix for file report sorting 2025-10-02 10:34:50 -04:00
Scott Idem
4aeded3a12 Working on making sure notifications are being sent to IDAA staff when a post or post comment is created or updated. It looks like it is working.... 2025-10-01 13:03:16 -04:00
Scott Idem
f96a14107a More work on using the new Element_ae_crud_v2 component 2025-09-30 13:11:52 -04:00
Scott Idem
99fe486842 Work on auto reload and related single property updates. Also bug fix for saving searched sessions. 2025-09-30 12:05:55 -04:00
Scott Idem
53a03f971f Added new auto reload for most object types with new v2 function and element. 2025-09-30 03:43:29 -04:00
Scott Idem
0c01eed5c8 More work on new CRUD element. Now able to update the session location in the list. 2025-09-29 18:58:54 -04:00
Scott Idem
22d7c4728d More work on the CRUD element 2025-09-29 14:17:27 -04:00
Scott Idem
24ba3940f8 Work on updating the custom CRUD element. 2025-09-29 14:06:47 -04:00
Scott Idem
f5e035318a Bug fix for saving location in session view 2025-09-29 10:55:51 -04:00
Scott Idem
c42ab0bfa0 Temporary bug fix for the file type reports. Minor update for IDAA and Jitsi iframe. 2025-09-24 18:00:12 -04:00
Scott Idem
abed17f5da Mostly cosmetic styles 2025-09-24 17:43:40 -04:00
Scott Idem
1ba9cd1c03 Lots of work on getting the Launcher actually working well. 2025-09-24 16:56:32 -04:00
Scott Idem
4220a354be Now with all reports broken out by type and things generally cleaned up. 2025-09-24 12:18:15 -04:00
Scott Idem
6f4f54a0c9 Mostly working with event file reports being moved out. 2025-09-24 10:50:28 -04:00
Scott Idem
08bc3142e6 Updates for Jitsi and IDAA 2025-09-23 14:41:06 -04:00
Scott Idem
2f4800f995 Adding notify/alert/message staff link to Google Form for IDAA. 2025-09-23 13:16:38 -04:00
Scott Idem
5bf231c468 Adding new Jitsi meeting links and related. 2025-09-23 12:41:14 -04:00
Scott Idem
7490545ba7 Work on Jitsi for IDAA 2025-09-23 11:23:05 -04:00
Scott Idem
43d64696d5 Sort of bug fix for hiding the header and footer at the right time. 2025-09-19 18:10:23 -04:00
Scott Idem
231c2eea17 Hiding scroll right button 2025-09-19 17:16:01 -04:00
Scott Idem
79c0c90eae Bug fix for scroll to end. 2025-09-19 16:50:14 -04:00
Scott Idem
5b7aa320e2 Getting rid of old code. Wrapping up for the day and week. 2025-09-19 16:23:39 -04:00
Scott Idem
f06358439f Making things look good. Now with scroll to top and bottom for Journals and Events. 2025-09-19 16:18:36 -04:00
Scott Idem
86fbbfdfb5 Never ending work on styles. Trying to make absolute positioned headers and footers work. Also added scroll to top for Journals. 2025-09-19 15:05:56 -04:00
Scott Idem
59b6577d68 Minor updates and version bump. 2025-09-19 10:40:08 -04:00
Scott Idem
fc4e59dd8c More work on styles... flex wraps everywhere. 2025-09-19 10:32:08 -04:00
Scott Idem
5527bce327 Saving changes from last night related to Jitsi. 2025-09-19 09:30:13 -04:00
Scott Idem
7a0ba7d571 More work on proof of concept for Jitsi meetings in IDAA's Novi site. 2025-09-18 18:47:25 -04:00
Scott Idem
811876e36a This new Jitsi page is working well. Now with URL params. 2025-09-18 16:54:46 -04:00
Scott Idem
7fd3ef4f63 Work on Jitsi for IDAA in the Novi site. 2025-09-18 15:52:45 -04:00
Scott Idem
8ff9f2439f Flex wrap... 2025-09-18 12:09:23 -04:00
Scott Idem
d6204d983a More work on scrolling. 2025-09-18 12:02:36 -04:00
Scott Idem
623b62f716 I think things are scrolling better. Other style clean up for dark mode. 2025-09-18 10:44:01 -04:00
Scott Idem
4f36d3eab3 Trying to at least make things scroll as well as they did yesterday... 2025-09-18 10:05:34 -04:00
Scott Idem
64f6b290ad This is not working... 2025-09-18 09:20:44 -04:00
Scott Idem
46a89c36fe Working on general styles 2025-09-17 19:21:01 -04:00
Scott Idem
6b6418b753 Making things look good. 2025-09-17 18:04:10 -04:00
Scott Idem
60fbfc434e More work on the Launcher and related 2025-09-17 16:36:04 -04:00
Scott Idem
c9e9fbec79 Lots of work on the Launcher. Still mostly just cleaning things up. 2025-09-17 15:15:50 -04:00
Scott Idem
186923671d Finally working on the Launcher piece again. Getting it updated to match the standards for everything else. 2025-09-17 13:40:57 -04:00
Scott Idem
b1aae55900 Oops forgot to add the column 2025-09-16 19:21:04 -04:00
Scott Idem
f1b3820fde Minor changes 2025-09-16 18:59:01 -04:00
Scott Idem
c670778ff3 The session reports look good now. Need to work on the file related reports next. 2025-09-16 18:57:34 -04:00
Scott Idem
15e0e423f2 Saving work on reports. Trying separate out the session specific reports. 2025-09-16 18:37:50 -04:00
Scott Idem
c7b8d49730 Lots of style improvements. Dark mode is mostly useable now. Work on reports. 2025-09-16 12:59:20 -04:00
Scott Idem
32b6f59245 Work on report config options and export of presenters 2025-09-16 10:14:58 -04:00
Scott Idem
c530ff8683 Work on AI related. 2025-09-15 18:09:37 -04:00
Scott Idem
fd3105b4e1 General clean up. Make things configurable. More AI stuff. 2025-09-12 16:20:50 -04:00
Scott Idem
0931b960b6 More query limit options. 2025-09-12 13:27:16 -04:00
Scott Idem
662e19b091 Work on event reports. Making them easier to manage. 2025-09-12 13:12:50 -04:00
Scott Idem
01273c3c4d Package updates and minor fixes 2025-09-11 19:01:59 -04:00
Scott Idem
b78cb8eac2 Now with basic AI summary for Journal Entries 2025-09-11 18:41:16 -04:00
Scott Idem
bbf9411213 More work on encryption related content and history. 2025-09-11 15:44:07 -04:00
Scott Idem
db95ed88a3 Code clean up. Version update. 2025-09-10 21:00:06 -04:00
Scott Idem
084e52be15 Wrapping up for the night. Lots of work on encryption related. 2025-09-10 20:58:10 -04:00
Scott Idem
cc6186467a Bug fixes and clean up for the recent history of Journal Entries. 2025-09-10 17:31:04 -04:00
Scott Idem
efa7833fb3 Style updates 2025-09-08 19:13:48 -04:00
Scott Idem
f842392aac More work on reports and config options 2025-09-08 17:43:30 -04:00
Scott Idem
438d366b98 Work on reports for LCI 2025-09-08 17:12:44 -04:00
Scott Idem
88c4f4e891 Bug fix for access denied message. Better formatting and extra Cancel edit button 2025-09-08 15:21:43 -04:00
Scott Idem
92a00ddee9 Sort of bug fix related to Novi linking. 2025-09-08 15:09:32 -04:00
Scott Idem
9e78ba970b Bug fix for restricted access message showing. 2025-09-05 17:41:27 -04:00
Scott Idem
5a0b13700f Finally better responsive reports again. 2025-09-04 19:31:27 -04:00
Scott Idem
bd964cac80 Still working on reports... Why are they not more responsive? 2025-09-04 18:50:50 -04:00
Scott Idem
f72a77ba60 More work related to reports and them refreshing correctly 2025-09-04 12:58:06 -04:00
Scott Idem
390bcf05a4 Work on Event reports. 2025-09-04 12:30:32 -04:00
Scott Idem
4fcf28f303 Bug fix for searching event sessions. 2025-09-04 09:57:06 -04:00
Scott Idem
84b18a33ed Hide menu by default... not testing. 2025-09-03 18:30:04 -04:00
Scott Idem
9869c695f7 General clean up. Show menu instead of link to all journals list. For now it just includes the most recent Entries loaded. 2025-09-03 18:27:24 -04:00
Scott Idem
66510c8ff1 Mostly style improvements for mobile sized 2025-09-03 16:19:54 -04:00
Scott Idem
7364ce5527 Remove debug code!! 2025-09-03 15:06:55 -04:00
Scott Idem
a6ae701e0f Minor update to logic 2025-09-03 14:52:53 -04:00
Scott Idem
711e24a7d0 Bug fixes and general code clean up related to the IDAA Archives. 2025-09-03 14:35:44 -04:00
Scott Idem
acb96729d1 Hide the Journal description by default 2025-08-22 15:57:35 -04:00
Scott Idem
7136eb04d8 Improvement to search progress and status and style 2025-08-22 15:51:08 -04:00
Scott Idem
7bb4e20ce7 Mostly style improvements. 2025-08-22 15:28:35 -04:00
Scott Idem
8a9864771b Minor improvements and fixes for Journals searching and styles. 2025-08-22 14:35:15 -04:00
Scott Idem
f3ee4ee987 Minor fixes. Better dark mode style. 2025-08-21 18:28:40 -04:00
Scott Idem
6d2c3cb056 Work on making the Entry search query work correctly. Moved things around. Hopefully easier to read and understand. 2025-08-21 18:00:32 -04:00
Scott Idem
33c6cb862e Trying to improve the sign in and passcode logic. Also related bug fixes with focus. Made Journal Entries marked as public visible to others. 2025-08-21 13:55:23 -04:00
Scott Idem
edfe9dee7a Finally work on the Journals to fix some bugs. Now with much better append and prepend to Journal Entry. 2025-08-19 18:50:23 -04:00
Scott Idem
ebaba77fe3 Version bump and package updates. 2025-08-19 15:57:28 -04:00
Scott Idem
6dec20d932 Bug fix to allow the Session POC to be null. Minor changes. 2025-08-19 13:53:54 -04:00
Scott Idem
35b6662b5b Styles... 2025-08-12 19:43:26 -04:00
Scott Idem
6f5b40d5ab Bug fix for Journal Entry view 2025-08-12 19:12:33 -04:00
Scott Idem
565712b818 Never ending work on styles 2025-08-12 18:59:01 -04:00
Scott Idem
0446822779 Minor alignment fix 2025-08-12 18:06:54 -04:00
Scott Idem
231462e3ad Working to make the dark mode look better. Work on the help tech component. General clean up. 2025-08-12 17:59:37 -04:00
Scott Idem
496eea48ee Version bump 2025-08-12 14:20:12 -04:00
Scott Idem
05d7e78444 Bug fixes 2025-08-12 14:19:54 -04:00
Scott Idem
1e20539b1a Creating a new branch and updating the version number to reflect the changes. 2025-08-12 13:45:02 -04:00
Scott Idem
0ecb9d15b0 General clean up. Better sorting for the IDAA Archives and BB Posts and Comments 2025-08-12 13:24:03 -04:00
Scott Idem
2f19422c72 First round of package updates 2025-08-12 12:10:55 -04:00
Scott Idem
d3f71be94e Minor update to make the selected Post Comment update when the liveQuery value changes. 2025-08-12 12:02:46 -04:00
Scott Idem
a4b3d3887f The notifications to Post Commenters should be working now. 2025-08-11 18:34:58 -04:00
Scott Idem
6622fc4169 Working on IDAA BB Post Comment notifications and related. 2025-08-11 17:50:01 -04:00
Scott Idem
7ef18ce105 More work on dark mode clean up. Wrapping up for the week. Happy almost birthday me. 2025-08-08 17:49:58 -04:00
Scott Idem
b95111503d Never ending working styling for light and dark mode in Novi. 2025-08-08 15:57:46 -04:00
Scott Idem
5187f6455a Getting the IDAA BB ready with the tech notification 2025-08-08 13:30:31 -04:00
Scott Idem
bfe9823902 More work on the notification component and IDAA. 2025-08-07 17:02:25 -04:00
Scott Idem
e286357c8d More work on the help tech notification. Added to Archives and Meetings so far. 2025-08-07 16:48:17 -04:00
Scott Idem
a789866642 First version of the technical help notification is ready for IDAA. 2025-08-07 10:29:46 -04:00
Scott Idem
d993ca5938 Saving work for the day. Need to finish this up for IDAA ASAP. 2025-08-06 21:40:18 -04:00
Scott Idem
0a4940161d Initial work on finally creating and implementing more generic and standardized CRUD functions for my Aether objects. It should work very well for Delete, Create, and Update. Load and Load List will need more work. 2025-08-01 17:30:30 -04:00
Scott Idem
9ed4bd85e1 Minor change to button style in Novi. 2025-07-29 17:34:54 -04:00
Scott Idem
93804c23d7 Bug fix for Archive Content sorting. This should be applied everywhere.... fun. Also slight improvement to the IDAA media player. Now with Play and View text. and a new icon in the button. 2025-07-29 12:34:56 -04:00
Scott Idem
dfd39ae8c9 Permission change so staff can remove linked files 2025-07-25 14:45:20 -04:00
Scott Idem
4568d63620 Bug fix for search results not showing. 2025-07-23 17:02:36 -04:00
Scott Idem
9d72337d43 Bug fix for event file table list. Links not showing. 2025-07-23 16:31:06 -04:00
Scott Idem
4861ae0499 Lot of updates to Svelte 5 syntax 2025-07-23 16:21:27 -04:00
Scott Idem
8504f4b79a Page clean up. Show less by default. 2025-07-23 14:22:48 -04:00
Scott Idem
1f024d70f3 Style improvements for icons and related. 2025-07-23 13:31:30 -04:00
Scott Idem
ced62cfd99 Style improvements for file uploads and related. 2025-07-23 13:22:34 -04:00
Scott Idem
5410717dcd Update to allow for searching based on the session POC (LCI Champion). Other minor bug fixes and code clean up. 2025-07-23 11:14:40 -04:00
Scott Idem
47eb745f3b Work on file uploads and manage. 2025-07-22 19:02:16 -04:00
Scott Idem
1e94043e19 Bug fix for IDAA Archive Content edit button. Updates related to new Launcher. 2025-07-22 15:56:07 -04:00
Scott Idem
7d07879e21 Do not show Play/View if nothing to play or view 2025-07-22 10:33:03 -04:00
Scott Idem
1d840052c7 More work on the new Launcher. Wrapping up for the day. 2025-07-21 20:36:44 -04:00
Scott Idem
2e39c5f67b More code clean up. 2025-07-21 18:21:36 -04:00
Scott Idem
e962d629a3 Saving before working on the event_file functions 2025-07-21 17:41:24 -04:00
Scott Idem
a3999e5617 Mass changes 2025-07-21 16:46:33 -04:00
Scott Idem
b54f43be35 Lots of code clean up. Finally working on the Launcher piece again. 2025-07-21 16:40:37 -04:00
Scott Idem
ee0e108322 More work on page load and cache checking 2025-07-21 13:07:25 -04:00
Scott Idem
0ac7bea046 Improvements on how posts are pulled in and sorted. 2025-07-18 18:15:18 -04:00
Scott Idem
fd69610a3a Wrapping up for now. 2025-07-16 15:12:26 -04:00
Scott Idem
eb396b0a55 More minor changes 2025-07-16 14:46:43 -04:00
Scott Idem
047f6d7048 More work on the menus... 2025-07-16 14:29:57 -04:00
Scott Idem
eaffc44772 More work on the system and debug menus and related info. 2025-07-16 13:57:12 -04:00
Scott Idem
32c3be9983 Forgot to change testing. 2025-07-15 23:36:37 -04:00
Scott Idem
03c0dd6d13 Sort of preventative bug fix early in page load 2025-07-15 23:35:23 -04:00
Scott Idem
a378f92c58 Small bug fix. 2025-07-15 21:32:19 -04:00
Scott Idem
fa5de6e7df Done for the night 2025-07-15 20:43:05 -04:00
Scott Idem
a52cc3808b The new new new version of the system menu seems to be working pretty well. Wrapping up for the day. 2025-07-15 20:36:13 -04:00
Scott Idem
7b853ede8c Moving on to the user sign in/out 2025-07-15 17:55:53 -04:00
Scott Idem
8692771efb Work on the system and debug menus 2025-07-15 16:16:10 -04:00
Scott Idem
6c79be7179 Now with better Zoom info support. 2025-07-14 21:11:58 -04:00
Scott Idem
de2b5db21a More updates for IDAA style 2025-07-14 14:44:51 -04:00
Scott Idem
37bcb7940d More work on the IDAA Recovery Meetings edit styling and related 2025-07-14 14:30:00 -04:00
Scott Idem
3e36100b9b Actually deleting things 2025-07-11 17:08:32 -04:00
Scott Idem
84138943a6 Removing old files 2025-07-11 17:07:19 -04:00
Scott Idem
bd10d98ada Package updates 2025-07-11 16:43:03 -04:00
Scott Idem
20ad1370e2 More style updates.... 2025-07-11 16:30:27 -04:00
Scott Idem
bcd42412f4 More style changes for Novi site. 2025-07-11 15:59:04 -04:00
Scott Idem
299d5b71c3 More layout fixes for IDAA.... 2025-07-11 15:05:38 -04:00
Scott Idem
e23999d728 Work on OSIT default theme 2025-07-11 13:34:02 -04:00
Scott Idem
b133aa1285 Various bug fixes and improvements. Now able to correctly create and delete Recovery Meetings. 2025-07-11 13:12:45 -04:00
Scott Idem
6e4649882c Improvements and other minor fixes. Wrapping up for the night. 2025-07-10 21:10:06 -04:00
Scott Idem
1e35681116 Minor changes 2025-07-10 19:53:27 -04:00
Scott Idem
ea30697ffb Lots of work on searching. Other related code clean up. 2025-07-10 19:46:41 -04:00
Scott Idem
8bf9d488cd Rework of the IDAA Recovery Meetings search. Other related updates and bug fixes. Added preventDefault() 2025-07-10 15:45:48 -04:00
Scott Idem
770e48842b Bug fixes for form save. Use custom preventDefault function. 2025-07-09 19:25:58 -04:00
Scott Idem
b030c0eec4 The IDAA Archives seem to be working pretty well. Still need some more testing. 2025-07-09 17:43:41 -04:00
Scott Idem
06cf665b06 Lots of clean up. Updates to Archive related admin section and some fields. 2025-07-09 16:33:48 -04:00
Scott Idem
8a80bb1a2f Working on bug fix for viewing the IDAA Recovery Meeting list after viewing a Meeting. 2025-07-09 14:33:55 -04:00
Scott Idem
4f6684e2bd Minor changes and clean up 2025-07-09 14:16:04 -04:00
Scott Idem
8bd42d1896 Bug fix for event_id being added as a URL param. Other fixes. 2025-07-09 14:05:51 -04:00
Scott Idem
d97e6e74aa More work on editing a meeting 2025-07-09 13:32:56 -04:00
Scott Idem
628659f348 Bug fixes for loading the IDAA Rec Mtg when navigating. Other improvements. 2025-07-09 11:46:27 -04:00
Scott Idem
6bdedecfd8 Working on getting Recovery Meeting edit to show correctly when navigating from list of meetings. 2025-07-08 20:12:28 -04:00
Scott Idem
8672796bac Minor improvements. Now with spell check! 2025-07-08 19:19:27 -04:00
Scott Idem
ef291f4be0 Recovery Meeting editing looks pretty good now. 2025-07-08 18:47:54 -04:00
Scott Idem
3c404acdfd Saving work on making things look good for IDAA Recovery Meetings and BB. 2025-07-08 18:16:31 -04:00
Scott Idem
fcefcc510b Saving work on IDAA pages 2025-07-08 16:53:45 -04:00
Scott Idem
32c1899cb6 Fixed a file upload and management bug. Trying to get to a good stopping point. 2025-07-07 18:34:40 -04:00
Scott Idem
b028bd7b32 More improvements for IDAA BB 2025-07-07 18:01:57 -04:00
Scott Idem
2eb4ddf1eb Sort of bug fix for file uploads 2025-07-07 17:16:23 -04:00
Scott Idem
1d08f960b0 Testing before going live.... 2025-07-07 16:44:58 -04:00
Scott Idem
b675cad43d Things look good for the IDAA BB. Need to test one last time from start to finish. 2025-07-07 16:27:12 -04:00
Scott Idem
f0fae620ea Finalish clean up for IDAA BB Posts 2025-07-07 15:25:47 -04:00
Scott Idem
cc8bbffdbf The sys menu finally looks and works better. Still needs more work though... Finally done for the week....? 2025-07-02 18:12:04 -04:00
Scott Idem
07979cb529 One last style update... 2025-07-02 17:49:24 -04:00
Scott Idem
429403ced6 Work on some layout and placement issues with the Journal cfg edit, sys menu, and debug menu. Wrapping up for the week. Long vacation weekend finally! 2025-07-02 17:38:34 -04:00
Scott Idem
dba6ac9e3b Hiding old buttons. 2025-07-02 17:14:54 -04:00
Scott Idem
d3a2485970 Minor correction. 2025-07-02 17:11:31 -04:00
Scott Idem
40061b92ca I have the new BB Posts working well for IDAA now. NOTE: I intentionally split the API calls that are triggered on layout and page loads. 2025-07-02 17:09:52 -04:00
Scott Idem
25de3407fd Hiding old buttons 2025-07-02 13:57:16 -04:00
Scott Idem
f8461f367e More work on the BB Posts for IDAA. Can now edit, update, and create now posts. 2025-07-02 13:52:30 -04:00
Scott Idem
50deb79f6b Not sure... I need to get to a good point tomorrow. 2025-07-01 16:58:25 -04:00
Scott Idem
a691e7d22d Wrapping up for hte day 2025-06-30 18:58:06 -04:00
Scott Idem
238416d903 The BB Posts have been updated to use a new page the Post. Seems to be working well. Wrapping up for the day. 2025-06-30 18:40:32 -04:00
Scott Idem
ec7ec7b566 Moving away from using modal with iframes. Scroll issues. Especially with Safari. 2025-06-30 18:09:08 -04:00
Scott Idem
2d66984581 Some code clean up. Bug fix for <Modal /> placement. 2025-06-30 15:54:48 -04:00
Scott Idem
088b32e4f1 Wrapping up for the day. The new IDAA versions are almost ready to go live. 2025-06-27 18:44:08 -04:00
Scott Idem
a6edc461f3 Trying to get to a good stopping point for the day. 2025-06-27 18:16:46 -04:00
Scott Idem
4c0b1d4c50 Work on style updates. Able to create new meetings again. 2025-06-27 17:36:00 -04:00
Scott Idem
e87fae34f8 The IDAA BB post page is looking good. Testing.... 2025-06-27 15:55:49 -04:00
Scott Idem
74a098fbde Just saving my work so far. 2025-06-27 15:31:18 -04:00
Scott Idem
fbcebcf816 Lots of clean up. Going to go with backup plan for IDAA and dealing with the Novi iframes. 2025-06-27 15:01:46 -04:00
Scott Idem
9914fd1700 General clean up of files. Less debug. Less extra stuff. 2025-06-27 12:44:23 -04:00
Scott Idem
bd0d96cb83 Work on general layout improvements. Making special cases for Safari browsers. 2025-06-26 20:41:25 -04:00
Scott Idem
4ef2e7cd6b Work on getting the scroll to work for Safari.... 2025-06-26 16:56:00 -04:00
Scott Idem
71b2de27ce Cleaning layout related... 2025-06-26 15:49:46 -04:00
Scott Idem
1f46b51c9a More significant updates to the general layout and styling per module. 2025-06-26 14:39:30 -04:00
Scott Idem
ce59a848d6 Fix scroll with overflow-auto 2025-06-26 12:32:24 -04:00
Scott Idem
b34f4bb3ce Giving more permissions to trusted/staff users. 2025-06-26 10:16:22 -04:00
Scott Idem
55dc2ab48c New scroll to top button and functions. 2025-06-25 18:29:33 -04:00
Scott Idem
c947e9e77a Making things look nicer. Also more respect for show/hide toggles. 2025-06-25 15:15:34 -04:00
Scott Idem
370396a633 Style updates. Allow easier ability to change presentation code and description. 2025-06-25 14:44:47 -04:00
Scott Idem
7cb8639230 Permission change for "Delete" presenter button. 2025-06-25 10:29:28 -04:00
Scott Idem
dde9801fe6 Updates getting ready for LCI 2025-06-24 18:51:07 -04:00
Scott Idem
7b4ec1fe7e Almost done with prep for LCI walk through. 2025-06-24 12:26:24 -04:00
Scott Idem
c47019ddba Mostly style improvements in general and for LCI 2025-06-24 11:48:56 -04:00
Scott Idem
d86558caa0 Lots of minor changes to get ready for LCI 2025-06-24 10:52:10 -04:00
Scott Idem
db4c061f19 Prep for LCI walk through again 2025-06-24 10:14:28 -04:00
Scott Idem
357e135451 Minor clean up 2025-06-23 19:03:04 -04:00
Scott Idem
2816c09003 More work on IDAA and LCI style and themes. 2025-06-23 18:26:49 -04:00
Scott Idem
626f0f2351 IDB table name fixes 2025-06-23 16:00:33 -04:00
Scott Idem
0b345e325e Updates related to IDAA API functions and related. Some style updates. 2025-06-23 15:49:53 -04:00
Scott Idem
1ad1251931 More work on IDAA styles 2025-06-23 14:47:37 -04:00
Scott Idem
e93c70e0e4 More IDAA related updates 2025-06-23 14:15:50 -04:00
Scott Idem
b59dcf20d5 Work on IDAA modules 2025-06-23 13:55:59 -04:00
Scott Idem
40cba6d841 Back to work on style changes related to TW 4 2025-06-23 13:10:11 -04:00
Scott Idem
574eb8c635 Last minute removing of older components that need to be updated later. No more Tab with Skeleton? 2025-06-20 18:15:51 -04:00
Scott Idem
4e63c8418e Package updates 2025-06-20 17:58:09 -04:00
Scott Idem
0bccaa226f Lots of work updating the styles. Trying to wrap up for the day/week. 2025-06-20 17:56:54 -04:00
Scott Idem
eebcd7c731 Updating the module menu to the new styling 2025-06-20 15:12:38 -04:00
Scott Idem
e5d41a7467 More updates 2025-06-20 12:57:18 -04:00
Scott Idem
b8a8653e0a More prep for LCI and other clean up 2025-06-20 12:35:22 -04:00
Scott Idem
93ffb8c3ad Package updates. More updates for LCI. 2025-06-20 10:59:04 -04:00
Scott Idem
94ccbd5f96 More updates. Done for the day. 2025-06-19 20:42:28 -04:00
Scott Idem
00322aee89 Wrapping up for the day. 2025-06-19 20:30:50 -04:00
Scott Idem
e18400d597 Updating styles to use the new Aether presets based on the TW and Skeleton presets. Prep for LCI 2025-06-19 20:25:09 -04:00
Scott Idem
09dc471842 More TW related updates. LCI related updates. 2025-06-19 17:09:44 -04:00
Scott Idem
f68a64b1f8 Saving slow but steady progress 2025-06-19 16:06:21 -04:00
Scott Idem
38e88d7b27 More updates everywhere related to TW v4 2025-06-19 15:05:15 -04:00
Scott Idem
1e0c6ec91b Work on general style clean up. So much to do... 2025-06-19 12:54:40 -04:00
Scott Idem
9617071361 Partial fix for hover selecting children to show/hide etc 2025-06-18 21:20:03 -04:00
Scott Idem
0d4885b778 Working on making Tailwind select children when hovering. 2025-06-18 21:10:53 -04:00
Scott Idem
376837950e Lots of work on upgrading to Tailwind CSS 4. Still more to go. Need to fix Modals everywhere. 2025-06-18 20:36:09 -04:00
Scott Idem
13912fd145 Still working on upgrading to Tailwind CSS v4 an dSkeleton v3..... 2025-06-18 17:59:48 -04:00
Scott Idem
db6e9dd019 NOT FULLY WORKING YET. Still working to upgrade fully to Tailwind CSS v4. 2025-06-18 14:48:42 -04:00
Scott Idem
e6394549bf Round 3 of updates. Preparing to update TailwindCSS to v4. 2025-06-18 13:31:42 -04:00
Scott Idem
2b65c0ff16 Second round of package updates 2025-06-18 13:19:51 -04:00
Scott Idem
f126abf39d Updating packages and theme finally 2025-06-18 13:09:04 -04:00
Scott Idem
61c78fc641 Cleaning up the session POC lookup 2025-06-16 19:34:18 -04:00
Scott Idem
487af455d0 Finally getting rid of the old display_name field. Use full_name_override! 2025-06-16 19:23:14 -04:00
Scott Idem
1d31d9d671 Updating the sign in process for presenters and session moderators. Got rid of old code. 2025-06-12 14:15:30 -04:00
Scott Idem
88d856ab2c Work on reports and prep for LCI 2025-06-11 12:19:32 -04:00
Scott Idem
e0f0b774ef Making the max person results sticky 2025-06-10 17:55:46 -04:00
Scott Idem
8c51380fb5 Cleaning up and updating the person related sections. Prep for LCI opening. 2025-06-10 17:37:04 -04:00
Scott Idem
c10abd596e General code clean up related to the person functions. Less direct use of params and params_json 2025-06-10 14:32:00 -04:00
Scott Idem
39a878ac38 Updated the person functions. General clean up of code. 2025-06-10 12:16:19 -04:00
Scott Idem
4c1449bb8f Minor bug fix for typo 2025-06-10 11:12:18 -04:00
Scott Idem
ac81aadd6f Now able to search the Journal Entries! 2025-06-04 17:54:37 -04:00
Scott Idem
8c9f0afc02 Code clean up 2025-06-04 14:17:58 -04:00
Scott Idem
bed4b9f70e Added the extra tmp_sort fields. 2025-06-04 14:13:08 -04:00
Scott Idem
dbbd8209cc More work on updating Archives and Posts to use tmp_sort_x and other changes. 2025-06-04 14:04:51 -04:00
Scott Idem
f38d9b58ab More updates to start using the newer/better way of saving to IDB. 2025-06-04 12:28:16 -04:00
Scott Idem
83ff5681d6 First part of of getting the IDAA Archives to use the newer method to save to the IDB. 2025-06-04 12:04:41 -04:00
Scott Idem
695a520eb3 Work on bug fix for presenters not being save in the IDB. Also cleaned up the related functions to use the more standard enabled, hidden, limit, and offset params. Other minor clean up 2025-06-04 11:27:37 -04:00
Scott Idem
8279fab244 Adding activity logging to IDAA related section. 2025-06-02 19:08:37 -04:00
Scott Idem
b97e4002bc More work on making things right for IDAA. 2025-06-02 16:47:33 -04:00
Scott Idem
a2e4c710a0 Work on bug fix or something for IDAA and the page not fully loading properly. Unsure if this is related to Novi permissions check? 2025-06-02 13:31:22 -04:00
Scott Idem
eb32ae1eaa Package updates 2025-06-02 11:20:20 -04:00
Scott Idem
72522ceef4 Less and less debug 2025-05-23 19:06:55 -04:00
Scott Idem
454ec2ea5d Less debug 2025-05-23 19:02:37 -04:00
Scott Idem
e8acce6c43 Even more less debug 2025-05-23 18:55:57 -04:00
Scott Idem
6c496992aa Less debug 2025-05-23 18:48:27 -04:00
Scott Idem
f6426eb574 Sort of fixing word wrap 2025-05-23 17:51:15 -04:00
Scott Idem
23f182b964 Now slightly better 2025-05-23 17:34:16 -04:00
Scott Idem
240855f649 Minor fix for width 2025-05-23 17:16:20 -04:00
Scott Idem
82af7a00e5 Minor quick fix for needing some header for appending to an entry. 2025-05-23 17:00:33 -04:00
Scott Idem
5cf076fbe3 More work on the event object functions. Also a minor update to the recent files list options. 2025-05-23 16:45:29 -04:00
Scott Idem
7c70d93a68 Saving work after rounds of updates to the event object type functions. 2025-05-23 16:17:42 -04:00
Scott Idem
589320a850 Hopefully a bug fix for file upload progress on Event pages. Using the older Events specific component. 2025-05-23 15:09:08 -04:00
Scott Idem
c4d95e1c6a Wrapping up for the night. 2025-05-22 19:55:46 -04:00
Scott Idem
14f4adeafa Finally getting the event session list to use the LiveQuery with a list of IDs to work correctly. Now replacing old version. Well... maybe some more testing tomorrow. 2025-05-22 18:19:20 -04:00
Scott Idem
a75f40bf8d Progress on more low level updates to the Events module loading process. 2025-05-22 17:06:59 -04:00
Scott Idem
6b585090d0 Clean up to get ready for CMSC and LCI. 2025-05-22 13:27:23 -04:00
Scott Idem
15d417ba52 Lots of work with linking files to Journal Entries. 2025-05-20 15:37:27 -04:00
Scott Idem
83a8377155 Work on adding files to a Journal Entry 2025-05-19 19:23:16 -04:00
Scott Idem
2976d618f8 Check for params before trying to update params 2025-05-19 17:25:02 -04:00
Scott Idem
9af5c960f7 More complete bug fix for the API POST issue. 2025-05-19 17:16:30 -04:00
Scott Idem
f2059da9d1 Important bug fix for posting form data. The headers are case sensitive. Changed them all to Content-Type. 2025-05-19 17:01:13 -04:00
Scott Idem
f88e6cef89 Fixed and improved encryption handling. Also wrapping up for the day. 2025-05-16 17:29:33 -04:00
Scott Idem
8b68b0d2bb Just a version bump 2025-05-16 14:20:28 -04:00
Scott Idem
83d52b4e79 Minor fix for iframe hiding 2025-05-16 14:17:19 -04:00
Scott Idem
3041246638 More less debug 2025-05-16 14:06:35 -04:00
Scott Idem
ec404ab428 Less debug 2025-05-16 14:04:07 -04:00
Scott Idem
616cf2890c Code cleaning 2025-05-16 14:02:26 -04:00
Scott Idem
170eeef877 Package updates 2025-05-16 13:59:39 -04:00
Scott Idem
10fba7fef6 Minor update to cache times 2025-05-16 13:51:47 -04:00
Scott Idem
40ce368e59 More style clean up. Less is more info. 2025-05-16 13:41:24 -04:00
Scott Idem
bae46782d6 Minor text fixes 2025-05-16 11:55:00 -04:00
Scott Idem
cc354100ae Updates to show/hide some reports 2025-05-16 11:46:04 -04:00
Scott Idem
49139b3a1f Now with prepend or append to Entry content option. 2025-05-15 19:03:22 -04:00
Scott Idem
698459c207 Styles... 2025-05-15 16:32:10 -04:00
Scott Idem
e794a4c23a More config options. Work on click to expand Entry contents when in a list. 2025-05-15 16:02:54 -04:00
Scott Idem
53856a913b Adding more config options. Making things look better. 2025-05-15 15:07:37 -04:00
Scott Idem
72fb34e3f1 Work on passcodes and encryption 2025-05-15 13:03:43 -04:00
Scott Idem
dae482906d More style fixes 2025-05-15 10:34:14 -04:00
Scott Idem
ffa33b5fa0 Improved for showing the person's name 2025-05-14 21:24:25 -04:00
Scott Idem
211945828d Minor style fix for mobile 2025-05-14 20:55:28 -04:00
Scott Idem
2be4da1172 Note about iframes 2025-05-14 19:59:23 -04:00
Scott Idem
c46c670e87 Mostly layout and style clean up 2025-05-14 19:57:22 -04:00
Scott Idem
372cafeb01 Now with extra save buttons 2025-05-14 18:02:24 -04:00
Scott Idem
42a813dc52 Things look cleaner now 2025-05-14 17:11:21 -04:00
Scott Idem
238757782b Moving things around... trying to keep it clean 2025-05-14 16:47:48 -04:00
Scott Idem
e96675174f Minor changes 2025-05-13 17:54:23 -04:00
Scott Idem
24e1190874 Minor clean up 2025-05-13 17:18:40 -04:00
Scott Idem
5232c88a45 Lots of work making CodeMirror work. It is in a mostly useable state now. 2025-05-13 17:06:10 -04:00
Scott Idem
b5642583c7 Trying out CodeMirror. I think I like it? Use it in readonly mode for view. Wrapping up for the day. 2025-05-12 19:56:56 -04:00
Scott Idem
3b38c31ba0 Real work on getting CodeMirror working. I at least have a fancy highlighter and basic editor. 2025-05-12 19:00:19 -04:00
Scott Idem
52be5ac1f7 Trying not to break anything while I work on using CodeMirror. I hope. 2025-05-12 18:30:58 -04:00
Scott Idem
247773235e Adding first iteration of search... 2025-05-12 13:47:28 -04:00
Scott Idem
b2b006249b The Event and Event Session should now be using the new IDB save method and related updates. Need to TEST TEST TEST more now. 2025-05-09 16:49:38 -04:00
Scott Idem
e008950411 Lots of work trying to use the new core db_save_ae_obj_li__ae_obj() function. 2025-05-09 16:12:46 -04:00
Scott Idem
5ef6d7dc0c More updates related to replacing the forEach loop with the for loop. Async... await... 2025-05-09 14:32:45 -04:00
Scott Idem
dc7da8c930 More general code clean up. Improvement to IDB save for Event. 2025-05-09 14:13:59 -04:00
Scott Idem
8f1f7bfa7e Code clean up. Journal IDB save has been updated to properly await. 2025-05-09 13:44:19 -04:00
Scott Idem
8c81f6f65e Work on making the Journal Entry saving better. 2025-05-08 19:37:15 -04:00
Scott Idem
0e249b2e6d Work to improve encryption and decryption 2025-05-08 14:34:38 -04:00
Scott Idem
74cf6b7ca8 A bit less debug with new API fetch pass 2025-05-07 19:54:20 -04:00
Scott Idem
df3b455c18 Less debug. Code clean up. And it uses the Svelte fetch function!! I think it just works... 2025-05-07 19:49:27 -04:00
Scott Idem
7af1d41ff3 The POST function no longer needs Axios. It will now retry X times if it fails. 2025-05-07 18:46:39 -04:00
Scott Idem
2ff7b4fd70 Delete will now retry and no longer needs Axios. Code clean up. Bug fix when deleting a Journal Entry. 2025-05-07 18:12:43 -04:00
Scott Idem
86221e6aec Axios should not longer be needed. 2025-05-07 16:34:39 -04:00
Scott Idem
cdc4ee1af9 Updating the API functions to make the retry. 2025-05-07 16:17:34 -04:00
Scott Idem
7ea65ee495 Package updates 2025-05-07 14:53:53 -04:00
Scott Idem
4b939d0ca0 Minor style fixes 2025-05-07 14:14:27 -04:00
Scott Idem
5bfb3892e6 Bug fix when creating new Entry 2025-05-06 20:10:10 -04:00
Scott Idem
de740e1e37 Now many things are hidden away in the new Journal Entry menu. 2025-05-06 20:01:28 -04:00
Scott Idem
89822ef540 Progress on moving must buttons and secondary things to the Journal Entry menu. 2025-05-06 18:17:39 -04:00
Scott Idem
13b8255055 Work on encryption and other clean up. 2025-05-06 17:07:10 -04:00
Scott Idem
a27fdd5fc4 Wrapping up for the day. History field mostly works now. But has bugs! 2025-05-05 17:49:44 -04:00
Scott Idem
66b122dca5 More work on encryption of content and history. 2025-05-05 17:10:19 -04:00
Scott Idem
0b61596833 Updates to make Journals more secure 2025-05-02 13:01:49 -04:00
Scott Idem
8f515e034b Bug fix for checking if caches is expired 2025-05-02 10:58:15 -04:00
Scott Idem
77c53065bc Pulling out parts into separate files. Also minor clean up. 2025-05-02 10:48:09 -04:00
Scott Idem
f2751cbaf9 Minor wording and style changes 2025-05-01 19:14:06 -04:00
Scott Idem
c6d21fadc2 More work on making the loading process better 2025-05-01 18:59:37 -04:00
Scott Idem
9687fe0c90 Improvements to access type and sign in and out process. 2025-05-01 17:09:06 -04:00
Scott Idem
63cb0f2969 Minor update 2025-05-01 14:56:44 -04:00
Scott Idem
81e322b98b Version bump 2025-04-30 19:16:44 -04:00
Scott Idem
70cf91b9ee Added notes 2025-04-30 18:59:39 -04:00
Scott Idem
122669aabf Style improvements 2025-04-30 18:52:05 -04:00
Scott Idem
9e4a67941e Saving work... 2025-04-30 18:25:02 -04:00
Scott Idem
4906e45b61 More clean up. Set the z index. 2025-04-30 17:44:55 -04:00
Scott Idem
15cb0aa0c5 Got themes working again. 2025-04-30 17:30:06 -04:00
Scott Idem
d7a1c03beb Minor button background change 2025-04-30 15:18:47 -04:00
Scott Idem
a76cc93acf Lots of work and testing on the debug and page load process. 2025-04-30 15:16:02 -04:00
Scott Idem
58bf335502 Added a third check for managers. Because why not. Wrapping up for the day. 2025-04-29 20:37:51 -04:00
Scott Idem
d256abc9b3 Working on more streamlined page load process 2025-04-29 20:15:37 -04:00
Scott Idem
007eb4ec9f Bug fix for attaching files to a BB Post. 2025-04-29 14:33:17 -04:00
Scott Idem
f2384f2714 Hopefully improved bug fix for IDAA members. 2025-04-29 13:31:52 -04:00
Scott Idem
be9c67e667 Work on cache handling 2025-04-29 12:31:21 -04:00
Scott Idem
6094d5a79d Now wrapping up... 2025-04-28 18:14:21 -04:00
Scott Idem
cbb73ed3fd Last minute clean up before wrapping up for the day. 2025-04-28 17:37:36 -04:00
Scott Idem
12167a3bc6 Now with actual encryption! 2025-04-28 17:18:29 -04:00
Scott Idem
8c3f05a2ed Work on encryption 2025-04-28 13:06:27 -04:00
Scott Idem
b5a4a38514 Bug fix for new Journal not linked to person_id 2025-04-28 11:59:38 -04:00
Scott Idem
6314d8f6e6 Now with first version of encryption! 2025-04-26 12:27:44 -04:00
Scott Idem
9bfb6580f2 More fixes related to permissions and Novi for IDAA 2025-04-25 16:36:21 -04:00
Scott Idem
f1dc6bd3dc Things are working better with the version changes. 2025-04-25 16:03:16 -04:00
Scott Idem
15ee3da76d Working on annoying version bug fixes. Also trying to make sure the show/hide lock menu works correctly. 2025-04-25 14:37:12 -04:00
Scott Idem
7c30b163b1 Improved sorting 2025-04-22 09:54:23 -04:00
Scott Idem
3ad1fa429a Minor change to info displayed 2025-04-18 15:44:44 -04:00
Scott Idem
d29d325af5 Turn off edit mode for safety 2025-04-18 15:28:05 -04:00
Scott Idem
1828b0f141 More improvements to the passcode lock process 2025-04-18 15:23:55 -04:00
Scott Idem
6d235d9159 Making the passcode entry faster 2025-04-18 15:00:16 -04:00
Scott Idem
ccba530223 Cleaned up the page loading for IDAA section 2025-04-18 14:01:51 -04:00
Scott Idem
1e099a275b More better better msg 2025-04-18 13:54:24 -04:00
Scott Idem
622e0db3d7 Better access denied warning message. 2025-04-18 13:49:33 -04:00
Scott Idem
fb937290eb Making a better version check and refresh/reload 2025-04-18 13:38:47 -04:00
Scott Idem
35b4079b90 Minor bug fixes 2025-04-18 11:02:34 -04:00
Scott Idem
204f2ed988 Package updates and minor clean up. 2025-04-18 10:44:06 -04:00
Scott Idem
3d2ff02f8c More package updates and testing 2025-04-18 10:25:10 -04:00
Scott Idem
1f10b64d13 Package updates 2025-04-18 10:11:47 -04:00
Scott Idem
f55931d0e3 Minor fix if PATCH fails 2025-04-14 21:01:56 -04:00
Scott Idem
a851c5fe64 Now with ability to set archive on datetime. Need to fix the timezone though. 2025-04-14 20:05:14 -04:00
Scott Idem
d274e37e37 Minor bug fixes 2025-04-14 18:52:59 -04:00
Scott Idem
3a47b77641 Work on hiding certain entries by default. Ability to append to an entry. 2025-04-14 18:33:59 -04:00
Scott Idem
2133fa520e Work on placement 2025-04-11 18:12:58 -04:00
Scott Idem
c4c657081f Possible bug fix for Svelte trigger loop 2025-04-11 17:36:13 -04:00
Scott Idem
c29cee4511 Lots of work on the new global app menu. 2025-04-11 17:16:14 -04:00
Scott Idem
3df0739e96 Working on new Aether App Menu. 2025-04-11 15:13:00 -04:00
Scott Idem
bb84b4bbb4 Adding a Change Password option. 2025-04-11 13:04:03 -04:00
Scott Idem
deef453580 Messing with Journal colors 2025-04-11 12:19:17 -04:00
Scott Idem
9adcc6d54c Making it easy to move an Entry to a different Journal. 2025-04-11 12:01:13 -04:00
Scott Idem
bd1ceee1ad Added check in case the Journal Entry creation fails 2025-04-09 19:50:49 -04:00
Scott Idem
2515ba5577 Cleaned up Journal Entry update functions 2025-04-09 16:18:56 -04:00
Scott Idem
3587258ec7 Working on saving journal entry edits 2025-04-09 15:45:38 -04:00
Scott Idem
b505424c9d Improved the way saving and updating of Journal Entry records work. 2025-04-09 13:43:01 -04:00
Scott Idem
adda3e79c5 Less other debug while working on Journals 2025-04-09 13:11:09 -04:00
Scott Idem
e1848dddb5 Work on Dexie liveQuery for Journals 2025-04-09 12:40:00 -04:00
Scott Idem
10c3e34b38 Improved sign in process and UI 2025-04-09 12:09:41 -04:00
Scott Idem
0d8b47af10 Now with the ability to look up user email and send user auth key. 2025-04-08 15:51:05 -04:00
Scott Idem
73eee7b8ff Trying to fix a bug when locking the quick access. It does not seem to stick. This ia a problem that needs to be fixed. For now I think I have a temporary workaround. 2025-04-04 19:12:32 -04:00
Scott Idem
b967eed0a5 Lots of work on user person security. Also some general clean up. Wrapping up for the day. 2025-04-04 18:31:31 -04:00
Scott Idem
6e41833f82 Improved styles with colors. More config options. Other updates. 2025-04-03 19:19:03 -04:00
Scott Idem
e022645f64 Now with a description and basic color scheme. Saving before making some more changes. 2025-04-03 18:27:59 -04:00
Scott Idem
2ab026611f More style work 2025-04-03 17:01:29 -04:00
Scott Idem
32553ca429 Style improvements 2025-04-03 16:51:44 -04:00
Scott Idem
98676dffd9 Style improvements 2025-04-03 16:13:18 -04:00
Scott Idem
ca2c5c7baf Fixed height bug when viewing Entry 2025-04-03 15:36:09 -04:00
Scott Idem
f59a159712 Minor change... 2025-04-02 17:48:55 -04:00
Scott Idem
ee5bd83673 Making things look better 2025-04-02 17:46:06 -04:00
Scott Idem
14d68e2a5d Never ending style tweaks 2025-04-02 17:31:50 -04:00
Scott Idem
bd4de70ac8 More style 2025-04-02 17:21:59 -04:00
Scott Idem
2575a57ba7 Work on overall styles 2025-04-02 17:19:38 -04:00
Scott Idem
304c1e4123 Improved copy function with rich text 2025-04-02 16:36:10 -04:00
Scott Idem
56502981d3 More style improvements when in edit mode 2025-04-02 16:08:04 -04:00
Scott Idem
376f77ad8f Style improvements for small screens 2025-04-02 16:00:08 -04:00
Scott Idem
e653d4d92a Lots of updates. And now with the ability to copy the rendered HTML version! 2025-04-02 15:30:36 -04:00
Scott Idem
7fa0d5cc5b General clean up and improvements. 2025-04-02 13:42:14 -04:00
Scott Idem
b9f4189a95 Code clean up 2025-04-02 13:00:29 -04:00
Scott Idem
b4f2be0f13 Now with the ability to clone template entries 2025-04-02 12:56:08 -04:00
Scott Idem
0e72d27dbd Saving work from last night 2025-04-02 11:24:08 -04:00
Scott Idem
7f1759ed4b Style fixes 2025-04-01 19:24:46 -04:00
Scott Idem
f6449b48e6 General improvements and minor fixes 2025-04-01 19:11:16 -04:00
Scott Idem
a41ecb45a9 Bug fix related to saving entries with mixed changes 2025-04-01 18:42:32 -04:00
Scott Idem
d393ed2c7a More work on making the Journal's module actually useful 2025-04-01 17:40:33 -04:00
Scott Idem
f423345e42 Minor temp bug fix 2025-03-28 17:27:25 -04:00
Scott Idem
aaaa0da1f9 Minor changes 2025-03-28 17:17:56 -04:00
Scott Idem
8539ccc2f4 Working on update a lot of pages using the ae_loc hub section. 2025-03-28 14:33:26 -04:00
Scott Idem
1059cf5683 Can now edit some basic Journal cfg and other clean up 2025-03-24 17:50:10 -04:00
Scott Idem
b37db30368 Working ability to create new Journals 2025-03-24 16:33:12 -04:00
Scott Idem
d424ec24c1 Again 2025-03-24 15:25:59 -04:00
Scott Idem
b393427651 Minor documentation update 2025-03-24 15:24:35 -04:00
Scott Idem
a19f6fe97e Merge remote-tracking branch 'refs/remotes/origin/ae_app_svelte_kit_v2_dev' into ae_app_svelte_kit_v2_dev 2025-03-24 15:23:26 -04:00
Scott Idem
d8d6728659 Update to documentation 2025-03-24 15:19:49 -04:00
Scott Idem
a9351b4143 More package updates 2025-03-24 15:02:23 -04:00
Scott Idem
00f3d6087f Package updates 2025-03-24 14:43:36 -04:00
Scott Idem
4715d13dda Wrapping up for the day. 2025-03-21 19:13:30 -04:00
Scott Idem
f45540a31f Trying to save changes 2025-03-21 17:16:07 -04:00
Scott Idem
db762a3c91 Journals is working pretty well. Most basic functions work now. 2025-03-21 17:08:58 -04:00
Scott Idem
8826ebf92f Lots of work on the Journals 2025-03-21 16:26:01 -04:00
Scott Idem
35354a9d0f Work on the new Journals. Just saving things while they are working well. 2025-03-21 11:03:38 -04:00
Scott Idem
596986afb7 Work on the new Journals module. Journal entries are now partially viewable. 2025-03-20 11:59:48 -04:00
Scott Idem
7fde0abb16 Renamed file to possibly use later 2025-03-20 09:30:07 -04:00
Scott Idem
0f22ad0584 Better handling of file types (extensions) 2025-03-18 17:33:27 -04:00
Scott Idem
253a1c59d6 Minor style change 2025-03-18 17:01:42 -04:00
Scott Idem
42ac7911f2 Using the hosted file select method is 90% working. It still needs to create a new hosted_file_link record. 2025-03-18 16:37:19 -04:00
Scott Idem
6749ecc4c3 Mostly style fixes 2025-03-18 15:23:41 -04:00
Scott Idem
c96e59c660 Getting ready to make the new hosted file management and tools live on dev. Then on prod... 2025-03-18 15:01:30 -04:00
Scott Idem
39d0a210f3 Lots of work on file management and video processing (ffmpeg). 2025-03-17 19:38:38 -04:00
Scott Idem
ab055beaff Modified the root style for overflow auto 2025-03-16 04:16:11 -04:00
Scott Idem
b62a267ee8 Wrapping up for the night at 4 AM. Made lots of progress with the Journals module. Should have saved more often. 2025-03-16 04:04:58 -04:00
Scott Idem
d8020b3d77 Saving my work beforeI rename the notes module to journals module. Finally. 2025-03-15 23:21:06 -04:00
Scott Idem
183d431e1d Doing some more work on the new Notes objects. 2025-03-15 21:24:25 -04:00
Scott Idem
f4bd387ed9 Package updates 2025-03-06 18:49:27 -05:00
Scott Idem
8acb4a8c7b Minor clean up 2025-03-04 18:06:05 -05:00
Scott Idem
87b1859b0b Version bump 2025-03-04 17:36:39 -05:00
Scott Idem
643edbd50a Package updates round 3ish. Other minor code clean up. 2025-03-04 17:31:40 -05:00
Scott Idem
3719739b57 Package updates round 2 2025-03-04 15:42:03 -05:00
Scott Idem
75c7a29ae4 Package updates round 1 2025-03-04 15:32:43 -05:00
Scott Idem
33bc99c2bd Bug fix for saving a post comment and the post data not being ready. 2025-03-04 12:26:45 -05:00
Scott Idem
7e92613536 Trying to get the email notifications to include all the post data. 2025-03-04 11:33:52 -05:00
Scott Idem
6e330a639e Minor bug fix 2025-02-26 11:11:56 -05:00
Scott Idem
89b14c877e Testing emails.... 2025-02-12 19:19:39 -05:00
Scott Idem
3078e33b97 Minor changes 2025-02-12 18:10:49 -05:00
Scott Idem
ed89776a60 Minor changes to style and related for IDAA recovery meeting status 2025-02-04 12:34:49 -05:00
Scott Idem
75d7a502f0 Adding the new event status for IDAA. 2025-02-04 12:01:32 -05:00
Scott Idem
fc517540dc Wrapping up for the day. 2025-01-28 18:57:14 -05:00
Scott Idem
cb19031989 Less debug 2025-01-28 18:49:34 -05:00
Scott Idem
bdea373c7f Better error handling if the site domain is not found or returned. 2025-01-28 18:47:30 -05:00
Scott Idem
acff856e25 Trying to optimize the initial fetch calls when loading. 2025-01-28 18:12:01 -05:00
Scott Idem
a9c96c905a Fade out long text 2025-01-28 16:25:07 -05:00
Scott Idem
c62507d484 Sort of bug fix and improvements for IDAA BB notifications and loading a post based on the URL param. 2025-01-28 11:51:27 -05:00
Scott Idem
db6b481983 Update to allow IDAA members to attach file. 2025-01-21 10:12:12 -05:00
Scott Idem
8b309d7b04 Forgot to save my work earlier...? 2025-01-15 22:05:41 -05:00
Scott Idem
6f11820857 Getting some basic things ready for CHOW 2025 2025-01-15 16:32:18 -05:00
Scott Idem
9dc4540329 Now images will show inline when viewing a post. 2025-01-14 16:48:24 -05:00
Scott Idem
7414cba165 Minor changes 2025-01-14 16:05:00 -05:00
Scott Idem
5d6df59ee1 Refresh of lookups is more likely. 2025-01-13 15:39:10 -05:00
Scott Idem
321c48513f Updates to clean up the lookups for time zones, countries, and subdivisions. 2025-01-13 15:28:24 -05:00
Scott Idem
8d4c770f19 Updated packages previously. Minor changes 2025-01-09 10:12:20 -05:00
Scott Idem
106e655574 Minor changes 2025-01-08 17:41:07 -05:00
Scott Idem
e921f3a785 More clean up for IDAA. Mostly style related. 2025-01-08 16:10:51 -05:00
Scott Idem
aa893b5ea6 Various changes after call with IDAA. Mainly style changes. 2025-01-08 15:25:30 -05:00
Scott Idem
b64f4b01ad Minor style change 2025-01-08 12:21:26 -05:00
Scott Idem
f8e88b0355 Trying to improve the hosted file manage. 2025-01-07 20:22:44 -05:00
Scott Idem
641c6b28c3 Working on new and improved video clipping utility. 2025-01-07 13:49:30 -05:00
Scott Idem
ff6e240a37 Better notes on if the home IP changes. 2025-01-06 16:42:54 -05:00
Scott Idem
ea115b6ac1 Less debug showing 2025-01-06 16:26:33 -05:00
Scott Idem
92af1a5962 Trying to track down a bug that is happening when run in Docker. Pretty sure it is related to a DNS resolution thing. The GET Object function shows better information if something goes wrong. 2025-01-06 16:24:47 -05:00
Scott Idem
b64e7a6b8a Wrapping up for the day. It is pretty much Christmas... 2024-12-24 12:55:18 -05:00
Scott Idem
69abe2e906 More IDAA Novi style changes. Less green for the options section. 2024-12-24 11:36:50 -05:00
Scott Idem
91e6556933 More style improvements for Novi 2024-12-24 11:28:26 -05:00
Scott Idem
f89183685a Lots of style clean up for buttons in Novi. Various bug fixes. Clean up of initial recovery meeting loading. 2024-12-24 11:18:37 -05:00
Scott Idem
47394d67ca Minor but annoying style fixes. Still more TipTap textareas to fix with w-full. Should that be the default? 2024-12-23 18:12:55 -05:00
Scott Idem
5977df38a7 Wrapping up for the day. Minor fixes to styling. 2024-12-23 18:04:04 -05:00
Scott Idem
f024ccbe0c Minor clean up 2024-12-23 17:39:46 -05:00
Scott Idem
066bd66178 Package updates and trying to fix a reversion with reactivity within the same page file. The Dexie liveQuery does not seem to refresh correctly when on the same page. It does show the update on child components. Even if nearly identical. 2024-12-23 17:23:36 -05:00
Scott Idem
24931de306 More work on tmp sorting fields 2024-12-11 16:18:28 -05:00
Scott Idem
3fef19488c Work on better sorting for the IDAA BB posts. Using generated tmp field. Date formats standardized. 2024-12-11 15:34:44 -05:00
Scott Idem
eee8a67235 Package updates. Point IDAA Recovery Meetings to prod server instead of dev. 2024-12-11 13:32:03 -05:00
Scott Idem
c96fb7bf00 Package updates 2024-12-11 13:17:00 -05:00
Scott Idem
3946e17300 Things are 98% ready to go for the new IDAA BB. 2024-12-11 13:16:00 -05:00
Scott Idem
c63ef94b77 Last minute changes to prep the new BB to go live soon. 2024-12-11 12:09:39 -05:00
Scott Idem
0f29682874 Improvements and fixes related to the BB. Increased default limit. 2024-12-11 11:44:54 -05:00
Scott Idem
9813d0544f Minor updates for IDAA's Novi site. 2024-12-10 18:51:47 -05:00
Scott Idem
af743f6e73 Last minute work for going live on Novi. Modified how the recurring text is generated. 2024-12-05 15:09:01 -05:00
Scott Idem
90c5b9414f More pages updated to Runes mode 2024-12-03 17:45:31 -05:00
Scott Idem
aaa6eaa433 Cleaning up the main layout. 2024-12-03 16:51:31 -05:00
Scott Idem
1bdd2aec65 Moving more pages to Runes mode. 2024-12-03 16:22:21 -05:00
Scott Idem
747e9fdb26 Setting the type to number for log_lvl. 2024-12-03 15:29:19 -05:00
Scott Idem
b3dfb5deec Starting to switch pages to Runes mode.... 2024-12-03 15:27:28 -05:00
Scott Idem
c34e62a0c5 Working on formatting for IDAA Novi site. 2024-12-03 14:48:59 -05:00
Scott Idem
6d94583885 More work on the new rich text editor... 2024-12-03 12:39:30 -05:00
Scott Idem
971ffbad02 Code clean up. Fewer warnings... 2024-12-03 11:36:24 -05:00
Scott Idem
0032d52ff9 Working on things after the Svelte upgrade. Got the new editor toolbar working again. I think I needed to reinstall the shadcn-svelte packages. 2024-12-03 11:15:08 -05:00
Scott Idem
e4797e178d More package and configuration related updates. 2024-12-03 10:28:56 -05:00
Scott Idem
c6631e221c More work on the new rich text editor. It may have a few bugs still. 2024-12-02 19:24:00 -05:00
Scott Idem
44907bc01f Getting the new new new rich text editor working. I think it is working... Yay Shad Editor that uses TipTap and Shadcn. 2024-12-02 18:56:45 -05:00
Scott Idem
940a7e9a21 Changes after major Svelte update. 2024-12-02 16:39:33 -05:00
Scott Idem
1f6793927b Upgrade to Svelte 5 and Vite 6!!! 2024-12-02 16:16:18 -05:00
Scott Idem
aeeb08ab36 Minor package updates 2024-12-02 15:45:39 -05:00
Scott Idem
a0473cb50b Trying to install updated packages to get the ShadEditor working. I hope. 2024-12-02 15:43:46 -05:00
Scott Idem
b7515f0680 Trying ShadEditor... Not sure about this. 2024-12-02 15:13:00 -05:00
Scott Idem
058c88c766 Clean up and working on the rich text editor. Will probably swap my version out for something else... 2024-12-02 14:56:22 -05:00
Scott Idem
c0fe69ea73 Bug fix for the archive_id not getting reset when viewing the list of archives. 2024-12-02 09:41:43 -05:00
Scott Idem
12b761e127 Minor updates before going live with the archives. 2024-11-26 10:53:49 -05:00
Scott Idem
5796ff09a1 Package updates 2024-11-22 17:26:57 -05:00
Scott Idem
4c09edaceb Trying to get more of the Tiptap buttons to work. Why does only bold, italics, strike work??? 2024-11-22 17:19:00 -05:00
Scott Idem
dd8181ba0e Style improvement 2024-11-22 14:36:37 -05:00
Scott Idem
7ba11a104d More work on getting the BB ready for IDAA. Improved notification handling. Bug fixes related to creating posts and comments. 2024-11-22 14:07:27 -05:00
Scott Idem
1868adad99 Work on post and comment email notifications. 2024-11-22 13:13:44 -05:00
Scott Idem
6a79cb165d Work on permissions and code clean up 2024-11-22 10:44:12 -05:00
Scott Idem
42aa9d356f More work on authentication for staff and against the Novi UUIDs. 2024-11-22 10:32:46 -05:00
Scott Idem
2f4e707e58 General clean up. Nothing should be broken... 2024-11-21 20:12:50 -05:00
Scott Idem
fbb45d50a7 Work on styles based on permissions and related. 2024-11-21 19:33:19 -05:00
Scott Idem
24a75d509f Less debug 2024-11-21 18:19:50 -05:00
Scott Idem
5ff63aa267 Still working on the initial loading... 2024-11-21 18:00:33 -05:00
Scott Idem
7e3420ec4d More work on the initial loading of things... Looks cleaner. 2024-11-21 17:23:31 -05:00
Scott Idem
600bf7caa4 More work on the initial loading. 2024-11-21 17:03:47 -05:00
Scott Idem
d1f04e4dc3 Trying to clean up the initial loading... Checking the local storage and indexes and site keys. 2024-11-21 16:45:13 -05:00
Scott Idem
2ef3c8bab5 Hopefully better and easier to understand site key allow access permissions.... 2024-11-21 14:57:58 -05:00
Scott Idem
cffde76c88 Re-work of site permissions and Novi permissions. 2024-11-21 14:19:08 -05:00
Scott Idem
fcec4ed96f Minor changes. 2024-11-20 19:06:07 -05:00
Scott Idem
e4485f2bda Fixes for layout and recurring pattern. 2024-11-20 18:36:24 -05:00
Scott Idem
1d713db712 More package updates. Less warnings. 2024-11-20 16:17:16 -05:00
Scott Idem
ec837c415a More package updates. Bug fix for the shared events menu. 2024-11-20 16:05:29 -05:00
Scott Idem
b8b86a5561 Package updates 2024-11-20 15:46:38 -05:00
Scott Idem
125ce32ddd Less extra stuff... less log entries and warnings. 2024-11-20 15:40:50 -05:00
Scott Idem
f6378c1a23 Less iframe related logging 2024-11-20 15:24:30 -05:00
Scott Idem
6b60c14159 Finally really starting to move things to v2 of the CRUD API. 2024-11-20 15:11:57 -05:00
Scott Idem
fd602a46ac Getting rid of Axios. Using (Svelte) fetch for get_object()... 2024-11-20 12:56:11 -05:00
Scott Idem
e220653f45 Removing old files. 2024-11-19 19:36:44 -05:00
Scott Idem
acf0a169ab Cleaning up old files 2024-11-19 19:33:01 -05:00
Scott Idem
90a74e5ce7 Code clean up. 2024-11-19 19:24:55 -05:00
Scott Idem
9ded8f7c7e Minor changes. Was working on the query status updates. Wrapping up for the day. 2024-11-19 19:11:52 -05:00
Scott Idem
185709e67a Much better searching triggers for recovery meetings (events). 2024-11-19 19:03:44 -05:00
Scott Idem
bf29856f6b Standardizing how things are reloaded. Triggers again! 2024-11-19 18:15:45 -05:00
Scott Idem
e2d67087ce Improvements on styling for Novi. 2024-11-19 17:42:26 -05:00
Scott Idem
9a681455ca Starting to finalize the recovery meetings for going live in Novi. 2024-11-19 17:31:29 -05:00
Scott Idem
fea392eff7 Bug fixes for enable related fields. 2024-11-19 16:53:24 -05:00
Scott Idem
fc8ee53724 Sort of bug fix or improvement for showing the post data. 2024-11-19 15:41:57 -05:00
Scott Idem
89885f9571 Now with a return to archive list button 2024-11-19 14:15:30 -05:00
Scott Idem
b0ae649824 Work on permissions for iframes and Novi. 2024-11-19 14:05:06 -05:00
Scott Idem
9a1d022d5a Work on permissions for IDAA members in Novi 2024-11-19 13:48:51 -05:00
Scott Idem
31272a8985 Moved library files around. Trying to keep things organized... 2024-11-19 13:29:05 -05:00
Scott Idem
28880f3160 Novi iframe related. Style improvements. 2024-11-19 13:12:49 -05:00
Scott Idem
77cc152837 General improvement for archive content creation. Better show/hide for enable and hide fields. Novi and iframe related updates. 2024-11-19 13:02:05 -05:00
Scott Idem
d1f37af192 Updated Novi iframe HTML content. 2024-11-15 17:01:21 -05:00
Scott Idem
01e320e172 Work on Novi related permissions. Pulled from iframe URL. 2024-11-15 16:56:56 -05:00
Scott Idem
6b34b806b2 Dealing with iframes. postMessage to parent iframe when certain things are opened/viewed. 2024-11-15 16:21:37 -05:00
Scott Idem
53deb5edcc Added m4a to the list 2024-11-15 15:42:58 -05:00
Scott Idem
a3a8f107d3 General clean up. Work on using iframes and setting the height correctly. 2024-11-15 14:38:59 -05:00
Scott Idem
547845ed62 Work on the delete functions. General clean up. 2024-11-15 13:03:58 -05:00
Scott Idem
0fd06ef216 More clean up and bug fixes related to posts. Better enable, hide, limit, etc params. 2024-11-15 12:24:20 -05:00
Scott Idem
bd5c8a0867 More work on the delete related functions. 2024-11-15 12:00:39 -05:00
Scott Idem
6a390762d3 Work on "delete" functions for recovery meetings. 2024-11-15 11:26:47 -05:00
Scott Idem
40081c2804 Marking old files for deletion. Improvement to archive content grouping. 2024-11-15 10:35:04 -05:00
Scott Idem
26faf50058 Fixed bug that disabled things when saved.... Sort of. At least for posts it was disabling them. I need to test this more. Again... 2024-11-14 18:52:04 -05:00
Scott Idem
804fddcb5a More work on the BB and related notifications. 2024-11-14 17:52:42 -05:00
Scott Idem
789c0d651e More work on posts. Getting ready for notifications working... 2024-11-14 16:36:46 -05:00
Scott Idem
dfd43963bb Improved the linked content (files) list. 2024-11-14 15:17:40 -05:00
Scott Idem
8df0bdf0cc Working on the style 2024-11-13 20:04:50 -05:00
Scott Idem
53f2f5dbc5 Now with links count 2024-11-13 18:54:44 -05:00
Scott Idem
4fe6194450 Working on posts with linked content (hosted files). 2024-11-13 18:31:44 -05:00
Scott Idem
7040578ac5 Updates related to the time being in 24 vs 12 hour format. 2024-11-13 10:43:44 -05:00
Scott Idem
26e1c89f1b Added parse for JSON before updating. 2024-11-12 16:58:20 -05:00
Scott Idem
34bcd9dd2d The archives are looking better now. Mostly style and sorting improvements. 2024-11-12 16:20:18 -05:00
Scott Idem
fedbbe9ae2 Improved styling. Less debug. Other clean up. 2024-11-12 14:13:51 -05:00
Scott Idem
1e1a4f1016 Better permissions. Ability to sort the meeting results. Other clean up. 2024-11-12 13:15:19 -05:00
Scott Idem
5d8a3a782e Fixed show/hide for hidden and disabled records. 2024-11-08 16:37:42 -05:00
Scott Idem
791454b8fa Minor text fixes 2024-11-08 15:47:41 -05:00
Scott Idem
f4538720b2 Lots of little fixes. Can now save archives. 2024-11-08 15:43:47 -05:00
Scott Idem
e6a9a5ceab Making the URL update when viewing and closing specific content. Other clean up. 2024-11-08 13:57:16 -05:00
Scott Idem
13d906428b Creating standardized functions. Mainly for create and delete. 2024-11-08 12:47:24 -05:00
Scott Idem
4fe04d9c3f Permission update 2024-11-08 11:30:58 -05:00
Scott Idem
abcd5c54c2 Can now save and delete archive content. This includes the hosted file. It still needs some work though. The content delete should also remove the hosted file. 2024-11-08 11:25:19 -05:00
Scott Idem
b93cacdfcc Wrapping up for the day. File uploads and deletes pretty much work now. Need to figure out whey the form does not refresh or just ignore? 2024-11-07 19:34:57 -05:00
Scott Idem
079ec80fbd Now with the ability to do basic archive content updates. The media player works. 2024-11-07 15:53:12 -05:00
Scott Idem
d3609764e3 Post and post comments now save correctly. The viewed post does not always reflect the update though. 2024-11-07 14:10:10 -05:00
Scott Idem
b381cbbc9e More clean up. Probably wrapping up for the day. Why are so many people so dumb, or they just don't seem to care. 2024-11-06 17:08:39 -05:00
Scott Idem
7e6b00c0eb Making the admin options consistent 2024-11-06 16:42:47 -05:00
Scott Idem
594074a1dd Minor changes 2024-11-06 15:41:21 -05:00
Scott Idem
6a609646cc Minor updates related to textarea 2024-11-06 15:38:12 -05:00
Scott Idem
94babed805 Minor fix for show/hide admin 2024-11-06 15:27:47 -05:00
Scott Idem
14d4be848a Cleaning things up before going back to making the forms functional. 2024-11-06 15:25:17 -05:00
Scott Idem
d0c4ef2179 Now with edit content partially ready. 2024-11-06 14:23:38 -05:00
Scott Idem
80965167a5 We can now see a list of archive content and it doesn't look terrible. 2024-11-06 13:15:16 -05:00
Scott Idem
b9698615be Now the list of archives shows. 2024-11-05 17:05:46 -05:00
Scott Idem
4d667d57a4 More work on getting post and comment editing working. 2024-11-05 16:33:24 -05:00
Scott Idem
cf997601f4 Work on editing of posts and posts comments. 2024-11-05 15:59:19 -05:00
Scott Idem
ac7016bb3e Starting real work on the IDAA BB (posts). Can at least view them and their comments now. 2024-11-05 12:37:46 -05:00
Scott Idem
4d2a78f80f Minor clean up 2024-11-04 17:31:39 -05:00
Scott Idem
23b82291a0 Improving the rich text editor. It still has some issues. Undo and redo do not work consistently. Wrapping up for the day? 2024-11-04 17:30:58 -05:00
Scott Idem
63f5168015 Making things looks nicer. Various fixes. 2024-11-04 16:59:30 -05:00
Scott Idem
ee7f981e2e Minor text fix 2024-11-04 15:34:44 -05:00
Scott Idem
deac79b861 Lots of work related to the IDAA Recovery Meetings. Better pulling in off lookup list data. 2024-11-04 15:29:16 -05:00
Scott Idem
2d047f5a10 Now able to save the HTML text from tip tap element. 2024-11-01 17:52:39 -04:00
Scott Idem
8ec01a1d64 More moving of files around. Hopefully nothing is broken again. 2024-10-30 18:40:47 -04:00
Scott Idem
26b3c53847 Things seem to be working. It said it was overwriting files though??? 2024-10-30 18:30:30 -04:00
Scott Idem
4f65374d7e More moving of files around. 2024-10-30 18:24:12 -04:00
Scott Idem
19ce353a26 Moving files around. Breaking links to sessions and other things. 2024-10-30 18:14:30 -04:00
Scott Idem
79e83c26a7 Ignore warnings 2024-10-30 16:33:27 -04:00
Scott Idem
80e7cecbf1 One last change 2024-10-28 18:10:37 -04:00
Scott Idem
a211570af9 Wrapping up for the day. Finally working on IDAA related events some more. 2024-10-28 18:07:06 -04:00
Scott Idem
2ebb411905 Package updates 2024-10-28 14:47:24 -04:00
Scott Idem
9c3fe62724 Updating the permissions. Now with show/hide email access link. 2024-10-28 14:37:24 -04:00
Scott Idem
07c0b569ca Now with the ability to log activities. Yay... 2024-10-23 01:53:25 -04:00
Scott Idem
cc084a32cf Fix for making the landscape images go full height and width. 2024-10-22 13:57:12 -04:00
Scott Idem
cfd78145f6 Quick package updates. 2024-10-20 16:53:24 -04:00
Scott Idem
b9d4f47f3a Got the screen saver mostly working now. Now config options yet though. 2024-10-20 16:51:22 -04:00
Scott Idem
2d644aafe8 Better sorting of presentations and session files. Slight style changes. 2024-10-18 19:14:20 -04:00
Scott Idem
295fa1b49a Finalizing things for LCI. The poster stuff looks better now. 2024-10-18 18:23:24 -04:00
Scott Idem
789d4f96e0 Minor clean up 2024-10-18 16:54:16 -04:00
Scott Idem
5da209ee1c Now with more download and copy link options. 2024-10-18 15:51:52 -04:00
Scott Idem
488031f90c The Launcher is looking pretty good for posters. 2024-10-18 13:21:35 -04:00
Scott Idem
de24f0dc80 Wrapping up for the night. Better alerts and things with the correct timezone... 2024-10-17 20:46:39 -04:00
Scott Idem
172763bc26 Working on device status and alerts. 2024-10-17 18:26:56 -04:00
Scott Idem
67b01c5884 Now with status updates for devices. 2024-10-17 12:56:10 -04:00
Scott Idem
a0d5a97d48 More npm commands 2024-10-17 11:24:24 -04:00
Scott Idem
7d9950fd70 Package and documentation updates. 2024-10-17 11:21:54 -04:00
Scott Idem
a2b7dd9450 Moved some files around. Improved style. No longer using other pres_mgmt nav component. Added links to the locations. 2024-10-17 10:36:03 -04:00
Scott Idem
63fea17472 Wrapping up for the day. Lots of improvements. More menu options for location and device related. 2024-10-16 18:48:14 -04:00
Scott Idem
590d274910 Better handling of the IDB refreshes and updates. 2024-10-16 16:21:17 -04:00
Scott Idem
deac7bd574 More work on making the locations and devices more useful. 2024-10-16 15:27:25 -04:00
Scott Idem
3c957692c3 Making progress with the new locations page and devices related. 2024-10-16 13:14:40 -04:00
Scott Idem
67a4fbe17e General code clean up. A lot less _random! 2024-10-16 10:28:47 -04:00
Scott Idem
9160591fc4 Making things look consistent on each page. 2024-10-15 18:57:02 -04:00
Scott Idem
2ae3fb07eb The last selected report is now sticky. 2024-10-15 17:21:22 -04:00
Scott Idem
1c7518ad3f Now with a working recent files report. Other improvements. 2024-10-15 17:01:43 -04:00
Scott Idem
9a951ee365 New reports for large files. Better file query as well. 2024-10-15 15:27:11 -04:00
Scott Idem
1189e2e877 Improvements to reports 2024-10-15 14:20:19 -04:00
Scott Idem
79a74db4ae Improving the reports 2024-10-15 11:11:33 -04:00
Scott Idem
7450edfdcf Now done for the night. 2024-10-14 20:05:57 -04:00
Scott Idem
4ec440146e Wrapping up for the night. 2024-10-14 19:11:05 -04:00
Scott Idem
e2a510ceaa Slow work on creating a textarea element thing. 2024-10-14 19:02:56 -04:00
Scott Idem
115751bcd7 Code updates for IDAA migration 2024-10-14 14:25:29 -04:00
Scott Idem
ca2658a324 Just package updates. Nothing should have broken... 2024-10-11 17:09:31 -04:00
Scott Idem
338c9b9b23 Making things look nicer still. 2024-10-10 16:44:23 -04:00
Scott Idem
5037579abe Cleaning things up. Making it easier to find things. 2024-10-10 16:02:47 -04:00
Scott Idem
980e8850be More clean up. 2024-10-10 13:14:38 -04:00
Scott Idem
ec2c37b715 Cleaned up the code. A lot less _random!! 2024-10-10 13:13:21 -04:00
Scott Idem
9093cc9957 Renamed some things to remove the _random part. 2024-10-10 12:56:22 -04:00
Scott Idem
79bc148e85 We can now download files remotely! 2024-10-09 20:47:46 -04:00
Scott Idem
6694ec92c7 Renamed a bunch of functions! 2024-10-08 16:34:06 -04:00
Scott Idem
c130e82fd0 Minor code clean up 2024-10-08 16:24:06 -04:00
Scott Idem
bd48596176 Better loading of presentations, presenters, and files. 2024-10-08 16:22:38 -04:00
Scott Idem
816db5281d Small permission view change 2024-10-08 15:03:06 -04:00
Scott Idem
d5ac26f0ef Use a list of file purpose options per event. 2024-10-08 14:38:39 -04:00
Scott Idem
f9b3b3a0eb Minor change 2024-10-08 14:10:43 -04:00
Scott Idem
a6b9115865 Working on handling received messages. Open a session. 2024-10-08 12:13:27 -04:00
Scott Idem
0799f9a1ab Show and hide the main launcher menu 2024-10-07 18:47:41 -04:00
Scott Idem
c3f4832f48 Now with a better download file container thing. Can change open with OS. 2024-10-07 18:30:09 -04:00
Scott Idem
03e6117024 Work on being remotely controlled. 2024-10-07 15:58:56 -04:00
Scott Idem
ccb36ca953 Cleaning things up like always. 2024-10-07 15:29:20 -04:00
Scott Idem
2ac495af8e Cleaned up the show/hide location and launcher links. 2024-10-07 14:47:26 -04:00
Scott Idem
820c151750 First attempt to add Google Analytics to SvelteKit. This should be per site. 2024-10-07 14:19:32 -04:00
Scott Idem
aef469ad9d Wrapping up for the day. Lots of tedious work on the Launcher. 2024-10-04 18:23:47 -04:00
Scott Idem
b6cd3f59e5 Work on the dev env and building for production and staging. 2024-10-03 20:48:41 -04:00
Scott Idem
7b84e1c1fc More work on the site permissions. 2024-10-03 16:29:45 -04:00
Scott Idem
0f49afec12 Working on improved default permissions and allow control per site and site domains. 2024-10-03 16:04:00 -04:00
Scott Idem
d6d4c88728 Various changes... Should have saved last night. Also send_email should NOT default to test mode! 2024-10-03 12:45:28 -04:00
Scott Idem
bb27942b52 Enable more security by default. 2024-10-02 18:21:01 -04:00
Scott Idem
89dd410aeb API bug fixes. Clean up. New util functions. Highlight times for newer files. 2024-10-02 17:32:36 -04:00
Scott Idem
1004104de0 Not sure... 2024-10-02 13:55:33 -04:00
Scott Idem
0e7df79bab Trying to fix things... 2024-10-02 13:33:17 -04:00
Scott Idem
581749ff41 The IDAA Recovery Meetings view and edit now display mostly correct. They still need work though. 2024-10-02 13:13:23 -04:00
Scott Idem
a189a1c336 Now the enable and hide buttons work. 2024-10-01 18:47:08 -04:00
Scott Idem
1c1845280b The search is working better now. Also better and less debug logging. 2024-10-01 18:09:27 -04:00
Scott Idem
fad58bf26f More general clean up. Making event queries easier to use and understand. 2024-10-01 16:59:08 -04:00
Scott Idem
47e9f9f5a1 General clean up. Improved event search and listing for IDAA. 2024-10-01 16:08:31 -04:00
Scott Idem
cca43b957a The event search now mostly works. 2024-10-01 13:36:50 -04:00
Scott Idem
d7284d5010 Improvement and updates to the event list loading and showing. 2024-10-01 11:28:50 -04:00
Scott Idem
35c901f144 Started pulling in the IDAA Bulletin Board code 2024-09-27 18:55:24 -04:00
Scott Idem
58928d07eb Moving files around 2024-09-27 17:42:46 -04:00
Scott Idem
6a8148228f Organizing things better. Hopefully nothing is broken!! Still need to move the event session related. 2024-09-27 17:21:57 -04:00
Scott Idem
d6c26e7511 The initial migration for IDAA Recovery Meetings. Progress 2024-09-27 15:20:26 -04:00
Scott Idem
495dd0e6d9 Trying to make the slightly newer event session search page load consistently. 2024-09-27 13:08:31 -04:00
Scott Idem
25a28d4ff6 Moved the AE Utilities functions 2024-09-27 10:46:28 -04:00
Scott Idem
894c84b857 Revert back to older Session Search page. 2024-09-26 19:12:46 -04:00
Scott Idem
8b6f171506 Cleaned up links. Added links in the Launcher to go back to the session search, location view, and session view. 2024-09-26 19:00:32 -04:00
Scott Idem
5311e4704f Work on the Launcher and how a "group" presenter is shown. 2024-09-26 18:37:09 -04:00
Scott Idem
0f40629bfe Done with clean up for the night. Still need to rename all of the shared functions that start with handle_. 2024-09-25 19:09:28 -04:00
Scott Idem
702da83ce5 Clean up before I wrap up for the day. 2024-09-25 18:54:45 -04:00
Scott Idem
32ca117b11 More work to prepare the new Archives, Posts, and Recovery Meetings 2024-09-25 18:44:49 -04:00
Scott Idem
283850e917 Adding in the Archives and Posts DB and functions to get ready for IDAA changes. 2024-09-25 17:55:42 -04:00
Scott Idem
514f36d998 Update to show the timestamps for bio updates. 2024-09-25 14:47:07 -04:00
Scott Idem
cc4446b549 More updates related to permissions. 2024-09-25 13:21:28 -04:00
Scott Idem
8005189bc4 Quick update for LCI 2024-09-25 12:54:58 -04:00
Scott Idem
41706fbcd7 Moving things around and preping things for IDAA. Archive, Posts, Recovery Meetings 2024-09-24 20:05:52 -04:00
Scott Idem
6dc89083ec Starting to import the IDAA files. 2024-09-24 18:32:19 -04:00
Scott Idem
267b5052c9 Moving more files around. 2024-09-24 18:23:22 -04:00
Scott Idem
9e6a7f0db0 Making the Launcher reload when link clicked to go there. 2024-09-24 17:15:06 -04:00
Scott Idem
476b303da4 Lots of work on the new Launcher. Also general reorganizing of files. 2024-09-24 16:55:23 -04:00
Scott Idem
bd2583fde3 Better scaling as the width changes 2024-09-23 16:42:53 -04:00
Scott Idem
80b394f16b Better sorting for files 2024-09-23 15:29:23 -04:00
Scott Idem
36283a215b Related bug fixes and updates for remote limits 2024-09-23 15:01:14 -04:00
Scott Idem
a92f8a486e Fixed the sorting of the files 2024-09-23 14:46:20 -04:00
Scott Idem
f9e156e862 Worked on new message for session page view. Wrapping up for the day. 2024-09-18 19:07:18 -04:00
Scott Idem
37c40ff4fc Changed the version number. Just because. 2024-09-18 18:16:14 -04:00
Scott Idem
2cf1ea39a8 Updated wording for default label. 2024-09-18 18:12:51 -04:00
Scott Idem
63f17e111b Improved the flexibility of the event files upload component. 2024-09-18 17:52:28 -04:00
Scott Idem
e46fa59a40 Require higher permission to edit 2024-09-18 17:07:27 -04:00
Scott Idem
ce476f9a65 Improvement in the showing of the edit button. 2024-09-18 17:03:49 -04:00
Scott Idem
04b549b874 I should have committed this earlier. Lots of changes to use LiveQuery and better menus. 2024-09-18 16:45:49 -04:00
Scott Idem
fd152cc27e Work on making wrappers and code clean up 2024-09-18 12:16:36 -04:00
Scott Idem
86b309f048 Making wrappers for some of the elements that will be expecting LQ variables directly. 2024-09-18 11:36:59 -04:00
Scott Idem
04fd046c53 Trying to get everything using the newer session list. Also trying to generally pass the LQ values directly to the child component. 2024-09-17 19:15:43 -04:00
Scott Idem
e95191d43a More package updates. Things seem to be working... 2024-09-17 17:28:21 -04:00
Scott Idem
3b56223be3 Package updates 2024-09-17 17:20:48 -04:00
Scott Idem
77b14a387e General clean up. Also changed everything over to data store element version 2. Seems to be working well... 2024-09-17 17:17:20 -04:00
Scott Idem
527d1f82b7 Now using the new new new, not mine, Modal element. 2024-09-16 19:02:16 -04:00
Scott Idem
1e3086560d Creating version to of the data store element. Hopefully better now. 2024-09-16 18:10:55 -04:00
Scott Idem
9fd84183d7 Renaming the page access code to site access code. 2024-09-16 16:25:53 -04:00
Scott Idem
f9c3fe4b21 Broad clean up of code. 2024-09-16 16:15:14 -04:00
Scott Idem
d122d1fb93 Trying to clean up things so they load faster. 2024-09-16 15:41:06 -04:00
Scott Idem
c7b48ca97a Chasing and fixing bugs 2024-09-16 15:09:23 -04:00
Scott Idem
1faf7fb18f Adding more options related to session content. 2024-09-16 14:43:28 -04:00
Scott Idem
3a6c462155 Changing the datetime formatter a bit. 2024-09-16 12:20:28 -04:00
Scott Idem
2a3e386f6f Always trying to keep things consistent. 2024-09-13 17:22:21 -04:00
Scott Idem
c711b29bea Making the buttons look the same... 2024-09-13 17:12:19 -04:00
Scott Idem
be58d87faf The session agreements look good. Time to clean up the presenter agreements to match. 2024-09-13 16:51:17 -04:00
Scott Idem
59eed57dec Now with updated_on date times. Other improvements and clean up. 2024-09-13 16:20:56 -04:00
Scott Idem
3d34e30c63 Added the opt out options to the session POC agree page. 2024-09-13 15:40:02 -04:00
Scott Idem
896fb76cc6 More work on the agreements and separating things into files. 2024-09-13 15:05:44 -04:00
Scott Idem
7e610ead67 Work on the new session POC functions and permissions. General code clean up and bug fixes. 2024-09-13 13:20:04 -04:00
Scott Idem
e7e532f61a Improvements to bio and agree 2024-09-12 19:36:33 -04:00
Scott Idem
9fd9f61ddb Bug fix for fields not set yet. 2024-09-12 18:52:59 -04:00
Scott Idem
e3b808a0e0 A lot of work to get the LCI Champions able to add a biography and agree to Terms and Conditions. 2024-09-12 18:37:03 -04:00
Scott Idem
225dd678a5 Minor updates 2024-09-12 14:06:22 -04:00
Scott Idem
0ae86593cd Text updates 2024-09-12 13:25:03 -04:00
Scott Idem
792c496d46 Setting a new version for this app 2024-09-12 13:16:40 -04:00
Scott Idem
faf37f8159 New branch and updates to the workspace file 2024-09-12 13:09:12 -04:00
Scott Idem
5b8121bfc7 Starting new branch and updating packages 2024-09-12 13:03:51 -04:00
Scott Idem
32f24f8ffa Minor update to style 2024-09-09 00:07:25 -04:00
Scott Idem
eacd40ac21 Clean up of the launcher links still... 2024-09-09 00:04:59 -04:00
Scott Idem
bf5bdd4a3e Work on the links to the legacy and new launchers. 2024-09-08 23:28:34 -04:00
Scott Idem
5fcae3b047 Sort of fix for the agreement text (LCI buttons) not showing correctly. 2024-09-06 18:50:44 -04:00
Scott Idem
8b913b73f8 Working on adjusting the permissions for session POC. They can upload/download files for the session and each presenter. 2024-09-04 16:06:50 -04:00
Scott Idem
86dc7797e0 Minor changes related to the site passcodes. 2024-09-04 15:19:45 -04:00
Scott Idem
d5d28149ad More work on making things look better. File counts look nicer. 2024-09-04 14:41:02 -04:00
Scott Idem
917cca09ce Cleaning up the hide/unhide buttons 2024-09-04 13:31:47 -04:00
Scott Idem
3919347383 General clean up and standardizing of things. More option buttons. Show/hide from launcher. 2024-09-04 13:05:21 -04:00
Scott Idem
5aaaaa164d Now with search by location name! 2024-09-03 17:42:15 -04:00
Scott Idem
e6694718e7 Work on location links and fixing presenters not showing correctly on session view page. 2024-09-03 16:41:54 -04:00
Scott Idem
9a0112e884 Work on location views and editing. Also session list clean up. 2024-09-03 14:51:36 -04:00
Scott Idem
acf89f3fa5 Style clean up 2024-08-23 18:35:27 -04:00
Scott Idem
4881fcf7f2 Hide the event files button for now. 2024-08-23 16:39:23 -04:00
Scott Idem
4fa8d0368f Working on the styling some more. 2024-08-23 16:34:28 -04:00
Scott Idem
014d244019 Merge remote-tracking branch 'refs/remotes/origin/ae_app_template_dev' into ae_app_template_dev 2024-08-23 14:35:17 -04:00
Scott Idem
4dbdd16bd0 Recovering from something that broke the styling. It is not 100% right, but much better. 2024-08-23 14:26:17 -04:00
Scott Idem
45d7cba562 General clean up. Trying to make things load faster and more smoothly. 2024-08-21 18:55:12 -04:00
Scott Idem
fed37a77b8 Working on sign in checking and permissions. This probably still needs to be reviewed some more. 2024-08-21 15:34:34 -04:00
Scott Idem
d8edd658b0 Adding a new notes module I guess. Because why not. 2024-08-20 21:00:03 -04:00
Scott Idem
5139a706d0 Removing hardcoded LCI related. General clean up of things. 2024-08-20 12:59:35 -04:00
Scott Idem
608bef7f21 Wrapping up for the day 2024-08-19 18:06:36 -04:00
Scott Idem
c61d3a4dca Trying to make this LiveQuery work. 2024-08-19 17:56:32 -04:00
Scott Idem
4871464adf Minor clean up 2024-08-19 16:45:11 -04:00
Scott Idem
722acdaa59 Apparently not much with badge printing... Cleaned up some code. 2024-08-19 16:30:27 -04:00
Scott Idem
1d496eb769 Duplicated and modified how the event file manager works slightly. 2024-08-19 15:23:34 -04:00
Scott Idem
75b0e53114 General clean up and renaming of functions. 2024-08-19 14:53:28 -04:00
Scott Idem
6aa499c79b Package updates 2024-08-19 14:34:40 -04:00
Scott Idem
fefd7c8027 Dim hidden sessions and presenters in the list 2024-08-19 14:24:10 -04:00
Scott Idem
85138e7b44 Wrapping up for the day. It is Friday. Things mostly work. 2024-08-16 18:34:54 -04:00
Scott Idem
68727d24cf Hide the Copy Access Link button if no person ID linked 2024-08-16 18:02:34 -04:00
Scott Idem
fa55e96be2 Refresh files after upload. For now this clears everything. It is may not be the most efficient, but it works. 2024-08-16 17:41:34 -04:00
Scott Idem
b0e15700b5 Adding config options and toggles for various things. 2024-08-16 17:18:15 -04:00
Scott Idem
37801ca769 Fixes for the reports. General clean up all around. Better usage of log_lvl. 2024-08-16 13:14:18 -04:00
Scott Idem
ca563fdf1f Merge remote-tracking branch 'refs/remotes/origin/ae_app_template_dev' into ae_app_template_dev
Merging changes from yesterday. Thought I did this already.
2024-08-16 10:06:47 -04:00
Scott Idem
fed5d2de65 Clean up based on notes. Adding some JSON event config options for presenter agree and presenter bio. 2024-08-16 10:06:16 -04:00
Scott Idem
f58cd9611b Config change 2024-08-15 21:29:37 -04:00
Scott Idem
7d167444c4 Cleaned up the presenter view. Less fields show if the person is not linked. 2024-08-15 20:18:09 -04:00
Scott Idem
5208480614 Made the session menu look nicer. 2024-08-15 19:52:12 -04:00
Scott Idem
f674503b21 Fixes for the QR codes refreshing too easily. Added option to turn the QR codes off and on. Need to add an event level setting or something. 2024-08-15 18:51:16 -04:00
Scott Idem
002c283c68 Working on dealing with the QR codes re-generating too easily. 2024-08-15 18:04:14 -04:00
Scott Idem
46cc89ad92 Bug fixes for the bio field and copy button 2024-08-15 16:34:34 -04:00
Scott Idem
f209f3ce37 Bug fix for biography copy button 2024-08-15 16:14:01 -04:00
Scott Idem
ef62b30dc9 Now with copy button for presenter bio 2024-08-15 15:49:26 -04:00
Scott Idem
fdca94b625 Minor updates to access 2024-08-15 15:42:18 -04:00
Scott Idem
f526e9094c Various bug fixes. Improvements to the security. Now with super and manager and others. 2024-08-15 15:35:00 -04:00
Scott Idem
35052898b4 Clean up. New ability to save the session search text. 2024-08-15 14:14:41 -04:00
Scott Idem
146bea0a67 Staring work on getting Electron working from Svelte app. Wrapping up for the day. 2024-08-14 19:23:29 -04:00
Scott Idem
cc4588c8a6 Renaming more of the standard object functions. 2024-08-14 16:16:56 -04:00
Scott Idem
0ec9cbbd08 Renaming standard object functions 2024-08-14 16:06:21 -04:00
Scott Idem
be81122e4e General clean up 2024-08-14 15:57:57 -04:00
Scott Idem
56c16bef10 Minor changes 2024-08-14 14:37:53 -04:00
Scott Idem
380a8d2ad3 Allow trusted access to new report 2024-08-14 14:07:21 -04:00
Scott Idem
0d48362529 Work on CRUD v2 API calls. Added sessions without files report. 2024-08-14 14:05:48 -04:00
Scott Idem
efedc88ade Minor fix for help buttons and button styling 2024-08-14 09:39:35 -04:00
Scott Idem
ca2dc11206 Done for the night. 2024-08-13 21:40:14 -04:00
Scott Idem
bdfb276199 Now with better reports showing and status. 2024-08-13 21:36:01 -04:00
Scott Idem
06a1d7a771 Cleaning things up. Minor bug fixes. 2024-08-13 20:57:19 -04:00
Scott Idem
53fea0d25d Cleaned up the menus. Added new options for max qry limits. 2024-08-13 20:01:44 -04:00
Scott Idem
37b2145f81 Now with ability to toggle hidden and disabled sessions. 2024-08-13 18:58:56 -04:00
Scott Idem
e83623526c Minor clean up 2024-08-13 16:52:38 -04:00
Scott Idem
3639342356 Minor bug fix and clean up 2024-08-13 16:48:35 -04:00
Scott Idem
00fcd8e747 Added ability to upload files individually to get the % uploaded. Added show/hide of manage files for sessions and presenters. Other clean up. 2024-08-13 16:42:10 -04:00
Scott Idem
d5dbeeabf7 Maybe wrapping up for the day. 2024-08-12 18:41:33 -04:00
Scott Idem
df36727540 Working on a better file upload element and component. Slow progress... 2024-08-12 18:35:36 -04:00
Scott Idem
074cf154f2 Improvement with file counts and related 2024-08-09 19:33:47 -04:00
Scott Idem
d7dfef4fe0 Wrapping up for the day/week. It is my birthday weekend! 2024-08-09 19:01:45 -04:00
Scott Idem
9a1995dd9f Lots of changes. Things are working better. Files are now showing for the session and presenter. Next is location and event. 2024-08-09 17:50:54 -04:00
Scott Idem
30e6384772 I quite for the night! The presentation list shows now... 2024-08-08 20:42:04 -04:00
Scott Idem
14fc1ee146 Now I am trying to get the presentation list to display... Why won't this work...? Adding URL params seems to have helped some. 2024-08-08 19:51:03 -04:00
Scott Idem
4ddc775aaa Things are finally working better again with the liveQuery. I hope. 2024-08-08 16:55:50 -04:00
Scott Idem
4141524d83 Just saving things... Not making good progress today. 2024-08-08 15:48:49 -04:00
Scott Idem
f407565fc7 Fixed up the new Launcher. The Dexie liveQuery is now working better. Added await tick();. 2024-08-07 18:37:45 -04:00
Scott Idem
cb1f4343db Show star for priority presenter 2024-08-07 17:32:05 -04:00
Scott Idem
ccd91571ce Bug fixes for sign in and related 2024-08-07 17:21:10 -04:00
Scott Idem
75ee8e1b5c Working on bug fix for sign in vs the selected presentation and presenter. Partially fixed. 2024-08-07 16:09:17 -04:00
Scott Idem
7aebf24996 Should have saved my work earlier... General clean up of initial API calls and saving to IDB. Other fixes and updates. 2024-08-07 15:26:58 -04:00
Scott Idem
0d34f81fa7 Wrapping up for the day. The new launcher is partially working. There is a lot of work to do. 2024-08-02 19:11:35 -04:00
Scott Idem
352639e702 Pulled out the event reports page menu. And other clean up. 2024-08-02 12:05:00 -04:00
Scott Idem
8d7627fd36 Clean up of button text 2024-08-01 21:08:45 -04:00
Scott Idem
40293e25b4 Making things scale better 2024-08-01 20:42:28 -04:00
Scott Idem
bf31a3f596 Minor menu show/hide fix 2024-08-01 20:01:34 -04:00
Scott Idem
ff498ed49e Fixed spacing... 2024-08-01 19:55:26 -04:00
Scott Idem
2177f6c379 Now with the event session search menu separated out. Also other clean up. 2024-08-01 19:52:52 -04:00
Scott Idem
98d09bac76 The session and presenter headers both look good. 2024-08-01 18:56:28 -04:00
Scott Idem
e0e380e450 Bug fixes and small improvements. The session page menu is now separate. Still testing that out. 2024-08-01 17:21:59 -04:00
Scott Idem
e7b7948b06 Going live with the new menu after some testing. 2024-08-01 16:33:03 -04:00
Scott Idem
cf9f914412 The new menu for the session page is working well. Time for clean up. 2024-08-01 15:43:39 -04:00
Scott Idem
2fca5b2c3b Creating new session_view.svelte. General clean up related to that and presenter_view.svelte 2024-08-01 10:46:09 -04:00
Scott Idem
f8b53baee8 Making things look nicer. 2024-07-25 18:26:38 -04:00
Scott Idem
86c8aa6c83 Now with hide files from launcher working. event_file.hide 2024-07-25 18:20:17 -04:00
Scott Idem
4a70869896 Improving person reports and related 2024-07-25 13:25:25 -04:00
Scott Idem
0ca8da1a6e Bug fix for total file count for a session 2024-07-25 12:51:34 -04:00
Scott Idem
d756059ad4 Now with ability to clear IDB event_file list 2024-07-25 12:36:39 -04:00
Scott Idem
6b9284951e General improvements to reports 2024-07-25 11:29:59 -04:00
Scott Idem
8b53cd2b7f End user should not see the person link 2024-07-24 18:33:21 -04:00
Scott Idem
fdb435cd1e Got rid of dollar symbol 2024-07-24 18:27:45 -04:00
Scott Idem
37c51cee29 Now with JSON validity checking. Done for the day. 2024-07-24 18:22:19 -04:00
Scott Idem
274b599ff1 Now with new button toggles and edit for JSON data! 2024-07-24 18:06:10 -04:00
Scott Idem
98849427d9 Scroll bars only show when needed in Chrome now. "auto" instead of "scroll" 2024-07-24 15:05:25 -04:00
Scott Idem
027d7a781d General clean up related to permissions and updating fields. 2024-07-24 15:00:48 -04:00
Scott Idem
c41be23995 Package updates and related 2024-07-23 18:40:15 -04:00
Scott Idem
b0633a5c24 Bug fixes for the file rename. 2024-07-19 17:01:42 -04:00
Scott Idem
b2293784e0 Now with the ability to rename files. Had to add a new Dexie update IDB function. 2024-07-19 16:40:56 -04:00
Scott Idem
af17a05022 Now with QR codes for sessions and presenters! 2024-07-18 17:15:12 -04:00
Scott Idem
a6f8f00e9e Implementing bug fix for Svelte params not being ready under my data value. Loading and referencing the params directly/explicitly instead. 2024-07-18 10:09:56 -04:00
Scott Idem
070e714aff Bug fix for loading pages with params! I think this has been a root cause of some of the issues for a while now. 2024-07-18 09:21:29 -04:00
Scott Idem
43488b8f76 Wrapping up for the day. I need to research this. Why does the first link (anchor tag) that is hovered over (triggers pre-load) fail if the URL has a “slug” value? Or any URL??? Any links after the first seem to pre-load fine. 2024-07-17 18:30:08 -04:00
Scott Idem
d16c47fedf Testing preloading 2024-07-17 18:24:43 -04:00
Scott Idem
064bba3d62 Reports can set max count. Bug fixes. Clean up. 2024-07-17 17:08:09 -04:00
Scott Idem
625169a321 Work on new core person list, view, and edit 2024-07-17 14:59:15 -04:00
Scott Idem
ad1e42010a Show more results by default 2024-07-12 16:24:47 -04:00
Scott Idem
997f470ebe Now with a new recent files report. 2024-07-12 16:20:23 -04:00
Scott Idem
47c742d004 Wrapping up for the day. The first report looks pretty good. 2024-07-11 17:44:43 -04:00
Scott Idem
945c943c61 Added a reports section and the first report. Agreed presenters. 2024-07-11 17:01:44 -04:00
Scott Idem
7eb3080f46 Updating packages. Stuck with issues related to ESLint and TypeScript. A version conflict. 2024-07-11 13:52:48 -04:00
Scott Idem
1bf90f128f Updates for the browser title. Wrapping up for the day! July 4th week! 2024-07-03 18:48:28 -04:00
Scott Idem
8f2eb2c27e Forgot to lock some fields down. And other minor changes. 2024-07-03 18:23:59 -04:00
Scott Idem
4aae2bead4 Separating out components and functions to make things more modular. 2024-07-03 17:48:06 -04:00
Scott Idem
270a1429f8 None code change 2024-07-03 11:54:31 -04:00
Scott Idem
a2d3d5b1f7 NPM package updates 2024-07-03 11:45:30 -04:00
Scott Idem
26a0a1dbd6 Working on new inline components for presenter list and event file list. 2024-07-02 18:53:41 -04:00
Scott Idem
889500e80d Finally got the horizontal scroll working correctly with the table. 2024-07-02 18:17:38 -04:00
Scott Idem
d8e062b8c7 Work in progress of moving the session list out to a separate component. 2024-07-02 15:42:45 -04:00
Scott Idem
a1515ba6b6 Code clean up 2024-07-02 14:35:12 -04:00
Scott Idem
27bed2f532 At good point for session searching. 2024-07-02 14:19:04 -04:00
Scott Idem
f4006e7226 The rate limited and delayed search is now working. 2024-07-02 14:00:39 -04:00
Scott Idem
afbe396caf Cleaning up search 2024-07-02 11:54:07 -04:00
Scott Idem
aae19249d4 More efficient query results processing 2024-07-02 11:38:33 -04:00
Scott Idem
d410953ce4 Work on new LiveQuery search results 2024-07-02 09:42:52 -04:00
Scott Idem
40aba339f8 Work on LiveQuery 2024-07-02 09:33:16 -04:00
Scott Idem
ed89c61aed Slight change to header padding 2024-07-01 23:30:43 -04:00
Scott Idem
6d06347e0a Will come back to the LiveQuery undefined thing later... 2024-07-01 20:37:19 -04:00
Scott Idem
acc39ecb50 Wrapping up for the day. Trying to get LQ to work with searching and bulkGet. 2024-07-01 20:33:57 -04:00
Scott Idem
861107c2fd Ready to move on to other areas. 2024-07-01 17:01:48 -04:00
Scott Idem
245757b501 Working on the logic for permissions 2024-07-01 16:53:54 -04:00
Scott Idem
88b0042919 This should have been saved earlier. Lots of moving code around to and clean up. 2024-07-01 16:22:10 -04:00
Scott Idem
9de9d31101 Trying to fix scrolling of the table. 2024-06-28 23:24:29 -04:00
Scott Idem
acaff7634d A lot of code clean up. Also making things look better. 2024-06-28 17:56:39 -04:00
Scott Idem
9f7a19c4b9 Making things look nicer 2024-06-28 13:06:13 -04:00
Scott Idem
e6aec67247 Minor changes 2024-06-28 12:17:43 -04:00
Scott Idem
4183c9022c Now with presenter biography. Used the speakers collection that was done for CHOW as the basis. 2024-06-28 12:02:45 -04:00
Scott Idem
298f87960a Now with ability to sync person record to presenter record. Also some other editing. 2024-06-28 10:45:37 -04:00
Scott Idem
a0085723c9 Now with new file manager 2024-06-28 09:48:12 -04:00
Scott Idem
d01ab2479f Ready to swap out the temporary presenter view file manager section 2024-06-28 09:42:51 -04:00
Scott Idem
2d490d8058 Fixed presentation name edit permissions 2024-06-27 18:43:01 -04:00
Scott Idem
a34d2af18e Show full SHA hash in title 2024-06-27 18:24:25 -04:00
Scott Idem
12c778c7e2 Now with a better file manager. It still needs work. 2024-06-27 18:19:29 -04:00
Scott Idem
37ac30c56c Working on new standalone event file manage element 2024-06-27 15:51:49 -04:00
Scott Idem
8c8748b571 Making more fields us the LiveQuery. Migrated format bytes function. General clean up. 2024-06-27 10:57:19 -04:00
Scott Idem
20e1c46461 Making things look better. Now with a person look up for the presenter record. Wrapping up for the day. 2024-06-26 18:19:17 -04:00
Scott Idem
4d7e48a170 Making things look pretty 2024-06-26 14:39:30 -04:00
Scott Idem
dd9c48e801 Minor fixes and new warning for outdated "app" version. 2024-06-26 14:06:38 -04:00
Scott Idem
7faa9d0459 Work on help information for session search and session view 2024-06-26 11:38:20 -04:00
Scott Idem
6a22f84f23 Failed to switch to LiveQuery for presenter list 2024-06-25 19:07:38 -04:00
Scott Idem
b604eaee56 Saving before trying to use live query with presentation list 2024-06-25 18:10:14 -04:00
Scott Idem
68d376b88d Now able to do stuff with the locations 2024-06-25 18:06:56 -04:00
Scott Idem
0090058238 Just working on select option list and related. 2024-06-25 14:50:40 -04:00
Scott Idem
a62ea7dc8d Making things work better. Adding CRUD select option list. 2024-06-25 14:05:03 -04:00
Scott Idem
21ad9d900c Adding the ability to quickly edit most fields. Other general clean up. 2024-06-25 11:37:03 -04:00
Scott Idem
4ad51b8e0b Wrapping up for the day. Now with ability to add a person. 2024-06-24 19:45:15 -04:00
Scott Idem
384f91bbe7 Create presentations and presenters. Making things look nicer. 2024-06-24 19:09:40 -04:00
Scott Idem
58a975bfe9 Updating npm and Svelte 2024-06-24 16:04:44 -04:00
Scott Idem
c6b7c7e803 Better (less) logging and other clean up 2024-06-24 15:51:30 -04:00
Scott Idem
0c4185f74c General code clean up 2024-06-24 14:42:14 -04:00
Scott Idem
31ba1c9200 Split up the giant events functions file! 2024-06-24 14:32:25 -04:00
Scott Idem
5211f83f23 Now with file counts on the search results! Done for the day. 2024-06-21 16:57:43 -04:00
Scott Idem
ecf2b3eca8 Now with updated POC edit and other 2024-06-21 16:03:08 -04:00
Scott Idem
e5cff89acb Now with search on presenter's name and email 2024-06-21 15:08:58 -04:00
Scott Idem
87b4f22bb1 Added select no POC option 2024-06-21 12:29:32 -04:00
Scott Idem
2552e1a839 Now with upload and download percent! Also better editing for session POC. 2024-06-21 12:25:36 -04:00
Scott Idem
fd114bce22 Now with the ability to edit a presentation name. Yay. 2024-06-20 18:32:24 -04:00
Scott Idem
991cb1e9da Working on ability to change the presentation name and session name... Making progress. Need to figure out why the file list does not reload correctly right after saving. 2024-06-20 17:45:00 -04:00
Scott Idem
d49f73583c Now with clear search text button. 2024-06-20 13:47:40 -04:00
Scott Idem
8c52722408 Bug fixes and clean up of Dexie DB related 2024-06-19 16:27:16 -04:00
Scott Idem
06add80718 A lot of cosmetic clean up and some code clean up. Also new util functions from old Svelte NPM library. 2024-06-19 14:01:15 -04:00
Scott Idem
5ef2d05e9c Enable emailing sign in links 2024-06-18 18:49:30 -04:00
Scott Idem
baf354fd46 Minor text change 2024-06-18 15:53:20 -04:00
Scott Idem
8044cd0723 POC sign is mostly working now 2024-06-18 15:52:14 -04:00
Scott Idem
519525540c File uploads for event presenters works. Other minor clean up. 2024-06-14 16:38:15 -04:00
Scott Idem
97f15f41f7 Finally got the presenter file upload working better. 2024-06-14 16:23:10 -04:00
Scott Idem
06df9a6230 Everything is working except for the file uploads 2024-06-14 14:42:49 -04:00
Scott Idem
65daf86cc7 General clean up of things 2024-06-14 11:23:37 -04:00
Scott Idem
37547a96d8 Time for bed! 2024-06-14 00:39:38 -04:00
Scott Idem
0156426f4b Trying to get things ready for tomorrow. Now with saving of opt outs and other. 2024-06-14 00:34:24 -04:00
Scott Idem
58d25e922c Stopping for dinner and a break. 2024-06-13 19:08:09 -04:00
Scott Idem
6bcc554737 Clean up of auth logic 2024-06-13 15:42:30 -04:00
Scott Idem
84f6f1eda8 Trying to wrap up for the day. 2024-06-12 18:58:06 -04:00
Scott Idem
b368abc91a Almost ready for LCI demo... 2024-06-12 13:23:56 -04:00
Scott Idem
987e411956 Now with session searching working 2024-06-12 13:00:27 -04:00
Scott Idem
bbd403b96d More updates 2024-06-11 20:02:37 -04:00
Scott Idem
98cd149d2c Lots of work on things for LCI! Should have saved more often. 2024-06-11 19:31:44 -04:00
Scott Idem
7cd71299b3 Changes for CHOW deadline 2024-06-07 11:47:28 -04:00
Scott Idem
87ef4cc6b9 Better update to the results cap based on admin or trusted 2024-05-29 15:09:31 -04:00
Scott Idem
8b201a68ed Increased list cap 2024-05-29 15:05:33 -04:00
Scott Idem
359c147167 Moving things to SK. Added events and sessions. 2024-05-24 19:02:52 -04:00
Scott Idem
d9ff625db6 More code clean up 2024-05-23 19:45:17 -04:00
Scott Idem
0005ba7dc9 Code clean up 2024-05-23 18:59:50 -04:00
Scott Idem
42fef3feb8 Re-working the API library functions and files 2024-05-23 18:20:31 -04:00
Scott Idem
fa58d1accb Changes from before AAPOR 2024-05-20 17:24:23 -04:00
Scott Idem
f72d7be5b2 Work on general clean up. Better export and email of sponsor data 2024-05-03 17:07:08 -04:00
Scott Idem
a3a32e188d Should have saved earlier... 2024-04-25 16:11:18 -04:00
Scott Idem
94d0cfeb4d Working on better bug fix for downloading export files. Some columns were missing. 2024-04-23 18:52:41 -04:00
Scott Idem
69c1250961 Improved downloading export files. 2024-04-23 17:42:44 -04:00
Scott Idem
2aff1aadbe Export now works for Sponsors, Speakers, and Leads 2024-04-23 17:10:24 -04:00
Scott Idem
ca04e9739f Saving my work! 2024-04-23 16:36:08 -04:00
Scott Idem
38e73cb40c Adding back in the sponsorship export stuff I was working on... Annoyed. 2024-04-23 15:53:43 -04:00
a87b4d53a0 Unknown changes from last week in Prague? 2024-04-23 15:37:51 -04:00
f8e81bf7e4 Better style 2024-04-11 11:32:45 -04:00
d242948d7e Show or hide alert. Enable or disable the default to scan 2024-04-11 11:17:59 -04:00
21ee101007 Clean up of style 2024-04-11 10:40:23 -04:00
2b61b8f4a5 Style updates 2024-04-11 10:11:44 -04:00
ab268532da Padding change and minor layout change 2024-04-11 06:27:46 -04:00
920dd176fe I am done for the night/morning... 2024-04-10 20:31:32 -04:00
5411df5893 Trying things... 2024-04-10 04:45:48 -04:00
ab906e4af1 More comments 2024-04-10 00:16:08 -04:00
46dcd389a4 This is nearly working. 2024-04-10 00:14:53 -04:00
f405d1b3b7 Quick save of things 2024-04-10 00:09:57 -04:00
0aae7f9361 I don't know 2024-04-09 23:35:19 -04:00
a51e96ea6e Hopefully this works... QR stop and start? 2024-04-09 23:05:32 -04:00
f68bccddb9 Trying to make things work for the morning... 2024-04-09 21:25:12 -04:00
4460b38098 Style clean up 2024-04-09 06:34:34 -04:00
8e6ab2c223 More generic short_name for PWA 2024-04-09 04:30:22 -04:00
d64a20e5b3 Done for the night for real! 2024-04-08 20:27:17 -04:00
f5ab1cecc1 Done for the night. 2024-04-08 20:11:47 -04:00
ef583e1328 Getting the badge search really ready 2024-04-08 19:12:44 -04:00
9eee2a928b Now supports the new redirect to the new search 2024-04-08 13:15:39 -04:00
1ae1a3d989 The AND LIKE query is now working! 2024-04-07 17:26:48 -04:00
bde3229270 Working on making the LIKE query work correctly. 2024-04-07 14:06:31 -04:00
02af46a48f The leads should be ready to go now..... 2024-04-07 12:59:06 -04:00
Scott Idem
562479313d Hiding not ready warning messages 2024-04-05 11:39:11 -04:00
Scott Idem
a5b0720933 More fixes 2024-04-04 23:39:18 -04:00
Scott Idem
847fad3151 Last minute updates 2024-04-04 23:31:59 -04:00
Scott Idem
16f65cf85f Now with much better badge search function. And bug fix for Payment tab. 2024-04-04 21:33:45 -04:00
Scott Idem
5671423467 Getting the badges up and running again 2024-04-03 19:37:28 -04:00
Scott Idem
8d2f4e30f4 Almost everything works!! Need to clean up export file. Missing custom questions and similar. 2024-04-03 18:10:47 -04:00
Scott Idem
9c85914b9f Progress 2024-04-03 16:28:11 -04:00
Scott Idem
ae1764579e Making things look good and fewer bugs 2024-04-03 15:54:51 -04:00
Scott Idem
b5588fd9a1 Slow and steady progress getting things working more smoothly. 2024-04-03 14:54:33 -04:00
Scott Idem
1555f0f8d0 Fixes for saving custom leads questions and adding by badge ID 2024-04-03 11:22:38 -04:00
Scott Idem
3abe92a2dc Various bug fixes for CHOW 2024-04-01 20:48:16 -04:00
Scott Idem
78b5fc1068 I am done...? 2024-03-29 20:33:10 -04:00
Scott Idem
841367afeb Changes have been made... 2024-03-29 15:35:40 -04:00
Scott Idem
0e26765312 General clean up. Less debug. Things work better? 2024-03-29 12:15:50 -04:00
Scott Idem
c0e1d666f4 Minor 2024-03-28 19:31:09 -04:00
Scott Idem
7a4a4cab5e Now with auto hide header and footer 2024-03-28 19:16:07 -04:00
Scott Idem
741878172c Less logging 2024-03-28 19:01:26 -04:00
Scott Idem
2b1b2b7d07 Now with ability to email license sign in link 2024-03-28 18:55:31 -04:00
Scott Idem
9851c69c30 Making things work smoothly 2024-03-28 17:34:19 -04:00
Scott Idem
86972f5a02 Svelte framework and lib updates 2024-03-28 13:23:58 -04:00
Scott Idem
b7bf152366 General updates 2024-03-28 12:58:23 -04:00
Scott Idem
7cc23077f3 Done for the night I guess. 2024-03-27 20:15:30 -04:00
Scott Idem
b336f18512 Testing auto reloading data stores. 2024-03-27 19:21:57 -04:00
Scott Idem
378ae11224 I think things are mostly working now... 2024-03-27 19:21:39 -04:00
Scott Idem
8d8fb0b638 I guess this is better than it was... 2024-03-27 17:55:31 -04:00
Scott Idem
3082c07e3e Should have saved my progress earlier. Trying to redo things without using localStorage initially. Shared data... 2024-03-27 11:36:06 -04:00
Scott Idem
a8a2131361 I am not really sure... I just want it to load correctly. 2024-03-26 20:15:24 -04:00
Scott Idem
a30690ea2a Mostly working before major fix for data shared. 2024-03-26 18:25:43 -04:00
Scott Idem
f20c6ef706 Finally getting the initial loading better 2024-03-26 17:12:35 -04:00
Scott Idem
4d486a580c Trying to make things work better... 2024-03-26 14:24:35 -04:00
Scott Idem
040e1e71e3 One last little bug 2024-03-25 20:25:15 -04:00
Scott Idem
54fb837581 Bug fix for QR scan. Clean up for the day! 2024-03-25 20:15:02 -04:00
Scott Idem
3ddef770c0 General work through out the day. Lots of interruptions from the dogs. 2024-03-25 19:26:49 -04:00
Scott Idem
b0f2e2ccdf Done for the day 2024-03-22 19:16:14 -04:00
Scott Idem
f97c83db03 Quick save before trying more drastic options for sorting and filtering... 2024-03-22 14:07:07 -04:00
Scott Idem
742205b84b Done for the night! 2024-03-21 20:02:17 -04:00
Scott Idem
976f4fe8c0 Done for the night 2024-03-21 19:45:42 -04:00
Scott Idem
cd79213b37 Wrapping up for the day? 2024-03-21 18:29:12 -04:00
Scott Idem
18c1c84044 This is a good point for things. Looks good and working well. 2024-03-21 18:13:05 -04:00
Scott Idem
7381797a28 This is just a good clean point of development. Still a lot of work though! 2024-03-21 15:20:48 -04:00
Scott Idem
c490bca265 Stuck and done for the night 2024-03-20 19:39:48 -04:00
Scott Idem
e21b7ef584 General clean up and making things a least look real. 2024-03-20 16:33:36 -04:00
Scott Idem
84e3098b72 Lots of general clean up and fixes. 2024-03-20 13:31:42 -04:00
Scott Idem
fbbaa1392b Wrapping up for the day. Lots of changes. 2024-03-19 20:23:42 -04:00
Scott Idem
19d2dd630b Minor fixes. Passes the client-reference-id to Stripe 2024-03-19 13:10:46 -04:00
Scott Idem
e4687aab2f Finalish updates before sending emails to ISHLT exhibitors 2024-03-19 11:38:47 -04:00
Scott Idem
d3ae087cd6 Lots of updates.... 2024-03-18 21:36:03 -04:00
Scott Idem
9b02b2f86c Bug fix for exhibit_id missing. Why? 2024-03-15 18:46:34 -04:00
Scott Idem
7d86a9b40f Wrapping up for the day? Now with iframe sort of support 2024-03-15 18:02:12 -04:00
Scott Idem
0400aa429b It has been a long two or three weeks... 2024-03-15 17:48:14 -04:00
Scott Idem
68b0efb6c9 Now with QR code scanner! 2024-03-14 20:31:37 -04:00
Scott Idem
20b42ac6aa Things are working! 2024-03-14 19:43:54 -04:00
Scott Idem
a97e5666a7 Just working on things. Slow progress... 2024-03-14 18:11:09 -04:00
Scott Idem
8bf1892bc7 Working on adding/updating licenses 2024-03-13 19:53:10 -04:00
Scott Idem
3f664eb5c0 Saving my work and trying something a little different. Using a number as the id instead of the email address. I feel like this will also cause issues. 2024-03-13 18:06:22 -04:00
Scott Idem
3c30664b2b Last minute work for CHOW. Hopefully done soon... 2024-03-13 10:32:22 -04:00
Scott Idem
fd4f2bdf35 Finally working Events - Leads for exhibitors 2024-03-12 19:28:10 -04:00
Scott Idem
7d1a4b735b Making things look nicer and more complete. 2024-03-12 14:29:16 -04:00
Scott Idem
a5431070d3 Minor changes 2024-03-11 14:06:53 -04:00
Scott Idem
ef2597d114 Bug fixes and clean up for CHOW 2024-03-11 12:48:23 -04:00
Scott Idem
1559bae11c General checks and clean up of things 2024-03-10 16:01:53 -04:00
Scott Idem
b6ba167a86 Done for the night! 2024-03-08 21:17:07 -05:00
Scott Idem
4136c08cdb Sponsor Hub part looks pretty good now. Still need to enable the delete buttons. 2024-03-08 21:09:58 -05:00
Scott Idem
875f327c90 Speakers form is now working pretty well. Including delete. 2024-03-08 18:00:36 -05:00
Scott Idem
b53566aa41 Minor changes 2024-03-08 14:41:36 -05:00
Scott Idem
d9ee195590 Documentation for what reason? 2024-03-08 13:08:54 -05:00
Scott Idem
409872ed4d More changes 2024-03-08 11:43:32 -05:00
Scott Idem
2ada1419d8 Lots of general clean up and work for CHOW going live 2024-03-08 11:27:18 -05:00
Scott Idem
5a147a98bb I am done for the night... 2024-03-08 00:09:17 -05:00
Scott Idem
1694dfb5c5 Getting ready to implement Dexie for Svelte 2024-03-07 13:33:51 -05:00
Scott Idem
ff00ec5c91 Ready to demo for Precon CHOW again 2024-03-07 12:09:23 -05:00
Scott Idem
c6abc0abca Clean up for CHOW... 2024-03-07 11:28:30 -05:00
Scott Idem
b020ded01c Changes. Work on new review page searching. 2024-03-06 20:39:38 -05:00
Scott Idem
aa712284ce Just worki on things 2024-03-06 13:47:41 -05:00
Scott Idem
e71cdab353 Wrapping up for the night. Things are working better. There are still API request misses or something. 2024-03-05 20:35:38 -05:00
Scott Idem
bed4f4a0f2 Work on the new Data Store element 2024-03-05 17:01:37 -05:00
Scott Idem
19a6ff6dbe Work on expanding the routes 2024-03-04 20:55:48 -05:00
Scott Idem
6c60ee3086 More quick updates and clean up 2024-03-04 13:43:24 -05:00
Scott Idem
a0947e349a General clean up for CHOW. 2024-03-04 10:59:15 -05:00
Scott Idem
04a8b49177 Quick changes for CHOW 2024-03-04 08:46:12 -05:00
Scott Idem
63990bb36a More changes based on feedback 2024-03-03 19:52:42 -05:00
Scott Idem
60f6386415 Changes based on feedback from Jordan (and Erin) 2024-03-03 19:22:44 -05:00
Scott Idem
7051cb92d5 Event presenter now has a slug directory 2024-03-03 12:51:14 -05:00
Scott Idem
64589ec11c Too many changes. Getting ready to show Jordan. 2024-03-03 12:31:32 -05:00
Scott Idem
1b12cd4aec Lots of bug fixes. Lots of clean up. Things work more consitently. 2024-03-02 20:54:45 -05:00
Scott Idem
4db9e68543 A lot of little changes everywhere. Sorry... 2024-03-02 20:09:25 -05:00
Scott Idem
0dbf869d5d Work on file related 2024-03-02 11:28:13 -05:00
Scott Idem
e69ff969f5 Walking away 2024-03-01 19:15:22 -05:00
Scott Idem
ff90fa5287 Done for reals... 2024-03-01 19:10:26 -05:00
Scott Idem
f09577d1d5 Done for the night... 2024-03-01 19:02:05 -05:00
Scott Idem
9fe4c51a67 Wrapping up for Friday night 2024-03-01 18:55:26 -05:00
Scott Idem
f4ed04497e Style clean up. A lot! Almost ready for CHOW going live. 2024-03-01 16:17:02 -05:00
Scott Idem
b21f9c0437 Wrapping up for the night. Mostly good for demo tomorrow morning. 2024-02-29 20:24:08 -05:00
Scott Idem
873e6d9f9a General clean up. Presenter form submission now works better. Need to do the same for the sponsorships submission form. 2024-02-29 16:14:31 -05:00
Scott Idem
e713313aca Bug fix for null bio field 2024-02-29 14:49:24 -05:00
Scott Idem
addadacc47 Getting ready for CHOW 2024 demo call tomorrow 2024-02-29 14:37:37 -05:00
Scott Idem
9310aac4d2 Button fix 2024-02-28 21:47:57 -05:00
Scott Idem
2c7e7ca027 Will now pull in agreements and accommations questions 2024-02-28 21:17:09 -05:00
Scott Idem
713fcb3c62 Bug fixes. Style setting improvements. 2024-02-28 15:45:02 -05:00
Scott Idem
00a28588b7 Wrapping up for the day 2024-02-27 20:21:02 -05:00
Scott Idem
ed97cba7a6 General clean up of code. Starting to wrap up for the night. 2024-02-27 19:20:21 -05:00
Scott Idem
714642380e Cleaning up code and making everything look better. 2024-02-27 18:38:46 -05:00
Scott Idem
22efa3fd96 Made the closing of the modals cleaner 2024-02-27 17:04:03 -05:00
Scott Idem
c93d84f3c3 Sponsorships and Speakers are working and looking pretty well. Other general clean up. 2024-02-27 16:50:37 -05:00
Scott Idem
9540aefd50 Finally got things working. Store act odd when being set under layout.ts? 2024-02-26 21:04:17 -05:00
Scott Idem
060c0500d3 General clean up. No working on the new event presenter components. 2024-02-26 15:03:01 -05:00
Scott Idem
740baa689e Need to work on not havnig the account ID set 2024-02-22 22:16:49 -05:00
Scott Idem
02f693c13e Making the loading a bit more efficient and cleaner 2024-02-22 20:28:29 -05:00
Scott Idem
d1328eb67c Added the access code component. Improved layout. General clean up and improvements. 2024-02-22 17:21:22 -05:00
Scott Idem
5a13852432 Wrapping up programming for tonight. Ready for CHOW demo? 2024-02-20 19:07:09 -05:00
Scott Idem
d51d059535 Finally got things mostly working. 2024-02-20 18:05:18 -05:00
Scott Idem
5bb9134641 I need to stop for the night. 2024-02-19 19:37:03 -05:00
Scott Idem
3403210efd This is the first commit of the day. It is probably the last. Lots of general prep for a demo. 2024-02-19 18:28:55 -05:00
Scott Idem
6f0680f282 Good first draft. Done for the night! 2024-02-16 22:15:54 -05:00
Scott Idem
bab68af7dc Making progress on pulling this together. 2024-02-16 21:38:41 -05:00
Scott Idem
9958724aaa I should have saved this long ago. Lots of changes. Learning a lot as well! 2024-02-16 20:12:19 -05:00
Scott Idem
cb9bd1648c Done for the night! 2024-02-15 19:19:01 -05:00
Scott Idem
a3d4354ef4 This is a good start. Many key features are working again!!! 2024-02-15 18:53:26 -05:00
Scott Idem
19f9983c9a Most things are working in the template now. 2024-02-15 11:55:42 -05:00
Scott Idem
17d99d080c Starting a new template using Svelte, SvelteKit, Tailwind, and Skeleton. 2024-02-15 09:49:35 -05:00
Scott Idem
ccbb783378 Variouu changes from a few days ago. 2024-02-15 09:20:57 -05:00
Scott Idem
970e7567fc More work on The Hub 2024-02-09 18:38:17 -05:00
Scott Idem
7f6f063f00 Work on the new Hub to manage the header, footer, menu, quick access, etc 2024-02-09 18:24:07 -05:00
Scott Idem
7775b88b35 General clean up of the new AE Sponsorships app. Making it portable too. 2024-02-08 17:51:38 -05:00
Scott Idem
06e0c98e68 Lots of clean up to get Sponsorships working. Need to add the edit next. 2024-02-07 18:23:15 -05:00
Scott Idem
143265ed9e Creating for AE "Sponsorships". Update file timestamps and remove unused files. Also switched to Svelte with Vite. 2024-02-06 18:23:01 -05:00
1285 changed files with 160012 additions and 4884 deletions

15
.ae_brief Normal file
View File

@@ -0,0 +1,15 @@
# Aether Project Brief: aether_app_sveltekit
**Last Updated:** 2026-05-21 22:25:05
**Current Agent:** mcp_agent
## 🛠️ What I Just Did
Implemented "Force Sync Location" feature. Optimized file download order with a 4-tier chronological sort (Global > Session > Presentation > Creation Date). Added UI button for onsite operators. Updated project documentation. Verified with npm run check.
## 🚧 Current Blockers
None.
## ➡️ Exact Next Steps
User to review changes. Ready for onsite testing/deployment.
---
*Generated by ae_brief*

47
.dockerignore Normal file
View File

@@ -0,0 +1,47 @@
# Build artifacts and local state
.svelte-kit/
.vite/
node_modules/
build/
dist/
.cache/
# VCS and IDE
.git/
.gitignore
.vscode/
.idea/
# OS junk
.DS_Store
.directory
# Logs and temp files
*.log
*.bak
*.tgz
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# Test output and dev-only dirs
tests/
test-results/
test_results/
coverage/
documentation/
backups/
.claude/
# Deployment artifacts
npm_deploy/
package-lock.json.bak
# Env files: exclude all live secrets, allow only the per-environment files needed for Docker builds.
# .env.local is workstation-only and must never enter a container image.
.env
.env.*
!.env.dev
!.env.test
!.env.prod

19
.env.dev.default Normal file
View File

@@ -0,0 +1,19 @@
# One Sky IT's Aether Framework and System — DEV template (dev-*.oneskyit.com)
# Copy to .env.dev and fill in real values.
# Aether API access
PUBLIC_AE_API_PROTOCOL=https
PUBLIC_AE_API_SERVER=dev-api.oneskyit.com
PUBLIC_AE_API_BAK_SERVER=test-api.oneskyit.com
PUBLIC_AE_API_PORT=443
PUBLIC_AE_API_PATH=
PUBLIC_AE_API_SECRET_KEY=XXXX
PUBLIC_AE_API_CRUD_SUPER_KEY=XXXX
# Bootstrap key: used only for the unauthenticated site-domain lookup on first load.
# Separate from the main API key — has limited permissions (no account_id required).
PUBLIC_AE_BOOTSTRAP_KEY=XXXX
PUBLIC_AE_NO_ACCOUNT_ID=No_Account_ID_Here

18
.env.prod.default Normal file
View File

@@ -0,0 +1,18 @@
# One Sky IT's Aether Framework and System — PROD template (api.oneskyit.com)
# Copy to .env.prod and fill in real values.
# Aether API access
PUBLIC_AE_API_PROTOCOL=https
PUBLIC_AE_API_SERVER=api.oneskyit.com
PUBLIC_AE_API_BAK_SERVER=bak-api.oneskyit.com
PUBLIC_AE_API_PORT=443
PUBLIC_AE_API_PATH=
PUBLIC_AE_API_SECRET_KEY=XXXX
PUBLIC_AE_API_CRUD_SUPER_KEY=XXXX
# Bootstrap key: used only for the unauthenticated site-domain lookup on first load.
# Separate from the main API key — has limited permissions (no account_id required).
PUBLIC_AE_BOOTSTRAP_KEY=XXXX
PUBLIC_AE_NO_ACCOUNT_ID=No_Account_ID_Here

18
.env.test.default Normal file
View File

@@ -0,0 +1,18 @@
# One Sky IT's Aether Framework and System — TEST template (test-api.oneskyit.com)
# Copy to .env.test and fill in real values.
# Aether API access
PUBLIC_AE_API_PROTOCOL=https
PUBLIC_AE_API_SERVER=test-api.oneskyit.com
PUBLIC_AE_API_BAK_SERVER=api.oneskyit.com
PUBLIC_AE_API_PORT=443
PUBLIC_AE_API_PATH=
PUBLIC_AE_API_SECRET_KEY=XXXX
PUBLIC_AE_API_CRUD_SUPER_KEY=XXXX
# Bootstrap key: used only for the unauthenticated site-domain lookup on first load.
# Separate from the main API key — has limited permissions (no account_id required).
PUBLIC_AE_BOOTSTRAP_KEY=XXXX
PUBLIC_AE_NO_ACCOUNT_ID=No_Account_ID_Here

13
.eslintignore Normal file
View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

31
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,31 @@
/** @type { import("eslint").Linter.Config } */
module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended',
'prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2017: true,
node: true
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
]
};

38
.gitignore vendored
View File

@@ -1,4 +1,36 @@
/node_modules/
/public/build/
.DS_Store
.directory
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
!.env.prod.default
!.env.test.default
!.env.dev.default
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
# Logs
logs
*.log
*.log.*
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Backups and archives
*.bak
*.tar.gz
backups/
# Temporary files
tmp/
temp/
*.kate-swp
test_results
test-results

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
engine-strict=true

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

17
.prettierrc Normal file
View File

@@ -0,0 +1,17 @@
{
"useTabs": false,
"tabWidth": 4,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 80,
"bracketSameLine": true,
"svelteSortOrder": "options-scripts-markup-styles",
"svelteIndentScriptAndStyle": false,
"svelteAllowShorthand": true,
"plugins": [
"prettier-plugin-svelte",
"prettier-plugin-tailwindcss"
],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

View File

@@ -1,3 +0,0 @@
{
"recommendations": ["svelte.svelte-vscode"]
}

129
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,129 @@
{
"prettier.documentSelectors": ["**/*.svelte"],
"tailwindCSS.classAttributes": [
"class",
"accent",
"active",
"animIndeterminate",
"aspectRatio",
"background",
"badge",
"bgBackdrop",
"bgDark",
"bgDrawer",
"bgLight",
"blur",
"border",
"button",
"buttonAction",
"buttonBack",
"buttonClasses",
"buttonComplete",
"buttonDismiss",
"buttonNeutral",
"buttonNext",
"buttonPositive",
"buttonTextCancel",
"buttonTextConfirm",
"buttonTextFirst",
"buttonTextLast",
"buttonTextNext",
"buttonTextPrevious",
"buttonTextSubmit",
"caretClosed",
"caretOpen",
"chips",
"color",
"controlSeparator",
"controlVariant",
"cursor",
"display",
"element",
"fill",
"fillDark",
"fillLight",
"flex",
"flexDirection",
"gap",
"gridColumns",
"height",
"hover",
"inactive",
"indent",
"justify",
"meter",
"padding",
"position",
"regionAnchor",
"regionBackdrop",
"regionBody",
"regionCaption",
"regionCaret",
"regionCell",
"regionChildren",
"regionChipList",
"regionChipWrapper",
"regionCone",
"regionContent",
"regionControl",
"regionDefault",
"regionDrawer",
"regionFoot",
"regionFootCell",
"regionFooter",
"regionHead",
"regionHeadCell",
"regionHeader",
"regionIcon",
"regionInput",
"regionInterface",
"regionInterfaceText",
"regionLabel",
"regionLead",
"regionLegend",
"regionList",
"regionListItem",
"regionNavigation",
"regionPage",
"regionPanel",
"regionRowHeadline",
"regionRowMain",
"regionSummary",
"regionSymbol",
"regionTab",
"regionTrail",
"ring",
"rounded",
"select",
"shadow",
"slotDefault",
"slotFooter",
"slotHeader",
"slotLead",
"slotMessage",
"slotMeta",
"slotPageContent",
"slotPageFooter",
"slotPageHeader",
"slotSidebarLeft",
"slotSidebarRight",
"slotTrail",
"spacing",
"text",
"track",
"transition",
"width",
"zIndex"
],
"explorer.fileNesting.enabled": false,
"cSpell.words": [
"lpignore",
"prejoin"
],
"markdownlint.config": {
"MD004": false,
"MD007": false,
"MD030": false,
"MD033": false
}
}

119
CLAUDE.md Normal file
View File

@@ -0,0 +1,119 @@
# Claude Code — Project: Aether App SvelteKit
**Part of:** One Sky IT / Aether Platform
**Backend:** `~/OSIT_dev/aether_api_fastapi/` via Docker
---
## CRITICAL: Privacy & Business Rules
### IDAA — International Doctors in Alcoholics Anonymous
- **ALL IDAA content is PRIVATE. Authentication required. Never public.**
- BB (Bulletin Board / Posts) — always private
- Archives — always private
- Recovery Meetings — always private
- A previous AI agent accidentally exposed IDAA BB publicly. This is a severe security failure.
- IDAA users authenticate via Novi API (Novi UUID per member) at `Authenticated` permission level.
- Default permission required: `trusted_access` or higher for IDAA module.
### Journals
- Private personal data. Always authenticated. Passcode/encryption features exist.
### General
- **Never expose private content publicly.** When in doubt — it's private.
- Code comments must explain the WHY for any non-obvious business logic.
Key features have been silently broken by AI agents that didn't understand intent.
---
## Mandatory Workflow
1. **Before starting:** Read `documentation/TODO__Agents.md` for active tasks
2. **Before committing:** Run `npx svelte-check` — no exceptions
3. **Commits:** Atomic — one component or fix per commit
4. **Never delete files with `rm`** — move to `~/tmp/agents_trash`
5. **Backend coordination:** Use `ae_send_message` or flag changes clearly
---
## Tech Stack
- **Framework:** Svelte 5 (runes mode) + SvelteKit v2
- **Styling:** Tailwind CSS v4, Flowbite; Skeleton UI being phased out
- **State:** `$state`, `$derived` runes + Dexie.js IndexedDB (`liveQuery`)
- **Icons:** Lucide
- **Editors:** CodeMirror 6 (primary), Edra/TipTap (secondary)
- **Markdown:** `marked` library
- **UI primitives:** ShadCN (`src/lib/components/ui/`)
- **Native/Electron bridge:** `src/lib/electron/electron_relay.ts` via `window.aetherNative`
---
## API (V3)
- Base: `/v3/crud/{obj_type}/` for all CRUD
- Lookups: `/v3/lookup/`
- Search: `POST /v3/crud/{obj_type}/search`
- Auth headers: `x-aether-api-key` + `x-account-id` (NOT Bearer tokens)
- Permissive PATCH: add `x-ae-ignore-extra-fields: true`
- Guide: `documentation/GUIDE__AE_API_V3_for_Frontend.md`
---
## Key Patterns
- **Canonical reference:** Journals module (`src/lib/ae_journals/`) — use as the pattern template
- **Object files:** `ae_<module>__<object>.ts` + `<object>.editable_fields.ts`
- **DB:** `db_<module>.ts` per module (Dexie instance)
- **LiveQuery:** `lq__xyz` (read-only), `lqw__xyz` (writable form snapshot)
- **Load pattern:** SWR — return Dexie cache immediately, refresh from API in background
- **Component naming:** `ae_comp__*` (route-level), `ae_<module>_comp__*` (module), `element_*` (reusable)
- **Standard fields:** `id`, `id_random`, `code`, `name`, `enable`, `hide`, `priority`, `sort`, `group`, `notes`, `created_on`, `updated_on`, `cfg_json`, `data_json`
---
## Source Layout
```
src/lib/
ae_api/ — API helpers (prefer V3)
ae_core/ — Account, User, Person, Site, Address, Contact
ae_events/ — Events, sessions, presenters, badges, locations, devices
ae_journals/ — Journals (canonical/frontier model)
ae_archives/ — Archives
ae_posts/ — Posts + Post Comments (IDAA BB)
ae_idaa/ — IDAA custom module
ae_reports/ — Reporting
elements/ — Reusable UI: V3 editor, CRUD, data store, CodeMirror
electron/ — Native app bridge
src/routes/
/core/ — Admin (accounts, people, sites, users, contacts)
/events/[id]/ — Events: pres_mgmt, launcher, badges, leads, settings
/journals/ — Journals
/idaa/ — IDAA: archives, bb, recovery_meetings, video_conferences
/hosted_files/ — File management
/testing/ — Dev testing
```
---
## Active Issues (check TODO__Agents.md for current state)
- Sev-1: `PUBLIC_AE_API_SECRET_KEY` audit — see TODO__Agents.md (assessed acceptable, 2026-03-11)
- V3 CRUD migration — remaining legacy API wrappers in events/sponsorships/core (see `PROJECT__Use_AE_API_V3_CRUD_upgrade.md`)
- Style Review Phase 3 — IDAA + Pres Mgmt card polish deferred post-April 2026 conference
---
## Key Docs
| File | Purpose |
|---|---|
| `documentation/TODO__Agents.md` | Active task list — read first |
| `documentation/GUIDE__Development.md` | Dev SOP |
| `documentation/GUIDE__AE_API_V3_for_Frontend.md` | V3 API reference (authoritative) |
| `documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md` | Dexie + liveQuery patterns |
| `documentation/GEMINI__Svelte_and_Me.md` | Svelte 5 runes patterns |
| `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` | Electron/Launcher |
| `documentation/PROJECT__AE_Events_Badges_Review_Print.md` | Badges — kiosk editing (Task 4.0 open) |

62
Dockerfile Normal file
View File

@@ -0,0 +1,62 @@
# Stage 1: Build the application
FROM node:24-alpine AS builder
WORKDIR /app
# Install dependencies first for better Docker layer caching.
COPY package*.json ./
RUN npm install
# Copy the rest of the source code.
COPY . .
# Build Argument to determine build environment (dev, test, prod).
ARG BUILD_MODE=dev
ENV NODE_ENV=production
# Sync the SvelteKit project to generate ./.svelte-kit/tsconfig.json
RUN npx svelte-kit sync
# Perform the build based on the BUILD_MODE argument.
# Each script uses vite --mode <name>, which reads .env.<name> directly — no cp hack needed.
RUN if [ "$BUILD_MODE" = "prod" ] || [ "$BUILD_MODE" = "production" ]; then \
npm run build:prod; \
elif [ "$BUILD_MODE" = "test" ]; then \
npm run build:test; \
else \
npm run build:dev; \
fi
# Copy the source env file to .env.runtime for the deploy stage.
# PUBLIC_* vars are baked into the JS bundle by vite; non-PUBLIC vars (AE_CFG_ID,
# AE_APP_NODE_PORT) are read by the Node server at runtime and need this file.
RUN if [ "$BUILD_MODE" = "prod" ] || [ "$BUILD_MODE" = "production" ]; then \
cp .env.prod .env.runtime; \
elif [ "$BUILD_MODE" = "test" ]; then \
cp .env.test .env.runtime; \
else \
cp .env.dev .env.runtime; \
fi
# Stage 2: Final runtime image
FROM node:24-alpine AS deploy-node
WORKDIR /app
# Copy built files and package info.
COPY --from=builder /app/build .
COPY --from=builder /app/package.json .
COPY --from=builder /app/package-lock.json .
# Install only production dependencies.
RUN npm install --omit=dev
# Copy the runtime env file (non-PUBLIC vars for the Node server).
COPY --from=builder /app/.env.runtime .env
# SvelteKit (via adapter-node) defaults to port 3000.
EXPOSE 3000
# Healthcheck to verify the app is running
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node -e "fetch('http://localhost:3000/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"
CMD ["node", "index.js"]

42
GEMINI.md Normal file
View File

@@ -0,0 +1,42 @@
# Aether Frontend Agent Context: Gemini CLI Standard
> **Role:** Aether App Orchestrator (SvelteKit Frontend)
> **Location:** GEMINI.md (Project Root)
## 🚨 MANDATORY PROTOCOL
You must follow the safety, testing, and coordination standards defined in:
`documentation/GUIDE__DEVELOPMENT.md`
---
## 🏗️ Technical Domain: Aether Frontend
### Stack & UI Standards
- **Framework:** SvelteKit v2 + Svelte 5 (Runes)
- **Styling:** Tailwind 4 + Skeleton UI + Skeleton V3 (SkeletonNext)
- **Local Cache:** Dexie.js (IndexedDB)
- **Icons:** Standardizing on **Lucide-Svelte**. Avoid legacy Font-Awesome where possible.
### Reactivity Patterns (The "Aether Way")
- **Svelte 5 Runes:** Use `$state`, `$derived`, and `$effect`.
- **Navigation Shield:** Use SvelteKit's `page.url` searchParams as the single source of truth for navigation-driven selection. Sync to global stores only via `untrack()` within effects.
- **Side-Effect Purge:** Keep `liveQuery` observables pure. Do NOT update global stores ($events_slct) inside derived observables to prevent infinite reactivity loops.
- **Dexie LiveQuery:** Use the `$` prefix (e.g., `$lq__obj`) consistently. Results from `liveQuery` are observables.
- **Initialization:** Always initialize reactive state (`$state`) outside of `$props()` destructuring.
### Performance & Data
- **SWR (Stale-While-Revalidate):** Never block SvelteKit `load` functions with API calls for cached data. Let the UI render instantly from IndexedDB and update reactively.
- **Triple-ID Pattern:** Map `id`, `[obj_type]_id`, and `[obj_type]_id_random` consistently.
- **ID Vision:** The backend uses String IDs. Primary keys should use `_id` or `id` (String) mapping to backend `id_random`.
## 🧠 Recent Strategy & Patterns
- **Safe Handover (Native):** Rename `.tmp` to `.file` ONLY after SHA-256 verification in Electron.
- **Envelopes:** API helpers automatically handle the `{data: ...}` envelope returned by the backend.
- **Bootstrap Paradox:** Use unauthenticated bypass (`x-no-account-id: "Nothing to See Here"`) for initial site/domain lookups.
- **Sev-1 Incident Recovery (2026-02-13):** Purged redundant/misplaced headers (`x-aether-api-token`, `Access-Control-Allow-Origin`). Unified all CRUD helpers to standard `/v3/crud/...` paths.
- **Account ID Scavenging:** Core fetch helpers now proactively read `account_id` from `localStorage` (`ae_loc`) if missing from config. This is the mandatory fix for Svelte 5 hydration race conditions where `onMount` triggers API calls before global stores are synced.
- **V3 Event File Mapping (2026-02-19):** Mapped prefixed backend fields (`hosted_file_hash_sha256`, `hosted_file_size`) to flat properties (`hash_sha256`, `file_size`) in the data layer. Required `inc_hosted_file=true` query param for full metadata retrieval.
- **Launcher Location Discovery:** Resolved missing locations in room select by ensuring `load_ae_obj_li__event_location` requests `hidden: 'all'` during background sync and initial load.
## 🤝 Coordination & Continuity
- **Handshake:** Use the `message` tool to notify the Backend Agent of UI/Data requirements.
- **Active Tasks:** Track your progress in `documentation/AGENT_TODO.md`.
- **Reference:** See `README.md` for build/deploy steps and `TODO.md` for project milestones.

329
README.md
View File

@@ -1,105 +1,304 @@
*Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)*
# One Sky IT's Aether App - SvelteKit v2 with Svelte v5
This uses SvelteKit version 2.x with Svelte version 5.x, DexieJS 4.x, TailwindCSS 4.1, and Skeleton.
# Modules
## Core (`/core/`)
Admin-only views for foundational Aether objects. Minimal UI — primarily used for data management.
- **Accounts** (`/core/accounts/`, `/core/accounts/[account_id]/`)
- **Activity Logs** (`/core/activity_logs/`)
- **Addresses** (`/core/addresses/`, `/core/addresses/[address_id]/`)
- **Contacts** (`/core/contacts/`, `/core/contacts/[contact_id]/`)
- **Lookups** (`/core/lookups/`) — Countries, subdivisions, time zones
- **People** (`/core/people/`, `/core/people/[person_id]/`)
- **Sites** (`/core/sites/`, `/core/sites/[site_id]/`)
- **Users** (`/core/users/`, `/core/users/[user_id]/`)
## Events (`/events/`)
The primary client-facing module for conference and event management.
### Event List (`/events/`)
### Event Detail (`/events/[event_id]/`)
Each event has four sub-modules, each in its own SvelteKit route group:
#### Presentation Management (`/(pres_mgmt)/`)
Manages the full conference program.
- `/events/[event_id]/pres_mgmt/` — Dashboard
- `/events/[event_id]/locations/` — Location list
- `/events/[event_id]/location/[event_location_id]/` — Location detail
- `/events/[event_id]/presenter/[presenter_id]/` — Presenter detail
- `/events/[event_id]/session/[session_id]/` — Session detail
- `/events/[event_id]/reports/` — Presenter, session, and file reports
#### Launcher (`/(launcher)/`)
Kiosk display system; runs on-site to show session schedules and presenter info.
- `/events/[event_id]/launcher/` — Launcher home
- `/events/[event_id]/launcher/[event_location_id]/` — Location-specific display
#### Badges (`/(badges)/`)
Badge printing and management for event attendees.
- `/events/[event_id]/badges/` — Badge list
- `/events/[event_id]/badges/[badge_id]/` — Badge detail
- `/events/[event_id]/badges/[badge_id]/print` — Print a single badge
- `/events/[event_id]/badges/[badge_id]/review` — Review before printing
- `/events/[event_id]/badges/print_list/` — Bulk print queue
- `/events/[event_id]/badges/stats/` — Badge statistics
- `/events/[event_id]/templates/` — Badge template management
#### Leads (`/(leads)/`)
Exhibitor lead capture via QR scan or manual entry.
- `/events/[event_id]/leads/` — Exhibit list
- `/events/[event_id]/leads/exhibit/[exhibit_id]/` — Exhibit detail and lead capture
- `/events/[event_id]/leads/exhibit/[exhibit_id]/lead/[exhibit_tracking_id]/` — Lead detail
#### Event Settings (`/settings/`)
- `/events/[event_id]/settings/` — Event configuration (basic info, pres mgmt, badges, abstracts)
## Journals (`/journals/`)
The "frontier" module — most fully-featured and used as the canonical implementation reference.
- `/journals/` — Journal list
- `/journals/[journal_id]/` — Journal detail and entry list
- `/journals/[journal_id]/entry/[journal_entry_id]/` — Journal entry detail and editor
## IDAA (`/idaa/`)
Custom module for the IDAA client. Built on core Aether objects (Events, Posts, Archives).
- `/idaa/` — IDAA home / dashboard
### Archives (`/idaa/archives/`)
- `/idaa/archives/` — Archive list with media player
- `/idaa/archives/[archive_id]/` — Archive detail and content list
### Bulletin Board (`/idaa/bb/`)
Built on the Posts and Post Comments objects.
- `/idaa/bb/` — Post list
- `/idaa/bb/[post_id]/` — Post detail and comments
### Recovery Meetings (`/idaa/recovery_meetings/`)
Built on the Events object.
- `/idaa/recovery_meetings/` — Meeting list with search/filter
- `/idaa/recovery_meetings/[event_id]/` — Meeting detail
### Video Conferences (`/idaa/video_conferences/`)
- `/idaa/video_conferences/` — Video conference list (Jitsi integration)
- `/idaa/jitsi_reports/` — Jitsi usage reports
## Hosted Files (`/hosted_files/`)
- `/hosted_files/` — File list and upload management
- `/hosted_files/video_util/` — Video processing utility
## Testing (`/testing/`)
Developer sandbox pages — not for production use.
- `/testing/ae_obj_field_editor/` — V3 field editor playground
- `/testing/data_store/` — Data store V3 playground
- `/testing/editor_test/` — CodeMirror / TipTap editor tests
- `/testing/hosted_files/` — File upload tests
# How to build and deploy SvelteKit:
The deployment is fully integrated into the unified **Aether Docker Environment** (`aether_container_env`). The application is built inside a clean Docker container using `vite build --mode <env>`, which reads the corresponding `.env.<env>` file for `PUBLIC_` variables.
## Environments
| Environment | Env file | Vite mode | API server |
| ----------- | ----------- | --------- | ------------------------- |
| dev | `.env.dev` | `dev` | `dev-api.oneskyit.com` |
| test | `.env.test` | `test` | `test-api.oneskyit.com` |
| prod | `.env.prod` | `prod` | `api.oneskyit.com` |
## Commands (from `aether_app_sveltekit/`)
```bash
# Active development — Vite HMR, no Docker
npm run dev
# Build Vite output only (no Docker)
npm run build:dev
npm run build:test
npm run build:prod
# Build Docker image and restart container locally
npm run build:docker:dev
npm run build:docker:test
npm run build:docker:prod
# Deploy to remote server (SSH → linode.oneskyit.com → deploy.sh)
npm run deploy:remote:test
npm run deploy:remote:prod
```
### Technical Details
- **Unified Orchestration**: All services (API, UI, Redis) are managed via `~/OSIT_dev/aether_container_env/docker-compose.yml`.
- **Dockerfile**: Multi-stage build. Stage 1 (builder) runs `vite build --mode $BUILD_MODE` using `.env.$BUILD_MODE`. Stage 2 (runtime) creates the final lightweight Node image.
- **Environment Handling**:
- `PUBLIC_` variables are baked into the image at build time via the `.env.<mode>` file.
- Private runtime variables are passed via the Docker Compose `.env` file in `aether_container_env/`.
- **Remote deploy**: `aether_container_env/deploy.sh` handles git pull + Docker build + restart on the server. Triggered via `npm run deploy:remote:*`.
### Client-Side Cache & IDB Version Management
The app uses Dexie (IndexedDB) as a local cache for API data (SWR pattern). To prevent
stale cached records from persisting across deploys, two version-tracking systems exist
in `src/lib/stores/store_versions.ts`:
**localStorage store versions (`AE_LOC_VERSION`, etc.)**
Track the schema of persisted Svelte stores (`ae_loc`, `ae_events_loc`, etc.).
Bump when a store's shape changes in a breaking way (field type change, required rename).
The check runs synchronously at module import time, before any store hydrates.
**IDB content versions (`IDB_CONTENT_VERSIONS`)**
Track the content shape of Dexie table rows — specifically what `properties_to_save`
writes to each table. Bump when `properties_to_save` in an object file changes in a way
that makes existing cached rows stale (fields added/removed/renamed, computed field behavior
changed). The `check_and_clear_idb_table()` helper reads a localStorage key per table and
clears the Dexie table on mismatch. Call it from the module's layout on mount.
**When to bump `IDB_CONTENT_VERSIONS`:**
If you change `properties_to_save` in `ae_events__event.ts` (or any other object file),
bump the matching entry here. Failure to do so has historically caused silent "no data"
states that are extremely difficult to diagnose — stale rows pass silently, filter to zero,
and the error looks identical to a genuinely empty result.
Currently wired: `events.event` (via `src/routes/idaa/(idaa)/+layout.svelte`).
All other tables are defined but not yet wired — see the comment block in `store_versions.ts`.
---
# svelte app
## Developing (Local HMR)
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
```bash
npx degit sveltejs/template svelte-app
cd svelte-app
```
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
## Get started
Install the dependencies...
```bash
cd svelte-app
npm install
```
...then start [Rollup](https://rollupjs.org):
For the best developer experience with Hot Module Replacement (HMR), start a local development server on your host machine:
```bash
npm run dev
```
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
The local dev server will communicate with the **FastAPI backend running in Docker** (typically via `dev-api.oneskyit.com`). This gives you the speed of local Svelte development with the power of the full Aether stack.
By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
# Rebuild the node_modules directory and manually install extra Svelte packages
If you're using [Visual Studio Code](https://code.visualstudio.com/) we recommend installing the official extension [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using other editors you may need to install a plugin in order to get syntax highlighting and intellisense.
## Building and running in production mode
To create an optimised version of the app:
Run the npm update to fix the node_modules directory and package.json
```bash
npm run build
npm list
npm outdated
npm update
npm outdated
npm list
```
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
## Single-page app mode
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
```js
"start": "sirv public --single"
```
## Using TypeScript
This template comes with a script to set up a TypeScript development environment, you can run it immediately after cloning the template with:
Other installs?:
Are both still needed? I know at least one of these is. 2024-07-23
```bash
node scripts/setupTypeScript.js
npm install --save-dev svelte-highlight
npm install --save-dev typescript-svelte-plugin
```
Or remove the script via:
---
# Set up and run
## Packages and dependencies
```bash
rm scripts/setupTypeScript.js
npm install --save-dev svelte-highlight typescript-svelte-plugin
npm install flowbite flowbite-svelte tailwind-merge @popperjs/core
```
## Deploying to the web
I am slowly switching from Font-Awesome to Lucide
### With [Vercel](https://vercel.com)
## Tiptap Editor
Install `vercel` if you haven't already:
- Eventually use Edra? https://edra.tsuzat.com/
- Best Rich Text Editor, made for Svelte Developers with Tiptap
- ShadEditor is "evolving" to be Edra.
- ShadCN is still stuck on Tailwind 3. Waiting to upgrade to Tailwind 4.x. Tailwind 4.x was released in late January 2025. ShadCN is still being worked on as of late March 2025.
- [https://github.com/huntabyte/shadcn-svelte/issues/1643](https://github.com/huntabyte/shadcn-svelte/issues/1643)
Need to install ShadCN and Lucide for the Tiptap editor.
```bash
npm install -g vercel
npm install shadcn-svelte
npm install lucide-svelte
npm install mode-watcher
```
Then, from within your project folder:
Now we initialize the ShadCN and ShadEditor packages. Follow the command line instructions.
```bash
cd public
vercel deploy --name my-project
npx shadcn-svelte@next init
npx shadcn-svelte@next add dropdown-menu button tooltip input popover separator
npx shadeditor init
```
### With [surge](https://surge.sh/)
Install `surge` if you haven't already:
More packages related to the Tiptap editor???
```bash
npm install -g surge
npm install @tiptap/extension-link @tiptap/extension-bullet-list @tiptap/extension-history @tiptap/extension-typography @tiptap/extension-underline
```
Then, from within your project folder:
### Environment file
The application uses standard SvelteKit `.env` files for build-time configuration (specifically for `PUBLIC_` prefixed variables).
- **`.env.dev`**: Used by `npm run build:docker:dev` and `npm run build:dev`.
- **`.env.test`**: Used by `npm run build:docker:test` and `npm run build:test`.
- **`.env.prod`**: Used by `npm run build:docker:prod` and `npm run build:prod`.
- **`.env.local`**: Used during local development (`npm run dev`).
**Note:** Runtime variables (like private API keys or DB credentials) are managed in the deployment directory's `.env` file and passed to the containers via Docker Compose.
---
## Developing
Start a local development server:
```bash
npm run build
surge public my-project.surge.sh
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Deployment
```bash
# Build Docker image locally and restart container
npm run build:docker:dev
npm run build:docker:prod
# Deploy to remote server (linode.oneskyit.com)
npm run deploy:remote:test
npm run deploy:remote:prod
```
These commands use the multi-stage **Dockerfile** to build the app in a clean environment and automatically restart the corresponding Docker containers.

View File

@@ -0,0 +1,23 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"cSpell.words": [
"autofetch",
"Axonius",
"displayplacer",
"elif",
"filelist",
"gsettings",
"onsave"
],
"git.autofetch": true,
"editor.defaultFormatter": "svelte.svelte-vscode",
"chat.tools.terminal.autoApprove": {
"npx svelte-check": true
}
}
}

View File

@@ -0,0 +1,27 @@
## BuildKit-friendly multi-stage Dockerfile example for Aether frontend
# Stage 1: dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --no-audit --prefer-offline
# Stage 2: build
FROM node:20-alpine AS build
WORKDIR /app
# optionally reuse deps from previous stage
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# If you want to use BuildKit cache mounts during local development, uncomment the next line
# RUN --mount=type=cache,target=/root/.npm npm ci
RUN npm run build
# Stage 3: runtime (static site served by nginx)
FROM nginx:stable-alpine AS runtime
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
# Notes:
# - Keep dependency installation separate from copying source to maximize cache hits when only application code changes.
# - For backend images, follow the same pattern: install deps early, copy source later, and keep a small final runtime image.

View File

@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
# Example CI script to build and push an image with buildx using registry cache.
# This script is provider-agnostic and intended to be run inside CI where
# Docker and buildx are available and authenticated against the registry.
REGISTRY=${REGISTRY:-ghcr.io/ORG/REPO}
IMAGE_TAG=${IMAGE_TAG:-staging}
CACHE_REF=${CACHE_REF:-${REGISTRY}:cache}
echo "Building ${REGISTRY}:${IMAGE_TAG} using registry cache ${CACHE_REF}"
docker buildx build \
--push \
--tag ${REGISTRY}:${IMAGE_TAG} \
--cache-from type=registry,ref=${CACHE_REF} \
--cache-to type=registry,ref=${CACHE_REF},mode=max \
.
echo "Build complete. Image: ${REGISTRY}:${IMAGE_TAG}"
# Optional: instruct devs how to run locally with a local cache
cat <<'EOF'
Local test with BuildKit and local cache:
DOCKER_BUILDKIT=1 docker build \
--tag myapp:staging \
--cache-to=type=local,dest=/tmp/docker-cache \
--cache-from=type=local,src=/tmp/docker-cache .
Prune local builder cache older than 72 hours:
docker builder prune --filter "until=72h" --force
EOF

17
components.json Normal file
View File

@@ -0,0 +1,17 @@
{
"$schema": "https://next.shadcn-svelte.com/schema.json",
"style": "default",
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app.css",
"baseColor": "gray"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks"
},
"typescript": true,
"registry": "https://next.shadcn-svelte.com/registry"
}

View File

@@ -0,0 +1,30 @@
# AE Docker CI Cache Policy (recommendation)
Purpose
- Provide a straightforward policy to keep build caches useful but bounded.
Recommendations
- Primary CI cache: **registry-based buildx cache** (preferred). Use a single cache ref (e.g. `ghcr.io/ORG/REPO:cache`) reused by CI builds.
- Local dev cache: use `--cache-to type=local` for fast iteration but prune periodically.
- Retention: keep registry cache for 30 days by default. Implement registry GC or lifecycle rule to delete older cache blobs.
Rotation strategy
- Option A (simple): CI always writes to the same cache ref `:cache`. Periodically (monthly) run a job to `docker pull` and `docker image rm` older tags if you use date-based tagging.
- Option B (date-tag): CI writes cache to `cache-YYYYMMDD` and a small scheduled job deletes tags older than 30 days.
Pruning commands (developer)
- Remove local build cache older than 72 hours:
```bash
docker builder prune --filter "until=72h" --force
```
- Remove all builder cache (aggressive):
```bash
docker builder prune --all --force
```
CI runner requirements
- `docker` and `docker buildx` available in runner environment.
- Registry credentials provided via CI secrets with permission to push/pull images.
Security & Secrets
- Do not store registry credentials in repo. Use CI secret storage.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,177 @@
# Aether Project Architecture
This document outlines the overall architecture and key technologies used in the Aether SvelteKit frontend project.
## 1. Project Overview
The Aether project is a Svelte and SvelteKit based application, utilizing Tailwind CSS and Skeleton for styling and UI elements. It serves as the frontend UI/UX for the Aether system, which interacts with a Python FastAPI backend.
## 2. Core Technologies
- **Frontend Framework:** Svelte 5 and SvelteKit v2
- **Routing:** SvelteKit's file-system based routing.
- **Styling:** Tailwind CSS v4
- **UI Component Libraries:**
- Skeleton (Design System, Tailwind Components, Functional Components - being phased out due to conflicts with Tailwind CSS v4)
- Flowbite (Tailwind Components)
- Custom Components (a growing library of `ae_comp__*` and `element_*` components)
- **Text/Code Editors:**
- CodeMirror 6.x (`element_editor_codemirror.svelte`) — source/code editing, markdown
- TipTap (`element_editor_tiptap.svelte`) — WYSIWYG rich-text for content fields (IDAA, Journals, Leads notes)
- **Icons:** Lucide Icons (SVG Icons)
- **Markdown Parsing:** `marked` library
- **State Management:** Svelte stores, potentially with `liveQuery` from Dexie for reactive IndexedDB interactions.
### 2.1. Journals as the Canonical Frontend Pattern
The Journals module is the current frontend reference for configuration modal structure and journal-entry field semantics. When other docs disagree, Journals should be treated as the implementation target until proven otherwise.
- Entry Config modal sections now follow `Metadata`, `Status & Security`, `Privacy Flags`, `Alerts & Messaging`, and `Admin`.
- `summary` is a first-class journal-entry field and belongs with metadata.
- `alert` and `alert_msg` are separate fields: the flag and its text payload.
- `priority` is a boolean flag in the object model, while `sort` remains the numeric ordering field.
## 3. Module Structure
The Aether project is organized into several modules, categorized as Core, Extended, and Custom.
### 3.1. Official Modules
#### Core Modules
These are foundational modules essential for the application's basic functionality.
- **Accounts:** Minimal implementation.
- **Files:** Manages hosted files.
- **People:** Minimal implementation for person records.
- **Sites:** Minimal implementation for site configurations.
- **Users:** Minimal implementation for user management.
#### Extended Modules
These modules provide additional features and functionalities.
- **Archives:** Minimal implementation.
- **Events:** Includes features for Badges and Presentation Management.
- **Posts:** Minimal implementation.
- **Journals:** Manages journal entries.
#### Custom Modules
These modules are tailored for specific client needs.
- **IDAA:** Includes Archives, Bulletin Board (BB), and Recovery Meetings functionalities.
## 4. Data Storage Mechanisms
### 4.1. Local Storage
Used for client-side persistence of various application states and configurations.
- `api`: API-related settings.
- `app`: Global application settings.
- `core`: Settings and data specific to core modules.
- `<module>`: Settings and data specific to extended modules.
- `<custom>`: Settings and data specific to custom modules.
### 4.2. IndexedDB (Dexie.js)
Used for more structured client-side data storage, often for caching and offline capabilities.
- `ae_core_db`: Core database instance.
- `<module>`: Module-specific database instances.
- `<custom>`: Custom module-specific database instances (none currently defined).
## 5. Data Sorting
Standardized sorting orders are applied across various data lists.
- **Default/General:** `group > priority (flag) > sort > updated_on/created_on`
- **Specific (e.g., Events):** `type > start_date/time > code or name`
## 6. Object Properties and Fields
A set of standardized field names and types are used across Aether objects.
### 6.1. Core Standard Fields
These fields are expected to be present in most Aether objects.
- `id`: Primary key for an object (internal use, often a UUID).
- `id_random`: Randomly generated ID for an object (often used for external exposure or URL parameters).
- `<object_type>_id_random`: Specific random ID for an object (e.g., `person_id_random`).
- `code`: Short, unique identifier.
- `name`: Display name.
- `enable`: Boolean for active/inactive status.
- `hide`: Boolean for visibility.
- `priority`: Boolean/tinyint(1) ordering flag used by the object model.
- `sort`: Numeric value for ordering within a priority group.
- `group`: Categorization string.
- `notes`: General notes/comments.
- `created_on`: Timestamp of creation.
- `updated_on`: Timestamp of last update.
### 6.2. Special Use Fields
Fields with specific purposes or conditional usage.
- `for_type`: Indicates the type of object this object is linked to.
- `for_id`: The ID of the object this object is linked to.
- `archive_on`: Timestamp for archiving.
- `passcode`: Password or access code.
- `external_id`: ID from an external system.
### 6.3. Configuration and JSON Fields
Fields designed to store JSON data.
- `cfg_json`: Configuration data in JSON format.
- `data_json`: General data in JSON format.
- `linked_li_json`: List of linked items in JSON format.
### 6.4. Special Generated Fields (Client-side)
Fields generated on the client-side, primarily for sorting or UI logic.
- `tmp_sort_1`
- `tmp_sort_2`
- `tmp_sort_3`
### 6.5. Future Standard Fields
A list of potential future standard fields, often prefixed with `obj_`. These are currently conceptual and not yet fully integrated.
- `obj_id`, `obj_ext_uid`, `obj_ext_id`, `obj_import_id`, `obj_code`, `obj_account_id`, `obj_passcode`, `obj_type`, `obj_type_ver_id`, `obj_name`, `obj_summary`, `obj_outline`, `obj_description`, `obj_enable`, `obj_enable_on`, `obj_archive_on`, `obj_hide`, `obj_priority`, `obj_sort`, `obj_group`, `obj_cfg_json`, `obj_notes`, `obj_created_on`, `obj_updated_on`.
## 7. Runtime Environment: Browser vs Electron
The Aether SvelteKit frontend runs in a standard web browser in almost all cases. The Electron native app is a **very specialized exception** with a narrow, specific purpose.
### 7.1. Standard Browser (Default)
All Aether modules run in a regular browser (Chrome, Chromium, Firefox, Safari). This includes:
- Badge printing — `window.print()` works well across Chrome, Chromium, and Firefox. Chrome is recommended for onsite badge printing stations.
- All CRUD operations, event management, journals, IDAA, reports, etc.
- No special browser configuration required.
### 7.2. Electron Native App (Specialized — Pres Mgmt Launcher Only)
The Electron app (`aether_app_native_electron/`) exists **solely** to support the **Events Presentation Management Launcher**. It provides OS-level capabilities that a browser sandbox cannot:
- Control of third-party presentation software (PowerPoint, Keynote, LibreOffice Impress)
- Local filesystem access for slide file management
- Hardware telemetry for connected devices
**What Electron is NOT used for:**
- Badge printing (browser works well)
- Any other Aether module
- Any general-purpose Aether functionality
The bridge is exposed as `window.aetherNative` (set by Electron's preload script). All code that calls `window.aetherNative` should degrade gracefully when it is `undefined` (i.e., in a normal browser). See: `src/lib/electron/electron_relay.ts`.
**When to assume Electron is available:** Only inside the `/events/[event_id]/(launcher)/` route group, and only when the page was loaded from the native app.
## 8. IndexedDB LiveQuery Usage
- `lq__xyz_obj`: Used for general read-only access to liveQuery results.
- `lqw__xyz_obj`: Used for forms and binding values, representing a writable snapshot of liveQuery results.
- **Note:** Care must be taken when binding to `lqw__xyz_obj` to manage updates and potential conflicts with the underlying liveQuery.

View File

@@ -0,0 +1,146 @@
# Aether Project Components
This document details the various UI components used throughout the Aether SvelteKit frontend project, categorized by their scope and functionality.
## 1. Aether Components (UI/UX)
### 1.1. System Components
These components are part of the core application shell and provide global functionalities.
- **`header`**: Application-wide header.
- **`main/module`s**: Main content area for modules.
- **`footer`**: Application-wide footer.
- **`app`**: Provides global application functionalities such as:
- Refresh application state.
- Clear IndexedDB.
- Clear local storage (settings).
- Toggle iframe visibility (also updates URL parameter).
- Copy current URL.
- Generate and display QR codes.
- **`menu`**: Various menus for different purposes:
- **`mode`**: Edit mode toggle, more options (all or details).
- **`access_type`**: Passcode input, clear access.
- **`user`**: Sign in/out, reset password, email link, change username and email.
- **`theme`**: Mode (light/dark), name (theme list).
- **`debug`**: Developer-facing tools:
- Toggle debug mode (also updates URL parameter).
- Show core and module storages.
- Manually set initial timestamp.
- **`scroll_to`**: Navigation controls for scrolling:
- Scroll to top of the page.
- Scroll page up.
- Scroll page down.
- Scroll to bottom of the page.
### 1.2. Core Components
These are reusable components that provide common functionalities across different modules.
- **`copy_btn`**: A button to copy content to the clipboard.
- Properties: `clipboard`, `bind:value`, `btn_text`, `btn_html`.
- **`txt_editor`**: A basic text area editor.
- **`md_editor`**: Markdown/rich-text editing handled by two active components:
- `element_editor_codemirror.svelte` — CodeMirror 6, used for source/code editing
- `element_editor_tiptap.svelte` — TipTap (WYSIWYG), used for rich-text content fields
- **`html_editor`**: HTML editor.
- **`media_player`**: Component for playing media files.
- Properties: `hosted_file`, `archive_content`, `media_player`.
- Bindings: `bind:host_id`, `bind:media_type`.
- Status: `stopped`, `paused`, `playing`.
- **`hosted_file_li`**: Manages a list of hosted files, making them available for selection.
- **`hosted_file_link_to`**: Lists links per object, with bindings to add/remove links.
- **`upload_to_host`**: Component for uploading files to the host.
- Handles multiple files.
- Properties: `link_type`, `link_id`, `inner fragment` (label html).
- Bindings: `bind:trigger`, `bind:show_spinner`, `bind:show_percent`.
- Status: `started`, `uploading`, `finished`.
- **`upload_file_tbl`**: Table for uploaded files, includes checks for duplicate file hashes and removal from the list.
- **`download_from_host`**: Component for downloading files from the host.
- Bindings: `bind:host_file_id`, `bind:filename`, `bind:file_ext`.
- Properties: `btn inner fragment`.
- Bindings: `bind:trigger`, `bind:show_spinner`, `bind:show_percent`.
- Status: `started`, `downloading`, `finished`.
- **`data_store`**: Component for interacting with data stores.
- **`element_ae_obj_field_editor`**: Standard single-field inline editor. Replaces retired `ae_crud` v1/v2 components.
- Props: `object_type`, `object_id`, `field_name`, `field_type`, `current_value`
- Field types: `text`, `textarea`, `select`, `tiptap`, `checkbox`, `date`, `datetime`, `number`
- Callbacks: `on_success`, `on_error`
- Respects `$ae_loc.edit_mode` — edit trigger hidden when edit mode is off.
- **`sql_qry`**: Component for executing SQL queries.
- **`obj_tbl`**: Object SQL results table or similar.
- **`qr_scanner`**: Component for scanning QR codes.
- **`websocket`**: Component for WebSocket communication.
### 1.3. Main / Module Components
These are components specific to main application sections or individual modules.
- **`menu`**:
- **`options`**: Various settings, show/hide content and options, limit, sorting options.
- **`actions`**: Various actions, sign in/out, email.
### 1.4. Object Menu
A standardized menu for interacting with objects.
- **Properties Displayed:** `id`, `name`, `group`, `priority`, `sort`, `alert`, `hide`, `enable`, `note`.
- **Future Properties:** `ext_id`, `ext_sys_id`, `code` (not yet ready).
- **Actions:** `create`, `view`, `edit`, `update`, `hide`, `disable`, `delete`, `alert` (message), `archive` (not yet ready).
- **Future Actions:** `copy`, `import`.
- **Sort Options:**
- `[default]`: `group > priority (flag) > sort (ASC/DESC) > alert > name`
- `[sort_updated]`: `group > priority (flag) > sort (ASC/DESC) > alert > updated_on > created_on`
- `[priority_updated]`: `group > priority (flag) > updated_on (ASC/DESC) > created_on`
- `[priority_name]`: `group > priority (flag) > name (ASC/DESC) > sort > alert > updated_on > created_on`
- `[name]`: `priority (flag) > name (ASC/DESC) > sort > alert > updated_on > created_on`
- `[created_on]`: `priority (flag) > created_on (ASC/DESC)`
- `[updated_on]`: `priority (flag) > updated_on (ASC/DESC) > created_on`
## 2. Pop-ups
Standardized structure for various types of pop-up elements.
- **`modal_header`**:
- `title`
- `close` button
- **`modal_main`**: Main content area of the modal.
- **`modal_meta`**: Meta-information section.
- **`modal_footer`**:
- `close` button
- **`Pop-up Modal (blocking)`**: A modal that blocks interaction with the rest of the page.
- `modal position`
- **`Pop-up Modal Inline`**: A modal that appears inline with content.
- `inline`, `inline-block`, `block` display options.
- **`Pop-up Dialog`**: A dialog box.
- `dialog position`
## 3. Containers
Generic container types used for layout and grouping.
### 3.1. Navigation
- `link`
- `download`
### 3.2. Forms
- `save` button/action
- `clear value` action
- `set null value` action
### 3.3. Other Containers
- `help`: Blue themed container.
- `info`: Blue themed container.
- `alert`: Yellow themed container.
- `warning`: Orange themed container.
- `error`: Red themed container.
- `message`: Green themed container.
## 4. CSS Styling for UI Elements
- **Warning/Hide Buttons:** `preset-tonal-warning hover:preset-filled-warning-500`
- **Error/Delete/Disable Buttons:** `preset-tonal-error hover:preset-filled-error-500`
- **Submenu:** `flex flex-row items-center justify-center gap-1`

View File

@@ -0,0 +1,100 @@
# Aether Project Data Structures
This document outlines the key data structures and their properties used within the Aether SvelteKit frontend project. It covers object properties, field definitions, and how data is managed.
## 1. Object Properties and Fields
### 1.1. Core Standard Fields
These fields are expected to be present in most Aether objects, providing a consistent base structure.
- `id`: Primary key for an object (internal use, often *returned* by the API as a randomized string value in place of the actual DB autonum).
- `id_random`: Randomly generated ID for an object (often used for external exposure or URL parameters).
- `<object_type>_id_random`: Specific random ID for an object (e.g., `person_id_random`).
- `code`: Short, unique identifier.
- `name`: Display name.
- `enable`: Boolean for active/inactive status.
- `hide`: Boolean for visibility.
- `priority`: Boolean/tinyint(1) ordering flag used by the object model.
- `sort`: Numeric value for ordering within a priority group.
- `group`: Categorization string.
- `notes`: General notes/comments.
- `created_on`: Timestamp of creation.
- `updated_on`: Timestamp of last update.
### 1.2. Journal Entry Fields
Journal entries use the shared object fields plus a few content-specific fields that matter in the UI and config modal.
- `summary`: Short entry summary shown in metadata and list contexts.
- `content`: Main body text for the entry.
- `alert`: Boolean flag used to highlight an entry as an alert.
- `alert_msg`: Supporting alert text shown when the alert flag is enabled.
- `private` / `public` / `personal` / `professional`: Visibility and audience flags used by the Entry Config modal.
### 1.3. Special Use Fields
Fields with specific purposes or conditional usage across different object types.
- `for_type`: Indicates the type of object this object is linked to (e.g., 'account', 'event').
- `for_id`: The ID of the object this object is linked to.
- `archive_on`: Timestamp indicating when an object was archived.
- `passcode`: A password or access code associated with an object.
- `external_id`: An identifier from an external system.
### 1.4. Configuration and JSON Fields
Fields designed to store structured data in JSON format.
- `cfg_json`: Configuration data for an object, stored as a JSON string.
- `data_json`: General purpose data for an object, stored as a JSON string.
- `linked_li_json`: A list of linked items, stored as a JSON string.
### 1.5. Special Generated Fields (Client-side)
These fields are generated on the client-side, primarily for facilitating UI logic, such as sorting. They are not typically stored in the backend database.
- `tmp_sort_1`: Temporary sort field 1.
- `tmp_sort_2`: Temporary sort field 2.
- `tmp_sort_3`: Temporary sort field 3.
### 1.6. Future Standard Fields
A list of potential future standard fields, often prefixed with `obj_`. These are conceptual and represent planned expansions to the data model.
- `obj_id`, `obj_ext_uid`, `obj_ext_id`, `obj_import_id`, `obj_code`, `obj_account_id`, `obj_passcode`, `obj_type`, `obj_type_ver_id`, `obj_name`, `obj_summary`, `obj_outline`, `obj_description`, `obj_enable`, `obj_enable_on`, `obj_archive_on`, `obj_hide`, `obj_priority`, `obj_sort`, `obj_group`, `obj_cfg_json`, `obj_notes`, `obj_created_on`, `obj_updated_on`.
## 2. Data Sorting
Standardized sorting orders are applied across various data lists to ensure consistent presentation.
- **Default/General Sorting:** `group > priority (flag) > sort > updated_on/created_on`
- **Specific Sorting (e.g., for time-based events):** `type > start_date/time > code or name`
## 3. Data Storage Mechanisms
### 3.1. Local Storage
Used for client-side persistence of various application states and configurations.
- `api`: Stores API-related settings and tokens.
- `app`: Stores global application settings and preferences.
- `core`: Stores settings and data specific to core modules.
- `<module>`: Stores settings and data specific to extended modules (e.g., `journals`, `events`).
- `<custom>`: Stores settings and data specific to custom modules (e.g., `idaa`).
### 3.2. IndexedDB (Dexie.js)
Used for more structured client-side data storage, often for caching, offline capabilities, and larger datasets.
- `ae_core_db`: The primary Dexie database instance for core application data.
- `<module>`: Module-specific database instances (e.g., `db_journals` for journal data).
- `<custom>`: Custom module-specific database instances (none currently defined, but reserved for future use).
### 3.3. IndexedDB LiveQuery Usage
Dexie's `liveQuery` is used to provide reactive data streams from IndexedDB.
- `lq__xyz_obj`: Represents a read-only liveQuery result for a single object.
- `lqw__xyz_obj`: Represents a writable liveQuery result, typically used for forms and data binding.
- **Note:** When using `lqw__xyz_obj`, developers must carefully manage updates to avoid conflicts with the underlying liveQuery and ensure data integrity.

View File

@@ -0,0 +1,93 @@
# Aether Project Naming Conventions
## 1. General Principles
- **Clarity:** Names should clearly convey their purpose and meaning.
- **Consistency:** Adhere strictly to these guidelines across the entire codebase.
- **Readability:** Prioritize names that are easy to read and understand.
- **Conciseness:** Avoid unnecessary verbosity, but not at the expense of clarity.
## 2. File Naming
- **Logic/Service Files:** `ae_<module>__<concept>.ts` (e.g., `ae_core__account.ts`, `ae_events__event.ts`)
- **Database Definition Files:** `db_<module>.ts` (e.g., `db_core.ts`, `db_journals.ts`)
- **Svelte Store Files:** `ae_<module>_stores.ts` (e.g., `ae_core_stores.ts`, `ae_journals_stores.ts`)
- **Svelte Components:**
- **Module-specific components:** `ae_comp__<module>__<component_name>.svelte` (e.g., `ae_comp__events__event_card.svelte`)
- **Generic/reusable components:** `element_<component_name>.svelte` (e.g., `element_input_file.svelte`, `element_qr_scanner_v2.svelte`)
- **SvelteKit Routes:** Follow SvelteKit's standard routing conventions (e.g., `+page.svelte`, `+layout.svelte`, `[id]/+page.svelte`).
- **CSS Files:** `ae-<module>-<purpose>.css` (e.g., `ae-c-idaa-light.css`, `ae-osit-default.css`)
## 3. Function and Variable Naming
- **Style:** Strictly `snake_case` for all function and variable names.
- **Deprecated:** `camelCase` should be refactored to `snake_case`.
- **Prefixes:**
- `load_ae_obj_id__<object_type>`: For loading a single Aether object by ID.
- `load_ae_obj_li__<object_type>`: For loading a list of Aether objects.
- `create_ae_obj__<object_type>`: For creating an Aether object.
- `update_ae_obj__<object_type>`: For updating an Aether object.
- `delete_ae_obj_id__<object_type>`: For deleting an Aether object by ID.
- `db_save_ae_obj_li__<object_type>`: For saving a list of Aether objects to IndexedDB.
- `db_update_ae_obj_id__<object_type>`: For updating an Aether object in IndexedDB.
- `process_ae_obj__<object_type>_props`: For module-specific data transformation functions.
- **Deprecated:** Ambiguous `handle_` prefixes should be replaced with more descriptive `snake_case` names (e.g., `handle_submit_form` -> `submit_form`).
## 4. Object and Property Naming
- **Singularity:** Use singular nouns for objects and properties (e.g., `example.id`, not `examples.id`).
- **IDs:**
- `id`: Primary key for an object (internal use, often a UUID).
- `<object_type>_id`: Specific ID for an object (e.g., `person_id`).
- `<object_type>_id_random`: Randomly generated ID for an object (often used for external exposure or URL parameters).
- `account_id`, `site_id`, `user_id`, etc.: Foreign keys.
- **Common Properties:**
- `code`: Short, unique identifier.
- `name`: Display name.
- `description`: Longer text description.
- `enable`: Boolean for active/inactive status.
- `hide`: Boolean for visibility.
- `priority`: Boolean/tinyint(1) ordering flag used by the object model.
- `sort`: Numeric value for ordering within a priority group.
- `group`: Categorization string.
- `notes`: General notes/comments.
- `created_on`: Timestamp of creation.
- `updated_on`: Timestamp of last update.
- **Special Use Properties:** `for_type`, `for_id`, `archive_on`, `passcode`, `external_id`.
- **Config/JSON Properties:** `cfg_json`, `data_json`, `linked_li_json`.
- **Special Generated Fields (Client-side):** `tmp_sort_1`, `tmp_sort_2`, `tmp_sort_3` (for client-side sorting).
## 5. List Suffixes
- **Simple Arrays:** Use `_li` suffix for simple, unordered arrays (e.g., `user_li`, `hosted_file_id_li`).
- **Key-Value Maps/Objects:** Use `_kv` suffix for key-value objects/maps (e.g., `user_kv`, `hosted_file_obj_kv`).
## 6. Interface and Type Naming
- **Style:** Use `PascalCase` for interface and type names (e.g., `Account`, `HostedFile`, `GenericCrudArgs`).
## 7. Constants
- **Style:** Use `SCREAMING_SNAKE_CASE` for constants (e.g., `MAX_RETRIES`, `DEFAULT_TIMEOUT`).
## 8. CSS Classes and IDs
- **Style:** Use `kebab-case` for CSS classes and IDs (e.g., `my-component-class`, `main-header-id`).
## 9. Data Sorting
- **Standard Order:** `group > priority (flag) > sort > updated_on/created_on`
- **Specific Order:** `type > start_date/time > code or name`
## 10. Local Storage and IndexedDB Keys
- **Local Storage:**
- `api`
- `app` (global)
- `core` (core modules)
- `<module>` (extended modules)
- `<custom>` (custom modules)
- **IndexedDB:**
- `ae_core_db`
- `<module>`
- `<custom>`

View File

@@ -0,0 +1,240 @@
# Performance Guidelines: Non-Blocking Load Pattern (SvelteKit + Dexie)
## Overview
To ensure instant page transitions and a high-performance feel, the Aether platform utilizes a **Non-Blocking Load Pattern** (also known as Stale-While-Revalidate or SWR). This pattern leverages Dexie's `liveQuery` for reactive UI and SvelteKit's `load` functions for background data synchronization.
## 🚀 The Core Principle
**Never block the `load` function with API calls if the data is already being observed by a `liveQuery`.**
The page should render *instantly* using cached data from IndexedDB. Fresh data from the API should settle in the background and update the UI automatically via reactivity.
---
## ❌ Anti-Pattern (Blocking)
This pattern causes a "white screen" or "frozen UI" while the browser waits for the API response.
```typescript
// +page.ts
export async function load({ params, parent }) {
const data = await parent();
const event_id = params.event_id;
// BAD: This blocks the navigation until the API responds.
const fresh_data = await events_func.load_ae_obj_id__event({
event_id: event_id,
try_cache: true
});
return { ...data, event_obj: fresh_data };
}
```
## ✅ Best Practice (Non-Blocking / SWR)
This pattern completes the navigation immediately.
```typescript
// +page.ts
export async function load({ params, parent }) {
const data = await parent();
const event_id = params.event_id;
if (browser) {
// GOOD: Fire and forget.
// This function updates IndexedDB in the background.
events_func.load_ae_obj_id__event({
event_id: event_id,
try_cache: true
});
}
return data; // Navigation completes instantly
}
```
```svelte
<!-- +page.svelte -->
<script lang="ts">
import { liveQuery } from 'dexie';
import { db_events } from '$lib/ae_events/db_events';
// UI reacts automatically when the background task finishes.
let lq__event_obj = $derived(
liveQuery(() => db_events.event.get(event_id))
);
</script>
{#if $lq__event_obj}
<h1>{$lq__event_obj.name}</h1>
{:else}
<p>Loading...</p>
{/if}
```
---
## 🛠️ When to use Await
Use `await` in `load` functions ONLY for:
1. **Critical Auth Checks:** If you must verify a session before even showing a layout.
2. **Parent Data:** `const data = await parent();` is necessary to build the context.
3. **Server-Side Rendering (SSR):** If the data *must* be present in the initial HTML for SEO (rare for Aether feature modules).
## 📈 Performance Gains
By adopting this pattern across the Events module, we achieved:
- **~200-500ms reduction** in perceived page load time.
- **Elimination of waterfalls** (sequential API calls).
- **Better offline support**, as the UI is always ready to show what's in the local cache.
---
## Svelte 5 Runes + liveQuery: Critical Patterns
These rules apply to all Svelte 5 runes-mode components (the entire Aether frontend). Violations here are a common source of subtle reactivity bugs and unnecessary re-renders.
### Rule 1: Use `$derived.by()` when liveQuery depends on a reactive value
**The problem:** `$derived(liveQuery(callback))` looks like it should re-run when a store value inside `callback` changes. It does NOT. Svelte tracks reactive dependencies synchronously during the expression evaluation. The `liveQuery` callback is called later inside Dexie's async context — Svelte's tracking is already finished. The dependency is never registered.
```svelte
<!-- ❌ WRONG: $events_slct.event_session_id is read inside the async callback.
Svelte never tracks it. The liveQuery is created once and never recreates
when event_session_id changes. -->
let lq__session = $derived(
liveQuery(() => db_events.session.get($events_slct.event_session_id))
);
```
```svelte
<!-- ✅ CORRECT: $derived.by() captures the ID in the outer synchronous closure.
Svelte tracks it. When event_session_id changes, $derived.by() re-runs,
creating a new liveQuery with the updated ID. -->
let lq__session = $derived.by(() => {
const id = $events_slct.event_session_id; // tracked here, synchronously
return liveQuery(() => db_events.session.get(id));
});
```
**Rule of thumb:** If the liveQuery result changes based on a reactive value (store property, `$state`, `$props`), always use `$derived.by()`. Reserve `$derived(liveQuery(...))` only for liveQueries that watch a table broadly and don't filter by a reactive value.
---
### Rule 2: Keep liveQuery closures pure (data-only)
**The problem:** Writing to a Svelte store inside a liveQuery callback runs inside Dexie's async transaction context. Svelte's reactive tracking is undefined there. The write may fire at unpredictable times and create hard-to-debug reactivity loops.
```svelte
<!-- ❌ WRONG: Store side-effect inside liveQuery async callback. -->
let lq__event_obj = liveQuery(async () => {
const obj = await db_events.event.get($events_slct.event_id);
if (obj) $events_slct.event_obj = obj; // BAD: side-effect in async context
return obj;
});
```
```svelte
<!-- ✅ CORRECT: liveQuery is pure data-only. Store sync happens in a $effect. -->
let lq__event_obj = liveQuery(async () => {
const id = $events_slct.event_id;
if (!id) return null;
return await db_events.event.get(id);
});
$effect(() => {
const result = $lq__event_obj;
if (result) {
untrack(() => {
// Cheap equality guard — only write if something actually changed.
if (result.updated_on !== $events_slct.event_obj?.updated_on ||
result.id !== $events_slct.event_obj?.id) {
$events_slct.event_obj = { ...result };
}
});
}
});
```
---
### Rule 3: Use cheap equality guards in `$effect` before writing to stores
Every store write in a `$effect` triggers downstream reactivity. Always guard with a comparison before writing. The cost of the comparison is always less than the cost of spurious re-renders.
**For single objects** — compare `id` + `updated_on` (O(1)):
```typescript
if (result.id !== $store.obj?.id || result.updated_on !== $store.obj?.updated_on) {
$store.obj = { ...result };
}
```
**For arrays** — join IDs into a string (O(n)), not `JSON.stringify` (O(n × field_count)):
```typescript
const new_ids = results.map(r => r.id).join(',');
const cur_ids = ($store.list ?? []).map(r => r.id).join(',');
if (new_ids !== cur_ids) {
$store.list = [...results];
}
```
**For flat objects** (e.g., merged config) — shallow key-by-key comparison (O(n keys)):
```typescript
function shallow_equal(a, b) {
const keys_a = Object.keys(a);
const keys_b = Object.keys(b);
if (keys_a.length !== keys_b.length) return false;
for (const k of keys_a) { if (a[k] !== b[k]) return false; }
return true;
}
if (!shallow_equal(current, new_val)) { $store = new_val; }
```
**Never use `JSON.stringify` for equality.** It serializes the full object tree on every reactive cycle and is O(total serialized bytes).
---
### Rule 4: Always use `untrack()` when writing to stores inside `$effect`
Without `untrack()`, reading a store to check its current value inside `$effect` registers it as a dependency — the effect re-runs whenever it writes, creating an infinite loop.
```svelte
<!-- ❌ WRONG: Reading $store.obj inside $effect creates a dependency loop. -->
$effect(() => {
const result = $lq__obj;
if (result.id !== $store.obj?.id) { // Reading $store.obj here is a dependency!
$store.obj = result; // This write re-triggers the effect.
}
});
```
```svelte
<!-- ✅ CORRECT: untrack() reads current store values without registering them
as reactive dependencies of the $effect. -->
$effect(() => {
const result = $lq__obj; // Tracked: effect re-runs when liveQuery emits
if (result) {
untrack(() => {
// Not tracked: reading $store.obj here won't cause a re-run.
if (result.id !== $store.obj?.id) {
$store.obj = result;
}
});
}
});
```
---
### Rule 5: Guard `console.log` calls with `log_lvl`
Raw `console.log(obj)` eagerly serializes objects (even large ones) on every call, blocking the main thread. All debug logging must be guarded.
```typescript
let log_lvl: number = $state(0); // Set to 0 in production; raise locally to debug.
// ❌ WRONG: Always runs, always serializes.
console.log('Result:', result_obj);
// ✅ CORRECT: Zero-cost when log_lvl is 0.
if (log_lvl) console.log('Result:', result_obj);
if (log_lvl > 1) console.log('Verbose:', result_obj); // Extra-verbose tier
```
**Never hardcode `log_lvl: 2` in a call-site or override `log_lvl` inside a function body.** The parameter default exists so callers can control verbosity. Overriding it forces debug logging regardless of what the caller passed.

View File

@@ -0,0 +1,230 @@
# Aether — Permissions and Security
**Last updated:** 2026-02-27
**Source of truth:** `src/lib/ae_utils/ae_utils__perm_checks.ts`, `src/lib/stores/ae_stores.ts`
---
## Access Level Hierarchy
Highest to lowest. Each level **inherits all access from every level below it**.
| Level | `access_type` string | Typical Use |
| --- | --- | --- |
| Super | `super` | OSIT internal — full system access |
| Manager | `manager` | Account managers |
| Administrator | `administrator` | Event/account admins |
| Trusted | `trusted` | **Onsite staff** — site passcode or AE login |
| Public | `public` | Site-wide passcode granted |
| Authenticated | `authenticated` | Identity verified (e.g. IDAA Novi UUID) |
| Anonymous | `anonymous` | Default — not signed in |
> **Note on Public vs Authenticated:** `public` is a *site-wide* unlock (anyone with the passcode). `authenticated` verifies a *specific identity*. In the hierarchy, public outranks authenticated because it implies broader site access.
---
## `$ae_loc` Store — Permission Flags
`$ae_loc` is a `persisted()` store (backed by localStorage). Key fields:
```typescript
$ae_loc.access_type // string: current access type ('anonymous', 'trusted', etc.)
// Cumulative boolean flags (true = "you have AT LEAST this level")
$ae_loc.anonymous_access // always true
$ae_loc.authenticated_access // true from authenticated and above
$ae_loc.public_access // true from public and above
$ae_loc.trusted_access // true from trusted and above ← most-used gate
$ae_loc.administrator_access // true from administrator and above
$ae_loc.manager_access // true from manager and above
$ae_loc.super_access // true only at super
// Exclusive check flags (true = "you are EXACTLY this level")
$ae_loc.trusted_check // true only if access_type === 'trusted'
$ae_loc.administrator_check // etc.
// (rarely needed — prefer the _access flags)
// Behavior flags
$ae_loc.edit_mode // boolean — user preference, see below
$ae_loc.adv_mode // boolean — advanced mode toggle
```
### Additional intermediate levels (in permission checks, not in hierarchy order)
`support`, `assistant`, `verified`, `provisional` — appear in `_access` flags but are not part of the canonical `access_level_order`. Treat as internal/intermediate.
---
## Edit Mode — Critical Rules
`$ae_loc.edit_mode` is a **user preference**, not a permission level.
**Rules that must never be broken:**
1. **Components must never write to `$ae_loc.edit_mode`** — only the system menu toggle and sign-out/permission-drop handlers may change it.
2. Edit mode is only available to `trusted` and above in 95% of modules (the toggle is hidden from lower-access users).
3. Edit mode persists across navigation — it is NOT reset by page loads or component mounts.
4. Sign-out and permission drops to below `authenticated` should reset `edit_mode` to `false`.
> **Background:** A bug was fixed (2026-02-27) where `ae_comp__badge_obj_view.svelte` was writing `$ae_loc.edit_mode = false` in a data-loading `$effect`, silently overriding the user's preference on every navigation to the badge print page.
---
## Authentication Methods
| Method | Grants | Used For |
| --- | --- | --- |
| Site passcode (`site_access_code_kv`) | `trusted`, `public`, or `authenticated` | Onsite staff and event attendees |
| AE Username + Password | `trusted` and above | Staff with AE accounts |
| Novi UUID | `authenticated` | IDAA members (Novi membership system) |
Passcodes are stored per-level in `$ae_loc.site_access_code_kv`:
```typescript
site_access_code_kv: {
administrator: null, // highest passcode tier
trusted: null, // onsite staff passcode
public: 'public1980', // example
authenticated: 'auth1980'
}
```
### `x-no-account-id` — Narrow Transport Exception
`x-no-account-id` is a transport-level escape hatch that strips account context before the request leaves the frontend. It is not a permission grant and it is not a replacement for JWT or `x-account-id`.
Use it only when the request truly cannot be made account-scoped. Current legitimate cases should stay narrow:
1. Bootstrap / site-domain discovery before the account is known.
2. Explicit public or guest endpoints that do not have an account context.
3. Helper paths that intentionally need a global-default fallback.
If a request already has a valid account context, prefer `x-account-id` and let the JWT carry session identity. Treat any new `x-no-account-id` use as temporary until it is reviewed and either replaced or justified.
---
## Utility Functions
### `process_permission_checks(access_type: string)`
Returns a full permission object (`_check` and `_access` flags) for a given access type string. Used when access type changes to update `$ae_loc`.
```typescript
import { process_permission_checks } from '$lib/ae_utils/ae_utils__perm_checks';
const checks = process_permission_checks('trusted');
// checks.trusted_access === true
// checks.administrator_access === false
```
### `compare_access_levels(level_a, level_b)`
Returns `1` if `level_a` is higher, `-1` if lower, `0` if equal. Useful for threshold comparisons.
---
## Privacy and Security Rules
### IDAA — International Doctors in Alcoholics Anonymous
- **ALL IDAA content is private. Always. No exceptions.**
- BB (Bulletin Board / Posts), Archives, Recovery Meetings — all require authentication.
- IDAA users authenticate via Novi UUID at `authenticated` level or higher.
- A prior agent accidentally exposed IDAA BB data publicly — treat any IDAA exposure as Sev-1.
#### IDAA IndexedDB (IDB) Caching — Auth-Before-Cache Rule
**Root cause discovered 2026-04:** SvelteKit `+page.ts`/`+layout.ts` load functions run *before* layout `$effect` hooks and fire during link prefetch (hover). `if (browser)` guards do NOT prevent this — they only prevent SSR. This means API calls inside these files execute before Novi auth completes, writing private IDAA data to the user's IndexedDB even for unauthenticated sessions.
**The fix — established pattern for all IDAA routes:**
1. **Load/layout `.ts` files = thin shells.** Pass URL params only. No API calls. No `if (browser)` data fetching.
2. **Data loading = `$effect` in `.svelte` files**, gated on:
```svelte
if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;
```
3. **Three IDB purge paths** in `(idaa)/+layout.svelte` (auth failure, anonymous no-UUID, Reset & Retry button) clear `db_posts`, `db_archives`, and `db_events` tables.
**Auth path matrix:**
| User type | `novi_verified` | `trusted_access` | Can load data? | Purge fires? |
| --- | --- | --- | --- | --- |
| Anonymous / unauthenticated | false | false | No | Yes (Case 1) |
| Novi-verified IDAA member | true | false | Yes | No |
| Manager / trusted access | false | true | Yes | No (Case 3 exemption) |
**Applied to routes (as of 2026-04-19):**
- `idaa/bb/+page.svelte` — `$effect` gate added; `bb/+page.ts` stripped
- `idaa/bb/[post_id]/+page.ts` — stripped; loading handled by trigger in `bb/+layout.svelte`
- `idaa/archives/+page.svelte` — `$effect` gate added; `archives/+layout.ts` stripped
- `idaa/archives/[archive_id]/+page.svelte` — `$effect` gate added; `[archive_id]/+page.ts` stripped
- `idaa/recovery_meetings/+page.svelte` — `$effect` gate already present; `+layout.ts` stripped
- `idaa/recovery_meetings/[event_id]/+page.svelte` — `$effect` gate added; `+page.ts` stripped
**When adding a new IDAA route:** never put API calls in `+page.ts`/`+layout.ts`. Always gate data fetching with the `$effect` pattern above.
### Journals
- Private personal data. Always authenticated. Passcode/encryption features exist.
- Never expose journal content publicly.
### `PUBLIC_AE_API_SECRET_KEY`
- Audit closed 2026-03-11. `PUBLIC_*` prefix is by design — key is always in the client bundle.
- Anonymous site-domain lookup uses the limited-permission `PUBLIC_AE_BOOTSTRAP_KEY` instead.
- Security model: API key is one layer; JWT + `x-account-id` scoping provides the primary auth.
- Do not introduce new usages. Prefer `PUBLIC_AE_BOOTSTRAP_KEY` for unauthenticated lookups.
### JWT usage guidance
- JWTs are the preferred proof of an established session. Keep them attached to authenticated flows instead of leaning on transport-level bypasses.
- If a route or helper can work with a JWT and an account ID, it should not need `x-no-account-id`.
- If a helper still needs the bypass today, document the reason and add a removal target.
### Email Display
Non-trusted users must never see a full email address. Obscure using:
```typescript
// joh***@example.com
function obscure_email(email: string): string {
const at = email.indexOf('@');
if (at < 0) return email;
return `${email.slice(0, Math.min(3, at))}***${email.slice(at)}`;
}
```
This pattern lives in `ae_comp__badge_obj_li.svelte` — move to `ae_utils` if needed elsewhere.
---
## Module-Specific Permission Patterns
### Journals — Entry Config Admin Actions
- Entry configuration admin controls are gated to `trusted_access` and above.
- `manager_access` and `administrator_access` see the Delete action, which performs a hard delete.
- `trusted_access` users see Remove instead, which follows disable semantics rather than a hard delete.
- The Admin section is the place for staff notes, enabled/default access state, and destructive entry actions; the template toggle belongs in Metadata, while visibility/audience flags remain separate.
### Events — Badges
| Scenario | Visibility | Print Action | Review Actions |
| --- | --- | --- | --- |
| Anonymous / below trusted | Unprinted only | None (name display only) | Email Review Link button (→ email API) |
| Trusted, not Edit Mode | Unprinted only | Clickable (first print) | Email Review Link button |
| Trusted, Edit Mode | All non-hidden | Clickable incl. reprint; shows `Nx` count | Email Review Link + direct Review Link (clipboard) |
- Print count badge: shown as `Nx` (e.g. `2×`) next to the printer icon when `print_count >= 1`
- Edit mode for badges: limited to `trusted_access` users (toggle hidden from lower levels)
- `person_passcode` field (for attendee-gated review URL): **not yet in DB** as of 2026-02-27
### IDAA
- Auth gate test must be the **first test** in any test file — privacy enforcement is a hard requirement.
- Default required permission: `trusted_access` or higher for module access.
---
## Common Template Patterns
```svelte
<!-- Gate on trusted access -->
{#if $ae_loc.trusted_access}
<!-- Gate on edit mode (always check trusted too — edit mode alone is insufficient) -->
{#if $ae_loc.trusted_access && $ae_loc.edit_mode}
<!-- Gate on administrator -->
{#if $ae_loc.administrator_access}
<!-- Show full vs obscured email -->
{$ae_loc.trusted_access ? email : obscure_email(email)}
```
> Never gate purely on `$ae_loc.edit_mode` without also checking a permission level. Edit mode is a UI preference, not a permission grant.

View File

@@ -0,0 +1,414 @@
# Aether UI — Component Style Patterns
> **Version:** 1.0 (2026-03-06)
> **Author:** One Sky IT / Scott Idem
> **Scope:** All Aether SvelteKit frontend components
> **Related:** `GUIDE__AE_UI_Style_Guidelines.md` (color rules, token definitions, a11y)
This document is a recipe book. Copy these patterns directly. Deviate only when a component's specific purpose genuinely requires it — and document why in a comment.
---
## 1. Hero Card
*Used for: session identity, presenter identity, location identity — the top-of-page "Is this the right one?" card.*
```svelte
<div class="rounded-xl border border-surface-200-800 bg-surface-50-900 shadow-sm overflow-hidden">
<div class="px-4 pt-4 pb-3 flex flex-col gap-3">
<!-- primary heading (h1 / h2) -->
<h1 class="text-2xl font-bold leading-snug">{name}</h1>
<!-- info chips row -->
<div class="flex flex-wrap gap-2 items-center">
<!-- time chip → primary color -->
<span class="inline-flex items-center gap-1.5 text-sm font-semibold px-3 py-1 rounded-full bg-primary-500/10 text-primary-700 dark:text-primary-300 transition-colors duration-200">
<span class="fas fa-clock text-xs" aria-hidden="true"></span>
Mon, Jan 1 2:00 PM
</span>
<!-- room/location chip → tertiary color -->
<span class="inline-flex items-center gap-1.5 text-sm font-semibold px-3 py-1 rounded-full bg-tertiary-500/10 text-tertiary-700 dark:text-tertiary-300 transition-colors duration-200">
<span class="fas fa-map-marker-alt text-xs" aria-hidden="true"></span>
Room 201
</span>
</div>
</div>
</div>
```
**Skeleton loading variant:**
```svelte
<!-- While liveQuery resolves -->
<div class="h-7 w-2/3 bg-surface-200-800 animate-pulse rounded"></div>
<div class="h-5 w-1/2 bg-surface-200-800 animate-pulse rounded-full"></div>
```
---
## 2. Standard Content Card
*Used for: description text, notes, secondary info panels.*
```svelte
<div class="rounded-lg border border-surface-200-800 bg-surface-50-900 px-4 py-3">
<!-- optional eyebrow label -->
<span class="text-xs font-bold uppercase tracking-wide opacity-40 block mb-1">Description</span>
<p class="whitespace-pre-wrap text-sm leading-relaxed">{description}</p>
</div>
```
**Variant — inner secondary panel:**
```svelte
<div class="bg-surface-100-900 rounded-lg px-3 py-2">
<!-- inner content -->
</div>
```
---
## 3. Table Row
*Used for: session search results tables, any `<tbody><tr>` list.*
```svelte
<tr
class="relative transition-colors duration-200"
class:opacity-50={obj?.hide}
class:preset-tonal-warning={!obj?.enable}
>
<td>
<a
href="/path/to/{obj.id}"
class="font-bold text-lg hover:text-primary-500 transition-colors duration-200"
>
{obj.name}
</a>
</td>
</tr>
```
- `opacity-50` for hidden/archived records
- `preset-tonal-warning` for disabled (not enabled) records — amber background
- `transition-colors duration-200` on both `<tr>` and `<a>`
---
## 4. List Item Card
*Used for: presentation list items, session details lists, any vertical card stack.*
```svelte
<ul class="space-y-4">
<li class="space-y-3 border border-surface-200-800 bg-surface-50-900 p-4 rounded-xl shadow-sm transition-colors duration-200">
<!-- Card heading bar -->
<h4 class="text-lg font-bold rounded-lg px-3 py-2 bg-surface-100-900 flex flex-wrap items-center gap-2">
{name}
<!-- code/tag badge -->
<span class="text-xs preset-tonal-warning px-2 py-0.5 rounded-md leading-none">
{code}
</span>
</h4>
<!-- Description block -->
<pre class="whitespace-pre-wrap p-3 bg-surface-100-900 rounded-lg text-sm">{description}</pre>
</li>
</ul>
```
**Rules:**
- Background goes on `<li>`, NOT on `<ul>`
- `<ul>` gets only spacing: `space-y-4` — never a background color
- The `<ul>` container in components should have `overflow-x-auto`, not `overflow-x-scroll`
---
## 5. Info Chips
### Time / Date chip (Primary — Teal)
```svelte
<span class="inline-flex items-center gap-1.5 text-sm font-semibold px-3 py-1 rounded-full bg-primary-500/10 text-primary-700 dark:text-primary-300 transition-colors duration-200">
<span class="fas fa-clock text-xs" aria-hidden="true"></span>
Monday, March 6 2:00 PM
</span>
```
### Location / Room chip (Tertiary — Indigo)
```svelte
<span class="inline-flex items-center gap-1.5 text-sm font-semibold px-3 py-1 rounded-full bg-tertiary-500/10 text-tertiary-700 dark:text-tertiary-300 transition-colors duration-200">
<span class="fas fa-map-marker-alt text-xs" aria-hidden="true"></span>
Main Hall B
</span>
```
### Code / Tag badge
```svelte
<span class="text-xs preset-tonal-warning px-2 py-0.5 rounded-md leading-none">
{code}
</span>
```
### Status badge (edit mode only)
```svelte
{#if $ae_loc.edit_mode}
<span class="badge preset-tonal-surface text-xs">code: {obj.code}</span>
{/if}
```
### Success count badge
```svelte
<span class="badge preset-tonal-success" class:hidden={!fileCount}>
<span class="fas fa-file-alt m-1" aria-hidden="true"></span>
{fileCount}×
</span>
```
---
## 6. Empty State Panel
*Used for: "No results found", "No sessions match your search", "Nothing to show yet".*
```svelte
<section
class="preset-tonal-warning p-6 rounded-xl shadow-sm lg:max-w-lg mx-auto"
role="status"
aria-live="polite"
>
<div class="flex flex-col items-center gap-2 text-center">
<span class="fas fa-search text-3xl opacity-50" aria-hidden="true"></span>
<strong class="text-xl">No sessions found</strong>
<p class="text-base opacity-80">
Use the search bar above to find your session.
</p>
</div>
<!-- optional details card -->
<div class="bg-surface-50-900/60 rounded-lg p-3 mt-4">
<span class="text-xs font-bold uppercase tracking-wide opacity-50 block mb-2">Search by any of:</span>
<ul class="space-y-1 text-sm">
<li class="flex items-center gap-1.5">
<span class="fas fa-angle-right text-xs opacity-50" aria-hidden="true"></span>
Session name
</li>
</ul>
</div>
</section>
```
---
## 7. Warning / Error Inline Banners
*Used for: disabled records, agreement-required gates.*
```svelte
<!-- Warning (amber — disabled/inactive) -->
<div class="bg-warning-100 p-4 border border-warning-300 rounded-md">
<h2 class="h3">
<span class="fas fa-exclamation-triangle text-warning-500 m-1" aria-hidden="true"></span>
Location Disabled
</h2>
<p>This location is currently disabled.</p>
</div>
<!-- Error (red — blocked/failed) -->
<div class="bg-error-100 p-4 border border-error-300 rounded-md">
<h2 class="h3">
<span class="fas fa-exclamation-triangle text-error-500 m-1" aria-hidden="true"></span>
Presenter Disabled
</h2>
<p>This presenter is currently disabled.</p>
</div>
```
---
## 8. File Upload Zone (`Comp_event_files_upload`)
The `class_li` prop styles the outer upload drop zone container:
```svelte
<Comp_event_files_upload
class_li="border border-surface-200-800 rounded-xl p-4 bg-surface-50-900 hover:bg-surface-100-900 transition-colors duration-200"
link_to_type="event_presenter"
link_to_id={presenter_id}
>
{#snippet label()}
<span>
<div class="text-lg">
<span class="fas fa-upload" aria-hidden="true"></span>
<strong>Upload presenter files</strong>
</div>
<div class="text-sm opacity-60 italic">
Supported: pptx, key, mp4, pdf, docx, xlsx, txt
</div>
</span>
{/snippet}
</Comp_event_files_upload>
```
**Note:** The label sub-description text uses `opacity-60 italic` — never `text-gray-600 dark:text-gray-400`.
---
## 9. Section Component Wrapper
*Used for: `ae_comp__event_*_obj_li.svelte` outer `<section>` elements.*
```svelte
<section
class="ae_comp event_X_obj_li px-0.5 py-2 space-y-2 min-w-full w-full container overflow-x-auto {container_class_li}"
>
```
**Rules:**
- `overflow-x-auto` — never `overflow-x-scroll`
- **Never include debug breakpoint borders** — remove before committing:
```
sm:border-l-red-400 md:border-l-yellow-400 lg:border-l-gray-100
sm:dark:border-l-red-600 md:dark:border-l-yellow-600 lg:dark:border-l-gray-700
border-dashed border-y-transparent border-r-transparent
```
---
## 10. Agreement / Consent Form Layout
*Used for: `ae_comp__event_presenter_form_agree.svelte`, `ae_comp__event_session_poc_form_agree.svelte`.*
```svelte
<!-- Consent text container -->
<div class="bg-surface-100-900 p-4 border border-surface-200-800 rounded-lg space-y-4">
<Element_data_store ds_code="consent_text" ds_type="html" class_li="p-2" />
</div>
<!-- Presenter name/identity highlight line -->
<p class="text-lg preset-tonal-warning p-2 rounded-t-md">
<strong>{presenter_name} ({email})</strong>
agrees to the following terms and conditions:
</p>
```
---
## 11. Modal Usage (Flowbite-Svelte)
```svelte
<!-- ✅ Correct — no manual color class; theme handles styling -->
<Modal title="Host Profile" bind:open={show_modal}>
<ProfileComponent />
{#snippet footer()}
<button onclick={() => show_modal = false} class="btn preset-tonal-warning">
Close
</button>
{/snippet}
</Modal>
<!-- ❌ Wrong — manual gray overrides bypass the theme -->
<Modal class="bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200 ...">
```
**Rule:** Never set `bg-*` or `text-*` color classes on `<Modal>`. Let the Flowbite component + active theme handle it. Only structural layout classes (`shadow-md`, `relative`, `flex`, etc.) belong on the `class` prop if needed.
---
## 12. Muted / Secondary Text
Replace all `text-gray-*` patterns with opacity wrappers on inherited text color:
```svelte
<!-- ✅ Theme-aware muted text -->
<span class="text-sm opacity-60">Secondary label</span>
<span class="text-sm opacity-40 italic">Hint or placeholder text</span>
<span class="text-xs font-bold uppercase tracking-wide opacity-40">Section eyebrow</span>
<!-- ❌ Fixed-color muted text -->
<span class="text-sm text-gray-500">Secondary label</span>
<span class="text-sm text-gray-600 dark:text-gray-400 italic">Hint text</span>
```
---
## 13. QR Code (Async Toggle)
```svelte
<!-- Gate on typeof === 'string', not truthy.
The store holds boolean `true` as a loading placeholder, which would
render as a broken <img src="true"> if not guarded. -->
{#if $lq__obj && typeof $store.qr_url?.[$lq__obj.id] === 'string'}
<div class="float-right ml-3 mb-1 flex flex-col items-center gap-1">
<button
type="button"
onclick={() => $store.qr_bigger = !$store.qr_bigger}
class="rounded focus-visible:ring-2 focus-visible:ring-primary-500"
title="Toggle QR code size"
aria-label="Toggle QR code size"
>
<img
src={$store.qr_url[$lq__obj.id]}
class="transition-all duration-500 rounded border border-surface-200-800"
class:h-20={!$store.qr_bigger}
class:w-20={!$store.qr_bigger}
class:h-40={$store.qr_bigger}
class:w-40={$store.qr_bigger}
alt="QR code link to this page"
/>
</button>
</div>
{/if}
```
---
## 14. POC / Host Button Pattern
```svelte
<div class="flex items-center gap-2">
<span class="text-sm font-semibold opacity-60">Host:</span>
<button
type="button"
class="btn btn-sm preset-tonal-primary transition-colors duration-200"
onclick={() => show_profile = true}
aria-haspopup="dialog"
>
<span class="fas fa-id-card mr-1" aria-hidden="true"></span>
{full_name}
</button>
</div>
```
---
## 15. Icon Usage Rules
| Context | Pattern |
|---|---|
| Decorative / visual only | `<span class="fas fa-clock" aria-hidden="true"></span>` |
| Icon with visible adjacent text | `aria-hidden="true"` on icon, text provides meaning |
| Icon-only button (no visible text) | `aria-label="Description"` on the `<button>` |
| Icon used as bullet point | `aria-hidden="true"` on icon |
**Never use `<i>` tags.** Always `<span class="fas ...">`.
---
## 16. Native `<select>` Dark Mode
Browser-native `<select>` and `<option>` elements **cannot be reliably styled** with Tailwind `dark:` utilities — the browser controls `<option>` rendering and ignores most CSS overrides. This causes the "light on light hover" bug in dark mode.
**Fix — add `color-scheme` directive to force OS-level dark styling:**
```svelte
<script>
import { ae_loc } from '$lib/ae_core/ae_stores';
</script>
<!-- Forces browser to render the select widget in dark/light OS mode matching your theme -->
<select
class="select text-xs p-1"
style:color-scheme={$ae_loc.dark_mode ? 'dark' : 'light'}
>
{#each options as opt}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
```
**Why this works:** `color-scheme: dark` instructs the browser to use its native dark-mode widget rendering (dark `<select>`, dark `<option>` backgrounds). It's the only cross-browser mechanism that affects `<option>` hover colors.
**Alternative — replace with custom Skeleton/Flowbite component** if you need full styling control (e.g., color-coded options, icons). Native `<select>` is acceptable for simple purpose dropdowns with the `color-scheme` fix above.
**Store reference:** `$ae_loc.dark_mode` — boolean, set by the theme engine in `ae_stores.ts`.

View File

@@ -0,0 +1,569 @@
# Aether UI/UX — Future Ideas
> Collection of concrete UX improvements for the Aether frontend. Each entry includes
> the rationale, current behavior, proposed change, and implementation notes.
> **Date:** 2026-05-17
---
## IDAA Recovery Meetings
### 1. Guided empty state with active filters — ✅ Implemented 2026-05-18
**Current behavior:** When filters return 0 results, the page shows:
"No recovery meetings found matching your criteria."
The member has no indication whether this is a bug, genuinely no data, or just
overly narrow filters.
**Proposed change:** When filters are active AND the result count is 0, show a
helpful prompt instead of the bare message:
```
No meetings found for these filters.
Try broadening your search or [Clear all filters →]
```
**Implementation notes:**
- Add a `has_active_filters` derived in `+page.svelte` that checks whether any of
`qry__physical`, `qry__virtual`, `qry__type`, or `qry__fulltext_str` is set.
- In the template's `{:else}` block (line ~443), branch on `has_active_filters`:
- `true` → show the guided message + "Clear Filters" button
- `false` → show the existing escape-hatch flow (timed "Refresh Meeting Cache" button
after 8 seconds, since zero unfiltered results always indicates a problem)
- The "Clear Filters" button resets all four filter fields to `null`/`''` and bumps
`search_version` to trigger a fresh unfiltered search.
- Distinct from the `error` state — this is a successful search (`qry__status === 'done'`)
with an empty result set.
**Implemented:** `src/routes/idaa/(idaa)/recovery_meetings/+page.svelte`. `has_active_filters`
derived checks `qry__physical`, `qry__virtual`, `qry__type`, and `qry__fulltext_str`. Empty
state branches on `has_active_filters`: active filters → guided message + "Clear Filters"
button; no active filters → existing escape-hatch flow (timed "Refresh Meeting Cache" after
8 seconds).
---
### 2. Quick-filter chips below the search bar — ✅ Implemented 2026-05-18
**Current behavior:** Members toggle filters via small checkboxes (Virtual, In-person)
and radio buttons (All, IDAA, Caduceus, Family Recovery). These require precise
mouse/tap targeting and scanning several lines of filter UI to discover and use.
**Proposed change:** Add a row of preset chip buttons directly below the search input:
```
[🖥 Virtual] [🏠 In-Person] [🩺 IDAA] [Caduceus] [Family Recovery] [All Types]
```
- Each chip toggles the corresponding filter (`qry__virtual`, `qry__physical`, `qry__type`)
and triggers an immediate search.
- Selected chips get a filled/pressed style; unselected chips are outlined.
- "All Types" is the default selected state (no type filter). Clicking another type
chip deselects "All Types" (radio behavior for the type dimension). Virtual and
In-person are independent toggles (checkbox behavior — can select both).
- The existing checkboxes/radio buttons remain as the underlying state storage
(`$idaa_loc.recovery_meetings.*`). The chips are a convenience layer — they write
to the same store fields and call `handle_search_trigger()`.
**Implementation notes:**
- Place in `ae_idaa_comp__event_obj_qry.svelte` between the search input row and the
current filter rows.
- Optionally hide the existing checkbox/radio filter rows when the chips are present
(or keep both — the checkboxes serve as accessible form controls; the chips are
the primary visual interaction).
- On mobile, chips wrap to a second row naturally with `flex-wrap`.
**Implemented:** `ae_idaa_comp__event_obj_qry.svelte`. Chips replaced the old
checkbox/radio/select UI entirely rather than layering on top. Two chip rows:
Row 1 — My Meetings (first), Virtual, In-Person. Row 2 — All / IDAA / Caduceus /
Family Recovery type chips. Cycling sort button replaces separate sort options
(see item below). Max Results uses a +/ stepper. Sort and max are in a third
row below the chips, inside the same `<form>` constraint.
---
### 3. Language: "Searching..." vs "Loading..."
**Current behavior:** The loading state always shows the same message:
```
🔄 Searching...
```
This appears on initial page load (when the user hasn't typed anything) and after
the user clicks Search or toggles a filter. The word "Searching" implies the user
initiated a search, which is misleading on initial page load — it's a cold cache
load, not an active search.
**Proposed change:** Distinguish the two loading contexts:
| Context | Message |
|---------|---------|
| Initial page load (no filters, no search text) | "Loading meetings..." |
| User clicked Search or toggled a filter | "Searching..." (keep current) |
**Implementation notes:**
- In `+page.svelte` template around line 422, check whether `qry__fulltext_str` is
empty AND no filter checkboxes/radios are active. If so, show "Loading meetings...";
otherwise show "Searching...".
- This is purely a label change — no logic changes needed. The condition can be the
same `has_active_filters` derived from item #1.
- Also update the list component's standalone loading state in
`ae_idaa_comp__event_obj_li.svelte` line 556-558 to use the same distinction.
---
### 4. Filter row collapsing on mobile
**Current behavior:** The query bar has three filter rows (Location checkboxes,
Type radios, Max/Sort selects) plus the search input row and the action button row.
Combined, this takes roughly 200px of vertical space. On mobile — especially inside
the Novi iframe on a phone — meeting cards are pushed below the fold.
**Proposed change:** On viewports below `md` (768px), collapse the Location and Type
filter rows behind a "Filters ▾" toggle. The Max Results and Sort selects stay visible
since they're used frequently. The action buttons (Show Hidden, Create Meeting, Export)
move inside the collapsed panel or stay visible based on available width.
```
[Search input............................] [Search]
[Filters ▾] [Max: 150 ▾] [Sort: Last Updated ▾]
```
Clicking "Filters ▾" expands the panel with Location checkboxes and Type radios.
**Implementation notes:**
- Use a `$state` boolean `show_filters` (session-only, resets on page load).
- Wrap the filter rows in a `{#if show_filters}` block.
- Persist in `$idaa_sess.recovery_meetings.show_filters_expanded` if you want the
state to survive navigation within the module (same tab session).
- The Tailwind `md:` breakpoint works for the collapse trigger: `class:hidden={!show_filters}`
combined with `class:md:block` to always show on desktop.
- Test inside the Novi iframe — Bootstrap v3 may add its own `hidden` behavior on
`md` breakpoints that conflicts with Tailwind's.
---
### 5. Human-readable schedule line on cards
**Current behavior:** The meeting card displays weekdays as a flat, dense span list:
```
Sunday Monday Wednesday Friday
```
The timezone is shown separately as `(America/Chicago)`, and the start time is in
a compact `7:00 PM` format. These three pieces of information are visually separated
and require the member to mentally assemble the schedule.
**Proposed change:** Render a computed one-liner that combines them:
```
🕐 Mondays, Wednesdays, Fridays at 7:00 PM CT
```
- Weekday names are built from the `weekday_*` booleans on the event object.
- "Mondays, Wednesdays" uses the range-joining convention (comma-separated, "and"
before the last item for two days; "Mondays through Fridays" for consecutive spans
of 3+ days).
- Timezone abbreviation is extracted from `timezone` (e.g., `America/Chicago``CT`,
`America/New_York``ET`). A small lookup table handles the common ones; fall back
to the raw timezone string for unknown values.
- If `timezone` is null/missing, fall back to the current flat display — don't
silently drop information.
- Today's meetings could optionally get a subtle "Today" badge or highlight (extra
polish, not required for the initial version).
**Implementation notes:**
- Add a `$derived` in `ae_idaa_comp__event_obj_li.svelte` that computes the schedule
string from the event object's `weekday_*` fields, `recurring_start_time`, and
`timezone`.
- Helper function in `ae_util` for the weekday list → natural language string
(e.g., `['Monday', 'Wednesday', 'Friday']``"Mondays, Wednesdays, and Fridays"`).
- Helper function or small lookup for timezone → abbreviation.
- Fall back to the current flat display when `timezone` is missing to avoid losing
information.
---
### 6. Show result count during search, not just after
**Current behavior:** The result count badge ("Results: 25") only appears inside the
list wrapper component (`ae_idaa_comp__event_obj_li.svelte` line 98-108) when the
visible result list is non-empty. During loading, the user sees only a spinner with
no indication of how many meetings exist or what the search is operating on.
**Proposed change:** Show a result count line at the page level (in `+page.svelte`)
that is always visible once the first search completes:
```
25 of 140 meetings ← after search completes, with result count + total
Searching 140 meetings... ← during initial load (cold cache, no prior result)
0 results for these filters ← empty but filters are active (ties into item #1)
```
**Implementation notes:**
- Lift the count display from the list component to `+page.svelte`, placed between
the query bar (`Comp__event_obj_qry`) and the list wrapper.
- The total count is available from the IDB fast path: after the initial unfiltered
search populates `db_events.event`, the total is `db_events.event.count()` (or
the count of records matching `account_id`).
- The visible count is `event_id_li.length` after search completes.
- Store the last known total in a `$state` variable so it persists across searches
(the total changes infrequently). Refresh the total on the first search after
page load.
- Format: `{visible} of {total} meetings` when filters/search are active;
`{visible} meetings` when browsing all (no active filters).
- During loading with no prior results: show "Loading meetings..." (from item #3)
rather than a count.
---
### 7. "Live Now" and "Starting Soon" indicators
**Current behavior:** Meetings are shown in a static list. To find one happening
now, a member must scan the "When" line of multiple cards and compare the time
to their own clock.
**Proposed change:** Add a high-visibility badge or pulse indicator for meetings
that are currently in progress or starting in the next 15 minutes.
- "LIVE NOW" (Green pulse badge) → if `current_time` is within `[start, start + 1 hour]`.
- "STARTING SOON" (Yellow badge) → if `current_time` is within `[start - 15 min, start]`.
- On the card, move the "Join Zoom" or "Join Jitsi" button to the very top or
make it significantly larger when the meeting is live.
**Implementation notes:**
- Add a `$derived` state `is_live` and `is_starting_soon` to the card component.
- Requires calculating "current time in meeting's timezone" using `Temporal` or
a date helper.
- Ensure the pulse animation is subtle and respects `prefers-reduced-motion`.
---
### 8. Local Timezone Conversion
**Current behavior:** Meetings show their native timezone (e.g., "7:00 PM America/Chicago").
The "Your TZ" line is currently a placeholder and doesn't perform conversion.
**Proposed change:** Automatically detect the member's browser timezone and
show the converted time if it differs from the meeting's native timezone.
```
🕐 7:00 PM CT (8:00 PM ET your time)
```
**Implementation notes:**
- Use `Intl.DateTimeFormat().resolvedOptions().timeZone` to get the user's TZ.
- If `user_tz !== meeting_tz`, perform the conversion.
- If the conversion results in a different day (e.g., late night ET vs early morning Europe),
prefix with "Tomorrow at..." or "Yesterday at...".
---
### 9. Favorites / "My Meetings" — ✅ Implemented 2026-05-18
**Current behavior:** Members scan the full list every time they want to find
their regular weekly meeting.
**Proposed change:** Add a "Star" icon to every meeting card.
- Starring a meeting adds it to a `favorites` list stored in `$idaa_loc`.
- Favorited meetings are pinned to the top of the list by default, regardless
of other sort orders.
- Add a "Favorites" filter toggle in the query bar to show *only* starred meetings.
**Implementation notes:**
- Store as an array of `event_id` strings in `$idaa_loc.recovery_meetings.favorites`.
- Update the `visible_event_obj_li` derived in `+page.svelte` to prioritize
these IDs in the sort logic.
**Implemented:** Star toggle on the `[event_id]` detail page
(`src/routes/idaa/(idaa)/recovery_meetings/[event_id]/+page.svelte`).
"My Meetings" filter chip is first in the filter chip row on the list page.
**Implementation differs from proposal:** favorites stored server-side in a
`data_store` record (code: `idaa_meetings_favorites`) as a UUID-keyed JSON map
rather than in `$idaa_loc` — this means favorites persist across browsers and
devices without Novi write capability. Pinning favorites to the top of the list
was not implemented; the filter chip shows only favorites instead.
---
### 10. "Add to Calendar" (iCal / Google)
**Current behavior:** Members must manually create calendar events if they
want reminders for recurring meetings.
**Proposed change:** Add an "Add to Calendar" dropdown button on the meeting
detail page (and optionally the card).
- Generates a `.ics` file or a Google Calendar URL with the recurring rule
(e.g., "Every Wednesday at 7pm").
- Includes the meeting name, description, and the Zoom/Jitsi link in the location field.
**Implementation notes:**
- Use a helper to generate RFC 5545 `RRULE` strings from the `weekday_*` and
`recurring_pattern` fields.
- Include the `attend_url` in the calendar event description for one-tap join
from phone lock screens.
---
### 11. Geographic Search for In-Person Meetings
**Current behavior:** The only location filter is a binary "Physical" checkbox.
Members must use fulltext search (e.g., "Chicago") to find local meetings.
**Proposed change:** Add a "City/State" search input or a map view.
- When `Physical` is checked, show a "Near [City, State]" input.
- Map view (optional): A toggle to switch from "List" to "Map" view, plotting
meetings on a map using their `location_address_json` coordinates.
**Implementation notes:**
- The `event` table has `location_address_json` which often contains city/state.
- Simple implementation: a city-picker dropdown populated from the distinct
`location_address_json->>'$.city'` values in the current result set.
---
### 12. Prominent "Join" button for virtual meetings
**Current behavior:** On each meeting card, the Zoom or Jitsi join link is rendered
as a small `btn-sm` inside the content area, visually equivalent to other label/value
rows. The "Meeting Details" button at the top of the card is rendered *larger* than the
join link — meaning the primary action for a member who wants to attend a meeting right
now is visually subordinate to a navigation link.
The copy-to-clipboard button for the join link is gated behind `$ae_loc.manager_access`,
so regular members have no easy way to share the link with a sponsee.
**Proposed change:** For virtual meetings, elevate the join button to a full-width
prominent CTA inside the card header area, directly below the meeting name and badges:
```
┌──────────────────────────────────────────────────┐
│ 📅 Monday Night IDAA Discussion 🖥 Virtual │
│ │
│ [ 🎥 Join Zoom Meeting ] ← full-width │
│ [ 📋 Meeting Details ] │
└──────────────────────────────────────────────────┘
```
- The Join button uses `preset-filled` (solid) styling; Meeting Details uses
`preset-outlined` (hollow). This makes the action hierarchy visually clear.
- On mobile especially, a full-width join button is much easier to tap than a
small inline link buried inside label rows.
- Replace manager-only clipboard with a Web Share API button for all members on
virtual meetings: `navigator.share({ title, url })` on mobile triggers the native
OS share sheet. Fall back to clipboard copy on desktop (where `navigator.share`
is often unavailable). This lets members easily send a meeting link to a sponsee.
- Passcode, if present, moves to the Meeting Details page — exposing it in the
list view is unnecessary and clutters the card.
**Implementation notes:**
- In `ae_idaa_comp__event_obj_li.svelte`, move the Zoom/Jitsi attend block from
the `event__content` section up into the `ae_options` div (line ~200), rendered
only when `idaa_event_obj?.virtual` is true and an attend URL exists.
- Keep the existing small label/link in `event__content` as a fallback for when
the prominent button is not shown (non-virtual meetings may still have a URL).
- Web Share: `{#if navigator?.share}` guard; wrap in a try/catch (user cancels
the share sheet throws `AbortError`).
- The Live Now / Starting Soon badges from item #7, when implemented, should also
interact with this button — e.g., a pulsing green border when the meeting is live.
---
### 13. "Today's Meetings" section at the top of the list
**Current behavior:** The meeting list shows all results sorted by the selected sort
order. To find a meeting happening today, a member must scan every card's "When" line
and mentally compare it to the current day and time. There is no at-a-glance view
of what's available right now or later today.
This is distinct from the "Live Now" badge in item #7 (which marks individual cards
after they're already displayed in a long list). This is a dedicated section pinned
above the main results.
**Proposed change:** Add a collapsible "Today" section at the very top of the results
list that shows only meetings scheduled on the current day of the week, sorted by
start time:
```
▼ Today — Sunday, May 17 3 meetings
┌─────────────────────────────────────────────────┐
│ Sunday Serenity Discussion 7:00 AM ET 🔴Live │
│ IDAA Sunday Big Book 2:00 PM CT │
│ Sunday Night IDAA 8:00 PM ET │
└─────────────────────────────────────────────────┘
All Meetings (140)
...
```
- The section is collapsed by default if it's empty (no meetings today).
- Meetings in the "Today" section also appear in the main list below — this is a
quick-access shortcut, not a filter.
- Past meetings (start time has already passed today) are dimmed but still shown;
a meeting may still be in progress.
**Implementation notes:**
- Compute current day-of-week in the browser: `new Date().getDay()` → 0=Sunday, 6=Saturday.
Map to the `weekday_*` boolean fields on the event object (e.g., day 0 → `weekday_sunday`).
- Filter `visible_event_obj_li` (already computed in the list wrapper) for items where
the matching `weekday_*` field is truthy. Sort by `recurring_start_time`.
- Store collapse state in `$idaa_sess.recovery_meetings.today_section_expanded` (session
only; default true so it's visible on first load).
- Renders correctly in the Novi iframe since it's just a filtered sub-list of existing
data — no additional API calls needed.
- If item #7 (Live Now) is implemented, the "Today" section naturally becomes the host
for the live/starting-soon badges, since that's where members will look first.
---
### 14. Data freshness indicator *(low priority — deprioritized)*
**Note:** Meeting records change infrequently — once established, a meeting's schedule,
type, and contact info are typically stable for months or years. The occasional update is
usually minor wording. Surfacing a freshness indicator for data this static would add
visual noise with very little member benefit. The existing error state (item #4 in the
bug fix, distinct "Unable to load meetings") and the escape-hatch cache-reset button
already handle the reliability-concern case. This idea is recorded for completeness but
is not recommended for implementation.
---
### 15. "Confirmed meetings only" default filter
**Current behavior:** All meetings are shown by default, including those with
`status === 'unknown'` (not yet confirmed by IDAA Central Office). The "Not Confirmed
by IDAA" warning badge on those cards is alarming-looking but does nothing to prevent
unverified meetings from dominating the list.
There is currently no filter to hide unconfirmed meetings. Members have no choice but
to see them all.
**Business rationale:** IDAA staff want meeting chairs to submit their meeting info for
verification. Defaulting to "confirmed only" creates a natural incentive: unconfirmed
meetings disappear from the default member view, which encourages chairs to contact
IDAA staff and get their meeting verified. It also gives members a cleaner, higher-
confidence list by default — they're not seeing meetings that may be outdated or
inactive.
**Proposed change:** Add a `qry__confirmed` filter field with three states:
| Value | Behavior |
|-------|----------|
| `'confirmed_only'` (default) | Hide meetings where `status === 'unknown'` |
| `'all'` | Show all meetings, confirmed and unconfirmed |
| `'unconfirmed_only'` | Show only unconfirmed (admin/staff use) |
The filter UI shows a simple toggle in the query bar:
```
[✓ Confirmed Only] ← default, shown as active chip or checkbox
```
When the member switches to "All", the unconfirmed meetings appear with their warning
badge (see item #15 for making that badge useful on mobile).
For trusted/admin users, the default should remain `'all'` so staff can see the full
picture without having to change a setting.
**Implementation notes:**
- Add `qry__confirmed: 'confirmed_only' | 'all' | 'unconfirmed_only'` to
`$idaa_loc.recovery_meetings` defaults, defaulting to `'confirmed_only'`.
Trusted users default to `'all'`.
- Apply the filter in both the IDB fast path (`db_events.event.filter()`) and the
API revalidation secondary filter in `handle_search_refresh`. IDB: check
`ev.status !== 'unknown'` when `qry__confirmed === 'confirmed_only'`. API: same
post-fetch client-side filter.
- Pass `qry__confirmed` to `events_func.search__event` if the API supports a
`status` filter param; otherwise handle it client-side only.
- The `no_results_no_filters` derived (used for the escape-hatch button) should NOT
treat `qry__confirmed === 'confirmed_only'` as an active filter — it's the default
state, not a narrowing choice the member made. Only count it as a filter if the
member explicitly switched it to `'all'` or `'unconfirmed_only'`.
- Add a count badge to the toggle: "Confirmed Only (132 of 140)" so members can see
how many unconfirmed meetings exist without having to switch the filter.
---
### 16. "Not Confirmed" status — inline explanation on mobile
**Current behavior:** Meetings with `status === 'unknown'` show a warning badge:
`⚠ Not Confirmed by IDAA ⚠`. The badge has a `title` attribute with a full explanation
(~2 sentences). Title tooltips are invisible on mobile — tapping the badge does nothing.
Members on phones see a alarming-looking warning with no explanation of what it means
or what they should do.
**Proposed change:** Make the badge tappable. On tap (or hover on desktop), show an
inline explanation panel directly below the badge:
```
⚠ Not Confirmed by IDAA [?]
↓ (on tap)
This meeting has not been confirmed by IDAA Central Office.
Please reach out to the chair for current information.
If this meeting is active, email info@idaa.org to confirm it.
[✕ Close]
```
**Implementation notes:**
- Add a `$state show_unconfirmed_info = false` per card (scoped to the `{#each}` block).
- Replace the `title` attribute with an `onclick` toggle that sets `show_unconfirmed_info`.
- The explanation renders in a `{#if show_unconfirmed_info}` block directly below the
badge row — a simple div with a rounded border and the existing tooltip text.
- The `mailto:info@idaa.org` link in the explanation is already in the tooltip text;
making it a real clickable link here rather than plain text in a tooltip is a direct
improvement for mobile members who want to report a confirmed meeting.
- This pattern also applies to other `title`-only tooltips on the page if they appear.
- Note: with item #15 defaulting to "confirmed only", most members will never encounter
this badge unless they switch to the "All" view. The inline explanation is still worth
implementing for that audience, but the default filter reduces how often it's seen.
---
---
### 17. Cycling sort button — ✅ Implemented 2026-05-18
**Problem:** Three separate sort chip buttons (Last Updated / Name A→Z / Name Z→A) took
too much horizontal space and caused layout bounce as the selected chip changed width.
**Implemented:** Single cycling button in `ae_idaa_comp__event_obj_qry.svelte`.
Clicking advances through `sort_modes` array (Last Updated → Name A→Z → Name Z→A → repeat)
using `$derived` index + `cycle_sort()` function. Button has `min-w-36` to prevent bounce.
Icon changes per mode (fa-clock / fa-sort-alpha-down / fa-sort-alpha-up-alt). A small
fa-redo icon indicates it's a cycling control.
---
### 18. Collapsible "Meeting Info" data store panel — ✅ Implemented 2026-05-18
**Problem:** The `Element_data_store` panel (code: `recovery_meetings_info`) displays
between the filter bar and the meeting results list. Once a member has read it, it
consumes vertical space on every page load and pushes results below the fold, especially
in the Novi iframe on mobile.
**Implemented:** Toggle button wrapping the `<Element_data_store>` in
`src/routes/idaa/(idaa)/recovery_meetings/+page.svelte`. Button shows
"Meeting Info" with a chevron (up = expanded, down = collapsed). Collapse state
persisted in `$idaa_loc.recovery_meetings.ds_info_collapsed` (localStorage) so the
user's preference survives page reloads. New field added to `idaa_local_data_struct`
in `ae_idaa_stores.ts` — no version bump needed (existing users without the field
get `undefined` which is falsy = expanded, the correct default).
---
## Notes
- The fulltext search (`qry__fulltext_str`) searches against the `default_qry_str`
field, which is a server-side composite that already includes contact info, day-of-week
text, meeting type, location, and other metadata. The placeholder text in the search
input is accurate — it genuinely searches contacts and schedule information despite
those fields being stored in separate columns.
- All changes must render correctly inside the Novi iframe context (Bootstrap v3.4.1
CSS conflicts — see `CLIENT__IDAA_and_customized_mods.md` for known issues).
- Mobile testing should cover Android Chrome specifically — the original "no meetings
found" bug disproportionately affected mobile users with intermittent connections.

View File

@@ -0,0 +1,444 @@
# Aether SvelteKit — AI Agent Bootstrap / Quickstart
> **Read this first.** This doc is the fast path to being productive on this project.
> It covers the rules, patterns, and gotchas that matter most.
> Deep dives are in the linked docs at the bottom.
---
## 1. What This Project Is
**Aether** is an event management platform built by One Sky IT (Scott Idem).
This repo is the frontend: **Svelte 5 (runes mode) + SvelteKit v2**.
The backend is a separate repo (`aether_api_fastapi`) — a FastAPI + MariaDB app
running in Docker. The frontend talks to it exclusively via the V3 REST API.
**Key clients:**
- **Conference organizers** — Presentation Management (pres_mgmt), Launcher, Badges
- **Exhibitors** — Leads capture
- **IDAA** — International Doctors in Alcoholics Anonymous (private medical/recovery community)
**Stack at a glance:**
| Layer | Technology |
|---|---|
| Framework | Svelte 5 (runes mode) + SvelteKit v2 |
| Styling | Tailwind CSS v4 + Flowbite (Skeleton UI being phased out) |
| State | `$state`/`$derived` runes + Dexie.js IndexedDB (`liveQuery`) |
| Icons | Lucide (`@lucide/svelte`) |
| Editors | CodeMirror 6 (primary), Edra/TipTap (secondary) |
| Native | Electron app for onsite launcher (`src/lib/electron/electron_relay.ts`) |
| Backend | FastAPI + MariaDB, V3 API (`/v3/crud/`, `/v3/lookup/`) |
| Auth | Custom headers: `x-aether-api-key` + `x-account-id`; JWT Bearer is auto-injected when a session exists |
---
## 2. Critical Rules — Read Before Touching Any Code
### Privacy (Sev-1 class failures if violated)
- **IDAA content is ALWAYS private.** All routes under `/idaa/` require authentication.
A previous AI agent accidentally made IDAA bulletin board data publicly accessible.
This is the single most serious class of mistake on this project. When in doubt — it's private.
- **Journals** are private personal data. Always authenticated.
### File Safety
- **Never use `rm`** to delete files. Move to `~/tmp/agents_trash` instead.
- Never commit `.env` files, API keys, or passwords.
### Before Every Commit
- Run `npx svelte-check` — zero errors, zero warnings. No exceptions.
- Atomic commits: one component or one fix per commit.
### Before Starting Any Task
- Read `documentation/TODO__Agents.md` — it has active tasks, known bugs, and context
about what was recently changed and why.
### V3 API — Never Include the Object ID in PATCH Body Fields
The ID is in the URL. Including it in `data_kv` causes a `400: Unknown column in SET`.
```ts
// WRONG — causes 400 error:
update_ae_obj__event_file({ event_file_id, data_kv: { event_file_id, file_purpose: 'final' } })
// CORRECT:
update_ae_obj__event_file({ event_file_id, data_kv: { file_purpose: 'final' } })
```
---
## 3. Environment & Deploy Cheat Sheet
There are **two separate `.env` systems** — do not confuse them:
| System | File | Controls |
|---|---|---|
| `aether_container_env/.env` | Docker orchestration | Ports, `AE_CFG_ID`, replicas, paths |
| `aether_app_sveltekit/.env.*` | Vite/SvelteKit build | `PUBLIC_*` API vars baked into the JS bundle |
**The 4 commands you run and which env file each uses:**
| Command | Env file read |
|---|---|
| `npm run dev` | `aether_app_sveltekit/.env.local` (Vite dev server, localhost:5173) |
| `npm run build:docker:dev` | `aether_app_sveltekit/.env.dev` (baked into local Docker image) |
| `npm run deploy:remote:test` | `/srv/apps/test_aether_app_sveltekit/.env.test` on Linode |
| `npm run deploy:remote:prod` | `/srv/apps/prod_aether_app_sveltekit/.env.prod` on Linode |
**The `.env.*` files are gitignored** (only `.default` templates are tracked). They must be
placed manually on each server during initial setup. On the workstation you only need
`.env.local` and `.env.dev`. The Linode servers each have exactly one env file for their environment.
**What goes in every SvelteKit env file** (same 8 vars, different values per env):
```env
PUBLIC_AE_API_PROTOCOL=https
PUBLIC_AE_API_SERVER=<api server hostname>
PUBLIC_AE_API_BAK_SERVER=<bak api hostname>
PUBLIC_AE_API_PORT=443
PUBLIC_AE_API_PATH=
PUBLIC_AE_API_SECRET_KEY=<key>
PUBLIC_AE_CRUD_SUPER_KEY=<key>
PUBLIC_AE_BOOTSTRAP_KEY=<key>
PUBLIC_AE_NO_ACCOUNT_ID=No_Account_ID_Here
```
---
## 4. Svelte 5 Runes Mode — Key Patterns & Gotchas
This codebase is **fully Svelte 5 runes mode**. No Svelte 4 syntax.
### The basics
```svelte
<script lang="ts">
// Props — with optional two-way binding
interface Props { count?: number; label: string; }
let { count = $bindable(0), label }: Props = $props();
// Reactive state
let value = $state('');
let upper = $derived(value.toUpperCase());
// Side effects (replaces onMount + $: reactive)
$effect(() => {
console.log('value changed:', value);
return () => { /* cleanup */ };
});
</script>
```
### What NOT to use (Svelte 4 patterns — do not introduce)
```ts
// ❌ No writable() stores for component state
import { writable } from 'svelte/store';
// ❌ No reactive declarations
$: doubled = count * 2;
// ❌ No onDestroy for cleanup — use $effect return instead
onDestroy(() => cleanup());
```
### `$bindable()` vs `$state()`
- Use `$bindable()` when the parent needs two-way binding on a prop.
- Use `$state()` for local component state with no external binding.
### Store reactivity trap (important for `$effect`)
The app uses `svelte-persisted-store` (Svelte 4 contract) for `$ae_loc`, `$ae_api`,
`$ae_sess`, etc. In Svelte 5 `$effect`, reading **any field** of a Svelte 4 store
subscribes to the **entire store**. This means unrelated writes to `$ae_loc`
(e.g. iframe height, SWR reload) will re-trigger your effect. Be conservative about
what you read from these stores inside `$effect` blocks. See `PROJECT__Stores_Svelte5_Migration.md`
for the long-term fix plan.
For search pages specifically, this usually means:
- keep true user preferences in persisted local state
- keep transient triggers, loading flags, and last-executed search keys in session state when possible
- let the page effect schedule the search, but put the duplicate-execution guard inside the search executor so page-load auto-search still runs after hydration
- if the search text or filters are mirrored from localStorage on mount, expect that mount-time writes can re-trigger the effect unless the executor has its own guard
### `{#await}` blocks
```svelte
{#await somePromise}
<LoadingSpinner />
{:then result}
<div>{result}</div>
{:catch error}
<ErrorMessage {error} />
{/await}
```
---
## 5. V3 API Patterns
### SWR (Stale-While-Revalidate) — the standard load pattern
Return cached Dexie data immediately, refresh from API in background.
```ts
async function load_ae_obj_id__my_obj({ api_cfg, obj_id }) {
// 1. Return stale cache immediately (fast)
const cached = await db.my_obj.get(obj_id);
if (cached) my_obj_state = cached;
// 2. Fetch fresh from API in background
_refresh_my_obj_background({ api_cfg, obj_id });
}
```
### Shared/Common Aether object fields
The core fields for almost all Aether objects are:
* id/id_random
* code - string
* name - string
* summary - string
* content - string
* alert - boolean
* alert_msg - text
* priority - boolean
* sort - int
* group - string
* hide - boolean
* enable - boolean
* default_qry_str - special concat string index
* notes - text
* created_on - timestamp
* updated_on - timestamp
### ID convention — never use `_id_random` fields
The V3 API uses random string IDs (e.g. `event_file_id = "aBc123"`). The `*_id_random`
fields are legacy aliases. The integer version of the ID is never returned by the API. Always use the short form:
```ts
// ✅ Correct
event_file_obj.event_file_id
// ❌ Wrong — legacy alias, don't use
event_file_obj.event_file_id_random
```
The short ".id" is also the randomized string, **not an integer** (autonum).
### PATCH — only field values in the body
```ts
// The obj_id goes in the URL (handled by update_ae_obj__* function).
// Only the fields you want to update go in data_kv.
await events_func.update_ae_obj__event_file({
api_cfg: $ae_api,
event_file_id: 'aBc123', // → becomes the URL path param
data_kv: { file_purpose: 'final' } // → only changed fields
});
```
### Auth headers (set automatically by `api.ts`)
```
x-aether-api-key: <PUBLIC_AE_API_SECRET_KEY>
x-account-id: <account_id>
```
**Do not treat `params.key` as an auth bypass.**
Only explicit `x-no-account-id: bypass` means "drop account context".
If `key` is present for business logic, keep `x-account-id` intact.
### Dexie queries — always use the object ID index, not `.get()`
All `db_core` (and other module) Dexie tables define their schema with `id` as the first
field (primary key), followed by the object's string ID (e.g. `person_id`). V3 **never**
returns `id`, so every record stored in Dexie has `id = undefined`. Calling `.get(value)`
does a primary key lookup — it will always miss when passed a string object ID.
```ts
// ❌ Wrong — .get() uses the primary key (id), which V3 never populates:
liveQuery(() => db_core.person.get(person_id))
// ✅ Correct — use .where() on the indexed object ID field:
liveQuery(() => db_core.person.where('person_id').equals(person_id).first())
```
This applies to every table in every module (`db_core`, `db_events`, etc.).
When looking up a single object by its string ID, always use `.where().equals().first()`.
---
## 6. Naming Conventions (snake_case; no camelCase)
| Pattern | Example | Used for |
|---|---|---|
| `ae_comp__*` | `ae_comp__event_badge.svelte` | Route-level components |
| `ae_<module>_comp__*` | `ae_events_comp__session_list.svelte` | Module-scoped components |
| `element_*` | `element_input_files_tbl.svelte` | Reusable library primitives |
| `lq__*` | `lq__journal_obj` | Read-only liveQuery |
| `lqw__*` | `lqw__journal_obj` | Writable form snapshot liveQuery |
| `ae_<module>__<obj>.ts` | `ae_journals__journal.ts` | Object type + functions |
| `db_<module>.ts` | `db_journals.ts` | Dexie instance per module |
The **canonical pattern reference** is the Journals module (`src/lib/ae_journals/`).
When building anything new, model it after Journals.
---
## 7. Mistakes Agents Have Made on This Project
These are real incidents — know them before you start.
1. **IDAA BB exposed publicly** — an agent removed an auth guard from the bulletin board
route. All IDAA content must be behind authentication. Always check route guards when
touching `/idaa/` routes.
2. **`event_file_id` in PATCH body (400 error)** — including the object ID in `data_kv`
when calling `update_ae_obj__*`. The V3 API tries to `SET event_file_id = ...` which
fails because it's a view alias, not a DB column. See Section 2 above.
3. **Bad `.d.ts` declaration silently hid 1368 errors** — a `declare module` in `app.d.ts`
(a script-context file) replaced the entire `@lucide/svelte` type exports instead of
merging. `svelte-check` showed 0 errors, masking real problems. If `svelte-check`
suddenly drops to 0 errors, verify it's not because a bad declaration wiped a module.
4. **Coarse store reactivity loop** — an `$effect` that read `$ae_loc.some_field` was
re-triggering repeatedly because unrelated writes to `$ae_loc` (e.g. SWR config reload)
fired the effect. In Svelte 5, any read of a Svelte 4 store inside `$effect` subscribes
to the whole store. Scope what you read carefully.
5. **`file_purpose == 'admin'` not hidden in Launcher** — the `hide_draft` prop hid
`outline` and `draft` files but not `admin` files. Gaps like this happen when a new
enum value is added to a field without auditing all the places that filter on it.
6. **Deleting files with `rm`** — always move to `~/tmp/agents_trash`. A deleted file may
contain context that's not recoverable from git if it was gitignored.
7. **Dexie `.get()` with a string object ID returns `undefined`** — Dexie `.get(value)`
looks up by the table's **primary key**, which is `id` (the first schema field). The V3
API never returns `id`, so it is always `undefined` in stored records. Passing a string
object ID (e.g. `person_id`) to `.get()` will silently return nothing. Always use
`.where('person_id').equals(person_id).first()` instead. This has caused liveQuery
blocks to always produce `undefined` even when the record exists in Dexie.
8. **Treating `$effect` blocks as auth bypass risks** — a `$effect` inside a child
component cannot bypass a parent `+layout.svelte` auth gate. Children only mount if
the parent calls `{@render children?.()}`. Adding redundant auth guards to `$effect`
blocks that can only run after the parent gate already passed is unnecessary — and
misleads future readers into thinking the parent gate is not sufficient on its own.
The **real** pre-gate risk is `+page.ts` / `+layout.ts`: universal load functions run
before any layout mounts and also fire during SvelteKit link prefetch. Keep those files
clean of data loads in private modules. See `GUIDE__SvelteKit2_Svelte5_DexieJS.md`
"SvelteKit Layout Hierarchy: Security and Execution Order" for the full explanation.
9. **Using query `key` as a proxy for bypass stripped `x-account-id`** — this caused
valid account-scoped requests to lose account context and 403. `key` can be a valid
endpoint/business param, but it is not equivalent to `x-no-account-id: bypass`. Keep
`x-no-account-id` usage narrow and temporary; do not expand it without a documented
allowlist case.
10. **Pre-stringifying `*_json` fields before passing to API wrappers** — the API wrappers
(`api_post__crud_obj.ts` for V3, `api.ts` for legacy CRUD) automatically serialize any
field ending in `_json` (e.g. `cfg_json`, `data_json`). Pass these as plain JS objects.
Pre-stringifying with `JSON.stringify()` before calling the wrapper will double-encode
the value in the legacy path (stringify sees a string and escapes it), and is at best
redundant on the V3 path. Both paths now pretty-print with 2-space indent.
See `GUIDE__AE_API_V3_for_Frontend.md` → section 3C for the full explanation.
11. **Broad Dexie result windows get silently clipped** — if a broad "All" view shows fewer
rows than a narrower filter, check for a page-level limit or an API revalidation step
replacing the local IDB result set. For empty text searches, the full local result set
should drive the display; server refreshes should update cache, not shrink visibility.
12. **Not bumping `IDB_CONTENT_VERSIONS` when changing `properties_to_save`** — this caused
the IDAA Recovery Meetings "no meetings found" bug for approximately one year (20252026).
**What happened:** A deploy changed `properties_to_save` in `ae_events__event.ts`, but no
one bumped `IDB_CONTENT_VERSIONS.events.event` in `store_versions.ts`. Existing users kept
the old stale event records in IndexedDB indefinitely. On the Recovery Meetings page, the
fast path (IDB search) returned those stale records, which all failed the `account_id`
filter and returned 0 results. The API call then either errored silently or was filtered
to 0 by the secondary client-side filter. Critically, the error state and the genuinely
empty state showed the **same** "No meetings found" message — users and staff had no
indication a failure had occurred. The manual Full Reset (via the `?` help panel) always
fixed it, but no one knew why it worked, making the root cause impossible to track down.
**The fix (2026-05-16):** `check_and_clear_idb_table()` in `store_versions.ts` is now
wired in `src/routes/idaa/(idaa)/+layout.svelte` for `db_events.event`. On a version
match it costs one localStorage read. On a mismatch it silently clears the table; the
SWR pattern then repopulates from the API on next load.
**The rule going forward:**
- When you change `properties_to_save` in any `ae_events__*.ts` file (or any other
object file) in a way that makes existing cached records stale — fields added, removed,
renamed, or where a computed field's behavior changes — **bump the matching entry in
`IDB_CONTENT_VERSIONS` in `src/lib/stores/store_versions.ts`**.
- If the table is not yet wired, wire it first (see the wiring instructions in the
`IDB_CONTENT_VERSIONS` comment block in `store_versions.ts`).
- Currently wired: `events.event`. All other tables are not yet wired.
**Also:** Never show the same UI message for both a failed API call and a genuinely empty
result. Always distinguish `qry__status === 'error'` from `qry__status === 'done'` with
0 results in your templates. Silent failures look like data problems and are extremely
difficult to diagnose.
13. **Breaking the API retry loop by returning errors instead of throwing them** — all four
`api_*_object.ts` files (`api_get_object.ts`, `api_post_object.ts`, `api_patch_object.ts`,
`api_delete_object.ts`) use a `.catch()` that returns the error as a value, followed by a
classification block. That block **must throw** for transient network failures (`TypeError`)
so they enter the retry loop. If you change it to `return false`, retries are silently
bypassed for the most common failure mode in hotel/conference WiFi — and nothing warns you.
**What happened (commit a10accfaa, Jan 2026):** A "silence background fetch noise" commit
changed `.catch()` to explicitly `return error`, then the classification block was changed
from a `throw` to `return false`. `TypeError` from `ERR_NETWORK_CHANGED` — the most common
failure on crowded WiFi — stopped retrying. The `retry_count = 5` parameter became dead
code for network errors. Went undetected for ~4 months.
**The retry classification these files must honor:**
- `TypeError` (ERR_NETWORK_CHANGED, WiFi blip) → **`throw`** → enters retry loop with backoff
- `AbortError` where `did_timeout_abort = true` (helper's own timer) → **`throw`** → retries
- `AbortError` where `did_timeout_abort = false` (navigation/unmount abort) → `return false`
- HTTP 400/401/403/422 → `return false` immediately (client errors are deterministic)
- HTTP 5xx → **`throw`** → retries with backoff
**How to verify after any change to the error block:** confirm that a `TypeError` still
produces up to 5 retry attempts with 2s→4s→6s→8s delays before returning false. A single
`return false` after the first network failure means the retry loop is broken.
**Also:** when reviewing these files, check that all four have:
- `ae_auth_error.set()` triggered on 401/403 (shows session-expired banner to the user)
- `timeout = 20000` default (was 60s in PATCH/DELETE until 2026-05-21 — 5-min worst case)
- `did_timeout_abort` flag per attempt (separates helper timeouts from caller aborts)
---
## 8. Source Layout (Quick Reference)
```
src/lib/
ae_api/ — API helpers (V3 preferred)
ae_core/ — Account, User, Person, Site, hosted files
ae_events/ — Events, sessions, presenters, badges, locations, files
ae_journals/ — Journals (canonical/frontier model — copy patterns from here)
ae_idaa/ — IDAA custom module (PRIVATE — always authenticated)
elements/ — Reusable UI: V3 field editor, data store, CodeMirror, QR scanner
electron/ — Native Electron bridge (electron_relay.ts)
stores/ — ae_stores.ts, ae_events_stores.ts, ae_idaa_stores.ts
src/routes/
/core/ — Admin (accounts, people, sites, users)
/events/[id]/
/(pres_mgmt)/ — Presentation management
/(launcher)/ — Event launcher (kiosk display)
/(badges)/ — Badge printing
/(leads)/ — Exhibitor leads
/journals/ — Journals
/idaa/ — IDAA module (PRIVATE)
/hosted_files/ — File management
```
---
## 9. Reading Order for Deeper Dives
Start here, then go deeper as needed:
| What you need | Read |
|---|---|
| Active tasks + known bugs | `documentation/TODO__Agents.md` ← always first |
| Dev workflow + commit rules | `documentation/GUIDE__Development.md` |
| V3 API reference | `documentation/GUIDE__AE_API_V3_for_Frontend.md` |
| Dexie / liveQuery patterns | `documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md` |
| Svelte 5 patterns + pitfalls | `documentation/GEMINI__Svelte_and_Me.md` |
| Permissions + auth levels | `documentation/AE__Permissions_and_Security.md` |
| Electron / native launcher | `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` |
| Store migration plan | `documentation/PROJECT__Stores_Svelte5_Migration.md` |
| Exhibitor Leads module | `documentation/MODULE__AE_Events_Exhibitor_Leads.md` |
| Naming conventions | `documentation/AE__Naming_Conventions.md` |

View File

@@ -0,0 +1,876 @@
# CLIENT: IDAA — International Doctors in Alcoholics Anonymous
**Client:** International Doctors in Alcoholics Anonymous (IDAA)
**Module Path:** `src/routes/idaa/`
**State Stores:** `src/lib/stores/ae_idaa_stores.ts`
**Last Updated:** 2026-05-18 (Default limit and stepper update)
---
## ⚠️ CRITICAL PRIVACY REQUIREMENT
**ALL IDAA content is PRIVATE. Authentication is required for ALL modules.**
IDAA serves a sensitive population — physicians in addiction recovery. Content exposure to the public is a **severe security failure** and a violation of member trust.
- A previous AI agent accidentally exposed IDAA Bulletin Board content publicly. This must never happen again.
- Every route, component, and API call in this module must enforce authentication.
- When in doubt: **it's private**.
**Required access level:** `trusted_access` or higher for all IDAA content.
---
## What IDAA Is
IDAA is a private membership organization for physicians in recovery. They use the Aether platform for:
- A private document archive (historical materials, meeting records)
- A members-only bulletin board (community posts and discussion)
- A searchable directory of in-person and virtual recovery meetings
- Video conferencing (Jitsi-based)
IDAA's Aether instance is embedded as an **iframe inside their existing Novi-powered website** (`idaa.org`). Novi is their external Association Management System (AMS) — it handles membership records and authentication. Aether receives the member context via URL parameters on iframe load.
### Breakout Links and Iframe Persistence
Members often need to open Jitsi meetings outside the Novi iframe (e.g., for full-screen features or on mobile). These are referred to as **Breakout Links**.
- **The Problem:** SvelteKit client-side navigation within the iframe often drops "bootstrap" query parameters like `?key=...` (site access key) and `?uuid=...` (Novi identity token).
- **The Requirement:** When a member breaks out of the iframe into a new browser tab, these keys **must** be present in the URL. Without them, the member will hit the site-domain gate or the IDAA auth gate and see "Access Denied."
- **The Solution:** The Video Conferences page uses a derived `breakout_url` that proactively re-injects the missing `key` (from `$ae_loc.allow_access`) and `uuid` (from `$idaa_loc.novi_uuid`) before generating the external link.
**Example Breakout URL:**
`https://client.oneskyit.com/idaa/video_conferences?uuid=...&key=...&room=...`
---
## Architecture: Composite Module
IDAA is **not a standalone module** — it is a **composition of three existing Aether modules**, access-gated and branded for the IDAA client.
| IDAA Feature | Aether Module Used | Library |
|---|---|---|
| Archives | Archives module | `src/lib/ae_archives/` |
| Bulletin Board (BB) | Posts module | `src/lib/ae_posts/` |
| Recovery Meetings | Events module (repurposed) | `src/lib/ae_events/` |
| Video Conferences | Jitsi (external embed) | External |
There is **no `src/lib/ae_idaa/`** library directory. IDAA-specific state and logic lives in `ae_idaa_stores.ts` and the route components only.
This design allows the IDAA module to be removed or updated without touching core modules.
---
## Route Structure
```
src/routes/idaa/
├── +layout.svelte # Root layout: Novi UUID extraction, iframe height sync
├── (idaa)/
│ ├── +layout.svelte # Access gate: blocks render if unauthorized; permission upgrade
│ ├── +page.svelte # IDAA dashboard — 3-module selector
│ ├── archives/ # Archives submodule
│ │ ├── +page.svelte # Archive list (LiveQuery)
│ │ └── [archive_id]/
│ │ ├── +page.svelte # Archive detail + content viewer
│ │ ├── ae_idaa_comp__archive_obj_id_view.svelte
│ │ ├── ae_idaa_comp__archive_obj_id_edit.svelte
│ │ ├── ae_idaa_comp__archive_content_obj_id_edit.svelte
│ │ └── ae_idaa_comp__modal_media_player.svelte
│ ├── bb/ # Bulletin Board (Posts) submodule
│ │ ├── +page.svelte # Post list (LiveQuery, archive-filtered)
│ │ └── [post_id]/
│ │ ├── +page.svelte # Post detail + comments
│ │ ├── ae_idaa_comp__post_obj_id_view.svelte
│ │ ├── ae_idaa_comp__post_obj_id_edit.svelte
│ │ └── ae_idaa_comp__post_comment_obj_id_edit.svelte
│ ├── recovery_meetings/ # Recovery Meetings (Events repurposed)
│ │ ├── +layout.ts # Layout loader (auth, stores)
│ │ ├── +layout.svelte # Layout wrapper
│ │ ├── +page.svelte # Meeting list + search filters
│ │ ├── ae_idaa_comp__event_obj_li_wrapper.svelte # List container/modal host
│ │ ├── ae_idaa_comp__event_obj_li.svelte # Individual list item card
│ │ ├── ae_idaa_comp__event_obj_qry.svelte # Query/filter bar
│ │ ├── ae_idaa_comp__event_obj_id_view.svelte # Meeting detail (read-only)
│ │ ├── ae_idaa_comp__event_obj_id_edit.svelte # Meeting edit form (active)
│ │ └── [event_id]/
│ │ ├── +page.svelte # Meeting detail page — renders view OR edit based on session flag
│ │ └── +page.ts
│ ├── video_conferences/ # Jitsi video conference integration
│ └── jitsi_reports/ # Jitsi meeting activity log report (trusted_access only)
```
> **Note:** Recovery Meetings has **two UI entry points**:
> 1. **Modal pattern** (primary list flow) — list, view, and edit components live at `recovery_meetings/`
> level, toggled via `$idaa_sess.recovery_meetings` session flags (`show__modal_view`, `show__modal_edit`).
> 2. **Direct page** (`[event_id]/+page.svelte`) — navigating to `/idaa/recovery_meetings/<id>` renders
> the same view/edit components gated by `$idaa_sess.recovery_meetings.edit__event_obj`.
>
> Both patterns use `ae_idaa_comp__event_obj_id_edit.svelte`. The edit form clears **both**
> `show__modal_edit` and `edit__event_obj` on save/cancel so it works correctly from either entry point.
---
## Authentication: Novi UUID System
IDAA members do not log in through Aether — they log in through Novi (idaa.org), and Novi passes their identity to the Aether iframe via URL parameters.
### URL Parameters (on iframe load)
```
?uuid=<36-char-uuid>
&iframe=true
&key=<site-access-key>
```
> **Security note (2026-03-09):** The iframe HTML files previously also passed `email` and `full_name`
> via URL params. These were unverifiable claims that could be spoofed via URL. They have been removed.
> The SvelteKit layout now verifies identity via the Aether server-side Novi proxy — the Novi API
> call originates from the server, not the member's browser.
> See "Iframe Integration" → "Novi UUID Verification Flow" below.
### Verification Flow (`(idaa)/+layout.svelte`)
When a `uuid` param is present in the URL, the layout performs an **async call to the Aether server-side endpoint** (`GET /v3/action/idaa/novi_member/{uuid}`), which proxies to Novi server-to-server:
1. The UUID actually exists in Novi's system (prevents fake/crafted UUIDs)
2. Gets verified name and email — these can't be forged via URL
3. Sets `$idaa_loc.novi_uuid`, `$idaa_loc.novi_email`, `$idaa_loc.novi_full_name`
4. Sets `$idaa_loc.novi_verified = true` on success
A `novi_verifying` UI state prevents the "Access Denied" screen from flashing during the API round-trip.
**All or nothing:** If the Novi API key is not configured on the site, or the verification call fails, access is denied. There is no URL-param fallback.
**Required `site_cfg_json` fields:**
```json
{
"novi_idaa_api_key": "Base64-encoded-key-from-Novi",
"novi_api_root_url": "https://www.idaa.org/api", // optional, this is the default
"novi_admin_li": ["uuid-1", "uuid-2"],
"novi_trusted_li": ["uuid-3", "uuid-4"],
"novi_idaa_group_guid_li": ["group-uuid"] // Jitsi moderators only
}
```
## Novi API Integration — How We Use It
This section documents the exact way Aether uses the Novi API for the IDAA integration so future maintainers can recreate the flow.
- **Purpose:** Verify a Novi-provided `uuid` received via iframe URL parameters, obtain a verified name/email from Novi, and upgrade Aether permissions for that session when appropriate.
- **All-or-nothing policy:** If the Novi API key is not configured or the verification call fails, the Novi-based access path is denied. The layout explicitly prevents child routes from rendering while verification is in-flight to avoid flashing "Access Denied".
- **Rate limits (Novi API):** 20 calls/second · 600 calls/minute · 100,000 calls/day. The Aether backend handles 429 responses; the frontend receives a `429` and retries once after 10 seconds. The 12-hour TTL cache on successful verification (Redis server-side + `$idaa_loc` client-side) prevents repeated calls during normal use. A `503` (Novi unreachable) is auto-retried once after 3 seconds before surfacing an error to the user.
### Verification Flow (implementation)
1. The IDAA iframe loads Aether pages with a `?uuid=<uuid>&iframe=true` param.
2. When the `uuid` param is present the IDAA layout calls the Aether server-side proxy:
```js
// simplified
fetch(`${aether_api_url}/v3/action/idaa/novi_member/${uuid}`, {
method: 'GET',
headers: {
'x-aether-api-key': api_key,
'x-account-id': account_id
}
})
// Aether calls Novi server-to-server; member's browser IP is never in the Novi call path.
```
3. On success (`200`), the layout reads `data.full_name` and `data.email` from the response and writes them to the IDAA store, marking verification success.
4. The layout then determines a target Novi permission level (`authenticated`, `trusted`, `administrator`) by checking configured UUID lists (`novi_trusted_li`, `novi_admin_li`) and upgrades the Aether session only if the Novi-derived level is higher than the current global level.
5. The layout also resets a few IDAA-specific query defaults (BB filters, etc.) to safe values after verification.
### Key `site_cfg_json` fields and where they are used
- **`novi_idaa_api_key`**: Base64-encoded Basic auth token provided by Novi. Used by the Aether **server** to authenticate against Novi — the frontend never touches the key itself. The frontend checks only for its *presence* in `site_cfg_json` as a guard meaning "IDAA is configured for this site". If missing, Novi-based access is denied.
- **`novi_api_root_url`**: Optional Novi API root (defaults to `https://www.idaa.org/api`). Read by the Aether server, not the frontend.
- **`novi_admin_li`**: Array of UUIDs treated as administrators for IDAA. Merged into `$idaa_loc.novi_admin_li` during layout initialization and used to set `administrator` level.
- **`novi_trusted_li`**: Array of UUIDs treated as trusted members. Merged into `$idaa_loc.novi_trusted_li` and used to set `trusted` level.
- **`novi_jitsi_mod_li` / `novi_idaa_group_guid_li`**: Lists used to map Jitsi moderator privileges and group GUIDs (where applicable).
- **`novi_bb_base_url`**: (optional) Base URL used to build links for Bulletin Board notification emails.
- **`jitsi_exclude_uuids`**: (optional) Array of Novi UUIDs to exclude from Jitsi Reports.
This is the canonical staff/test filter. UUIDs are matched case-insensitively against
`final_participants[].novi_uuid` when present. Example: `["uuid-1", "uuid-2"]`.
- **`jitsi_known_meetings`**: (optional) Array of meeting names / room names to keep in the report.
When this list is non-empty, only matching `room_name` values are shown. Matching is
case-insensitive.
- **Legacy fallback:** `jitsi_exclude_names` is still honored for older configs, but it should be
migrated to UUIDs.
- **Email config values** (`noreply_email`, `noreply_name`, `admin_email`, `admin_name`): used by functions that send notification emails (BB posts, comments, recovery meetings).
### Stores / runtime fields set by verification
- `$idaa_loc.novi_uuid` — the verified UUID
- `$idaa_loc.novi_email` — verified email (normalized)
- `$idaa_loc.novi_full_name` — display name built from Novi fields
- `$idaa_loc.novi_verified` — boolean flag indicating successful verification
- `$idaa_loc.novi_admin_li`, `$idaa_loc.novi_trusted_li` — merged lists from site config
These fields are read elsewhere in the IDAA UI to enable flows for verified users (for example: creating meetings, posting comments, or auto-populating contact info in notifications).
### Where in the codebase this runs (examples)
- The Novak UUID verification and permission-upgrade logic is implemented in the IDAA layout: [src/routes/idaa/(idaa)/+layout.svelte](src/routes/idaa/(idaa)/+layout.svelte).
- UI elements that permit actions for verified Novi users or trusted members check these values. Example: the "Create New Meeting" button allows creation when either the session has `trusted_access` or a `novi_uuid` is present — see [src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte](src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_qry.svelte).
### Security notes and operational guidance
- The previous implementation leaked `email` and `full_name` via URL params — this was removed because those values are unauthenticated and can be spoofed.
- The API key is sensitive — keep it only in site `cfg_json` and do not expose it in client-side code or public repositories. The key is read and used exclusively by the Aether backend; it is never sent to the browser.
- If Novi changes their customer API shape, update `app/methods/idaa_novi_verify_methods.py` in the backend (display name/email normalization) and this documentation.
If you need a compact checklist for re-creating this flow in another integration, ask and I will add a small runbook with exact request/response field mappings.
### ~~Planned: Server-Side Novi Verification~~ ✅ Implemented (2026-05-19)
**Problem solved:** The previous client-side Novi API call originated from the member's browser.
Hotel/conference WiFi, VPNs, corporate/hospital networks, and Cloudflare IP reputation filtering
could block these calls and produce false "Access Denied" for legitimate members.
**Solution implemented:** A FastAPI endpoint proxies the Novi call server-to-server
(Aether → Novi), with Redis caching. Members' browser IPs are no longer in the call path.
**Endpoint:** `GET /v3/action/idaa/novi_member/{uuid}`
- Standard Aether auth headers (`x-aether-api-key`, `x-account-id`)
- Server reads `novi_idaa_api_key` / `novi_api_root_url` from site `cfg_json`
- Redis cache: `idaa:novi_member:{uuid}` — 4-hour TTL, only 200s cached
- `404` results never cached (recently-joined members not incorrectly denied)
**Frontend:** `verify_novi_uuid()` in `(idaa)/+layout.svelte` now calls this endpoint with
standard Aether headers. The `novi_idaa_api_key` is still checked for presence in
`site_cfg_json` as a proxy for "is IDAA configured for this site" (server holds the key itself).
**Full API spec:** `GUIDE__AE_API_V3_for_Frontend.md` §12.
### Permission Levels (Ascending)
| Level | Condition | Access |
|---|---|---|
| Anonymous | No UUID, unrecognized UUID, or verification failure | No access |
| Authenticated | UUID verified against Novi API | View own content, limited actions |
| Trusted | Verified UUID in `novi_trusted_li` | Full member access to all IDAA content |
| Administrator | Verified UUID in `novi_admin_li` | Full access + edit/manage |
`novi_trusted_li` and `novi_admin_li` are managed in Aether site config (not in Novi directly).
## Identity Linkage: The Novi UUID Rule (Triple Linkage)
**CRITICAL ARCHITECTURAL STANDARD:**
All member-generated content in the IDAA module MUST be explicitly linked to the member's Novi UUID via the `external_person_id` field. This linkage is the primary mechanism for ownership, edit permissions, and auditing.
### 1. Mandatory at Creation
Linkage MUST happen at the moment of initial object creation (POST). Shell records created without an `external_person_id` are considered orphaned and may be inaccessible to the creator.
### 2. Triple Linkage Scope
The following objects require mandatory `external_person_id` linkage:
- **Recovery Meetings** (`ae_Event`)
- **Bulletin Board Posts** (`ae_Post`)
- **Post Comments** (`ae_PostComment`)
### 3. Implementation Patterns
- **Buttons:** Creation buttons (e.g., "Create New Meeting") must include `external_person_id: $idaa_loc.novi_uuid` in their initial `create_ae_obj` payload.
- **Edit Forms:** Edit components must provide robust fallbacks to `$idaa_loc.novi_uuid` for new or incomplete records, ensuring identity is captured even if the initial creation call was narrow.
- **Identity Sync:** Along with the UUID, `full_name` and `email` should also be synced from `$idaa_loc` to provide human-readable context in notifications and admin views.
- **Race Condition Defense:** `$idaa_loc` may be briefly null on mount before the store hydrates from localStorage. Creation buttons and edit submit handlers must scavenge identity directly from `localStorage.getItem('ae_idaa_loc')` as a fallback when the store value is missing.
### 4. Staff Editing Rules (IDAA Trusted/Admin Staff)
IDAA staff have their own Novi UUID. When they edit member content, their identity must **not** overwrite the member's `external_person_id`, `full_name`, or `email`.
| Content Type | `external_person_id` for staff | `full_name` / `email` for staff |
|---|---|---|
| BB Post | **Readonly** (unless `administrator_access`) — member's UUID preserved | Same — rendered from existing record, not staff identity |
| Post Comment | **Preserved** — form state initializes from existing record first | Same |
| Recovery Meeting | **Intentionally editable** for trusted staff — staff can reassign meeting ownership | Contact 1 renders from existing `contact_li_json[0]` first; staff identity only fills if blank |
The fallback to `$idaa_loc.novi_uuid` (the current user's UUID) only fires when the record has **no** existing `external_person_id`. For any record properly created after the 2026-04-07 triple-linkage enforcement, this fallback should never be reached.
### 5. Recovery Meetings — Contact 1 Convention
In 99% of cases, **Contact 1 should be the same person linked via `external_person_id`** — the IDAA member who owns and runs the meeting. These are two separate fields:
- `external_person_id` — the ownership/identity link (Novi UUID). Determines who may edit the meeting.
- `contact_li_json[0]` — the displayed contact info (name, email, phone). Shown to members searching for meetings.
They are expected to match but are set independently. Members unlock Contact 1 via confirm dialog if they need to list a different contact. Staff can edit both fields directly.
### Permission Upgrade Rule
```
// RULE: Only UPGRADE to Novi-based permissions, NEVER downgrade.
// If a user has a higher global Aether role (site manager, super),
// their global role is preserved and not overwritten by Novi auth.
```
This ensures that OSIT staff with `super` or `manager` roles retain full access regardless of Novi UUID status.
### Non-Novi Sign-in Paths (unaffected)
- **User/Pass or Auth Link:** No `uuid` in URL → layout Novi block does not run
- **Shared Passcode:** No `uuid` in URL → layout Novi block does not run
### Access Gate (`(idaa)/+layout.svelte`)
The inner layout blocks ALL rendering if the user is not authorized:
- `novi_verifying = true` → "Verifying identity..." spinner (message updates during retry)
- `verify_error_type === 'rate_limited'` → yellow "Identity Verification Unavailable" panel with:
- **Try Again** — calls `handle_verify_retry()` (respects retry_count, waits 10 s before re-calling Novi)
- **Clear Cache & Reload** — clears IDB + localStorage + sessionStorage, then reloads
- **Full Reset** — same clear but also navigates to `/` with `invalidateAll`
- `verify_error_type === 'api_error'` → same yellow panel (API returned non-2xx, not a rate limit)
- Verification failed or no UUID → "Access Denied" error page
- Access check runs before any child routes render
---
## Module 1: Archives
**Route:** `/idaa/archives/`
**Library:** `src/lib/ae_archives/`
**Types:** `ae_Archive`, `ae_ArchiveContent`
The Archives module stores IDAA historical content — meeting records, conference proceedings, historical documents, and media.
### Object Types
**Archive (Container)**
- Represents a collection (e.g., "2019 Conference Proceedings")
- Key fields: `name`, `description`, `original_datetime`, `original_location`, `archive_on`
- `archive_on` — date when this archive collection is auto-hidden (scheduled visibility control)
**ArchiveContent (Items)**
- Individual items within an archive
- Supports multiple content types: `'text'`, `'file'`, `'url'`, `'video'`
- Key fields: `archive_content_type`, `content_html`, `url`, `hosted_file_id`, `duration`
- Video/audio content has a dedicated media player component
### Database (Dexie)
```
db_archives.archive — Archive containers
db_archives.content — Archive content items (linked by archive_id)
```
### Demo / Test IDs
- Archive: `nAA2bHLv8RK` (id: 1) "One Sky Test Archive"
- Archive Content: `UjKzrk-GKu5` (id: 1) "Hosted File Test"
---
## Module 2: Bulletin Board (BB)
**Route:** `/idaa/bb/`
**Library:** `src/lib/ae_posts/`
**Types:** `ae_Post`, `ae_PostComment`
The BB is the IDAA members-only community discussion board. It is the **most sensitive module** — public exposure must never occur.
### Object Types
**Post (Thread)**
- Key fields: `title`, `content`, `anonymous`, `full_name`, `email`
- `archive_on` — date after which the post is hidden from all views
- `archive` — boolean flag for immediate archival
- `enable_comments` — controls whether replies are allowed
- `post_comment_count` — cached count of replies
**PostComment (Reply)**
- Key fields: `post_id`, `content`, `anonymous`, `full_name`, `email`
- Replies inherit the parent post's visibility rules
### Post Visibility / Archival Filter
Posts with `archive_on` set to a past date are **automatically hidden** from all queries. This is enforced at the component level via a LiveQuery filter:
```typescript
// This filter is REQUIRED — do not remove it
filter((x) => !x.archive_on || archiveDate > now)
```
Archived posts are soft-deleted — they remain in the database for audit purposes but are not shown to members.
Most recent first (sorted `updated_on DESC`).
### Database (Dexie)
```
db_posts.post — Posts (threads)
db_posts.comment — Post comments (linked by post_id)
```
---
## Module 3: Recovery Meetings
**Route:** `/idaa/recovery_meetings/`
**Library:** `src/lib/ae_events/` (standard Events module, repurposed)
**Types:** `ae_Event` (standard event type, filtered for meeting context)
Recovery Meetings reuses the Aether Events object to represent AA recovery meetings. These are NOT conferences — they are regular ongoing meetings (weekly, monthly, etc.) available to IDAA members.
### Search Filters
Members can filter meetings by:
- **Fulltext search** — name, location, day of week, contacts (debounced 250ms; uses SWR pattern)
- **Virtual** — online meetings (Zoom, Jitsi, other)
- **In-person** — physical location meetings
- **Meeting type** — IDAA / Caduceus / Family Recovery
- **My Meetings** — star toggle; shows only meetings the member has starred (favorites)
**Sort options:** Last Updated (default), Meeting Name AZ, Meeting Name ZA.
**Empty state behavior:**
- Zero results with active filters → "No meetings found for these filters" + "Clear all filters" button
- Zero results with no filters → bare message shown, then after 8s a "Refresh Meeting Cache" escape hatch appears (clears IDB and re-fetches from API — indicates a stale-cache problem, not a real empty set)
Search uses the standard Aether SWR pattern (IDB cache returned immediately, then API refreshes in background).
### Search Architecture — What Is and Isn't Searched
The fulltext search runs against the `default_qry_str` field (backend-computed STORED GENERATED
column, contains: `id_random`, type, name, description, timezone, recurring pattern/text,
location text, **contact name and email**).
**Contact names and emails ARE searchable via the API path.** `default_qry_str` includes
contact data, so the API `lk_qry` LIKE search on that field covers contacts automatically.
**IDB fast-path gap:** The local cache (Dexie) fast-path returns all cached meetings without
text filtering — users see the unfiltered list immediately, then the API result (with contacts
filtered) replaces it after the background refresh completes. The IDB path does not parse
`contact_li_json` for instant local text matching.
**Known history (2026-05-19):** Contact search appeared broken due to two issues now resolved:
1. The backend STORED GENERATED columns (`default_qry_str`, `contact_li_json_ext`) had stale
values; forced a rebuild via fake updates on each event record.
2. The recovery meetings page secondary filter was re-running text matching against response
fields — silently dropping results that matched only via `default_qry_str` (e.g. by contact
name, since that field may not appear in the response body). Fix: removed text re-filtering
from the secondary filter (type / physical / virtual OR-logic only).
**Remaining enhancement (tracked in TODO__Agents.md):**
- Add `contact_li_json_ext` to the IDB fast-path filter in `search__event()` and the recovery
meetings page so contact matches appear instantly from cache, not only after API refresh.
### My Meetings (Favorites)
Members can star meetings to build a personal "My Meetings" list. The star toggle appears:
- On each card in the meeting list (`ae_idaa_comp__event_obj_li.svelte`)
- On the meeting detail page nav bar (`[event_id]/+page.svelte`)
Favorites are stored in the `data_store` table (code: `idaa_meetings_favorites`, scoped to the
IDAA account). The record's `json` field holds `{ [novi_uuid]: [event_id, ...] }` — one shared
record per account containing all members' favorites. This means:
- Favorites persist across browsers and devices (server-side)
- Does **not** write to `ae_event` rows (avoiding the `ON UPDATE current_timestamp()` side effect)
- Known last-write-wins race condition if two members toggle simultaneously — acceptable for ~1000 members
- Pre-created DB records: ID 150 (`gaTKSVPagFj`, account_id=1, dev/demo), ID 151 (`knJh8zhyKT0`, account_id=13, live IDAA)
The star button uses inline styles (not `.btn`) to avoid Bootstrap v3 box-model overrides in the iframe.
### Edit Form — Sections and Key Fields
The edit form (`ae_idaa_comp__event_obj_id_edit.svelte`) is organized into these sections.
All fields map directly to the `ae_Event` object; none are IDAA-specific custom fields.
| Section | Key Fields |
| --- | --- |
| **General Information** | `name` (required), `description` (TipTap rich text), `type` (IDAA / Caduceus / Family Recovery) |
| **How to Attend** | `physical` (bool), `virtual` (bool) toggles; conditionally shows: |
| → Physical | `location_address_json` (name, line_13, city, state, postal, country), `location_text` (TipTap) |
| → Virtual | Platform toggle: **Zoom** (`attend_url_code` meeting ID, `attend_url_passcode`, `attend_json.zoom.passcode_enc`, `attend_json.zoom.domain`, `attend_json.zoom.full_url`), **Jitsi** (`attend_json.jitsi.*`), **Other** (`attend_url`, `attend_url_passcode`, `attend_phone`, `attend_phone_passcode`) |
| → Both | `attend_text` (TipTap — additional attendance instructions) |
| **Schedule** | `recurring_pattern` (weekly/every other week/monthly/other), `weekday_*` (SunSat booleans), `timezone`, `recurring_start_time`, `recurring_end_time`, `recurring_text` (optional TipTap, auto-generated with `*gen*` prefix if blank) |
| **Contacts** | `external_person_id` (Novi UUID link), `contact_li_json[0]` (Contact 1: name, email, phone_mobile, phone_home, phone_office — name/email locked to Novi user by default), `contact_li_json[1]` (Contact 2: same fields, optional) |
| **Admin Options** | `status`, `hide`, `priority`, `sort`, `group`, `enable`, `notes` (TipTap) — **trusted_access only** |
**Rich text fields** all use `AE_Comp_Editor_TipTap` with separate `*_new_html` state variables
(not bound to `$idaa_slct.event_obj` directly) to track change state for the save-button logic.
**Zoom URL auto-generation:** Triggered by `$idaa_trig = 'update_zoom_full_url'`. An `$effect`
reconstructs `attend_json.zoom.full_url` from domain + meeting_id + passcode_enc whenever
the Meeting ID, Passcode, Encrypted Passcode, or Domain fields change.
**Recurring text auto-generation:** If `recurring_text` is blank or contains the `*gen*` prefix,
the submit handler generates a human-readable string (e.g., `*gen* weekly: Monday, Wednesday at 7:00 PM America/Chicago`).
Members can opt into a custom text via "Add More Details?" (admin/trusted only).
**Contact 1 lock:** Contact 1 name and email default to the logged-in Novi member's identity
(`$idaa_loc.novi_full_name`, `$idaa_loc.novi_email`). They are `readonly` unless the user
explicitly unlocks them via confirm dialog (or has administrator access).
### Jitsi Integration
Some virtual meetings are hosted via Jitsi. Members with a Jitsi moderator UUID (`novi_jitsi_mod_li`) have elevated permissions in video sessions.
### Edit Form — Implementation Notes (v2)
- The v2 edit form uses a `<style>` block with `@apply`. Tailwind v4 requires
`@reference "../../../../app.css";` at the top of any component `<style>` block that uses `@apply`.
- The country subdivision lookup list (`lu_country_subdivision_list`) contains duplicate entries —
specifically Puerto Rico (`PR`) has two rows with `code = '-'`. The `{#each}` key must use
the array index (`i`) rather than `sub.code` to avoid a Svelte `each_key_duplicate` error.
The duplicate entries are a **backend data quality issue** that should be cleaned up in the DB.
### Demo / Test IDs
No dedicated IDAA recovery meeting demo records — uses the standard Event demo record for dev:
- Event: `pjrcghqwert` (id: 1) "Demo One Sky IT Conference"
---
## Module 4: Video Conferences (Jitsi)
**Route:** `/idaa/video_conferences/`
Embeds Jitsi video conferences directly in the IDAA module. Separate from Recovery Meetings — this is for IDAA board meetings or special sessions, not regular AA meetings.
Moderation permissions are controlled by `novi_jitsi_mod_li` in the IDAA store.
---
## Module 5: Jitsi Reports
**Route:** `/idaa/jitsi_reports/`
**Access:** `trusted_access` or `novi_verified` — same gate as the rest of `(idaa)/`
**Data source:** `activity_log` table — `jitsi_meeting_event` and `jitsi_meeting_stats` log types
**Library function:** `qry__jitsi_report()` in `src/lib/ae_reports/reports_functions.ts`
An admin/staff reporting tool that aggregates raw Jitsi activity logs into human-readable meeting sessions. It is **not** a member-facing page — IDAA members do not see it.
**Reminder:** this page now filters staff by Novi UUID and can whitelist known meeting names from site config.
### View Modes
Two display modes, toggled via a button in the page header:
| Mode | Description |
| --- | --- |
| **Grouped by Room** (default) | One collapsible section per `room_name`. Each section contains a compact table: Date / Time / Duration / Attendees / Participant List. Mirrors the output of the offline Python script (`create_jitsi_report.py`). |
| **Flat List** | Original card-per-session accordion layout. Better for drilling into event timelines and raw participant lists. |
Both modes use the same filtered data set — switching views does not reset filters.
### Dark Mode / Surface Safety
The page now uses explicit page and row surfaces so dark mode does not collapse into white-on-white
text in either the regular app or the Novi iframe.
### Filters
| Filter | Default | Logic |
| --- | --- | --- |
| **Min. Participants** | 2 | Minimum `real_participant_count` to display a session. Used as the only size filter. |
| **Room Name** | edit mode only | Case-insensitive substring match against `room_name`. Hidden unless AE global edit mode is on. |
| **From / To** | last 60 days / today | Date range applied to `start_time`. "To" date includes the full end of day. |
A "Reset Filters" button appears whenever any filter is non-default.
In edit mode, two extra toggles appear:
- **Show excluded IDs** — temporarily include the UUIDs listed in `jitsi_exclude_uuids`
- **Show all meetings** — temporarily ignore `jitsi_known_meetings`
An "Active Exclusions" panel below the filter bar shows the currently applied Novi UUID exclusions
and known meeting-name whitelist values. Each list is collapsible so the page stays compact.
### Staff / Meeting Filtering
**Problem:** Staff/test accounts and one-off test rooms distort the reports.
**Site config keys:**
```json
{
"jitsi_exclude_uuids": ["uuid-1", "uuid-2"],
"jitsi_known_meetings": ["IDAA-BIPOC-Meeting", "IDAA-Sunday-Meeting"]
}
```
**How it works:**
1. The page reads `$ae_loc.site_cfg_json?.jitsi_exclude_uuids` and excludes matching participants by Novi UUID.
The UUID comes from the Jitsi log `url_params.uuid` field. `g_uuid` is the meeting/group UUID and is not used here.
2. If a participant record does not include a UUID in the activity log, it is left visible; UUIDs are used whenever available.
3. `real_participant_count = real_participants.length` drives filters, exports, and the per-meeting attendee count.
4. Room-level unique participant counts are computed from Novi UUIDs when present, with display-name fallback only for UUID-less records.
5. If `$ae_loc.site_cfg_json?.jitsi_known_meetings` is non-empty, only meetings whose `room_name` matches one of the listed names are shown.
6. The Room Name filter is only shown when global edit mode is enabled.
**Temporary stopgap:** the report also hides these staff display names through the same UUID-exclusion toggle until the long-term logging fix lands:
`Scott I.`, `Brie P.`, `Michelle V.`
**Note:** matching is case-insensitive on the stored `room_name` / meeting name.
### Summary Stats
Shown above the meeting list when data is loaded. Stats reflect the **filtered + exclusion-applied** view:
- **Meetings Shown** — count of sessions passing all filters
- **Total Participants** — sum of `real_participant_count` across all shown sessions
- **Avg Duration** — mean session duration (HH:MM:SS)
- **Total Duration** — sum of all session durations (HH:MM:SS)
In grouped view, each room header also shows its own subtotals (meeting count, unique participants by Novi UUID when available).
Each meeting instance keeps the full participant list visible; the **Copy names** button is edit-mode only so staff can grab the list for follow-up reports without exposing extra controls to normal viewers.
### Caching / Load Behavior
The page now reads cached `activity_log` rows from IndexedDB first, renders that result immediately,
then refreshes from the API in the background. That keeps the report usable even when the network
round-trip is slow.
Both the cache path and the API refresh now page through the matching activity-log set in
`created_on DESC` order with a 1000-row page size before building the report. That avoids the old
"first 500 rows" behavior that could hide newer sessions if the log table grew large.
The report page keeps the newest session first in both the flat list and the grouped-by-room view;
grouped room rows are also sorted newest session first within each room.
### Jitsi URL Builder
Collapsible panel, visible to `trusted_access` users only. Generates properly-formatted Jitsi meeting URLs for IDAA rooms. Component: `ae_idaa_comp__jitsi_url_builder.svelte`.
### Video Conferences → Reports Link
Trusted Access users now get a footer link on the Video Conferences page that jumps back to the Jitsi Reports page. It preserves the current iframe context so the staff workflow stays inside the Novi embed.
**Future idea:** make that link include a `room=` query param for the current meeting so Jitsi Reports can auto-filter to that meeting instance, and have Reset clear that param again.
### Export
CSV and JSON export buttons in the page header export the **currently filtered + exclusion-applied** data set.
### Room Name Fragmentation
The same logical meeting can appear as multiple rooms (e.g. `IDAA-BIPOC-Meeting`, `IDAA-BIPOC-Meeting-2026`, `IDAA-BIPOC-Meeting-March-31`) because the Jitsi URL builder appends a date suffix to generate unique per-session room names. In grouped view, these appear as separate groups. A future normalization pass (strip trailing date suffixes) could optionally merge them — not implemented yet.
### Data Flow
```text
activity_log table
└── qry__jitsi_report() # reports_functions.ts — fetches + aggregates by meeting_id
└── MeetingReport[] # { meeting_id, room_name, start_time, final_duration,
# final_participants, final_participant_count, events }
└── jitsi_reports/+page.svelte
├── apply exclusion list → real_participants / real_participant_count
├── apply filters → meetings_filtered
├── derive grouped view → Map<room_name, MeetingReport[]>
└── render flat or grouped
```
---
## State Management (`ae_idaa_stores.ts`)
Four stores manage all IDAA state:
### `idaa_loc` (localStorage — persistent across sessions)
Stores Novi auth context and per-submodule query settings:
```typescript
{
novi_uuid: string | null // Member UUID (set on verification success)
novi_email: string | null // Verified email from Novi API
novi_full_name: string | null // Verified name from Novi API
novi_verified: boolean // true after successful Novi API verification
novi_admin_li: string[] // Admin UUID list (from site config)
novi_trusted_li: string[] // Trusted member UUID list
novi_jitsi_mod_li: string[] // Jitsi moderator UUIDs
archives: { enabled, hidden, limit, offset, edit__archive_obj, edit__archive_content_obj }
bb: { enabled, hidden, limit, offset, edit__post_obj, edit__post_comment_obj,
qry__enabled, qry__hidden, qry__limit, qry__offset, qry__order_by, qry__order_by_li }
recovery_meetings: {
qry__enabled, qry__hidden, qry__limit, qry__offset,
qry__fulltext_str, qry__physical, qry__virtual, qry__type,
qry__order_by, qry__order_by_li,
qry__favorites_only, // true = show only starred meetings (My Meetings filter)
edit__event_obj // null or event_id string when edit form is open
}
}
```
### `idaa_sess` (in-memory only — resets on page load)
UI state per submodule:
```typescript
{
archives: { qry__status, show__modal_edit__archive_id, show__modal_view__archive_id,
show__modal_edit__archive_content_id, show__modal_view__archive_content_id, obj_changed }
bb: { qry__status, edit__post_obj, show__inline_edit__post_obj, show__modal_edit__post_id,
show__modal_view__post_id, obj_changed }
recovery_meetings: {
qry__status, // null | 'loading' | 'done' | 'error'
qry__fulltext_str, // session-only copy (separate from persisted loc copy)
search_version, // incremented to trigger a new search cycle
edit__event_obj, // null | event_id — controls edit form visibility
show__modal_edit, show__modal_view,
show__modal_edit__event_id, show__modal_view__event_id,
attend_platform, // 'Zoom' | 'Jitsi' | null — platform selected in virtual attend section
obj_changed
}
}
```
### `idaa_slct` (sessionStorage — selection tracking)
```typescript
{
event_id: string | null
archive_id: string | null
archive_content_id: string | null
post_id: string | null
post_comment_id: string | null
}
```
### `idaa_trig` / `idaa_prom`
Trigger flags and promise tracking for async operations (standard Aether pattern).
---
## Iframe Integration
The IDAA module is embedded in `idaa.org` via iframe. This requires:
1. **Height sync** — The root layout posts `message` events to the parent frame for dynamic height adjustment (content length varies)
2. **URL parameter auth** — Novi passes member context via query string on load
3. **No standard navigation** — Members navigate within the iframe; Aether's nav chrome is hidden or minimal in this context
### Novi UUID Verification Flow
**Iframe HTML files** (in `static/`): Pass only `uuid` to the iframe src — no Novi API calls in the browser:
```text
idaa_novi_iframe_archives.html
idaa_novi_iframe_bulletin_board.html
idaa_novi_iframe_recovery_meetings.html
idaa_novi_iframe_jitsi_meeting.html ← reference pattern (unchanged)
```
**SvelteKit layout** (`(idaa)/+layout.svelte`): Calls `GET /customers/{uuid}` on the Novi API using the `novi_idaa_api_key` from `site_cfg_json`. Sets verified name/email in `$idaa_loc` and grants permissions. Shows a "Verifying identity..." spinner during the async call.
**Jitsi page** (`video_conferences/+page.svelte`): Checks `$idaa_loc.novi_verified` in `fetch_novi_data()`. If the layout already verified the user, it reuses `$idaa_loc.novi_email` / `$idaa_loc.novi_full_name` and skips the duplicate member details API call. The group moderator check (`get_novi_group_moderators`) always runs — it is Jitsi-specific.
### ⚠️ Iframe CSS Conflicts (Bootstrap v3)
When `$ae_loc.iframe = true`, the root layout (`+layout.svelte`) injects two external stylesheets from Novi's CDN:
```text
https://assets-staging.noviams.com/novi-core-assets/css/fontawesome.css — safe, icon-only
https://assets-staging.noviams.com/novi-core-assets/css/c/idaa/idaa.css — Bootstrap v3.4.1 ⚠️
```
`idaa.css` is a full **Bootstrap v3.4.1** bundle. It applies global styles to bare HTML elements
(`input`, `select`, `textarea`, `h1h6`) and commonly named classes (`.btn`, `.badge`, `.active`,
`.text-*`, `.bg-*`). These will compete with Tailwind v4 + Skeleton UI.
**Known consequences:**
- Bare form elements (`<input>`, `<select>`) receive Bootstrap's height/padding resets on top of Tailwind
- `.btn` class gets Bootstrap button colors, potentially overriding `preset-*` Skeleton classes
- `<section>` and heading elements may get unexpected margins/padding from Bootstrap's typography reset
- Class names `.field-input` and `.field-label` (used in the v2 edit form's scoped `<style>` block)
also exist in `idaa.css`'s date picker — Svelte's scoped attribute selector wins, but be aware
- In iframe widths near Tailwind `sm`, avoid hiding critical button labels behind breakpoint classes
and do not depend on color-only active states; Bootstrap's `.active`/button styling can make the
selected state nearly invisible unless the control uses an obvious fill/ring change plus
`aria-pressed`
**Mitigation:** The iframe CSS conflicts existed before v2 and are not new. The v2 form uses the
same Skeleton/Tailwind component classes as the rest of the app. Avoid using bare `<section>`,
`<article>`, or block-level HTML5 elements as style hooks; use `<div>` with explicit classes instead.
---
## Testing Requirements
### Auth Gate Tests Come First
**For every IDAA submodule, the first test written must be an authentication enforcement test.**
```typescript
// ✅ Required test pattern for each IDAA module
test('Archives - unauthenticated user cannot access content', async ({ page }) => {
// Inject localStorage WITHOUT trusted_access
// Navigate to /idaa/archives/
// Assert: access denied message shown, no archive content visible
});
test('Archives - trusted member can access content', async ({ page }) => {
// Inject localStorage WITH trusted_access + novi_uuid
// Navigate to /idaa/archives/
// Assert: archive list renders
});
```
### Privacy in Test Data
- Never use real member data in test fixtures
- Use canonical demo IDs from `tests/_helpers/env.ts` only
- Test names should document the privacy rule being enforced, not just the behavior
### Trusted Access State Injection
Tests that need authenticated IDAA access must set `trusted_access: true` and `novi_uuid` in the injected `ae_loc` localStorage:
```typescript
// In addInitScript or env helper
ae_loc.trusted_access = true;
ae_loc.idaa_loc = { novi_uuid: 'test-uuid-value', ... };
```
### Current Test Coverage (as of 2026-04-07)
| Module | State | Notes |
|---|---|---|
| Archives | ⚠️ Smoke only | `archive_content.test.ts` — no auth gate test |
| Bulletin Board | ❌ None | Priority — most sensitive module |
| Recovery Meetings | ✅ Substantial | `tests/idaa_recovery_meeting_edit.test.ts` — form render, field interactions, PATCH payload verification (all sections), real backend save, creation linkage (Novi UUID in POST body) |
| Video Conferences | ❌ None | Jitsi complexity, lower priority |
| Jitsi Reports | ❌ None | Admin-only tool; lower privacy risk than member modules |
**Pending:** BB Post and Post Comment creation linkage tests (pattern established in Recovery Meetings test).
---
## External Links (idaa.org)
- Archives: `https://www.idaa.org/idaa-archives`
- Bulletin Board: `https://www.idaa.org/idaa-bulletin-board`
- Meetings: `https://www.idaa.org/idaa-meetings`
---
## Related Documentation
- [AE API V3 for Frontend](./GUIDE__AE_API_V3_for_Frontend.md)
- [Development Guide](./GUIDE__Development.md)
- [Naming Conventions](./AE__Naming_Conventions.md)
- [Playwright Test README](../tests/README.md)
---
## IDAA Novi Groups and Moderators
### "IDAA Association Admins Group" = "409e91dc-f5a3-486c-a964-71b7d19e6841"
* Scott
* Michelle
* Brie
### "IDAA Couples Meeting" = "e9e162f0-3d03-4241-9682-340135ec3fb8"
* "Gregory X Boehm" "00ee764c-7559-496b-9d18-40d3e9092c0c"
* "Kee B. PARK" "24ab3297-bfce-473c-9311-4b31e3a8974f"
* "Laura Lander" "ac697456-61fe-4f7d-a8b8-d04866032320"
* "Nancy J Duff-Boehm" "5c7c09bc-4f23-432c-bfd9-87a66b548502"
* "Owen Lander" "9671a2c4-ff95-48c2-bcde-5c6eba95cded"
* "Susan Park" "4a9f94c5-d766-4808-ab76-117c9e43903a"
### "Student/Resident Meeting Moderators" "d76d2c00-962d-40f6-a2e8-ed9c85594d96"
* "Melissa Eve Valasky" "182d1db3-caa9-41bc-b04a-2facc6859aeb"
* "Steven L. Klein" "5724aad7-6d89-47e7-8943-966fd22911bd"
### "IDAA BIPOC Meeting" "873d3ad0-2605-4ccf-824c-638c16b2b9cf"
* "Paula Lynn Bailey-Walton" "68383ba2-0989-4860-9ea6-073f9698df67"
* "Tasha Hudson" "03d5408c-3c13-4c3a-a93f-49871f9050b1"
---
**Document Status:** ✅ Current
**Last Verified:** 2026-05-19 — Access Gate: documented new `verify_error_type` error-handling states and retry/reset UI; Search Architecture: corrected contact-search status (now works via `default_qry_str` in API path — two root causes fixed 2026-05-18/19); noted IDB fast-path gap as remaining enhancement

View File

@@ -0,0 +1,99 @@
# Gemini's Svelte 5 Learnings and Best Practices
This document outlines key insights and strategies developed during Svelte 5 (with runes mode) refactoring and bug-fixing tasks. It specifically addresses common pitfalls and effective patterns for an AI agent working with Svelte.
## 1. Async/Await in Svelte (Runes Mode)
The most frequent source of errors has been related to asynchronous operations and the `await` keyword.
- **Rule:** Any function or block containing `await` _must_ be explicitly marked `async`.
- This applies to callbacks within promise chains (`.then()`, `.catch()`, `.finally()`), DOM event handlers (`onclick`, `onchange`), and Svelte lifecycle functions (`onMount`).
- **Common Error:** `Cannot use keyword 'await' outside an async function`. This happens when `await` is used in a function/block that is not `async`.
- **Solution:** Ensure the surrounding function or the callback itself is declared `async`.
- `somePromise.finally(async () => { await someAsyncOperation(); });`
- `onclick={async () => { await someAsyncOperation(); }}`
- `onMount(async () => { await someAsyncOperation(); });`
- **Asynchronous Navigation (`goto`):**
- The correct pattern for asynchronous navigation using `goto` in Svelte 5 is `await goto(await resolve(path), options);`.
- `import { resolve } from '$app/paths';` is crucial when using `resolve()`. A missing import will lead to `no-undef` errors for `resolve`.
- `resolve(path)` is crucial to ensure the path is correctly resolved before navigation, especially in universal applications.
- The `await` before `goto` is necessary if subsequent code depends on the navigation completing or if `invalidateAll` is used.
- **Clearing Stale Caches:** When encountering confusing linting or build errors that don't seem to match the current code, especially after significant refactorings or dependency changes, always try running `npm run clean` to clear stale build artifacts and then re-lint/re-build.
- **Refactoring Promise Chains:**
- When converting `.then().catch().finally()` chains to `async/await` structure, encapsulate the asynchronous operation within a `try...catch...finally` block.
- **Incorrect:**
```javascript
someAsyncFunc()
.then(() => { await anotherAsyncFunc(); }) // ERROR: .then() callback is not async
.catch(() => { /* ... */ })
.finally(() => { await lastAsyncFunc(); }); // ERROR: .finally() callback is not async
```
- **Correct:**
```javascript
async () => {
try {
const result = await someAsyncFunc();
await anotherAsyncFunc(result);
} catch (error) {
console.error(error);
} finally {
await lastAsyncFunc();
}
};
```
- **Note:** If the entire handler (e.g., `onclick`) is already `async`, you can `await` the promise directly within it.
## 2. Reactive Declarations & Scoping
Svelte 5's runes mode introduces new ways of managing reactivity, and understanding variable lifecycles is key.
- **`$state` for Reactive Variables:**
- Variables that are expected to trigger re-renders when their values change, or whose changes need to be observed, should be declared with `$state`.
- **Common Error:** Warnings like "This reference only captures the initial value of `data`. Did you mean to reference it inside a closure instead?" or "Variable `x` is updated, but is not declared with `$state(...)`."
- **Solution:** Declare the variable using `$state(initialValue)`.
- **Context for `data` prop:** When a `data` prop (from a SvelteKit `load` function) is accessed outside of reactive declarations (like `$effect` or event handlers), Svelte might warn that it only captures its initial value. To ensure reactivity or to correctly process it, use it within `$effect` or derive a `$state` variable from it.
- **Function Scoping and Redeclaration:**
- **Common Error:** `Identifier 'function_name' has already been declared`. This occurs when functions with the same name are defined in overlapping scopes.
- **Solution:**
- If a function is only needed within a specific block (e.g., an `onMount` callback), define it _inside_ that block to limit its scope.
- If a function is truly global and needs to be accessible from templates and multiple `onMount` blocks, define it once outside of any `onMount` and ensure it doesn't conflict with other definitions.
- Be mindful of helper functions that might be implicitly pulled into global scope by the Svelte compiler if not correctly encapsulated.
## 3. `replace` Tool Usage Strategy (Critical for AI)
My efficiency heavily relies on the `replace` tool, and precision is paramount.
- **Exact `old_string` Matching:**
- The `old_string` parameter _must_ precisely match the target text in the file, including all whitespace, indentation, newlines, and comments. Even a single character difference will cause the tool to fail with "0 occurrences found".
- For multi-line replacements, always copy the exact block from the `read_file` output.
- **Contextual Specificity:**
- Avoid generic `old_string` patterns (e.g., `onclick={() => {`) if there are many such occurrences in a file. Instead, expand the `old_string` to include enough surrounding unique context (e.g., the entire button element or parent `div`) to ensure it matches only one instance (`expected_replacements: 1`).
- **Iterative Refinement:**
- For complex refactorings involving multiple changes in a file, perform changes in small, atomic steps.
- **Always `read_file` before each `replace` operation.** This ensures the `old_string` is based on the absolute latest content of the file, preventing mismatches due to previous modifications or unexpected formatting.
- After each `replace` operation, immediately run `npm run build` (or `npm run lint`) to validate the change and catch new errors early. This is crucial for catching cascading issues introduced by partial refactorings.
## 4. Error Debugging Workflow
- **Prioritize Compiler/Build Errors:** These are blocking issues that prevent the application from running. Address them first.
- **Analyze Error Messages:** Read the full error message carefully, including line numbers, and look for keywords (e.g., `await`, `async`, `declared`, `undefined`).
- **Consult Svelte Documentation:** The Svelte compiler often provides helpful links (`https://svelte.dev/e/js_parse_error`) which should be a first point of reference if the error is unfamiliar.
- **Re-read File Content:** If a `replace` operation fails or produces unexpected results, immediately use `read_file` to verify the exact state of the file before attempting another change.
## 5. Safe Property Binding (Preventing `props_invalid_value`)
Svelte 5 enforces strict contracts for bound properties (`bind:prop`). If a component expects a property to have a fallback/default value, you cannot bind `undefined` to it.
- **The Error:** `Uncaught Svelte error: props_invalid_value. Cannot do bind:prop={undefined} when prop has a fallback value`.
- **The Cause:** Attempting to bind a variable that is currently `undefined` to a component prop that has a default value (e.g., `let { prop = false } = $props()`). Svelte cannot determine whether to use the bound `undefined` or the component's internal default, so it throws an error.
- **The Fix:** **Always initialize bound variables.**
- **Initialization:** Ensure the variable you are binding to is initialized to a valid value (matching the prop's type) *before* the component mounts.
- Example: `let myVar = $state(false);` instead of `let myVar = $state();`.
- **Data Fetching:** If the data comes from an asynchronous source (API, DB), ensure the object properties have default values *immediately* upon assignment, even before the data is fully populated.
- **Good:** `$slct.obj = { ...apiResult, myBoundProp: apiResult.myBoundProp ?? false };`
- **Bad:** `$slct.obj = apiResult;` (if `apiResult.myBoundProp` is missing/undefined).
- **Updates/resets:** When resetting or updating the object, explicitly re-initialize the bound properties.
- `obj = {};` -> `obj = { myBoundProp: false };`

View File

@@ -0,0 +1,704 @@
# Aether API V3 Frontend Integration Guide (Svelte/TypeScript)
This guide defines the standards for interacting with the **Aether API V3 CRUD** and **Action** endpoints.
---
## 1. Authentication and Security (Mandatory)
V3 architecture enforces strict **Multi-Tenant Isolation** and **Machine Authorization**. Requests require two levels of validation.
### A. The "Entry Ticket" (API Key)
**Mandatory for all requests.** identifies the application or client.
* **Header:** `x-aether-api-key: <your_app_key>`
* **Status Code:** `403 Forbidden` if missing or invalid.
### B. The "Visa" (Account Context)
Required for any non-public data (Journals, Badges, Users, etc.).
1. **Standard Access**: Provide the `x-account-id` (the random string ID).
* **Header:** `x-account-id: <account_id>`
2. **Administrative Bypass**: For authorized scripts needing global access.
* **Header:** `x-no-account-id: bypass`
* **Scope:** Narrow escape hatch only. Keep it limited to allowlisted bootstrap/public/global-default paths and prefer `x-account-id` or JWT-backed requests everywhere else.
3. **Token Access**: Provide a **JWT** in the query string.
* **Query Param:** `?jwt=<token>`
4. **Important Distinction:** A query parameter named `key` is **not** an account-context bypass signal.
* `key` may be used by specific endpoints/business logic, but it must **not** cause the frontend to remove `x-account-id`.
* Only explicit `x-no-account-id: bypass` should strip account context.
> [!NOTE]
> The `x-no-account-id` path should continue to shrink over time. If you need a new use, document why `x-account-id` or JWT cannot cover it and mark the use as temporary unless it is a hard bootstrap/global-default requirement.
> [!CAUTION]
> **UNSUPPORTED HEADERS:** The header `x-aether-api-token` is **NOT recognized** by the V3 API. If you send it, the backend will treat you as a guest and block access to private data.
---
## 2. Bootstrapping (The FQDN Handshake)
When the frontend first loads and doesn't know the `account_id`, it performs a "handshake" using its domain name.
**Endpoint:** `POST /v3/crud/site_domain/search`
**Body:**
```json
{
"and": [
{ "field": "fqdn", "op": "eq", "value": "demo.oneskyit.com" }
]
}
```
**Results:**
* Returns 200 + a list containing the `account_id` (random string ID) and `site_id` (random string ID).
* ** デザイン Choice:** If the domain is not found, it returns **200 OK with an empty list `[]`**. It is NOT a 404.
> **Access Key Support**
>
> Some client deployments restrict their domain via an access key passed in the browser URL (e.g. `?key=abc123`). The frontend reads this param and forwards it as `access_key` in the POST body.
>
> **How to pass the key:**
> ```json
> {
> "and": [
> { "field": "fqdn", "op": "eq", "value": "client.example.com" },
> { "field": "access_key", "op": "eq", "value": "abc123" }
> ]
> }
> ```
> If `key` is absent, empty, or falsy — **omit `access_key` from the payload entirely**. Do not send `"access_key": ""`.
>
> **Server behavior:**
> - `site_access_key` (site-level key) takes priority. If set, all domains under that site require it.
> - `site_domain_access_key` (domain-level key) is used as fallback when `site_access_key` is not set.
> - A domain is **public** only when **both** key columns are NULL/empty.
> - Falsy `access_key` values are ignored server-side as a safety net.
> - Match → `200` with the record. No match → `200` with empty list `[]`.
> - Do **not** use `access_code_kv_json` for this — that field is for UI features only.
>
> | Browser URL | `access_key` in payload | Result |
> |---|---|---|
> | `https://dev-demo.oneskyit.com` | *(omit)* | ✅ Returns record (public) |
> | `https://client.example.com/?key=correct` | `"correct"` | ✅ Returns record |
> | `https://client.example.com/` | *(omit)* | ❌ Empty (key required) |
> | `https://client.example.com/?key=wrong` | `"wrong"` | ❌ Empty (wrong key) |
> | `https://client.example.com/?key=` | *(omit — strip empty)* | ❌ Empty (key required) |
>
---
## 3. Standard CRUD Patterns
### A. GET by ID
Used when the ID is known.
* **Endpoint:** `GET /v3/crud/{obj_type}/{id}`
* **Security:** Returns 403 if the record doesn't belong to your `x-account-id`.
### B. POST Search
The primary way to retrieve data.
* **Endpoint:** `POST /v3/crud/{obj_type}/search`
* **Security:** Automatically filters results to only show records belonging to your `x-account-id`. If no account context is provided, it will return **0 records** for private objects.
### C. POST Create / PATCH Update
Modify data in the system.
* **Endpoints:**
* `POST /v3/crud/{obj_type}/`
* `PATCH /v3/crud/{obj_type}/{id}`
* **Strict Mode (Default):** The API validates your payload against the Pydantic model. If you send fields that do not exist in the model, the database might return a 400 "Unknown column" error.
* **Permissive Mode (Header):** To allow the frontend to send "extra" fields (like local UI state) without causing errors, use the following header:
* **Header:** `x-ae-ignore-extra-fields: true`
* **Behavior:** When set to `true`, the backend will automatically strip any fields from the payload that are not defined in the object's model before attempting to save to the database.
#### `*_json` field serialization — do NOT pre-stringify in route/component code
The frontend API wrappers (`src/lib/ae_api/api_post__crud_obj.ts` for V3, `src/lib/api/api.ts` for legacy CRUD) automatically serialize any field whose name ends in `_json` (e.g. `cfg_json`, `data_json`) before sending. They pretty-print with 2-space indent via an internal `serialize_json_field_pretty()` helper.
**Pass `*_json` fields as plain JS objects from routes and components.** The serialization layer handles the rest.
```ts
// ✅ Correct — pass as plain object; V3 wrapper serializes it
await update_ae_obj__site({ site_id, data_kv: { cfg_json: { jitsi_token_endpoint: url } } });
// ❌ Wrong — double-encodes the JSON string (the wrapper would stringify an already-stringified value)
await update_ae_obj__site({ site_id, data_kv: { cfg_json: JSON.stringify({ jitsi_token_endpoint: url }) } });
```
The V3 wrapper (`api_post__crud_obj.ts`) only serializes when `typeof value === 'object'`, so it will not double-encode a plain string. The legacy wrapper (`api.ts`) stringifies unconditionally, so pre-stringifying there **will** produce double-encoded JSON. In both cases, the right answer is to pass the raw object and let the layer handle it.
### D. ID Fields in Responses (Vision ID Convention)
> [!IMPORTANT]
> **V3 responses always use random string IDs — never database integers.**
All V3 responses — `POST` create, `GET` single, `GET` list, search, and `PATCH` update — contain:
| Field | Type | Use |
| :--- | :--- | :--- |
| `{obj_type}_id` | `string` | **Primary public ID.** Use this for subsequent `PATCH` calls and UI routing. |
| `{obj_type}_id_random` | `string` | Legacy alias. Same value as `{obj_type}_id`. Present for backward compat only. |
**Example — create then immediately PATCH:**
```ts
const created = await postArchiveContent(archiveId, payload);
const newId = created.data.archive_content_id; // random string e.g. "xK9mP3qRtL2"
// Use it directly in the PATCH URL — no lookup needed
await patchArchiveContent(newId, { name: 'Updated Name' });
// PATCH /v3/crud/archive/{archive_id}/archive_content/{newId}
```
> **Note on `_id_random` suffix:** The `{obj_type}_id_random` field is a legacy artifact from the pre-Vision model. Once you confirm `{obj_type}_id` is a random string (length 1122), you do not need `_id_random` as a fallback. New code should only read `{obj_type}_id`.
---
## 4. V3 Uniform Lookup System
The V3 Lookup system provides a hierarchical, deduplicated interface for standardized reference tables (Countries, Timezones, etc.). It supports global defaults, account-level overrides, and object-level overrides, with optional site-specific whitelisting.
### How the hierarchy works
Each lookup table (`lu_v3_country`, `lu_v3_time_zone`, etc.) can hold multiple rows for the same logical item at different scopes:
| Scope | `account_id` | `for_type` / `for_id` | Wins over |
|---|---|---|---|
| Global default | `NULL` | `NULL` / `NULL` | nothing |
| Account override | set | `NULL` / `NULL` | Global default |
| Object override | set | set | Account override + Global default |
The API uses `ROW_NUMBER() PARTITION BY group` to collapse all rows for the same item down to the single highest-priority winner before returning results. **`group` is the identity key** — it is what makes two rows "the same item competing for priority."
> [!IMPORTANT]
> **The `group` field is not a display label.** It is the deduplication key. Each lookup type uses a different natural key for `group`:
>
> | Lookup type | `group` value | Example |
> |---|---|---|
> | `country` | ISO alpha-2 code | `"US"`, `"CA"`, `"GB"` |
> | `country_subdivision` | subdivision code | `"US-NY"`, `"CA-ON"` |
> | `time_zone` | IANA timezone name | `"America/New_York"`, `"US/Eastern"` |
>
> For `time_zone`, `group` and `name` must always be identical — there is no concept of "override all US timezones as a group." Each timezone is its own identity.
### A. List Lookups
Retrieve the deduplicated, ranked list for a lookup type.
* **Endpoint:** `GET /v3/lookup/{lu_type}/list`
* **Available Types:** `country`, `country_subdivision`, `time_zone`
* **Parameters:**
* `site_id` (Optional): Random ID of the site — applies a **Whitelist Policy** (see §C).
* `only_priority` (Optional): `true` returns only `priority=1` items (e.g., common time zones).
* `for_type` / `for_id` (Optional): Object context — activates object-level override matching.
* `include_disabled` (Optional): `true` includes shadowed/disabled records (useful for admin views).
**Frontend keying:** Always key Svelte `{#each}` blocks on `group`, not `id` or `name`. `group` is guaranteed unique in the response. Keying on `id` will break if an account override wins (different `id`, same logical item).
### B. Resolve Identity
Resolves a string to a single lookup record.
* **Endpoint:** `GET /v3/lookup/{lu_type}/resolve?q=VALUE`
* **Usage:** Use when you have an external code (e.g., ISO `"US"`) and need the full Aether record. Scans `name`, `group`, and other identity fields.
### C. Site Whitelist Policy
To restrict which lookup items appear for a specific site, add a `lookup_policy` to `site.cfg_json`:
```json
{
"lookup_policy": {
"country": ["US", "CA", "GB"],
"time_zone": ["America/New_York", "US/Eastern"]
}
}
```
> **Whitelist values must match the `group` field** — i.e., the natural key for that type (ISO code for country, IANA name for time zone). Using a display name will silently return no results for that item.
### D. Adding and managing client overrides
When a client needs a customized label or wants to hide/reorder lookup items, create override records rather than modifying global defaults.
**Rules:**
1. **Never modify global default rows** (`account_id = NULL`). Those are shared across all accounts. Any change there affects every client.
2. **Set `group` to the exact same value as the global default row** for the item you are overriding. If `group` doesn't match, the override creates a new item instead of replacing the existing one.
3. **Set `account_id`** to the client's account ID. Leave `for_type` / `for_id` null unless the override is specific to a single object (e.g., one site).
**Example — rename "US/Eastern" for one account:**
```sql
INSERT INTO lu_v3_time_zone
(account_id, name, name_override, `group`, enable, priority, sort)
VALUES
(42, 'US/Eastern', 'Eastern Time (Client Label)', 'US/Eastern', 1, 1, 50);
```
The `name_override` field is the display label the frontend should prefer when set. `group = 'US/Eastern'` ensures this row competes with — and wins over — the global default in the `PARTITION BY group` deduplication.
**To disable an item for one account** (hide it from their dropdowns):
```sql
INSERT INTO lu_v3_time_zone
(account_id, name, `group`, enable)
VALUES
(42, 'US/Samoa', 'US/Samoa', 0);
```
Setting `enable = 0` on an account-scoped row shadows the global default for that account only.
**To remove a client override** (revert to global default):
Simply delete the row where `account_id = <client>` and `group = '<item>'`. The global default row is unaffected and immediately resumes winning.
### E. Adding new global lookup items
When seeding new lookup data (e.g., adding timezones in bulk):
1. Set `group = name` for every row (for `time_zone`). This is a hard invariant — if `group` is set to a regional label like `"United States"` instead of the timezone name, the entire group collapses to a single winner and all but one entry disappear from the API response.
2. Set `account_id = NULL` and `for_type = NULL` / `for_id = NULL` for global defaults.
3. After seeding, verify with:
```sql
-- Should return 0 rows; any result means multiple items will collapse into one
SELECT `group`, COUNT(*) AS cnt
FROM lu_v3_time_zone
WHERE account_id IS NULL
GROUP BY `group`
HAVING cnt > 1;
```
---
## 5. Event File Data Retrieval (Hosted Files)
Every Event File (`event_file`) **must** have a linked Hosted File (`hosted_file`). The Hosted File itself is a metadata record for binary content (files), which is accessed via separate Action endpoints (e.g., `/v3/action/hosted_file/download`). This API endpoint provides metadata about the associated hosted file. To retrieve this additional metadata:
* **Endpoint:** `GET /v3/crud/event_file/{event_file_id}`
* **Query Parameter:** Add `inc_hosted_file=true`
* Example: `/v3/crud/event_file/<event_file_id>?inc_hosted_file=true`
**Response Impact:**
1. **Top-Level Convenience Fields:** The response will include top-level fields for commonly needed hosted file data. These are populated directly from the SQL view via JOINs.
* `hosted_file_hash_sha256` (string)
* `hosted_file_subdirectory_path` (string)
* `hosted_file_content_type` (string)
* `hosted_file_size` (string - in bytes)
2. **Nested Hosted File Object:** A full `hosted_file` object will be nested under the `hosted_file` key. This object (`Hosted_File_Base` model) will contain all its standard fields, including `id` (random string ID), `hash_sha256`, `content_type`, `size`, etc.
---
## 6. Hosted File Actions: Convert & Clip (Frontend Notes)
These helper endpoints let the frontend request small server-side transformations without uploading new blobs. They return a newly-created `hosted_file` metadata object on success.
- **Convert (PDF → Image)**
- Method: `GET`
- Path: `/v3/action/hosted_file/{hosted_file_id}/convert_file`
- Required query params: `link_to_type`, `link_to_id`
- Optional query params: `filename_no_ext` (defaults to `automated_hosted_file_conversion`), `to_type` (defaults to `webp`)
- Auth: standard V3 headers (`x-aether-api-key`, `x-account-id` / `x-no-account-id` / `?jwt=`)
- Behavior: converts the first page of a PDF to `webp` or `png`, saves a new `hosted_file`, and returns its metadata. Returns 400 on failure.
- **Clip Video**
- Method: `GET`
- Path: `/v3/action/hosted_file/{hosted_file_id}/clip_video`
- Required query params: `link_to_type`, `link_to_id`, `start_time`, `end_time` (format `HH:MM:SS`)
- Optional query params: `filename_no_ext` (defaults to `automated_hosted_file_clip_video`), `reencode` (bool), `scale_down` (bool)
- Auth: standard V3 headers
- Behavior: extracts a clip using `ffmpeg` and saves it as a new `hosted_file`. Defaults to stream-copying to be fast; set `reencode=true` to force H.264 or `scale_down=true` to resize. Returns 400 on failure.
- Behavior: extracts a clip using `ffmpeg` and saves it as a new `hosted_file`.
- Defaults to stream-copying to be fast; set `reencode=true` to force H.264 or `scale_down=true` to resize.
- For longer-running clips you can schedule the job in the background by adding `?background=true`. When scheduled the API returns `202 Accepted` and the clip runs asynchronously on the server; check the returned `hosted_file` record later via the standard V3 `hosted_file` endpoints.
- Returns 400 on synchronous failure; returns 202 when scheduled successfully.
Frontend guidance:
- Call these routes with the same `link_to_type` / `link_to_id` you plan to associate the resulting hosted_file with — the server resolves random IDs for you.
- After a successful response, use the V3 `hosted_file` action endpoints (download/delete) to manage or retrieve the new file.
- These endpoints run synchronously and can take time for large inputs; for heavy or batch workloads use a queued job pattern instead.
- These endpoints may take time for large inputs. Prefer using `?background=true` to schedule work and receive a `202 Accepted` response for async processing. For heavy or batch workloads use a queued job pattern instead.
---
## 8. Email Send Action
Send a transactional email via the Aether API.
- **Method:** `POST`
- **Path:** `/v3/action/email/send`
- **Auth:** `x-aether-api-key` + `x-account-id` (or `x-no-account-id` / `?jwt=`)
**Request body:**
```json
{
"from_email": "noreply@example.com",
"from_name": "Example App",
"to_email": "user@example.com",
"to_name": "Alice Smith",
"subject": "Your login link",
"body_html": "<p>Click <a href=\"...\">here</a> to log in.</p>",
"body_text": "Visit ... to log in.",
"cc_email": null,
"bcc_email": null
}
```
**Query params:**
| Parameter | Type | Default | Description |
|---|---|---|---|
| `test` | bool | `false` | Simulate send without delivering |
**Response:** `data` contains `{ from_email, to_email, subject }` (first 40 chars of subject). `400` if delivery failed.
> **Replaces:** `POST /util/email/send` (disabled as of May 2026).
---
## Axonius Zoom CSV Upload (Temporary — Apr 2026, EXPIRED)
Purpose: Staff-only quick upload to upsert Event Person + Event Badge records from a Zoom Events registrant CSV.
- **Endpoint:** `POST /event/{event_id}/badge/import/zoom_csv`
- **Auth:** include `x-aether-api-key` (if required) and account context via `x-account-id: <ACCOUNT_ID>`. Admin bypass (`x-no-account-id: bypass`) or `?jwt=<token>` are accepted per site policy.
- **Request:** `multipart/form-data` with single file field `file` (Zoom CSV). Query params:
- `begin_at` (int, default `0`)
- `end_at` (int, default `20000`)
- `return_detail` (bool, default `false`)
- Delimiter is auto-detected; Zoom CSV layout: row 1 = metadata, row 2 = blank, row 3 = headers (the backend skips the first two rows).
Behavior / notes:
- The handler forces `Registrant email` to be used as the `external_id`. `Unique identifier` is used as `external_registration_id` only when it is meaningful (placeholders like `N/A`, `NA`, `UNKNOWN` are ignored).
- Per-ticket custom fields are parsed (Organization, Job title, Phone, Address lines, City, State/Province, Postal/Zip, Country, etc.).
- Marketing-consent values are mapped to `agree_to_tc` and `allow_tracking`.
- TEMP AXONIUS MAPPING: the import temporarily defaults `event_badge_template_id` to `21` and `event_badge_template_id_random` to `RKYp2HcQm9o`. Ticket-name → `badge_type_code` mapping is applied for some labels (e.g., contains "sponsor" → `sponsor`; contains "attend"/"attendee" → `attendee`). This mapping is temporary (April 2026) — surface this to staff.
- Rows missing `Registrant email` are skipped.
- The server upserts via existing backend methods and creates/updates `event_person`, `event_person_profile`, and `event_badge` records as needed.
Frontend guidance:
- UI must be staff-only and should validate an `event_id` is selected.
- For large files, use `begin_at`/`end_at` to process in chunks.
- Prefer `return_detail=false` for large imports to reduce payload size.
Common errors:
- `403` — missing/invalid account context or API key.
- `404` — event not found.
- `500` — file save or processing error.
Example curl (replace placeholders):
```bash
curl -v -X POST "https://api.example.com/event/<EVENT_ID>/badge/import/zoom_csv?begin_at=0&end_at=20000&return_detail=false" \
-H "x-aether-api-key: <API_KEY>" \
-H "x-account-id: <ACCOUNT_ID>" \
-F "file=@/path/to/zoom_export.csv"
```
Sample success (summary mode, `return_detail=false`):
```json
{
"data": [
{
"event_id": "xK9mP3qRtL2",
"event_id_random": "xK9mP3qRtL2",
"external_id": "alice@example.com",
"given_name": "Alice",
"family_name": "Smith",
"email": "alice@example.com"
}
],
"meta": {
"status_code": 200,
"status_name": "OK",
"success": true,
"data_type": "list",
"data_list_count": 1
}
}
```
Sample success (detailed, `return_detail=true`) — `data` contains full `event_person` objects with nested `event_badge` (may include temporary `event_badge_template_id`: `21` and `event_badge_template_id_random`: `RKYp2HcQm9o`).
Paste this section into the guide as a temporary Axonius-specific note (April 2026). Consider linking staff to a sample Zoom CSV for QA.
---
## 7. User Actions (`/v3/action/user/`)
Stateful user account operations that are not standard CRUD. All require `x-aether-api-key`.
> [!IMPORTANT]
> **Migration from legacy `/user/*` routes:** The table below maps each legacy endpoint to its V3 replacement. Run both in parallel during transition; remove legacy routes once traffic logs confirm they are quiet.
>
> | Legacy | V3 Replacement |
> |---|---|
> | `GET /user/authenticate` | `POST /v3/action/user/authenticate` |
> | `POST /user/verify_password` | `POST /v3/action/user/verify_password` |
> | `PATCH /user/{id}/change_password` | `POST /v3/action/user/{id}/change_password` |
> | `GET /user/{id}/new_auth_key` | `GET /v3/action/user/{id}/new_auth_key` |
> | `GET /user/{id}/email_auth_key_url` | `GET /v3/action/user/{id}/email_auth_key_url` |
> | `GET /user/lookup` | `POST /v3/crud/user/search` |
> | `GET /user/lookup_email` | `POST /v3/crud/user/search` |
> | `GET /user/lookup_username` | `POST /v3/crud/user/search` |
### A. Authenticate
Authenticate a user by **username + password** or **user_id + auth_key**.
- **Method:** `POST`
- **Path:** `/v3/action/user/authenticate`
- **Auth:** `x-aether-api-key` + `x-account-id` (scopes username lookups to the correct account)
- **Security improvement:** Credentials are in the **POST body**, not query params — safe from URL logging.
**Request body:**
```json
{ "username": "scott", "password": "MyPassword123!" }
```
or:
```json
{ "user_id": "<user_id_random>", "auth_key": "<one_time_key>", "valid_email": true }
```
- `valid_email` (optional `bool`): if `true`, marks `email_verified = true` on success.
- `inc_user_role_list` (optional query param, default `false`): include role list in the returned user object.
**Response on success:** Full user object (same shape as `GET /v3/crud/user/{id}`).
**Errors:** `400` missing credentials, `403` wrong password / account disabled / account not yet enabled / account expired, `404` user not found.
> **Auth key flow:** Auth keys are one-time-use — the key is cleared from the DB immediately on successful authentication. Request a new one via `GET /v3/action/user/{id}/new_auth_key`.
---
### B. Verify Password
Check a user's current password without changing it.
- **Method:** `POST`
- **Path:** `/v3/action/user/verify_password`
- **Auth:** `x-aether-api-key` + `x-account-id`
**Request body:**
```json
{ "user_id": "<user_id_random>", "current_password": "MyPassword123!" }
```
or use `"username"` instead of `"user_id"` to look up by username within the account.
**Response:** `data: true` on match. `400` if the user has no password set, `403` on mismatch, `404` if user not found.
---
### C. Change Password
Change a user's password. Optionally verify the current password first.
- **Method:** `POST`
- **Path:** `/v3/action/user/{user_id}/change_password`
- **Auth:** `x-aether-api-key` + `x-account-id`
**Request body:**
```json
{ "new_password": "NewPassword456!", "current_password": "MyPassword123!" }
```
- `new_password` is required (minimum 10 characters).
- `current_password` is optional. If provided, it is verified before the change is applied. Omit it for admin-driven resets.
**Response:** `data: true` on success. `403` if `current_password` provided but wrong.
---
### D. Generate New Auth Key
Generate a fresh one-time-use auth key for the user and write it to the DB.
- **Method:** `GET`
- **Path:** `/v3/action/user/{user_id}/new_auth_key`
- **Auth:** `x-aether-api-key` + `x-account-id`
**Response:**
```json
{ "data": { "auth_key": "<new_key>" } }
```
The returned key can then be passed to `/authenticate` (as `auth_key`) or embedded in a login URL. The user record must have `allow_auth_key = true` for key-based authentication to work.
---
### E. Email Auth Key URL
Generate a new auth key and email a one-time login link to the user's email address.
- **Method:** `GET`
- **Path:** `/v3/action/user/{user_id}/email_auth_key_url`
- **Auth:** `x-aether-api-key` + `x-account-id`
**Query Parameters:**
| Parameter | Type | Default | Description |
|---|---|---|---|
| `root_url` | `string` | *(required)* | Base URL the login link is built from. Must be provided — if omitted the link in the email will be malformed (`None?...`). |
| `key_param_name` | `string` | `auth_key` | Query param name used for the auth key in the generated link. |
> [!IMPORTANT]
> `root_url` is **required in practice**. The FastAPI query param accepts `null` but the email builder does not guard against it — omitting it produces a broken link in the email.
**Magic link URL format (default `key_param_name`):**
```
{root_url}?user_id={user_id_random}&auth_key={auth_key}&valid_email=True
```
The frontend at `root_url` should read these query params and call `POST /v3/action/user/authenticate` with `{ "user_id": "...", "auth_key": "..." }`. Note that `valid_email=True` is **always** injected — authenticating via a magic link automatically marks the user's email as verified.
**Response:** `data: true` on success (email sent). `404` if user not found. `500` if delivery failed — common causes: account email not configured, user `enable = false`, or `allow_auth_key = false`.
---
### F. User Lookups via V3 CRUD Search
The three legacy lookup routes (`lookup`, `lookup_email`, `lookup_username`) are replaced by standard V3 CRUD search:
```typescript
// Look up by user_id (Vision ID)
POST /v3/crud/user/search
{ "and": [{ "field": "id_random", "op": "eq", "value": "<user_id>" }] }
// Look up by email
POST /v3/crud/user/search
{ "and": [{ "field": "email", "op": "eq", "value": "user@example.com" }] }
// Look up by username
POST /v3/crud/user/search
{ "and": [{ "field": "username", "op": "eq", "value": "scott" }] }
```
Results are automatically scoped to the `x-account-id` provided in the request.
---
## 10. Event Exhibit Tracking Export (Leads Export)
Allows an exhibitor to download all lead-capture records for their exhibit as a CSV or XLSX file.
- **Method:** `GET`
- **Path:** `/v3/action/event_exhibit/{exhibit_id}/tracking_export`
- **Auth:** Standard V3 headers (`x-aether-api-key` + `x-account-id` or `?jwt=`)
### Query Parameters
| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `file_type` | `CSV` \| `XLSX` | `CSV` | Output format. |
| `return_file` | bool | `true` | `true` → file download response. `false` → JSON body with row data. |
### Response
- `Content-Type: text/csv` (CSV) or `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` (XLSX)
- `Content-Disposition: attachment; filename="leads_export_<timestamp>.csv"`
- If there are no tracking records, a valid file with headers only is returned (not a 404).
### Columns Returned
Fixed columns (always present), followed by any custom question columns flattened from `responses_json`:
`event_exhibit_tracking_id`, `created_on`, `updated_on`, `event_exhibit_name`, `event_badge_full_name`, `event_badge_email`, `event_badge_professional_title`, `event_badge_affiliations`, `event_badge_location`, `event_badge_country`, `external_person_id`, `exhibitor_notes`, `priority`, `enable`, `hide`, `[custom question codes…]`
> **Note:** `exhibitor_notes` has HTML tags stripped automatically for clean CSV output.
### Permission Requirement — `leads_api_access`
> [!IMPORTANT]
> This endpoint enforces a **per-exhibit permission flag**. The `event_exhibit` record **must** have `leads_api_access = true` set in the database, OR the caller must have manager-level account access (JWT with `manager: true`).
>
> If `leads_api_access` is `false` or `null` on the exhibit, the API returns:
> ```json
> { "detail": "Access denied: leads API access is not enabled for this exhibit." }
> ```
> **Fix:** Enable the flag on the exhibit record via `PATCH /v3/crud/event_exhibit/{id}` with `{ "leads_api_access": true }`, or set it directly in the database/admin panel.
#### Dual purpose of `leads_api_access`
This flag serves two related but distinct roles:
1. **3rd-party API access (original intent):** Controls whether external systems (exhibitor apps, badge-scanning devices, etc.) are permitted to push or pull lead data for this exhibit via the API.
2. **UI export gate (new):** The frontend should read `leads_api_access` from the exhibit record and use it to show or hide the export/download button. Only render the button when the flag is `true` — this prevents users from triggering a request that will always 403.
The recommended pattern is to fetch the exhibit record first and gate the UI on this field before the user ever sees the export option. The API enforces the same check server-side as a safety net.
### Example Request
```ts
const resp = await fetch(
`https://dev-api.oneskyit.com/v3/action/event_exhibit/${exhibitId}/tracking_export?file_type=CSV&return_file=true`,
{
headers: {
'x-aether-api-key': API_KEY,
'x-account-id': accountId,
},
}
);
// resp is a file blob — use URL.createObjectURL() or trigger a download
const blob = await resp.blob();
const url = URL.createObjectURL(blob);
```
---
## 12. IDAA: Server-Side Novi Member Verification
Verifies a Novi AMS member UUID by proxying the Novi API call through the Aether backend. This eliminates false "Access Denied" failures for members on hotel/conference WiFi, VPNs, and Cloudflare-filtered networks — the Novi call originates from the server's IP, not the member's browser IP.
- **Method:** `GET`
- **Path:** `/v3/action/idaa/novi_member/{uuid}`
- **Auth:** Standard V3 (`x-aether-api-key` + `x-account-id` or `?jwt=`)
### Request
| Parameter | Location | Required | Description |
|---|---|---|---|
| `uuid` | Path | Yes | Novi member UUID (from Novi AMS) |
### Response on success (`200 OK`)
```json
{
"data": {
"verified": true,
"full_name": "Alice S.",
"email": "alice+member@idaa.org"
}
}
```
- `full_name`: `"{FirstName} {LastName[0]}."` format. Falls back to the Novi `Name` field if first/last are absent.
- `email`: Novi `Email` field with space → `+` normalization applied (Novi quirk — `alice member@idaa.org` → `alice+member@idaa.org`).
### Error responses
| Status | Meaning | Frontend action |
|---|---|---|
| `404` | UUID not found in Novi, or Novi returned 200 with no identity data (empty-member anti-pattern — member may have just joined) | Treat as denied / not a member |
| `429` | Novi rate limit hit | Surface as `'rate_limited'`; advise retry |
| `503` | Novi unreachable or Novi 5xx error | Auto-retry once after 3s; if retry also fails, surface as `'api_error'` |
### Migration from direct Novi call — ✅ Complete (2026-05-19)
`+layout.svelte:verify_novi_uuid()` now calls this endpoint instead of Novi directly. Response code mapping (for reference):
| Direct Novi result | This endpoint returns | Frontend behavior |
|---|---|---|
| `200` with identity data | `200` | `verified` |
| `200` with no identity data | `404` | `denied` |
| `404` | `404` | `denied` |
| `429` | `429` | Auto-retry after 10s; `'rate_limited'` if retry fails |
| Network error / Novi 5xx | `503` | Auto-retry after 3s; `'api_error'` if retry fails |
### Caching
Verified results are cached in Redis (`idaa:novi_member:{uuid}`, 4-hour TTL). `404` results are **never** cached so recently-joined members are not incorrectly denied on their next attempt.
---
## 11. Troubleshooting 403 Forbidden
If you receive a 403 on a valid ID:
1. Verify `x-aether-api-key` is correct.
2. Ensure you are sending `x-account-id` and NOT `x-aether-api-token`.
3. Verify the record actually belongs to the account ID you are sending.
4. Check if the object is marked `public_read: True` in the registry. (Posts and Archive Content allow guest access; Journals and Badges do not).
5. Confirm the frontend is not treating `params.key` as an implicit bypass and stripping `x-account-id`.
6. If list/search endpoints work but `GET /v3/crud/{obj_type}/{id}` still returns 403, this is likely endpoint-level policy (e.g., requires stronger auth like JWT) rather than a transport/header bug.

View File

@@ -0,0 +1,201 @@
# Aether API V3 WebSocket Integration Guide
This guide explains how to implement real-time communication using the **Aether API V3 WebSocket** protocol. V3 introduces granular routing, strict message schemas, and improved multi-tenant isolation compared to previous versions.
---
## 1. Key Improvements (V2 vs V3)
| Feature | WebSocket V2 (Legacy) | WebSocket V3 (Modern) |
| :--- | :--- | :--- |
| **URL Prefix** | `/ws/` or `/ws_redis/` | `/v3/ws/` |
| **Routing** | **Global**: Every client receives every message. | **Granular**: Redis filters messages before sending. |
| **Performance**| Low efficiency at scale (Python filtering). | High efficiency (Redis native pub/sub). |
| **Schema** | Loose JSON objects. | Strict Pydantic-validated models. |
| **Presence** | None / Manual. | Automatic Redis-backed presence sets. |
---
## 2. Connection Strategy
### A. Endpoint URL
The V3 WebSocket path requires both a `group_id` and a `client_id`. Both are treated as **opaque unique strings** by the backend — no specific format is enforced.
```text
wss://[api_domain]/v3/ws/group/{group_id}/client/{client_id}
```
**`group_id`** — identifies the shared channel (e.g., an event ID, a room name, or a Vision ID random string). All clients using the same `group_id` receive group-targeted messages together.
**`client_id`** — uniquely identifies this specific connection. The backend accepts any unique string (UUID, timestamp, Vision ID — no format validation). The **recommended pattern** is a UUID v4 generated once and persisted in `localStorage` so the same identity is reused across page reloads and sessions on that browser.
> Use `ws://` for local development and `wss://` in production (any HTTPS site). The Nginx config must include the Upgrade block — see Section 6.
### B. Authentication
Browsers **cannot** set custom HTTP headers on WebSocket connections. Pass the API Key and account context as **query parameters** instead:
| Parameter | Purpose | Example |
| :--- | :--- | :--- |
| `api_key` | Entry Ticket (machine auth) | `?api_key=<your_app_key>` |
| `jwt` | Visa (user / account context) | `&jwt=<token>` |
| `x_account_id` | Alt account context | `&x_account_id=<account_id>` |
**Full example URL:**
```text
wss://dev-api.oneskyit.com/v3/ws/group/{group_id}/client/{client_id}?api_key=<key>&jwt=<token>
```
### C. Connection Example (TypeScript)
```ts
// client_id: generated once, persisted in localStorage for stable identity across reloads
if (!localStorage.getItem('controller_client_id')) {
localStorage.setItem('controller_client_id', crypto.randomUUID());
}
const client_id = localStorage.getItem('controller_client_id')!; // UUID v4, e.g. "550e8400-e29b-41d4-a716-446655440000"
const group_id = "event_abc123"; // Any unique string identifying the shared channel
const api_key = import.meta.env.VITE_API_KEY;
const jwt = getSessionToken(); // your JWT helper
const ws_url = `wss://dev-api.oneskyit.com/v3/ws/group/${group_id}/client/${client_id}?api_key=${api_key}&jwt=${jwt}`;
const socket = new WebSocket(ws_url);
socket.onopen = () => {
console.log("Connected to Aether WS V3");
};
```
---
## 3. The V3 Message Schema
All messages sent and received over V3 must follow the standardized **WS_Message_V3** structure.
### Message Fields
| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `version` | string | Auto | Always `"3"`. Set by server. |
| `msg_type` | string | Yes | `'msg'`, `'cmd'`, `'heartbeat'`, `'presence'` |
| `target` | string | Yes | `'direct'`, `'group'`, `'broadcast'`, `'echo'` |
| `from_id` | string | Auto | **Server fills this from the URL path** — do not send. |
| `to_id` | string | Conditional | Target Client ID. Required when `target` is `'direct'`. |
| `group_id` | string | Auto | **Server fills this from the URL path** — do not send. |
| `cmd` | string | No | Specific action keyword (e.g., `'RELOAD'`). |
| `msg` | string | No | Human-readable text content. |
| `payload` | object | No | Flexible key-value data. |
| `sent_at` | string | Auto | ISO 8601 Timestamp. Set by server. |
> **Frontend tip:** Only send `msg_type`, `target`, and whatever content fields you need (`msg`, `cmd`, `payload`, `to_id`). The server enforces `from_id`, `group_id`, and `sent_at` from the connection context, preventing spoofing.
---
## 4. Message Targeting Logic
V3 uses the `target` field to determine which Redis channel to use, ensuring only the intended recipients receive the data.
### A. Group Broadcast
Sends the message to every client connected to the same `group_id`.
```json
{
"msg_type": "msg",
"target": "group",
"msg": "Hello team!"
}
```
### B. Direct Message (DM)
Sends the message to one specific client ID, regardless of their group.
```json
{
"msg_type": "msg",
"target": "direct",
"to_id": "target_client_random_id",
"msg": "Private message just for you."
}
```
### C. System Broadcast
Sends the message to **every** connected client on the platform (use sparingly).
```json
{
"msg_type": "cmd",
"target": "broadcast",
"cmd": "MAINTENANCE_WARNING"
}
```
### D. Echo
Sends the message back only to the sender (useful for testing round-trip latency).
```json
{
"msg_type": "msg",
"target": "echo",
"msg": "Ping!"
}
```
---
## 5. Specialized Message Types
### Commands (`cmd`)
Used for remote control or orchestration.
```json
{
"msg_type": "cmd",
"target": "group",
"cmd": "RELOAD_UI",
"payload": { "force": true }
}
```
### Heartbeats (`heartbeat`)
Keep the connection alive and **refresh presence** in the backend. Should be sent every 30-60 seconds.
- The server intercepts `heartbeat` messages and refreshes the Redis presence TTL (1 hour window) before echoing back.
- Without periodic heartbeats, a client idle for >1 hour may disappear from the presence set even while still connected.
- Use `target: 'echo'` so the server sends the heartbeat straight back — useful for measuring round-trip latency.
```json
{
"msg_type": "heartbeat",
"target": "echo"
}
```
---
## 6. Infrastructure Requirements (Nginx)
Unlike standard REST endpoints, WebSockets require explicit "Upgrade" handling in the Nginx gateway. If you are deploying to a new server, ensure the following block is present in your Nginx configuration:
```nginx
location /v3/ws {
proxy_pass http://fastapi_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_read_timeout 2100s; # Match your app's max heartbeat/session time
}
```
---
## 7. Common Pitfalls & Troubleshooting
- **HTTP 404 Errors**: This almost always means Nginx is missing the `location /v3/ws` block and is trying to serve the request as a static file from the disk.
- **HTTP 400 Errors**: Check your `Host` header. Nginx routes requests based on the `server_name` directive. If you connect to an IP or a non-standard hostname (like `localhost`), ensure it is explicitly listed in your Nginx config.
- **Connection Drops**: If the connection drops exactly after 60 seconds, check your Nginx `proxy_read_timeout`. It should be set high (e.g., `2100s`) to allow for long-lived WebSocket sessions.
---
## 8. Migration Guide (V2 to V3)
If you are upgrading from the legacy V2 WebSocket (`/ws/group/...`):
1. **Change the URL**: Prepend `/v3/` to your WebSocket path.
2. **Wrap your JSON**: In V2, you might have sent `{"msg": "hi"}`. In V3, this must be `{"msg_type": "msg", "target": "group", "msg": "hi"}`.
3. **Use unique string IDs**: Both `group_id` and `client_id` in the path are opaque strings — any unique value works (timestamp, UUID, Vision ID random string). Just don't use raw database integer IDs. For `to_id` in direct messages, use whatever `client_id` that target client registered with.
4. **Listen for `msg_type`**: Update your frontend handlers to switch logic based on the `msg_type` field instead of proprietary keys.

View File

@@ -0,0 +1,207 @@
# Aether Events — Onsite Badge Printing
Notes on setup, process, hardware, and browser behavior for onsite badge printing at events.
---
## Overview
Aether badge printing uses the browser's native `window.print()` — no special software or print
server needed. The badge render page (`/events/[event_id]/badges/print/[badge_id]`) outputs
print-ready HTML/CSS, and the browser sends it directly to the connected printer via CUPS (Linux)
or the OS print system (macOS/Windows).
Chrome (Chromium) is the recommended browser for onsite kiosk stations.
Firefox is a solid alternative, especially for Save-to-PDF workflows.
---
## Recommended Workflow — Onsite Kiosk
1. Open the event's badge printing page: `/events/[event_id]/badges`
2. Search for the attendee (name, badge ID, or QR scan)
3. Open the badge print page — review the rendered badge
4. Click **Print Badge** in the controls panel (or use keyboard shortcut)
5. In the browser print dialog:
- Set Margins to **None** (Chrome) or leave defaults (Firefox)
- Confirm paper/card size matches the stock loaded in the printer
- Print
6. `print_count` increments automatically on each print via the Print Badge button
For high-volume events, consider the **rapid QR scan** mode in the Leads module or using a
dedicated kiosk session where the operator only handles physical card handoff.
---
## Browser Settings
### Chrome / Chromium (Recommended for kiosk use)
Chrome is recommended for onsite badge printing stations. Key print dialog settings:
| Setting | Correct value | Notes |
|---|---|---|
| Margins | **None** or **Minimum** | Default margins add URL/date headers — breaks badge centering |
| Paper size | Match card stock (e.g. 3.5" × 5.5") | Zebra driver may override this automatically |
| Background graphics | **On** | Required for colored header/footer stripe to print |
| Pages | 1 | PVC single-sided — only front should print |
**Important:** Chrome ignores CSS `@page { size }` for Save to PDF — it defaults to letter/A4.
For physical printer output, the printer driver controls paper size. This is expected behavior.
To lock Chrome settings for a kiosk, set Margins to "None" once and Chrome remembers per-printer.
### Firefox
Firefox honors CSS `@page { size }` which makes it ideal for PDF generation.
For physical printing, Firefox generally "just works" without margin adjustments.
| Setting | Notes |
|---|---|
| Paper size | Can be set in dialog, but CSS `@page { size }` is honored |
| Margins | Default is usually fine; remove headers/footers if they appear |
| Background graphics | Enable for colored stripes and header images to print |
### General Notes
- **Background graphics must be enabled** in any browser — otherwise header images, footer
color stripes, and tonal backgrounds will not print.
- Private/incognito mode blocks PWA install prompts — use normal browser sessions for kiosk.
- For highest reliability, set the kiosk machine to auto-login and open Chrome to the event URL.
---
## Linux / CUPS Setup
For Linux workstations and dedicated kiosk machines running Linux:
1. Install CUPS if not already present: `sudo pacman -S cups` (Arch) or equivalent
2. Start the CUPS service: `sudo systemctl enable --now cups`
3. Open the CUPS web UI: `http://localhost:631`
4. Add the printer and install the appropriate driver (see per-printer sections below)
5. Print a test page from CUPS to confirm card feed and quality
6. In Chrome: select the CUPS printer name under Destination in the print dialog
On macOS and Windows, use the vendor-provided driver installer.
---
## Printers
---
### Zebra ZC10L — PVC Card Printer
**Card stock:** 3.5" × 5.5" PVC cards (CR80 extended)
**Tested:** 2026-03-17 (rental test day, Arch Linux)
**Status:** Working. Confirmed suitable for Axonius NYC (mid-April 2026).
#### Physical Setup
- Connect via USB (the ZC10L supports USB and Ethernet)
- Load PVC card stock per the Zebra loading instructions — cards face-up, landscape
- The ZC10L prints one side (single-sided dye-sub thermal); do not attempt duplex on PVC stock
#### Linux Driver
- Download the Zebra ZC10L CUPS driver from zebra.com (ZC Series Linux support)
- Install the `.deb` or extract the PPD file and add to CUPS manually
- In CUPS (`http://localhost:631`), add the printer and select the ZC10L PPD
- Set default paper size to **3.5" × 5.5"** (or CR80 Extended if listed)
- Print a blank test page from CUPS before using Chrome
> **Note:** Driver version tested: *(update here after confirming)*
> CUPS printer name used: *(update here after setup)*
#### Chrome Print Settings (ZC10L)
| Setting | Value |
|---|---|
| Destination | Zebra ZC10L (CUPS name) |
| Paper size | 3.5 × 5.5 in (or as set in CUPS) |
| Margins | **None** |
| Background graphics | On |
| Pages | 1 (front only) |
#### CSS Layout
The ZC10L uses the `badge_3.5x5.5_pvc` layout. The PVC layout CSS is at:
`src/routes/events/[event_id]/(badges)/badges/print/badge_layout_zebra_zc10l_pvc.css`
This layout hides `.badge_back` in `@media print` — only the front face prints.
`@page { size: 3.5in 5.5in; margin: 0; }` is set in the CSS.
#### Known Behaviors / Watch-outs
- Chrome with **Default** margins: inserts URL/date headers, offsets badge — use **None**
- Chrome with **None** or **Minimum** margins: correct output
- Firefox: works correctly out of the box with this layout
- Physical card alignment: if the badge appears offset on the card, a CSS margin tweak may
be needed in the PVC layout file — note the offset and adjust `print_margin_cfg` once that
field is wired to the UI
- Font sizes: if name/affiliation text appears too small at physical scale, adjust via the
font size controls (+ / ) in the print controls panel; note the preferred values for this
event's template
#### Test Results (2026-03-19)
- Card feeds and prints without jam: ✅
- Single-sided PVC confirmed (back does not print): ✅
- Chrome margins None/Minimum: correct output ✅
- Firefox: correct output ✅
- QR code scannable from printed card: ✅
- Print tracking (`print_count` increment): ✅
- Font sizes / visual quality: tested; specific calibration pending client design direction
- Driver version: *(record here)*
- Physical offset needed: *(record if any)*
---
### Epson — Fan-Fold / Label Printer
**Status:** Not yet tested. Section to be filled in after testing.
Common Epson models used for fan-fold name badge stock: TM-T88 series, C3500, LX series.
Fan-fold stock is typically 4" × 3" or 4" × 6" paper labels.
#### CSS Layout
Fan-fold badges would use a layout sized to the specific label stock.
A new CSS layout file will need to be created per stock size if not already present.
Naming convention: `badge_layout_epson_[model]_[size].css`
#### Setup Notes
*(To be filled in after testing — cover: driver source, CUPS setup, paper size, Chrome settings)*
#### Known Behaviors
*(To be filled in after testing)*
---
## Print Tracking
The badge print page tracks print counts per badge:
- `print_count` — increments on each **Print Badge** button click
- `print_first_datetime` — timestamp of first print
- An amber "Printed N×" chip appears in the print page header after the first print
The reprint shortcut (trusted access + edit mode) does **not** increment the count.
Only the **Print Badge** button path increments the count. This is intentional — reprints
for alignment or quality checks should not inflate the print count.
---
## Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| White border around printed badge | Chrome Default margins | Change to None or Minimum |
| URL / date printed at top or bottom | Chrome Default margins | Change to None |
| Header image / stripe not printing | Background graphics disabled | Enable in print dialog |
| Badge appears on wrong-size output | Paper size mismatch | Set correct size in CUPS and/or print dialog |
| Card jams | Card stock misloaded | Re-seat cards per printer manual; check stock orientation |
| Badge content clipped | Layout overflow | Check font size — use control to reduce if needed |
| Second blank card ejected | Duplex triggered on PVC | Confirm `.badge_back { display: none }` in print CSS for this layout |

View File

@@ -0,0 +1,124 @@
# Guide — Aether Events: Onsite Runbook
This guide covers the human-centric logistics and "In the Heat of the Moment" support for onsite event operations.
---
## Badge Printing
Aether badge printing uses the browser's native `window.print()` — no special software or print
server needed.
### Kiosk Station Setup
- **Browser:** Use **Chrome (Chromium)** for all kiosk stations.
- **Settings:** Set Margins to **None**. Enable **Background Graphics**.
- **Mode:** Use normal browser sessions (not Incognito) to allow PWA caching.
### Printer Reference: Zebra ZC10L (PVC)
- **Stock:** 3.5" × 5.5" PVC cards.
- **Orientation:** Cards face-up, landscape in the hopper.
- **Single-Sided:** Only the front face prints; the back section is hidden via CSS.
### Printing Workflow
1. **Search:** Find the attendee by name or QR scan in the Badges module.
2. **Review:** Open the print page and confirm the layout looks correct.
3. **Print:** Click **Print Badge**. `print_count` increments automatically.
4. **Handoff:** Verify the card print quality before handing it to the attendee.
---
## Exhibitor Leads (Lead Retrieval)
Exhibitors use a PWA (Progressive Web App) to scan badges and capture leads.
### Exhibitor Support Workflow
1. **Booth Lookup:** Help the exhibitor find their booth in the Leads landing page.
2. **Sign-In:** Assist with the **Shared Passcode** or individual **Licensed User** login.
3. **App Install:** Encourage them to "Add to Home Screen" (iOS) or click the Install button (Android/Chrome) for offline stability.
4. **Scanning Demo:** Show them the **Rapid Scan** mode. Remind them that attendees must have `allow_tracking = true` on their record to be scanned.
### Managing Licenses
- License counts are managed in the **Manage** tab (Admin or Shared Passcode only).
- If an exhibitor needs more staff slots, update the `license_max` in the Exhibit record.
---
## Speaker Ready Room (SRR)
... (rest of the file) ...
The SRR is the central hub for content management and presenter support.
### SRR Practice Stations
Stations mirror the session room setup exactly:
- Same Mac laptop model and adapter/dongle configuration as the podiums.
- Projector and screen (where possible).
- Launcher running in **Native** mode — ensures verification matches the podium experience.
### Staffing Roles
| Role | Access Level | Typical Tasks |
|---|---|---|
| **OSIT Staff** | `trusted_access` | Manage devices, monitor via VNC, deep troubleshooting. |
| **Client Staff** | `authenticated_access` | Upload files, view session lists, assist presenters. |
| **Presenter** | `authenticated_access` | Self-upload via QR link (if enabled). |
### SRR Workflow — Day-of-Show
1. **Check-in:** Staff looks up the presenter's session in Presentation Management.
2. **Upload:** File is uploaded to the presenter/session record.
3. **Verification:** Staff opens the file on a practice station to confirm rendering.
4. **Launcher Sync:** File propagates to the podium. Use **Force Sync Location** in the Launcher config if immediate full-room caching is needed.
5. **Proceed:** Presenter walks to the room; the podium kiosk already has the file cached.
---
## Onsite Operation (Managing Parallel Rooms)
### SRR Overview Page
The Pres Mgmt overview (`/events/[id]/pres_mgmt`) is the "Command Center":
- Monitor file status per session.
- Filter by location and time block to stay ahead of active sessions.
### Per-Room Monitoring
- Use **VNC or RustDesk** to monitor all podium screens in real time from the SRR.
- Confirm "Native Sync" status chip in the bottom-left of the Launcher is green/idle before sessions start.
### Session Transitions
- **Timing:** Ideally, sessions show/hide based on `datetime_start`.
- **Manual Control:** In looser schedules, use Launcher controls to manually select the current session.
---
## Pre-Show Checklist
### 12 Weeks Before
- [ ] Event created with correct dates and timezone.
- [ ] `mod_pres_mgmt_json` configured for client needs.
- [ ] Locations (rooms) created and named.
- [ ] Sessions created, assigned to locations, and timed.
- [ ] Launcher devices (`event_device`) registered with correct codes.
- [ ] Device-to-location assignments confirmed.
### Day Before (SRR Setup)
- [ ] Mac laptops at podiums booted; Electron app running.
- [ ] Each podium confirms it loaded the correct room's Launcher.
- [ ] SRR practice stations confirmed (matching hardware).
- [ ] Run **Force Sync Location** on all podiums to pre-cache all day-1 content.
- [ ] VNC/RustDesk connections established to all podiums.
### Day of Show
- [ ] Confirm all session times are accurate before the first block.
- [ ] Monitor SRR queue and verify every file on a practice station.
- [ ] Check VNC wall to ensure all podiums are online and synced.
---
## Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Session not in Launcher | Datetime wrong or Location unassigned. | Verify session metadata in Pres Mgmt. |
| File uploaded but missing | Polling lag or attached at wrong level. | Wait 30s; check if file is at Session vs Presenter level. |
| File opens slowly | Not in native cache yet. | Check "Native Sync" chip; use Force Sync in config. |
| File won't open | Corrupt upload or missing Mac codec. | Test on SRR station; convert or re-upload. |
| Drifted schedule | Room timing shifted. | Use Launcher controls to manually select the active session. |
| `lock_config` resets changes | Remote config is forced. | Edit the master `mod_pres_mgmt_json` in Event Settings. |
| Move laptop to new room | Hardware reassignment. | Update `location_id` in `event_device` record; restart Electron. |

View File

@@ -0,0 +1,276 @@
# Aether UI — Design System Style Guidelines
> **Version:** 1.2 (2026-03-20)
> **Author:** One Sky IT / Scott Idem
> **Scope:** All Aether SvelteKit frontend components
> **Related:** `AE__UI_Component_Patterns.md`, `ae-firefly.css`, `documentation/AE__Components.md`
---
## 1. Design Philosophy
**"Shiny serenity, like a firefly."**
The Aether UI is calm, focused, and softly luminous. It must be immediately readable under conference-room lighting, at a glance, by presenters who are nervous and in a hurry. Staff in the Speaker Ready Room need scan-speed identity confirmation. Remote presenters uploading files from home need a clear, unambiguous interface that doesn't waste their time.
Core principles:
- **Identity first.** The user's first question is always *"Am I in the right place?"* Answer it with the hero card — name, time, room — before anything else is shown.
- **Progressive disclosure.** Admin fields (codes, IDs, passcodes) are hidden unless `edit_mode` is active.
- **Theme-aware always.** Zero hardcoded colors. Every background, border, and text color must respond to light/dark mode and the active theme via CSS variables.
- **Transitions, not pops.** Every interactive state change is smoothed with `transition-colors duration-200`.
- **Section 508 / WCAG 2.1 AA** compliance is non-negotiable. Contrast ratios, focus indicators, ARIA labels, and screen-reader regions are required everywhere.
---
## 2. Technical Stack Mandates (2026 Standard)
To maintain codebase health and performance, all new development must adhere to the following stack:
### 🚀 Svelte 5 Runes
- **Mandatory**: Use `$state`, `$derived`, and `$effect`.
- **Snippet pattern**: Use `{@render snippet()}` for reusable UI blocks within components.
- **Avoid**: Legacy `export let` (use `$props()`), `onMount` for reactive derived state (use `$derived` or `$effect`), and `$$slots` (use Snippets).
### 🎨 Tailwind 4 + Skeleton v4
- **Mandatory**: Use `preset-*` classes for interactive elements (e.g., `preset-tonal-primary`).
- **Forbidden**: Legacy Skeleton v3 `variant-*` classes.
- **Customization**: Use Tailwind 4 `@theme` blocks for project-wide overrides.
- **URLs**: Skeleton for Svelte for LLMs docs: https://www.skeleton.dev/llms-svelte.txt
### 🔣 Lucide Icons
- **Mandatory**: Use `@lucide/svelte` components (e.g., `<Calendar size="1em" />`).
- **Migration**: Replaced all FontAwesome `fas fa-*` icons in general modules.
- **🚨 Exception: IDAA Module**: The IDAA module **must** retain FontAwesome and Bootstrap classes. It integrates with Novi CMS which relies on these legacy standards. **Do not migrate IDAA icons.**
---
## 3. The AE_Firefly Theme
**App default since 2026-03-06.** Set in `ae_stores.ts` as `theme_name = 'AE_Firefly'`.
File: `src/ae-firefly.css` | Activated by: `data-theme="AE_Firefly"`
| Role | Palette Name | Hue | Use Case |
|---|---|---|---|
| **Primary** | Luminescent Teal | ~184° | Primary actions, date/time chips, focus rings, anchor links |
| **Secondary** | Warm Amber-Gold | ~90° | Secondary actions, copy/email buttons, soft highlights |
| **Tertiary** | Night-Sky Indigo | ~277° | Location/room chips, depth accents |
| **Surface** | Moonlit Slate | ~215233° | All backgrounds — off-white (light) → midnight slate (dark) |
| **Warning** | Amber semantic | n/a | Disabled/inactive records, "no results" states, code badges |
| **Success** | Green semantic | n/a | Active/complete states, file count badges |
| **Error** | Red semantic | n/a | Errors, disabled-presenter states |
### Contrast Guarantees
- Body text (`surface-950` on `surface-50`): > 15:1 in light mode ✓
- Primary buttons: ≥ 3:1 for interactive component threshold ✓
- Designed using OKLCH perceptual lightness — not HSL estimates
---
## 4. Color Token Rules
### ✅ Always Use Theme Tokens
**Backgrounds / surfaces:**
```
bg-surface-50-900 ← card faces (light: near-white, dark: deep slate)
bg-surface-100-900 ← inner sections, code blocks, secondary panels
bg-surface-50-950 ← page-level containers
bg-surface-200-800 ← skeleton pulse placeholders, dividers
```
**Borders:**
```
border-surface-200-800 ← standard card/panel borders
border-primary-500 ← focus rings (via focus-visible:ring)
border-warning-500 ← warning/caution interactive zones
border-surface-500/20 ← subtle section dividers
```
**Primary color accents (teal):**
```
bg-primary-500/10 ← tinted chip background (time chips)
text-primary-700 dark:text-primary-300 ← chip text with auto dark mode
hover:text-primary-500 ← link hover color
```
**Tertiary color accents (indigo):**
```
bg-tertiary-500/10 ← tinted chip background (room/location chips)
text-tertiary-700 dark:text-tertiary-300
```
**Skeleton presets:**
```
preset-tonal-warning ← "no results", disabled rows, code tag badges
preset-tonal-primary ← primary action buttons
preset-tonal-surface ← neutral/secondary actions, status badges
preset-tonal-success ← success states, file count badges
preset-tonal-error ← error/blocked states
preset-filled-*-500 ← solid-fill buttons (hover state for tonal)
```
**Warning/error semantic backgrounds:**
```
bg-warning-100 border border-warning-300 ← inline warning banners
bg-error-100 border border-error-300 ← inline error banners
```
---
### ❌ Never Use These
| Forbidden | Reason | Replace With |
|---|---|---|
| `bg-gray-*` | Fixed color, breaks dark mode | `bg-surface-*` tokens |
| `bg-neutral-*` | Same — fixed hue | `bg-surface-*` tokens |
| `bg-white` | Light-mode only | `bg-surface-50-900` |
| `text-gray-*` | Breaks dark mode | `opacity-60` on inherited text |
| `text-neutral-*` | Same | `opacity-*` |
| `border-gray-*` | Non-theme border | `border-surface-200-800` |
| `bg-yellow-*`, `text-yellow-*` | Bypasses warning semantic | `preset-tonal-warning` |
| `bg-red-*`, `text-red-*` | Bypasses error semantic | `bg-error-100` / `preset-tonal-error` |
| `bg-white dark:bg-gray-800` | Manual light/dark pair | Let theme tokens handle it — remove entirely |
| `text-gray-600 dark:text-gray-400` | Manual light/dark pair | `opacity-60` |
| `rounded-container-token` | Skeleton v3 class | `rounded-xl` |
| `variant-*` (v3) | Skeleton v3 class | `preset-*` (v4) |
| `preset-filled-surface-300-700` | v3 dual-shade notation | `bg-surface-200-800` or `bg-surface-100-900` |
| `preset-filled-surface-400-600` | v3 dual-shade notation | `bg-surface-100-900` |
| `overflow-x-scroll` | Forces scrollbar visible | `overflow-x-auto` |
---
## 5. Transitions & Animation
All interactive state changes must be smoothed — no hard pops.
| Element | Classes |
|---|---|
| Table row | `transition-colors duration-200` on `<tr>` |
| List item card | `transition-colors duration-200` on `<li>` |
| Link hover | `transition-colors duration-200` on `<a>` |
| Info chips | `transition-colors duration-200` on `<span>` |
| QR code toggle (size) | `transition-all duration-500` on `<img>` |
| Collapsible sections | Use Skeleton `Accordion` or CSS `transition-all` — don't add custom unless needed |
| Buttons | Handled automatically by Skeleton preset classes |
---
## 6. Loading / Skeleton States
When `liveQuery` data is still resolving, show pulse placeholders instead of nothing:
```svelte
<!-- Title placeholder -->
<div class="h-7 w-2/3 bg-surface-200-800 animate-pulse rounded"></div>
<!-- Chip/pill placeholder -->
<div class="h-5 w-1/2 bg-surface-200-800 animate-pulse rounded-full"></div>
<!-- Icon placeholder -->
<div class="h-5 w-5 bg-surface-200-800 animate-pulse rounded-full"></div>
```
Always wrap in `{#if $lq__obj}{...}{:else}...skeleton...{/if}`**never** show real content structure before data exists.
---
## 7. Dark Mode Rules
- **Never write `dark:` overrides for background or text colors.** The Firefly theme handles both modes through CSS variables. Writing `dark:bg-gray-800` or `dark:text-gray-400` bypasses the theme and breaks if the user switches themes.
- **Exception allowed:** `dark:text-primary-300` and `dark:text-tertiary-300` in info chips are intentional — they reference theme variables that gracefully degrade.
- **Exception allowed:** `dark:border-surface-700` in fine-grained border work when `border-surface-200-800` isn't strong enough.
---
## 8. Accessibility (Section 508 / WCAG 2.1 AA)
| Requirement | Implementation |
|---|---|
| Decorative icons | `aria-hidden="true"` on all icons |
| Icon-only buttons | `aria-label="..."` or `title="..."` + visible context |
| Async content regions | `role="status" aria-live="polite"` on loading/empty sections |
| Focus indicators | `focus-visible:ring-2 focus-visible:ring-primary-500` on custom interactive elements |
| Interactive dialogs | `aria-haspopup="dialog"` on trigger buttons |
| Form inputs | Visible `<label>` linked via `for` / `id`, or explicit `aria-label` |
| Color-only information | Always pair color coding with icon or text — never color alone |
| Minimum touch target | 44×44px effective hit area for all tap targets |
| Button label + icon | All buttons should include **both a Lucide icon and text label**. Icon-only is acceptable for space-constrained toolbar/header actions (with `title` attribute); text-only is acceptable when layout is extremely tight. The icon+text combination aids non-English-native users who may not read the label fluently. |
---
## 9. Debug Code — Remove Before Committing
These patterns are breakpoint debuggers added during development. **Never commit them:**
```html
<!-- Breakpoint border debugger — REMOVE before commit -->
sm:border-l-red-400 md:border-l-yellow-400 lg:border-l-gray-100
sm:dark:border-l-red-600 md:dark:border-l-yellow-600 lg:dark:border-l-gray-700
```
Also flag on code review:
- `console.log(...)` that isn't behind a `log_lvl` guard
- `border-dashed border-y-transparent border-r-transparent` left on production components
- `overflow-x-scroll` (should be `overflow-x-auto`)
---
## 10. QR Code Pattern — Critical Bug Prevention
The async QR generation code uses a boolean `true` as a loading placeholder:
```typescript
$events_sess.pres_mgmt.session_qr_url[$lq__obj.id] = true; // ← loading
// ... async ...
$events_sess.pres_mgmt.session_qr_url[$lq__obj.id] = result; // ← URL string
```
**Always gate display on `typeof ... === 'string'`**, not just truthy:
```svelte
<!-- ✅ Correct -->
{#if typeof $events_sess.pres_mgmt.session_qr_url?.[$lq__obj.id] === 'string'}
<!-- ❌ Wrong — renders broken <img src="true"> during load -->
{#if $events_sess.pres_mgmt.session_qr_url[$lq__obj.id]}
```
---
## 11. Tailwind Utility Usage Notes
- **`opacity-*` for muted text**: Use `opacity-60` (secondary) or `opacity-40` (tertiary/hint) instead of `text-gray-*`. This works in any theme and both modes.
- **`/` opacity modifier**: `bg-primary-500/10` is preferred over separate `opacity-10` — it targets only the background.
- **`text-sm leading-relaxed`**: Standard for body-level descriptive text in cards.
- **`tracking-wide uppercase`**: Use for section label/eyebrow text with `opacity-40`.
- **`whitespace-pre-wrap`**: Required for any `<pre>` or `<p>` displaying user-entered multi-line text (preserves breaks without horizontal overflow).
---
## 12. Known Issues & Workarounds
### `btn` + `preset-filled-*` resolves to transparent inside `card` components
**Symptom:** A button using `btn preset-filled-primary` (or any `preset-filled-*`) inside a `card` div renders with `background-color: transparent`, making it invisible against the card surface.
**Root cause:** The Skeleton v4 `btn` class sets a transparent background via a CSS variable chain. When nested inside a `card` element, the `preset-filled-*` class fails to win the specificity battle and the button appears invisible. This affects both light and dark mode.
**Workaround:** Skip `btn` and `preset-filled-*` entirely for buttons inside `card` elements. Use direct Tailwind token classes instead:
```svelte
<!-- ✅ Correct — works reliably inside cards -->
<button class="w-full rounded-xl py-5 font-bold flex items-center justify-center gap-2
bg-primary-500 text-white hover:brightness-110 transition-all cursor-pointer">
...
</button>
<!-- Secondary / cancel button inside a card -->
<button class="w-full rounded-lg py-3 text-sm font-medium flex items-center justify-center gap-2
border border-surface-500/40 hover:bg-surface-200-800 transition-colors cursor-pointer opacity-70">
...
</button>
<!-- ❌ Broken inside card — do not use -->
<button class="btn btn-xl preset-filled-primary">...</button>
```
**Scope:** `btn` + `preset-*` classes work correctly on standalone buttons (e.g. page headers, nav bars). The issue is specific to the `card` component context. If we migrate away from Skeleton `card`/`btn`, this issue goes away.

View File

@@ -0,0 +1,191 @@
# Aether Development SOP (Frontend)
> **Version:** 1.2 (2026-03-17)
> **Location:** documentation/GUIDE__Development.md
## 1. Verification (The "Test-First" Mandate)
**Rule:** No code is to be committed unless it has passed local verification.
### Required Checks
1. **Svelte Integrity:** `npx svelte-check`
- **Zero Tolerance:** If a task introduces even a single svelte-check warning or error, it must not be merged. Resolve all warnings before committing.
2. **Type Safety:** Ensure interfaces in `src/lib/types/ae_types.ts` match backend schemas.
3. **Reactivity Check:** Verify Svelte 5 runes (`$state`, `$derived`) are not creating race conditions with Dexie `liveQuery`.
4. **Build Check:** For major changes, run `npm run build:dev` to ensure no SSR or build-time failures.
5. **Integration Tests:** For changes to badge print, event layouts, or auth/store logic, run the relevant Playwright test file(s):
```bash
npx playwright test tests/event_badge_render.test.ts tests/event_badge_attendee_workflow.test.ts
```
Run the full suite with `npm run test:integration`. The badge tests (`event_badge_*.test.ts`) are the canonical integration test template.
## 2. Commit Policy
- **Atomic Commits:** One component or one logic fix per commit. Do not batch unrelated changes.
- **Safety:** Use `~/tmp/agents_trash` for file removal; never use `rm` directly on source files.
- **Secrets:** Never commit `.env`, API keys, or passwords.
## 3. Coordination (The Handshake)
You are not working in a vacuum. Coordinate with the Backend Agent via MCP tools.
### Mandatory Messaging Triggers
- **Data Requirements:** When a UI feature requires a new field or endpoint.
- **API Failures:** When a V3 endpoint returns unexpected data or errors.
- **Blocked:** If stuck in a loop or lacking information, use `ae_send_message` to ask the Backend Agent, or flag for Scott.
### Tools
- `ae_send_message` / `ae_inbox` — agent-to-agent messaging
- `ae_task_list` / `ae_task_add` / `ae_task_complete` — shared Kanban board
- `ae_log_work` — log activity to daily journal
## 4. Continuity (Before Starting Work)
1. Review `documentation/TODO__Agents.md` for active tasks.
2. Check `~/agents_sync/README.md` for fleet status and cross-agent tasks.
3. Describe your plan before making code changes across multiple files.
## 5. Key Documentation
| File | Purpose |
| --- | --- |
| `documentation/TODO__Agents.md` | Active task list — read first |
| `documentation/GUIDE__AE_API_V3_for_Frontend.md` | V3 API reference (authoritative) |
| `documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md` | Dexie + liveQuery patterns |
| `documentation/GEMINI__Svelte_and_Me.md` | Svelte 5 runes patterns |
| `documentation/AE__Architecture.md` | System architecture overview |
| `documentation/AE__Naming_Conventions.md` | Naming rules |
| `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` | Electron/Launcher reference |
| `tests/README.md` | Playwright test guide — shared helpers, hard-won lessons, demo IDs |
## 6. Inline Field Editing — `element_ae_obj_field_editor`
The standard component for single-field inline editing throughout the platform. Wraps a `PATCH /v3/crud/{obj_type}/{obj_id}` call behind a click-to-edit UI that respects `$ae_loc.edit_mode`.
```svelte
import Element_ae_obj_field_editor from '$lib/elements/element_ae_obj_field_editor.svelte';
```
### Basic usage — text field with custom display
Wrap the display content in the default snippet. The component renders it in view mode and swaps in the input on edit.
```svelte
<Element_ae_obj_field_editor
object_type={'event_session'}
object_id={session.id}
field_name={'name'}
field_type={'text'}
current_value={session.name}
on_success={() => events_func.load_ae_obj_id__event_session({ api_cfg: $ae_api, event_session_id: session.id })}
>
<h1 class="text-2xl font-bold">{session.name}</h1>
</Element_ae_obj_field_editor>
```
### Field types
| `field_type` | Input rendered |
| --- | --- |
| `text` (default) | `<input type="text">` — Enter key saves |
| `textarea` | `<textarea>` — use `textarea_rows` prop |
| `select` | `<select>` — pass `select_options={{ value: 'Label' }}` |
| `checkbox` | `<input type="checkbox">` — shows Enabled/Disabled |
| `tiptap` | TipTap rich-text editor |
| `date` | `<input type="date">` |
| `datetime` | `<input type="datetime-local">` |
| `number` | `<input type="number">` — Enter key saves |
### Select with nullable FK
```svelte
<Element_ae_obj_field_editor
object_type={'event_presenter'}
object_id={presenter.event_presenter_id}
field_name={'person_id'}
field_type={'select'}
current_value={presenter.person_id}
select_options={$slct.person_obj_kv}
allow_null={$ae_loc.administrator_access}
on_success={() => events_func.load_ae_obj_id__event_presenter({ api_cfg: $ae_api, event_presenter_id: presenter.event_presenter_id })}
>
{presenter.person_id ?? 'Not linked'}
</Element_ae_obj_field_editor>
```
### Key props
| Prop | Default | Notes |
| --- | --- | --- |
| `current_value` | — | Required. Bound with `$bindable` — liveQuery updates flow through automatically |
| `allow_null` | `false` | Shows a "Set Null" button in edit mode |
| `display_block` | `false` | Makes the wrapper `display: block` instead of `inline-block` |
| `on_success` | — | Callback after successful PATCH — use to trigger SWR cache refresh |
| `object_reload` | `true` | Triggers internal SWR reload after patch (in addition to `on_success`) |
### Behavior notes
- The edit trigger button is `visibility: hidden` (not `display: none`) when `$ae_loc.edit_mode` is off — this preserves layout so the page doesn't shift when edit mode toggles.
- Optimistic display: draft value is shown immediately after save; cleared once liveQuery confirms the update came back from the DB.
- `on_success` should always call the relevant `load_ae_obj_id__*` function to keep Dexie in sync.
---
## 7. URL Parameters
URL params consumed by the app. Params are read by layouts and applied on mount.
### Global (active on all routes — read by `src/routes/+layout.svelte`)
| Param | Values | Effect |
| --- | --- | --- |
| `iframe` | `true` / `false` | Enables iframe mode — hides the AE system bar for all users by default, suppresses sign-in/passcode UI |
| `show_menu` | `true` | Override: show the AE system bar inside an iframe. Intended for admins/trusted users who need menu access while testing an embed. |
| `hide_menu` | `true` | Explicitly hide the AE system bar outside of iframe mode (e.g. fullscreen kiosk pages). |
| `theme` | theme name | Applies a theme on load, then removes param from URL (no history entry) |
| `theme_mode` | `light` / `dark` | Applies theme mode on load, then removes param from URL |
### IDAA Module (`/idaa/` routes)
| Param | Values | Consumed by | Effect |
| --- | --- | --- | --- |
| `iframe` | `true` | `idaa/+layout.svelte` | Hides IDAA nav chrome |
| `uuid` | Novi UUID | `idaa/(idaa)/+layout.svelte` | Sets Novi UUID → triggers member auth lookup |
### IDAA Video Conferences (`/idaa/video_conferences`)
| Param | Values | Effect |
| --- | --- | --- |
| `uuid` | Novi UUID | Member identity for Jitsi JWT |
| `key` | site key string | Site auth key |
| `room` | room name | Jitsi room name |
| `moderator` | `true` | Grants moderator role + JWT, enables lobby and activity logging |
| `domain` | hostname | Jitsi server (default: `jitsi.dgrzone.com`) |
| `start_muted` | `true` | Start audio muted |
| `start_hidden` | `true` | Start video off |
| `incoming_msg_sound` | `true` | Disable incoming message sound |
| `participant_joined_sound` | `true` | Disable participant joined sound |
| `participant_left_sound` | `true` | Disable participant left sound |
| `reaction_sound` | `true` | Disable reaction sound |
| `raise_hand_sound` | `true` | Disable raise hand sound |
### Events Launcher (`/events/[id]/launcher`)
| Param | Values | Effect |
| --- | --- | --- |
| `session_id` | session ID | Pre-selects a session on load |
| `iframe` | `true` | Iframe mode flag |
| `launcher_menu` | show/hide | Show/hide launcher menu chrome |
| `launcher_header` | show/hide | Show/hide launcher header |
| `launcher_footer` | show/hide | Show/hide launcher footer |
### Events Sign-In (`/events/[id]/sign_in_out`)
| Param | Values | Effect |
| --- | --- | --- |
| `person_id` | person ID | Pre-fill attendee |
| `person_pass` | passphrase | Auto-authenticate attendee |
| `presentation_id` | ID | Pre-select presentation |
| `presenter_id` | ID | Pre-select presenter |
| `session_id` | ID | Pre-select session |
### Badges (`/events/[id]/badges/print_list`)
| Param | Values | Effect |
| --- | --- | --- |
| `printed_status` | filter value | Filter badge list by print status |
| `badge_type_code` | code string | Filter badge list by type |

View File

@@ -0,0 +1,491 @@
# Stability Patterns for liveQuery + Svelte 5
Dexie's `liveQuery` works well with Svelte 5 runes, but the combination requires a few stable patterns so queries don't get recreated unintentionally and components render correctly on a "cold start" (empty IndexedDB).
- Keep the observable instance stable: wrap `liveQuery` in a stable `$derived` so the observable isn't recreated on every render. Recreate the `liveQuery` only when explicit dependencies change (IDs, filters, or search keys).
```typescript
// stable derived wrapper — only recreated when `id` changes
let lq__obj = $derived(
(() => {
// capture the dependency(s) in a single stable closure
const id = url_id;
return liveQuery(async () => {
if (!id) return null;
console.log('[LQ] running for id=', id);
return await db.table.get(id);
});
})()
);
```
- Use `$derived.by(() => ...)` where available in your runes shim to build queries from a computed set of inputs (IDs list, search params). This preserves a stable observable instance while still reacting to explicit dependency changes.
- Avoid capturing mutable objects or inline expressions in the `liveQuery` closure. If the closure captures a changing reference, the query may be recreated unexpectedly or miss the first write.
## Common Gotchas and Fixes (Why things sometimes need multiple refreshes)
- Cold start (IDB empty) + non-blocking API writes: If you mount a component before data is written to IDB, `liveQuery` may run against an empty DB. The API write will populate IDB later, but sometimes a chain of dependent queries (e.g., presentations -> presenters) won't all rerun in the order you expect. The symptoms you described — session shows after one refresh, presenters only after a second — are consistent with either (a) queries recreated in the wrong order or (b) dependent store values being set only after some subscriptions are already created.
### Critical Discovery (2026-02-26): The "try_cache: false" Bug
**Symptom:** Nested data (e.g., Session → Presentations → Presenters) requires multiple manual refreshes to display on cold-start, even when using blocking loads.
**Root Cause:** Two interconnected issues in nested data loaders:
1. **Disabled caching in nested loads**: Parent loads were passing `try_cache: false` to child loads, meaning presentations and presenters were fetched from API but **never written to IndexedDB**.
2. **Missing microtask yields**: Even when caching was enabled, components would mount and subscribe to liveQuery *before* IndexedDB writes completed, causing race conditions.
**Example of the Bug:**
```typescript
// Session loader (BROKEN)
await db_save_ae_obj_li__ae_obj({ table: 'session', obj_li: [session] });
// Loads presentations but disables caching ❌
return await load_presentations({ ..., try_cache: false });
// Presentations fetch from API ✅
// Presentations SKIP IndexedDB write ❌
// Presenters SKIP IndexedDB write ❌
// Component mounts, liveQuery finds only session ❌
```
**The Fix:**
```typescript
// Session loader (FIXED)
await db_save_ae_obj_li__ae_obj({ table: 'session', obj_li: [session] });
await Promise.resolve(); // Yield to observers
// Preserve parent's try_cache value ✅
return await load_presentations({ ..., try_cache });
// Presentations fetch AND write to IDB ✅
await Promise.resolve(); // Yield to observers
// Presenters fetch AND write to IDB ✅
await Promise.resolve(); // Yield to observers
// Component mounts, liveQuery finds all data ✅
```
**Key Lessons:**
1. **Always preserve `try_cache` through nested loads** unless you have a specific reason to disable caching for that operation
2. **Add `await Promise.resolve()` after IndexedDB writes** to ensure Dexie's liveQuery observers fire before the function returns
3. **Block on nested loads with `await Promise.all()`** instead of fire-and-forget `forEach()` when the page needs complete data for first render
Fixes:
- Prefer the "Blocking Loader" when you can: `await` the API call in `+page.ts` so IDB is populated before Svelte mounts.
- If you cannot block, return an `initial_*` object from `+page.ts` and use it as an immediate fallback in your component so the UI renders from that payload while `liveQuery` takes over for subsequent updates. Example from Aether:
```svelte
<Comp_event_presentation_obj_li
lq__event_presentation_obj_li={$lq__event_presentation_obj_li ?? data.initial_session_obj?.event_presentation_li ?? []}
{log_lvl}
/>
```
- Ensure store IDs are set before subscribers that depend on them are created. Use `untrack()` (or an equivalent non-reactive assignment) to set IDs in stores during initialization so components subscribe to the correct IDs immediately:
```typescript
$effect(() => {
if (!ae_acct) return;
untrack(() => {
$events_slct.event_id = url_event_id;
$events_slct.event_session_id = url_session_id;
});
});
```
- When you have chains (presentations depend on session; presenters depend on presentation.person_id), make the dependent liveQuery explicitly wait for the upstream ID and log inside each query to verify the order — adding a small `await Promise.resolve()` or `await 0` inside the `liveQuery` is sometimes useful during debugging to ensure the JS microtask queue has a chance to settle after DB writes.
## Practical Patterns from Aether (Journals & Events & IDAA Recovery Meetings)
- Journals: The journaling pages use SWR-style background refreshes but reliably render because either (a) the page `+page.ts` blocks to populate DB for critical views, or (b) components accept `data.initial_*` fallback values until `liveQuery` emits. This hybrid approach avoids the "refresh twice" problem while keeping navigation snappy.
- Journals broad views: if text search is empty, let the local IDB result set drive the visible list. The API can revalidate the cache in the background, but it should not replace a broad "All" view with a limited slice that hides valid rows.
- Sessions / Presentations: The session page demonstrates several best practices:
- Use `url_*` constants (derived from `data.params`) so the `liveQuery` closure captures a stable value instead of the reactive store directly.
- Provide `initial_session_obj` from `+page.ts` as a first-draw fallback to child components.
- Use `$derived.by(() => liveQuery(...))` for presentation lists so the observable instance is stable across renders and recreated only when `event_session_id` or `search` changes.
- Search pages with persisted filters or saved query text should keep the auto-search trigger in a page-level `$effect`, but the duplicate guard should live inside the actual search executor. That preserves the first page-load search while blocking repeated identical reruns from localStorage-backed rerenders. In practice:
- derive a single `qry_key` from the search inputs
- debounce in the `$effect`
- compare `qry_key` against a `last_executed_key` inside `handle_search_refresh()`
- keep transient loading flags and trigger counters in session state when the value is only used to force a refresh, not as a persisted preference
Example (presentation list pattern):
```typescript
let lq__event_presentation_obj_li = $derived(
liveQuery(async () => {
if (!url_session_id) return [];
console.log('[LQ] Querying Presentations for Session:', url_session_id);
return await db_events.presentation.where('event_session_id').equals(url_session_id).sortBy('name');
})
);
```
## Debugging Checklist
- Add a small `console.log` inside each `liveQuery` closure to confirm when it runs and what `id` it sees.
- Verify that `+page.ts` either `await`s critical loads or returns `initial_*` payloads for first-render hydration.
- Confirm that dependent store values (selected IDs) are assigned before components subscribe — use `untrack` to prevent extra reactive cycles.
- If a search page stops auto-loading after a localStorage change, check whether the duplicate guard was placed in the `$effect` instead of the executor. Guarding too early can suppress the initial search; guard at execution time instead.
- If a broad Dexie-backed list shows fewer rows than a narrower filter, look for a limit or revalidation step overwriting the local IDB result set. Broad views should stay unbounded unless the user is actually narrowing by text.
- Ensure your `liveQuery` closures return quickly and do not throw; any exception inside the query can stop updates.
- If a dependent query appears stale, temporarily add `await 0` in the upstream query or an explicit `Promise.resolve()` after the IDB write to force the microtask queue to flush during debugging.
## Summary Recommendations
- Prefer blocking loads for primary views when first-render correctness matters.
- Use `initial_*` fallback data when non-blocking loads are required.
- Wrap `liveQuery` in stable `$derived` instances and only recreate when explicit inputs change.
- Use `untrack` to set selection IDs during initialization to avoid subscribe-order bugs.
- Add targeted logs inside `liveQuery` closures to diagnose ordering and subscription behavior.
These patterns are deliberately conservative — they trade minimal blocking or small explicit fallbacks for predictable first-render behaviour. The Aether app's Journals and Event session pages are working examples of these techniques in practice.
## Examples in this repository
The following files demonstrate stable `liveQuery` usage, `initial_*` fallbacks, and stable `$derived` wrappers used across the Aether app. Inspect these for copy/paste patterns and concrete implementations.
- Journals page (stable LQ + search patterns): [src/routes/journals/[journal_id]/+page.svelte](src/routes/journals/[journal_id]/+page.svelte#L51)
- Journals layout (blocking background loader): [src/routes/journals/[journal_id]/+layout.ts](src/routes/journals/[journal_id]/+layout.ts#L1)
- Session page with URL capture + initial fallback: [src/routes/events/[event_id]/(pres_mgmt)/session/[session_id]/+page.svelte](src/routes/events/[event_id]/(pres_mgmt)/session/[session_id]/+page.svelte#L41)
- Presentation management overview (stable derived + search): [src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte](src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte#L70)
- Event settings example (simple observable): [src/routes/events/[event_id]/settings/+page.svelte](src/routes/events/[event_id]/settings/+page.svelte#L51)
- Badge/detail pages (examples of nested LQ): [src/routes/events/[event_id]/(badges)/badges/+page.svelte](src/routes/events/[event_id]/(badges)/badges/+page.svelte#L66)
Refer to these files when you need concrete code examples to adopt the patterns described above.
## References
This document provides a guide to integrating Svelte (with a focus on Runes) and Dexie.js for building reactive web applications. It covers key concepts and best practices for managing reactivity between Svelte components and the Dexie.js database.
## Svelte 5 Migration Guide
Svelte 5 introduces "runes" as a new way to manage reactivity. This is a major change from previous versions of Svelte, and it's important to understand the breaking changes before migrating.
### Key Breaking Changes
- **`let` is no longer reactive:** In Svelte 4, any `let` variable declared in the top-level scope of a component was automatically reactive. In Svelte 5, you must explicitly declare reactive state using the `$state` rune.
- **`$:` is replaced by `$derived` and `$effect`:** The `$` label is no longer used for reactive statements. Instead, you should use the `$derived` rune for computed values and the `$effect` rune for side effects.
- **`export let` is replaced by `$props`:** Component props are now declared using the `$props` rune, which provides a more flexible and explicit way to define component APIs.
- **Event handling:** The `on:` directive is replaced by event attributes (e.g., `onclick`). Component events are now handled using callback props instead of `createEventDispatcher`.
- **Slots are replaced by snippets:** The `<slot>` element is replaced by the `{#snippet ...}` block, which provides a more powerful and flexible way to pass content to components.
For a complete list of breaking changes, refer to the [Svelte 5 migration guide](https://svelte.dev/docs/svelte/v5-migration-guide).
## Dexie.js Quick Reference
Dexie.js is a lightweight, minimalistic wrapper for IndexedDB that makes it easier to work with client-side databases.
### Key Classes and Methods
- **`Dexie`:** The main class for creating and managing IndexedDB databases.
- `new Dexie(databaseName)`: Creates a new database instance.
- `version(versionNumber).stores({ ... })`: Defines the database schema.
- **`Table`:** Represents an object store (table) in the database.
- `add(item)`: Adds a new item to the table.
- `put(item)`: Adds or updates an item in the table.
- `update(key, changes)`: Updates an existing item.
- `delete(key)`: Deletes an item by its primary key.
- `get(key)`: Retrieves an item by its primary key.
- `where(index)`: Starts a query using an index.
- `toArray()`: Retrieves all items from the table as an array.
- **`Collection`:** Represents a collection of items resulting from a query.
- `toArray()`: Retrieves all items in the collection as an array.
- `first()`: Retrieves the first item in the collection.
- `last()`: Retrieves the last item in the collection.
- `each(callback)`: Iterates over each item in the collection.
- `modify(changes)`: Updates all items in the collection.
- `delete()`: Deletes all items in the collection.
For a complete list of API methods, refer to the [Dexie.js API Reference](https://dexie.org/docs/API-Reference).
## Integrating Svelte Runes and Dexie.js
The combination of Svelte Runes and Dexie.js allows for the creation of highly reactive and efficient web applications.
### The `liveQuery` Function
Dexie.js provides a `liveQuery` function that returns an observable of the query result. This observable can be used to automatically update the UI whenever the data in the database changes.
### Using `liveQuery` with Svelte Runes
To use `liveQuery` with Svelte Runes, you can create a custom readable store that wraps the `liveQuery` observable. This store can then be used in your Svelte components to display and interact with the data.
**1. Create a `liveQuery` store:**
```typescript
import { liveQuery } from 'dexie';
import { readable } from 'svelte/store';
import { db } from './db'; // Your Dexie database instance
export function createLiveQueryStore<T>(query: () => T | Promise<T>) {
return readable<T | undefined>(undefined, (set) => {
const subscription = liveQuery(query).subscribe({
next: (result) => set(result),
error: (error) => console.error(error)
});
return () => subscription.unsubscribe();
});
}
```
**2. Use the `createLiveQueryStore` in your component:**
```html
<script>
import { createLiveQueryStore } from './stores';
import { db } from './db';
const friends = createLiveQueryStore(() => db.friends.toArray());
</script>
<ul>
{#if $friends} {#each $friends as friend}
<li>{friend.name}</li>
{/each} {/if}
</ul>
```
The `createLiveQueryStore` function creates a readable store that automatically updates whenever the data in the `friends` table changes. The `$friends` variable in the component will always contain the latest data from the database.
## SvelteKit Layout Hierarchy: Security and Execution Order
Understanding *when* SvelteKit code runs is critical for private-data modules like IDAA.
### Execution order on any navigation
```text
1. +layout.ts / +page.ts ← run FIRST — before any component mounts
also fired by SvelteKit link prefetch (on hover)
2. Parent +layout.svelte mounts → its $effect blocks run
3. Child +layout.svelte mounts → only if parent called {#render children?.()}
4. +page.svelte mounts → only if every parent in the chain rendered children
5. $effect blocks in all of the above run after mount
```
### The auth-gate consequence
A `{:else if authenticated} {@render children?.()}` block in a `+layout.svelte`
controls whether **everything below it** ever mounts. If the gate blocks rendering,
no child layout or page component instantiates — their `$effect` blocks, event
handlers, and liveQuery closures never run.
```svelte
<!-- (idaa)/+layout.svelte -->
{:else if $ae_loc.trusted_access || $idaa_loc.novi_verified}
{@render children?.()} ← children only mount if this branch runs
{:else}
<p>Access Denied</p> ← children never mount; their $effects never run
{/if}
```
**`$effect` blocks inside a child component cannot bypass a parent layout auth gate.**
They are already inside the gate. Adding redundant auth guards to `$effect` blocks
that only run after a parent has already verified access is unnecessary — and misleads
future readers into thinking the parent gate alone is not sufficient.
### Where the actual pre-gate risk lives: `+page.ts` / `+layout.ts`
Universal load functions run *before* components mount and *before* layout effects
execute. They also fire during SvelteKit link prefetch — triggered by the user
hovering a link, even if they never navigate. This makes them unsafe for private data:
```text
User hovers an /idaa/ link →
SvelteKit prefetch fires →
+page.ts runs (no layout has mounted yet, no auth gate has run) →
API call / IDB write happens for an unauthenticated user
```
**Rule for private modules (IDAA, Journals):** `+page.ts` and `+layout.ts` files must
not call any data load functions that write to IDB. Move all data loading to `$effect`
blocks in the corresponding `+page.svelte`, gated inside the auth-checked layout render.
The comments in every `+page.ts` under `src/routes/idaa/(idaa)/` explain this pattern.
### The `$effect` auth guards in IDAA `+page.svelte` files
These ARE still useful — but for a different reason than layout bypass:
```ts
// In bb/+page.svelte
$effect(() => {
if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;
posts_func.load_ae_obj_li__post(...)
});
```
Because `$ae_loc` is a Svelte 4 coarse-grained store, any unrelated write to it
(iframe height, SWR reload) re-triggers this `$effect`. The guard prevents a spurious
API call if `$idaa_loc.novi_verified` has been cleared between re-runs (e.g. TTL
expiry mid-session). It is a reactivity guard, not a layout-bypass guard.
---
## Page Load Strategies (Avoiding the "Waterfall")
When loading data for a primary page view (e.g., viewing a specific Journal, Session, or Person), you must choose a synchronization strategy to ensure the UI renders correctly on the first load.
### ❌ The "Fire & Forget" Anti-Pattern (AVOID)
Triggering a background load in `+page.ts` without `await` leads to race conditions.
1. `+page.svelte` mounts immediately.
2. `liveQuery` runs against an empty IndexedDB.
3. API data arrives later and writes to IndexedDB.
4. **Failure:** Svelte 5 + Dexie `liveQuery` may not automatically detect this first "cold start" update without a manual refresh.
### ✅ The "Blocking Loader" Pattern (RECOMMENDED)
Ensure the data is in IndexedDB **before** the component mounts.
1. In `+page.ts`, `await` the API load function.
2. In `+page.svelte`, the `liveQuery` will see the data immediately upon mount.
**Example (+page.ts):**
```typescript
export async function load({ params }) {
// Blocking await ensures IDB is populated
await journals_func.load_ae_obj_id__journal({
journal_id: params.journal_id,
try_cache: true
});
return {};
}
```
### ✅ The "Hydrate & Subscribe" Pattern (ADVANCED)
If you must use non-blocking loads, you must pass the initial data to the component to "hydrate" the state before the subscription takes over.
1. In `+page.ts`, `await` the load and **return the object**.
2. In `+page.svelte`, use the returned object as a fallback or initial state.
**Example (+page.svelte):**
```svelte
<script>
let { data } = $props();
let lq__obj = $derived(liveQuery(async () => db.table.get(id)));
</script>
<!-- Use fallback to handle the gap before liveQuery emits -->
{#if $lq__obj || data.initial_obj}
<View object={$lq__obj ?? data.initial_obj} />
{/if}
```
## The `untrack()` Reactive-Tracking Trap
`untrack()` is used inside `$effect` to read reactive values without registering them as tracked dependencies of that effect. This is correct for most "read-once" values (params, IDs) where you don't want the effect re-running on every change. But it has a silent failure mode: if a value you *need* to re-read is consumed inside `untrack()`, the effect becomes a one-shot and never retries when that value changes.
### Symptom
An effect runs once, reads a store value inside `untrack()`, takes an early-exit path (e.g. "no API key → skip"), and never retries — even after the store value is updated by a background process.
### Real Example (IDAA Novi Verification Bug — 2026-03-25)
The IDAA layout verifies Novi UUIDs. `site_cfg_json` (which contains the Novi API key) was read **inside** `untrack()`:
```typescript
// BUG: site_cfg_json read inside untrack → one-shot, never retries
$effect(() => {
if (!browser) return;
const uuid = data.url.searchParams.get('uuid'); // tracked ✓
untrack(() => {
const site_cfg_json = $ae_loc.site_cfg_json; // ← NOT tracked ✗
const api_key = site_cfg_json?.novi_idaa_api_key ?? null;
if (!api_key) return; // exits silently on first load with stale cache
verify_novi_uuid(uuid, api_key, ...);
});
});
```
On first load, the Dexie cache returned a stale `site_cfg_json` missing the API key. The effect exited early. The background refresh later updated `$ae_loc.site_cfg_json`, but because `site_cfg_json` was consumed inside `untrack()`, the effect never re-ran.
**Fix:** Move the dependency read **outside** `untrack()`:
```typescript
// FIX: site_cfg_json tracked outside untrack → effect re-runs when it changes
$effect(() => {
if (!browser) return;
const uuid = data.url.searchParams.get('uuid'); // tracked ✓
const site_cfg_json = $ae_loc.site_cfg_json; // tracked ✓ — effect re-runs on change
untrack(() => {
// Guard: already verified for this UUID — don't repeat the round-trip
if ($idaa_loc.novi_verified && $idaa_loc.novi_uuid === uuid) return;
const api_key = site_cfg_json?.novi_idaa_api_key ?? null;
if (!api_key) return;
verify_novi_uuid(uuid, api_key, ...);
});
});
```
The guard inside `untrack()` is important: without it, every unrelated change to `$ae_loc` would re-trigger verification.
### Rule of Thumb
Before wrapping a store read in `untrack()`, ask: **"Do I need this effect to re-run if this value changes?"**
- If yes → read it **outside** `untrack()`, and add a guard inside to prevent redundant work.
- If no → `untrack()` is correct.
---
## Svelte 5 Binding Pitfalls
### 1. `props_invalid_value` (The "Expression Binding" Error)
Svelte 5's `bind:` directive is more restrictive than previous versions. You can only bind to a simple **Identifier** or **MemberExpression**.
**❌ Invalid Pattern (Causes Compile Error):**
Attempting to normalize a value *inside* the binding will fail.
```svelte
<!-- Error: Can only bind to an Identifier or MemberExpression -->
<Launcher_menu bind:slct__event_session_id={$events_slct.event_session_id || null} />
```
**✅ Correct Pattern:**
Ensure the source value is already normalized before binding, or use a reactive effect to handle the fallback.
```typescript
// Normalize in an effect or derivation
$effect(() => {
if ($events_slct.event_session_id === undefined) {
$events_slct.event_session_id = null;
}
});
```
```svelte
<!-- Bind directly to the normalized property -->
<Launcher_menu bind:slct__event_session_id={$events_slct.event_session_id} />
```
---
## Safe Data Processing for IndexedDB Sorting
When preparing data for IndexedDB, especially when creating composite sort keys, it is critical to handle `null` or `undefined` values safely to prevent runtime crashes that can interrupt the data synchronization process.
### 1. Safe String Padding
Attempting to call `.toString()` or `.padStart()` on a `null` or `undefined` value will throw a `TypeError`. This is a common pitfall when processing optional fields like `sort` or `group`.
**Bad Pattern (Crash Risk):**
```typescript
// Crashes if obj.sort is null or undefined
obj.tmp_sort_1 = `${obj.sort.toString().padStart(3, '0')}`;
obj.tmp_sort_2 = `${obj.sort?.toString().padStart(3, '0') ?? ''}`; // Still risky if chaining is misunderstood
```
**Good Pattern (Safe):**
```typescript
// Safely handle null/undefined by defaulting to 0 or an empty string BEFORE string manipulation
const sort_val = (obj.sort ?? 0).toString().padStart(3, '0');
```
### 2. Correct Sorting with Dexie
Dexie's `sortBy()` method returns a new array sorted by the specified key. It **ignores** previous `reverse()` calls on the collection. To achieve a descending sort, you must sort first and then reverse the resulting array.
**Incorrect (Ascending Sort Result):**
```typescript
// .reverse() is ignored by .sortBy()
let results = await db.table.where('id').equals(id).reverse().sortBy('sort_key');
```
**Correct (Descending Sort Result):**
```typescript
// Sort ascending first, then reverse the array
let results = await db.table.where('id').equals(id).sortBy('sort_key');
return results.reverse();
```
## References
* https://dexie.org/llms.txt - Dexie.js and Dexie Cloud — LLM Guide and Documentation Summary

View File

@@ -0,0 +1,61 @@
# Aether Journals: Configuration & Settings Map
This document tracks all available settings across the three levels of the Journals module.
## 1. Module Level (Global)
* **Scope:** Applied across the entire journals application for the current site/user.
* **Storage:** Browser Local Storage (via `journals_loc` persisted store).
* **UI Location:** Journals Landing Page -> Settings Icon (Top Right).
| Setting | Type | Description | Save Type |
| :--- | :--- | :--- | :--- |
| `datetime_format` | enum | Preferred display for date/time strings. | Manual (Save Changes) |
| `time_format` | enum | Preferred display for time-only strings. | Manual (Save Changes) |
| `entry.auto_save` | boolean | If true, entry edits are debounced and saved to DB. | Manual (Save Changes) |
| `show_id_random` | boolean | Display technical UUIDs in metadata footers. | Manual (Save Changes) |
| `enable_session_passcode_cache`| boolean | If true, private passcodes are held in session store. | Manual (Save Changes) |
## 2. Journal Level (Specific Journal)
* **Scope:** Applied to a specific journal object and its metadata.
* **Storage:** Remote MariaDB (via `update_ae_obj__journal`).
* **UI Location:** Journal View -> Menu -> Edit Journal.
| Setting | Type | Description | Save Type |
| :--- | :--- | :--- | :--- |
| `name` | string | Display name of the journal. | Manual (Save Changes) |
| `description` | markdown | Detailed purpose/notes for the journal. | Manual (Save Changes) |
| `type_code` | enum | Category of journal (Diary, Log, etc.). | Manual (Save Changes) |
| `group` | string | Sorting group for the journal list. | Manual (Save Changes) |
| `sort` | integer | Manual sort order weight. | Manual (Save Changes) |
| `priority` | boolean | Flag for "Starred" or "Pinned" status. | Manual (Save Changes) |
| `enable` | boolean | Global activation flag for the journal. | Manual (Save Changes) |
| `hide` | boolean | If true, journal is hidden from standard views. | Manual (Save Changes) |
| `passcode` | string | Module-level encryption passcode. | Manual (Save Changes) |
| `private_passcode`| string | User-specific secondary encryption secret. | Manual (Save Changes) |
| `cfg_json.*` | JSON | UI/UX overrides (colors, default viewers, etc.). | Manual (Save Changes) |
## 3. Journal Entry Level (Specific Entry)
* **Scope:** Applied to an individual entry within a journal.
* **Storage:** Remote MariaDB (via `update_ae_obj__journal_entry`).
* **UI Location:** Entry View -> Settings Button.
| Setting | Type | Description | Save Type |
| :--- | :--- | :--- | :--- |
| `name` | string | Entry title. | Auto-Save (if enabled) |
| `content` | markdown | Main text body. | Auto-Save (if enabled) |
| `category_code` | enum | User-defined category for the entry. | Auto-Save / Manual |
| `tags` | string | Comma-separated list of tags. | Auto-Save / Manual |
| `priority` | boolean | If true, entry is pinned or highlighted. | Manual (Done) |
| `enable` | boolean | Activation flag for the entry. | Manual (Done) |
| `hide` | boolean | If true, entry is hidden from standard lists. | Manual (Done) |
| `sort` | integer | Manual sort order weight. | Manual (Done) |
| `archive_on` | datetime | Scheduled date for automatic archiving. | Manual (Done) |
| `private` | boolean | Trigger for E2EE (Encryption). | Manual (Done) |
| `alert` | boolean | Trigger for visual "Alert" state. | Manual (Done) |
| `group` | string | Grouping key for the list view. | Manual (JSON only) |
## 📐 Data Normalization Rules
To prevent infinite reactivity loops and trivial save cycles, the following normalizations are applied before comparison:
1. **Strings:** Trimmed and `null` treated as `""`.
2. **Booleans:** Forced to `true/false` (no nulls).
3. **JSON:** Deep stringification comparison (`JSON.stringify`).

View File

@@ -0,0 +1,365 @@
# MODULE: Aether Events — Badge Templates
**Module Path:** `src/routes/events/[event_id]/(badges)/templates/`
**API Module:** `src/lib/ae_events/ae_events__event_badge_template.ts`
**Database Table:** `event_badge_template`
**Last Updated:** 2026-03-02
---
## Overview
Badge templates define the visual and structural configuration for printing event badges.
Each template applies to one category of badge (e.g., general attendees, workshops,
exhibitors). An event typically has 13 templates.
**Key principle:** One template per badge stock type/audience. Do not use flags on a
single template to drive multiple layouts — create a separate template instead.
**Common template sets:**
- **General Attendees** — main conference badge (most attendees)
- **Workshops / Pre-conference** — alternate header, possibly different badge type list
- **Exhibitors** — distinct footer stripe colors, exhibitor-specific badge types
Each template uses the same physical badge stock and printer configuration.
---
## DB Field Reference
### Core Identity
| Field | Type | Notes |
|---|---|---|
| `id` | int | Internal PK |
| `id_random` | str | External-facing ID (AE Triple ID pattern) |
| `event_id` | str | Parent event |
| `name` | str | Template display name |
| `description` | str | Optional description |
### Image Assets
| Field | Type | Notes |
|---|---|---|
| `logo_path` | str (URL) | Org logo — fallback when no header image |
| `logo_filename` | str | **Deprecated** — redundant with logo_path; do not use |
| `header_path` | str (URL) | Front-of-badge header image — primary branding |
| `secondary_header_path` | str (URL) | Back-of-badge header image (falls back to header_path) |
| `footer_path` | str (URL) | Optional footer image — rarely used |
| `header_row_1` | str/HTML | Text fallback line 1 when no header image |
| `header_row_2` | str/HTML | Text fallback line 2 |
| `footer_title`, `footer_left`, `footer_right` | str | Legacy Flask-era fields — not used |
| `header_background`, `footer_background` | str | Legacy — not used; do not add to new templates |
### Network / WiFi
| Field | Type | Notes |
|---|---|---|
| `wireless_ssid` | str | WiFi network name — displayed on badge back |
| `wireless_password` | str | WiFi password — displayed on badge back |
### QR Code Behavior
| Field | Type | Notes |
|---|---|---|
| `show_qr_front` | bool (0/1) | Show attendee QR code on front of badge |
| `show_qr_back` | bool (0/1) | Show attendee QR code (+ ID text) on back of badge |
### Badge Type List
| Field | Type | Notes |
|---|---|---|
| `badge_type_list` | JSON string | List of `{code, name}` objects for this template |
**Format:**
```json
[
{"code": "current_member", "name": "Member"},
{"code": "guest", "name": "Guest"},
{"code": "staff", "name": "Staff"},
{"code": "test", "name": "Test"}
]
```
The badge type footer stripe color is driven by CSS rules targeting the `code` value
as a class on the footer element. Each event/template defines its own list — there is
no global default. The component derives this list from the template at render time.
### Ticket Definitions
| Field | Type | Notes |
|---|---|---|
| `ticket_list` | JSON string | List of `{num, code, name}` for this template's tickets |
| `ticket_1_text` `ticket_8_text` | HTML | Ticket block HTML printed on badge back |
**ticket_list format:**
```json
[
{"num": 1, "code": "foundation_reception", "name": "Foundation Reception"},
{"num": 2, "code": "volunteer_reception", "name": "Volunteer Reception"}
]
```
The `ticket_N_code` field on the badge object references a ticket by its `code`. The
corresponding `ticket_N_text` on the template provides the HTML rendered on the badge.
### Print Layout / Styling
| Field | Type | Notes |
|---|---|---|
| `layout` | str | Layout code — see Layout Codes below |
| `style_filename` | str | CSS filename for locally-served stylesheets |
| `style_href` | str (URL) | **Preferred** — external URL for custom CSS |
| `script_src` | str (URL) | **Do not use** — Flask-era arbitrary script injection |
### Access Control
| Field | Type | Notes |
|---|---|---|
| `passcode` | str | Shared passcode for template management access |
| `enable` | bool | Standard AE enable flag |
| `hide` | bool | Standard AE hide flag |
| `priority`, `sort`, `group` | int/str | Standard AE sort fields |
| `notes` | str | Internal notes |
### New Field (pending backend addition)
| Field | Type | Notes |
|---|---|---|
| `duplex` | bool | **Planned** — when `false`, back section is hidden from print (`@media print`) |
The `duplex` field controls whether the back-of-badge section renders during printing.
When `false` (single-sided), `badge_back` gets `print:hidden` applied so only the front
prints. The back section still displays on screen for configuration reference.
The first event using this system (Axonius, NYC, mid-April 2026) uses single-sided PVC
cards on a Zebra ZC10L — `duplex` will be `false` for that event's templates.
---
## External CSS Approach
### Why External
Badge templates may need visual adjustments mid-event (e.g., a color correction, a
footer fix) without deploying a new SvelteKit build. Hosting the CSS at an external URL
allows changes to take effect on next page load without any deployment.
### How It Works
The `style_href` field contains a full URL to a CSS file hosted on the static server
(e.g., `https://static.oneskyit.com/c/ISHLT/css/badges_custom_ishlt.css`).
The print page (`print/+page.svelte`) or the badge view should conditionally add a
`<link>` element via `<svelte:head>` when `style_href` is populated:
```svelte
<svelte:head>
{#if $lq__event_badge_template_obj?.style_href}
<link
rel="stylesheet"
href={$lq__event_badge_template_obj.style_href}
/>
{/if}
</svelte:head>
```
This is not yet implemented — tracked as a pending Phase 1 item.
### CSS Scope
External badge CSS should scope all rules under `.badge_front`, `.badge_back`, etc.
to avoid bleeding into the rest of the app. The classes used in
`ae_comp__badge_obj_view.svelte` are the canonical hook points:
- `.badge_front` — entire front card
- `.badge_back` — entire back card
- `.badge_header` — front header area
- `.badge_body` — front content area (name, title, affiliations, location)
- `.badge_footer` — front footer stripe
- `.badge_back_header` — back header area
- `.badge_back_content` — back content area
- `.badge_footer_center.<code>` — footer text per badge type code (for color stripes)
### layout field
The `layout` field encodes physical badge stock dimensions. Standard codes to use:
| Code | Dimensions | CSS file | Description |
| --- | --- | --- | --- |
| `badge_4x5_fanfold` | 4" × 5" (101.6 × 127mm) | `badge_layout_epson_4x5_fanfold.css` | Epson ColorWorks C3500 / ExpoBadge fanfold — preferred for general conference use (ISHLT, demos) |
| `badge_3.5x5.5_pvc` | 3.5" × 5.5" (88.9 × 139.7mm) | `badge_layout_zebra_zc10l_pvc.css` | PVC card, Zebra ZC10L — single-sided, set `duplex=0` |
| `badge_4x6_fanfold` | 4" × 6" (101.6 × 152.4mm) | `badge_layout_epson_4x6_fanfold.css` | Single-sided fanfold; Axonius Adapt 2026 (June 2026). Lanyard hole: 5/8in × 1/8in, centered, 1/4in from top. |
| `badge_4x6_fanfold_tickets` | 4" × 6" + tear-offs | *(pending)* | Fanfold with ticket stubs |
Layout CSS files live in `src/lib/ae_events/badges/css/` and are imported by
`ae_comp__badge_obj_view.svelte`. Rules are scoped under `[data-layout="..."]` on the
wrapper so multiple layouts can coexist in the bundle without conflict.
`@page` paper size rules are injected per-layout from `print/+page.svelte <svelte:head>`
(attribute selectors cannot scope `@page` rules, so they're handled dynamically).
---
## Template-Derived Features (component behavior)
### badge_type_list → badge type select
The badge type dropdown shown when editing a badge is derived from the template's
`badge_type_list` JSON, not a hardcoded list. This was a bug (fixed 2026-03-02).
See `ae_comp__badge_obj_view.svelte``badge_type_code_li` is now `$derived.by()`.
### "Info section" flags (exhibitor_info, presenter_info, etc.)
These flags (`exhibitor_info`, `presenter_info`, `staff_info`, `vip_info`, `vote_info`)
**do not exist as DB columns**. They appeared as placeholder `{#if}` blocks in the
badge view component from Flask-era development and were never implemented.
The correct approach is **one template per badge audience** — an Exhibitor template will
have exhibitor-specific `badge_type_list`, header images, and CSS. No flags needed.
The dead `{#if $lq__event_badge_template_obj.exhibitor_info}` blocks in
`ae_comp__badge_obj_view.svelte` should be removed in a future cleanup pass.
---
## Properties Saved to IDB (Dexie)
The `properties_to_save` array in `ae_events__event_badge_template.ts` controls what
gets cached locally. Current state — fields **NOT** in properties_to_save that exist
in DB and may be needed:
- `style_href` — needed once external CSS is wired via `<svelte:head>`
- `passcode` — not needed client-side
- `footer_title`, `footer_left`, `footer_right` — not needed (legacy)
- `header_background`, `footer_background` — not needed (legacy)
- `script_src` — do not add; this field should not be used
- `duplex`**add when backend adds the field**
---
## Standard Template Setup (per event)
### 1. General Attendees template
- `header_path`: event-specific conference header image
- `secondary_header_path`: back-of-badge header (often same or related image)
- `wireless_ssid` + `wireless_password`: venue WiFi
- `show_qr_back`: `1` (back QR is standard for most events)
- `show_qr_front`: `0` (usually off for front)
- `badge_type_list`: full list of member/guest/staff/test types
- `ticket_list` + `ticket_N_text`: event-specific tickets if applicable
- `style_href`: client-specific CSS URL
- `layout`: appropriate layout code
- `duplex`: `1` (or `0` for single-sided events like Axonius 2026)
### 2. Workshop / Pre-conference template
- Same as above but with workshop-specific header images
- `badge_type_list`: reduced list (workshop-relevant types only)
- `ticket_list`: may be empty `[]`
- `duplex`: match main template
### 3. Exhibitor template
- Exhibitor-specific header images
- `badge_type_list`: exhibitor-only types (`ex_all`, `ex_booth`, `guest`, `staff`, `test`)
- `ticket_list`: `[]` (exhibitors typically don't have event tickets)
- `show_qr_back`: may be `0` (exhibitors scan others, they don't need their own QR prominent)
- `wireless_ssid` + `wireless_password`: same venue WiFi
---
## Print Layout Architecture
### How the print CSS works
The print page (`print/+page.svelte`) injects `<style>` blocks into `<svelte:head>` that
take effect only in `@media print`. Multiple layers of the SvelteKit layout chain must
be neutered to get a clean print surface.
**`#ae_main_content` — cannot dissolve, must passthrough:**
`#ae_main_content` has `overflow: auto` (it is the events layout scroll container). CSS
spec prohibits `display: contents` from overriding elements with overflow clipping —
Firefox enforces this strictly, Chrome is lenient. Workaround: strip all its visual/layout
effects with explicit `display: block; overflow: visible; position: static; width: 100%`.
**Wrappers dissolved via `display: contents` (safe — no overflow constraints):**
| Selector | Source | Why dissolved |
|---|---|---|
| `.main_content` | `events/+layout.svelte` | `pb-48`, `pt-20+`, `grow` |
| `#badge_render_area` | `print/+page.svelte` | Screen-only right-padding offset for controls panel |
**App chrome hidden via `print:hidden`:**
- `nav.submenu` (events layout nav bar)
- `footer.footer` (events layout footer)
- Scroll-to-top / scroll-to-bottom button div
- Kiosk header (`<header>` in print page)
- Controls panel (`<div>` fixed right in print page)
- Debug info section (edit mode only)
- Root layout: offline banner, session expired banner, hydration overlay, sys/debug menus
**Badge centering — `position: fixed`:**
`.event_badge_wrapper` uses `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%)`.
In print, `position: fixed` anchors relative to the `@page` content area, bypassing the entire
ancestor hierarchy (no containing-block height dependency, no overflow-clip interference).
**Future per-template margins:** `print_margin_cfg` is already parsed from `cfg_json`
in `print/+page.svelte`. A dynamic `@page { margin: ... }` injection can be built from
that value when a UI for it exists.
---
### Cross-browser print behavior — IMPORTANT
Verified 2026-03-12 by comparing print-to-PDF output from both browsers across multiple
print dialog settings.
#### `@page { size }` — paper size
| Browser | Save to PDF | Physical printer |
|---|---|---|
| **Firefox** | Paper size locked — cannot change in dialog; CSS `@page { size }` used ✅ | Can select paper size in dialog |
| **Chrome/Chromium** | Paper size locked — cannot change in dialog; uses system default (letter, A4, etc.) ❌ | Can select paper size under "More settings" |
Chrome intentionally does not honor `@page { size }` for Save as PDF. It uses the system
default paper size. This is a Chrome design decision, not a bug in our code.
For actual printing to Epson/Zebra hardware: the printer driver controls paper size from
the loaded badge stock. CSS `@page { size }` is advisory only. Real badge printing is
unaffected by Chrome's behavior.
Use Firefox for accurate print-to-PDF proofing — it produces a correctly-sized PDF that
matches the badge stock dimensions exactly.
#### Margins — Chrome "Default" causes layout problems
| Chrome margin setting | Result |
|---|---|
| **Default** | ❌ Adds URL, date, and page-number headers/footers into the printable area. These eat into the space that `position: fixed; top: 50%` references, making the badge appear off-center or clipped against the footer. |
| **None** | ✅ Correct — badge centered cleanly |
| **Minimum** | ✅ Correct — small margins, badge still centered |
| **Custom (reasonable values)** | ✅ Correct |
The badge content itself is **not** distorted. Verified: Chrome "None" margins on an A4
page produces the badge perfectly horizontally centered (page center 297.5 pts, badge
content center 297.5 pts). The CSS centering logic is correct.
**Staff guidance for Chrome:**
- Set **Margins → None** (or Minimum) in Chrome's print dialog.
- Optionally set paper size to match badge stock under "More settings" when printing to PDF.
- For physical printer: select correct paper size under "More settings".
Firefox users can use "Save to PDF" directly — it just works.
---
## Related Files
| File | Role |
|---|---|
| `ae_events__event_badge_template.ts` | API + IDB functions; `properties_to_save` |
| `db_events.ts` | Dexie schema for `badge_template` table |
| `templates/+page.svelte` | Template list + create/edit/delete UI |
| `templates/ae_comp__badge_template_form.svelte` | Template create/edit form |
| `[badge_id]/ae_comp__badge_obj_view.svelte` | Badge render — consumes template data |
| `[badge_id]/print/+page.svelte` | Print page — loads template, hosts `<svelte:head>` CSS |
| `documentation/MODULE__AE_Events_Badges.md` | Badge object reference |
---
## Pending / TODO
- [x] Wire `style_href` via `<svelte:head>` in print page — done in `print/+page.svelte`; also in `properties_to_save`. (2026-03-18 verified)
- [x] Add `duplex` to `properties_to_save` — done. (2026-03-18 verified)
- [x] Add `duplex`-driven suppression to `badge_back` section — done in `ae_comp__badge_obj_view.svelte`; `show_badge_back` derived from `duplex` field.
- [x] `badge_4x6_fanfold` layout CSS created (`badge_layout_epson_4x6_fanfold.css`), imported in badge component, `@page 4in 6in` wired in print page. (2026-05-15)
- [ ] `badge_4x5_fanfold` layout CSS exists but is stale (not used in 2+ years) — review against actual hardware before next use.
- [ ] Remove dead `exhibitor_info` / `presenter_info` / `staff_info` / `vip_info` / `vote_info` `{#if}` blocks from `ae_comp__badge_obj_view.svelte` (if they were carried over from v1)
- [ ] Improve `ae_comp__badge_template_form.svelte` to edit all relevant fields (currently minimal)

View File

@@ -0,0 +1,106 @@
# Aether Events — Badges
The Badges module manages event attendee records and their physical badge configurations. It supports multi-source imports, field protection for onsite edits, and multi-tier access control for self-service review.
---
## Data Model & Hierarchy
### Core Objects
- **Event Badge** (`event_badge`): The attendee record containing name, title, affiliations, and tracking flags.
- **Badge Template** (`event_badge_template`): The visual and structural configuration for printing (branding, layout, QR placement).
### Relationships
- **Badge → Event:** Many-to-one.
- **Badge → Template:** Many-to-one (via `event_badge_template_id`).
- **Badge → Person:** Optional link to core Aether Person record for unified profiles.
---
## Critical Design Pattern: Override Fields
### Purpose
The `*_override` fields pattern (established in 2018) protects data from being overwritten during scheduled cron syncs from external systems (iMIS, Novi, etc.). This ensures that staff corrections or attendee self-updates persist across multiple sync cycles.
### How It Works
1. **Import:** External systems populate **REGULAR** fields only.
2. **Display Logic:** The UI displays the `*_override` field if it has a value; otherwise, it falls back to the regular field.
3. **HTML Rendering:** Certain display fields (Name, Title, Affiliations, Location) support HTML markup for rich text formatting (bold, italics, line breaks) on the physical badge.
### Standard Override Pairs
| Regular Field | Override Field | Editable By | HTML? |
|---|---|---|---|
| `full_name` | `full_name_override` | Staff, Attendee | ✅ |
| `professional_title` | `professional_title_override` | Staff, Attendee | ✅ |
| `affiliations` | `affiliations_override` | Staff, Attendee | ✅ |
| `location` | `location_override` | Staff, Attendee | ✅ |
| `email` | `email_override` | Staff Only | No |
| `badge_type` | `badge_type_override` | Staff Only | No |
---
## External System Integration
Aether acts as a **Pull-Only** consumer for registration data. It does not push changes back to external systems, maintaining them as the source of truth for base registration while Aether handles the "Onsite Truth."
### Supported Sources
- **iMIS**, **Novi AMS**, **Impexium** (Associations)
- **Zoom**, **Cvent** (Registrations)
- **Confex** (Abstracts/Presenters)
- **Custom CSV/Excel**
---
## Access Control & Permissions
| Level | Access |
|---|---|
| **Authenticated** | View own badge, limited self-edit (overrides only). |
| **Trusted** | Search all badges, view all, reprint existing badges. |
| **Administrator** | Full CRUD, bulk operations, override any field. |
| **Manager** | All Admin + Event/Template configuration. |
### Attendee Self-Service (`/review`)
Attendees can access their own record via a passcode-gated link (typically `?passcode=...`). This allows them to verify their info and provide preferred name/title overrides before printing.
---
## Search & Filter Capabilities
- **Fulltext Search:** Matches against a consolidated `default_qry_str` (Name, email, IDs).
- **Multi-Word Logic:** Queries like "Scott Idem" are split and treated as `LIKE %Scott% AND LIKE %Idem%`.
- **QR Scan Search:** Scanning an attendee's QR code (from a confirmation email or old badge) immediately jumps to their record.
- **Advanced Filters:** Filter by Badge Type, Printed Status, or Affiliations (Staff only).
---
## Print Tracking
Aether tracks the lifecycle of every physical badge to prevent unauthorized reprints and monitor kiosk activity.
| Field | Purpose |
|---|---|
| `print_count` | Increments on every "Print Badge" action. |
| `print_first_datetime` | Timestamp of the very first print. |
| `print_last_datetime` | Timestamp of the most recent print. |
> **Operational Note:** Reprints triggered via the Edit Mode shortcut do not increment the count; only the formal "Print Badge" workflow does.
---
## Route Map (Badges)
| URL | Purpose |
|---|---|
| `/events/[id]/badges` | Main search and attendee list. |
| `/events/[id]/badges/templates` | Badge template management. |
| `/events/[id]/badges/[id]/print` | The actual print-ready render page. |
| `/events/[id]/badges/[id]/review` | Attendee-facing self-service form. |
---
## Related Documentation
👉 **[MODULE__AE_Events_Badge_Templates.md](./MODULE__AE_Events_Badge_Templates.md)** (Technical reference for layouts)
👉 **[GUIDE__AE_Events_Badges_Onsite.md](./GUIDE__AE_Events_Badges_Onsite.md)** (Hardware & station setup)
👉 **[GUIDE__AE_Events_Onsite_Runbook.md](./GUIDE__AE_Events_Onsite_Runbook.md)** (Onsite operational checklists)

View File

@@ -0,0 +1,79 @@
# Aether Events — Launcher (Podium Display)
The Launcher module provides the podium display interface that runs on each session room's kiosk machine. It is designed to work in standard browsers but is optimized for the **Aether Desktop (Electron)** native shell.
---
## Operational Modes
| Mode | Use Case | File Handling |
|---|---|---|
| **Default** | Browser on any machine | Files downloaded on demand via browser. |
| **Onsite** | Browser on event network | Faster polling; browser-managed files. |
| **Native** | Electron app on podium Mac | Background pre-cache; atomic file handover. |
For production onsite use, **Native mode on Mac laptops** is the target. The Electron
app pre-caches all session files in the background so presentations open instantly without
a network round-trip at the moment of launch.
---
## Launcher Display Views
| View | Shown When |
|---|---|
| **Session view** | Active session with session-level files. |
| **Presentation view** | Active session with named presentations. |
| **Presenter view** | Presentation selected; shows presenter bio/photo. |
| **Poster/group view** | Special layout for poster sessions. |
| **Screensaver** | No active session; idle state. |
---
## Sync Engine & File Handling
### Background Sync (File Warming)
When a user navigates to a session in the Launcher UI, the background engine automatically warms the cache for that specific session by downloading all associated files.
### Force Sync Location
To ensure full room readiness (e.g., during SRR setup or overnight), operators can trigger a **Force Sync Location** via the configuration menu. This performs a recursive fetch of all sessions, presentations, and presenters for the room and queues every file for the day for download.
### Download Priority & Room Readiness
To ensure the podium is ready for the day's first sessions, the Launcher sync engine uses a 4-tier chronological sorting priority:
1. **Global Assets:** Event and Location level files (branding, walk-in slides) are cached first.
2. **Session Schedule:** Files for the earliest sessions in the room are prioritized.
3. **Presentation Order:** Within a session block, speakers are prioritized by their scheduled start time.
4. **First-In Fairness:** When times are equal, older uploads are prioritized over late revisions (respecting on-time presenters).
### Native File Opening (Safe Handover)
1. Verify SHA-256 hash in permanent cache.
2. Atomic copy to system `[tmp]` directory.
3. Rename to original filename (e.g., `Abstract_101.pptx`).
4. OS opens the file via a **Launch Profile** (AppleScript or Shell command).
---
## Device & Native Integration
Each Launcher kiosk is registered as an `event_device` record in Aether. The technical specifications for the Electron bridge, hashed cache protocol, and hardware actuators are documented in:
👉 **[MODULE__AE_Events_Launcher_Native.md](./MODULE__AE_Events_Launcher_Native.md)**
---
## Route Map (Display)
| URL | Purpose |
|---|---|
| `/events/[id]/launcher` | Launcher home — select location |
| `/events/[id]/launcher/[location_id]` | Launcher display for a specific room |
---
## Access Levels
| Feature | Minimum Access |
|---|---|
| View Launcher display | `authenticated_access` |
| Manual session selection | `trusted_access` |
| Advanced Config / Sync Control | `trusted_access` (via Configuration Drawer) |

View File

@@ -0,0 +1,94 @@
# Aether Events — Launcher Configuration Menu (Inventory)
> **Status:** Current Reference (v3.0)
> **Location:** `src/routes/events/[event_id]/(launcher)/launcher_cfg.svelte`
This document provides a detailed inventory of the Launcher's configuration menu settings as of May 2026. This serves as the baseline for the v3.1 reorganization into a modal-based tabbed interface.
---
## 1. UI Architecture & Visibility
The configuration menu currently resides in a slide-out **Drawer** (sidebar).
### 1.1 Visibility Modes
- **Standard Mode:** Default view for onsite operators. Hides advanced technical and destructive controls.
- **Technical Mode (`$ae_loc.edit_mode`):** Toggled via a subtle pencil icon. Reveals advanced diagnostic fields, manual overrides, and debug tools.
- **Native Mode (`$ae_loc.is_native`):** Automatically detected when running in the Electron shell. Shows OS-level controls (Filesystem, Power, Apps).
### 1.2 Section Expansion Logic
- **`collapsed`**: Content hidden.
- **`auto`**: Expanded by default; collapses when another "auto" section opens.
- **`pinned`**: Remains expanded regardless of other interactions.
---
## 2. Menu Inventory (Tabbed View)
### Tab 1: Setup (Onsite Operator Focus)
| Section | Feature | Technical Mode Only |
| :--- | :--- | :--- |
| **Display & App Modes** | Session Mode Preset (Oral vs Poster Kiosk) | |
| | Operational Env (Web / App / Onsite) | |
| | Interface Visibility (Hide Header/Menu/Footer/Times) | |
| | Clock Format (12/24 hour) | |
| | WebSocket Debugger Toggle | Yes |
| | Poster Modal Title Toggle | Yes |
| | Native Test Mode (Simulation) | Yes |
| **Remote Controller** | WS Connection Status Badge | |
| | Controller Strategy (Local / Remote / Local Push) | |
| | Connect / Disconnect Action | |
| | Group Reload (WS trigger) | |
| | Channel Group Code (Locked/Unlockable) | Yes |
| **Poster Screen Saver** | Idle Timeout Summary | |
| | Timer Overrides (Idle / Cycle / Loop) | Yes |
### Tab 2: Device (Technical & Native Focus)
| Section | Feature | Technical Mode Only |
| :--- | :--- | :--- |
| **Sync Engine & Timers** | Pause / Resume Sync | |
| | Force Sync Location (Recursive fetch) | |
| | Polling Periods (Event/Device/Loc/Sess/Pres/Presenter) | Yes |
| | Cache Hash Prefix Length (1-3 chars) | Yes |
| **System & Sync Health** | CPU & RAM Usage Gauges | |
| | Heartbeat Status & Timestamp | |
| | Sync Progress (Cached vs Total) | |
| | Active Sync Filename (Animated) | |
| | Hostname & IP List | Yes |
| | Raw Device JSON Inspector | Yes |
| **Native OS Management** | Open Cache / Temp Folders | |
| | Window Control (Maximize / Kiosk) | |
| | Display Mode (Extend / Mirror) | |
| | Presentation Remote (Prev/Start/Stop/Next) | |
| | Reset Wallpaper (Site Header) | Yes |
| | Kill Presentation Apps (PowerPoint/Keynote/etc) | Yes |
| | Power Actions (Reboot / Shutdown) | Yes |
| | Manual Terminal Command Entry | Yes |
| **Wallpaper** | Primary Display URL Preset/Input | |
| | External Display URL Preset/Input | |
| | Save & Apply Wallpaper | |
| | Restore macOS Default | |
| **Launch Timing** | Per-Profile Post-Open Delay (ms) Overrides | Yes |
| **Application Updates** | Update Source (File / URL) | Yes |
| | Check for Updates | |
| | Install & Relaunch | |
### Tab 3: Dev (Technical/Developer Focus)
| Section | Feature | Technical Mode Only |
| :--- | :--- | :--- |
| **Local Reset & Actions** | Maintenance Select (Wipe IDB / LocalStorage) | Yes |
| | Global Sys Menu Toggle | Yes |
| | Global Debug Menu Toggle | Yes |
| | Cache .tmp Cleanup (Native Only) | Yes |
| | API Endpoint & Account ID Summary | Yes |
---
## 3. Global Actions (Footer)
- **Close:** Dismisses the configuration menu.
- **Reload:** Performs a full browser `location.reload()`.
- **Debug Panel:** Opens the raw state inspector (Technical Mode Only).

View File

@@ -0,0 +1,110 @@
# Aether Events — Unified Launcher Configuration (Vision v3.1)
> **Status:** Strategic Design / Unified Proposal
> **Author:** Gemini CLI (Interactive Agent)
> **Target:** Full consistency across all configuration modules.
## 1. Unified Design Language
To eliminate the "created by 3 different people" feel, all components must strictly adhere to this shared specification.
### 1.1 Color Palette & Semantics
- **Primary (Blue):** Main actions, active tabs, and standard configuration toggles.
- **Secondary (Green):** Safe actions (Connect, Sync, Apply).
- **Warning (Orange):** Technical overrides that require caution (Timers, Native Shell).
- **Error (Red):** Destructive actions (Resets, Shutdown, Kill Apps).
- **Surface (Gray):** Containers, input backgrounds, and inactive states.
### 1.2 Typography & Spacing
- **Section Headers:** `text-sm font-bold uppercase tracking-tight` (Provided by Wrapper).
- **Field Labels:** `text-[10px] font-bold uppercase tracking-wider opacity-60 mb-1`.
- **Sub-Descriptions:** `text-[9px] italic opacity-40 leading-snug mt-1`.
- **Status Badges:** `text-[8px] font-bold uppercase tracking-tighter`.
- **Grid Standard:**
* Single Column for complex fields.
* `grid-cols-2` with `gap-4` for standard inputs.
* `grid-cols-3` or `grid-cols-4` only for small buttons or icon toggles.
---
## 2. Structural Reorganization (The "Aether" Layout)
The menu is now a **Vertical Sidebar Modal**. This allows for persistent navigation while dedicating the large right pane to content.
### Tab 1: 🖥️ Display (General Operator)
*Focus: What the screen looks like.*
- **Category: Layout & UI**
- Presets: Oral/Default vs Poster Kiosk (One-tap setup).
- Toggles: Header, Menu, Footer, Times visibility.
- Formatting: Clock (12/24h), Date formats.
- **Category: Screen Saver**
- Idle Timeout (Minutes).
- Mode: Image Cycle vs Video vs Custom.
### Tab 2: 🔌 Connectivity (Onsite Tech)
*Focus: How it talks to the network.*
- **Category: WebSocket Control**
- Connection Status & Signal Strength.
- Controller Mode: Local vs Remote vs Push.
- Group Code: Channel sharding for multi-room management.
- **Category: API Context**
- Current Endpoint, Account, and Site context.
### Tab 3: 🔄 Sync & Health (Onsite Tech)
*Focus: Data integrity and performance.*
- **Category: Sync Engine**
- Status: Active vs Paused.
- Action: Force Sync Location (recursive metadata fetch).
- Stats: Cached Files vs Total Files (Progress bar).
- **Category: System Telemetry**
- CPU & RAM usage (Visual gauges).
- Heartbeat monitor (Last success timestamp).
- Device Identity: Hostname, IP list, Local paths.
### Tab 4: 🛠️ Native Shell (Specialized / Mac)
*Focus: OS-level capabilities.*
- **Category: App Control**
- Window: Maximize, Kiosk Mode, Fullscreen.
- Automation: Kill presentation apps (Clean slate).
- Remote: Virtual clicker (Prev/Next/Start/Stop).
- **Category: System Action**
- Displays: Extend vs Mirror (Native bridge).
- Folders: Open Cache / Open Temp.
- Power: Reboot / Shutdown (With confirmation).
### Tab 5: 🖼️ Wallpaper (Branding)
*Focus: Event-specific aesthetics.*
- **Category: Customization**
- Primary Display: URL/Preset.
- Secondary/Projector: URL/Preset.
- Action: Apply to OS (Native) + Preview (Web).
### Tab 6: 🧪 Advanced (Developer Mode)
*Focus: Fine-tuning and updates.*
- **Category: Performance**
- Polling Intervals (Event, Device, Room, Session, Presenter).
- Cache Sharding (Prefix length).
- **Category: Launch Logic**
- Per-Profile Post-Open Delays (ms).
- **Category: Updates**
- Source: File vs URL.
- Version: Current vs Target.
- Action: Download/Install.
### Tab 7: 🧹 Maintenance (Emergency)
*Focus: Troubleshooting.*
- **Category: Resets**
- Wipe IndexedDB (Module selective).
- Clear LocalStorage (Reset config).
- **Category: Diagnostics**
- Raw Device JSON inspector.
- Terminal Command Entry.
---
## 3. Implementation Plan: The "Cohesion" Refactor
1. **Standardize `Launcher_Cfg_Section.svelte`:** Ensure padding and spacing are baked into the wrapper so children don't have to define it.
2. **Create `Launcher_Cfg_Field.svelte`:** A new helper component to handle the Label + Description + Input pattern consistently.
3. **Audit Sub-Components:** Update all 10 components to use the new colors, grid patterns, and typography.
4. **Polish Transitions:** Ensure the Modal entry and Tab switching are butter-smooth with Svelte 5 transitions.

View File

@@ -0,0 +1,275 @@
# Aether Events — Launcher: Native Integration
> **Status:** Operational / Permanent Reference
> **Last Updated:** 2026-05-21 (Reorganized)
> **Primary Platform:** macOS (Darwin)
> **Fallback Platform:** Linux / Windows
## 1. Overview
The Aether Events Launcher utilizes an Electron-based "Native Shell" to provide OS-level capabilities that are normally restricted by browser sandboxing. This enables persistent file caching, direct control of presentation software (Keynote, PowerPoint), and hardware telemetry.
### Operational Modes
| Mode | Purpose | File Handling |
| :--- | :--- | :--- |
| **Default** | Standard web browser access. | Direct downloads; no local caching. |
| **Onsite** | Web access on event networks. | Faster polling; browser-based file management. |
| **Native** | Dedicated Podium Kiosk (Electron). | Full background pre-caching; atomic safe-handover. |
---
## 2. Architecture: The Three-Layer Bridge
The integration is built on a decoupled three-layer communication model to ensure security and cross-platform flexibility.
### 2.1 Layer 1: The Engine (Main Process)
- **Repo:** `~/OSIT_dev/aether_app_native_electron/` (separate git repo)
- **File:** `aether_app_native_electron/src/main/*.ts`
- **Role:** Performs the heavy lifting (Filesystem, Shell, AppleScript).
- **Responsibilities:**
- Managing the **Hashed Cache** directory.
- Executing `osascript` intents for presentation control.
- Spawn/Kill process management.
### 2.2 Layer 2: The Gatekeeper (Preload Script)
- **Namespace:** `window.aetherNative`
- **Role:** Securely exposes whitelisted IPC channels to the Renderer.
- **Standards:** Uses `contextBridge.exposeInMainWorld` to prevent arbitrary code execution.
### 2.3 Layer 3: The Messenger (SvelteKit Relay)
- **File:** `src/lib/electron/electron_relay.ts`
- **Role:** Provides a clean, typed API for Svelte components.
- **Responsibilities:**
- Mapping `camelCase` UI triggers to `snake_case` IPC calls.
- Resolving an extension alias to a canonical Launch Profile, then to a single
`native_template` string before crossing IPC.
The reason for this split is simple: Launch Profiles are policy, while Native Templates are
executable strings. Keeping that distinction explicit prevents the bridge from mixing config
objects with runtime commands.
---
## 3. The "Zero-Config" Lifecycle
To support rapid onsite deployment, the native app requires zero manual setup.
1. **Seed:** On launch, the Main process reads a local `seed.json` (Device ID + API Key).
2. **Identity:** Calls `GET /v3/crud/event_device/{id}` to pull device config and extract `app_base_url` (the event FQDN) and `account_id`.
3. **Site Context:** POSTs to `/v3/crud/site_domain/search?limit=1` with the FQDN to resolve the correct site. No JWT — auth is `x-aether-api-key` + `x-account-id` throughout.
4. **Launch:** Navigates the SvelteKit frontend directly to the assigned Event Launcher route (`/events/{eventId}/launcher/{locationId}`).
---
## 4. Podium Reliability Protocol
The system is designed to ensure that a presentation never fails due to network instability.
### 4.1 Hashed Cache Pattern
Files are stored persistently using their SHA-256 hash to prevent filename collisions and handle versioning.
- **Root:** `~/Library/Caches/OSIT/file_cache/`
- **Subdirectory:** First 2 characters of hash (e.g., `ab/`)
- **Filename:** `{hash}.file`
### 4.2 Background Sync (File Warming)
When a user navigates to a session in the Launcher UI, the `LauncherBackgroundSync` component warms the cache for that specific session. To ensure full room readiness, a **Force Sync Location** trigger is available in the configuration UI.
1. **Metadata Fetch:** The system fetches all sessions, presentations, and presenters for the current location into the local database (Dexie).
2. **Chronological Priority:** Missing files are added to the download queue and sorted to prioritize the event schedule:
- **Tier 1: Global Assets** — Event and Location level files (virtual time 0).
- **Tier 2: Session Schedule** — Earliest sessions are prioritized first.
- **Tier 3: Presentation Order** — Within a session, speakers are prioritized by their start time.
- **Tier 4: Integrity & Fairness** — Tie-breakers use `created_on` (oldest first) to ensure on-time uploads are cached before last-minute revisions.
3. **Download:** Triggers background downloads via `aetherNative.download_to_cache` sequentially to preserve network bandwidth and ensure file integrity.
### 4.3 Safe Handover (Launch Sequence)
When a user clicks "Open", the system follows a non-destructive sequence:
1. **Verify:** Confirm hash exists in the permanent cache.
2. **Copy:** Create an atomic copy in the system `[tmp]` directory.
3. **Restore:** Rename the copy to its original filename (e.g., `Abstract_101.pptx`).
4. **Execute:** Launch the file via the OS.
---
## 5. Automation & Actuators (Phase 5)
The native shell provides specialized handlers for controlling the "Podium Experience."
### 5.1 Presentation Acts
| Action | Handler | Actuator (macOS) |
| :--- | :--- | :--- |
| **Launch** | `launch_presentation` | `open` or `osascript` (slideshow start) |
| **Control** | `control_presentation` | `osascript` (next/prev slide) |
| **Clean Up** | `kill_processes` | `killall -INT` (graceful exit) |
### 5.2 System Management
- **Telemetry:** Pushes `cpu_usage`, `memory_free_gb`, and `foreground_app` via heartbeats using the `get_device_info` relay.
- **Self-Update (Roadmap):** Plan to monitor Syncthing `admin_share` for newer `.app` versions and perform atomic swaps.
### 5.3 Implemented Actuators (Phase 5 Complete)
- **Recording:** `manage_recording({action})` — Aperture session capture (`start`, `stop`, `status`). macOS only.
- **Display Layouts:** `set_display_layout({mode, configStr?})` — Mirror / Extend displays. macOS only. **Primary:** native `display_control` binary (`resources/bin/display_control`) uses CoreGraphics APIs directly — no Homebrew dependency. Built from `scripts/display_control.m` via `scripts/build-display-control.sh` on a Mac; commit the binary to the repo. **Fallback:** [`displayplacer`](https://github.com/jakehilborn/displayplacer) (`brew install displayplacer`) used when binary is absent or `configStr` override is set. Failures are logged to the Electron console but do not block file open. A **Display Mode** toggle (Extend / Mirror) is available in the Launcher config — Native OS section, visible without Technical Mode.
- **Power Control:** `power_control({action})` — Shutdown, reboot, sleep. macOS + Linux.
- **Window Control:** `window_control({action})` — Maximize, minimize, fullscreen, kiosk mode.
- **Wallpaper:** `set_wallpaper({path?, url?, url_external?, display?, api_key?, account_id?})` — Downloads from URL (cached locally) or applies a local path. Per-display targeting (`'all'`/`'primary'`/`'external'`). macOS only in production; Linux returns a dev-mode preview payload.
> **Note:** `update_app` is implemented as a stub — downloads but does not install. Not yet functional for end users.
---
## 6. Launcher Configuration & Management
The Launcher features a standardized, responsive configuration interface designed for onsite technical management.
### 6.1 UI Architecture
- **Tabbed Navigation:** Categorized into System, Sync, and General settings.
- **Section Wrapper (`Launcher_Cfg_Section`):** A shared component providing a consistent header, icon, and responsive grid container.
### 6.2 3-Way State Logic
To manage screen real estate on varying laptop resolutions, all configuration sections utilize a 3-way visibility state:
- **`collapsed`**: Content is hidden.
- **`auto`**: Expanded by default, but automatically closes if another "auto" section is opened.
- **`pinned`**: Expanded and remains open regardless of other section interactions.
### 6.3 Technical Mode (`edit_mode`)
The UI dynamically filters fields based on the user's focus. Enabling Technical Mode (`$ae_loc.edit_mode`) reveals advanced diagnostic and writeable fields.
| Category | Standard View (Read-Only) | Technical Mode (Read/Write) |
| :--- | :--- | :--- |
| **Health** | Heartbeat, RAM Usage, Sync Stats | Hostname, IP List, Raw Device JSON |
| **OS Bridge** | Folder Buttons, Recording Toggle | Manual Terminal Commands, Reset Wallpaper |
| **Sync** | Sync Completion Status | Millisecond Timers, Cache Prefix Logic |
| **Update** | Current Version Status | Manual Update Paths, URL Overrides |
---
## 7. Implementation Reference (IPC Whitelist)
All functions below are exported from `src/lib/electron/electron_relay.ts` and safely
no-op when `window.aetherNative` is not present (i.e., in browser/non-native mode).
### Config & Info
- `get_device_config()` — Returns hydrated device settings injected by the native shell on startup.
- `get_device_info()` — Returns OS metadata, IP list, hostname, and path placeholders (`[home]`, `[tmp]`).
### File Cache
- `check_cache({cache_root, hash, hash_prefix_length?, verify_hash?})` — Verifies a file exists in the local hashed cache. `verify_hash: true` re-hashes to confirm integrity.
- `download_to_cache({url, cache_root, hash, api_key, account_id, hash_prefix_length?})` — Streams a file download to the hashed cache with SHA-256 integrity check. Stale `.tmp` files (older than 5 min) from crashed downloads are cleaned up automatically on each call.
- `copy_from_cache_to_temp({cache_root, hash, temp_root, filename, hash_prefix_length?})`**Preferred primitive.** Copies a cached file to temp and returns `{ success, path }`. The Svelte caller decides what to do next (run a script, open it, etc.).
- `launch_from_cache({cache_root, hash, temp_root, filename, hash_prefix_length?, native_template?})` — Combines copy + launch in one call. Executes the provided `native_template` string after the file is copied to temp. If no template is supplied, treat it as an error and do not rely on Electron-side defaults.
> `hash_prefix_length` defaults to `2` throughout. Do not change without coordinating all devices — mismatched values create orphaned cache subdirectories.
### Shell & OS
- `open_folder(path)` — Opens a path in the OS file manager.
- `run_cmd({cmd, timeout?, return_stdout?})` — Async shell command execution.
- `run_cmd_sync({cmd, return_stdout?})` — Synchronous shell command execution.
- `run_osascript(script)` — Executes an AppleScript string. macOS only. **Hardened (2026-05-11):** writes script to a temp `.scpt` file; multi-line scripts and paths with special characters now work correctly. No shell escaping needed in the passed string.
- `kill_processes({process_name_li})` — Terminates processes by name. macOS/Linux: `pkill -f`. Windows: `taskkill /F`.
- `open_local_file_v2(path)` — Opens a file with its default OS application.
### Presentations (Phase 5)
- `launch_presentation({path, app?, os?})` — Platform-aware launcher. macOS: PowerPoint/Keynote via AppleScript. Linux: LibreOffice Impress. Resolves `[home]`/`[tmp]` placeholders.
- `control_presentation({app, action})` — Slide navigation (`next`/`prev`/`start`/`stop`) for PowerPoint or Keynote via AppleScript.
### System Management (Phase 5)
- `set_wallpaper({path?, url?, url_external?, display?, api_key?, account_id?})` — Sets desktop wallpaper. Downloads from `url` (cached to `~/Library/Caches/OSIT/wallpaper/`) or applies a local `path`. `url_external` targets the projector/second display separately. macOS only in production; Linux returns a dev-mode preview payload without applying.
- `window_control({action, value?})` — Electron window management: maximize, minimize, fullscreen, kiosk.
- `set_display_layout({mode, configStr?})` — Mirror or extend displays via [`displayplacer`](https://github.com/jakehilborn/displayplacer). macOS only. Auto-detects via `displayplacer list`; `configStr` overrides auto-detection when set. Binary lookup order: bundled `resources/bin/displayplacer``/opt/homebrew/bin/` (Apple Silicon) → `/usr/local/bin/` (Intel). Requires `brew install displayplacer` on each venue Mac if not bundled.
- `power_control({action})` — Shutdown, reboot, or sleep the host machine. macOS + Linux.
- `manage_recording({action, options?})` — Aperture capture control (`start`/`stop`/`status`). macOS only.
- `open_external({url, app?})` — Opens a URL in Chrome, Firefox, or the default browser.
- `update_app(args)`**Stub only.** Downloads but does not install. Not yet functional.
- `list_tools()` — Returns a self-documenting manifest of all available native bridge functions.
### Path Placeholders
All paths passed to native handlers should use tokens rather than hardcoded OS paths:
- `[home]` — Resolved to the user's home directory by the native bridge.
- `[tmp]` — Resolved to the system temporary directory.
---
## 8. Launch Profiles and Native Templates (No-Rebuild File Handling)
This launcher uses two related concepts:
- **Launch Profile**: the Svelte-side config object keyed by file extension. A profile decides
which app to use, whether to extend or mirror displays, whether to use an explicit open
command, whether to run post-open automation, and how long to wait before running it.
- **Native Template**: the single AppleScript or shell command string handed to Electron after
Svelte resolves the profile. This is what Electron actually executes.
The Svelte launcher resolves a profile and then passes a native template string to
`launch_from_cache`. Electron only executes the template it receives. If Svelte has not
resolved a template yet, it should stop before IPC and surface a missing-profile error.
This keeps all fallback logic in Svelte, where it can be edited without rebuilding Electron.
The native layer should not invent or guess a default launch path.
The built-in defaults are organized as canonical profile names plus extension aliases. That
lets multiple file types share one profile without repeating the same app/script details.
The profile object also carries `post_delay_ms`, and a device-specific per-profile
`launch_profiles[profile].post_delay_ms` override can tune the delay without changing the bridge
contract. URL-based presentations remain a special pseudo-extension handled separately from
the cache open flow.
### Native Template Formats
| Format | Example |
| :--- | :--- |
| **AppleScript** (macOS) | Multi-line AppleScript string with `{{path}}` placeholder |
| **Shell command** | String prefixed with `shell:` — e.g. `shell:open "{{path}}"` |
The placeholder `{{path}}` is replaced with the full resolved path to the file in the temp
directory after the atomic copy from cache.
### Where to Configure
Launch profiles are resolved in priority order by `get_launch_profile()` in
`launcher_file_cont.svelte`:
1. **`event_device.data_json.launch_profiles`** — API-driven, per-device. Highest priority.
Set via the `event_device` record (Pres Mgmt → Device Management or direct DB edit).
2. **`$events_loc.launcher.launch_profiles`** — Local persistent config. Editable via the
Launcher config UI (planned) or direct `localStorage` manipulation.
If neither is set, the resolved native template is `null` and the launcher should not call
Electron until an explicit template is available.
Why: this avoids a second hidden source of truth. The profile map can evolve independently of
the executable string, and Electron stays a thin executor rather than a policy engine.
### Key Format
Keys are lowercase file extensions without the dot. A `"default"` key catches all
unrecognised extensions.
The JSON below illustrates the `native_template` emitted after profile resolution, not the
full Launch Profile object schema.
```json
// event_device.data_json.launch_profiles example
{
"launch_profiles": {
"pptx": "tell application \"Microsoft PowerPoint\"\n activate\n open (POSIX file \"{{path}}\")\n delay 3\nend tell\ntell application \"System Events\"\n keystroke return using command down\nend tell",
"key": "tell application \"Keynote\"\n activate\n open (POSIX file \"{{path}}\")\n delay 1\n start (front document)\nend tell",
"pdf": "shell:open \"{{path}}\"",
"default": "shell:open \"{{path}}\""
}
}
```
### AppleScript Execution — All Handlers Hardened (2026-05-11)
All AppleScript execution in the native shell now writes scripts to a temp `.scpt` file and
runs `osascript "<path>"` rather than the old `osascript -e "<inline>"` approach.
- **`run_osascript`** — hardened (2026-05-11, earlier batch)
- **`launch_from_cache`** — hardened (same batch)
- **`launch_presentation`** — hardened (2026-05-11, follow-up fix; was the last handler still using `-e`)
- **`control_presentation`** — uses single-line scripts with no path interpolation; `-e` is safe here and retained for simplicity
The `-e` approach breaks on (1) multi-line scripts and (2) file paths containing spaces,
quotes, or parentheses — common in conference presentation filenames.
### Not Exposed via Relay (intentional)
- `get_seed_config` / `get_jwt` — Exposed in the preload but not relayed to the UI. The JWT and seed are injected into the environment at startup; components should not call these directly.

View File

@@ -0,0 +1,284 @@
# Aether Events — Exhibitor Leads Module (v3)
**Status:** Implemented and ready for demo. Core lead capture flow works end-to-end.
**Platform:** PWA only — mobile-first, offline-capable.
**Target users:** Conference exhibitors scanning attendee badges at their booths.
### Recent Changes (2026-04-03)
- Migrated Leads persisted state to Svelte5 PersistedState: `leads_loc` now implemented at `src/lib/stores/ae_events_stores__leads.svelte.ts` and the store version constant `AE_LEADS_LOC_VERSION` added to `src/lib/stores/store_versions.ts`.
- Payment UI adjustments: `ae_comp__exhibit_payment.svelte` now accepts a `leads_require_payment` prop and enforces the event-level `mod_exhibits_json.leads_require_payment` flag; a loading guard was added so the component waits for the exhibit record (Dexie `liveQuery`) before deciding which UI to show.
- Tests: update `tests/_helpers/leads_helpers.ts` to seed `leads_loc` defaults and `__version` when needed to avoid localStorage wipe caused by store version checks.
---
## What It Does
The Exhibitor Leads module lets conference exhibitors capture and manage attendee leads directly
from their booth. Exhibitors scan or search attendee badges and build a list of contacts they met.
All data is cached locally (IndexedDB / Dexie.js) for spotty or offline venue Wi-Fi, with
background SWR revalidation against the API when the network is available.
Key capabilities:
- **Badge scanning** — QR scan or text search (name, email, affiliations, badge ID)
- **Lead list** — filterable/sortable, per-exhibitor or per-staff-member view
- **Lead detail** — custom question responses, notes (rich text), priority boolean flag, hide/unhide
- **Export** — CSV/XLSX download of all leads for an exhibit
- **License management** — assign staff accounts (email + passcode) per max license count
- **Custom questions** — configurable per-exhibit follow-up questions (ratings, dropdowns, text)
- **Offline-first** — IndexedDB cache survives network drops; syncs on reconnect
- **PWA install** — Chrome/Android native install prompt; iOS Safari "Add to Home Screen" nudge
---
## Access Levels
Three sign-in levels are supported within this module:
| Level | How to sign in | What they can do |
|---|---|---|
| **Aether Platform Auth** | Standard Aether login (manager/trusted access) | Full admin bypass; all exhibit data |
| **Shared Exhibit Passcode** | Enter booth's `staff_passcode` | Manage licenses, view/add leads |
| **Licensed User** | Email + individual passcode from `license_li_json` | Add and manage leads for this booth |
Auth state is persisted in `$events_loc.leads.auth_exhibit_kv[exhibit_id]` (localStorage-backed).
A booth only shows in the landing page search to non-admins if it is marked `priority = true` (i.e. paid).
### `allow_tracking` Opt-In
Attendees must have `allow_tracking = true` on their badge record to be added as a lead.
Attendees without this flag are blocked at both the QR scanner and the manual search:
- QR scan shows a "Tracking Blocked" warning card (`ShieldOff` icon)
- Manual search shows an "Opt-Out" badge per result row; the "Add as Lead" button is suppressed
---
## Route Structure
```
/events/[event_id]/leads/
→ Exhibit search / landing page — find your booth
/events/[event_id]/leads/exhibit/[exhibit_id]/
→ Main exhibitor view — all 4 tabs
/events/[event_id]/leads/exhibit/[exhibit_id]/lead/[exhibit_tracking_id]/
→ Lead detail view — edit notes, custom responses, flags
```
---
## Module Tabs
### Tab 1 — Start / Sign In
The only tab visible when not signed in as a licensed leads user.
- **Sign in with shared passcode** — grants booth management access (license management, passcode change)
- **Sign in as licensed user** — grants lead capture access (email + passcode)
- **PWA install prompt** — Chrome/Android native install button; iOS "Share → Add to Home Screen" instructions
- **License list** — shown when signed in via shared passcode or Aether admin; add/edit/remove staff slots
### Tab 2 — Add Leads
Visible only when signed in (licensed user or Aether auth).
- **Text search** — search by name, email, affiliations, badge ID
- **QR scan** — three modes (persisted per exhibit in `tab_scan_qualify`):
- **Confirm** (`rapid`) — scan, then choose per badge: **Add & Scan Next** (resets after 2s) or **Add & View Lead** (navigates to detail)
- **Auto** — no confirmation tap; adds immediately and auto-resets (high-throughput)
- **Multi** — BarcodeDetector batch scan; up to 4 badges in one frame as a confirm grid
- Previously-removed leads detected on scan — shown a "Previously Removed" card with **Restore & Scan Next** / **Restore & View Lead** buttons
- Results show "Add as Lead" or "View Lead" depending on whether already captured
- `external_person_id` and `group` resolved by auth type — see [Capture Identity](#capture-identity) below
### Tab 3 — Leads List
The main lead management view.
- **Search** — full-text across name, email, notes (local IDB fast path + API revalidation)
- **Sort** — Newest first, Oldest first, Name A→Z, Name Z→A
- **Filter by staff member** — "All Leads" or filter by individual licensed user
- **Show/hide hidden records** — toggles `hide` filter on IDB and API results
- **Export** — downloads CSV/XLSX for the exhibit (`leads_api_access` required)
### Tab 4 — Manage / Config
Exhibit configuration and app settings.
**Admin Tools** (manager_access only):
- Payment status toggle (`priority` boolean field)
- Max licenses, small/large device counts
**Booth Profile** (all signed-in users):
- Exhibitor name, booth description (rich text)
**Access & Security**:
- View/change shared staff passcode
- Sign out button
**Lead Retrieval Config**:
- Exhibit Leads Licensees — manage staff accounts (`administrator_access` OR signed in via shared exhibit passcode)
- Qualifiers & Questions — custom question config
- Licenses & Billing — Stripe payment (only shown when `event.mod_exhibits_json.leads_require_payment = true`)
**App Settings**:
- Auto-hide header/footer toggle
- Show Extra Details toggle
- Refresh interval (1120 seconds, default 25s), countdown timer, last-refresh timestamp
- Reload App, Clear IDB, Hard Reset (clears localStorage)
---
## Data Model
### `event_exhibit`
One exhibitor's presence at an event.
| Field | Purpose |
|---|---|
| `event_exhibit_id` | Primary / URL-safe ID |
| `name` | Exhibitor display name |
| `code` | Booth number |
| `staff_passcode` | Shared sign-in code |
| `priority` | `1` = paid/active |
| `license_max` | Max licensed staff slots |
| `license_li_json` | Array of `{ full_name, email, passcode }` |
| `leads_custom_questions_json` | Array of question definitions |
| `leads_device_sm_qty` / `leads_device_lg_qty` | Device count tracking |
### `event_exhibit_tracking`
One captured lead — links an exhibit to a badge.
| Field | Purpose |
|---|---|
| `event_exhibit_tracking_id` | Primary key |
| `event_exhibit_id` | Parent exhibit |
| `event_badge_id` | Captured attendee's badge |
| `external_person_id` | Capturing staff's email (from license) |
| `exhibitor_notes` | Rich text notes (HTML via TipTap) |
| `responses_json` | `{ [question_code]: { response: value } }` |
| `priority` | Star/flag for high-priority leads |
| `hide` | Soft-delete / hide from list |
| Denormalized badge fields | `event_badge_full_name`, `event_badge_email`, `event_badge_affiliations`, `event_badge_professional_title` |
---
## Key Files
### Routes
| File | Role |
|---|---|
| `leads/+page.svelte` | Exhibit search/landing |
| `leads/exhibit/[exhibit_id]/+page.svelte` | Main exhibitor view — orchestrates all tabs |
| `leads/exhibit/[exhibit_id]/+layout.svelte` / `+layout.ts` | Layout / data load |
| `leads/exhibit/[exhibit_id]/lead/[exhibit_tracking_id]/+page.svelte` | Lead detail |
### Components
| File | Role |
|---|---|
| `ae_tab__start.svelte` | Tab 1 — welcome, sign-in, license list |
| `ae_tab__add.svelte` | Tab 2 — QR scan + text search toggle |
| `ae_tab__manage.svelte` | Tab 4 — admin tools, booth config, app settings |
| `ae_comp__exhibit_signin.svelte` | Sign-in UI (shared passcode + licensed user) |
| `ae_comp__lead_qr_scanner.svelte` | QR scanner (rapid / qualify mode) |
| `ae_comp__lead_manual_search.svelte` | Manual badge search + add |
| `ae_comp__exhibit_tracking_search.svelte` | Lead list search/filter/sort bar |
| `ae_comp__exhibit_tracking_obj_li.svelte` | Lead list item renderer |
| `ae_comp__exhibit_license_list.svelte` | License slot manager |
| `ae_comp__exhibit_custom_questions.svelte` | Custom question config editor |
| `ae_comp__exhibit_payment.svelte` | **STUB** — Stripe placeholder |
| `ae_comp__exhibit_search.svelte` | Exhibit search on the landing page |
| `lead/ae_comp__lead_detail_form.svelte` | Custom question response editor |
### Lib Functions
| File | Role |
|---|---|
| `src/lib/ae_events/ae_events__exhibit.ts` | Exhibit load, search, create, update |
| `src/lib/ae_events/ae_events__exhibit_tracking.ts` | Tracking load, search, create, update, export |
Both aggregated into `events_func` via `src/lib/ae_events/ae_events_functions.ts`.
---
## Offline / PWA Notes
- All data is stored in `db_events` (Dexie.js) — `exhibit` and `exhibit_tracking` tables
- SWR pattern: IDB cache returned immediately; background API fetch updates IDB and triggers UI refresh
- Search: local IDB first pass (fast), then API revalidation via `search__exhibit_tracking`
- `beforeinstallprompt` event captured at module load time (`src/lib/pwa/pwa_install.svelte.ts`)
— fires within ~1 second of page load, before any Svelte `$effect` runs
- iOS Safari: no native install prompt; shows "Share → Add to Home Screen" instructions instead
---
## Capture Identity
`external_person_id` and `group` on every `event_exhibit_tracking` record record who captured the lead. Resolved at capture time in all three lead capture components (single scanner, multi scanner, manual search):
| Auth type | `kv.type` | Value stored |
| --- | --- | --- |
| Licensed exhibit user | `'licensed'` | Their email address (`kv.key`) |
| Shared exhibit passcode | `'shared'` | `'shared_passcode'` (label — raw passcode is NOT stored) |
| Aether user (admin bypass, no kv) | `undefined` | `$ae_loc.access_type` — e.g. `'trusted'`, `'manager'`, `'super'` |
`kv` = `$events_loc.leads.auth_exhibit_kv[exhibit_id]` (localStorage-persisted exhibit sign-in state).
---
## Lead Soft-Delete / Re-enable
Leads are never hard-deleted. "Remove Lead" sets `enable = false`. Key behaviors:
- **Leads list** always filters out `enable = false` records (both IDB fast-path and API results) — no flash of removed records
- **QR scanner**: if a previously-removed badge is scanned, the scanner detects it via `existing_leads_map` (IDB) or API fallback search (`search__exhibit_tracking` with `qry_badge_id` + `enabled: 'not_enabled'`) and shows the reenable card instead of an error
- **Lead detail page**: "Remove Lead" button (two-click confirm in header) sets `enable = false` and navigates back. "Restore Lead" card appears at the bottom of the right sidebar when `enable` is falsy.
- `search__exhibit_tracking` supports `qry_badge_id` param (added) and `enabled: 'not_enabled'` to find disabled records for a specific badge + exhibit combination
---
## Known Gaps
None currently. See TODO__Agents.md for remaining smoke test items.
## Implemented (previously listed as gaps)
### Payment / Stripe
`ae_comp__exhibit_payment.svelte` is fully implemented. Three states: paid (`priority=true` green
confirmation card), Stripe not configured (admin hint), payment form with license tier selector.
Visibility is event-wide: set `event.mod_exhibits_json.leads_require_payment = true` in the event
settings JSON to enable. When `false` (default), both the header CreditCard button and the
"Licenses & Billing" accordion in the Manage tab are hidden. The Stripe component itself is
unchanged — gating is done in `+page.svelte` and `ae_tab__manage.svelte`.
### License Management — Shared Passcode Access
Implemented. The license section in the Manage tab is visible to Aether admins and to anyone
signed in via the shared exhibit passcode (`auth_exhibit_kv[exhibit_id].type === 'shared'`).
### "My Leads" filter for shared-passcode users
Fixed. `external_person_id` is stored as the literal `'shared_passcode'` for shared users (not
the raw passcode string). The `search_params` derived in `+page.svelte` now checks `kv.type ===
'shared'` and resolves to `'shared_passcode'` instead of `kv.key`, so the "My Leads" filter
correctly returns their captured records.
---
## OSIT Admin Notes
- Mark `priority = 1` on an exhibit to make it visible in public search and to enable lead capture
- `license_max` controls how many licensed staff slots an exhibit can have
- Export endpoint: `GET /v3/action/event_exhibit/{id}/tracking_export` — requires `leads_api_access`
- Custom questions are stored per-exhibit in `leads_custom_questions_json` (not global)
- The exhibitor landing page link format: `/events/[event_id]/leads/exhibit/[exhibit_exhibit_id]/`
## Old Files for Reference
@backups/legacy/events_leads_v2/exhibit/[slug]/+page.svelte
@backups/legacy/events_leads_v2/exhibit/[slug]/leads_manage.svelte
@backups/legacy/events_leads_v2/exhibit/[slug]/leads_payment.svelte

View File

@@ -0,0 +1,139 @@
# Aether Events — Presentation Management
The Presentation Management module handles the full lifecycle of conference content: sessions, presentations, presenters, presentation files, and room/location assignments. It serves as the "Back Office" interface for event staff.
---
## Data Model
### Object Hierarchy
```text
Event
├── Event File (walk-in/out, hold slides for the whole event)
├── Location (physical room — assigned to Sessions, not the other way around)
├── Track (optional grouping; rarely used)
└── Session (time block; Location assigned here, but may be unset initially)
├── Session File (moderator slides, group/hold slides for this session)
└── Presentation (a talk within the session; must belong to exactly one Session)
└── Presenter (belongs to exactly one Presentation)
└── Presenter File (their slides/materials — the common case)
```
> **Import note:** When program data is initially imported (sessions, presentations,
> presenters), Locations are often not assigned to Sessions yet — rooms may not be
> finalized or the venue's room list may not be set up in Aether. Location assignment
> typically happens as a separate step once the room list is confirmed.
### Relationships
- **Session → Location:** Many-to-one. A Session is assigned to one Location; a Location
hosts many Sessions across the event timeline. Location may be null initially.
- **Presentation → Session:** Many-to-one. A Presentation belongs to exactly one Session.
A Session can have many Presentations (or none, for session-only setups).
- **Presenter → Presentation:** Many-to-one. A Presenter belongs to exactly one Presentation.
Optionally linked to an `event_person_id` for cross-referencing the person record.
- **Event File:** Can be attached at any level — Presenter, Presentation, Session,
Location, or Event. See the File Attachment Levels table.
### File Attachment Levels
Files (`event_file`) can be attached at five levels:
| Level | When Used | Typical Content |
|---|---|---|
| **Presenter** | 99% of the time for individual speakers | Their PowerPoint/PDF/video |
| **Session** | Moderator slides; group/hold content for a specific session | "Session 3 — Group Discussion.pptx" |
| **Location** | Walk-in/out or hold slides for a room across all sessions | Looped PPTX playing between sessions |
| **Event** | Walk-in/out or hold slides used everywhere | Looped PPTX; branding overlay |
| **Presentation** | File attached to the presentation record itself (less common) | Varies |
### Key Objects
| Object | Table | Purpose |
|---|---|---|
| Session | `event_session` | Time block; Location and datetime range assigned here |
| Location | `event_location` | Physical room |
| Presentation | `event_presentation` | A talk within a session; belongs to exactly one Session |
| Presenter | `event_presenter` | Person linked to exactly one Presentation |
| Event Person | `event_person` | Person record within the event context |
| Event File | `event_file` | Uploaded file; attached at Presenter, Presentation, Session, Location, or Event level |
---
## Client Setup Variation
There are no rigid "modes" — events are configured with as much or as little structure
as needed. The platform handles the full range:
**Minimal setup (BGH):**
Sessions have room and time info. No Presentations or Presenters defined.
Staff upload files directly at the session or location level onsite.
**Mid-range setup:**
Sessions defined with named Presentations. Presenters may or may not be tracked.
Mix of pre-uploaded and onsite files. QR codes may be used for quick session/presenter lookup.
**Full setup (LCI):**
Sessions, Presentations, Presenters all defined and managed. External ID labeling
(e.g., "LCI Member ID"). Agreement tracking for presenters. Files managed per-presenter.
The config that drives this is `event.mod_pres_mgmt_json` — see the Configuration section.
---
## Configuration — `mod_pres_mgmt_json`
The event's Presentation Management behavior is controlled by `event.mod_pres_mgmt_json`.
### Convention
| Prefix | Default state | Meaning |
|---|---|---|
| `hide__` | `false` = visible | Feature is ON by default; set `true` to suppress |
| `show__` | `false` = hidden | Feature is OFF by default; set `true` to enable |
### Common Config Keys
| Key | Default | Notes |
|---|---|---|
| `lock_config` | `false` | `true` = force remote→local sync; prevents user overrides of local config |
| `hide__session_code` | `false` | Hide session code column/field |
| `hide__session_description` | `false` | Hide session description field |
| `hide__session_location` | `false` | Hide location field on session view |
| `hide__session_datetime` | `false` | Hide datetime fields |
| `hide__presentation_code` | `false` | Hide presentation code |
| `hide__presenter_code` | `false` | Hide presenter code |
| `hide__location_code` | `false` | Hide location code |
| `show__launcher_link` | `false` | Show direct Launcher link in session view |
| `show__session_qr` | `false` | Show QR code for session (SRR lookup) |
| `show__presenter_qr` | `false` | Show QR code for presenter (SRR lookup) |
| `label__person_external_id` | `null` | Override label for external ID field (e.g., `"Member ID"`) |
| `label__session_poc_name` | `null` | Override label for session POC (e.g., `"Champion"`) |
| `file_purpose_option_kv` | `{}` | Key-value map of file purpose options (e.g., `{"ppt": "PowerPoint", "pdf": "PDF"}`) |
---
## Route Map (Administration)
| URL | Purpose |
|---|---|
| `/events/[id]/pres_mgmt` | Overview — sessions list, search, filter by location |
| `/events/[id]/pres_mgmt/config` | Config editor (admin only) |
| `/events/[id]/session/[session_id]` | Session detail — files, presentations, timing, alert |
| `/events/[id]/presenter/[presenter_id]` | Presenter detail — bio, files, agreement, alert |
| `/events/[id]/location/[location_id]` | Location detail — session schedule for this room, alert |
| `/events/[id]/locations` | All locations list |
| `/events/[id]/reports` | Reports — sessions, presenters, files |
---
## Access Levels
| Feature | Minimum Access |
|---|---|
| View pres_mgmt overview | `authenticated_access` |
| Upload files | `authenticated_access` |
| Edit sessions / presentations | `trusted_access` |
| Edit config | `administrator_access` + `edit_mode` |
| Device management | `administrator_access` |

View File

@@ -0,0 +1,625 @@
# PROJECT: AE Events Badges — Review Form & Print Font Controls
**Created:** 2026-02-27
**Last Updated:** 2026-03-18
**Branch:** `ae_app_3x_llm`
**Priority:** HIGH — first live event is Axonius, NYC, mid-April 2026
**Owner:** Scott Idem / One Sky IT
**Status:** ✅ TASK 1 COMPLETE | ✅ TASK 2 COMPLETE | ✅ TASK 3 COMPLETE | ✅ TASK 4.1 COMPLETE | ⏳ TASK 4.0 OPEN
---
## Design Intent — Two Complementary Flows
### Flow 1: Remote Badge Review (email link)
- Staff emails a review link to the attendee before the event.
- Attendee opens the link on their own device, reviews their badge info, and edits permitted fields.
- **Email address rule:** Always send to `event_badge.email` — never `email_override`.
`email_override` is a display/badge field only. It cannot be trusted as a delivery address
(attendee may have changed it to something different for badge display purposes).
- Component: `ae_comp__badge_review_form.svelte` — plain form, no badge render.
- Route: `/events/[event_id]/badges/[badge_id]/review/`
### Flow 2: Kiosk / Onsite Badge Station (print page)
- Hardware: a laptop + badge printer (Epson fanfold or Zebra PVC card) at the check-in table.
- At the event, an attendee walks up to a badge station (check-in kiosk).
- A staff member or volunteer pulls up the attendee's badge on the print page.
- The **print page is a kiosk tool**, not just a print queue:
- Attendee reviews their badge info and can edit permitted fields **in real time**,
with the live badge render updating as they make changes.
- Staff/volunteers are present to assist with any questions.
- Once satisfied, staff prints the badge.
- The key differentiator vs the review form: **the live badge render** shows exactly how
the badge will print. Attendees and staff can see changes immediately.
- Component: `ae_comp__badge_obj_view.svelte`
- Route: `/events/[event_id]/badges/[badge_id]/print/`
### Permission Model — Same Logic, Both Flows
Both flows should respect the same permission model:
- **Attendee-level** (basic Authenticated access): can edit `pronouns_override`,
`full_name_override`, `professional_title_override`, `affiliations_override`,
`location_override`, `phone_override`, `email_override`, `allow_tracking`, `agree_to_tc`.
- **Staff-level** (trusted_access+): all attendee fields + `email`, `badge_type_code_override`,
`badge_type_override`, `hide`, `priority`, `notes`, and font size controls.
- Permissions are configured per-event in `event.mod_badges_json.edit_permissions`.
Hardcoded defaults are used until that config is implemented.
**Current gap (TASK 4):** The print page edit button is currently gated to trusted_access only.
It needs to be accessible to attendees at the kiosk (with appropriate field-level gating),
matching the permission model already implemented in `ae_comp__badge_review_form.svelte`.
---
## Next Up for Badges (TASK 4)
### 0. Kiosk Editing — Print Page Permission Model Alignment
**This is the most important gap before the first live event.**
Currently the print page edit button is staff-only (trusted_access gate). At the kiosk,
attendees need to be able to edit their own fields (same attendee-level permissions as the
review form), with staff-only fields gated appropriately.
Work needed:
- Wire the same `can_edit_fields` / `can_edit(field)` permission logic into the print page
that `ae_comp__badge_review_form.svelte` already uses.
- The edit panel on the print page should show attendee-editable fields to all authenticated
users, and staff-only fields to trusted_access+.
- The badge render (v1 or v2) should update live as the attendee edits fields.
- Consider whether the print page needs its own inline edit panel (sidebar or overlay)
or whether it should share/reuse the review form component alongside the badge render.
- **Do NOT use `email_override` as the send-to address** — always use `event_badge.email`.
### 1. Auto-Scaling Badge Text — In Progress
`ae_comp__badge_obj_view.svelte` using `element_fit_text.svelte` (binary search auto-scale).
Toggle between v1 (heuristic) and v2 (auto-scale) on the print page via the `v1`/`v2` header button.
Heights tuned per layout in `fit_heights` derived object. Still needs visual tuning with real badges.
### 2. QR Code on Badge Front — `ae_comp__badge_obj_view.svelte`
The badge template has a `show_qr` flag (or similar). When toggled on, the QR code should
appear on the front face of the printed badge. Currently QR is only shown on the review form.
- Check the badge template fields for the QR toggle field name (`show_qr`, `qr_enabled`, etc.)
via `ae_describe event_badge_template` and inspect `ae_comp__badge_obj_view.svelte`.
- The QR code data URL is generated with:
```typescript
qr_data_url = await core_func.js_generate_qr_code('obj', {
obj_type: 'event_badge',
obj_id: event_badge_id
});
```
See `ae_comp__badge_review_form.svelte` for the working pattern.
- Position on badge: typically bottom-right corner of the badge face, sized to fit within
the template's layout constraints. Do NOT alter structural badge dimensions.
- Must be hidden on `ae_comp__badge_obj_view.svelte` when `show_qr` is falsy.
### 2. Badge Print Controls — UX Improvements (ae_comp__badge_print_controls.svelte)
- Consider: keyboard shortcuts (+ / -) for font sizing while a field is active
- Consider: "Apply to all badges" workflow for font size presets
### 4. Leads Module
Next major work after badge polish. See `documentation/MODULE__AE_Events_Leads.md` (if it
exists) for context. Exhibitor lead scanning via QR code at exhibitor booth → capture attendee
badge data, gated by `allow_tracking` on the badge.
---
## Implementation Status
### ⏳ TASK 4.0: Kiosk Editing — NOT STARTED (updated 2026-03-18)
Print page edit access needs to be opened to attendee-level permissions, not just trusted_access.
The permission model, field list, and `can_edit()` helper from `ae_comp__badge_review_form.svelte`
should be the reference. See Design Intent section above.
**Note (2026-03-18):** `style_href` and `duplex` are both fully implemented and verified in code —
the MODULE doc TODO list was stale. `duplex` is in `properties_to_save`; v2 badge render gates
`show_badge_back` on it. `style_href` loads via `<svelte:head>` in `print/+page.svelte`.
### ✅ TASK 4.1: Auto-Scaling Badge Text v2 — COMPLETE (2026-03-12)
**Files created/updated:**
- `src/lib/elements/action_fit_text.ts` — Svelte action
- `src/lib/elements/element_fit_text.svelte` — Component wrapper
- `src/routes/events/.../ae_comp__badge_obj_view.svelte` — V2 badge render (canonical)
Debug blocks gated behind `$ae_loc.edit_mode` (hidden in production).
- `print/+page.svelte` — Always uses v2 now. v1/v2 toggle removed. Header redesigned for kiosk UX.
- `ae_comp__badge_print_controls.svelte` — Identity card at top, pronouns moved to attendee section,
"Staff adjustments" divider before badge_type field.
- `print_list/+page.svelte` — Updated to import v2.
- `ae_comp__badge_obj_view.svelte` (v1) — **Moved to ~/tmp/agents_trash/**
**Kiosk UX improvements (2026-03-12):**
- Print page header: cleaner, shows name + "Ready"/"Printed N×" status chip, event name.
Header Print Now button removed (duplicate); only Re-print shortcut visible in trusted+edit mode.
- Controls right panel: identity card at top confirms who the badge belongs to before printing.
Pronouns field is now an attendee-level field (was trusted-only). Staff section labelled.
- Debug JSON blocks in v2 badge render hidden behind global edit_mode flag.
**Print page CSS centering work (also 2026-03-12) — ⚠️ Chromium PDF issue pending:**
Multiple iterations to center the badge on the printed page, working around SvelteKit layout
hierarchy issues. Current approach: `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%)`
on `.event_badge_wrapper`. Key findings:
- `#ae_main_content` has `overflow: auto` — Firefox (spec-compliant) won't let `display: contents`
dissolve it. Workaround: explicit passthrough block (`display: block; overflow: visible; width: 100%`).
- `app.css` global `overflow: hidden` on `html, body` creates a BFC that collapses body to badge-width.
Override with `overflow: visible !important` in print CSS.
- **Chrome ignores `@page { size }` for Save as PDF** — verified 2026-03-12. Firefox honors it.
Chrome uses the system default paper size; Firefox locks to the CSS `@page { size }` value.
Neither browser lets you change paper size in "Save to PDF" mode — only when printing to a
physical printer. For actual Epson/Zebra printing, the driver controls paper size; unaffected.
- **Chrome "Default" margins cause the "squish"** — Chrome's Default margin setting inserts
URL/date/page-number headers and footers into the printable area. These eat into the space
that `position: fixed; top: 50%` references, making the badge off-center or clipped. Fix:
set **Margins → None** (or Minimum) in Chrome's print dialog. Content centering verified
correct: Chrome A4 + None margins produces badge center = 297.5 pts on a 297.5 pt wide page.
Use Firefox for accurate PDF proofing.
- See `documentation/MODULE__AE_Events_Badge_Templates.md` → "Print Layout Architecture" for full
technical details.
### ✅ TASK 3: Badge Print Controls Panel — COMPLETE (2026-03-02)
**Files created/modified:**
- `ae_comp__badge_print_controls.svelte` — NEW. Right-edge control panel with per-field
accordion sections. Font size controls + inline edit forms gated by access level.
- `print/+page.svelte` — layout changed from `flex-row` to fixed right panel.
**Design decisions:**
- Controls panel is `position: fixed right-0 top-20 bottom-0 w-64` — out of normal flow,
always visible regardless of viewport width. `top-20` (80px) clears the page header.
- Badge area gets `pr-64` to prevent content from hiding under the fixed panel.
`print:pr-0` and `print:hidden` on the panel restore a clean print layout.
- `bg-white dark:bg-zinc-900` gives the panel a solid background to prevent bleed-through.
**Per-field accordion structure (one open at a time):**
| Field | Access | Font Controls |
| --- | --- | --- |
| Name | Trusted+ edit | ✅ |
| Professional Title | All auth edit | ✅ |
| Affiliations | All auth edit (textarea) | ✅ |
| Location | All auth edit | ✅ |
| Lead Scanning (allow_tracking) | All auth edit | — |
| Pronouns | Trusted+ edit | — |
| Badge Type | Trusted+, only when template has badge_type_list | — |
**Access level note:**
`is_trusted = $derived($ae_loc.trusted_access === true)` — covers Trusted, Administrator,
Manager, Super (cascade). No need to OR in `administrator_access`.
**badge_type_override coupling:**
When badge type is changed via dropdown, both `badge_type_code_override` AND
`badge_type_override` are saved together (name comes from template list). Same behavior in
`ae_comp__badge_obj_view.svelte` and `ae_comp__badge_review_form.svelte`.
Edge case: custom names (e.g. code=`member`, name=`"Life Member"`) must be set manually in DB.
**Font size config (moved from print page to controls component):**
| Field | Default px | Range | Step |
|--------------|------------|----------|------|
| Name | 58px | 2080px | 2px |
| Title | 34px | 1456px | 2px |
| Affiliations | 38px | 1460px | 2px |
| Location | 34px | 1456px | 2px |
Font sizes flow back to the parent via `$bindable()` props so `ae_comp__badge_obj_view`
stays in sync without prop-drilling through a third component.
---
### ✅ TASK 1: Badge Review Form — COMPLETE
The badge review form (`ae_comp__badge_review_form.svelte`) is now fully functional with:
- ✅ All editable fields with access-level gating
- ✅ Print status display section
- ✅ QR code generation and display (hover zoom + click expand)
- ✅ Options and Tickets fields (staff edit / attendee view)
- ✅ Save/Cancel with change detection
- ✅ Override field revert buttons
- ✅ **HTML rendering** for full_name, professional_title, affiliations, location
- ✅ **Accessibility toggle** for text enlargement (text-2xl ↔ text-4xl)
- ✅ **Help modal** with 6 sections of attendee guidance (Flowbite Modal component)
- ✅ Local edit mode (never writes to `$ae_loc.edit_mode`)
**Bug fixed (2026-02-27):** `default_authenticated_fields` and `default_trusted_fields` in
`review/+page.svelte` had incorrect field names causing `can_edit()` to silently drop saves.
Fixed to use exact names matching the form's `can_edit()` checks.
### ✅ TASK 2: Badge Print Font Controls — COMPLETE (v1)
Implemented in commit `3d7279da`. This is a **first draft** — auto font scaling using
mm/inch units is planned as a future iteration.
**What was built:**
- `ae_comp__badge_obj_view.svelte`: 4 new optional props (`font_size_name`, `font_size_title`,
`font_size_affiliations`, `font_size_location`, all `number` in px). When provided, replaces
auto inch-based Tailwind class sizing with an inline `font-size: Npx` style. Existing
auto-sizing behavior is completely unchanged when props are absent.
- `print/+page.svelte`: Screen-only (`print:hidden`) control panel with 4 rows, one per field.
Each row: label, `[]` button, value display (`58px` or `Auto`), `[+]` button, `[↺]` reset.
Step: 2px. `null` state = auto (uses existing inch-based auto-sizing). First `+` click
activates at a sensible default that approximates the current auto inch values.
**Default px values when first activated (≈ inch equivalents at 96dpi):**
| Field | Default px | Approx. inch | Range |
|--------------|------------|--------------|----------|
| Name | 58px | ≈ .60in | 2080px |
| Title | 34px | ≈ .35in | 1456px |
| Affiliations | 38px | ≈ .40in | 1460px |
| Location | 34px | ≈ .35in | 1456px |
**Future:** Auto font scaling using mm/inch units (physical paper stock measurements).
Will likely need to revisit the inch ↔ mm conversion and potentially expose the auto-sizing
logic as adjustable rather than replacing it with px overrides.
---
## Context
The Events Badges module is mostly complete for navigation and search. Two key pieces of
functional UI were needed before the first show:
1. **Badge Review Form** ✅ — `ae_comp__badge_review_form.svelte` now has complete field
rendering, edit inputs gated by access level, save/cancel API calls, and display-only
sections (QR code, print status, option/ticket checkmarks). Also includes accessibility
features (text enlargement) and help modal for attendee guidance.
2. **Badge Print Font Controls** ⏳ — The print page header needs screen-only controls
(hidden during `window.print()`) to bump font sizes for the name, professional title,
affiliations, and location sections before printing. These only affect the `ae_comp__badge_obj_view.svelte` render — not the page layout/template structural dimensions.
Read `documentation/MODULE__AE_Events_Badges.md` for full module context before starting.
---
## MANDATORY: Before You Start
1. Run `ae_describe event_badge` (MCP tool) to confirm which fields actually exist in the
DB. Several fields in the spec below may need to be added to `properties_to_save` in
`src/lib/ae_events/ae_events__event_badge.ts` if they are not already saved to IDB.
2. Fields to specifically confirm exist in `event_badge` schema:
- `pronouns`, `pronouns_override`
- `phone`, `phone_override`
- `allow_tracking`
- `agree_to_tc`
- `other_1_code` through `other_8_code` (the "option" fields)
- `ticket_1_code` through `ticket_8_code`
- `registration_type`, `registration_type_code`
- `registration_type_override`, `registration_type_code_override`
3. Run `npx svelte-check` before committing. Baseline is **77 errors** (all pre-existing,
none in the badge module files). Do not introduce new errors.
4. Do NOT write to `$ae_loc.edit_mode` from any badge component. This was a critical
bug (fixed 2026-02-27). See `documentation/AE__Permissions_and_Security.md`.
---
## TASK 1: Badge Review Form (HIGH PRIORITY)
### File to build
`src/routes/events/[event_id]/(badges)/badges/[badge_id]/ae_comp__badge_review_form.svelte`
This component is already imported and used by `review/+page.svelte`. Props it receives:
```typescript
interface Props {
event_id: string;
event_badge_id: string;
lq__event_badge_obj: any; // Svelte 5 store from liveQuery
can_edit_fields: string[]; // Which fields this user can edit
is_staff: boolean; // True if trusted_access or higher
log_lvl?: number;
}
```
`can_edit_fields` values:
- `['*']` — administrator (all fields)
- Array of field names — specific editable fields
- `[]` — read-only (shouldn't normally reach this component, but handle it)
### Helper
Use a helper derived inside the component:
```typescript
function can_edit(field: string): boolean {
return can_edit_fields.includes('*') || can_edit_fields.includes(field);
}
```
### Save / Cancel Pattern
Follow the Journals module pattern (`src/lib/ae_journals/`). Key points:
- Use `import { events_func } from '$lib/ae_events_functions'`
- Call `events_func.update_ae_obj__event_badge({ event_badge_id, event_id, data_kv })`
- Only send changed fields in `data_kv` (compare against `$lq__event_badge_obj` values)
- Show save/cancel buttons only when something has changed (`has_changes` derived)
- Show a success/error state briefly after save (1-2 seconds, then reset)
- Cancel resets local state back to `$lq__event_badge_obj` values
- Use `data-testid="badge-review-save-btn"` and `data-testid="badge-review-cancel-btn"`
### Save API Call
```typescript
await events_func.update_ae_obj__event_badge({
api_cfg: $ae_api, // from ae_loc store or passed as prop — check how ae_comp__badge_obj_view.svelte does it
event_badge_id: event_badge_id,
event_id: event_id,
data_kv: { /* only changed fields */ }
});
```
Check `ae_comp__badge_obj_view.svelte` for the existing save pattern — it already works
and can be used as reference.
---
### Section 1: Display-Only Status Bar (all access levels) ✅ IMPLEMENTED
Always show at top of form. Read-only. No edit controls.
```
Print Status: [Not yet printed] OR [Printed 3× — first: Jan 5 2026, last: Jan 5 2026]
```
**Implemented:** Shows print count with first/last print datetimes. Hidden if `print_count < 1`.
Uses `$lq__event_badge_obj.print_count`, `print_first_datetime`, `print_last_datetime`.
Format datetimes with `ae_util.iso_datetime_formatter(dt, 'datetime_iso_12_no_seconds')`.
Import `ae_util` from `$lib/ae_utils/ae_utils`.
---
### Section 2: QR Code (all access levels) ✅ IMPLEMENTED
Display the attendee's badge QR code. This is the same QR code shown on the printed badge
itself — scanning it at the badge station triggers automatic badge search and print.
**Implemented using `core_func.js_generate_qr_code()`:**
```typescript
qr_data_url = await core_func.js_generate_qr_code('obj', {
obj_type: 'event_badge',
obj_id: event_badge_id
});
```
**Features:**
- Hover: Zoom overlay effect (`qr_hovered` state)
- Click: Expand/collapse that pushes content down (`qr_expanded` state)
- Displays as data URL image from QR code generation
- Reactive: automatically regenerates when `event_badge_id` changes
---
### Section 3: Editable Fields ✅ IMPLEMENTED
Render each field as: read-only display when `!can_edit(field)`, or an `<input>` /
`<select>` / `<textarea>` when `can_edit(field)`.
Show `(overridden)` label next to override fields when the override value differs from
the base field value.
**HTML Rendering (implemented 2026-02-27):**
The following fields render HTML markup using `{@html}` when viewing (not when editing):
- `full_name_override` / `full_name`
- `professional_title_override` / `professional_title`
- `affiliations_override` / `affiliations`
- `location_override` / `location`
This allows for rich text formatting (bold, italic, line breaks, etc.) in badge displays.
**Accessibility Features (implemented 2026-02-27):**
- Text enlargement toggle button in sticky header
- Normal size: `text-2xl` on field values
- Enlarged size: `text-4xl` on field values
- Button shows visual feedback (gray → blue, "Larger" → "Normal" label)
- Applied consistently across all text fields
**Help Modal (implemented 2026-02-27):**
- Flowbite Modal component with 6 sections
- Sections: Reviewing Badge, Editing Info, Accessibility, QR Code, Lead Scanning, Assistance
- Triggered by Help button (BadgeQuestionMark icon) in sticky header
- Currently has placeholder text — can be customized per event/client
#### Attendee-Editable Fields (shown to all access levels with link)
| Field | Input Type | Notes |
|---|---|---|
| `pronouns_override` | text input | Fallback display: `pronouns` |
| `full_name_override` | text input | Fallback display: `full_name`; **renders HTML** when viewing |
| `professional_title_override` | text input | Fallback display: `professional_title`; **renders HTML** when viewing |
| `affiliations_override` | textarea | Fallback display: `affiliations`; **renders HTML** when viewing |
| `phone_override` | text input (tel) | Fallback display: `phone` |
| `location_override` | text input | Fallback display: `location`; **renders HTML** when viewing |
| `allow_tracking` | checkbox | Label: "Allow exhibitor lead scanning" |
| `agree_to_tc` | checkbox | Label: "I agree to the Terms and Conditions" + placeholder T&C text block |
#### Staff-Only Additional Fields (shown when `is_staff === true`) ✅ IMPLEMENTED
| Field | Input Type | Notes |
|---|---|---|
| `email_override` | email input | Fallback display: `email` |
| `badge_type_code_override` | select | Options: member, non-member, guest, exhibitor, staff, test; also updates `badge_type_override` text |
> **Edge case — custom badge type name:** If an attendee needs a standard badge type code (for
> CSS styling) but a slightly different displayed name (e.g. code=`member`, name=`"Life Member"`),
> set `badge_type_override` directly in the DB. Do **not** use the dropdown — selecting from the
> dropdown in the UI overwrites `badge_type_override` with the standard name from the template
> list. This is an intentional trade-off: coded for the normal case (dropdown keeps both fields in
> sync), special cases handled manually by Scott in the DB.
| `registration_type_code_override` | select | Same options as badge_type for now; also updates `registration_type_override` |
| `hide` | checkbox | Label: "Hidden from search results" |
| `priority` | number input | |
| `notes` | textarea | |
#### Staff-Only: Options & Tickets (read-edit, shown when `is_staff === true`) ✅ IMPLEMENTED
**Other/Options** (`other_1_code` through `other_8_code`):
- If field has a value: show as editable text input with label "Option X"
- If field is empty/null: show faintly as "Option X (empty)" — staff can still set it
- These represent event-specific add-ons or membership indicators
**Tickets** (`ticket_1_code` through `ticket_8_code`):
- Same pattern as options above, label "Ticket X"
#### Attendee-Only: Options & Tickets (display only) ✅ IMPLEMENTED
When `!is_staff` and the field has a value: show `[✓] Option X` or `[✓] Ticket X`.
When the field is empty: hide entirely (attendees don't see empty slots).
---
### Section 4: Terms & Conditions Block (all, only when `agree_to_tc` in can_edit_fields) ✅ IMPLEMENTED
Placeholder text for now:
```
By checking this box, I confirm that the information on my badge is correct to the best
of my knowledge. I agree that this badge may be used for identification purposes during
the event and that my attendance may be recorded by exhibitors using the lead scanning
feature if I permit it.
```
Show this before the `agree_to_tc` checkbox. If `agree_to_tc` is not in `can_edit_fields`,
hide the entire block.
---
### Field State Pattern (Svelte 5 runes)
```typescript
// Initialize local editable state from badge object
let local_full_name_override = $state($lq__event_badge_obj?.full_name_override ?? '');
let local_pronouns_override = $state($lq__event_badge_obj?.pronouns_override ?? '');
// ... etc for each editable field
// Detect changes
let has_changes = $derived(
local_full_name_override !== ($lq__event_badge_obj?.full_name_override ?? '')
|| local_pronouns_override !== ($lq__event_badge_obj?.pronouns_override ?? '')
// ... etc
);
// Build changed-fields-only payload
function build_save_payload(): Record<string, any> {
const payload: Record<string, any> = {};
if (local_full_name_override !== ($lq__event_badge_obj?.full_name_override ?? ''))
payload.full_name_override = local_full_name_override || null; // empty string → null
// ... etc
return payload;
}
```
**Important:** Empty string inputs should save as `null` (clears the override, falls back
to base field). Use `value || null` in the payload.
---
## TASK 2: Badge Print Font Size Controls (MEDIUM PRIORITY)
### Where to add
`src/routes/events/[event_id]/(badges)/badges/[badge_id]/print/+page.svelte`
Add a screen-only (`print:hidden`) control panel between the header and the badge render.
This panel lets staff adjust font sizes for the four text-heavy sections before clicking Print.
### Controls needed
```
Font Size Controls (screen only, hidden during print):
[Name] [] [14px] [+]
[Title] [] [12px] [+]
[Affiliations] [] [11px] [+]
[Location] [] [10px] [+]
```
- Start with sensible defaults (match what `ae_comp__badge_obj_view.svelte` currently uses)
- Min/max per field (e.g., 8px24px for name, 7px18px for others)
- Pass the sizes as props into `ae_comp__badge_obj_view`
### Props to add to `ae_comp__badge_obj_view.svelte`
`ae_comp__badge_obj_view.svelte` currently has internal font size logic. It needs to
accept optional override props:
```typescript
// New optional props:
font_size_name?: number; // px
font_size_title?: number; // px
font_size_affiliations?: number; // px
font_size_location?: number; // px
```
When these props are provided, use them instead of the internally computed sizes.
When not provided, fall back to existing auto-sizing behavior.
**IMPORTANT:** Do NOT touch structural dimensions (overall badge width/height, header/footer
sizes, template layout). Only the text content font sizes.
---
## Key Files
| File | Role |
|---|---|
| `[badge_id]/ae_comp__badge_review_form.svelte` | **BUILD THIS** — review form stub |
| `[badge_id]/ae_comp__badge_obj_view.svelte` | Badge render + print button; add font size props |
| `[badge_id]/print/+page.svelte` | Print page; add font size control panel |
| `[badge_id]/review/+page.svelte` | Review page; already wired, passes `can_edit_fields` |
| `src/lib/ae_events/ae_events__event_badge.ts` | API functions: `update_ae_obj__event_badge` |
| `src/lib/ae_events/db_events.ts` | Dexie schema — `properties_to_save` for badge |
| `src/lib/ae_utils/ae_utils.ts` | `ae_util.iso_datetime_formatter()` |
| `documentation/MODULE__AE_Events_Badges.md` | Full module reference |
| `documentation/AE__Permissions_and_Security.md` | Permission flags, edit_mode rules |
| `documentation/GUIDE__AE_API_V3_for_Frontend.md` | V3 API reference |
## Access Level Reference
```typescript
// From $ae_loc store (persisted localStorage)
$ae_loc.trusted_access // true = trusted and above (onsite staff)
$ae_loc.administrator_access // true = administrator and above
$ae_loc.edit_mode // boolean — user preference toggle (NEVER write to this from components)
```
`is_staff` prop on the review form = `$ae_loc.trusted_access`.
`trusted_access` is `true` for Trusted and every level above it (Administrator, Manager, Super)
— no need to OR in `administrator_access` since it's already implied by the cascade.
---
## Patterns to Follow
- **Canonical module reference:** `src/lib/ae_journals/` — most complete, most advanced
- **Svelte 5 runes:** `$state`, `$derived`, `$derived.by()`, `$effect` — no legacy `$:` syntax
- **Icons:** Lucide Svelte only — `import { Save, X, Check, ... } from 'lucide-svelte'`
- **No Font Awesome** (`fas fa-*`) anywhere in the badge module
- **Styling:** Tailwind CSS v4 + Skeleton UI utility classes (`btn`, `preset-tonal-*`, `input`, `card`)
- **Commits:** Atomic — one component per commit; run `npx svelte-check` before every commit
---
## What NOT to Do
- Do NOT touch `@page` CSS or badge template structural dimensions — print layout is out of scope
- Do NOT write to `$ae_loc.edit_mode` from any component
- Do NOT connect `mod_badges_json.edit_permissions` yet — hardcoded field lists are intentional for now
- Do NOT implement the email API — `send_review_email()` placeholder stays as `alert()`
- Do NOT add `person_passcode` DB field — out of scope for this sprint
---
## Testing
Run existing badge tests after any changes:
```bash
npm run test:unit
npx playwright test tests/events/badges/
```
Baseline: all badge tests passing as of 2026-02-26 (`f5e98b8c`).
Add `data-testid` attributes to key interactive elements:
- `badge-review-save-btn`
- `badge-review-cancel-btn`
- `badge-review-full-name-input`
- `badge-review-agree-to-tc-checkbox`

View File

@@ -0,0 +1,238 @@
# Project: Pres Mgmt Config Cleanup & Config UI
**Status:** Planning / Ready to Execute
**Priority:** High (BGH conference in ~2 weeks; only one active event using pres_mgmt)
**Created:** 2026-04-02
**Related:** `TODO__Agents.md`, `PROJECT__Stores_Svelte5_Migration.md`
---
## Background
The `event.mod_pres_mgmt_json` config grew organically across several conferences
(LCI, BGH, etc.) and has accumulated serious inconsistencies:
- Mixed `show__` and `hide__` prefixes for the same concepts
- Some features have BOTH `show__foo` and `hide__foo` keys active simultaneously
- Duplicate keys with different names (`file_purpose_option_kv` = `file_purpose_option_li`)
- Dead config (`HOLD__*` prefix)
- Type inconsistency (`label__person_external_id: false` vs `"LCI member ID"` string)
- Keys in the DB not consumed by `sync_config__event_pres_mgmt()`
- Bug: `label__session_poc_name_short` is read then immediately overwritten (line 970-972 in ae_events__event.ts)
- `hide_launcher_link` / `hide_launcher_link_legacy` missing the `__` separator (inconsistent)
- `show_content__presentation_description` uses a third naming convention
- Admin must edit DB records directly to change config — error-prone
The local config (`events_loc.pres_mgmt`) is also tangled into the main `events_loc`
persisted store which is part of the paused Svelte 5 migration.
---
## Goals
1. **Canonical config schema** — define a TypeScript interface for `mod_pres_mgmt_json`
2. **Consistent naming convention** — one rule for all `show__`/`hide__` keys
3. **New Svelte 5 store** — break out local pres_mgmt config from `events_loc`
4. **Config UI** — admin page within pres_mgmt to manage the remote config
5. **No more direct DB edits** for routine pres_mgmt configuration
---
## Convention Decision
**Rule: the prefix reflects the default state.**
| Prefix | Default | Use for |
|--------|---------|---------|
| `hide__` | `false` = visible | Features ON by default that can be turned off |
| `show__` | `false` = hidden | Features OFF by default that can be turned on |
**Never have both `show__foo` and `hide__foo` for the same concept.**
- Visibility controls (codes, descriptions, POC, biography) → default visible → `hide__`
- Opt-in features (access links, launcher, QR links) → default hidden → `show__`
---
## Canonical Remote Config Schema
`PressMgmtRemoteCfg` — the authoritative TypeScript interface for `event.mod_pres_mgmt_json`:
```typescript
interface PressMgmtRemoteCfg {
// System
lock_config: boolean; // true = force remote→local sync (prevent user overrides)
// Labels (event-specific terminology overrides)
label__person_external_id: string | null; // default: 'External ID'
label__presenter_external_id: string | null; // default: 'External ID'
label__session_poc_type: string | null; // e.g. 'champion', 'poc'
label__session_poc_name: string | null; // e.g. 'Champion', 'Point of Contact'
// Codes (visible by default — hide to suppress)
hide__location_code: boolean;
hide__presentation_code: boolean;
hide__presenter_code: boolean;
hide__session_code: boolean;
// Session fields (visible by default)
hide__session_description: boolean;
hide__session_location: boolean;
hide__session_msg: boolean;
hide__session_poc: boolean;
hide__session_poc_biography: boolean;
hide__session_poc_profile_pic: boolean;
// Presenter fields
hide__presenter_biography: boolean;
// Presentation fields
hide__presentation_datetime: boolean;
hide__presentation_description: boolean; // replaces show_content__presentation_description
// Opt-in features (hidden by default — show to enable)
show__copy_access_link: boolean;
show__email_access_link: boolean;
show__launcher_link: boolean;
show__launcher_link_legacy: boolean;
// Requirements
require__presenter_agree: boolean;
require__session_agree: boolean;
// Navigation/UI constraints
limit__navigation: boolean;
limit__options: boolean;
// File upload config
file_purpose_option_kv: Record<string, {
name: string;
disabled?: boolean;
hidden?: boolean;
}> | null;
// Report visibility (key = report slug, value = true to hide)
hide__report_kv: Record<string, boolean>;
}
```
### Keys Removed vs. Current DB Records
| Removed Key | Reason |
|-------------|--------|
| `file_purpose_option_li` | Duplicate of `file_purpose_option_kv` |
| `HOLD__file_os_selection_option` | Dead/held feature |
| `hide__copy_access_link` | Conflicts with `show__copy_access_link` — use `show__` |
| `hide__email_access_link` | Conflicts with `show__email_access_link` — use `show__` |
| `hide__launcher_link` | Conflicts with `show__launcher_link` — use `show__` |
| `hide__launcher_link_legacy` | Conflicts with `show__launcher_link_legacy` — use `show__` |
| `hide__report_li` | Superseded by `hide__report_kv` |
| `show__navigation` | Ambiguous — covered by `limit__navigation` |
| `label__session_poc_name_short` | Was a bug — never applied (overwritten immediately) |
| `show_content__presentation_description` | Renamed to `hide__presentation_description` |
---
## New Svelte 5 Local Store
**Do NOT touch `events_loc` or the paused Svelte 5 migration.**
Instead, create a standalone store for pres_mgmt local config.
**File:** `src/lib/stores/ae_events_stores__pres_mgmt.svelte.ts`
```typescript
import { PersistedState } from 'runed';
import { pres_mgmt_loc_defaults } from './ae_events_stores__pres_mgmt_defaults';
export const pres_mgmt_loc = new PersistedState('ae_pres_mgmt_loc', pres_mgmt_loc_defaults);
// Usage: pres_mgmt_loc.current.hide__session_code
```
- New localStorage key: `ae_pres_mgmt_loc` (separate from `ae_events_loc`)
- Version gate: add `AE_PRES_MGMT_LOC_VERSION` to `store_versions.ts`
- `sync_config__event_pres_mgmt()` writes to `pres_mgmt_loc.current` directly
Consumer syntax change:
```
BEFORE: $events_loc.pres_mgmt.hide__session_code
AFTER: pres_mgmt_loc.current.hide__session_code
```
---
## Config UI Page
**Route:** `/events/[event_id]/(pres_mgmt)/pres_mgmt/config/`
**Access:** `$ae_loc.manager_access` only
**Button visibility:** Edit mode only (`$ae_loc.edit_mode`)
### Page behavior
- Loads `event.mod_pres_mgmt_json` fresh from API on page open
- Displays grouped form sections (see below)
- Save = load → merge → PATCH `/v3/crud/event/{event_id}` with `{ mod_pres_mgmt_json: updated }`
- The existing settings form at `/events/[id]/settings` has its pres_mgmt section removed or replaced with a link
### Form sections (grouped)
1. **System**`lock_config`
2. **Labels**`label__*` fields (text inputs, nullable)
3. **Session Visibility**`hide__session_*` toggles
4. **Presenter Visibility**`hide__presenter_*` toggles
5. **Presentation Visibility**`hide__presentation_*` toggles
6. **Code Visibility**`hide__*_code` toggles
7. **Opt-in Features**`show__*` toggles
8. **Requirements**`require__presenter_agree`, `require__session_agree`
9. **Navigation Limits**`limit__navigation`, `limit__options`
10. **File Purpose Config**`file_purpose_option_kv` (JSON editor or structured form)
11. **Report Visibility**`hide__report_kv` (key-value toggles)
---
## Migration Path
Safe and backward compatible — old DB records fall through to `?? false` defaults.
1. No DB migration script needed — old keys are simply ignored by the updated sync function
2. Active events (BGH) get updated via the new UI after it's built
3. The `sync_config__event_pres_mgmt()` rewrite is the critical step — it must handle the
canonical keys and clean defaults before the UI ships
---
## Implementation Steps
- [ ] **Step 1** — Define `PressMgmtRemoteCfg` TypeScript interface (new file or in `ae_events__event.ts`)
- [ ] **Step 2** — New `ae_events_stores__pres_mgmt.svelte.ts` with `PersistedState`; add version gate to `store_versions.ts`
- [ ] **Step 3** — Rewrite `sync_config__event_pres_mgmt()` in `ae_events__event.ts` to use canonical keys and write to the new store
- [ ] **Step 4** — Build config UI page at `(pres_mgmt)/pres_mgmt/config/+page.svelte` (manager_access + edit_mode gated)
- [ ] **Step 5** — Strip `ae_comp__event_settings_pres_mgmt_form.svelte` from settings page (or replace with a link to new page)
- [ ] **Step 6** — Migrate all `$events_loc.pres_mgmt.*` references in pres_mgmt templates to `pres_mgmt_loc.current.*`
- [ ] **Step 7** — Update BGH (and any other active events) via new UI
- [ ] **Step 8**`npx svelte-check` clean; commit
### Step 6 scope (mechanical find-replace)
The `$events_loc.pres_mgmt` pattern appears across:
- `ae_comp__event_session_obj_li.svelte`
- `ae_comp__events_menu_opts.svelte`
- `session/[session_id]/+page.svelte`
- `session/[session_id]/session_view.svelte`
- `session/[session_id]/session_page_menu.svelte`
- `locations/locations_page_menu.svelte`
- `reports/+page.svelte`
- `pres_mgmt/+page.svelte`
- (and likely others — run `grep -r 'events_loc.pres_mgmt' src/` to get full list)
---
## Notes
- The `lock_config: true` default means most events will always sync from remote.
This is intentional — it prevents presenter laptops from drifting into different configs.
- `file_purpose_option_kv` may need a structured editor (not raw JSON) to be usable.
Consider a simple key-value form row per purpose type for Phase 2.
- QR link keys (`hide__presenter_qr_link`, `hide__session_qr_link`) appeared in LCI config
but are not in the canonical schema above. Evaluate whether they're actively used before
adding them back.
- `limit__navigation` and `limit__options` are in the DB but not currently read by
`sync_config__event_pres_mgmt()`. Confirm where they're consumed before adding to sync.

View File

@@ -0,0 +1,51 @@
# Project Plan: Aether AE Obj Field Editor v3 (Consolidated)
> **Status:** 🟡 Mostly Complete — Phase 3 items + GUIDE update remaining
> **Date:** February 13, 2026 (last updated: 2026-03-20)
> **Target Component:** `src/lib/elements/element_ae_obj_field_editor.svelte`
> **Replaces:** `element_ae_crud.svelte` and `element_ae_crud_v2.svelte`
## 1. Overview
Consolidate the legacy CRUD components into a single, high-performance "Aether Object Field Editor" (v3). This component will be the standard for single-property editing across the platform, fully aligned with the FastAPI V3 CRUD patterns and Svelte 5 Runes.
## 2. Strategic Objectives
- **Consolidation:** Retire `v1` and `v2` components in favor of a single, unified codebase.
- **API Alignment:** Native support for `PATCH /v3/crud/{obj_type}/{obj_id}`.
- **Svelte 5 Runes:** Pure `$props`, `$state`, and `$derived` implementation. No legacy imports.
- **Callback Pattern:** Replace `createEventDispatcher` with callback props (`on_patch`, `on_success`, `on_error`).
- **Iconography:** Standardize on **Lucide-Svelte**.
- **Mobile-First:** Improved "Tap to Edit" targets and mobile-responsive popovers.
## 3. Implementation Phases
### Phase 1: Foundation & Reactivity (COMPLETED)
- [x] Create the new `v3` component shell.
- [x] Implement strict TypeScript interface for Props.
- [x] Use `$state` for local "draft" values to prevent reactivity loops with the global store.
- [x] Implement the `handle_patch` logic using the central `api.patch` helper.
### Phase 2: UI & UX Refinement (COMPLETED)
- [x] Standardize Tailwind classes (using Tailwind 4 patterns).
- [x] Implement "Edit Mode" awareness (syncing with `$ae_loc.edit_mode`).
- [x] Add a "Save" loading state with Lucide's `LoaderCircle` spinner.
- [x] Implement a clear "Cancel" path that restores the original value.
### Phase 3: Field Type Parity (IN PROGRESS)
- [x] Support `text`, `textarea`, `select`, `tiptap`, and `checkbox`.
- [x] Add `datetime` support using native browser pickers — `date` and `datetime-local` inputs implemented.
- [ ] Implement searchable dropdowns for the `select` type.
### Phase 4: Migration & Cleanup
- [x] Create a playground route for V3 verification (`/testing/ae_obj_field_editor`).
- [x] Deprecate and remove `v1` and `v2` files — `element_ae_crud.svelte` and `element_ae_crud_v2.svelte` removed 2026-03-20.
- [ ] Update `GUIDE__Development.md` with the new usage patterns.
## ⚠️ Security & Reliability Stabilization (NEW)
- [x] **Account Context:** Fixed 403 errors by unifying API helpers to the `/v3/crud/` standard.
- [x] **Race Conditions:** Implemented `localStorage` scavenging for Account IDs to fix Svelte 5 hydration lags.
- [x] **Protocol Hygiene:** Purged redundant/misplaced headers (`x-aether-api-token`, `Access-Control-Allow-Origin`).
## 4. Maintenance & Standards
- Component must respect `$ae_loc.trusted_access` for visibility of edit triggers.
- Always use `type="button"` for internal actions to prevent form collisions.
- Maintain the `object_reload` pattern for SWR cache invalidation.

View File

@@ -0,0 +1,408 @@
# PROJECT: Site Passcode Security — API-Verified Auth
**Last updated:** 2026-04-10
**Status:** Backend work in progress — frontend pending backend completion
**Priority:** High — passcodes for trusted/administrator access currently in localStorage plaintext
---
## Problem Statement
When a user loads the Aether frontend, the site bootstrap response includes `access_code_kv_json` — a JSON object containing all passcodes for all access levels (administrator, trusted, public, authenticated). The frontend stores this verbatim in `$ae_loc.site_access_code_kv`, which is persisted in localStorage.
**Result:** Anyone with DevTools → Application → Local Storage can see every passcode for every access level on any Aether site. For public/authenticated this is low risk, but for trusted and administrator this is a real exposure — these passcodes can grant control over event data, badge printing, edit mode, etc.
The passcode check (`handle_check_access_type_passcode` in `e_app_access_type.svelte`) is entirely local — it reads the cached values and compares directly. No API call is made. The backend already has a `/authenticate_passcode` endpoint that verifies server-side, but it needs the fixes described below before the frontend can rely on it.
### Source of Truth
`site.access_code_kv_json` is the single source of truth for all passcodes. The `v_site_domain` DB view joins this field from the site table — there is no separate copy. Both the bootstrap response and `/authenticate_passcode` read from the same data.
---
## Threat Model
| Threat | Current | After Fix |
|---|---|---|
| Attacker inspects localStorage | Sees all passcodes in plaintext | Sees a JWT (opaque, no passcode) |
| Attacker uses stolen trusted passcode | Trivial if they have localStorage access | Still possible if they enter the passcode — unavoidable |
| Attacker replays an old passcode after it changes | Works forever (cached value never refreshes) | Fails — API verifies against current DB value |
| Attacker tampers with `access_type` in localStorage | Grants apparent permission but API calls still fail | Same — `access_type` is still persisted separately |
| Passcode reuse across sessions | Works indefinitely | JWT TTL enforces session expiry per role |
| Offline / API-unavailable entry | Works (local cache) | **Blocked** — requires API to verify |
### The fundamental constraint
Passcode-based access is inherently weaker than username/password login with a hashed credential. The system's security model layers passcode access below user login, and API calls themselves are still gated by `x-aether-api-key` + `x-account-id`. The passcode primarily controls **what the frontend shows** and some API-level permission gates for trusted routes.
---
## Proposed Solution: API-Verified Passcode + JWT Session
### Core idea
1. **Never send passcodes to the client.** The frontend stops reading/storing `access_code_kv_json` from the bootstrap response.
2. **Passcode entry triggers an API call** to `/authenticate_passcode`. API verifies server-side against the DB.
3. **On success, the API returns a JWT** — the JWT contains the role, account context, and expiry.
4. **Store the JWT in `$ae_loc.jwt`** (already a field, already wired into `$ae_api`).
5. **On page reload**, check the JWT's `eat` (expires-at) claim locally (base64 decode, no signature verification needed client-side). If expired, drop to anonymous. If valid, `access_type` is already persisted in `$ae_loc`.
### Session restore on reload
- `access_type` still persists in localStorage (no change here)
- The JWT is the **proof** that the access was legitimately granted and is still valid
- On page load: decode JWT payload (base64 the middle segment), check `eat` vs `Date.now()/1000`
- If JWT expired → reset `access_type` to anonymous, clear JWT
- If JWT valid → no action needed, `access_type` is already correct
This gives session expiry without a network call on every page load.
---
## TTL Per Role — Decided
| Access Level | JWT TTL | Notes |
|---|---|---|
| `super` | 8 hours | Highest privilege |
| `manager` | 24 hours | |
| `administrator` | 48 hours | |
| `trusted` | 48 hours | Onsite staff — covers multi-day events |
| `public` | 24 hours | |
| `authenticated` | 12 hours | |
| `anonymous` | N/A | No passcode |
---
## Caching Decision
**No passcode caching.** Every passcode entry makes one API call. The JWT handles session persistence — no passcode ever touches localStorage. Performance impact is only at the moment of entry (~50150ms), which is acceptable for a once-per-session action.
---
## Backend Changes Required
**Note:** The backend fixes described below have been implemented and tested in the `aether_api_fastapi` repository (the `/authenticate_passcode` endpoint now uses explicit role priority, returns a full passcode JWT with `auth_type: 'passcode'`, applies per-role TTLs, and validates passcode length). Frontend changes can proceed once the backend deployment with these fixes is available.
### Backend Agent Follow-Up
If the backend team revisits this area, keep the next round focused on narrowing escape hatches rather than adding new ones:
1. Audit every `x-no-account-id` use and decide whether it is still required for bootstrap, public delivery, or a global-default fallback.
2. Prefer JWT-backed auth once a session exists; do not add new transport-level bypass paths for authenticated UI flows.
3. Mark any remaining bypass-only helper as temporary and add a removal target.
4. Plan the eventual removal of `access_code_kv_json` from public bootstrap payloads once passcode auth is fully deployed.
### Frontend special-case endpoints to review
These are the current frontend-facing exceptions that the backend work should assume are special-cased. None require a frontend/client code change today, but some are intentionally temporary.
| Frontend path / helper | Status | Notes |
| --- | --- | --- |
| `src/routes/+layout.ts` | Keep | Bootstrap site-domain lookup before account context is known. |
| `src/routes/manifest.webmanifest/+server.ts` | Keep | Public PWA branding lookup; bootstrap key only. |
| `src/lib/ae_core/ae_core__site.ts` | Keep | Cache-first site-domain bootstrap path. Still a bootstrap-only special case. |
| `src/lib/ae_api/api_get__data_store.ts` + `src/lib/ae_core/core__data_store.ts` + `src/lib/elements/element_data_store.svelte` | Temporary | Global-default fallback. Target state is JWT-backed account-scoped access only. |
| `src/lib/ae_core/ae_core_functions.ts` | Remove candidate | Legacy site-domain helper with forced no-account scope. |
| `src/routes/testing/+page.svelte` | Dev-only | Useful for trace testing; do not add to any production allowlist. |
**Phase 2 status:** Not started — removing `access_code_kv_json` from the public site model remains pending.
**File:** `aether_api_fastapi/app/routers/api.py`
The `/authenticate_passcode` endpoint exists and is structurally correct but has four issues that must be fixed before the frontend migrates to using it.
### Fix 1: Passcode matching must use explicit priority order
**Current (wrong):**
```python
for role, code in access_codes.items(): # dict insertion order — not guaranteed
if str(code) == str(passcode):
matched_role = role
break
```
**Required:**
```python
ROLE_PRIORITY = ['super', 'manager', 'administrator', 'trusted', 'public', 'authenticated']
matched_role = None
for role in ROLE_PRIORITY:
code = access_codes.get(role)
if code and str(code) == str(passcode):
matched_role = role
break
```
This ensures that if a config mistake causes two roles to share a passcode, the higher-privilege role always wins. It also makes the intent explicit and independent of JSON storage order.
### Fix 2: JWT payload must include all six role flags
**Current (incomplete):**
```python
payload = {
'account_id': account_id_random,
'administrator': (matched_role == 'administrator'),
'manager': (matched_role == 'manager'),
'super': (matched_role == 'super'),
# trusted / public / authenticated missing
...
}
```
**Required:**
```python
payload = {
'account_id': account_id_random,
'super': (matched_role == 'super'),
'manager': (matched_role == 'manager'),
'administrator': (matched_role == 'administrator'),
'trusted': (matched_role == 'trusted'),
'public': (matched_role == 'public'),
'authenticated': (matched_role == 'authenticated'),
'json_str': json.dumps({
'auth_type': 'passcode', # distinguishes from user login JWTs
'site_id': site_id,
'role': matched_role # canonical role string — frontend uses this
})
}
```
The `auth_type: 'passcode'` marker is critical — it allows the frontend and any future backend consumers to distinguish a passcode JWT from a user login JWT.
### Fix 3: Per-role TTL
**Current:**
```python
token = sign_jwt(
secret_key=settings.JWT_KEY,
ttl=3600 * 24, # hardcoded 24h for all roles
**payload
)
```
**Required:**
```python
ROLE_TTL = {
'super': 8 * 3600, # 8 hours
'manager': 24 * 3600, # 24 hours
'administrator': 48 * 3600, # 48 hours
'trusted': 48 * 3600, # 48 hours
'public': 24 * 3600, # 24 hours
'authenticated': 12 * 3600, # 12 hours
}
token = sign_jwt(
secret_key=settings.JWT_KEY,
ttl=ROLE_TTL[matched_role],
**payload
)
```
### Fix 4: Add minimum length validation to `passcode` field
**Current:**
```python
passcode: str = Field(..., description="The passcode to verify")
```
**Required:**
```python
passcode: str = Field(..., min_length=5, description="The passcode to verify")
```
This matches the frontend's 5-character trigger and prevents empty/trivial submissions.
### Complete corrected endpoint (for reference)
```python
ROLE_PRIORITY = ['super', 'manager', 'administrator', 'trusted', 'public', 'authenticated']
ROLE_TTL = {
'super': 8 * 3600,
'manager': 24 * 3600,
'administrator': 48 * 3600,
'trusted': 48 * 3600,
'public': 24 * 3600,
'authenticated': 12 * 3600,
}
class PasscodeAuthRequest(BaseModel):
"""Request model for site-based passcode authentication."""
site_id: str = Field(..., description="Random string ID of the site")
passcode: str = Field(..., min_length=5, description="The passcode to verify")
@router.post('/authenticate_passcode', response_model=Resp_Body_Base)
async def authenticate_passcode(
auth_req: PasscodeAuthRequest,
response: Response = Response,
):
"""
Passcode-to-JWT Endpoint.
Verifies a passcode against site.access_code_kv_json (single source of truth —
v_site_domain joins from the same site record).
Returns a signed JWT with the site's account context, full role flags, and
a per-role TTL. The jwt.json_str.auth_type='passcode' field distinguishes
this token from a user login JWT.
"""
site_id = auth_req.site_id
passcode = auth_req.passcode
# 1. Look up the site record
search_data = {'id_random': site_id}
if record := sql_select(table_name='site', data=search_data):
# 2. Parse access codes
access_codes_raw = record.get('access_code_kv_json')
access_codes = {}
if access_codes_raw:
try:
access_codes = json.loads(access_codes_raw) if isinstance(access_codes_raw, str) else access_codes_raw
except Exception as e:
log.error(f"Failed to parse access_code_kv_json for site {site_id}: {e}")
# 3. Verify passcode in explicit priority order (highest privilege wins)
matched_role = None
for role in ROLE_PRIORITY:
code = access_codes.get(role)
if code and str(code) == str(passcode):
matched_role = role
break
if matched_role:
log.info(f"Auth Success: Verified '{matched_role}' passcode for site {site_id}")
# 4. Resolve account context
account_id_random = record.get('account_id_random')
if not account_id_random:
if account_id_int := record.get('account_id'):
account_id_random = get_id_random(record_id=account_id_int, table_name='account')
# 5. Mint JWT with complete role flags and per-role TTL
payload = {
'account_id': account_id_random,
'super': (matched_role == 'super'),
'manager': (matched_role == 'manager'),
'administrator': (matched_role == 'administrator'),
'trusted': (matched_role == 'trusted'),
'public': (matched_role == 'public'),
'authenticated': (matched_role == 'authenticated'),
'json_str': json.dumps({
'auth_type': 'passcode',
'site_id': site_id,
'role': matched_role
})
}
token = sign_jwt(
secret_key=settings.JWT_KEY,
ttl=ROLE_TTL[matched_role],
**payload
)
return mk_resp(
data={'jwt': token, 'account_id': account_id_random, 'role': matched_role},
response=response
)
else:
log.warning(f"Auth Failed: Invalid passcode for site {site_id}")
return mk_resp(data=False, status_code=401, response=response, status_message="Invalid passcode.")
else:
log.warning(f"Auth Failed: Site {site_id} not found.")
return mk_resp(data=False, status_code=404, response=response, status_message="Site not found.")
```
### Backend Phase 2 (follow-up — not blocking frontend)
**Remove `access_code_kv_json` from the `Site_Domain_Base` response model** (`site_domain_models.py`). This ensures passcodes are never sent to the client even if future code reads from the bootstrap. Requires confirming no other endpoint consumers rely on `access_code_kv_json` being in the base response before making this change.
---
## Frontend Changes Required
**These depend on the backend fixes above being deployed first.**
### 1a. `src/lib/app_components/e_app_access_type.svelte`
Replace `handle_check_access_type_passcode` entirely. The new version:
- Is `async`
- Adds `auth_pending: boolean = $state(false)` and `auth_error: string | null = $state(null)`
- Uses a direct `fetch` call (NOT `post_object` — avoids triggering the session-expired banner on a 401)
- On success: sets `$ae_loc.access_type = data.role`, stores `$ae_loc.jwt = data.jwt`, triggers `process_permission_check` as before
- On 401: shows inline error, clears `entered_passcode`, resets `checked_passcode = null` to allow retry
- On network error: shows inline connection error
- Clears `auth_error` when `entered_passcode` changes
API call shape:
```http
POST /authenticate_passcode
Content-Type: application/json
x-aether-api-key: <from $ae_api.headers['x-aether-api-key']>
Body: { site_id: $ae_loc.site_id, passcode: entered_passcode }
```
Add to template (near the passcode input):
```svelte
{#if auth_pending}
<Loader size="1em" class="animate-spin text-gray-400" />
{/if}
{#if auth_error}
<span class="text-error-500 text-xs">{auth_error}</span>
{/if}
```
### 1b. `src/routes/+layout.ts`
**Stop caching passcodes from bootstrap** — remove line ~394:
```ts
// ae_loc_init['site_access_code_kv'] = json_data.access_code_kv_json || {};
```
**Add passcode JWT expiry check** — after the block around line 84 where `ae_loc_json.jwt` is read, add:
```ts
// Enforce passcode JWT TTL on page load.
// Decodes the JWT payload (base64, no secret needed) and resets access to anonymous if expired.
// User login JWTs (auth_type !== 'passcode') are left untouched.
if (ae_loc_json?.jwt) {
try {
const parts = ae_loc_json.jwt.split('.');
if (parts.length === 3) {
const jwt_payload = JSON.parse(atob(parts[1]));
const json_str = typeof jwt_payload.json_str === 'string'
? JSON.parse(jwt_payload.json_str)
: jwt_payload.json_str;
if (json_str?.auth_type === 'passcode' && jwt_payload.eat < Date.now() / 1000) {
// Passcode JWT has expired — revoke access
ae_loc_json.jwt = null;
ae_loc_json.access_type = 'anonymous';
}
}
} catch {
// Malformed JWT — leave untouched, let existing handling deal with it
}
}
```
### 1c. `src/lib/stores/ae_stores__auth_loc_defaults.ts` (cleanup)
Remove `site_access_code_kv` from the `AuthLocState` interface and the `auth_loc_defaults` object. The field is unused after 1a. Confirm no other component reads from it first (current grep: only `e_app_access_type.svelte` uses it — confirmed).
---
## Migration Notes
- Users with existing localStorage will still have `site_access_code_kv` cached — this is harmless after the frontend stops reading it. No forced cache clear needed.
- Existing persisted `access_type` is unaffected — users keep their current session level until their JWT expires or they manually clear storage.
- The `$ae_loc.jwt` field is already used by the user login flow. The `auth_type: 'passcode'` marker in `json_str` ensures the expiry logic only targets passcode sessions, not user login sessions.
---
## Files Affected
| File | Repo | Change |
| --- | --- | --- |
| `app/routers/api.py` | `aether_api_fastapi` | **Backend — do first.** Priority ordering, full JWT payload, per-role TTL, min_length on passcode |
| `app/models/site_domain_models.py` | `aether_api_fastapi` | Phase 2: remove `access_code_kv_json` from public model |
| `src/lib/app_components/e_app_access_type.svelte` | `aether_app_sveltekit` | Replace local check with async API call; loading/error UI |
| `src/routes/+layout.ts` | `aether_app_sveltekit` | Stop caching passcodes; add JWT expiry check |
| `src/lib/stores/ae_stores__auth_loc_defaults.ts` | `aether_app_sveltekit` | Cleanup: remove `site_access_code_kv` |
| `documentation/AE__Permissions_and_Security.md` | `aether_app_sveltekit` | Update passcode auth section to reflect new flow |

View File

@@ -0,0 +1,411 @@
# PROJECT: Aether App — Comprehensive Style Review
**Status:** Phase 1 & 2 Complete — Phase 3 Deferred (post-April 2026 conference)
**Priority:** Medium
**Created:** 2026-03-13
**Updated:** 2026-03-16
**Related:** `src/app.css`, `src/routes/+layout.svelte`, `documentation/AE__UI_Component_Patterns.md`
---
## 1. Objective
Audit and unify the visual design system across all Aether modules. The goal is consistent:
- Color token usage (Skeleton `preset-*` / `surface-*` / semantic tokens)
- Button and interactive element styling
- Dark mode handling
- Typography hierarchy
- Layout patterns (cards, lists, tables, modals, banners)
- Icon system (Lucide only)
This review covers all modules and their distinct use cases. Changes must be sequenced to avoid breaking live-production systems (Events Launcher, IDAA).
### Scope of Modules
| Module | Routes | Primary Users | Notes |
|---|---|---|---|
| Core / App Shell | `+layout.svelte`, `e_app_sys_bar.svelte` | All users | Foundation — impacts everything |
| Core Admin | `/core/` | OSIT staff / managers | Manager-only section |
| Journals | `/journals/` | Authenticated users | Canonical/frontier model |
| Events — General | `/events/`, `/events/[id]/` | Event staff, attendees | Hub page |
| Events — Pres Mgmt | `/events/[id]/(pres_mgmt)/` | Event coordinators | Operations tool |
| Events — Launcher | `/events/[id]/(launcher)/` | AV/tech staff (Electron kiosk) | ⚠️ Live production — April 2026 conference |
| Events — Badges | `/events/[id]/(badges)/` | Registration desk staff | On-site kiosk use |
| Events — Leads | `/events/[id]/(leads)/` | Exhibitor booth staff | On-site kiosk use |
| IDAA | `/idaa/` | IDAA members (iframe) | ⚠️ Privacy-critical, iframe context |
---
## 2. Current State: Dual-Generation Problem
The codebase has two parallel style generations that co-exist:
| Era | Pattern | Where Used |
|---|---|---|
| **Modern** | `preset-tonal-*`, `preset-filled-*`, Lucide icons, Svelte 5 runes | `sys_bar`, Journals, IDAA dashboard |
| **Legacy** | `variant-soft-*`, `variant-filled-*`, FontAwesome `fas fa-*` icons | Events routes (most), some Core |
The Journals module is the canonical reference for modern patterns — when in doubt, match it.
---
## 3. Color Token Architecture
Three overlapping systems are in use. The goal is to consolidate to two:
### ✅ System 1: Skeleton Semantic Tokens (keep, expand usage)
Used for colored/semantic elements.
```
primary, secondary, tertiary, success, warning, error, surface
```
Usage: `preset-tonal-primary`, `bg-primary-500/10`, `text-error-500`, `border-warning-500`
### ✅ System 2: Plain Tailwind Grayscale (keep for neutrals)
Used for neutral backgrounds, borders, and text. Predictable across all Skeleton themes.
```
gray-50/100/200/300/400/500/600/700/800/900
```
Usage: `bg-gray-50 dark:bg-gray-900`, `border-gray-200 dark:border-gray-700`
### ❌ System 3: Hardcoded RGB/HSL (eliminate)
```
bg-orange-600/90 — root layout banner
hsla(0, 100%, 50%, .5) — journal entry eye icon
rgb(243 244 246) — form dark mode hacks in <style> blocks
```
These are brittle, not theme-aware, and create maintenance debt.
### Decision Rule
> **Semantic color needed?** → Use Skeleton token (`preset-*`, `text-primary-*`, etc.)
> **Neutral background/border/text?** → Use `gray-*` with `dark:` pair
> **Hardcoded color?** → Replace with token. No exceptions.
---
## 4. Dark Mode Architecture
### Current Setup (already correct in app.css)
```css
@custom-variant dark (&:where(.dark, .dark *)); /* Tailwind v4 class-based dark mode */
html.dark { color-scheme: dark; } /* Native controls follow app theme */
html.light { color-scheme: light; }
```
### Gap: Skeleton Form Classes Lack Dark Mode
Skeleton's `.input`, `.select`, `.textarea` classes do not include dark mode styles. This causes white text on white backgrounds in dark mode. Currently patched with inline `<style>` blocks per-component (see `e_app_sys_bar.svelte` lines 693707).
**Fix:** Global utility in `app.css` (see Phase 1, Step 1). Once added, remove per-component patches.
---
## 5. Button System
### Standard: `preset-*` (Skeleton v4 pattern) ✅
```html
<button class="btn preset-tonal-secondary">Secondary</button>
<button class="btn btn-sm preset-filled-primary">Primary</button>
<button class="btn preset-outlined-surface">Outlined</button>
```
> **Note:** All legacy `variant-*` classes have been fully removed from the codebase. Use only `preset-*` classes for all buttons and interactive elements.
### Custom `ae_btn_*` Classes (app.css)
These exist in `app.css` and wrap the `preset-*` system. They are valid but underused. Consider adopting where button groups need reuse.
---
## 6. Icon System
**Standard:** Lucide (`@lucide/svelte`) — SVG, tree-shakeable, consistent stroke weight
**Legacy:** FontAwesome (`fas fa-*` / `far fa-*`) — CSS class-based, heavier
FontAwesome is still imported (likely via global CSS). Goal: complete removal from all new work; migrate existing usage to Lucide progressively.
### FontAwesome → Lucide Reference Map (events module)
| FontAwesome | Lucide Component | Notes |
|---|---|---|
| `fa-cogs` | `Settings` | Settings/config |
| `fa-chart-line` | `TrendingUp` | Reports/charts |
| `fa-map-marked-alt` | `MapPinned` | Location with map |
| `fa-map-marker-alt` | `MapPin` | Location marker |
| `fa-tools` | `Wrench` | Tools/maintenance |
| `fa-search` | `Search` | Search |
| `fa-chalkboard-teacher` | `PresentationIcon` or `GraduationCap` | Presenter |
| `fa-plane` | `Plane` | Travel/remote |
| `fa-sync-alt fa-spin` | `RefreshCw` + CSS animation | Spinner |
| `fa-arrow-up` | `ArrowUp` | Up arrow |
| `fa-arrow-down` | `ArrowDown` | Down arrow |
| `fa-list-ol` | `ListOrdered` | Ordered list |
| `fa-file-csv` | `FileSpreadsheet` | CSV file |
| `fa-toggle-on` | `ToggleRight` | Toggle |
| `fa-calendar-alt` | `CalendarDays` | Calendar |
| `fa-exclamation-triangle` | `TriangleAlert` | Warning |
| `fa-lock` | `Lock` | Locked |
| `fa-unlock` | `Unlock` | Unlocked |
| `fa-eye` | `Eye` | Visible |
| `fa-eye-slash` | `EyeOff` | Hidden |
| `fa-trash` | `Trash2` | Delete |
| `fa-edit` / `fa-pencil` | `Pencil` | Edit |
| `fa-plus` | `Plus` | Add |
| `fa-times` / `fa-close` | `X` | Close/remove |
| `fa-check` | `Check` | Confirm |
| `fa-ban` | `Ban` | Denied/blocked |
| `fa-user` | `User` | Person |
| `fa-users` | `Users` | Group |
| `fa-tag` | `Tag` | Tag/label |
| `fa-print` | `Printer` | Print |
| `fa-download` | `Download` | Download |
| `fa-upload` | `Upload` | Upload |
| `fa-copy` | `Copy` | Copy |
| `fa-qrcode` | `QrCode` | QR code |
| `fa-id-card` | `IdCard` | Badge/ID |
| `fa-file-alt` | `FileText` | File |
| `fa-compress-arrows-alt` | `Minimize2` | Collapse |
| `fa-expand` | `Maximize2` | Expand |
| `fa-angle-right` | `ChevronRight` | Nav arrow |
| `fa-angle-down` | `ChevronDown` | Accordion |
| `fa-clock` | `Clock` | Time |
### Lucide Usage Pattern
```svelte
<script>
import { Settings, Search, MapPin } from '@lucide/svelte';
</script>
<!-- Decorative icon with adjacent label -->
<Settings size="1em" class="shrink-0" aria-hidden="true" />
<!-- Icon-only button — MUST have aria-label -->
<button aria-label="Settings" class="btn btn-sm preset-tonal-surface">
<Settings size="1.1em" />
</button>
```
---
## 7. Typography
### Standard Hierarchy
```html
<!-- Page title -->
<h1 class="text-3xl sm:text-4xl font-black tracking-tight">Title</h1>
<!-- Section heading -->
<h2 class="text-xl font-bold">Section</h2>
<!-- Card/item heading -->
<h3 class="text-lg font-semibold">Item</h3>
<!-- Label / eyebrow -->
<span class="text-xs font-bold uppercase tracking-wide opacity-40">Label</span>
<!-- Muted secondary text -->
<span class="text-sm opacity-60">Secondary info</span>
<!-- Hint / placeholder text -->
<span class="text-xs opacity-40 italic">Hint</span>
```
### Rule: Opacity Over Fixed Colors for Muted Text
```html
<!-- ✅ Theme-aware muted text -->
<span class="text-sm opacity-60">Note</span>
<!-- ❌ Fixed muted text — breaks in dark mode -->
<span class="text-sm text-gray-500">Note</span>
```
> **Exception:** `text-gray-*` is acceptable in components that intentionally use plain Tailwind grayscale for neutrality (e.g., Journals list cards), as long as `dark:text-gray-*` counterpart is always included.
---
## 8. Card & Layout Patterns
See `documentation/AE__UI_Component_Patterns.md` for the full pattern reference.
Key standard patterns:
- **List item card:** `border border-gray-200 dark:border-gray-700 border-l-4 border-l-primary-500/40` with hover intensification
- **Content card:** `rounded-lg border border-surface-200-800 bg-surface-50-900 px-4 py-3`
- **Glow accent:** `absolute -inset-1 bg-linear-to-r from-primary-500 to-secondary-500 rounded-2xl blur opacity-25 dark:opacity-40 group-hover:opacity-60 transition duration-1000 group-hover:duration-200 pointer-events-none`
- **Empty state:** `preset-tonal-warning p-6 rounded-xl` centered, with icon + heading + description
---
## 9. Accessibility Rules
- **Never remove focus rings.** `focus:ring-0` on text inputs fails WCAG 2.1 AA. Use `focus:ring-2 focus:ring-primary-500` instead.
- **Icon-only buttons must have `aria-label`.** No exceptions.
- **Decorative icons must have `aria-hidden="true"`.** This applies to ALL FontAwesome `<span class="fas ...">` elements too.
- **Color is not the only status indicator.** Pair color with text or icon shape.
- **Form labels must be explicit.** Use `<label for="...">` or `aria-label`.
---
## 10. Module-by-Module Status
### Core / App Shell
| Item | Status | Notes |
|---|---|---|
| Root layout banners (offline, expired) | ✅ Done | `bg-orange-600/90``preset-tonal-warning` (2026-03-16) |
| `e_app_sys_bar.svelte` | ✅ Modern | Best-practice reference component |
| `e_app_theme.svelte` | 🟡 Legacy | Redundant with sys_bar theme section; keep but no new work |
| `core/+layout.svelte` | ✅ Done | `variant-*``preset-*` (2026-03-16) |
| `core/+page.svelte` | ✅ Good | Excellent card grid template |
| All `/core/` files (21 files) | ✅ Done | `variant-*``preset-*`, FA → Lucide (2026-03-16) |
### Journals
| Item | Status | Notes |
|---|---|---|
| Overall | ✅ Canonical reference | Use as template for all new work |
| `ae_comp__journal_obj_li.svelte` | ✅ Excellent | Card pattern, icon sizing, hover states |
| `ae_comp__journal_entry_obj_li.svelte` | ✅ Done | `bg-slate-*``bg-gray-*`; hardcoded HSL → Tailwind tokens (2026-03-16) |
| `ae_comp__journal_entry_header.svelte` | ✅ Done | `focus:ring-0` restored to `focus:ring-2` (2026-03-16) |
### Events — General
| Item | Status | Notes |
|---|---|---|
| `events/+page.svelte` | ✅ Done | FA → Lucide; `variant-ghost-surface``preset-outlined-surface` (2026-03-16) |
| `events/+layout.svelte` | ✅ Done | FA → Lucide (spinners, arrows) (2026-03-16) |
| `events/ae_comp__events_menu_nav.svelte` | ✅ Done | FA → Lucide (2026-03-16) |
| `events/[id]/+page.svelte` | ✅ Done | FA → Lucide; `variant-*``preset-*` (2026-03-16) |
| `events/ae_comp__event_file_obj_tbl.svelte` | ✅ Done | FA → Lucide (2026-03-16) |
| `events/ae_comp__event_presentation_obj_li.svelte` | ✅ Done | FA → Lucide (2026-03-16) |
| Lucide inline flow | ✅ Done | Global `svg.lucide { display: inline }` rule in `app.css` (2026-03-16) |
### Events — Pres Mgmt
| Item | Status | Notes |
| --- | --- | --- |
| All 24 pres_mgmt files | ✅ Done | FA → Lucide; `variant-*``preset-*` (2026-03-16) |
| Card styling for session/presenter lists | 🔒 Phase 3 | Deferred to post-April 2026 |
### Events — Launcher ⚠️ Live Production
| Item | Status | Notes |
| --- | --- | --- |
| FA → Lucide | ✅ Done | All FA spans were already in HTML comments — launcher is clean (verified 2026-03-16) |
| `variant-*` | ✅ Done | The `variant-soft-secondary` at line 329 is inside a comment block — no live variants remain |
| Card styling / UX polish | 🔒 Phase 3 | Deferred to post-April 2026 conference |
### Events — Badges
| Item | Status | Notes |
| --- | --- | --- |
| FA → Lucide | ✅ Done | All badge files migrated (2026-03-16) |
| `badge_upload_form.svelte` | ✅ Done | `variant-*``preset-*` (2026-03-16) |
| `badge_template_form.svelte` | ✅ Done | `variant-*``preset-*` (2026-03-16) |
| `code_to_html` | ✅ Refactored | FA HTML string dict → `code_to_icon` Lucide component map (2026-03-16) |
### Events — Leads
| Item | Status | Notes |
| --- | --- | --- |
| FA → Lucide | ✅ Done | All leads files migrated (2026-03-16) |
| `ae_comp__exhibit_signin.svelte` | ✅ Done | `variant-*``preset-*` (2026-03-16) |
| `ae_comp__lead_qr_scanner.svelte` | ✅ Done | `variant-*``preset-*` (2026-03-16) |
| `ae_tab__add.svelte` | ✅ Done | `variant-*``preset-*` (2026-03-16) |
### IDAA ⚠️ Privacy-Critical
| Item | Status | Notes |
| --- | --- | --- |
| `(idaa)/+page.svelte` | ✅ Modern | Semantic tokens, good access gate |
| FA CDN | ✅ Scoped | Moved from `app.html``idaa/+layout.svelte` `<svelte:head>` (2026-03-16) |
| Archives, BB, Recovery Meetings | 🔒 Deferred | Full style review deferred to Phase 3 |
| **All IDAA FA → Lucide** | 🔒 Last priority | Review only after non-IDAA modules are complete |
---
## 11. Issues Ranked by Priority
| # | Severity | Issue | Location | Phase |
|---|---|---|---|---|
| 1 | 🔴 A11y | `focus:ring-0` removes focus indicator on journal name input | `ae_comp__journal_entry_header.svelte:108` | 1 |
| 2 | 🔴 Maintenance | No global dark mode form fix — per-component patches scattered | `e_app_sys_bar.svelte` lines 693707; others | 1 |
| 3 | 🟡 Consistency | FontAwesome icons throughout events module | 61 event files | 12 |
| 4 | 🟡 Consistency | `variant-*` buttons used instead of `preset-*` | Events, Badges, Leads routes | 12 |
| 5 | 🟡 Theme | Hardcoded `bg-orange-600/90` on root layout offline banner | `+layout.svelte` | 2 |
| 6 | 🟡 Theme | Hardcoded HSL colors on journal entry eye icon | `ae_comp__journal_entry_obj_li.svelte` | 2 |
| 7 | 🟢 Consistency | `bg-slate-*` used in journal entry instead of `bg-gray-*` | `ae_comp__journal_entry_obj_li.svelte` | 2 |
| 8 | 🟢 Polish | Pres Mgmt pages lack card styling (bare `<ul>` lists) | `pres_mgmt/+page.svelte` and sub-pages | 3 |
| 9 | 🟢 Polish | Root layout banner uses direct `font-semibold text-white` instead of preset | `+layout.svelte` | 2 |
---
## 12. Implementation Plan
### Phase 1: Quick Wins (current)
**Step 1 — Global form dark mode utility** ✅ Target: `src/app.css`
Add a global utility so Skeleton `.input`, `.select`, `.textarea` classes render correctly in dark mode. This eliminates all per-component `<style>` patches.
**Step 2 — FontAwesome → Lucide in events nav/layout files**
Scope: Non-Launcher, non-IDAA files only. Priority order:
1. `src/routes/events/ae_comp__events_menu_nav.svelte` — top-level navigation
2. `src/routes/events/+layout.svelte` — spinner and sort icons
**Step 3 — Standardize `variant-*` → `preset-*` in events**
Scope: `+page.svelte`, `settings/+page.svelte`, `sign_in_out.svelte`
Skip: Launcher files (frozen), IDAA files (deferred)
---
### Phase 2: Consolidation
- Replace hardcoded banner color in root layout (`bg-orange-600/90``preset-tonal-warning`)
- Fix journal entry eye icon hardcoded HSL colors
- Fix `bg-slate-*` inconsistency in journal entry
- Migrate `variant-*` in remaining events files: Pres Mgmt, Badges, Leads
- Remove per-component `<style>` dark mode patches (now covered by global utility)
- Add responsive typography to events hub and pres mgmt pages
---
### Phase 3: Module Refactors (post-April 2026 conference)
- Events Launcher: FontAwesome → Lucide, `variant-*``preset-*`
- Events Pres Mgmt: Card styling for session/presenter lists
- IDAA: Full style review (Archives, BB, Recovery Meetings)
- Create `.prose-journal` utility in app.css to centralize markdown prose overrides
---
## 13. Files to Modify (Phase 1)
| File | Change |
|---|---|
| `src/app.css` | Add global `.dark` form element utility |
| `src/routes/events/ae_comp__events_menu_nav.svelte` | Replace `fas fa-*` with Lucide imports |
| `src/routes/events/+layout.svelte` | Replace `fas fa-sync-alt fa-spin` and arrow icons with Lucide |
| `src/routes/events/+page.svelte` | Replace `variant-ghost-surface``preset-outlined-surface` |
| `src/routes/events/[event_id]/settings/+page.svelte` | Replace `variant-filled-secondary/primary``preset-*` |
| `src/routes/events/[event_id]/sign_in_out.svelte` | Replace `variant-soft-warning``preset-tonal-warning` |
---
## 14. Testing Notes
- Run `npx svelte-check` after every file change
- Test dark mode toggle after form utility added — confirm inputs render correctly in dark
- Test light mode — confirm no regressions
- Events nav: verify all Lucide icons render at correct size and with correct meaning
- Launcher: do not touch — verify no unintended changes via `git diff`
- IDAA: do not touch — verify no unintended changes via `git diff`
---
## 15. What We Are NOT Changing (Phase 1)
- Events Launcher files — frozen until post-April 2026 conference
- All IDAA files — deferred to last phase
- Root layout banner hardcoded color — Phase 2
- Journal entry HSL eye icon colors — Phase 2
- Pres Mgmt card styling — Phase 3

View File

@@ -0,0 +1,189 @@
# Aether Journals UI Update (2026)
> **Status:** 🚧 Phase 4 Active (Security/Encryption Blockers remain; Journal Entry config rework in progress)
> **Last Updated:** 2026-05-05
> **Primary Agent:** Frontend SvelteKit Agent
## 1. Project Overview
This document outlines the modernization of the Journals module UI in the SvelteKit frontend (`aether_app_sveltekit`). The primary goals are to fully leverage the generic V3 API architecture and introduce high-velocity productivity features for journal management.
**Context:** The backend transition to the generic `api_crud` router is complete. Custom legacy routers have been removed. The frontend must now fully align with this pattern and provide a frictionless user experience.
---
## 2. Core Objectives
### 🎯 Primary Goals
1. **V3 API Verification:** Ensure all CRUD operations utilize the generic `api_crud` endpoints (Verified).
2. **Quick Add UI:** Implement a specialized interface for rapid, friction-free entry creation.
3. **Append/Prepend UI:** Allow users to quickly add text to the beginning or end of existing entries without full edit mode.
4. **Interop & Portability:** Robust import/export logic for Markdown/HTML (Nextcloud Notes compatibility).
5. **Security Hardening:** Review and harden client-side encryption logic (BLOCKED).
---
## 3. Technical Architecture
### Backend (Completed)
* **Router:** `api_crud` (Generic)
* **Definitions:** `app/ae_obj_types_def.py` -> `app/object_definitions/journals.py`
* **Endpoints:** `/v3/crud/journal/...` and `/v3/crud/journal_entry/...`
### Frontend (In Progress)
* **State Management:** `src/lib/ae_journals/ae_journals_stores.ts`
* **Local Storage:** Dexie.js (`db_journals`)
* **API Client:** `src/lib/api/api.ts` -> `get_ae_obj`
* **Export Engine:** Centralized templates in `src/lib/ae_journals/ae_journals_export_templates.ts`.
---
## 4. Feature Specifications
### ⚡ Quick Add (Complete)
* **Component:** `src/routes/journals/ae_comp__journal_entry_quick_add.svelte`
* **Behavior:** Creates a new `journal_entry` attached to the active journal without leaving the list view.
### 📝 Append / Prepend (Complete)
* **Interaction:** Fast text injection via `AeCompModalJournalEntryAppend`.
* **Logic:** Updates entry content without full editor state overhead.
### 🔄 Interop (Markdown/HTML) (Complete)
* **Goal:** Bulk export/import for data portability.
* **Templates:** Standard, Personal Log, Amazon Vine.
---
## 5. Implementation Plan
### Phase 1: Foundation (Done)
- [x] Backend cleanup (remove legacy routers).
- [x] Verify frontend uses V3 API (`ae_journals__journal.ts`).
### Phase 2: Rapid Entry (Complete)
- [x] Create `ae_comp__journal_entry_quick_add.svelte`.
- [x] Integrate Quick Add into `+page.svelte`.
### Phase 3: Content Manipulation & Portability (Complete)
- [x] Implement Append/Prepend logic.
- [x] Implement Bulk Export/Import system.
- [x] Establish centralized Export Template engine.
### Phase 4: Polish & Security (ACTIVE)
- [x] Implement Auto-Save toggle and visual status indicators.
- [x] Extract decryption workflow to non-reactive helper.
- [x] **Standardize Configuration Modals:** Refactored Module, Journal, and Entry configuration into a unified tabbed UI.
- [x] **Journal Entry Config cleanup:** Summary now lives in Metadata; Alert lives in its own Alerts & Messaging section; Privacy Flags is visibility-only; Admin controls are split out and gated to trusted-access and above.
- [x] **Shared Flags widget:** `AE_Object_Flags` now shows visible button text and hover titles instead of icon-only controls.
- [x] **Modal sizing:** Entry config modal now expands to viewport height instead of stopping at a fixed 60vh body cap.
- [x] **Delete/Remove behavior:** Entry config Admin section now uses the real delete helper. Managers/admins see Delete (hard delete); trusted access sees Remove (disable semantics).
- [x] **RESOLVED:** Decryption workflow stability (Fixed via dependency isolation).
- [x] **Style Standardization (2026-03-06):** Full Skeleton v4 `preset-*` class pass across all 17 journal components. See style token table in Lessons Learned below.
- [x] **Dark mode fixes:** Entry content hover, journal view section/description background and text colors.
- [x] **Modal close button:** All 3 config modals use `dismissable={false}` + explicit `<X>` button in header snippet for correct right-aligned placement.
- [x] **Global select padding:** Added `padding-inline: 0.5rem` to `@layer base` in `app.css` (safe — utility `px-*` classes override it where intentional).
- [ ] Solidify E2EE passcode system for Journals and Entries.
- [ ] Audit encryption flow for Quick Added and Imported entries.
- [ ] Integrate Outbound Email sharing.
---
## 🧠 Lessons Learned: Solving the Svelte 5 Reactivity Hang
During the implementation of the Privacy/Decryption toggle and the new Configuration Modals, we encountered critical browser hangs caused by infinite reactivity loops. Here is how we resolved them:
### 1. Rigorous Dependency Isolation (`untrack`)
Svelte 5 runes (`$effect`, `$derived`) automatically track **every** reactive variable read inside them.
* **The Problem:** An effect would read `save_status` or `tmp_entry_obj.content` to decide if it should sync, but the act of syncing would update those same variables, re-triggering the effect.
* **The Fix:** Wrap any "check-only" state or store reads in `untrack(() => ... )`. This allows the effect to use the value without becoming a dependency of it. This is **CRITICAL** when initializing local state from props inside an effect.
### 2. Standardized Modal UI ("Aether Orange") & Style Token Conventions
We have established a unified design language for configuration interfaces and all Journals UI. **Use these as the module template.**
#### Modal Chrome
* **Header/Footer:** `bg-orange-100 dark:bg-orange-900` with consistent orange borders.
* **Close button:** Always use `dismissable={false}` on the `<Modal>` and add an explicit `<button>` with `<X>` inside the `{#snippet header()}` so placement is fully in our control. The `flex-1` class on the `<h3>` pushes it right.
* **Tabs:** Center-aligned `btn btn-sm` with `preset-filled-primary` (active) / `preset-tonal-surface` (inactive).
* **Icons:** Every tab and primary action should have a Lucide icon for better scannability.
* **Button titles:** Any button that uses icon+text or icon-only must include a descriptive `title` for hover clarity.
* **Explicit Persistence:** Follow "Edit working copy → Save Changes" pattern to prevent accidental store/API churn.
#### Skeleton v4 Style Token Reference (Journals = canonical example)
| Intent | Class |
|---|---|
| Primary CTA (save, create, open) | `btn preset-filled-primary` |
| Neutral / cancel / close | `btn preset-tonal-surface` |
| Secondary action | `btn preset-tonal-secondary` |
| Success (confirmed save) | `btn preset-filled-success` |
| Warning (caution action) | `btn preset-tonal-warning hover:preset-filled-warning-500` |
| Error / danger (delete, force reset) | `btn preset-tonal-error hover:preset-filled-error-500` |
| Warning action (remove/disable) | `btn preset-tonal-warning hover:preset-filled-warning-500` |
| Active tab | `preset-filled-primary` |
| Inactive tab | `preset-tonal-surface` |
| Icon button | `btn-icon btn-icon-sm preset-tonal-surface` |
| Input (base) | `input` |
| Input (small) | `input input-sm` |
| Select (base) | `select` |
| Select (small) | `select select-sm` |
| Textarea | `textarea` |
| Badge (info/neutral) | `badge preset-tonal-surface` |
| Badge (success) | `badge preset-tonal-success` |
| Badge (error) | `badge preset-tonal-error` |
#### Removed Patterns (never use)
- All `variant-*` classes are now fully removed from the codebase. Use only `preset-*` classes for all buttons and interactive elements.
- `variant-form-material` — Skeleton v2, removed from all inputs/selects/textareas
- `input-bordered` — non-standard, removed
- DaisyUI `modal` / `modal-box` / `modal-action` wrapper divs inside Flowbite `<Modal>` — removed
#### Dark Mode Rules
- Any `bg-{color}-100` dynamic background **must** have a `dark:bg-gray-800` (or similar) override — light shades are unreadable in dark mode.
- Hover states on content areas need both light and dark variants: `hover:bg-blue-100 dark:hover:bg-blue-950`.
- Text locked to `dark:text-gray-900` is almost always wrong — use `dark:text-gray-100`.
### 3. Dexie LiveQuery Subscriptions
* **The Problem:** Accessing `liveQuery` observables directly in templates results in `[object Object]` or `undefined` property errors.
* **The Mandate:** ALWAYS use the `$` prefix (e.g. `$lq__obj`) when passing or using data from a Dexie `liveQuery`.
### 4. Manual Deep Copying vs. Proxies
Svelte 5 state is backed by Proxies.
* **The Problem:** Using `JSON.parse(JSON.stringify(proxy))` can sometimes trigger unexpected behavior or loops when used inside a reactive context.
* **The Fix:** Implement a manual `deep_copy` helper or selective property assignment when syncing "Original" vs "Temporary" state. This ensures `orig_entry_obj` is a plain JS object, making the `has_unsaved_changes` check stable.
### 5. Journal Entry Config Layout Notes
The Entry Config modal now follows a stricter section grammar:
* `Metadata` contains category, tags, summary, archive date, and template.
* `Status & Security` contains enabled/hidden/priority/sort.
* `Visibility & Audience` contains only visibility/audience toggles.
* `Alerts & Messaging` contains alert flag + alert message.
* `Admin` is gated to trusted access and above, and is the only place for notes plus delete/remove actions.
### 3. Concurrency Locking (`is_processing`)
* **The Problem:** Decryption (Async) and Auto-Save (Debounced Async) can fire nearly simultaneously.
* **The Fix:** Use a simple `is_processing` boolean flag. If any async workflow is active, block others from starting and prevent the `has_unsaved_changes` derived rune from reporting `true`.
### 4. Comparison Normalization
* **The Problem:** Trivial differences (e.g., `null` vs `""` or trailing whitespace) would trigger "unsaved changes" and fire the save loop.
* **The Fix:** Use a `normalize()` function in the `has_unsaved_changes` derived rune to trim strings and treat `null/undefined` as empty strings during comparison.
---
## ⚠️ Technical Blocker: The "Decryption-Sync" Loop (Resolved)
### The Issue
The component is suffering from a **Reactive Feedback Loop** between decryption, auto-save, and background IDB refreshes.
1. **Decrypting content** triggers a change in `tmp_entry_obj.content`.
2. The **Auto-Save effect** sees this as a manual user edit and saves to the database.
3. **Dexie LiveQuery** detects the DB update and refreshes the object.
4. The **Sync effect** resets the entry to its encrypted state (from DB).
5. The **Auto-Decryption effect** fires again, starting the loop over.
### What we tried:
* **`is_processing` flags:** Attempted to block reactivity during decryption.
* **`untrack()`:** Attempted to isolate store updates.
* **Reference Sync:** Attempted to update `orig_entry_obj` simultaneously with decryption to fool the change detector.
* **Direct Store Updates:** Switching from property assignment to `journals_sess.update()` to fix Svelte 5 notification failures.
### Future Fix Ideas:
1. **Native Svelte 5 State:** Refactor `journals_sess` from a Svelte 4 `Writable` to a class using `$state`.
2. **Logic Extraction:** Move decryption logic into a non-reactive class/helper to isolate side effects from the UI render cycle.
3. **Hash Comparison:** Use content hashes for change detection instead of string comparisons to avoid whitespace/normalization loops.

View File

@@ -0,0 +1,97 @@
# Project: Svelte 4 Store → Svelte 5 State Migration
**Status:** Execution / Phase B (In Progress)
**Priority:** High (post-April 2026 conference)
**Created:** 2026-03-30
**Related:** `TODO__Agents.md` — [Stores] Svelte 5 State Migration entry
---
## Background
All core Aether stores (`ae_loc`, `idaa_loc`, `ae_events_loc`, etc.) are being migrated from
Svelte 4 stores to Svelte 5 `$state` using the `runed` library's `PersistedState`. This provides
fine-grained reactivity, ensuring that effects only re-run when specific fields they access are
updated, rather than on every write to the store object.
### Phase B Progress & Learnings (Updated 2026-03-30)
1. **Dependency Installed**: `runed` is now a project dependency.
2. **Module Resolution Strategy**:
- Core store files renamed: `ae_stores.ts``ae_stores.svelte.ts`, etc.
- **Critical Discovery**: SvelteKit and CLI tools (`svelte-check`) struggled to resolve
extension-less imports (like `$lib/stores/ae_stores`) when only `.svelte.ts` existed.
- **Solution**: Created `.ts` wrapper files (e.g., `src/lib/stores/ae_stores.ts`) that
simply re-export everything via `export * from './ae_stores.svelte'`. This maintains
backward compatibility for all existing import paths without manual updates.
3. **API Confirmation**: Confirmed that `runed`'s `PersistedState` uses `.current` to access
the state object, matching the intended migration syntax.
4. **Mass Replacement**:
- `$ae_loc``ae_loc_v5.current`
- `$idaa_loc``idaa_loc_v5.current`
- `$events_loc``events_loc_v5.current`
- These replacements have been applied across the entire `src/` directory (~2000+ sites).
5. **Import Updates**: A robust Python script was used to surgically add `ae_loc_v5`,
`idaa_loc_v5`, and `events_loc_v5` to existing import blocks, avoiding `import type`
lines and duplicates.
---
## Syntax Changes: Before / After
### Store declaration
```typescript
// BEFORE (ae_stores.svelte.ts)
export const ae_loc: Writable<key_val> = persisted('ae_loc', defaults);
// AFTER (ae_stores.svelte.ts)
export const ae_loc_v5 = new PersistedState('ae_loc', defaults);
```
### Reading/Writing (Consumers)
```svelte
<!-- BEFORE -->
{$ae_loc.theme_mode}
{#if $ae_loc.trusted_access}
<!-- AFTER -->
{ae_loc_v5.current.theme_mode}
{#if ae_loc_v5.current.trusted_access}
```
### Batch Update Pattern
```typescript
// Use Object.assign for multi-field updates to maintain fine-grained reactivity
Object.assign(ae_loc_v5.current, { theme_mode: 'dark', edit_mode: true });
```
---
## Implementation Notes
### Prettier & Formatting
Because the mass migration touches ~250 files, formatting may be inconsistent (especially in
import blocks). It is recommended to run `npm run format` (if available) or rely on standard
linting after the imports are stabilized.
### Batching vs. "Big Sweep"
While the original plan suggested smaller batches, the global nature of `ae_loc` and the
hundreds of interdependent files made a "Big Sweep" more efficient to ensure the app remains
in a consistent state. However, verification should still be done module-by-module.
---
## Current Status (Pause Point)
- [x] Phase A: Plan written.
- [x] Phase B: Core infrastructure setup (runed, renames, wrappers).
- [x] Phase B: Mass variable replacement ($ae_loc -> ae_loc_v5.current).
- [/] Phase B: Mass import update (In Progress/Verifying).
- [ ] Phase B: Final validation (`svelte-check` clean).
**Do NOT stage or commit until `npx svelte-check` is fully verified.**
The app currently has a high error count due to the transition period where imports are being
re-aligned. Final verification is the next step after the pause.

View File

@@ -0,0 +1,121 @@
# Project: CRUD V3 Final Migration
> **Status:** 🟡 Surgical Cleanup (90% Complete — Events Module Fully Migrated)
> **Last Updated:** 2026-05-21
> **Goal:** Eliminate all dependency on legacy API wrappers (`create_ae_obj_crud`, `get_ae_obj_id_crud`, etc.) and ensure 100% adoption of the V3 Standard (`/v3/crud/...`).
---
## 1. Executive Summary
While the **Journals** and **Identity (User/Account)** modules have been successfully migrated to the V3 architecture, a significant portion of the **Events**, **Sponsorships**, and **IDAA** modules still rely on legacy V1/V2 wrappers. This document serves as the master checklist to reach 100% V3 compliance.
**Why this matters:**
* **Security:** V3 enforces strict multi-tenant isolation via JWT.
* **Maintenance:** Legacy wrappers in `api.ts` contribute to technical debt and "God Object" anti-patterns.
* **Performance:** V3 offers optimized search and partial updates (PATCH) that legacy endpoints lack.
---
## 2. Migration Audit (Findings)
The following files have been identified as using legacy CRUD wrappers.
### 🔴 High Priority: Events Module
- [x] `src/lib/ae_events/ae_events__event_session.ts` (Migrated 2026-01-30)
- [x] `src/lib/ae_events/ae_events__event_presenter.ts` (Migrated 2026-01-30)
- [x] `src/lib/ae_events/ae_events__event_presentation.ts` (Migrated 2026-01-30)
- [x] `src/lib/ae_events/ae_events__event_location.ts` (Migrated 2026-01-30)
- [x] `src/lib/ae_events/ae_events__event_badge_template.ts` (Migrated 2026-01-30)
- [x] `src/lib/ae_events/ae_events__event_device.ts` (Migrated 2026-01-30)
- [x] `src/lib/ae_events/ae_events__exhibit.ts` (Migrated 2026-01-28)
- [x] `src/lib/ae_events/ae_events__event_file.ts` (Migrated 2026-01-30)
### 🟠 Medium Priority: Core & Sponsorships
Legacy patterns persisting in core logic and config modules.
- [ ] `src/lib/ae_sponsorships/ae_sponsorships_functions.ts`
- [x] `src/lib/ae_core/core__hosted_files.ts` (Migrated 2026-01-20)
- [x] `src/lib/ae_core/core__site.ts` (Migrated 2026-01-26)
- [ ] `src/lib/ae_core/core__site_domain.ts` (STILL USES `get_ae_obj_id_crud` for bootstrap)
- [ ] `src/lib/ae_core/ae_core_functions.ts` (STILL USES `get_ae_obj_id_crud` / `update_ae_obj_id_crud`)
- [ ] `src/lib/ae_core/core__country_subdivisions.ts`
- [ ] `src/lib/ae_core/core__time_zones.ts`
- [ ] `src/lib/ae_core/core__countries.ts`
### 🟡 Low Priority: UI Components & Routes
Specific UI components that make direct API calls instead of using store functions.
- [ ] `src/lib/elements/element_data_store.svelte` (Direct `create_ae_obj_crud`)
- [x] `src/lib/elements/element_data_store_v2.svelte`
- [ ] `src/routes/events/[event_id]/event_page_menu.svelte`
- [x] `src/routes/events/[event_id]/(pres_mgmt)/session/ae_comp__event_session_alert.svelte` (Migrated to `update_ae_obj`)
- [ ] `src/routes/events/ae_comp__event_session_obj_li.svelte`
- [ ] `src/routes/idaa/(idaa)/recovery_meetings/ae_idaa_comp__event_obj_id_edit.svelte`
- [ ] `src/routes/events/[event_id]/(pres_mgmt)/presenter/[presenter_id]/ae_comp__event_presenter_form_agree.svelte` (STILL USES `update_ae_obj_id_crud`)
---
## 3. Migration Procedure
For each file listed above, follow this standard refactoring pattern:
1. **Imports:**
* Remove imports of `create_ae_obj_crud`, `update_ae_obj_id_crud`, etc.
* Import V3 helpers: `get_ae_obj`, `create_ae_obj`, `update_ae_obj`, `delete_ae_obj`, `search_ae_obj`.
2. **Pattern Replacement:**
* **Get (Single):**
* *Old:* `get_ae_obj_id_crud({ api_cfg, obj_type: 'event_session', obj_id: '...' })`
* *New:* `get_ae_obj({ api_cfg, obj_type: 'event_session', obj_id: '...' })`
* **Get (List):**
* *Old:* `get_ae_obj_li_for_obj_id_crud_v2(...)`
* *New:* `get_ae_obj_li(...)` or `search_ae_obj(...)` if complex filtering is needed.
* **Update:**
* *Old:* `update_ae_obj_id_crud({ ..., fields: { name: 'New Name' } })`
* *New:* `update_ae_obj({ ..., data: { name: 'New Name' } })`
* *Note:* Ensure payload whitelisting is applied! V3 will 400 Error on unknown columns.
* **Create:**
* *Old:* `create_ae_obj_crud({ ..., fields: { ... } })`
* *New:* `create_ae_obj({ ..., data: { ... } })`
3. **Verification:**
* Verify the module still loads data (check Network tab for `/v3/` requests).
* Verify saving works (check for 400 Bad Request errors).
## 4. Standard Practices (V3)
### A. Permissive Update Mode
To simplify frontend state management, V3 supports ignoring unknown fields in update payloads.
- **Header:** `x-ae-ignore-extra-fields: true` (Enabled by default in `api_patch_object`).
- **Use Case:** Allows syncing objects that contain read-only metadata (e.g. `created_on`, `_lq_id`) without manual scrubbing.
### B. Structured Error Handling
V3 returns detailed error metadata in the `meta.details` object.
- **Implementation:** Core helpers automatically extract this metadata.
- **FastAPI Fallback:** Standard `{"detail": "..."}` responses are automatically wrapped into the `meta.details` format by the frontend helpers.
---
## 5. Known Pitfalls
### A. The "Integer Trap" (Search Mapping)
**Issue:** The backend automatically maps certain fields (like `account_id`) from string IDs to internal integers.
**Symptom:** Providing a string ID in a search body that the backend maps to an integer can result in **Zero Results** if the underlying view expects a string.
**Final Solution (Body + Header Injection):**
1. **Body:** Inject the raw field name (e.g. `account_id`) into the `search_query.and` array to bypass automatic backend mapping.
2. **Headers:** Pass `headers: { 'x-account-id': ... }` manually to provide context for Auth validation.
3. **Isolation (IDAA):** Due to specific bugs in the IDAA module, it has been temporarily isolated to a legacy V2 search function (`qry_ae_obj_li__event_v2`) using `default_qry_str` for text searching, while the main module continues to use the V3 implementation.
---
## 6. Final Cleanup
Once all checkboxes above are completed:
1. [x] Remove legacy exports from `src/lib/api/api.ts`.
2. [x] Delete `src/lib/ae_api/api_get__crud_obj_li_v1.ts`.
3. [x] Delete `src/lib/ae_api/api_get__crud_obj_li_v2.ts`.
4. [x] Delete `src/lib/ae_api/api_get__crud_obj_id.ts` (Legacy version).

View File

@@ -0,0 +1,58 @@
# IDAA Recovery Meetings: UI/UX Improvement Roadmap
This document outlines proposed enhancements for the IDAA Recovery Meeting module. The goal is to make it easier for members to find and attend meetings, especially on mobile devices, while providing IDAA staff with better tools to manage meeting data quality.
## 🏆 The "Big Wins" (Highest Member Impact)
### 1. Automatic Timezone Conversion
* **The Problem:** Meetings currently show their "native" time (e.g., 7:00 PM Central). Members must manually calculate the time for their own location.
* **The Fix:** The app will automatically detect the member's local timezone and show a converted time side-by-side (e.g., *"7:00 PM Central — 8:00 PM your time"*).
### 2. "Live Now" & "Todays Meetings"
* **The Fix:**
* **Live Now:** A high-visibility green "LIVE" badge will pulse next to meetings currently in progress.
* **Todays Section:** A dedicated section at the very top of the list will show only meetings happening today, sorted by time, so members don't have to scroll through the full 140+ meeting list.
### 3. Clearer Meeting Schedules
* **The Problem:** Days of the week are currently listed as a flat string (Sunday Monday Wednesday).
* **The Fix:** Convert schedules into natural language one-liners: *"Mondays, Wednesdays, and Fridays at 7:00 PM."* This is much faster for the human eye to scan.
### 4. Favorites ("My Meetings")
* **The Fix:** Members can "Star" their regular meetings. These favorites will be pinned to the top of their list for one-tap access every week.
### 5. "Add to Calendar"
* **The Fix:** A button to automatically add a recurring meeting to a members Google, Apple, or Outlook calendar, including the Zoom/Jitsi link in the calendar event description.
---
## 🛡️ Staff Tools & Data Quality
### 6. "Confirmed Only" Default View
* **The Strategy:** To encourage meeting chairs to keep their information current, we propose defaulting the list to show **only** meetings confirmed by the Central Office.
* **Member Benefit:** Higher confidence that the meeting they are about to join is active and the link is correct.
* **Staff Benefit:** Creates a natural incentive for chairs to contact IDAA to get "Verified," as unverified meetings would require an extra click to see.
### 7. Mobile-Friendly "Not Confirmed" Explanations
* **The Problem:** On mobile, the warning badge for unconfirmed meetings doesn't explain *why* it's there or *how* to fix it.
* **The Fix:** Tapping the badge will show a simple popup: *"This meeting hasn't been verified recently. If you are the chair, please email info@idaa.org to confirm."*
---
## 📱 Ease-of-Use & Mobile Polishing
### 8. Prominent "Join" Buttons & Easy Sharing
* **The Fix:** For virtual meetings, we will move the "Join Zoom" button to a prominent, full-width position at the top of the card. We will also add a "Share" button so members can easily text a meeting link to a sponsee.
### 9. Simplified "Quick-Filter" Chips
* **The Fix:** Instead of small checkboxes, we will add large "Chips" (buttons) for common filters: `[🖥 Virtual]` `[🏠 In-Person]` `[🩺 IDAA]` `[Caduceus]`. These are much easier to tap on a phone screen.
### 10. Intelligent "No Results" Guidance
* **The Problem:** If a member filters too narrowly (e.g., "Caduceus meetings in Hawaii on Tuesdays"), they just see a blank screen.
* **The Fix:** A helpful prompt will appear: *"No meetings found for these filters. [Clear all filters →]"* to prevent members from thinking the app is broken.
---
### Next Steps
1. **Feedback:** Staff identifies which 34 items are the highest priority for the next update.
2. **Prototype:** We implement the high-priority items in the testing environment for staff review.
3. **Deployment:** Changes are pushed live to the IDAA website.

View File

@@ -0,0 +1,99 @@
# Frontend Agent Task List
> Use this file to track steps for complex features or bug fixes.
> **Status:** Stable — ongoing development.
## 🔴 CMSC Charlotte — May 27 (Presentation Management)
**Drive down:** May 25 | **Setup:** May 26 morning | **Show:** May 27+
- [x] **[Launcher] Composable open flow** — `handle_open_file()` uses `copy_from_cache_to_temp` +
`run_osascript` / `run_cmd` directly with per-step error handling. Complete.
- [x] **[Launcher] Slide control scripts in Svelte config** — AppleScript post_scripts live in
`ae_launcher__default_launch_profiles.ts`. VLC focus-stealing fix applied. Complete.
- [x] **[Launcher] Kill Apps button** — "Kill Apps" button added to Native OS config (System
Actions, edit mode only). Kills PowerPoint, Keynote, Adobe Acrobat Reader DC, VLC, soffice.
List overridable via `event_device.other_json.launcher.kill_process_li`. Auto-cleanup on file
open (deferred — manual button sufficient for CMSC).
- [ ] **[Launcher] End-to-end test on macOS** — test pptx and key opens on a real podium Mac.
- [ ] **[Launcher/Electron] Wallpaper stops applying after several changes (post-CMSC)** —
Append timestamp/random suffix to temp filename so macOS always sees a new path.
- [ ] **[Launcher/Electron] Wallpaper drift after display hotplug (post-CMSC)** —
Add resilient reconciliation loop or event-driven reapply on topology change.
---
## 🔴 Axonius DC — June 9 (Badge Printing)
**Setup/Registration:** June 8 | **Show:** June 9
- [ ] **[Badges] Epson C3500 fanfold badge layout** — Create/configure a fanfold badge layout
compatible with the Epson C3500 continuous stock format.
---
## 🚧 V3 CRUD Migration (Surgical Cleanup)
Finalizing the 100% adoption of V3 Standard endpoints and retirement of legacy wrappers.
- [x] **[Badges] Presenter Agreement Form** — migrated to `update_ae_obj` (2026-05-21)
- [ ] **[Core] Site Domain Bootstrap Refactor** — `load_ae_obj_by_fqdn__site_domain` in
`core__site_domain.ts` still uses legacy ID-lookup-by-FQDN. Refactor to use V3
`api.search_ae_obj` with fqdn filter per integration guide.
- [ ] **[Core] Legacy Utility Helpers** — Refactor `ae_core_functions.ts` to use V3 helpers.
- [ ] **[Cleanup] Delete Legacy Wrappers** — Once all callsites are migrated, remove
`src/lib/ae_api/api_get__crud_obj_id.ts` and the legacy exports from `api.ts`.
---
## 🚧 High Priority Workstreams
### [Stores] Svelte 4 → Svelte 5 State Migration
The app uses `svelte-persisted-store` (coarse reactivity). Migration target: replace with Svelte 5
`$state`-based persistence for fine-grained updates.
- [ ] **Phase A — Project plan + wrapper decision:** Write `PROJECT__Stores_Svelte5_Migration.md`.
- [ ] **Phase B — Core auth stores (highest impact):** `ae_loc`, `idaa_loc`.
- [ ] **Phase C — Remaining persisted stores:** `ae_api`, `ae_events_stores`.
- [ ] **Phase D — Non-persisted writable stores:** `ae_sess`, `slct`, `ae_snip`, etc.
### [Stores] IDB Content Version System
- [x] Write `check_and_clear_idb_tables()` helper.
- [x] Wire helper into `db_journals.ts` and IDAA layout.
- [ ] Roll out to `db_events.ts` (module-wide: session, presenter, badge, etc.).
- [ ] Roll out to `db_core.ts` (site_domain, person, user).
### [TypeScript] svelte-check hidden errors
- [ ] **[flowbite-svelte] `ModalProps.children` — 31 errors across 26 files.**
Replace `children` prop binding with Svelte snippet syntax.
### [Journals] Journal Entry Config follow-ups
- [ ] **[Journals] Entry passcode secondary auth** — implement `passcode_hash` comparison.
- [ ] **[Journals] Summary AI shortcut** — add button to modal.
---
## 🧪 Testing & Optimization
- [ ] **[IDAA] IDB fast-path contact search** — parse `contact_li_json` in `search__event()`.
- [ ] **[IDAA] Optimize Recovery Meetings SQL VIEW and indexes.**
- [ ] **[IDAA / Events] Audit `default_qry_str` coverage** in all other event search pages.
- [ ] **[Launcher/VLC] Linux playback investigation** — fullscreen + pause-on-end flags.
---
## ⚙️ DevOps & Backend
- [ ] **[Backend] `event_file` — add `cfg_json` column (post-CMSC)** — The per-file display
override currently uses a localStorage workaround (`$events_loc.launcher.file_display_overrides`)
because `event_file` has no JSON blob column. Proper fix: add `cfg_json` to the `event_file` DB
table, expose it through the FastAPI model, then migrate the frontend back to reading/writing the
backend field (restoring global/cross-device persistence). Frontend code is in
`launcher_file_cont.svelte` — search for `file_display_overrides`.
- [ ] **[Backend] Re-add `Access-Control-Allow-Private-Network: true` CORS header.**
- [ ] **[DevOps] Nginx caching** — Investigate `index.html` cache-pickup issues.
- [ ] **[DevOps] Simplify Dockerfile env file selection** — Use plain `.env` instead of `BUILD_MODE`.
---
## ✅ Completed (archived)
See the full completed history in:
[documentation/archive/TODO__Agents__ARCHIVE_2026-03.md](documentation/archive/TODO__Agents__ARCHIVE_2026-03.md)
[documentation/archive/TODO__Agents__ARCHIVE_2026-04.md](documentation/archive/TODO__Agents__ARCHIVE_2026-04.md)
[documentation/archive/TODO__Agents__ARCHIVE_2026-05.md](documentation/archive/TODO__Agents__ARCHIVE_2026-05.md)

View File

@@ -0,0 +1,289 @@
# PROJECT: Access Control UX — Session Expired & Access Denied
**Status:** Complete
**Priority:** Medium-High
**Created:** 2026-02
**Updated:** 2026-03-11
**Related:** `src/routes/+layout.svelte`, `src/lib/ae_api/`, `src/lib/stores/ae_stores.ts`
---
## 1. Objective
Clean up inconsistent access-denied and session-expired UX across the app:
1. **Session Expired banner** — When the API returns 401/403, show a non-blocking dismissible banner in the root layout rather than silently failing. The `flag_expired` placeholder in the root layout is already wired for this but nothing sets it.
2. **Standardize Access Denied display** — Replace the one-off browser `alert()` in event settings with a proper in-page gate. Create a small reusable component for inline denial cards.
3. **Maintain intentional special cases** — IDAA Novi UUID gate and the Root Site Access Key gate are correct and must not be touched.
---
## 2. Current State Inventory
### Pattern A — Root Layout: Site Access Key Gate ✅ Working, keep as-is
**File:** `src/routes/+layout.svelte` lines 130135, 299305
**Type:** Full-screen blocker
**Logic:** If `site_access_key` is set and `allow_access` key doesn't match + user is not `trusted_access`, `flag_denied = true`.
**Current UI:** Minimal full-screen `<h1>Access Denied</h1>` + Reload button.
**Verdict:** Works correctly. The minimal styling is intentional (it's a hard site gate). Keep but no code change needed unless you want to polish the styling later.
---
### Pattern B — Root Layout: Session Expired Banner 🔴 Declared, never set
**File:** `src/routes/+layout.svelte` line 63
**Variable:** `let flag_expired: boolean = $state(false)`**never set anywhere.**
**Intent:** This should show a non-blocking dismissible banner whenever the API returns 401/403, signaling a stale JWT/session.
**Gap:** API helpers detect 401/403 and log diagnostic info but never fire any store event.
**Fix:** See Implementation Plan step 1.
---
### Pattern C — Core Layout: Manager Access Gate ✅ Already correct
**File:** `src/routes/core/+layout.svelte` lines 1320, 6275
**Logic:**
- `onMount`: 500ms delay then `goto('/')` if still not `manager_access` (handles slow hydration)
- `{:else}` block: Shows a styled "Access Restricted" card with Lock icon, description, "Return Home" button while waiting/denied
**Verdict:** This pattern is correct and consistent. The `{:else}` visual gate prevents flashing. The delayed redirect is a graceful fallback. **No changes needed.**
---
### Pattern D — IDAA Layout: Novi UUID Gate ✅ Intentionally custom, keep as-is
**File:** `src/routes/idaa/(idaa)/+layout.svelte`
**Logic:** Async POST to `https://www.idaa.org/api` to verify Novi UUID. `novi_verifying` flag prevents Access Denied flash during network round-trip.
**Verdict:** Intentionally custom to IDAA's member verification flow. **Do not standardize or touch this.**
---
### Pattern E — Event Settings: browser `alert()` 🟡 Needs fix
**File:** `src/routes/events/[event_id]/settings/+page.svelte` lines 4447
**Current code:**
```ts
if (!$ae_loc.administrator_access) {
if (browser) {
alert('Access Denied: Administrative privileges and Edit Mode required.');
goto(`/events/${event_id}`);
}
}
```
**Problems:**
1. `alert()` is a blocking browser dialog — ugly, inconsistent with app UX
2. Runs in module-level `if` block (not `onMount`) — can fire before hydration is fully complete
3. No visual component shown; just redirects
**Fix:** Remove `alert()`, move check into `onMount` with a small delay (like `/core` pattern), or add an inline gate using a reusable component.
---
### Pattern F — Badge Review: Inline "Access Denied" Card 🟡 Acceptable, minor polish
**File:** `src/routes/events/[event_id]/(badges)/badges/[badge_id]/review/+page.svelte` lines 315330
**Context:** Passcode check failure — attendee entered wrong passcode
**Current UI:**
```html
<div class="card p-6 space-y-4 max-w-sm">
<div class="flex items-center gap-2 text-error-500">
<h3 class="text-lg font-semibold">Access Denied</h3>
</div>
<p class="text-sm text-gray-700">{passcode_error}</p>
<button ... >Try Again</button>
</div>
```
**Verdict:** Contextually appropriate and functional. The "Try Again" button is good UX. This is a prime candidate to be replaced with a reusable component once one exists, but it is not broken.
---
### Pattern G — API Helpers: 401/403 Detection Without UI Feedback 🔴 Gap
**Files:** `src/lib/ae_api/api_get_object.ts`, `api_post_object.ts`, `api_patch_object.ts` (all ~line 237)
**Current behavior:** Logs auth diagnostics to console, returns `false` or `null`. No store event fired.
**Gap:** When a JWT expires mid-session, the user sees requests silently fail (data doesn't load/save) with no explanation. They may think the app broke.
**Fix:** On 401/403, set `ae_auth_error` store → root layout watches it and sets `flag_expired = true`.
---
### Pattern H — Presenter Auth (`auth__person`) — Existing system, no UX issues to fix now
**Store:** `$events_loc.auth__person` — stores authenticated presenter identity
**URL params:** `?person_id=...&person_pass=...&presentation_id=...&presenter_id=...`
**Anonymous toggle:** Per-event config allows presenters to upload files without signing in
**Verdict:** Auth system is working. The gating UI in the presenter pages is contextually managed. Not in scope for this cleanup. Revisit when building out the Leads feature or a future auth refactor.
---
## 3. Issues Ranked by Priority
| # | Severity | Issue | File | Fix |
|---|---|---|---|---|
| 1 | 🔴 High | API 401/403 silently fails — users have no feedback | `api_*.ts` | Wire to `ae_auth_error` store |
| 2 | 🔴 High | `flag_expired` never set — session expired banner never shows | `+layout.svelte` | Watch `ae_auth_error`, render banner |
| 3 | 🟡 Medium | `alert()` in event settings — ugly, blocking, not idiomatic | `settings/+page.svelte` | Replace with `onMount` gate + reusable component |
| 4 | 🟢 Low | Badge review inline card — not reusing a component | `badges/.../review/+page.svelte` | Replace when `element_access_denied.svelte` is ready |
---
## 4. Design Decisions
### 4a. Session Expired Banner Design
- **Non-blocking top bar** — similar to the existing `is_offline` and `api_unreachable` banners in the root layout
- **Dismissible** — user clicks X to clear; or auto-hides after signing back in
- **Message:** "Your session has expired. Please reload or sign in again." with a Reload button
- **Trigger:** Any 401 or 403 from any of the three API helpers
### 4b. `ae_auth_error` Store
Simple writable in `ae_stores.ts`:
```ts
export const ae_auth_error = writable<{ type: 'expired' | 'denied' | null, ts: number | null }>({ type: null, ts: null });
```
This is intentionally minimal — just enough to signal the root layout.
### 4c. Reusable `element_access_denied.svelte`
A small card component for inline access denial within a page:
```
Props:
- title?: string (default: "Access Denied")
- message?: string (default: "You do not have permission to view this content.")
- show_reload?: boolean
- show_return_home?: boolean
- action_label?: string (optional extra button)
- on_action?: () => void
```
Location: `src/lib/elements/element_access_denied.svelte`
### 4d. Event Settings Fix
The settings page check should mirror the `/core` pattern:
- Move to `onMount` with 500ms grace delay
- No `alert()` — if not authorized, the redirect fires silently after the delay
- Add inline gate (`{:else}` block with "Access Restricted" message) if the user somehow lands here
### 4e. What We Are NOT Changing
- Root Layout site access key gate — working correctly
- `/core` layout — already correct
- IDAA Novi UUID gate — intentionally custom
- Presenter auth system (`auth__person`) — not in scope
---
## 5. Implementation Plan
### Step 1: Add `ae_auth_error` store ✅ DONE (2026-03-11)
**File:** `src/lib/stores/ae_stores.ts`
Add after the existing store declarations:
```ts
// Auth error signal — set by API helpers on 401/403 to trigger root layout session-expired banner
export const ae_auth_error = writable<{ type: 'expired' | null, ts: number | null }>({ type: null, ts: null });
```
---
### Step 2: Wire API helpers to `ae_auth_error` ✅ DONE (2026-03-11)
**Files:** `src/lib/ae_api/api_get_object.ts`, `api_post_object.ts`, `api_patch_object.ts` (same pattern in all three)
In the existing `if (response.status === 401 || response.status === 403)` block, add one line after the existing `console.warn(...)`:
```ts
import { ae_auth_error } from '$lib/stores/ae_stores';
// ...
ae_auth_error.set({ type: 'expired', ts: Date.now() });
```
**Note:** Only import `ae_auth_error` — no other store changes. Do NOT import `ae_auth_error` at module level if the API helpers are used SSR-side. Use a dynamic import or guard with `browser` check if needed.
---
### Step 3: Wire `flag_expired` in root layout ✅ DONE (2026-03-11)
**File:** `src/routes/+layout.svelte`
Add an `$effect` that watches `$ae_auth_error` and sets `flag_expired`:
```ts
$effect(() => {
if ($ae_auth_error?.type === 'expired' && $ae_auth_error?.ts) {
untrack(() => { flag_expired = true; });
}
});
```
Add the dismissible banner to the template (after/near the existing `is_offline` banner, in the `{#if browser && $ae_loc?.allow_access}` block):
```html
{#if flag_expired}
<div class="fixed top-0 left-0 right-0 z-50 bg-warning-500 text-white px-4 py-2 flex items-center justify-between">
<p class="text-sm font-semibold">Your session has expired. Please reload or sign in again.</p>
<div class="flex gap-2">
<button class="btn btn-sm preset-filled-surface" onclick={() => window.location.reload()}>Reload</button>
<button class="btn btn-sm" onclick={() => { flag_expired = false; ae_auth_error.set({ type: null, ts: null }); }}>Dismiss</button>
</div>
</div>
{/if}
```
---
### Step 4: Create `element_access_denied.svelte` ✅ DONE (2026-03-11)
**File:** `src/lib/elements/element_access_denied.svelte`
Reusable card for inline access denial. Props per design decision 4c.
---
### Step 5: Fix Event Settings `alert()` ✅ DONE (2026-03-11)
**File:** `src/routes/events/[event_id]/settings/+page.svelte`
Replace the module-level `if (!$ae_loc.administrator_access)` + `alert()` block with:
1. Move check into `onMount` with the same 500ms grace-delay pattern as `/core`
2. Add `{:else}` gate in the template using `element_access_denied.svelte`
3. Remove the `browser` guard (not needed inside `onMount`)
---
### Step 6 (Optional / Low Priority): Swap badge review inline card ✅ DONE (2026-03-11)
**File:** `src/routes/events/[event_id]/(badges)/badges/[badge_id]/review/+page.svelte`
Replace inline access denied card with `element_access_denied.svelte` once the component exists. Keep "Try Again" action via `on_action` prop.
---
## 6. Files to Modify Summary
| File | Change |
|---|---|
| `src/lib/stores/ae_stores.ts` | Add `ae_auth_error` writable store |
| `src/lib/ae_api/api_get_object.ts` | Set `ae_auth_error` on 401/403 |
| `src/lib/ae_api/api_post_object.ts` | Set `ae_auth_error` on 401/403 |
| `src/lib/ae_api/api_patch_object.ts` | Set `ae_auth_error` on 401/403 |
| `src/routes/+layout.svelte` | Watch `ae_auth_error`, render session-expired banner |
| `src/routes/events/[event_id]/settings/+page.svelte` | Remove `alert()`, fix auth gate pattern |
| `src/lib/elements/element_access_denied.svelte` | **NEW** — reusable inline denial card |
| `src/routes/events/[event_id]/(badges)/badges/[badge_id]/review/+page.svelte` | Swap inline card with component (low priority) |
---
## 7. Testing Notes
- **Session expired banner:** Force a 401 by testing with an expired JWT or by calling an API with wrong credentials. Banner should appear. Dismiss should clear it. Reload should reload browser.
- **Event settings gate:** Navigate to `/events/{id}/settings` without `administrator_access`. Should redirect without any `alert()` dialog.
- **Badge review:** Enter a bad passcode — Access Denied card should appear with "Try Again".
- **IDAA, /core, root site key gate:** Verify no regressions.
---
## 8. Risks & Notes
- The API helpers (`api_get_object.ts` etc.) run in a module context that is imported across many components. The `ae_auth_error` store must only be imported/used inside a `browser` guard or dynamically if the helpers are ever called SSR-side. Check `src/lib/ae_core/ae_core__site.ts` (which uses these helpers during SSR hydration) — **do not set the store from SSR context.** Add `if (browser)` before the `ae_auth_error.set(...)` call.
- The root layout sync effect runs `untrack()` to prevent circular store updates. The new `$effect` for `ae_auth_error` must also use `untrack()` to be safe.
- `flag_expired` should NOT permanently gate the UI — it should only show a top banner. The user may have been mid-editing and should not lose their work. The current `flag_denied` full-screen block is for site key access only.

View File

@@ -0,0 +1,53 @@
# Project: AE Docker + CI BuildKit Implementation
**Status:** Proposed
**Goal:** Make Docker image builds for Aether cache-friendly using BuildKit/buildx and CI registry caching, while keeping local developer caches small and manageable.
Summary
- Implement a BuildKit-friendly multi-stage `Dockerfile` pattern for frontend and API images.
- Add CI `buildx` examples that push/read registry-based cache to avoid local disk bloat.
- Provide cache retention/rotation guidance and developer commands for safe pruning.
Scope
- Repository areas: `aether_container_env/`, root `Dockerfile` (if present), and CI pipeline definitions (Gitea/Drone or other).
- Non-goal: full CI pipeline migration to a new provider. This work provides CI snippets and a PR-ready set of files for your CI team.
Deliverables (this PR)
- `documentation/PROJECT__AE_Docker_CI_BuildKit_implement.md` (this file)
- `aether_container_env/Dockerfile.buildkit.example` — BuildKit-friendly multi-stage Dockerfile example.
- `aether_container_env/ci_buildx_example.sh` — standalone CI script examples (registry cache + local cache usage).
- `documentation/AE_Docker_CI_cache_policy.md` — cache rotation and prune guidance.
Tasks (implementation checklist)
- [ ] Review existing `Dockerfile`(s) under `aether_container_env/` and repository root.
- [ ] Replace/extend Dockerfile with multi-stage BuildKit-friendly layout (use example as guide).
- [ ] Ensure `.dockerignore` (already added) excludes large build artifacts.
- [ ] Add CI step using `docker buildx build` with `--cache-from` and `--cache-to` pointed at a registry cache.
- [ ] Add a scheduled job or registry lifecycle rule to delete old cache images (30 days default).
- [ ] Document required CI secrets and permissions (registry write/read) for the operations team.
- [ ] Run verification builds (dev local with BuildKit; CI runs with cache) and record timings.
Verification
- Local dev: `DOCKER_BUILDKIT=1` build with `--cache-to`/`--cache-from` shows cache hits on second run and faster build time.
- CI: subsequent CI runs log `cache hit` from `buildx` and total build time reduced vs baseline.
- Confirm registry contains `cache` image tags and that rotation job/prune removes old entries.
Notes about Gitea/CI
- Gitea does not include native Actions like GitHub; teams typically use Drone CI, Tekton, or a self-hosted runner that can execute the `docker`/`buildx` CLI.
- The provided `ci_buildx_example.sh` is intentionally provider-agnostic — pasteable into Drone, Jenkins, GitLab CI, or any shell-capable runner.
Risks & Mitigations
- Risk: Unbounded registry cache growth. Mitigation: enforce retention policy and rotation job; prefer a single `cache` tag reused by CI.
- Risk: Developers unfamiliar with BuildKit. Mitigation: examples show simple `DOCKER_BUILDKIT=1` usage and local cache prune commands.
Next steps for the container team
1. Review examples in `aether_container_env/` and adapt the Dockerfile to your runtime constraints (ssl certs, env injection, secrets).
2. Add a CI job using the `ci_buildx_example.sh` snippet; configure registry credentials as secrets.
3. Add a scheduled job to rotate/delete old cache images or configure registry lifecycle rules.
4. Run a before/after benchmark of `time npm run build:prod` inside the build stage to quantify improvement.
Files included in this PR for reference:
- `aether_container_env/Dockerfile.buildkit.example`
- `aether_container_env/ci_buildx_example.sh`
- `documentation/AE_Docker_CI_cache_policy.md`

View File

@@ -0,0 +1,258 @@
# Project: Badges Config Cleanup & Config UI
**Status:** Executed — Complete
**Priority:** Medium-High (post-April 2026 BGH conference; same pattern as pres_mgmt cleanup)
**Created:** 2026-04-02
**Related:** `TODO__Agents.md`, `PROJECT__AE_Events_PressMgmt_Config_Cleanup.md`, `PROJECT__Stores_Svelte5_Migration.md`, `MODULE__AE_Events_Badges.md`
---
## Background
The badges module has accumulated the same class of problems as pres_mgmt before its cleanup:
- `mod_badges_json` is typed as `any` in `ae_types.ts` and `key_val | null` in `db_events.ts` — no canonical TypeScript interface exists.
- Badge search and UI state still lives in `events_loc.badges` (Svelte 4 nested store), with manual `typeof x === 'undefined'` guards in `+page.svelte` and `ae_comp__badge_search.svelte`.
- `ae_events_stores__badges_defaults.ts` already has typed `BadgesLocState` and `BadgesSessState` interfaces wired into `events_loc` — but these have not yet been promoted to a standalone `PersistedState` like pres_mgmt's `pres_mgmt_loc`.
- The `edit_permissions` sub-object (which controls which badge fields each access level may edit) is documented and wired up in `ae_comp__event_settings_badges_form.svelte`, but the review page (`[badge_id]/review/+page.svelte`) still uses hardcoded defaults with `TODO` markers instead of reading from `mod_badges_json`.
- `trusted_passcode` and `administrator_passcode` are stored in `mod_badges_json` and managed via the legacy settings form — no dedicated config UI exists.
- Admin must edit the settings form (or DB directly) to change badge config — no standalone, grouped config page exists (unlike pres_mgmt, which now has `/pres_mgmt/config`).
---
## Goals
1. **Canonical config schema** — define `BadgesRemoteCfg` TypeScript interface for `mod_badges_json`
2. **New Svelte 5 store** — promote `events_loc.badges` to a standalone `PersistedState` (`badges_loc`) with its own localStorage key
3. **Wire `edit_permissions`** — connect the review page to `mod_badges_json.edit_permissions` (remove hardcoded defaults)
4. **Config UI** — dedicated admin page at `(badges)/badges/config/` for managing `mod_badges_json`
5. **Security review** — ensure passcode fields are never exposed to non-administrator access
---
## Canonical Remote Config Schema
`BadgesRemoteCfg` — the authoritative TypeScript interface for `event.mod_badges_json`:
```typescript
interface BadgesRemoteCfg {
// Search & UI behaviour
enable_mass_print: boolean; // show the mass-print controls
enable_add_badge_btn: boolean; // show the "Add Badge" button
enable_upload_badge_li_btn: boolean; // show the "Upload Badge List" button
enable_search_qr: boolean; // enable QR scan search
// QR code configuration
qr_type: string | null; // QR payload format (e.g. 'badge_id', 'url')
// Access control — passcodes for attendee / staff tiered access
// WARNING: Only expose to administrator_access. Never render client-side for lower levels.
trusted_passcode: string | null;
administrator_passcode: string | null;
// Field-level edit permissions per access tier
// key = access level ('authenticated' | 'trusted' | 'administrator')
// value.can_edit = string[] of field keys, or '*' for all fields
edit_permissions: {
authenticated?: { can_edit: string[] | '*' };
trusted?: { can_edit: string[] | '*' };
administrator?: { can_edit: string[] | '*' };
};
}
```
### Default field permissions (encoded in defaults, not hardcoded in review page)
```typescript
// Attendee (passcode-authenticated)
authenticated.can_edit = [
'pronouns_override',
'full_name_override',
'professional_title_override',
'affiliations_override',
'phone_override',
'location_override',
'allow_tracking',
'agree_to_tc',
]
// Trusted staff
trusted.can_edit = [
'pronouns_override',
'full_name_override',
'professional_title_override',
'affiliations_override',
'phone_override',
'location_override',
'email_override',
'badge_type_code_override',
'registration_type_code_override',
'allow_tracking',
'agree_to_tc',
'hide',
'priority',
'notes',
// other_1_code ... other_8_code
// ticket_1_code ... ticket_8_code
]
// Administrator
administrator.can_edit = '*'
```
---
## New Svelte 5 Local Store
**Do NOT touch `events_loc` or the paused Svelte 5 migration.**
Instead, promote the existing `BadgesLocState` to a standalone store.
**Files to create/modify:**
- **New store:** `src/lib/stores/ae_events_stores__badges.svelte.ts`
- **Defaults file:** `src/lib/stores/ae_events_stores__badges_defaults.ts` (already exists — no change needed to the types)
- **Version gate:** add `AE_BADGES_LOC_VERSION` to `store_versions.ts`
```typescript
// ae_events_stores__badges.svelte.ts
import { PersistedState } from 'runed';
import { badges_loc_defaults } from './ae_events_stores__badges_defaults';
export const badges_loc = new PersistedState('ae_badges_loc', badges_loc_defaults);
// Usage: badges_loc.current.fulltext_search_qry_str
```
New localStorage key: `ae_badges_loc` (separate from `ae_events_loc`)
Consumer syntax change:
```
BEFORE: $events_loc.badges.fulltext_search_qry_str
AFTER: badges_loc.current.fulltext_search_qry_str
```
### Store migration scope
`$events_loc.badges` is used in two files (~48 references total):
- `(badges)/badges/+page.svelte` — all search params, inline guards (lines 59-73, 116-148, 423-424)
- `(badges)/badges/ae_comp__badge_search.svelte` — all filter bindings (lines 40-228)
The manual `typeof x === 'undefined'` guards in `+page.svelte` are eliminated entirely —
`PersistedState` with typed defaults guarantees fields always exist.
---
## Review Page — Wire `edit_permissions`
**File:** `(badges)/badges/[badge_id]/review/+page.svelte`
Currently has two `TODO` markers at lines ~60 and ~197 where `can_edit_fields` is built
from hardcoded arrays instead of `mod_badges_json.edit_permissions`.
**After this change:**
1. Load `lq__event_obj` (already available via Dexie liveQuery in that page)
2. Derive `can_edit_fields` from `$lq__event_obj?.mod_badges_json?.edit_permissions`
3. Fall back to the defaults from `BadgesRemoteCfg` defaults if `edit_permissions` is not set
4. The `ae_comp__badge_review_form.svelte` component interface is already correct — it accepts `can_edit_fields: string[]` prop
---
## Config UI Page
**Route:** `/events/[event_id]/(badges)/badges/config/`
**Access:** `$ae_loc.administrator_access` only (passcodes present — stricter than pres_mgmt's manager_access)
**Button visibility:** Edit mode only (or always visible in the section header, admin-gated)
### Page behaviour
- Loads `event.mod_badges_json` fresh from API (or Dexie) on page open
- Displays grouped form sections (see below)
- Save = load → merge draft → PATCH `/v3/crud/event/{event_id}` with `{ mod_badges_json: updated }`
- Settings page `Badges (mod_badges_json)` section gets a link to this page + raw JSON fallback (same pattern as pres_mgmt)
### Form sections
1. **Search & UI**`badge_id_only_search`, `enable_mass_print`, `enable_add_badge_btn`, `enable_upload_badge_li_btn`, `enable_search_qr`
2. **QR Config**`qr_type` (text input)
3. **Access Passcodes**`trusted_passcode`, `administrator_passcode` (masked inputs; only visible to administrator_access)
4. **Attendee Editable Fields**`edit_permissions.authenticated.can_edit` (checkbox list per known field)
5. **Staff Editable Fields**`edit_permissions.trusted.can_edit` (checkbox list per known field)
> Administrator is always `*` (all fields) — no UI control needed, show as read-only note.
---
## Settings Page Changes
`settings/+page.svelte``Badges (mod_badges_json)` section:
```svelte
<!-- Replace the form+toggle with: -->
<p class="text-sm text-surface-500">
Manage badge search, print controls, QR config, passcodes, and field permissions.
</p>
<a href="/events/{event_id}/badges/config" class="btn btn-sm preset-tonal-primary">
Open Badges Config
</a>
<!-- Raw JSON fallback for debugging / emergency edits -->
<details class="mt-2">
<summary class="text-xs text-surface-400 cursor-pointer">Raw JSON (advanced)</summary>
<!-- existing CodeMirror editor remains here -->
</details>
```
The old `ae_comp__event_settings_badges_form.svelte` can be retired after the config page is live —
keep the file for now but stop importing it from the settings page.
---
## Security Notes
- `trusted_passcode` and `administrator_passcode` are sensitive credentials.
- The config page must be gated at `administrator_access` (not just `manager_access`).
- Input fields should use `type="password"` with a show/hide toggle — do not render as plain text.
- Never include passcode values in client-side logs or error messages.
- `edit_permissions` affects what data attendees can self-modify — changes take effect on the next page load (no caching concern since it's read from `mod_badges_json` on load).
---
## Migration Path
Safe and backward compatible — the review page already falls back to hardcoded defaults.
1. New `BadgesRemoteCfg` interface — no DB changes needed
2. `ae_events_stores__badges.svelte.ts` — new file, new localStorage key (`ae_badges_loc`)
3. Migrate `$events_loc.badges.*``badges_loc.current.*` in two files (~48 refs)
4. Wire review page `can_edit_fields` to `mod_badges_json.edit_permissions`
5. Build config UI page and update settings page
---
## Implementation Steps
- [x] **Step 1** — Define `BadgesRemoteCfg` TypeScript interface (added to `ae_events_stores__badges_defaults.ts`; also extracted `default_authenticated_can_edit` and `default_trusted_can_edit` constants)
- [x] **Step 2** — Created `ae_events_stores__badges.svelte.ts` with `PersistedState`; added `AE_BADGES_LOC_VERSION` to `store_versions.ts`
- [x] **Step 3** — Migrated `$events_loc.badges.*``badges_loc.current.*` in `+page.svelte` and `ae_comp__badge_search.svelte`; removed all manual `typeof` guards
- [x] **Step 4** — Wired `edit_permissions` into review page `can_edit_fields`; the two TODO blocks resolved
- [x] **Step 5** — Built config UI at `(badges)/badges/config/+page.svelte` (administrator_access gated)
- [x] **Step 6** — Updated settings page `Badges` section with link to config page; retired the old form component import
- [ ] **Step 7** — Update active event(s) via new UI; verify passcode fields function correctly
- [x] **Step 8**`npx svelte-check` clean; commit
> **Implementation note (2026-04-02):** Passcode fields use plain `type="text"` inputs, not `type="password"`. This matches the admin UI convention for this codebase.
### Step 3 scope (find-replace)
```
grep -rn 'events_loc\.badges' src/
```
Affected files:
- `src/routes/events/[event_id]/(badges)/badges/+page.svelte` (~35 refs)
- `src/routes/events/[event_id]/(badges)/badges/ae_comp__badge_search.svelte` (~13 refs)
---
## Notes
- `BadgesLocState` already has typed interfaces in `ae_events_stores__badges_defaults.ts` — this is ahead of where pres_mgmt was. Steps 1-3 are therefore lower risk.
- The `BadgesSessState` (in-memory, resets on page load) does **not** need to move — it can stay in `events_sess.badges` inside the main store for now; it contains no persisted user prefs.
- `enable_search_qr` and `qr_type` need validation: verify what QR type values are actually consumed by the scan component before exposing them as free-text inputs. A select with known options is safer.
- Badge type code options (`member`, `non-member`, `guest`, etc.) are defined per Event Badge Template — the config page should not hardcode them. If badge type selects are needed in config, pull from `db_events.badge_template` liveQuery.
- The `agree_to_tc` field in `can_edit_fields` is a placeholder — no Terms & Conditions flow exists yet. Gate it with a note in the UI.

View File

@@ -0,0 +1,175 @@
Aether Events Exhibitor Leads v3
=======
## Overview:
* Mobile first!
* Offline caching for spotty network connections
* Clean and simple
* Exhibitors have between 0 and X license
* Sign in and "claim" an assigned license linked to an Exhibitor
* Collect and manage Leads per Exhibitor
* Export Leads data to CSV or XLSX
* Exhibitors will have the link to their Exhibit sent to them or they can use the Exhibit Search to find their Exhibit and sign in with the shared passcode or their assigned licensed user credentials.
---
## Primary Leads Pages/Tabs
1. start - Sign In / Licenses
* Exhibit passcode sign in
* Exhibitor Leads user sign in
* Payment - Leads Payment
2. add - Add (Search/QR)
* Text search
* QR scan
3. leads - Leads List
* List of Attendees (Event Badge ID) linked to Exhibit
* Click to view/edit Exhibit Tracking entry
4. manage - Leads (app and exhibit) Manage
---
## Exhibitor Sign In and Licenses:
* An Exhibit needs to have a "Shared exhibit passcode" for primary sign in.
* A Licensed Exhibit staff person needs to have a License assigned to them. It is important that the email address not be changed per license. Or if they do, be aware of the affects on tracked attendees. They can then sign in with that email address and passcode assigned. And/Or we need to make the email sign in link work as well.
* Once signed in with the Exhibit passcode, they can change it (passcode) and assign Exhibit Leads licenses once they have paid.
* An exhibitor marked as "priority" means they have paid. Only OSIT admins can mark them as paid.
* Should at least claim/assign one initial user license so an Exhibit related staff can sign in (without the shared Exhibit passcode). Then add, update, or remove licenses based on max allowed per Exhitibor.
Payment:
* Not Paid: <span class="fas fa-question text-red-500 m-1"></span> <span class="fas fa-credit-card mx-1"></span> Waiting for payment
* Paid: <span class="fas fa-check text-green-500 m-1"></span> <span class="fas fa-credit-card mx-1"></span> Marked as paid
### Licenses:
A client staff (Trusted Access or above) or someone signed in with an Exhibit passcode can add/edit/remove icenses.
License:
* full_name
* email
* passcode
---
## Structure Overview
* There are 4 primary tabs for the Event Exhibit Leads module.
* The overall header/footer should hide by default once signed in.
* If not signed in, only the first tab, to start and sign in is shown.
---
## [tab 1] Start / Sign In / Payment
* Only shows when not signed in as Exhibit Licensed Leads User
* Sign in with Exhibit passcode
* This will then allow them to Manage the Exhibit License
* Sign in with Exhibit Licensed user
### [tab section] Payment
* Shows if signed in with Exhibit Passcode and not marked as paid / priority
* Field for license info - event_exhibit.license_li_json
* Use Exhibit passcode first?
* Select number of licenses
* Select number of small/large devices
* Make payment (Stripe)
### [tab section] Licensed Users
* Shows if signed in with Exhibit Passcode and are marked as paid / priority
* Fill in Exhibit staff users per max licenses
## [tab 2] Add - Search / QR Scan
* One button that toggles between showing and hiding the QR add mode? The text search is always shown above or below the QR scan camera image area.
* Search - Essentially the same as the Badge search. Full name, email, affiliations, ID, etc
* QR Scan - Allow for auto add toggle instead of confirming per scan. Allow for manual entire of Badge ID
* The QR scan is basically just using the Event Badge ID encoded in the persons QR code on their badge. In fact it can probably just populate the text search field?
* Must include the Leads licensed user's email address when adding to the Exhibitor Tracking list. Linked using event_exhibit_tracking with the Licensed user's email address.
## [tab 3] Leads - List of Attendee Leads for Exhibitor
* Allow for toggle between showing all per Exhibit and per licensed user based on their email address. Not perfect, but works well enough.
* Allow for easy edit or remove
* Button to Export Data - CSV or XLSX
* Toggle for show/hide Hidden records
* Select options for sorting: Newest added first, Oldest added first, Alpha ascending, Alpha descending, Last updated first
## [tab 4] Manage / Config
### Exhibit Specific
* Priority/payment toggle - Administrator Access or above
* Max licenses (number) - readonly or edit for Administrator Access or above
* Small devices (number) - readonly or edit for Administrator Access or above
* Large devices (number) - readonly or edit for Administrator Access or above
* Exhibit (shared) Passcode
* Same Exhibit Leads License list component as the Start tab's Licensed Users section
### App Specific
* Show/Hide Payment Tab
* Additional Settings:
* List refresh interval in seconds - default 25 seconds; 1 second to 2 minutes (120000)
* Basic reload/refresh
* Clear Indexed DB
* Clear localStorage
* Auto hide header/footer on sign in - default true
* (?) Turn on iframe mode
* (?) Show or hide additional details - Use "$events_loc.show_details"?
## [page] View / Edit Tracked Attendee (event_exhbit_id, event_badge_id
* Able to edit the obvious things
* Can add custom exhibitor_notes and custom responses_json
* Can set Priority (Star/Flag)
* Can set Sort (number)
* Can set Hide (toggle)
---
## App Specific - Leads Module Config Options and Stored Data
* Signed in using Shared Exhibit Passcode
* Can make payment and manage user licenses
* Need to be able to sign in with Shared Passcode and Leads License passcode.
* Signed in using one Leads License slot they were assigned to
* Things will be tied to their email address
* Can add, edit, remove, etc the Leads specific to an Exhibit.
* This list is shared among all Licensed staff for a specific Exhibit. It is recommended they stay toggled to only showing their additions by default.
* Need to be able to sign in with Shared Passcode and Leads License passcode.
* Show and Hide payment tab (override sort of)
* Show and Hide header/footer (override sort of)
* Use iframe mode??
* Light and Dark mode??
* List refresh interval
* Tracking list sorting
* Show and hide hidden records
* Show and hide enabled records (Administrator Access or above)
There are essentially 3 ways to "Sign In":
1. Aether Platform in general using a defined Core User or using a Client Site specific passcode.
2. Exhibit shared passcode for general Exhibitor management. Should only be briefly needed to pay and assign license slots
3. Licensed Leads User using the email address and passcode assigned by the Exhibitor manager or OSIT/client staff person.
---
## First Steps for Leads v3
Create stub directories, pages, and other supporting files for each primary tab, and each primary section within each tab, and for viewing a specific Lead (Exhibit Tracking).
Once we have that looking good, then we can add the functionality. So we are kind of laying out the framework and pre-documenting (commenting) the files as we go.
---
## For Reference if Needed
Known goodish reference version of Leads v2ish:
d1021e28227db6d49b16cc48e324872b1a322da3 November 19, 2025 at 10:45 PM
Hopefully that is not needed...

View File

@@ -0,0 +1,153 @@
Aether Events Exhibitor Leads v3 - Details
=======
## [root page] Leads for Exhibitors Main Landing
* Has a search for Exhibits section that allows for searching Exhibits by name or code.
* Results can be sortable. They can be sorted by name, booth number, or last created/updated.
* This is for exhibitors to find their Exhibit and sign in with the shared passcode or their assigned licensed user credentials.
* If not signed in with Administrator Access or above, then only show results for Exhibitors that have been marked as "priority" (paid). Otherwise show all Exhibitors (to admins).
* Has a list of Exhibits with basic info and link to Exhibit Leads management page for each Exhibit. Do not show results by default or if less than 3 characters in search and they are not signed in with Trusted Access or above.
## [exhibit_id page] Exhibit Leads Module
* This is minimalistic. Only the tabs and content needed.
### Header/Footer
* Overall header/footer should hide by default once signed in. Use Aether iframe mode?
* Leads module header should show the Exhibit name and booth number. It should also have a button to toggle the Add Lead tab and Lead List tab. There should also be a button to show the Manage/Config tab for the Exhibit.
Header: [Name and Booth # text] [Add Lead / Lead List toggle] [Manage/Config button]
### Tabs:
I am probably using the term "tab" loosely here. It may just be sections that show/hide based on buttons or whatever. The main thing is to keep the UI simple and not overwhelm the user with too many options at once. The main flow should be to easily add leads and then view/manage those leads. The Manage/Config tab is more for the person managing the Exhibit and should be separate from the lead capture and management interface.
1. Start - Sign In / Licenses / Payment
2. Add - Search/QR
3. Leads - List of Attendee Leads for Exhibitor
4. Manage - Leads (app and exhibit) Manage
### [tab 1] Start / Sign In / Payment
* Only shows when not signed in as Exhibit Licensed Leads User
* Show notification and or message that this is a PWA and can be installed on their device for easier access.
* Sign in with Exhibit passcode
* This will then allow them to Manage the Exhibit License
* Sign in with Exhibit Licensed user
* This will then allow them to Manage the Exhibit License
* Payment - Leads Payment
* Not Paid: <span class="fas fa-question text-red-500 m-1"></span> <span class="fas fa-credit-card mx-1"></span> Waiting for payment
* Paid: <span class="fas fa-check text-green-500 m-1"></span> <span class="fas fa-credit-card mx-1"></span> Marked as paid
* Need to switch to Lucide icons
* Sections:
* Sign in with Exhibit "shared" passcode
* This will then allow them to Manage the Exhibit License
* Change to Sign Out once signed in
* Once signed in, show message to the Exhibit person. Allow them to select number of Licenses, make a payment, and manage the Licenses if paid.
* Sign in with Exhibit Licensed user
* This will then allow them to Manage the Exhibit License
* Change to Sign Out once signed in
* Once signed in, show message to the Leads user. A big button should allow them to then start adding leads.
* Payment (Stripe) - Leads Payment
* Only show if signed in with Exhibit "shared" passcode and not marked as paid.
* Licenses (table):
* A client staff (Trusted Access or above) or someone signed in with an Exhibit passcode can add/edit/remove licenses.
* License:
* full_name
* email
* passcode
* Buttons and Inputs:
* Sign in with Exhibit passcode
* Sign in with Exhibit Licensed user
* Payment - Leads Payment
* Add/Edit/Remove Licenses (if signed in with Exhibit passcode or Trusted Access or above)
### [tab 2] Add - Search/QR
* Show only when signed in as Exhibit Licensed Leads User or Trusted Access or above.
* Allow for text search of Attendee Badge ID, QR code, name, email, or affiliations.
* Allow for QR code scan to add Attendee Badge as Lead.
* Once found, show basic Attendee Badge info and button to "Add as Lead".
* If already added as Lead, show message and button to "View Lead".
* Sections:
* Text search
* QR scan
* Results with "Add as Lead" or "View Lead" button
* Buttons and Inputs:
* Text input for search
* Button to trigger search
* Button to trigger QR scan (opens camera and scans QR code on badge)
* Button to "Add as Lead" if Attendee Badge found and not already a Lead
* Button to "View Lead" if Attendee Badge found and already a Lead
Functions needed:
* Search function to find Attendee Badge by Badge ID, QR code, name, email, or affiliations.
* QR code scan function to read QR code and find Attendee Badge.
* Add Lead function to create Exhibit_tracking entry linking Exhibit and Attendee Badge.
### [tab 3] Leads - List of Attendee Leads for Exhibitor
* Allow for toggle between showing all per Exhibit and per licensed user based on their email address. Not perfect, but works well enough.
* Allow for easy edit or remove
* Sections:
* List of Leads with basic info and buttons to Edit or Remove
* Options:
* Filter by Licensed user email address (dropdown of emails that have added leads for this Exhibit)
* Toggle for show/hide Hidden records
* Select options for sorting: Newest added first, Oldest added first, Alpha ascending, Alpha descending, Last updated first
* Buttons and Inputs:
* Button to Export Data - CSV or XLSX
* Toggle for show/hide Hidden records
* Select options for sorting: Newest added first, Oldest added first, Alpha ascending, Alpha descending, Last updated first
* Should it have a text search?
* NOTE: It is probably easiest for them to us the search tab to find a lead that has already been added. It will show "View Lead" button if already added.
Functions needed:
* Load Leads function to get Exhibit_tracking entries for the Exhibit.
* Filter function to filter by Licensed user email address.
* Sort function to sort by selected option.
* Export function to export displayed Leads to CSV or XLSX.
### [tab 4] Manage - Leads (app and exhibit) Manage / Config
#### Exhibit Specific
* Priority/payment toggle - Administrator Access or above
* Max licenses (number) - readonly or edit for Administrator Access or above
* Small devices (number) - readonly or edit for Administrator Access or above
* Large devices (number) - readonly or edit for Administrator Access or above
* Exhibit (shared) Passcode
* Same Exhibit Leads License list component as the Start tab's Licensed Users section
#### App Specific
* Show/Hide Payment Tab
* Additional Settings:
* List refresh interval in seconds - default 25 seconds; 1 second to 2 minutes (120000)
* Basic reload/refresh
* Clear Indexed DB
* Clear localStorage
* Auto hide header/footer on sign in - default true
* (?) Turn on iframe mode
* (?) Show or hide additional details - Use "$events_loc.show_details"?
* Sections:
* Exhibit Specific Manage/Config
* App Specific Manage/Config
* Buttons and Inputs:
* Exhibit Specific:
* Priority/payment toggle - Administrator Access or above
* Max licenses (number) - readonly or edit for Administrator Access or above
* Small devices (number) - readonly or edit for Administrator Access or above
* Large devices (number) - readonly or edit for Administrator Access or above
* Exhibit (shared) Passcode
* Same Exhibit Leads License list component as the Start tab's Licensed Users section
* App Specific:
* Show/Hide Payment Tab
* Show last refresh time and counter for next refresh based on the List refresh interval setting.
* Additional Settings:
* List refresh interval in seconds - default 25 seconds; 1 second to 2 minutes (120000)
* Basic reload/refresh (F5)
* Clear Indexed DB
* Clear localStorage
* Auto hide header/footer on sign in - default true
* (?) Turn on iframe mode
* (?) Show or hide additional details - Use "$events_loc.show_details"?
* Functions:
* Update Exhibit configuration function to update the Exhibit with the new settings.
* Update App configuration function to update the app-wide settings for the Leads module.

View File

@@ -0,0 +1,200 @@
# PROJECT: Zebra ZC10L Hardware Test Day
**Created:** 2026-03-12
**Planned date:** ~week of 2026-03-16 (printer rented for one day)
**Hardware:** Zebra ZC10L, 3.5" × 5.5" PVC card stock
**Goal:** Validate real-world badge printing before Axonius (NYC, mid-April 2026)
**Owner:** Scott Idem / One Sky IT
---
## Before the Printer Arrives — Pre-Test Checklist
These must be done before the printer is on-site so you're not burning rental time on setup.
- [ ] **Remove debug outlines** from `print/+page.svelte` print CSS.
The lime/blue/red/orange/purple/cyan debug outlines are still in the file. Remove the
entire `TEMPORARY DEBUG OUTLINES` block. Commit before the test day.
- [ ] **Zebra ZC10L Linux driver** — install CUPS driver ahead of time.
- Check Zebra's site for the Linux CUPS driver for ZC10L.
- Install, configure in CUPS (`http://localhost:631`), do a test page print with a spare card.
- Confirm the card feeds without jam and ink/dye-sub layer applies cleanly.
- Driver may need the printer set to the correct card stock size (3.5" × 5.5").
- [ ] **Wire `style_href`** — add `<link rel="stylesheet" href={...}>` to `<svelte:head>` in
`print/+page.svelte` when `$lq__event_badge_template_obj?.style_href` is set.
Without this, any client-specific external CSS won't load.
See `documentation/MODULE__AE_Events_Badge_Templates.md` → "External CSS Approach".
- [ ] **Confirm single-sided print (duplex=0)**`duplex` backend field doesn't exist yet.
For PVC cards, the badge back must NOT print. Verify this works by checking that
`.badge_back` is hidden in `@media print` when the layout is `badge_3.5x5.5_pvc`.
The PVC CSS (`badge_layout_zebra_zc10l_pvc.css`) may already handle this — confirm.
If not, add a print rule: `[data-layout="badge_3.5x5.5_pvc"] .badge_back { display: none !important; }`
- [ ] **Test event + template in dev DB** — create/confirm:
- Event with `mod_badges_json` configured
- PVC template: `layout: badge_3.5x5.5_pvc`, `duplex: 0` (once backend supports it),
`header_path` set to a real image URL, `badge_type_list` JSON populated
- Test badge records (see "Test Data Set" below)
- [ ] **Test data set** — create badge records covering:
- Short name: "Kim Lee"
- Long name: "Bartholomew Vandenberghe-Christopoulos"
- HTML in name: `<b>Dr.</b> Patricia Adams`
- HTML in affiliations: `University of Minnesota<br>Dept. of Surgery`
- Badge with no affiliations, no location
- Badge with all 8 ticket codes set
- Three different badge_type_codes (e.g. member, staff, guest) — to verify footer
stripe color for each
- [ ] **Browser setup on kiosk machine** — confirm Chrome and Firefox both installed.
Test print dialog settings once driver is working:
- Chrome: Margins → None (required), paper size set to 3.5×5.5 if available
- Firefox: should just work — `@page { size: 3.5in 5.5in }` is honored
---
## Test Day Checklist
### 1. Basic Print Path
- [ ] Card feeds and prints without jam
- [ ] Badge fills the card edge-to-edge (no white border, no clipping)
- [ ] Content horizontally centered on the card
- [ ] Content vertically centered on the card
- [ ] No debug outlines visible (confirm cleanup commit applied)
**Chrome:**
- [ ] Print → Margins: **None** → correct output
- [ ] Print → Margins: **Default** → bad (expected — documents the known issue)
- [ ] Print → Margins: **Minimum** → correct output
- [ ] "More settings" → paper size: does selecting 3.5×5.5 matter, or does Zebra driver override?
**Firefox:**
- [ ] Print → just works out of the box
### 2. Single-Sided PVC
- [ ] Only the front face prints — back does NOT print
- [ ] No second card ejected or blank card printed for the back
### 3. Visual Quality
- [ ] Font sizes readable at 3.5×5.5 physical scale (name, title, affiliations, location)
- [ ] Auto-scaling text (v2) — does it look natural, not crunched?
- [ ] Header image renders correctly (colors, resolution, no pixelation)
- [ ] Footer stripe color correct for each badge_type_code tested
- [ ] HTML in name/affiliations renders correctly (bold, line break, etc.)
- [ ] Badge with no affiliations — no awkward blank space
### 4. Edge Cases — Badge Content
- [ ] Long name auto-scales without overflow or clipping
- [ ] HTML markup in name field: `<b>Dr.</b>` renders bold on the physical card
- [ ] HTML line break in affiliations: two-line org renders cleanly
- [ ] Badge with no location — layout doesn't break
- [ ] Badge with all 8 ticket/option codes — back of badge (if applicable) lays out cleanly
### 5. Print Tracking
- [ ] `print_count` increments after printing via the **Print Badge** button
- [ ] `print_first_datetime` set on first print
- [ ] "Printed N×" amber chip appears in print page header after first print
- [ ] Reprint via Re-print shortcut (trusted + edit mode) does NOT increment count
- [ ] Second print via Print Badge button DOES increment count (to 2)
### 6. QR Code
- [ ] QR on printed card is scannable with a phone camera
- [ ] QR scans to the correct badge ID (test with `/events/[id]/badges` search by QR scan)
- [ ] QR on back (if `show_qr_back=1`) also scans correctly
- [ ] If `show_qr_back=0` — no QR code visible on back
### 7. Font Size Controls
- [ ] Manual font size override (+ / in controls panel) changes the badge render live
- [ ] Change is visible on the physical printed card
- [ ] Reset (↺) returns to auto-sizing
- [ ] Auto-sizing produces a reasonable default for all test names without manual adjustment
### 8. Edit Fields at Kiosk
- [ ] Badge info editable before printing: change full_name_override, verify change appears on card
- [ ] Save change → re-print → new value printed
- [ ] Cancel reverts to saved value
- [ ] Badge type dropdown changes footer stripe color on the rendered badge
---
## Known Limitations on Test Day
These are not bugs — just gaps that won't be addressed during the test day:
- **`style_href` external CSS**: Must be wired before test day (see pre-checklist). If not
done, client-specific CSS from the template won't load — fall back to default styles.
- **`duplex` backend field**: Not yet in the backend schema. Single-sided behavior depends
on the PVC CSS hiding `.badge_back` in print. Verify manually.
- **Per-template print margins (`print_margin_cfg`)**: UI doesn't exist yet. If the card
needs a physical offset for the Zebra's feed, apply a manual CSS tweak to the PVC layout
CSS file and revert after testing.
- **Kiosk attendee editing (TASK 4.0)**: Edit panel is currently trusted_access only.
Attendee self-edit at kiosk isn't finished — staff will need to do all edits on test day.
- **`@page { size }` in Chrome**: Chrome ignores the CSS page size for Save as PDF.
For physical Zebra printing the driver controls paper size — this is fine.
---
## Known Print Dialog Behavior (for reference)
| Browser | Save to PDF | Physical Printer |
|---|---|---|
| Firefox | Paper size locked to CSS `@page { size }` ✅ | Can select paper size in dialog |
| Chrome | Paper size = system default (letter/A4) ❌ | Can select paper size under "More settings" |
| Chrome Margin Setting | Result |
|---|---|
| Default | ❌ Inserts URL/date/page chrome, offsets badge centering |
| None | ✅ Badge centered correctly |
| Minimum | ✅ Badge centered correctly |
---
## Things to Note / Capture During Testing
Use this section to log observations on the day:
```
DATE: ___________
Driver version: ___________
Card stock type: ___________
CUPS printer name: ___________
Observations:
-
-
-
Font size adjustments needed:
Name default: was __px, adjusted to __px
Title default: was __px, adjusted to __px
Affiliations: was __px, adjusted to __px
Location: was __px, adjusted to __px
Physical offset needed (crop/margin): ___________
Bugs found:
-
-
```
---
## Follow-Up After Test Day
- [ ] Update font size defaults in `ae_comp__badge_print_controls.svelte` based on observations
- [ ] Note any physical margin/offset needed into `cfg_json: { print_margin: {...} }` once that UI exists
- [ ] Document driver version and CUPS config that worked in this file
- [ ] Commit any fixes, re-run `npx svelte-check`, commit clean

View File

@@ -0,0 +1,45 @@
**AE Firefly Theme Repair — Summary (Recovery & Integration Complete)**
- **Summary**: Investigation, targeted repair, and successful integration of the AE Firefly theme family and key UI components. This document records the final resolution, the migration of the Svelte 5 Modal component, and the validation of the `ae_app_3x_llm` branch as the project's stable baseline.
### 1. Root Cause Resolution (Themes)
- **Variables outside selectors**: Fixed. Custom-property declarations in `src/ae-firefly*.css` were moved into proper `[data-theme='AE_Firefly*']` selector blocks.
- **Variable precedence**: Resolved by enforcing `--background: ... !important` within the theme-specific selectors to ensure they override global `:root` defaults.
- **Files Repaired**:
- `src/ae-firefly.css`
- `src/ae-firefly-steelblue.css`
- `src/ae-firefly-indigo.css`
- `src/ae-firefly-rainbow.css`
### 2. Actions Taken (Recovery Phase)
- **Surgical Integration**: Selective harvesting of "90% done" work from WIP branches while preserving the integrity of the Lucide migration and Svelte 5 baseline.
- **`element_modal_v1.svelte`**:
- Successfully imported from `wip-modal-fix-attempt`.
- **Full Refactor**: Migrated from deprecated `svelte/legacy` and `<slot>` patterns to **Svelte 5 Snippets** and `{@render}` tags.
- Verified and tested as the new standard modal component.
- **Selective Vetting**:
- **Abandoned**: `element_input_v2.svelte` and legacy Badge View v1 (rejected due to legacy FontAwesome regressions and high error counts).
- **Removed**: Redundant `element_data_store_v2.svelte` (superseded by `v3`).
- **Kept**: Clean, Lucide-based versions of all core components already present on `ae_app_3x_llm`.
### 3. Repository State (Final Validation)
- **Baseline**: `ae_app_3x_llm` is now the unified, verified "known-good" state.
- **Validation**: `npx svelte-check` performed on the merged state returned **0 errors and 0 warnings**.
- **Cleanup**: Temporary integration branches have been deleted.
- **Backups**: `wip-modal-fix-attempt` and `wip/theme-fix` remain as reference points but are no longer active.
### 4. Merged Files (Key Updates)
- `src/ae-firefly*.css` (Repaired themes)
- `src/lib/elements/element_modal_v1.svelte` (New Svelte 5 Modal)
- `documentation/PROJECT__AE_Firefly_Theme_Repair_SUMMARY.md` (This document)
### 5. Final Status
- **Status**: **COMPLETE / STABLE**
- **Branch**: `ae_app_3x_llm`
- **Verification**: Verified via `svelte-check` and theme inspections.
---
*Prepared by: Gemini CLI (March 17, 2026)*
---
*Archival note (2026-03-20): `element_modal_v1.svelte` (referenced in §2 as "new standard modal") was subsequently retired — it had zero active importers. Modal usage in the codebase relies on Flowbite `<Modal>` component. See `AE__UI_Component_Patterns.md` §11.*

View File

@@ -0,0 +1,163 @@
PRES-MGMT Session View — Refactor Plan
**STATUS: ✅ RESOLVED (2026-02-26)**
## Resolution Summary
The "refresh twice" bug was fixed by addressing **two root causes** in the nested data loader chain:
1. **Disabled caching in nested loads**: Changed `try_cache: false` to `try_cache` (preserve parent value) in:
- `ae_events__event_session.ts``_refresh_session_id_background()`
- `ae_events__event_presentation.ts``_refresh_presentation_li_background()`
2. **Missing microtask yields**: Added `await Promise.resolve()` after each `db_save_ae_obj_li__ae_obj()` call to ensure Dexie liveQuery observers fire before functions return.
3. **Fire-and-forget nested loads**: Changed `forEach()` to `await Promise.all()` in presentation loader to block until all presenter loads complete.
**Result:** Session view now renders presentations AND presenters on first load without manual refresh.
**Performance Impact:** Adds ~100-200ms to initial navigation (time to write 1-5 presenter records + microtask yields), but guarantees correct first-render.
**Files Modified:**
- `src/lib/ae_events/ae_events__event_session.ts` (lines 100-107)
- `src/lib/ae_events/ae_events__event_presentation.ts` (lines 187-198)
- `src/lib/ae_events/ae_events__event_presenter.ts` (line 157-161)
**Documentation Updated:**
- `documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md` - Added "try_cache: false" bug section with detailed explanation
- Code comments added explaining the critical fixes in all modified loader functions
- Journals module updated with microtask yields for consistency (already preserved try_cache correctly)
**Test Status:**
- Manual testing: ✅ Confirmed working (session + presentations + presenters render on first load)
- Playwright test: Created at `tests/coldstart_event_session.test.ts` (requires valid session ID to run)
**Lessons Learned:**
1. **Always preserve `try_cache` through nested data loads** - Disabling caching at any level breaks the entire chain
2. **Add microtask yields after IndexedDB writes** - Ensures liveQuery observers fire before functions return
3. **Block on nested loads with `await Promise.all()`** - Fire-and-forget forEach() causes race conditions
4. **Journals module was already correct** - It preserved `try_cache` for nested entry loads; only added yields for consistency
5. **The existing blocking pattern was sufficient** - No special helper function needed; the bug was in the loader implementation, not the architecture
**What We Implemented:**
We effectively implemented **Option A (Blocking Hydration)** but at a lower level than originally planned. Instead of creating a new `load_session_with_relations()` helper, we fixed the existing loader chain to properly block and cache nested data. The `+page.ts` already used `await` for the session load - it just wasn't working because the underlying loaders weren't caching nested data.
---
## Original Plan (For Reference)
Goal
Make the Presentation Management Session view deterministic on cold-start (empty IndexedDB). The page must render Presentations, Presenters, and Hosted Files without requiring manual refreshes.
Constraints
- Svelte 5 runes and Dexie `liveQuery` behavior (observable recreation, subscription timing).
- Minimize user-perceived latency — keep navigation snappy where possible.
- Avoid large architectural changes unless necessary.
Options (high level)
A) Blocking Hydration (recommended for correctness)
- Block the route `+page.ts` load until the session and all directly required related objects are fetched from the backend and written into IndexedDB. Return `initial_session_obj` in the load data for immediate rendering.
- Pros: simplest to guarantee first-draw correctness; minimal component changes.
- Cons: adds latency to navigation (can be mitigated with optimistic UI or progress indicator).
B) Prefetch Related Records + Hydrate Fallback (hybrid)
- Non-blocking load but `+page.ts` returns `initial_session_obj` and small related-objects payloads (presentations, presenter IDs, hosted_file metadata). Components use these fallbacks while `liveQuery` takes over.
- Pros: keeps navigation responsive; often sufficient.
- Cons: requires careful payload shaping and DB write ordering.
C) Explicit Dependency Chaining in UI (advanced)
- Keep non-blocking loads and use explicit dependency chaining: write session -> await write completion -> then write presentations -> await -> then presenters, ensuring microtask queue flushes between writes. Use targeted `liveQuery` re-creation only when upstream dependency fully resolved.
- Pros: minimal route latency; deterministic ordering.
- Cons: more complex to implement and test.
Recommendation
Start with Option A (Blocking Hydration) for the session page to restore deterministic behavior quickly. After correctness is achieved, consider converting to Option B or C for improved perceived performance if needed.
Detailed Steps (Option A - Blocking Hydration)
1) Add a small helper in `events_func` (e.g., `load_session_with_relations`) that:
- fetches session by ID from API
- fetches related presentations (limit/filters as needed)
- fetches presenters referenced by those presentations (deduplicate IDs)
- fetches hosted_file metadata for presentation files (if required for the view)
- writes all results to IndexedDB in a controlled order (session -> presentations -> presenters -> hosted_files)
- returns a compact `initial_session_obj` payload containing fields needed for first-draw (session, presentation list, presenter summary)
Implementation note: Use `await db.transaction('rw', db_events.session, db_events.presentation, db_events.presenter, async () => {...})` if atomicity helps. Alternatively write in sequential awaits and call `await Promise.resolve()` after each write to let the microtask queue settle.
2) Update route loader: `src/routes/events/[event_id]/(pres_mgmt)/session/[session_id]/+page.ts` (create if missing) to call and `await` the helper, then return `initial_session_obj` on `data`.
Example pseudo-code:
export async function load({ params, parent }) {
const data = await parent();
if (browser) {
const init = await events_func.load_session_with_relations({ api_cfg: data[data.account_id].api, session_id: params.session_id, log_lvl: 0 });
data.initial_session_obj = init;
}
return data;
}
3) Ensure the page component `+page.svelte` uses the `initial_session_obj` as immediate fallback (it already does in Aether).
4) Add instrumentation logs inside `liveQuery` closures and the helper to verify ordering during QA.
5) Add tests (see below) and manual verification steps.
Alternative (Option B - Hybrid) Implementation Notes
- If you cannot block the route, return an `initial_session_obj` that includes minimal related object arrays (IDs + small metadata) and have `+page.svelte` write those into IDB before mounting heavy child components.
- Use `untrack()` to set selection IDs so stores are updated without causing premature reactivity loops.
Explicit Dependency Chaining (Option C) Notes
- Implement a single `prefetch` function that sequentially performs writes and `await Promise.resolve()` between stages.
- For debugging, add microtask delays (e.g., `await 0`) between writes to observe behaviour.
Testing and Verification
1) Integration test (Playwright recommended)
- Clear IndexedDB for the app origin.
- Navigate to `/events/<event_id>/.../session/<session_id>` and assert that the presentation list and presenters are visible within N ms without manual refresh.
- Repeat on subsequent navigations to ensure no regressions.
2) Unit tests
- For `events_func.load_session_with_relations`, stub API responses and assert DB writes are made in expected order.
3) Manual QA
- With a cold profile or after clearing Site storage, navigate to the session page and confirm content is present after the initial navigation and that no manual refreshes are required.
Migration and Rollout
- Implement Option A behind a feature flag if you want to control rollout.
- Short-term: apply Option A to the single problematic route to reduce blast radius.
- Long-term: consider a library-level helper to standardize "blocking prefetch for nested related records" across other pages.
Rollback Plan
- Because changes are additive and limited to one route and helper, revert the `+page.ts` modification and helper call to restore prior behavior.
Deliverables for tomorrow
- `events_func.load_session_with_relations` helper (TS) + unit tests
- Updated `+page.ts` loader for session route to `await` helper and return `initial_session_obj`
- Small test harness / Playwright test that reproduces the cold-start issue and verifies the fix
- Instrumentation logs temporarily enabled for QA
Estimated effort
- Blocking hydration implementation + tests: 2-4 hours
- Hybrid or chaining implementations: additional 2-6 hours depending on thoroughness
Notes about Svelte 5 + Dexie specifics
- Keep `liveQuery` closures stable; capture primitive IDs rather than reactive objects.
- Use `$derived` and `$derived.by` to keep observable instances stable across renders.
- Use `untrack()` when setting selection values to avoid premature subscriptions.
- After DB writes, allowing the microtask queue to settle (`await Promise.resolve()`) helps ensure observers are notified in the expected order during development and debugging.
If you want I can implement Option A for the session route tomorrow (create helper, update loader, add test).

View File

@@ -0,0 +1,106 @@
# Project: Unified Aether Platform Orchestration (V3)
> **Status: ✅ COMPLETE (2026-03-10)**
> `ae_app` is live in `aether_container_env/docker-compose.yml`. Both frontend and backend deploy together via `npm run deploy:staging` / `npm run deploy:prod`. Internal `ae_api` networking active. Healthcheck wired. Old `ae_env_node_app` is superseded (archive when ready).
> **Goal:** Consolidate the SvelteKit Frontend and FastAPI Backend into a single Docker Compose environment within `aether_container_env`.
## 1. Overview & Benefits
Currently, the platform runs in two separate Docker environments (`ae_env_node_app` and `aether_container_env`). Combining them into one allows for:
- **Unified Lifecycle:** Start/Stop/Update the entire stack with one command.
- **Internal Networking:** The Frontend (SvelteKit) can communicate with the Backend (FastAPI) via the high-speed internal Docker network (`ae_api:5005`) instead of routing through external IPs.
- **Shared Infrastructure:** Single instances of Redis, Dozzle, and Nginx serving both tiers.
- **Simplified Deployment:** Reduces the need for sibling directory dependencies on production servers.
---
## 2. Target Architecture (Workstation & Prod)
The unified environment will reside in `~/OSIT_dev/aether_container_env/`.
### Directory Mapping
- **API Source:** `~/OSIT_dev/aether_api_fastapi` -> Mounted to `ae_api`
- **App Source:** `~/OSIT_dev/aether_app_sveltekit` -> Mounted to `ae_app`
- **Nginx Config:** `~/OSIT_dev/aether_container_env/conf/nginx/`
- **Logs:** `~/OSIT_dev/aether_container_env/logs/`
---
## 3. Implementation Plan
### Step 1: Update `aether_container_env/.env`
Add these new variables to the master `.env` file:
```bash
# --- SvelteKit Frontend settings ---
AE_APP_SRC=/home/scott/OSIT_dev/aether_app_sveltekit
CONTAINER_AE_APP=ae_app_dev
AE_APP_REPLICAS=4
AE_APP_NODE_PORT=3001
# Note: Use internal DNS 'ae_api' for PUBLIC_AE_API_SERVER_INTERNAL
```
### Step 2: Integrate `ae_app` into `docker-compose.yml`
Add the consolidated SvelteKit service. This replaces the legacy Flask service:
```yaml
ae_app:
restart: always
build:
context: ${AE_APP_SRC}
dockerfile: Dockerfile
target: deploy-node
scale: ${AE_APP_REPLICAS}
env_file:
- ./.env
ports:
- "${AE_APP_NODE_PORT}:3000"
extra_hosts:
- "${DOCKER_AE_SERVER_EXTRA_HOST}"
- "${DOCKER_AE_APP_SERVER_EXTRA_HOST}"
- "${DOCKER_AE_API_SERVER_EXTRA_HOST}"
volumes:
# Mount source for real-time dev (Optional for production)
- ${AE_APP_SRC}:/srv/aether_app
- ${HOSTED_FILES_SRC}:/srv/hosted_files
- ${HOSTED_TMP_SRC}:/srv/hosted_tmp
depends_on:
- ae_api
- redis
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
```
### Step 3: Nginx Gateway Configuration
Update (or create) the Nginx template in `conf/nginx/` to route traffic to the internal `ae_app` service.
**Example Upstream Block:**
```nginx
upstream svelte_backend {
# Internal Docker DNS handles load balancing across replicas
server ae_app:3000;
# ip_hash; # Enable if session persistence is needed
}
```
### Step 4: Verification & Migration
1. **Healthcheck:** Ensure `src/routes/health/+server.ts` is active.
2. **Internal Proxy Test:** Update SvelteKit's `PUBLIC_AE_API_SERVER` to `ae_api` (internal) and verify connectivity.
3. **Clean Up:** Once verified, the old `ae_env_node_app` directory can be archived.
---
## 4. Scaling & Performance
- **API Scaling:** Controlled by `AE_API_REPLICAS`.
- **App Scaling:** Controlled by `AE_APP_REPLICAS`.
- **Memory Management:** Each replica is isolated. Shared state (caching) is handled via the internal **Redis** service.
- **Healthchecks:** Docker will automatically restart any `ae_app` replica that fails the `/health` check.
---
## 5. Deployment Commands (Unified)
```bash
# From aether_container_env/
docker compose up -d --build --remove-orphans
docker compose ps
docker compose logs -f ae_app
```

View File

@@ -0,0 +1,31 @@
## ✅ Completed (2026-03)
- [x] **[Stores] Phase 1 — Dead code cleanup** (`ae_stores.ts`, `ae_events_stores.ts`, `ae_idaa_stores.ts`): removed `ver_idb`, stale comments, `console.log` lines, Stripe button block (zero consumers), personal Novi UUIDs, dead alternatives. Net: 202 lines across 3 files. svelte-check: 0 errors. (2026-03-16)
- [x] **[Stores] Phase 2a — Split defaults into domain sub-files**: `ae_stores__auth_loc_defaults.ts`; `ae_events_stores__badges/launcher/leads/pres_mgmt_defaults.ts`. Spread-merged back into store structs — zero consumer changes. (2026-03-16)
- [x] **[Stores] Phase 2b — TypeScript interfaces for defaults sub-files**: `SiteCfgJson`, `AePerson`, `AeUser`, `AccessType`, `AuthLocState`; `BadgesLocState/SessState`; `SectionState`, `LauncherLocState/SessState`; `LeadsLocState/SessState`, `TmpLicense`; `PresMgmtLocState/SessState`. svelte-check: 0 errors. (2026-03-16)
- [x] **[UI]** Style Review Phase 1 & 2 complete — all non-frozen, non-IDAA routes migrated: FA→Lucide (events, pres_mgmt, core, badges, leads, hosted_files), `variant-*``preset-*` (all modules), `code_to_html` badge dict refactored to Lucide component map, FA CDN scoped to IDAA layout, global `svg.lucide { display: inline }` CSS rule added to fix icon inline flow. See `documentation/PROJECT__AE_Style_Review.md`. (2026-03-16)
- [x] **[UI]** Pres Mgmt Phase 3 — FA→Lucide icon migration across all 24 pres_mgmt files. (2026-03-16)
- [x] **[IDAA]** `ae_idaa_comp__event_obj_id_edit.svelte` — inlined Tailwind utilities, removed `<style>` block; eliminated all 23 `@apply`/`@reference` svelte-check warnings. (2026-03-16)
- [x] **[Badges]** Badge print page svelte-check fix: extracted print CSS to `static/ae-print-badge.css`; fixed unclosed `<script>` tag in `print/+page.svelte`. (2026-03-16)
- [x] **[Svelte/Tests]** svelte-check cleanup: fixed `select_ref_badge_type` `$state()` declaration; two `<svelte:component>` deprecations in launcher components; `page.evaluate()` two-arg pattern in `badge_print_layout.test.ts`. (2026-03-16)
- [x] **[Launcher]** Hosted file download button `require_auth` prop — added `require_auth?: boolean` (default `true`) to `ae_comp__hosted_files_download_button.svelte`; all existing consumers unchanged. Launcher `launcher_file_cont.svelte` passes `require_auth={false}` so unauthenticated kiosk users can open/download files without being blocked. (2026-03-16)
- [x] **[Security]** `PUBLIC_AE_API_SECRET_KEY` audit complete. Key is `PUBLIC_*` by design (always in client bundle). Highest-risk anonymous path uses limited-permission `PUBLIC_AE_BOOTSTRAP_KEY`. Full server-side migration not justified given JWT + account_id auth layers. Current state acceptable. (2026-03-11)
- [x] **[UX]** Session Expired banner — `ae_auth_error` store wired to API helpers; root layout sets `flag_expired` on 401/403; non-blocking dismissible banner rendered. (2026-03-12)
- [x] **[UX]** Access Denied UI standardized — `element_access_denied.svelte` created; `/core` layout, `/events/settings`, and `/events/badges/review` updated to use it. (2026-03-12)
- [x] **[Build]** Rollup/Vite circular dependency warnings eliminated — `manualChunks` in `vite.config.ts` colocates all `svelte/*` internals into a single `svelte-vendor` chunk, preventing `runtime.js` / `index-client.js` split (~35 warnings gone). (2026-03-11)
- [x] **[Refactor]** `try_cache` audit + sponsorship/event_file/hosted_file SWR alignment — removed vestigial `try_cache` params from `generate_qr_code`, `ae_core_functions` wrappers; added SWR fast/slow path to sponsorship loaders; changed `event_file` and `hosted_file` single-object loader defaults from `false``true` for consistency. (2026-03-11)
- [x] **[DevOps]** Frontend + Backend unified into single `aether_container_env` Docker Compose. `ae_app` service live with healthcheck, single exposed port (`AE_APP_NODE_PORT`), internal `ae_api` networking. Deploy scripts in `package.json` both target `../aether_container_env/docker-compose.yml`. (2026-03-10)
- [x] **[DevOps]** `/health` endpoint live at `src/routes/health/+server.ts`. Docker `HEALTHCHECK` uses it. (2026-03-10)
- [x] **[UI]** Dark mode `color-scheme` fix — `html.dark/light { color-scheme }` in `app.css`; all native browser controls now sync to app dark mode. (2026-03-10)
- [x] **[Launcher]** Location select → session auto-load bug fixed via `$derived.by()` liveQuery pattern. (2026-03-10)
- [x] **[Svelte]** `state_referenced_locally` warning fixes — 10 warnings resolved in IDAA archives/BB. (2026-03-09)
- [x] **[TypeScript]** Sign In/Out TS errors fixed — `user_id` / `person_id` typed as `string | null`. (2026-03-09)
- [x] **[Tests]** All badge data integrity and attendee workflow Playwright tests passing. Root causes documented in `tests/README.md`. (2026-03)
- [x] **[Badges]** Badge print controls panel, QR code, duplex wiring, review form, print button, multi-word fulltext search, `data-testid` attributes. (2026-03)
- [x] **[UI]** Firefly Theme + Pres Mgmt Visual Redesign (5 files). (2026-03-06)
- [x] **[Docs]** UI Style Guidelines + Component Patterns docs created. (2026-03-06)
- [x] **[API]** V3 Lookup system integration; Event File V3 mapping; `event_session` search 400-error fix. (2026-02/03)
- [x] **[API]** All CRUD helpers on V3 `/v3/crud/...` paths. (2026-02)
- [x] **[Security]** Purged `x-aether-api-token`; fixed misplaced CORS headers; Account ID Scavenging. (2026-02)
- [x] **[Security]** Playwright integration tests replace `verify_jwt_logic.js` simulation tests. (2026-03)
- [x] **[Framework]** `AE_Obj_Field_Editor_V3` with Svelte 5 Runes. CRUD v2 fully retired. (2026-03-05)
- [x] **[IDAA]** Bulletin Board and Recovery Meetings functionality verified. (2026-02)

View File

@@ -0,0 +1,227 @@
# Frontend Agent Task List
> Use this file to track steps for complex features or bug fixes.
> **Status:** Stable — ongoing development.
## 🚧 Upcoming High Priority
### [Stores] Svelte 4 → Svelte 5 State Migration (prerequisite for Phase 2c)
The app uses `svelte-persisted-store` (Svelte 4 store contract) for all core persisted state
(`ae_loc`, `idaa_loc`, `ae_api`, `ae_sess`, etc.). In Svelte 5 `$effect`, reading **any field**
of a Svelte 4 store subscribes to the **entire store** — coarse-grained reactivity. This is the
root cause of the IDAA Novi re-auth bug (2026-03-30): unrelated `$ae_loc` writes (e.g. iframe
height, SWR cfg reload) triggered the Novi verification effect repeatedly.
Migration target: replace `svelte-persisted-store` with Svelte 5 `$state`-based persistence
(e.g. `runed` `PersistedState`, or a lightweight custom wrapper). This gives fine-grained
reactivity — only effects that actually read a changed field re-run.
**Phased approach (do NOT do all at once):**
- [ ] **Phase A — Project plan + wrapper decision:** Write `PROJECT__Stores_Svelte5_Migration.md`.
Decide: `runed` library vs. custom `$state` + localStorage wrapper. Audit all store consumers.
Identify stores in priority order. Estimate blast radius per store.
- [ ] **Phase B — Core auth stores (highest impact, start here):**
- `ae_loc` (persisted) — auth flags, site cfg, UI state; ~471 consumer sites across 150+ files
- `idaa_loc` (persisted) — Novi auth, IDAA query prefs
These two cause the most reactive noise. Migrating them also unlocks Phase 2c (separate `ae_auth`
store) since the callsite sweep is now required anyway.
- [ ] **Phase C — Remaining persisted stores:**
- `ae_api` (persisted) — API config / JWT
- `ae_events_stores` persisted entries (badges, launcher, leads, pres_mgmt loc stores)
- [ ] **Phase D — Non-persisted writable stores:**
- `ae_sess`, `idaa_sess`, `slct`, `slct_trigger`, `ae_auth_error`, `ae_trig`, `ae_snip`, etc.
- Lower urgency (no localStorage churn), but fine-grained reactivity still beneficial.
- [ ] **Phase E — Phase 2c (unblocked after B):** Split `ae_loc` into `ae_auth` + `ae_app`
(see entry below — ~471 callsites, but sweep is cheap once already touching every consumer).
**Project plan doc needed:** Yes — scope is app-wide. Do NOT start Phase B without Phase A.
---
### [Stores] Refactor — Phase 2c (deferred)
Phases 1, 2a, 2b are complete (see ✅ Completed below). One phase remaining:
- [ ] **Phase 2c — Actual separate stores (`ae_auth`, `ae_app`):** Requires touching ~471
`$ae_loc.*` auth-field read sites across 150+ files. Deferred until a Svelte runes migration
of the store layer itself (touching every component anyway makes the callsite sweep cheap).
### [Backend] Join event_location_id onto event_presenter API view
The `event_presenter` object currently has `event_session_id` but not `event_location_id`.
When navigating from the Presenter View to the Launcher, the frontend has to do a secondary
session lookup to discover the location (magic redirect in launcher base `+page.svelte`).
Joining `event_session.event_location_id` into the presenter view/response would let the
frontend pass the location directly in the Launcher URL without the extra lookup.
- [x] Backend: added `event_location_id` (and `event_location_id_random`) to the `event_presenter` view or API response (2026-04-09)
- [x] Frontend: updated `ae_EventPresenter` type and `properties_to_save`; now pass as `events__launcher_id` in `presenter_page_menu.svelte` (2026-04-09)
### [TypeScript] svelte-check hidden errors — discovered 2026-03-27
**HOW WE FOUND THIS:** The `@lucide/svelte` 0.577.0 update (2026-03-10) dropped `class` from
`IconProps`. Fixing it required a `declare module '@lucide/svelte'` augmentation. That
augmentation was mistakenly placed in `app.d.ts`, which is a *script-context* declaration file
(no `export {}`). In that context, `declare module` is an **ambient replacement**, not a merge —
it wiped all icon exports from svelte-check's view, surfacing 1368 previously hidden errors.
Once moved to `src/lucide-augment.d.ts` (a proper module file with `export {}`), the masking
lifted and the real pre-existing errors became visible.
**Lesson:** A broken ambient declaration can silently hide unrelated errors. If svelte-check
suddenly jumps to 0 errors, verify it's not because a bad `.d.ts` replaced a package's types.
**Current state (2026-03-31):** 32 errors, 0 warnings — all `ModalProps.children`.
- [ ] **[flowbite-svelte] `ModalProps.children` — 31 errors across 26 files.** The flowbite-svelte
`Modal` component API changed; `children` is no longer a direct prop (now Svelte snippet-based).
Affected files span journals, pres_mgmt, events/settings, and IDAA archives.
Run `npx svelte-check 2>&1 | grep ModalProps` to get the current list.
Fix pattern: replace `children` prop binding with Svelte snippet syntax per flowbite-svelte docs.
- [ ] **[IDAA] Make `contact_li_json_ext` searchable — Recovery Meeting contact search (2026-04-08)**
Members cannot search for meetings by contact name or email. `contact_li_json` data is not
included in `default_qry_str` and MariaDB cannot substring-search a JSON longtext directly.
The `event` table already has `contact_li_json_ext` (STORED GENERATED, indexed) to work around this.
**Backend (blocked on this first):** Add `contact_li_json_ext` to the searchable fields
whitelist for the `event` object type — likely a one-line change in `ae_obj_types_def.py`
or the event object definition. Message sent to backend agent 2026-04-08.
**Frontend (after backend ships):**
- `src/lib/ae_events/ae_events__event.ts``search__event()`: add `contact_li_json_ext`
as an OR condition alongside `default_qry_str` when `qry_str` is present.
- `src/routes/idaa/(idaa)/recovery_meetings/+page.svelte` fast-path IDB filter: parse
`contact_li_json` and include contact names/emails in the local text match check.
- [ ] **[IDAA / Events] Audit `default_qry_str` coverage in other event search pages.**
The backend was updated 2026-03-31 to expose `default_qry_str` in API responses.
Frontend fix applied to Recovery Meetings (`+page.svelte` + `properties_to_save`).
Check all other event search pages that use `db_events.event.filter()` or a secondary
post-API text filter — they may have the same mismatch (local searches `name`/`description`
only while server uses `default_qry_str`). Start with: any route under `/events/` or `/idaa/`
that has a full-text search input.
- [x] **[package.json] Remove orphaned ShadCN/bits-ui packages.** `shadcn-svelte` and `bits-ui`
remain in `package.json` but have no usages — `src/lib/components/ui/` was removed 2026-03-27
(trashed to `~/tmp/agents_trash/shadcn_components_ui_2026-03-27`). Removed from `package.json` and
`package-lock.json` on 2026-04-02.
### [IDAA] Jitsi config editor + live site fix
- [ ] **Fix live site (id=17) `jitsi_token_endpoint` pointing to dev-api:** DB has
`https://dev-api.oneskyit.com/api/jitsi_token` for both site 10 and site 17 (IDAA live).
Need to update site 17 in **production** to `https://api.oneskyit.com/api/jitsi_token`.
SQL: `UPDATE site SET cfg_json = JSON_SET(cfg_json, '$.jitsi_token_endpoint', 'https://api.oneskyit.com/api/jitsi_token') WHERE id = 17;`
- [ ] **Add IDAA Jitsi config editor UI** to the jitsi_reports page (administrator_access only),
alongside the existing Jitsi URL Builder section. Should allow editing key fields in
`site_cfg_json` without needing phpMyAdmin:
- `jitsi_token_endpoint` — the JWT signing endpoint (needs to point to prod)
- Jitsi domain default (currently hardcoded as `jitsi.dgrzone.com` fallback in the page)
- `novi_jitsi_mod_li` — list of Novi UUIDs who get moderator privileges
Read from `$ae_loc.site_cfg_json`, PATCH the site record via V3 CRUD
(`PATCH /v3/crud/site/{id}/`), reload `$ae_loc.site_cfg_json` on save so it takes
effect without re-login.
### [PWA] Service worker ignoring `chrome-extension://` requests
Browser console shows repeated errors:
```text
TypeError: Failed to execute 'put' on 'Cache': Request scheme 'chrome-extension' is unsupported
```
The service worker's fetch/install handler is trying to cache requests with `chrome-extension://`
URLs (injected by browser extensions), which the Cache API rejects. Fix: filter out non-`http`/`https`
requests before attempting to cache. In the service worker fetch handler, add a guard:
```js
if (!event.request.url.startsWith('http')) return; // skip chrome-extension:// etc.
```
Locate in `static/service-worker.js` or the Vite PWA plugin config. Low severity — doesn't break
functionality, but pollutes the console and may cause unhandled promise rejections.
### [Badges] Remaining badge work before first live event
- **Badge print controls UX polish:** Scott has improvements in mind — TBD next session.
File: `ae_comp__badge_print_controls.svelte`.
### [CSS] Global placeholder text color — too dark in light mode
Placeholder text inherits full input text color in light mode (Tailwind CSS default), making
placeholders indistinguishable from filled-in values. Most visible in badge print controls
where placeholders show the actual badge value (e.g. "John Smith").
Workaround: scoped `::placeholder` rule added to `ae_comp__badge_print_controls.svelte`
(gray-400 light / gray-500 dark) — `commit 7733ef8`.
**Long-term fix:** Add a global rule to the main CSS (e.g. `src/app.css` or a theme file):
```css
::placeholder {
color: #9ca3af; /* gray-400 */
opacity: 1; /* overrides Firefox's 0.54 default */
}
.dark ::placeholder {
color: #6b7280; /* gray-500 */
}
```
Once the global rule is in place, remove the scoped workaround from the badge controls.
### [Leads] Exhibitor Lead Scanning — IN PROGRESS (demo-ready prep)
Module is substantially built as a PWA (no Electron). Core flow works end-to-end.
Spec: `documentation/PROJECT__AE_Events_Exhibitor_Leads_v3.md` and `_detail.md`.
Full audit: `src/routes/events/[event_id]/(leads)/` and `src/lib/ae_events/ae_events__exhibit*.ts`.
**What's working:**
- Exhibit search/landing (`/leads/`) — SWR, local + API search, sort
- Exhibit detail page — 4-tab layout, sticky header with Add/List toggle, auto-refresh timer
- Tab 1 (Start): sign-in via shared passcode OR licensed user (email + passcode)
- Tab 2 (Add): QR scan (confirm mode — replaced rapid/qualify) + manual badge search; duplicate/re-enable detection on both
- Tab 3 (List): SWR lead list, licensee filter (All / My Leads), sort options, export button
- Tab 4 (Manage): admin tools, booth profile edit, passcode, license mgmt, custom questions config, app settings (refresh interval, clear IDB/localStorage, reload)
- Lead detail page: view/edit custom question responses, exhibitor notes (TipTap), priority/enable flags
- Export wired to V3 action endpoint `/v3/action/event_exhibit/{id}/tracking_export` (CSV/XLSX)
**Remaining before demo:**
- [x] **Export endpoint** — V3 action endpoint confirmed live on backend (2026-03-16). Returns 403 if
`leads_api_access` is not enabled on the exhibit — expected behavior. Export button now gated in
UI: only renders when `$lq__exhibit_obj?.leads_api_access === true`. Enable via:
`PATCH /v3/crud/event_exhibit/{id}` with `{ "leads_api_access": true }`.
- [x] **`allow_tracking` gate** — implemented (2026-03-16). QR scanner shows a warning card and
blocks the add. Manual search shows a ShieldOff "Opt-Out" badge per row and guards `add_as_lead`.
Opt-in model: `allow_tracking` must be explicitly `true` on the badge. Also added `allow_tracking`
and `agree_to_tc` to `ae_EventBadge` in `ae_types.ts`.
**Demo note:** ensure test badges have `allow_tracking = true` or no one can be added.
- [x] **Payment component**`ae_comp__exhibit_payment.svelte` fully implemented (2026-03-27).
Reads Stripe config from `$ae_loc.site_cfg_json` (`stripe_publishable_key`, `stripe_btn_1/3/6/10_license`).
License tier selector (1/3/6/10 users) with `{#key}` remount pattern for Stripe web component.
3 states: paid confirmation (priority=true), admin setup hint / "contact organizer" (no Stripe config),
payment form. `client_reference_id=exhibit_id`. TypeScript declaration in `app.d.ts`.
Stripe keys verified visible in `$ae_loc.site_cfg_json` on dev/demo site. Keys need validity check in Stripe dashboard.
- [x] **End-to-end smoke test (canceled by client)** — sign in with shared passcode, scan/search a badge, add a lead, view detail, add notes/responses, export CSV; canceled 2026-04-09.
- [x] **Install prompt** — PWA install nudge implemented (2026-03-16). `pwa_install.svelte.ts`
singleton captures `beforeinstallprompt` (Chrome/Android/desktop) and detects iOS Safari
for manual "Share → Add to Home Screen" instructions. Reusable `element_pwa_install_prompt.svelte`
placed on the Leads Start tab between the feature grid and sign-in. `pwa_install.init()` wired
into root `+layout.svelte`; dismiss persists 7 days via localStorage. svelte-check: 0 errors.
### [DevOps] Remaining deployment items
- [x] **Wire AE_APP_REPLICAS:** `docker-compose.yml` line 147 already has `scale: ${AE_APP_REPLICAS:-1}`. (verified 2026-03-11)
- [x] **Archive ae_env_node_app:** Archived as tar.gz under `~/OSIT_dev/backups/`; old history/docs moved to `~/OSIT_dev/for_reference_only/`. (2026-03-11)
- [x] **Build Optimization:** Current state finalized. Local Gitea instance stood up at `git.dgrzone.com` (Docker, home server) — future: migrate repos from Bitbucket, verify Backblaze/restic backups cover Gitea data. (2026-03-11)
- [x] **Remote deploy script:** `aether_container_env/deploy.sh` — SSH-triggered from workstation via `npm run deploy:remote:test/prod`. Handles git pull (ff-only) + docker build + restart. Tested and working on test env. (2026-03-25)
- [x] **`.env.default` cleanup:** Removed 16 dead variables, added missing `AE_NETWORK_NAME`/`CONTAINER_DOZZLE`/`AE_DOZZLE_PORT`, parameterized all container names (`CONTAINER_MARIADB`, `CONTAINER_PMA`, `CONTAINER_AE_OPS`) with `:-default` fallbacks in compose. ("Dozzle" = log viewer container.) (2026-03-26)
- [x] **Prod deploy:** Run `npm run deploy:remote:prod` (off-peak). Prerequisites: both repos pushed to Bitbucket ✓; verify `.env.prod` exists in `/srv/apps/prod_aether_app_sveltekit/` on Linode before running. (2026-03-30)
- [x] **Bitbucket → SSH migration:** Switched all three repos (`aether_app_sveltekit`, `aether_container_env`, `aether_api_fastapi`) to SSH remotes (`git@bitbucket.org`) on workstation. App passwords deprecated — SSH unaffected. (2026-03-27)
- [ ] **Branch strategy cleanup:** All environments (test, prod, bak) currently pull from same branches. `deploy.sh` defaults are `ae_app_3x_llm` / `development` — acceptable for now but should establish proper branch separation (e.g. `main`/`master` for prod).
- [ ] **Tier 2 deploy (Gitea webhook):** Push-triggered deploys via Gitea webhook → listener on Linode → `deploy.sh`. Deferred until Gitea usage is more established.
### [General]
- [x] **Temp Cleanup:** `cleanup_tmp_files` wired in `launcher_background_sync.svelte`; called at launcher startup. Confirmed working. (2026-03-11)
- [x] **`window.print()` for badge print button:** Wired in `ae_comp__badge_print_controls.svelte` — increments count, fires `window.print()`, redirects to badge search. (done)
- **Input Field Audit:** Several input fields are missing `name`/`id` attributes or `data-testid`. Known examples: badge override fields in `ae_comp__badge_obj_view.svelte`; template name input in `ae_comp__badge_template_form.svelte`. Matters for: accessibility, autofill, label associations, and test targeting. (For tests, use `getByLabel()` rather than `input[value*=...]` which only checks the HTML attribute, not the Svelte-bound DOM property.)
## ✅ Completed (2026-03)
## ✅ Completed (archived)
See the full completed history in [documentation/TODO__Agents__ARCHIVE_2026-03.md](documentation/TODO__Agents__ARCHIVE_2026-03.md).

View File

@@ -0,0 +1,54 @@
# Frontend Agent Task List (Archived May 2026)
## ✅ Completed (2026-05)
### [API] GET/POST retry hardening — differentiate timeout aborts vs intentional aborts
**Status:** ✅ Completed (2026-05-21)
- GET/POST now explicitly distinguish abort class in helper code.
- Timeout-triggered aborts are retryable via existing retry loop; intentional aborts fail fast.
- Backoff behavior retained (`2s -> 4s -> 6s -> 8s`).
- Validation done via Playwright tests.
### [API] PATCH/DELETE retry hardening — parity with GET/POST
**Status:** ✅ Completed (2026-05-21)
- PATCH and DELETE now implement the same retry-classification model used in GET/POST.
- Added explicit fail-fast for 400/401/403/422.
- DELETE now triggers the session-expired banner on 401/403.
### [Testing] V3 API performance probe (basic stress rounds)
**Status:** ✅ Completed baseline harness (2026-05-21)
- Implemented a gated Playwright probe for quick repeated list-query timing against live V3 endpoints.
- Writes reports to `tests/results/`.
### [IDAA] Random "Access Denied" — Root Cause Review & Fixes
**Status:** ✅ Resolved (2026-05-19)
- Server-side Novi verification migrated to V3 action endpoint.
- Extended Novi TTL to 12 hours.
- Hardened retry and timeout logic in `+layout.svelte`.
### [IDAA] Server-side Novi verification — 503 not auto-retried
**Status:** ✅ Fixed (2026-05-20)
### [IDAA] Jitsi Reports filters
**Status:** ✅ Finished (2026-05-06)
- Added Novi UUID exclusion plus meeting-name whitelist filtering.
### [PWA] Service worker ignoring `chrome-extension://` requests
**Status:** ✅ Fixed (2026-05-14)
- Added guard to filter out non-http/https requests before Attempting to cache.
### [Electron/Launcher] Display mirroring auto-detection
**Status:** ✅ Completed (2026-05-20)
- `native:set-display-layout` now auto-detects displays via `displayplacer list`.
### [Launcher] Force Sync Location
**Status:** ✅ Completed (2026-05-21)
- Implemented manual trigger and background engine logic to pre-cache all location files.
### [Launcher] Chronological Download Priority
**Status:** ✅ Completed (2026-05-21)
- Refactored download queue to prioritize Event Assets > Early Sessions > Presentation Order > Created Date.
### [Launcher] Error handling + fallback
**Status:** ✅ Completed (2026-05-14)
- Post-script failure surfaces 'fallback' status; `open_cmd` failure falls back to OS default.

40
eslint.config.js Normal file
View File

@@ -0,0 +1,40 @@
// @ts-check
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import svelte from 'eslint-plugin-svelte';
import prettier from 'eslint-config-prettier';
import globals from 'globals';
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
svelte.configs['flat/recommended'],
prettier,
{
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
},
{
files: ['**/*.svelte'],
languageOptions: {
parserOptions: {
parser: tseslint.parser
}
}
},
{
ignores: ['build/', '.svelte-kit/', 'node_modules/']
},
{
rules: {
'@typescript-eslint/no-unused-vars': 'warn',
// No base path configured — this rule is not applicable to this project
'svelte/no-navigation-without-resolve': 'off'
}
}
);

10182
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +1,122 @@
{
"name": "svelte-app",
"version": "1.0.0",
"private": true,
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"start": "sirv public --single --port 5555",
"validate": "svelte-check"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"@rollup/plugin-typescript": "^8.0.0",
"@tsconfig/svelte": "^1.0.0",
"rollup": "^2.3.4",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"svelte": "^3.0.0",
"svelte-check": "^1.0.0",
"svelte-preprocess": "^4.0.0",
"tslib": "^2.0.0",
"typescript": "^4.0.0"
},
"dependencies": {
"axios": "^0.21.1",
"sirv-cli": "^1.0.0"
}
"name": "osit-aether-app-svelte",
"version": "3.00.20",
"description": "One Sky IT's Aether App created with Svelte, SvelteKit, Tailwind CSS, Lucide, Font Awesome, and Skeleton UI. -Scott Idem",
"homepage": "https://oneskyit.com/",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"build:dev": "vite build --mode dev",
"build:test": "vite build --mode test",
"build:prod": "vite build --mode prod",
"preview": "vite preview",
"test": "npm run test:integration && npm run test:unit",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write .",
"test:integration": "playwright test",
"test:unit": "vitest",
"build:docker:dev": "docker compose -f ../aether_container_env/docker-compose.yml build ae_app && docker compose -f ../aether_container_env/docker-compose.yml up -d ae_app",
"compose:down": "docker compose -f ../aether_container_env/docker-compose.yml --profile database down",
"deploy:remote:test": "ssh linode.oneskyit.com 'bash /srv/env/test_aether/deploy.sh test'",
"deploy:remote:prod": "ssh linode.oneskyit.com 'bash /srv/env/prod_aether/deploy.sh prod'"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@playwright/test": "^1.56.1",
"@skeletonlabs/skeleton": "^4.*.*",
"@skeletonlabs/skeleton-svelte": "^4.*.*",
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/adapter-node": "^5.0.0",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.48.5",
"@sveltejs/vite-plugin-svelte": "^6.0.0",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10",
"@types/eslint": "^9.0.0",
"@types/node": "^25.0.0",
"@types/qrcode": "^1.5.5",
"@typescript-eslint/eslint-plugin": "^8.47.0",
"@typescript-eslint/parser": "^8.47.0",
"clsx": "^2.1.1",
"eslint": "^9.0.0",
"eslint-config-prettier": "^10.0.0",
"eslint-plugin-svelte": "^3.13.0",
"flowbite": "^4.0.0",
"globals": "^16.5.0",
"highlight.js": "^11.10.0",
"lowlight": "^3.2.0",
"mode-watcher": "^1.0.0",
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.2.6",
"sass-embedded": "^1.81.0",
"svelte": "^5.43.10",
"svelte-awesome-color-picker": "^4.0.0",
"svelte-check": "^4.0.0",
"svelte-highlight": "^7.8.4",
"svelte-idle": "^3.0.1",
"tailwind-merge": "^3.0.0",
"tailwind-variants": "^3.*.*",
"tailwindcss": "^4.1.10",
"tailwindcss-animate": "^1.0.7",
"tslib": "^2.4.1",
"typescript": "^5.5.0",
"typescript-svelte-plugin": "^0.3.50",
"vite": "^7.0.0",
"vitest": "^4.0.0"
},
"vitest": {
"exclude": [
"tests"
]
},
"type": "module",
"overrides": {
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.8",
"@codemirror/language": "^6.11.3",
"@codemirror/commands": "^6.10.0",
"@codemirror/lang-markdown": "^6.5.0",
"@codemirror/autocomplete": "^6.20.0",
"@codemirror/search": "^6.5.11",
"@codemirror/lint": "^6.9.2",
"@lezer/common": "^1.4.0",
"@lezer/highlight": "^1.2.3",
"@lezer/lr": "^1.4.4"
},
"dependencies": {
"@codemirror/autocomplete": "^6.20.0",
"@codemirror/commands": "^6.10.0",
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.3",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-markdown": "^6.5.0",
"@codemirror/language": "^6.11.3",
"@codemirror/language-data": "^6.5.1",
"@codemirror/lint": "^6.9.2",
"@codemirror/search": "^6.5.11",
"@codemirror/state": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/view": "^6.38.8",
"@floating-ui/dom": "^1.6.0",
"@lucide/svelte": "^0.*.0",
"@popperjs/core": "^2.11.0",
"@tailwindcss/vite": "^4.1.10",
"axios": "^1.7.0",
"dayjs": "^1.11.10",
"dexie": "^4.0.0",
"flowbite-svelte": "^1.28.1",
"html5-qrcode": "^2.3.8",
"lucide-svelte": "^0.*.0",
"marked": "^17.0.0",
"openai": "^6.10.0",
"prettier-plugin-tailwindcss": "^0.7.2",
"qrcode": "^1.5.4",
"runed": "^0.37.1",
"svelte-persisted-store": "^0.12.0",
"typescript-eslint": "^8.47.0"
}
}

25
playwright.config.ts Normal file
View File

@@ -0,0 +1,25 @@
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
webServer: {
command: 'npm run dev',
port: 5173,
reuseExistingServer: true,
},
testDir: 'tests',
testMatch: 'tests/**/*.test.ts',
testIgnore: ['tests/disabled/**'],
reporter: 'list',
use: {
baseURL: 'http://demo.localhost:5173',
// baseURL: 'https://dev-demo.oneskyit.com',
trace: 'on-first-retry',
// Arch Linux: Playwright's downloaded Chromium requires Ubuntu system libs (libicu74 etc.)
// that don't exist on Arch. Use the system Chromium package instead.
launchOptions: {
executablePath: '/usr/bin/chromium',
},
}
};
export default config;

View File

@@ -1,88 +0,0 @@
html, body {
position: relative;
/* width: 100%; */
height: 100%;
max-width: 100%;
}
body {
/* color: #333;
margin: 0;
padding: 8px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; */
}
h1 {
color: hsla(0, 0%, 5%, 1);
/*text-transform: uppercase;*/
/* font-size: 4rem; */
/* font-weight: 100; */
}
h2 {
color: hsla(0, 0%, 10%, 1);
/*text-transform: uppercase;*/
/* font-size: 4rem; */
/* font-weight: 100; */
}
a {
/* color: rgb(0,100,200);
text-decoration: none; */
}
a:hover {
/* text-decoration: underline; */
}
a:visited {
/* color: rgb(0,80,160); */
}
label {
/* display: block; */
}
input, button, select, textarea {
/* font-family: inherit;
font-size: inherit;
-webkit-padding: 0.4rem 0;
padding: 0.4rem;
margin: 0 0 0.5rem 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px; */
}
input:disabled {
color: hsla(0, 0%, 50%, 1);
}
button {
/* color: #333;
background-color: #f4f4f4;
outline: none; */
}
button:disabled {
color: hsla(0, 0%, 50%, 1);
}
button:not(:disabled):active {
background-color: hsla(0, 0%, 50%, 1);
}
button:focus {
/* border-color: #666; */
}
button:hover {
/* background-color: green; */
}
#my_body {
/* outline: solid thin red; */
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,35 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Index - Svelte</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.2/css/all.css">
<link rel='stylesheet' href='https://static.oneskyit.com/css/global.css'>
<link rel='stylesheet' href='/app_global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.9.4/dayjs.min.js" integrity="sha512-XZSHSEFj4QeE0G4pwy4tToyAhF2VXoEcF9CP0t1PSZMP2XHhEEB9PjM9knsdzcEKbi6GRMazdt8tJadz0JTKIQ==" crossorigin="anonymous"></script>
<script defer src='https://static.oneskyit.com/js/utilities.js'></script>
<script defer src='/build/bundle.js'></script>
</head>
<body>
<header><h1>Index - Dev Svelte App</h1></header>
<main>
<a href="log_client_viewing">log_client_viewing</a>
<a href="membership_member_manage">membership_member_manage</a>
<a href="user_person">user_person</a>
<div id="test_container" class="svelte_container">
</div>
</main>
<footer>
This is the footer
</footer>
</body>
</html>

View File

@@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Log Client Viewing - Dev Svelte App</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.2/css/all.css">
<link rel='stylesheet' href='https://static.oneskyit.com/css/global.css'>
<link rel='stylesheet' href='/app_global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.9.4/dayjs.min.js" integrity="sha512-XZSHSEFj4QeE0G4pwy4tToyAhF2VXoEcF9CP0t1PSZMP2XHhEEB9PjM9knsdzcEKbi6GRMazdt8tJadz0JTKIQ==" crossorigin="anonymous"></script>
<script defer src='https://static.oneskyit.com/js/utilities.js'></script>
<script defer src='/build/bundle.js'></script>
<script>
let account_id = 'TblpWmPauKw';
</script>
</head>
<body>
<header><h1>Log Client Viewing - Dev Svelte App</h1></header>
<main>
<div id="log_client_viewing_list_container" class="svelte_container">
</div>
</main>
</body>
</html>

View File

@@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Membership Member Manage - Dev Svelte App</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.2/css/all.css">
<link rel='stylesheet' href='https://static.oneskyit.com/css/global.css'>
<link rel='stylesheet' href='/app_global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.9.4/dayjs.min.js" integrity="sha512-XZSHSEFj4QeE0G4pwy4tToyAhF2VXoEcF9CP0t1PSZMP2XHhEEB9PjM9knsdzcEKbi6GRMazdt8tJadz0JTKIQ==" crossorigin="anonymous"></script>
<script defer src='https://static.oneskyit.com/js/utilities.js'></script>
<script defer src='/build/bundle.js'></script>
<script>
let account_id = '_PRgRxSkzu-Xg-V4Ft1RGg';
let membership_member_id = '6jMd9WwnMbs';
</script>
</head>
<body>
<header><h1>Membership Member Manage - Dev Svelte App</h1></header>
<main>
<div id="membership_member_manage_container" class="svelte_container">
</div>
</main>
</body>
</html>

View File

@@ -1,36 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>User Person - Dev Svelte App</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.2/css/all.css">
<link rel='stylesheet' href='https://static.oneskyit.com/css/global.css'>
<link rel='stylesheet' href='/app_global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.9.4/dayjs.min.js" integrity="sha512-XZSHSEFj4QeE0G4pwy4tToyAhF2VXoEcF9CP0t1PSZMP2XHhEEB9PjM9knsdzcEKbi6GRMazdt8tJadz0JTKIQ==" crossorigin="anonymous"></script>
<script defer src='https://static.oneskyit.com/js/utilities.js'></script>
<script defer src='/build/bundle.js'></script>
<script>
let account_id = '_PRgRxSkzu-Xg-V4Ft1RGg';
let person_id = 'OfcrSXX_evI';
let user_id = 'VMvg8X3QnZM';
</script>
</head>
<body>
<header><h1>User Person - Dev Svelte App</h1></header>
<main>
<div id="user_container" class="svelte_container">
</div>
<div id="person_container" class="svelte_container">
</div>
</main>
</body>
</html>

View File

@@ -1,83 +0,0 @@
import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';
import typescript from '@rollup/plugin-typescript';
import css from 'rollup-plugin-css-only';
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
export default {
input: 'src/main.ts',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
preprocess: sveltePreprocess({ sourceMap: !production }),
compilerOptions: {
// enable run-time checks when not in production
dev: !production
}
}),
// we'll extract any component CSS out into
// a separate file - better for performance
css({ output: 'bundle.css' }),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
typescript({
sourceMap: !production,
inlineSources: !production
}),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};

View File

@@ -0,0 +1,320 @@
#!/usr/bin/env python3
"""
migrate_fa_to_lucide.py — Replace FontAwesome <span class="fas fa-X"> with Lucide components.
Usage:
python3 scripts/migrate_fa_to_lucide.py src/routes/events/[event_id]/\(pres_mgmt\)/
Skips content inside HTML comments. Adds/merges lucide-svelte imports.
"""
import re
import sys
import os
from pathlib import Path
# ── FA icon → Lucide component name ─────────────────────────────────────────
FA_TO_LUCIDE = {
'fa-spinner': 'LoaderCircle',
'fa-cog': 'LoaderCircle', # only when fa-spin
'fa-sync-alt': 'RefreshCw',
'fa-times': 'X',
'fa-exclamation-triangle': 'TriangleAlert',
'fa-check': 'Check',
'fa-check-circle': 'CircleCheck',
'fa-plus': 'Plus',
'fa-minus': 'Minus',
'fa-save': 'Save',
'fa-edit': 'Pencil',
'fa-eye': 'Eye',
'fa-eye-slash': 'EyeOff',
'fa-toggle-on': 'ToggleRight',
'fa-toggle-off': 'ToggleLeft',
'fa-star-of-life': 'Asterisk',
'fa-id-card': 'IdCard',
'fa-paper-plane': 'Send',
'fa-map-marker-alt': 'MapPin',
'fa-file-alt': 'FileText',
'fa-envelope': 'Mail',
'fa-book': 'BookOpen',
'fa-angle-right': 'ChevronRight',
'fa-user': 'User',
'fa-tasks': 'ListChecks',
'fa-plane': 'Plane',
'fa-list': 'List',
'fa-link': 'Link',
'fa-file-archive': 'Archive',
'fa-comment-dots': 'MessageCircle',
'fa-chevron-up': 'ChevronUp',
'fa-chevron-down': 'ChevronDown',
'fa-camera': 'Camera',
'fa-barcode': 'Barcode',
'fa-upload': 'Upload',
'fa-search': 'Search',
'fa-mail-bulk': 'Mails',
'fa-laptop-code': 'Laptop',
'fa-copy': 'Copy',
'fa-user-tag': 'Tag',
'fa-user-secret': 'UserRound',
'fa-users': 'Users',
'fa-user-circle': 'CircleUser',
'fa-sort': 'ArrowUpDown',
'fa-question': 'HelpCircle',
'fa-map-marked': 'MapPinned',
'fa-list-ol': 'ListOrdered',
'fa-laptop': 'Laptop',
'fa-info': 'Info',
'fa-building': 'Building2',
'fa-user-slash': 'UserX',
'fa-user-check': 'UserCheck',
'fa-unlink': 'Unlink',
'fa-star': 'Star',
'fa-search-location': 'MapPin',
'fa-remove-format': 'RemoveFormatting',
'fa-qrcode': 'QrCode',
'fa-key': 'Key',
'fa-heartbeat': 'HeartPulse',
'fa-hat-wizard': 'Wand2',
'fa-fingerprint': 'Fingerprint',
'fa-file-csv': 'FileSpreadsheet',
'fa-file': 'File',
'fa-clock': 'Clock',
'fa-clipboard-list': 'ClipboardList',
'fa-chart-line': 'TrendingUp',
'fa-chalkboard-teacher': 'Presentation',
'fa-calendar-day': 'CalendarDays',
'fa-bell-slash': 'BellOff',
'fa-bell': 'Bell',
# ── Additional mappings ──────────────────────────────────────────────────
'fa-arrow-left': 'ArrowLeft',
'fa-arrow-right': 'ArrowRight',
'fa-arrow-up': 'ArrowUp',
'fa-arrow-down': 'ArrowDown',
'fa-ban': 'Ban',
'fa-broom': 'Trash2', # closest semantic match
'fa-calendar-alt': 'CalendarDays',
'fa-database': 'Database',
'fa-door-open': 'DoorOpen',
'fa-download': 'Download',
'fa-exchange-alt': 'ArrowLeftRight',
'fa-file-image': 'FileImage',
'fa-lock': 'Lock',
'fa-magic': 'Sparkles',
'fa-print': 'Printer',
'fa-sticky-note': 'StickyNote',
'fa-sync': 'RefreshCw',
'fa-tag': 'Tag',
'fa-trash': 'Trash2',
'fa-user-ninja': 'UserRound',
'fa-user-tie': 'UserRound',
'fa-video': 'Video',
'fa-archive': 'Archive',
'fa-link-slash': 'Unlink',
'fa-question-circle': 'HelpCircle',
# ── Additional unmapped icons ──────────────────────────────────────────────
'fa-compress-arrows-alt':'Minimize2',
'fa-expand-arrows-alt': 'Maximize2',
'fa-secret': 'ShieldCheck',
'fa-user-shield': 'ShieldUser',
'fa-user-nurse': 'UserRound',
'fa-user-friends': 'Users',
'fa-user-plus': 'UserPlus',
'fa-user-edit': 'UserRoundPen',
'fa-palette': 'Palette',
'fa-eraser': 'Eraser',
'fa-code': 'Code',
'fa-lock-open': 'LockOpen',
'fa-unlock': 'LockOpen',
'fa-trash-alt': 'Trash2',
'fa-folder-open': 'FolderOpen',
'fa-minus-circle': 'MinusCircle',
'fa-plus-circle': 'PlusCircle',
'fa-window-close': 'X',
'fa-cut': 'Scissors',
'fa-caret-down': 'ChevronDown',
'fa-caret-right': 'ChevronRight',
'fa-cogs': 'Settings2',
'fa-phone': 'Phone',
'fa-phone-slash': 'PhoneOff',
'fa-flag': 'Flag',
'fa-calendar-week': 'CalendarDays',
'fa-address-book': 'BookUser',
'fa-info-circle': 'Info',
'fa-comment-slash': 'MessageX',
'fa-paperclip': 'Paperclip',
'fa-keyboard': 'Keyboard',
'fa-crosshairs': 'Crosshair',
'fa-redo': 'RotateCcw',
'fa-tools': 'Wrench',
'fa-video-slash': 'VideoOff',
'fa-home': 'House',
'fa-calendar': 'Calendar',
'fa-check-square': 'SquareCheck',
'fa-square': 'Square',
'fa-times-circle': 'CircleX',
'fa-undo': 'RotateCcw',
'fa-trash-restore': 'ArchiveRestore',
'fa-lock-open': 'LockOpen',
'fa-compress': 'Minimize2',
'fa-expand': 'Maximize2',
'fa-grip-lines': 'GripHorizontal',
'fa-bars': 'Menu',
'fa-refresh': 'RefreshCw',
}
# Skip modifiers — not real icon names
FA_MODIFIERS = {'fas', 'far', 'fab', 'fa-spin', 'fa-fw', 'fa-lg', 'fa-2x', 'fa-sm'}
# ── Pattern: <span class="fas fa-X [extras]" [other-attrs]></span> ───────────
# [^>]* matches newlines too (character class, not dot)
SPAN_RE = re.compile(
r'<span\s+class="((?:fas|far|fab)\s+fa-[^"]*)"[^>]*>\s*</span>'
)
# ── Comment splitter ─────────────────────────────────────────────────────────
COMMENT_RE = re.compile(r'(<!--[\s\S]*?-->)')
# ── Lucide import line ────────────────────────────────────────────────────────
IMPORT_RE = re.compile(r"import\s*\{([^}]+)\}\s*from\s*'@lucide/svelte'\s*;?")
def parse_fa_class(class_str):
"""Return (icon_name, extra_classes, has_spin) from a FA class string."""
parts = class_str.split()
icon_name = None
has_spin = 'fa-spin' in parts
extra = []
for p in parts:
if p in FA_MODIFIERS:
continue
elif p.startswith('fa-'):
if icon_name is None:
icon_name = p # first real icon name wins
extra.append(p)
return icon_name, extra, has_spin
def replace_span(m):
"""Regex sub callback: replace a single FA span with a Lucide component."""
"""
if icon_name is None:
return m.group(0)
lucide = FA_TO_LUCIDE.get(icon_name)
if lucide is None:
print(f' ⚠ no mapping for {icon_name!r} — left as-is', file=sys.stderr)
return m.group(0)
classes = extra[:]
if has_spin:
classes.append('animate-spin')
class_attr = f' class="{" ".join(classes)}"' if classes else ''
return f'<{lucide} size="1em"{class_attr} />'
def process_content(content):
"""Replace FA spans, skip HTML comments. Return (new_content, used_icons)."""
used_icons = set()
def track_and_replace(m):
result = replace_span(m)
if result != m.group(0):
# Extract lucide name from result
lucide_name = result.split()[0].lstrip('<')
used_icons.add(lucide_name)
return result
# Split by comments; only process non-comment segments
parts = COMMENT_RE.split(content)
new_parts = []
for part in parts:
if part.startswith('<!--'):
new_parts.append(part)
else:
new_parts.append(SPAN_RE.sub(track_and_replace, part))
return ''.join(new_parts), used_icons
def add_import(content, icons):
"""Add/merge lucide-svelte import line inside <script>."""
if not icons:
return content
sorted_icons = sorted(icons)
existing = IMPORT_RE.search(content)
if existing:
current = [s.strip() for s in existing.group(1).split(',') if s.strip()]
merged = sorted(set(current) | set(sorted_icons))
new_import = f"import {{ {', '.join(merged)} }} from '@lucide/svelte';"
return content[:existing.start()] + new_import + content[existing.end():]
else:
# Insert after the last COMPLETE import statement (handles multiline imports).
# A complete import statement ends with: } from '...'; or import '...';
complete_import_re = re.compile(
r'^[ \t]*import\b[\s\S]*?(?:from\s*[\'"][^\'"]+[\'"]|[\'"][^\'"]+[\'"])\s*;?',
re.MULTILINE
)
all_imports = list(complete_import_re.finditer(content))
if all_imports:
pos = all_imports[-1].end()
new_line = f"\n import {{ {', '.join(sorted_icons)} }} from '@lucide/svelte';"
return content[:pos] + new_line + content[pos:]
# Fallback: add after <script> tag
script_tag = content.find('<script')
if script_tag != -1:
end = content.index('>', script_tag) + 1
return content[:end] + f"\n import {{ {', '.join(sorted_icons)} }} from 'lucide-svelte';" + content[end:]
return content
def migrate_file(filepath):
path = Path(filepath)
original = path.read_text()
new_content, used_icons = process_content(original)
if used_icons:
new_content = add_import(new_content, used_icons)
if new_content != original:
path.write_text(new_content)
print(f'{path.name} ({len(used_icons)} icon types: {", ".join(sorted(used_icons))})')
return True
else:
print(f' {path.name} (no changes)')
return False
def main():
if len(sys.argv) < 2:
print('Usage: migrate_fa_to_lucide.py <directory_or_file ...>')
sys.exit(1)
# IDAA Guardrail: Abort if any target is src/routes/idaa or a subdirectory
for arg in sys.argv[1:]:
if Path(arg).resolve().as_posix().endswith('src/routes/idaa') or '/src/routes/idaa/' in Path(arg).resolve().as_posix():
print('ABORT: This script must not be run against src/routes/idaa or its subdirectories. IDAA content is private and protected.')
sys.exit(1)
targets = []
for arg in sys.argv[1:]:
p = Path(arg)
if p.is_dir():
targets.extend(sorted(p.rglob('*.svelte')))
elif p.is_file():
targets.append(p)
else:
print(f'Not found: {arg}', file=sys.stderr)
changed = 0
for t in targets:
if migrate_file(t):
changed += 1
print(f'\n{changed}/{len(targets)} files modified.')
if __name__ == '__main__':
main()

View File

@@ -1,21 +0,0 @@
<script lang="ts">
import Test from './test.svelte';
export let name: string;
</script>
<section>
<p>Name: {name}</p>
<Test />
</section>
<style>
section {
text-align: center;
}
p {
color: green;
}
</style>

View File

@@ -1,75 +0,0 @@
<script lang="ts">
export let name: string;
let count = 0;
function handleClick() {
count += 1;
}
let m = { x: 0, y: 0 };
function handle_mousemove(event) {
m.x = event.clientX;
m.y = event.clientY;
}
let first_name = '';
let last_name = '';
let full_name = 'Not Here';
$: full_name = first_name + ' ' + last_name;
let name_checked = false;
</script>
<main>
<input bind:value={first_name}>
<input bind:value={last_name}>
<label>Check me <input type=checkbox bind:checked={name_checked}></label>
<h1>Hello {full_name}!</h1>
{#if name_checked}
<p>Thank you. We will bombard your inbox and sell your personal details.</p>
{:else}
<p>You must opt in to continue. If you're not paying, you're the product.</p>
{/if}
<button disabled={!name_checked}>
Subscribe
</button>
<p>This is a new app using Svelte.</p>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
<div class="my_class">My fancy class!</div>
<div on:mousemove={handle_mousemove}>
The mouse position is {m.x} x {m.y}
</div>
</main>
<style>
main {
text-align: center;
padding: 1rem;
max-width: 240px;
margin: 0 auto;
}
h1 {
color: hsla(0, 50%, 50%, 1);
/*text-transform: uppercase;*/
font-size: 4rem;
font-weight: 100;
}
@media (min-width: 640px) {
main {
max-width: none;
}
}
</style>

166
src/ae-c-idaa-light.css Normal file
View File

@@ -0,0 +1,166 @@
[data-theme='AE_c_IDAA_light'] {
--text-scaling: 1.067;
--base-font-color: var(--color-surface-950);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
--anchor-font-color: var(--color-primary-600);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
--radius-base: 0.375rem;
--radius-container: 0.75rem;
--default-border-width: 1px;
--default-divide-width: 1px;
--default-ring-width: 1px;
--body-background-color: var(--color-surface-50);
--color-primary-50: oklch(85.73% 0.07 251.8deg);
--color-primary-100: oklch(78.5% 0.09 252.03deg);
--color-primary-200: oklch(71.06% 0.1 253.6deg);
--color-primary-300: oklch(63.76% 0.12 253.85deg);
--color-primary-400: oklch(56.32% 0.14 255.25deg);
--color-primary-500: oklch(49.23% 0.15 256.36deg);
--color-primary-600: oklch(43.11% 0.14 258.86deg);
--color-primary-700: oklch(36.85% 0.14 261.54deg);
--color-primary-800: oklch(30.41% 0.13 263.99deg);
--color-primary-900: oklch(23.91% 0.12 265.91deg);
--color-primary-950: oklch(16.96% 0.12 264.05deg);
--color-primary-contrast-light: var(--color-primary-50);
--color-primary-contrast-500: var(--color-primary-contrast-light);
--color-primary-contrast-600: var(--color-primary-contrast-light);
--color-primary-contrast-700: var(--color-primary-contrast-light);
--color-primary-contrast-800: var(--color-primary-contrast-light);
--color-primary-contrast-900: var(--color-primary-contrast-light);
--color-primary-contrast-950: var(--color-primary-contrast-light);
--color-secondary-50: oklch(96.26% 0.06 196.24deg);
--color-secondary-100: oklch(89.14% 0.07 220.79deg);
--color-secondary-200: oklch(82.13% 0.08 234.87deg);
--color-secondary-300: oklch(75.03% 0.11 245.33deg);
--color-secondary-400: oklch(68.15% 0.14 250.72deg);
--color-secondary-500: oklch(61.37% 0.16 255.34deg);
--color-secondary-600: oklch(55.1% 0.16 256.81deg);
--color-secondary-700: oklch(48.64% 0.15 258.4deg);
--color-secondary-800: oklch(41.84% 0.15 260.39deg);
--color-secondary-900: oklch(35.05% 0.14 262.03deg);
--color-secondary-950: oklch(28.12% 0.14 262.47deg);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-secondary-contrast-600: var(--color-secondary-contrast-light);
--color-secondary-contrast-700: var(--color-secondary-contrast-light);
--color-secondary-contrast-800: var(--color-secondary-contrast-light);
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
--color-tertiary-50: oklch(100% 0 none);
--color-tertiary-100: oklch(96.07% 0.01 251.15deg);
--color-tertiary-200: oklch(91.88% 0.03 252.69deg);
--color-tertiary-300: oklch(87.99% 0.05 253.24deg);
--color-tertiary-400: oklch(83.81% 0.06 253.57deg);
--color-tertiary-500: oklch(79.93% 0.08 253.32deg);
--color-tertiary-600: oklch(72.53% 0.08 251.75deg);
--color-tertiary-700: oklch(64.93% 0.08 249.75deg);
--color-tertiary-800: oklch(57.14% 0.09 247.99deg);
--color-tertiary-900: oklch(49.18% 0.09 246.55deg);
--color-tertiary-950: oklch(41.1% 0.09 246.54deg);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-tertiary-contrast-800: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
--color-success-50: oklch(95.82% 0.06 184.52deg);
--color-success-100: oklch(91.55% 0.08 172.29deg);
--color-success-200: oklch(87.44% 0.11 165.22deg);
--color-success-300: oklch(83.26% 0.13 161.2deg);
--color-success-400: oklch(79.56% 0.16 157.13deg);
--color-success-500: oklch(76.12% 0.18 153.61deg);
--color-success-600: oklch(69.31% 0.17 151.81deg);
--color-success-700: oklch(62.07% 0.16 149.95deg);
--color-success-800: oklch(54.9% 0.15 147.65deg);
--color-success-900: oklch(47.26% 0.14 145.54deg);
--color-success-950: oklch(39.64% 0.13 143.79deg);
--color-success-contrast-light: var(--color-success-50);
--color-success-contrast-800: var(--color-success-contrast-light);
--color-success-contrast-900: var(--color-success-contrast-light);
--color-success-contrast-950: var(--color-success-contrast-light);
--color-warning-50: oklch(98.26% 0.1 108.02deg);
--color-warning-100: oklch(95.84% 0.12 104.66deg);
--color-warning-200: oklch(93.48% 0.13 102.21deg);
--color-warning-300: oklch(91.49% 0.15 100.17deg);
--color-warning-400: oklch(89.28% 0.16 98.19deg);
--color-warning-500: oklch(87.14% 0.17 96.01deg);
--color-warning-600: oklch(79.88% 0.16 96.31deg);
--color-warning-700: oklch(72.35% 0.14 95.62deg);
--color-warning-800: oklch(64.73% 0.13 95.92deg);
--color-warning-900: oklch(56.77% 0.11 94.87deg);
--color-warning-950: oklch(48.63% 0.1 95.22deg);
--color-warning-contrast-light: var(--color-warning-50);
--color-warning-contrast-800: var(--color-warning-contrast-light);
--color-warning-contrast-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: var(--color-warning-contrast-light);
--color-error-50: oklch(81.88% 0.1 38.14deg);
--color-error-100: oklch(75.88% 0.13 31.15deg);
--color-error-200: oklch(70.29% 0.16 27.32deg);
--color-error-300: oklch(65.15% 0.19 25.65deg);
--color-error-400: oklch(60.98% 0.21 25.56deg);
--color-error-500: oklch(57.86% 0.22 26.62deg);
--color-error-600: oklch(52.52% 0.2 26.86deg);
--color-error-700: oklch(46.81% 0.18 27.02deg);
--color-error-800: oklch(41.15% 0.16 27.63deg);
--color-error-900: oklch(35.01% 0.14 27.9deg);
--color-error-950: oklch(28.69% 0.12 29.23deg);
--color-error-contrast-light: var(--color-error-50);
--color-error-contrast-400: var(--color-error-contrast-light);
--color-error-contrast-500: var(--color-error-contrast-light);
--color-error-contrast-600: var(--color-error-contrast-light);
--color-error-contrast-700: var(--color-error-contrast-light);
--color-error-contrast-800: var(--color-error-contrast-light);
--color-error-contrast-900: var(--color-error-contrast-light);
--color-error-contrast-950: var(--color-error-contrast-light);
--color-surface-50: oklch(100% 0 none);
--color-surface-100: oklch(93.98% 0 105.57deg);
--color-surface-200: oklch(87.66% 0 67.88deg);
--color-surface-300: oklch(81.35% 0 106.1deg);
--color-surface-400: oklch(74.79% 0 84.45deg);
--color-surface-500: oklch(68.29% 0 91.36deg);
--color-surface-600: oklch(60.99% 0 91.38deg);
--color-surface-700: oklch(53.5% 0 84.49deg);
--color-surface-800: oklch(46.03% 0 91.43deg);
--color-surface-900: oklch(37.94% 0 84.52deg);
--color-surface-950: oklch(29.34% 0 84.54deg);
--color-surface-contrast-light: var(--color-surface-50);
--color-surface-contrast-700: var(--color-surface-contrast-light);
--color-surface-contrast-800: var(--color-surface-contrast-light);
--color-surface-contrast-900: var(--color-surface-contrast-light);
--color-surface-contrast-950: var(--color-surface-contrast-light);
}

205
src/ae-c-lci.css Normal file
View File

@@ -0,0 +1,205 @@
[data-theme='AE_c_LCI'] {
--text-scaling: 1.067;
--base-font-color: var(--color-surface-950);
--base-font-color-dark: var(--color-surface-50);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-color-dark: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
--anchor-font-color: var(--color-primary-500);
--anchor-font-color-dark: var(--color-primary-500);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
--radius-base: 0.375rem;
--radius-container: 0.75rem;
--default-border-width: 1px;
--default-divide-width: 1px;
--default-ring-width: 1px;
--body-background-color: var(--color-surface-50);
--body-background-color-dark: var(--color-surface-950);
--color-primary-50: oklch(85.1% 0.07 265.19deg);
--color-primary-100: oklch(77.89% 0.08 264.31deg);
--color-primary-200: oklch(70.32% 0.08 264.44deg);
--color-primary-300: oklch(62.86% 0.09 263.87deg);
--color-primary-400: oklch(54.96% 0.1 263.8deg);
--color-primary-500: oklch(47.12% 0.11 262.88deg);
--color-primary-600: oklch(40.9% 0.1 264.73deg);
--color-primary-700: oklch(34.53% 0.1 267.34deg);
--color-primary-800: oklch(28.16% 0.09 268.81deg);
--color-primary-900: oklch(21.29% 0.09 271.12deg);
--color-primary-950: oklch(12.88% 0.09 264.05deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-primary-contrast-50: var(--color-primary-contrast-dark);
--color-primary-contrast-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: var(--color-primary-contrast-dark);
--color-primary-contrast-400: var(--color-primary-contrast-dark);
--color-primary-contrast-500: var(--color-primary-contrast-light);
--color-primary-contrast-600: var(--color-primary-contrast-light);
--color-primary-contrast-700: var(--color-primary-contrast-light);
--color-primary-contrast-800: var(--color-primary-contrast-light);
--color-primary-contrast-900: var(--color-primary-contrast-light);
--color-primary-contrast-950: var(--color-primary-contrast-light);
--color-secondary-50: oklch(96.14% 0.06 196.21deg);
--color-secondary-100: oklch(89.81% 0.07 212.45deg);
--color-secondary-200: oklch(83.71% 0.08 223.06deg);
--color-secondary-300: oklch(77.42% 0.1 231.73deg);
--color-secondary-400: oklch(71.44% 0.12 237.59deg);
--color-secondary-500: oklch(65.39% 0.14 243.22deg);
--color-secondary-600: oklch(58.93% 0.13 245.07deg);
--color-secondary-700: oklch(52.09% 0.12 248.03deg);
--color-secondary-800: oklch(45.27% 0.12 250.54deg);
--color-secondary-900: oklch(38.01% 0.11 254.24deg);
--color-secondary-950: oklch(30.67% 0.11 256.73deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
--color-secondary-contrast-400: var(--color-secondary-contrast-dark);
--color-secondary-contrast-500: var(--color-secondary-contrast-dark);
--color-secondary-contrast-600: var(--color-secondary-contrast-dark);
--color-secondary-contrast-700: var(--color-secondary-contrast-light);
--color-secondary-contrast-800: var(--color-secondary-contrast-light);
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
--color-tertiary-50: oklch(87.75% 0.12 326.52deg);
--color-tertiary-100: oklch(80.92% 0.13 323.93deg);
--color-tertiary-200: oklch(73.87% 0.14 321.55deg);
--color-tertiary-300: oklch(66.9% 0.15 319.41deg);
--color-tertiary-400: oklch(59.72% 0.16 317.25deg);
--color-tertiary-500: oklch(52.73% 0.17 315.13deg);
--color-tertiary-600: oklch(46.6% 0.16 314.18deg);
--color-tertiary-700: oklch(40.43% 0.14 312.8deg);
--color-tertiary-800: oklch(33.85% 0.13 309.88deg);
--color-tertiary-900: oklch(27.23% 0.12 306.83deg);
--color-tertiary-950: oklch(19.83% 0.1 302.7deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-400: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-500: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-600: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-700: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-800: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
--color-success-50: oklch(95.23% 0.07 195.99deg);
--color-success-100: oklch(90.22% 0.09 189.46deg);
--color-success-200: oklch(85.11% 0.1 186.03deg);
--color-success-300: oklch(80.35% 0.12 181.75deg);
--color-success-400: oklch(75.55% 0.12 178.92deg);
--color-success-500: oklch(71.19% 0.13 174.73deg);
--color-success-600: oklch(64.29% 0.12 173.65deg);
--color-success-700: oklch(57.46% 0.11 171.75deg);
--color-success-800: oklch(50.18% 0.1 170.68deg);
--color-success-900: oklch(42.87% 0.09 167.65deg);
--color-success-950: oklch(34.91% 0.07 164.42deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-success-contrast-50: var(--color-success-contrast-dark);
--color-success-contrast-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: var(--color-success-contrast-dark);
--color-success-contrast-400: var(--color-success-contrast-dark);
--color-success-contrast-500: var(--color-success-contrast-dark);
--color-success-contrast-600: var(--color-success-contrast-dark);
--color-success-contrast-700: var(--color-success-contrast-light);
--color-success-contrast-800: var(--color-success-contrast-light);
--color-success-contrast-900: var(--color-success-contrast-light);
--color-success-contrast-950: var(--color-success-contrast-light);
--color-warning-50: oklch(95.67% 0.05 84.56deg);
--color-warning-100: oklch(92.83% 0.06 82.16deg);
--color-warning-200: oklch(90.12% 0.08 80.33deg);
--color-warning-300: oklch(87.59% 0.1 80.01deg);
--color-warning-400: oklch(85.03% 0.12 78.35deg);
--color-warning-500: oklch(82.46% 0.14 76.71deg);
--color-warning-600: oklch(76.34% 0.13 72.25deg);
--color-warning-700: oklch(70.34% 0.13 68.09deg);
--color-warning-800: oklch(63.99% 0.13 63.18deg);
--color-warning-900: oklch(57.91% 0.13 57.97deg);
--color-warning-950: oklch(51.69% 0.13 51.44deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-warning-contrast-50: var(--color-warning-contrast-dark);
--color-warning-contrast-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: var(--color-warning-contrast-dark);
--color-warning-contrast-400: var(--color-warning-contrast-dark);
--color-warning-contrast-500: var(--color-warning-contrast-dark);
--color-warning-contrast-600: var(--color-warning-contrast-light);
--color-warning-contrast-700: var(--color-warning-contrast-light);
--color-warning-contrast-800: var(--color-warning-contrast-light);
--color-warning-contrast-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: var(--color-warning-contrast-light);
--color-error-50: oklch(84.29% 0.09 46.91deg);
--color-error-100: oklch(78.63% 0.12 39.19deg);
--color-error-200: oklch(72.92% 0.14 34.35deg);
--color-error-300: oklch(67.88% 0.17 31.48deg);
--color-error-400: oklch(63.09% 0.19 30.02deg);
--color-error-500: oklch(59.32% 0.21 29.47deg);
--color-error-600: oklch(53.56% 0.19 29.25deg);
--color-error-700: oklch(47.75% 0.17 29.2deg);
--color-error-800: oklch(41.51% 0.15 28.7deg);
--color-error-900: oklch(35.35% 0.14 28.7deg);
--color-error-950: oklch(28.69% 0.12 29.23deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-error-contrast-50: var(--color-error-contrast-dark);
--color-error-contrast-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: var(--color-error-contrast-dark);
--color-error-contrast-400: var(--color-error-contrast-dark);
--color-error-contrast-500: var(--color-error-contrast-light);
--color-error-contrast-600: var(--color-error-contrast-light);
--color-error-contrast-700: var(--color-error-contrast-light);
--color-error-contrast-800: var(--color-error-contrast-light);
--color-error-contrast-900: var(--color-error-contrast-light);
--color-error-contrast-950: var(--color-error-contrast-light);
--color-surface-50: oklch(100% 0 none);
--color-surface-100: oklch(97.02% 0 none);
--color-surface-200: oklch(94.01% 0 none);
--color-surface-300: oklch(91.12% 0 196.34deg);
--color-surface-400: oklch(88.07% 0 196.37deg);
--color-surface-500: oklch(84.99% 0 196.4deg);
--color-surface-600: oklch(77.78% 0 196.47deg);
--color-surface-700: oklch(70.09% 0 196.54deg);
--color-surface-800: oklch(62.51% 0 196.61deg);
--color-surface-900: oklch(54.34% 0 196.68deg);
--color-surface-950: oklch(46.22% 0 196.73deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
--color-surface-contrast-50: var(--color-surface-contrast-dark);
--color-surface-contrast-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark);
--color-surface-contrast-400: var(--color-surface-contrast-dark);
--color-surface-contrast-500: var(--color-surface-contrast-dark);
--color-surface-contrast-600: var(--color-surface-contrast-dark);
--color-surface-contrast-700: var(--color-surface-contrast-dark);
--color-surface-contrast-800: var(--color-surface-contrast-dark);
--color-surface-contrast-900: var(--color-surface-contrast-light);
--color-surface-contrast-950: var(--color-surface-contrast-light);
}

353
src/ae-firefly-axonius.css Normal file
View File

@@ -0,0 +1,353 @@
/*
* AE Firefly — Axonius variant
* Primary: #ff6112 (Axonius orange)
* Aether Platform / One Sky IT, LLC — Design System Theme
*
* Color philosophy:
* Primary — Axonius Orange: #ff6112 brand color
* Secondary — Warm Amber-Gold: consistent with AE_Firefly
* Tertiary — Night-Sky Indigo: consistent with AE_Firefly
* Surface — Moonlit Slate: consistent with AE_Firefly
*
* NOTE: Each data-theme selector is fully self-contained — CSS custom
* properties do NOT inherit across theme selectors. All color ramps must
* be defined here even if identical to the base Firefly theme.
*
* Based on: Skeleton v4 theme CSS variable structure
* Variant of: src/ae-firefly.css (AE_Firefly)
*/
html[data-theme='AE_Firefly_Axonius'] {
--text-scaling: 1.067;
--background: var(--color-surface-50) !important;
--base-font-color: var(--color-surface-950);
--base-font-color-dark: var(--color-surface-50);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-color-dark: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
/* Anchors: Axonius orange in light, lighter in dark */
--anchor-font-color: var(--color-primary-600);
--anchor-font-color-dark: var(--color-primary-300);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
--radius-base: 0.375rem;
--radius-container: 0.875rem;
--default-border-width: 1px;
/* PRIMARY — Axonius Orange (#ff6112) */
--color-primary-50: #fff5ef;
--color-primary-100: #ffe0d1;
--color-primary-200: #ffc7a8;
--color-primary-300: #ffad7f;
--color-primary-400: #ff9356;
--color-primary-500: #ff6112;
--color-primary-600: #e6550f;
--color-primary-700: #bf4b0d;
--color-primary-800: #993f0b;
--color-primary-900: #7c3509;
--color-primary-950: #5f2b08;
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
/* SECONDARY — Warm Amber-Gold (same as AE_Firefly) */
--color-secondary-50: oklch(97.5% 0.06 102deg);
--color-secondary-100: oklch(93.5% 0.095 100deg);
--color-secondary-200: oklch(89.5% 0.128 98deg);
--color-secondary-300: oklch(85.5% 0.155 95deg);
--color-secondary-400: oklch(81% 0.17 93deg);
--color-secondary-500: oklch(76% 0.17 90deg);
--color-secondary-600: oklch(68.5% 0.16 87deg);
--color-secondary-700: oklch(60.5% 0.145 85deg);
--color-secondary-800: oklch(52% 0.13 83deg);
--color-secondary-900: oklch(43.5% 0.11 81deg);
--color-secondary-950: oklch(35% 0.09 79deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
/* TERTIARY — Night-Sky Indigo (same as AE_Firefly) */
--color-tertiary-50: oklch(95.5% 0.042 283deg);
--color-tertiary-100: oklch(89% 0.068 281deg);
--color-tertiary-200: oklch(81.5% 0.092 279deg);
--color-tertiary-300: oklch(73.5% 0.112 278deg);
--color-tertiary-400: oklch(65% 0.132 277deg);
--color-tertiary-500: oklch(55.5% 0.142 276deg);
--color-tertiary-600: oklch(48.5% 0.138 275deg);
--color-tertiary-700: oklch(41.5% 0.128 274deg);
--color-tertiary-800: oklch(34.5% 0.112 273deg);
--color-tertiary-900: oklch(27.5% 0.098 272deg);
--color-tertiary-950: oklch(20% 0.082 271deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
/* SUCCESS */
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
/* WARNING */
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
/* ERROR */
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
/* SURFACE — Moonlit Slate (same as AE_Firefly) */
--color-surface-50: oklch(99.2% 0.003 220deg);
--color-surface-100: oklch(97% 0.006 217deg);
--color-surface-200: oklch(93.5% 0.009 215deg);
--color-surface-300: oklch(88.5% 0.012 213deg);
--color-surface-400: oklch(81.5% 0.015 212deg);
--color-surface-500: oklch(70.5% 0.016 215deg);
--color-surface-600: oklch(59% 0.018 218deg);
--color-surface-700: oklch(47.5% 0.02 222deg);
--color-surface-800: oklch(30.5% 0.022 226deg);
--color-surface-900: oklch(24.5% 0.025 229deg);
--color-surface-950: oklch(15.5% 0.028 233deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
}
html.dark[data-theme='AE_Firefly_Axonius'] {
--background: var(--color-surface-950) !important;
--default-border-width: 1px;
--default-divide-width: 1px;
--default-ring-width: 1px;
--body-background-color: var(--color-surface-50);
--body-background-color-dark: var(--color-surface-950);
/* PRIMARY — Axonius Orange */
--color-primary-50: #fff5ef;
--color-primary-100: #ffe0d1;
--color-primary-200: #ffc7a8;
--color-primary-300: #ffad7f;
--color-primary-400: #ff9356;
--color-primary-500: #ff6112;
--color-primary-600: #e6550f;
--color-primary-700: #bf4b0d;
--color-primary-800: #993f0b;
--color-primary-900: #7c3509;
--color-primary-950: #5f2b08;
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-primary-contrast-50: var(--color-primary-contrast-dark);
--color-primary-contrast-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: var(--color-primary-contrast-dark);
--color-primary-contrast-400: var(--color-primary-contrast-dark);
--color-primary-contrast-500: var(--color-primary-contrast-light);
--color-primary-contrast-600: var(--color-primary-contrast-light);
--color-primary-contrast-700: var(--color-primary-contrast-light);
--color-primary-contrast-800: var(--color-primary-contrast-light);
--color-primary-contrast-900: var(--color-primary-contrast-light);
--color-primary-contrast-950: var(--color-primary-contrast-light);
/* SECONDARY — Warm Amber-Gold */
--color-secondary-50: oklch(97.5% 0.06 102deg);
--color-secondary-100: oklch(93.5% 0.095 100deg);
--color-secondary-200: oklch(89.5% 0.128 98deg);
--color-secondary-300: oklch(85.5% 0.155 95deg);
--color-secondary-400: oklch(81% 0.17 93deg);
--color-secondary-500: oklch(76% 0.17 90deg);
--color-secondary-600: oklch(68.5% 0.16 87deg);
--color-secondary-700: oklch(60.5% 0.145 85deg);
--color-secondary-800: oklch(52% 0.13 83deg);
--color-secondary-900: oklch(43.5% 0.11 81deg);
--color-secondary-950: oklch(35% 0.09 79deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
--color-secondary-contrast-400: var(--color-secondary-contrast-dark);
--color-secondary-contrast-500: var(--color-secondary-contrast-dark);
--color-secondary-contrast-600: var(--color-secondary-contrast-light);
--color-secondary-contrast-700: var(--color-secondary-contrast-light);
--color-secondary-contrast-800: var(--color-secondary-contrast-light);
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
/* TERTIARY — Night-Sky Indigo */
--color-tertiary-50: oklch(95.5% 0.042 283deg);
--color-tertiary-100: oklch(89% 0.068 281deg);
--color-tertiary-200: oklch(81.5% 0.092 279deg);
--color-tertiary-300: oklch(73.5% 0.112 278deg);
--color-tertiary-400: oklch(65% 0.132 277deg);
--color-tertiary-500: oklch(55.5% 0.142 276deg);
--color-tertiary-600: oklch(48.5% 0.138 275deg);
--color-tertiary-700: oklch(41.5% 0.128 274deg);
--color-tertiary-800: oklch(34.5% 0.112 273deg);
--color-tertiary-900: oklch(27.5% 0.098 272deg);
--color-tertiary-950: oklch(20% 0.082 271deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-400: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-500: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-600: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-700: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-800: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
/* SUCCESS */
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-success-contrast-50: var(--color-success-contrast-dark);
--color-success-contrast-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: var(--color-success-contrast-dark);
--color-success-contrast-400: var(--color-success-contrast-dark);
--color-success-contrast-500: var(--color-success-contrast-dark);
--color-success-contrast-600: var(--color-success-contrast-dark);
--color-success-contrast-700: var(--color-success-contrast-light);
--color-success-contrast-800: var(--color-success-contrast-light);
--color-success-contrast-900: var(--color-success-contrast-light);
--color-success-contrast-950: var(--color-success-contrast-light);
/* WARNING */
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-warning-contrast-50: var(--color-warning-contrast-dark);
--color-warning-contrast-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: var(--color-warning-contrast-dark);
--color-warning-contrast-400: var(--color-warning-contrast-dark);
--color-warning-contrast-500: var(--color-warning-contrast-dark);
--color-warning-contrast-600: var(--color-warning-contrast-dark);
--color-warning-contrast-700: var(--color-warning-contrast-light);
--color-warning-contrast-800: var(--color-warning-contrast-light);
--color-warning-contrast-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: var(--color-warning-contrast-light);
/* ERROR */
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-error-contrast-50: var(--color-error-contrast-dark);
--color-error-contrast-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: var(--color-error-contrast-dark);
--color-error-contrast-400: var(--color-error-contrast-light);
--color-error-contrast-500: var(--color-error-contrast-light);
--color-error-contrast-600: var(--color-error-contrast-light);
--color-error-contrast-700: var(--color-error-contrast-light);
--color-error-contrast-800: var(--color-error-contrast-light);
--color-error-contrast-900: var(--color-error-contrast-light);
--color-error-contrast-950: var(--color-error-contrast-light);
/* SURFACE — Moonlit Slate */
--color-surface-50: oklch(99.2% 0.003 220deg);
--color-surface-100: oklch(97% 0.006 217deg);
--color-surface-200: oklch(93.5% 0.009 215deg);
--color-surface-300: oklch(88.5% 0.012 213deg);
--color-surface-400: oklch(81.5% 0.015 212deg);
--color-surface-500: oklch(70.5% 0.016 215deg);
--color-surface-600: oklch(59% 0.018 218deg);
--color-surface-700: oklch(47.5% 0.02 222deg);
--color-surface-800: oklch(35.5% 0.022 226deg);
--color-surface-900: oklch(24.5% 0.025 229deg);
--color-surface-950: oklch(15.5% 0.028 233deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
--color-surface-contrast-50: var(--color-surface-contrast-dark);
--color-surface-contrast-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark);
--color-surface-contrast-400: var(--color-surface-contrast-dark);
--color-surface-contrast-500: var(--color-surface-contrast-dark);
--color-surface-contrast-600: var(--color-surface-contrast-light);
--color-surface-contrast-700: var(--color-surface-contrast-light);
--color-surface-contrast-800: var(--color-surface-contrast-light);
--color-surface-contrast-900: var(--color-surface-contrast-light);
--color-surface-contrast-950: var(--color-surface-contrast-light);
}

169
src/ae-firefly-bgh.css Normal file
View File

@@ -0,0 +1,169 @@
/*
* AE Firefly — BGH variant
* Base color input: #076a72
* OKLCH primary ramp centered near hue ≈185° (teal/cyan family)
* Variant of: src/ae-firefly.css (AE_Firefly)
*/
html[data-theme='AE_Firefly_BGH'] {
--text-scaling: 1.067;
--background: var(--color-surface-50) !important;
--base-font-color: var(--color-surface-950);
--base-font-color-dark: var(--color-surface-50);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-color-dark: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
/* Anchors: teal in light, lighter teal in dark */
--anchor-font-color: var(--color-primary-600);
--anchor-font-color-dark: var(--color-primary-300);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
--radius-base: 0.375rem;
--radius-container: 0.875rem;
/* Map common design-system tokens used by Tailwind/Skeleton presets */
/* Set --primary as H S% L% (no wrapper), matching project's convention in src/app.css */
--primary: 184.5 88.5% 23.7%;
--primary-foreground: 210 20% 98%;
--primary-hex: #076a72;
/* PRIMARY — OKLCH ramp (hue ≈185°) */
--color-primary-50: oklch(96.5% 0.025 189deg);
--color-primary-100: oklch(91% 0.05 187deg);
--color-primary-200: oklch(84.5% 0.078 186deg);
--color-primary-300: oklch(76.5% 0.105 185deg);
--color-primary-400: oklch(67.5% 0.125 185deg);
--color-primary-500: oklch(50.5% 0.13 185deg);
--color-primary-600: oklch(44% 0.125 184deg);
--color-primary-700: oklch(37.5% 0.115 183deg);
--color-primary-800: oklch(30.5% 0.105 182deg);
--color-primary-900: oklch(23.5% 0.09 181deg);
--color-primary-950: oklch(16% 0.075 180deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
/* Hex fallback for the core brand color (500) if needed */
--color-primary-500-hex: #076a72;
/* --- Secondary (copied from AE_Firefly defaults) --- */
--color-secondary-50: oklch(97.5% 0.06 102deg);
--color-secondary-100: oklch(93.5% 0.095 100deg);
--color-secondary-200: oklch(89.5% 0.128 98deg);
--color-secondary-300: oklch(85.5% 0.155 95deg);
--color-secondary-400: oklch(81% 0.17 93deg);
--color-secondary-500: oklch(76% 0.17 90deg);
--color-secondary-600: oklch(68.5% 0.16 87deg);
--color-secondary-700: oklch(60.5% 0.145 85deg);
--color-secondary-800: oklch(52% 0.13 83deg);
--color-secondary-900: oklch(43.5% 0.11 81deg);
--color-secondary-950: oklch(35% 0.09 79deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
/* --- Tertiary --- */
--color-tertiary-50: oklch(95.5% 0.042 283deg);
--color-tertiary-100: oklch(89% 0.068 281deg);
--color-tertiary-200: oklch(81.5% 0.092 279deg);
--color-tertiary-300: oklch(73.5% 0.112 278deg);
--color-tertiary-400: oklch(65% 0.132 277deg);
--color-tertiary-500: oklch(55.5% 0.142 276deg);
--color-tertiary-600: oklch(48.5% 0.138 275deg);
--color-tertiary-700: oklch(41.5% 0.128 274deg);
--color-tertiary-800: oklch(34.5% 0.112 273deg);
--color-tertiary-900: oklch(27.5% 0.098 272deg);
--color-tertiary-950: oklch(20% 0.082 271deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
/* --- Success (kept consistent across Firefly variants) --- */
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
/* --- Warning --- */
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
/* --- Error --- */
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
/* --- Surface (important for light-mode backgrounds) --- */
--color-surface-50: oklch(99.2% 0.003 220deg);
--color-surface-100: oklch(97% 0.006 217deg);
--color-surface-200: oklch(93.5% 0.009 215deg);
--color-surface-300: oklch(88.5% 0.012 213deg);
--color-surface-400: oklch(81.5% 0.015 212deg);
--color-surface-500: oklch(70.5% 0.016 215deg);
--color-surface-600: oklch(59% 0.018 218deg);
--color-surface-700: oklch(47.5% 0.02 222deg);
--color-surface-800: oklch(35.5% 0.022 226deg);
--color-surface-900: oklch(24.5% 0.02 52deg);
--color-surface-950: oklch(15.5% 0.022 48deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
}
html.dark[data-theme='AE_Firefly_BGH'] {
--background: var(--color-surface-950) !important;
/* Minimal dark-mode contrast tokens for components */
--color-primary-contrast-50: var(--color-primary-contrast-dark);
--color-primary-contrast-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: var(--color-primary-contrast-dark);
--color-primary-contrast-400: var(--color-primary-contrast-dark);
--color-primary-contrast-500: var(--color-primary-contrast-light);
}

397
src/ae-firefly-indigo.css Normal file
View File

@@ -0,0 +1,397 @@
/*
* AE Firefly — Indigo variant
* "Deep night, rich as velvet."
* Aether Platform / One Sky IT, LLC — Design System Theme
*
* Aesthetic vision (Scott Idem, 2026-03-09):
* A deep, rich purple-indigo variant of the Firefly system.
* Inspired by the deep indigo of a clear night sky — the hours
* before dawn when the dark is at its most velvety and profound.
* Authoritative and calm, with a warm rose accent for warmth.
*
* Color philosophy:
* Primary — Deep Indigo: rich blue-violet (~266°), luminous depth
* Secondary — Violet: companion purple, warmer/rosier tone (~290°)
* Tertiary — Dusty Rose: warm complement, plum/rose tones (~341°)
* Surface — Velvet Slate: subtly purple-tinged neutral; near-white
* (light mode) → deep midnight indigo-grey (dark mode)
*
* Section 508 / WCAG 2.1 AA:
* - Body text (surface-950 on surface-50 light): >15:1 contrast ✓
* - Primary-filled buttons meet ≥3:1 for interactive components ✓
* - Contrast crossover points calculated per OKLCH approximate RL
*
* Based on: Skeleton v4 theme CSS variable structure
* Variant of: src/ae-firefly.css (AE_Firefly)
*/
html[data-theme='AE_Firefly_Indigo'] {
--text-scaling: 1.067;
--background: var(--color-surface-50) !important;
--base-font-color: var(--color-surface-950);
--base-font-color-dark: var(--color-surface-50);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-color-dark: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
/* Anchors: indigo in light, lighter indigo in dark */
--anchor-font-color: var(--color-primary-600);
--anchor-font-color-dark: var(--color-primary-300);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
--radius-base: 0.375rem;
}
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
html[data-theme='AE_Firefly_Indigo'] {
--color-primary-50: oklch(95.5% 0.04 270deg);
--color-primary-100: oklch(89.5% 0.072 270deg);
--color-primary-200: oklch(82.5% 0.108 269deg);
--color-primary-300: oklch(74.5% 0.135 268deg);
--color-primary-400: oklch(65% 0.155 267deg);
--color-primary-500: oklch(50.5% 0.16 266deg);
--color-primary-600: oklch(43.5% 0.152 265deg);
--color-primary-700: oklch(37% 0.138 264deg);
--color-primary-800: oklch(30% 0.12 263deg);
--color-primary-900: oklch(23% 0.1 262deg);
--color-primary-950: oklch(15.5% 0.08 261deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-secondary-50: oklch(96.5% 0.032 297deg);
--color-secondary-100: oklch(91.5% 0.058 295deg);
--color-secondary-200: oklch(85.5% 0.09 293deg);
--color-secondary-300: oklch(78.5% 0.115 292deg);
--color-secondary-400: oklch(70% 0.132 291deg);
--color-secondary-500: oklch(60% 0.14 290deg);
--color-secondary-600: oklch(52.5% 0.135 289deg);
--color-secondary-700: oklch(45% 0.126 288deg);
--color-secondary-800: oklch(37.5% 0.112 286deg);
--color-secondary-900: oklch(30% 0.094 284deg);
--color-secondary-950: oklch(22% 0.076 282deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-tertiary-50: oklch(96.5% 0.022 348deg);
--color-tertiary-100: oklch(91% 0.042 346deg);
--color-tertiary-200: oklch(84.5% 0.068 344deg);
--color-tertiary-300: oklch(76.5% 0.095 343deg);
--color-tertiary-400: oklch(68% 0.118 342deg);
--color-tertiary-500: oklch(57.5% 0.128 341deg);
--color-tertiary-600: oklch(50% 0.122 340deg);
--color-tertiary-700: oklch(43% 0.112 339deg);
--color-tertiary-800: oklch(35.5% 0.098 338deg);
--color-tertiary-900: oklch(28% 0.08 337deg);
--color-tertiary-950: oklch(20.5% 0.062 336deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-surface-50: oklch(99% 0.003 270deg);
--color-surface-100: oklch(96.5% 0.006 268deg);
--color-surface-200: oklch(92.5% 0.01 266deg);
--color-surface-300: oklch(87% 0.014 265deg);
--color-surface-400: oklch(78.5% 0.018 265deg);
--color-surface-500: oklch(66.5% 0.02 267deg);
--color-surface-600: oklch(54.5% 0.022 269deg);
--color-surface-700: oklch(42.5% 0.024 270deg);
--color-surface-800: oklch(31% 0.026 272deg);
--color-surface-900: oklch(20.5% 0.03 274deg);
--color-surface-950: oklch(13% 0.034 276deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
}
html.dark[data-theme='AE_Firefly_Indigo'] {
--background: var(--color-surface-950) !important;
--radius-container: 0.875rem;
--default-border-width: 1px;
--default-divide-width: 1px;
--default-ring-width: 1px;
--body-background-color: var(--color-surface-50);
--body-background-color-dark: var(--color-surface-950);
/* ===================================================================
* PRIMARY — Deep Indigo
* Hue: ~266°. Rich blue-violet indigo — the color of deep night sky
* in the hours before dawn. Luminous depth without harshness.
* CSS named "indigo" (#4B0082) is near oklch(20%, 0.18, 302°) but
* that's too dark to use as a 500 primary. This palette centers on
* a richer, usable indigo that reads clearly as "indigo" while
* maintaining sufficient contrast at mid-range shades.
* At 500 (L≈50%): sufficient contrast with primary-50 text (≥4:1).
* =================================================================== */
--color-primary-50: oklch(95.5% 0.04 270deg);
--color-primary-100: oklch(89.5% 0.072 270deg);
--color-primary-200: oklch(82.5% 0.108 269deg);
--color-primary-300: oklch(74.5% 0.135 268deg);
--color-primary-400: oklch(65% 0.155 267deg);
--color-primary-500: oklch(50.5% 0.16 266deg);
--color-primary-600: oklch(43.5% 0.152 265deg);
--color-primary-700: oklch(37% 0.138 264deg);
--color-primary-800: oklch(30% 0.12 263deg);
--color-primary-900: oklch(23% 0.1 262deg);
--color-primary-950: oklch(15.5% 0.08 261deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-primary-contrast-50: var(--color-primary-contrast-dark);
--color-primary-contrast-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: var(--color-primary-contrast-dark);
--color-primary-contrast-400: var(--color-primary-contrast-dark);
--color-primary-contrast-500: var(--color-primary-contrast-light);
--color-primary-contrast-600: var(--color-primary-contrast-light);
--color-primary-contrast-700: var(--color-primary-contrast-light);
--color-primary-contrast-800: var(--color-primary-contrast-light);
--color-primary-contrast-900: var(--color-primary-contrast-light);
--color-primary-contrast-950: var(--color-primary-contrast-light);
/* ===================================================================
* SECONDARY — Violet
* Hue: ~290°. A companion purple, slightly warmer and rosier than
* the primary indigo. Creates a rich monochromatic depth while
* remaining clearly distinct from the primary.
* Used for secondary actions, badges, and soft highlights.
* =================================================================== */
--color-secondary-50: oklch(96.5% 0.032 297deg);
--color-secondary-100: oklch(91.5% 0.058 295deg);
--color-secondary-200: oklch(85.5% 0.09 293deg);
--color-secondary-300: oklch(78.5% 0.115 292deg);
--color-secondary-400: oklch(70% 0.132 291deg);
--color-secondary-500: oklch(60% 0.14 290deg);
--color-secondary-600: oklch(52.5% 0.135 289deg);
--color-secondary-700: oklch(45% 0.126 288deg);
--color-secondary-800: oklch(37.5% 0.112 286deg);
--color-secondary-900: oklch(30% 0.094 284deg);
--color-secondary-950: oklch(22% 0.076 282deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
--color-secondary-contrast-400: var(--color-secondary-contrast-dark);
--color-secondary-contrast-500: var(--color-secondary-contrast-light);
--color-secondary-contrast-600: var(--color-secondary-contrast-light);
--color-secondary-contrast-700: var(--color-secondary-contrast-light);
--color-secondary-contrast-800: var(--color-secondary-contrast-light);
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
/* ===================================================================
* TERTIARY — Dusty Rose / Plum
* Hue: ~341°. A warm, muted rose-plum that provides the crucial
* warm counterpoint to the cool indigo-violet palette. Prevents
* the theme from feeling cold — like the warm glow of dawn
* breaking against a deep indigo sky.
* Used for location chips, warm accents, tertiary elements.
* =================================================================== */
--color-tertiary-50: oklch(96.5% 0.022 348deg);
--color-tertiary-100: oklch(91% 0.042 346deg);
--color-tertiary-200: oklch(84.5% 0.068 344deg);
--color-tertiary-300: oklch(76.5% 0.095 343deg);
--color-tertiary-400: oklch(68% 0.118 342deg);
--color-tertiary-500: oklch(57.5% 0.128 341deg);
--color-tertiary-600: oklch(50% 0.122 340deg);
--color-tertiary-700: oklch(43% 0.112 339deg);
--color-tertiary-800: oklch(35.5% 0.098 338deg);
--color-tertiary-900: oklch(28% 0.08 337deg);
--color-tertiary-950: oklch(20.5% 0.062 336deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-400: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-500: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-600: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-700: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-800: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
/* ===================================================================
* SUCCESS — Bioluminescent Green
* Hue: ~152°. Consistent with AE_Firefly for recognizable semantic
* color meaning across OSIT themes.
* =================================================================== */
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-success-contrast-50: var(--color-success-contrast-dark);
--color-success-contrast-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: var(--color-success-contrast-dark);
--color-success-contrast-400: var(--color-success-contrast-dark);
--color-success-contrast-500: var(--color-success-contrast-dark);
--color-success-contrast-600: var(--color-success-contrast-dark);
--color-success-contrast-700: var(--color-success-contrast-light);
--color-success-contrast-800: var(--color-success-contrast-light);
--color-success-contrast-900: var(--color-success-contrast-light);
--color-success-contrast-950: var(--color-success-contrast-light);
/* ===================================================================
* WARNING — Amber Orange
* Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-warning-contrast-50: var(--color-warning-contrast-dark);
--color-warning-contrast-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: var(--color-warning-contrast-dark);
--color-warning-contrast-400: var(--color-warning-contrast-dark);
--color-warning-contrast-500: var(--color-warning-contrast-dark);
--color-warning-contrast-600: var(--color-warning-contrast-dark);
--color-warning-contrast-700: var(--color-warning-contrast-light);
--color-warning-contrast-800: var(--color-warning-contrast-light);
--color-warning-contrast-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: var(--color-warning-contrast-light);
/* ===================================================================
* ERROR — Soft Coral/Rose
* Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-error-contrast-50: var(--color-error-contrast-dark);
--color-error-contrast-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: var(--color-error-contrast-dark);
--color-error-contrast-400: var(--color-error-contrast-light);
--color-error-contrast-500: var(--color-error-contrast-light);
--color-error-contrast-600: var(--color-error-contrast-light);
--color-error-contrast-700: var(--color-error-contrast-light);
--color-error-contrast-800: var(--color-error-contrast-light);
--color-error-contrast-900: var(--color-error-contrast-light);
--color-error-contrast-950: var(--color-error-contrast-light);
/* ===================================================================
* SURFACE — Velvet Slate
* A subtly purple-tinged neutral — barely perceptible, it gives
* surfaces a velvet quality that harmonizes with the indigo palette
* without being purple-on-purple. Light mode: soft white with a
* whisper of purple. Dark mode: deep midnight indigo-grey.
*
* 50 → body-bg light: near-white with ImperceptibleISTIC purple cast
* 950 → body-bg dark: deep midnight with indigo depth
* =================================================================== */
--color-surface-50: oklch(99% 0.003 270deg);
--color-surface-100: oklch(96.5% 0.006 268deg);
--color-surface-200: oklch(92.5% 0.01 266deg);
--color-surface-300: oklch(87% 0.014 265deg);
--color-surface-400: oklch(78.5% 0.018 265deg);
--color-surface-500: oklch(66.5% 0.02 267deg);
--color-surface-600: oklch(54.5% 0.022 269deg);
--color-surface-700: oklch(42.5% 0.024 270deg);
--color-surface-800: oklch(31% 0.026 272deg);
--color-surface-900: oklch(20.5% 0.03 274deg);
--color-surface-950: oklch(13% 0.034 276deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
--color-surface-contrast-50: var(--color-surface-contrast-dark);
--color-surface-contrast-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark);
--color-surface-contrast-400: var(--color-surface-contrast-dark);
--color-surface-contrast-500: var(--color-surface-contrast-dark);
--color-surface-contrast-600: var(--color-surface-contrast-light);
--color-surface-contrast-700: var(--color-surface-contrast-light);
--color-surface-contrast-800: var(--color-surface-contrast-light);
--color-surface-contrast-900: var(--color-surface-contrast-light);
--color-surface-contrast-950: var(--color-surface-contrast-light);
}

393
src/ae-firefly-rainbow.css Normal file
View File

@@ -0,0 +1,393 @@
/*
* AE Firefly — Rainbow variant
* "All the colors of wonder."
* Aether Platform / One Sky IT, LLC — Design System Theme
*
* Aesthetic vision (Scott Idem, 2026-03-09):
* A celebration-of-color variant of the Firefly system.
* The three brand color slots span the visible spectrum:
* Coral-Red (primary) → Emerald-Green (secondary) → Violet (tertiary).
* Warm cream surfaces let the saturated accents breathe without
* competing. Joyful and energetic — still calm, still Firefly.
*
* Color philosophy:
* Primary — Vivid Coral-Red: ~15°, warm and energetic
* Secondary — Emerald Green: ~148°, lush and clear
* Tertiary — Rich Violet: ~295°, deep and luminous
* Surface — Sunrise Cream: barely warm neutral; warm white
* (light mode) → deep warm charcoal (dark mode)
*
* Section 508 / WCAG 2.1 AA:
* - Body text (surface-950 on surface-50 light): >15:1 contrast ✓
* - Primary-filled buttons meet ≥3:1 for interactive components ✓
* - Chroma values kept within sRGB gamut across the full ramp
*
* Based on: Skeleton v4 theme CSS variable structure
* Variant of: src/ae-firefly.css (AE_Firefly)
*/
html[data-theme='AE_Firefly_Rainbow'] {
--text-scaling: 1.067;
--background: var(--color-surface-50) !important;
--base-font-color: var(--color-surface-950);
--base-font-color-dark: var(--color-surface-50);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-color-dark: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
/* Anchors: coral-red in light, lighter coral in dark */
--anchor-font-color: var(--color-primary-600);
--anchor-font-color-dark: var(--color-primary-300);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
}
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
html[data-theme='AE_Firefly_Rainbow'] {
--color-primary-50: oklch(97% 0.02 15deg);
--color-primary-100: oklch(92% 0.048 14deg);
--color-primary-200: oklch(86% 0.085 13deg);
--color-primary-300: oklch(79% 0.125 13deg);
--color-primary-400: oklch(71% 0.16 13deg);
--color-primary-500: oklch(60% 0.19 14deg);
--color-primary-600: oklch(52.5% 0.178 15deg);
--color-primary-700: oklch(45% 0.162 16deg);
--color-primary-800: oklch(37.5% 0.142 17deg);
--color-primary-900: oklch(30% 0.118 18deg);
--color-primary-950: oklch(22.5% 0.092 19deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-secondary-50: oklch(97% 0.04 152deg);
--color-secondary-100: oklch(92.5% 0.072 150deg);
--color-secondary-200: oklch(87% 0.105 149deg);
--color-secondary-300: oklch(81% 0.132 148deg);
--color-secondary-400: oklch(74.5% 0.152 148deg);
--color-secondary-500: oklch(62% 0.16 148deg);
--color-secondary-600: oklch(53.5% 0.148 148deg);
--color-secondary-700: oklch(45.5% 0.132 147deg);
--color-secondary-800: oklch(37.5% 0.112 146deg);
--color-secondary-900: oklch(29.5% 0.09 145deg);
--color-secondary-950: oklch(21.5% 0.068 144deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-tertiary-50: oklch(96.5% 0.03 299deg);
--color-tertiary-100: oklch(91% 0.058 297deg);
--color-tertiary-200: oklch(84.5% 0.092 296deg);
--color-tertiary-300: oklch(77% 0.122 295deg);
--color-tertiary-400: oklch(68.5% 0.148 295deg);
--color-tertiary-500: oklch(57% 0.158 295deg);
--color-tertiary-600: oklch(49.5% 0.15 294deg);
--color-tertiary-700: oklch(42.5% 0.138 293deg);
--color-tertiary-800: oklch(35.5% 0.122 292deg);
--color-tertiary-900: oklch(28.5% 0.102 291deg);
--color-tertiary-950: oklch(21% 0.08 290deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-surface-50: oklch(99.2% 0.004 75deg);
--color-surface-100: oklch(97% 0.007 72deg);
--color-surface-200: oklch(93.5% 0.01 70deg);
--color-surface-300: oklch(88.5% 0.013 68deg);
--color-surface-400: oklch(81.5% 0.016 66deg);
--color-surface-500: oklch(70.5% 0.018 64deg);
--color-surface-600: oklch(59% 0.018 62deg);
--color-surface-700: oklch(47.5% 0.018 58deg);
--color-surface-800: oklch(35.5% 0.02 55deg);
--color-surface-900: oklch(24.5% 0.02 52deg);
--color-surface-950: oklch(15.5% 0.022 48deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
}
html.dark[data-theme='AE_Firefly_Rainbow'] {
--background: var(--color-surface-950) !important;
--radius-base: 0.375rem;
--radius-container: 0.875rem;
--default-border-width: 1px;
--default-divide-width: 1px;
--default-ring-width: 1px;
--body-background-color: var(--color-surface-50);
--body-background-color-dark: var(--color-surface-950);
/* ===================================================================
* PRIMARY — Vivid Coral-Red (warm end of the rainbow)
* Hue: ~15°. The warm, energetic anchor of the spectrum —
* sits between orange and red for maximum vibrancy and warmth.
* Kept within sRGB gamut across the full ramp.
* At 500 (L≈60%): sufficient contrast with primary-50 text (≥4:1).
* =================================================================== */
--color-primary-50: oklch(97% 0.02 15deg);
--color-primary-100: oklch(92% 0.048 14deg);
--color-primary-200: oklch(86% 0.085 13deg);
--color-primary-300: oklch(79% 0.125 13deg);
--color-primary-400: oklch(71% 0.16 13deg);
--color-primary-500: oklch(60% 0.19 14deg);
--color-primary-600: oklch(52.5% 0.178 15deg);
--color-primary-700: oklch(45% 0.162 16deg);
--color-primary-800: oklch(37.5% 0.142 17deg);
--color-primary-900: oklch(30% 0.118 18deg);
--color-primary-950: oklch(22.5% 0.092 19deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-primary-contrast-50: var(--color-primary-contrast-dark);
--color-primary-contrast-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: var(--color-primary-contrast-dark);
--color-primary-contrast-400: var(--color-primary-contrast-dark);
--color-primary-contrast-500: var(--color-primary-contrast-light);
--color-primary-contrast-600: var(--color-primary-contrast-light);
--color-primary-contrast-700: var(--color-primary-contrast-light);
--color-primary-contrast-800: var(--color-primary-contrast-light);
--color-primary-contrast-900: var(--color-primary-contrast-light);
--color-primary-contrast-950: var(--color-primary-contrast-light);
/* ===================================================================
* SECONDARY — Emerald Green (mid-spectrum, the heart of the rainbow)
* Hue: ~148°. Clear, lush emerald — the most recognizable
* "rainbow green." Positioned at the center of the visible spectrum,
* it bridges the warm red primary and the cool violet tertiary.
* Used for secondary actions, success-adjacent highlights, badges.
* =================================================================== */
--color-secondary-50: oklch(97% 0.04 152deg);
--color-secondary-100: oklch(92.5% 0.072 150deg);
--color-secondary-200: oklch(87% 0.105 149deg);
--color-secondary-300: oklch(81% 0.132 148deg);
--color-secondary-400: oklch(74.5% 0.152 148deg);
--color-secondary-500: oklch(62% 0.16 148deg);
--color-secondary-600: oklch(53.5% 0.148 148deg);
--color-secondary-700: oklch(45.5% 0.132 147deg);
--color-secondary-800: oklch(37.5% 0.112 146deg);
--color-secondary-900: oklch(29.5% 0.09 145deg);
--color-secondary-950: oklch(21.5% 0.068 144deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
--color-secondary-contrast-400: var(--color-secondary-contrast-dark);
--color-secondary-contrast-500: var(--color-secondary-contrast-light);
--color-secondary-contrast-600: var(--color-secondary-contrast-light);
--color-secondary-contrast-700: var(--color-secondary-contrast-light);
--color-secondary-contrast-800: var(--color-secondary-contrast-light);
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
/* ===================================================================
* TERTIARY — Rich Violet (cool end of the rainbow)
* Hue: ~295°. Deep blue-violet — the "indigo and violet" end of
* the arc. Completes the warm-cool spectrum span across the three
* brand color slots. Creates striking contrast with the warm primary.
* Used for location chips, deep accents, tertiary elements.
* =================================================================== */
--color-tertiary-50: oklch(96.5% 0.03 299deg);
--color-tertiary-100: oklch(91% 0.058 297deg);
--color-tertiary-200: oklch(84.5% 0.092 296deg);
--color-tertiary-300: oklch(77% 0.122 295deg);
--color-tertiary-400: oklch(68.5% 0.148 295deg);
--color-tertiary-500: oklch(57% 0.158 295deg);
--color-tertiary-600: oklch(49.5% 0.15 294deg);
--color-tertiary-700: oklch(42.5% 0.138 293deg);
--color-tertiary-800: oklch(35.5% 0.122 292deg);
--color-tertiary-900: oklch(28.5% 0.102 291deg);
--color-tertiary-950: oklch(21% 0.08 290deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-400: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-500: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-600: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-700: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-800: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
/* ===================================================================
* SUCCESS — Bioluminescent Green
* Hue: ~152°. Consistent with AE_Firefly for recognizable semantic
* color meaning across OSIT themes.
* =================================================================== */
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-success-contrast-50: var(--color-success-contrast-dark);
--color-success-contrast-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: var(--color-success-contrast-dark);
--color-success-contrast-400: var(--color-success-contrast-dark);
--color-success-contrast-500: var(--color-success-contrast-dark);
--color-success-contrast-600: var(--color-success-contrast-dark);
--color-success-contrast-700: var(--color-success-contrast-light);
--color-success-contrast-800: var(--color-success-contrast-light);
--color-success-contrast-900: var(--color-success-contrast-light);
--color-success-contrast-950: var(--color-success-contrast-light);
/* ===================================================================
* WARNING — Amber Orange
* Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-warning-contrast-50: var(--color-warning-contrast-dark);
--color-warning-contrast-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: var(--color-warning-contrast-dark);
--color-warning-contrast-400: var(--color-warning-contrast-dark);
--color-warning-contrast-500: var(--color-warning-contrast-dark);
--color-warning-contrast-600: var(--color-warning-contrast-dark);
--color-warning-contrast-700: var(--color-warning-contrast-light);
--color-warning-contrast-800: var(--color-warning-contrast-light);
--color-warning-contrast-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: var(--color-warning-contrast-light);
/* ===================================================================
* ERROR — Soft Coral/Rose
* Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-error-contrast-50: var(--color-error-contrast-dark);
--color-error-contrast-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: var(--color-error-contrast-dark);
--color-error-contrast-400: var(--color-error-contrast-light);
--color-error-contrast-500: var(--color-error-contrast-light);
--color-error-contrast-600: var(--color-error-contrast-light);
--color-error-contrast-700: var(--color-error-contrast-light);
--color-error-contrast-800: var(--color-error-contrast-light);
--color-error-contrast-900: var(--color-error-contrast-light);
--color-error-contrast-950: var(--color-error-contrast-light);
/* ===================================================================
* SURFACE — Sunrise Cream
* A barely-warm neutral (hue ~70°, chroma 0.004-0.022) that lets
* the vibrant brand colors breathe. The warmth prevents the surface
* from feeling clinical when the vivid accents are in use.
*
* 50 → body-bg light: warm near-white, like morning paper
* 950 → body-bg dark: deep warm charcoal, like a dim theatre
* =================================================================== */
--color-surface-50: oklch(99.2% 0.004 75deg);
--color-surface-100: oklch(97% 0.007 72deg);
--color-surface-200: oklch(93.5% 0.01 70deg);
--color-surface-300: oklch(88.5% 0.013 68deg);
--color-surface-400: oklch(81.5% 0.016 66deg);
--color-surface-500: oklch(70.5% 0.018 64deg);
--color-surface-600: oklch(59% 0.018 62deg);
--color-surface-700: oklch(47.5% 0.018 58deg);
--color-surface-800: oklch(35.5% 0.02 55deg);
--color-surface-900: oklch(24.5% 0.02 52deg);
--color-surface-950: oklch(15.5% 0.022 48deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
--color-surface-contrast-50: var(--color-surface-contrast-dark);
--color-surface-contrast-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark);
--color-surface-contrast-400: var(--color-surface-contrast-dark);
--color-surface-contrast-500: var(--color-surface-contrast-dark);
--color-surface-contrast-600: var(--color-surface-contrast-light);
--color-surface-contrast-700: var(--color-surface-contrast-light);
--color-surface-contrast-800: var(--color-surface-contrast-light);
--color-surface-contrast-900: var(--color-surface-contrast-light);
--color-surface-contrast-950: var(--color-surface-contrast-light);
}

View File

@@ -0,0 +1,386 @@
/*
* AE Firefly — SteelBlue variant
* "Polished metal, like light on still water."
* Aether Platform / One Sky IT, LLC — Design System Theme
*
* Aesthetic vision (Scott Idem, 2026-03-09):
* A metallic, professional cool-blue variant of the Firefly system.
* Inspired by polished steel and chrome — the reflective shimmer of
* cool metal under crisp light. Calm authority, not cold distance.
*
* Color philosophy:
* Primary — Steel Blue: polished metallic blue (~214°), cool and precise
* Secondary — Burnished Gold: warm metallic contrast (~52°), brass/copper
* Tertiary — Cobalt Navy: deeper blue for depth and dimension (~229°)
* Surface — Chrome Silver: barely-cool neutral with a subtle blue cast
* (light mode: bright chrome) → (dark mode: gunmetal slate)
*
* Section 508 / WCAG 2.1 AA:
* - Body text (surface-950 on surface-50 light): >15:1 contrast ✓
* - Primary-filled buttons meet ≥3:1 for interactive components ✓
* - Contrast crossover points calculated per OKLCH approximate RL
*
* Based on: Skeleton v4 theme CSS variable structure
* Variant of: src/ae-firefly.css (AE_Firefly)
*/
html[data-theme='AE_Firefly_SteelBlue'] {
--text-scaling: 1.067;
--background: var(--color-surface-50) !important;
--base-font-color: var(--color-surface-950);
--base-font-color-dark: var(--color-surface-50);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-color-dark: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
/* Anchors: steel blue in light, lighter steel blue in dark */
--anchor-font-color: var(--color-primary-600);
--anchor-font-color-dark: var(--color-primary-300);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
--radius-base: 0.375rem;
--radius-container: 0.875rem;
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
--color-primary-50: oklch(96.5% 0.022 214deg);
--color-primary-100: oklch(91% 0.045 213deg);
--color-primary-200: oklch(84.5% 0.072 212deg);
--color-primary-300: oklch(76.5% 0.097 212deg);
--color-primary-400: oklch(67% 0.115 213deg);
--color-primary-500: oklch(56% 0.115 214deg);
--color-primary-600: oklch(49% 0.112 214deg);
--color-primary-700: oklch(41.5% 0.105 213deg);
--color-primary-800: oklch(34% 0.095 212deg);
--color-primary-900: oklch(26.5% 0.08 211deg);
--color-primary-950: oklch(18.5% 0.065 210deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-secondary-50: oklch(97.5% 0.055 56deg);
--color-secondary-100: oklch(93.5% 0.09 55deg);
--color-secondary-200: oklch(89.5% 0.12 54deg);
--color-secondary-300: oklch(85.5% 0.148 53deg);
--color-secondary-400: oklch(81.5% 0.162 52deg);
--color-secondary-500: oklch(76.5% 0.162 51deg);
--color-secondary-600: oklch(68.5% 0.152 50deg);
--color-secondary-700: oklch(60.5% 0.138 49deg);
--color-secondary-800: oklch(52% 0.122 48deg);
--color-secondary-900: oklch(43.5% 0.102 47deg);
--color-secondary-950: oklch(35% 0.084 46deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-tertiary-50: oklch(95.5% 0.025 232deg);
--color-tertiary-100: oklch(89.5% 0.048 231deg);
--color-tertiary-200: oklch(82.5% 0.072 230deg);
--color-tertiary-300: oklch(74.5% 0.095 229deg);
--color-tertiary-400: oklch(65.5% 0.12 229deg);
--color-tertiary-500: oklch(54.5% 0.135 230deg);
--color-tertiary-600: oklch(47% 0.132 230deg);
--color-tertiary-700: oklch(39.5% 0.122 229deg);
--color-tertiary-800: oklch(32% 0.108 228deg);
--color-tertiary-900: oklch(25% 0.09 227deg);
--color-tertiary-950: oklch(17.5% 0.072 226deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-surface-50: oklch(99% 0.004 220deg);
--color-surface-100: oklch(96.5% 0.008 218deg);
--color-surface-200: oklch(92.5% 0.012 217deg);
--color-surface-300: oklch(87% 0.016 216deg);
--color-surface-400: oklch(78.5% 0.02 215deg);
--color-surface-500: oklch(66.5% 0.022 217deg);
--color-surface-600: oklch(54.5% 0.025 220deg);
--color-surface-700: oklch(42.5% 0.028 223deg);
--color-surface-800: oklch(31% 0.032 226deg);
--color-surface-900: oklch(20.5% 0.035 228deg);
--color-surface-950: oklch(13% 0.04 232deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
}
html.dark[data-theme='AE_Firefly_SteelBlue'] {
--background: var(--color-surface-950) !important;
--default-border-width: 1px;
--default-divide-width: 1px;
--default-ring-width: 1px;
--body-background-color: var(--color-surface-50);
--body-background-color-dark: var(--color-surface-950);
/* ===================================================================
* PRIMARY — Polished Steel Blue
* Hue: ~214°. Cool metallic blue — the shine of polished steel
* under directional light. Professional, precise, and distinctive.
* Approx: #4682B4 (CSS SteelBlue) sits at oklch(56%, 0.113, 214°).
* At 500 (L≈56%): sufficient contrast with primary-50 text (≥4:1).
* =================================================================== */
--color-primary-50: oklch(96.5% 0.022 214deg);
--color-primary-100: oklch(91% 0.045 213deg);
--color-primary-200: oklch(84.5% 0.072 212deg);
--color-primary-300: oklch(76.5% 0.097 212deg);
--color-primary-400: oklch(67% 0.115 213deg);
--color-primary-500: oklch(56% 0.115 214deg);
--color-primary-600: oklch(49% 0.112 214deg);
--color-primary-700: oklch(41.5% 0.105 213deg);
--color-primary-800: oklch(34% 0.095 212deg);
--color-primary-900: oklch(26.5% 0.08 211deg);
--color-primary-950: oklch(18.5% 0.065 210deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-primary-contrast-50: var(--color-primary-contrast-dark);
--color-primary-contrast-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: var(--color-primary-contrast-dark);
--color-primary-contrast-400: var(--color-primary-contrast-dark);
--color-primary-contrast-500: var(--color-primary-contrast-light);
--color-primary-contrast-600: var(--color-primary-contrast-light);
--color-primary-contrast-700: var(--color-primary-contrast-light);
--color-primary-contrast-800: var(--color-primary-contrast-light);
--color-primary-contrast-900: var(--color-primary-contrast-light);
--color-primary-contrast-950: var(--color-primary-contrast-light);
/* ===================================================================
* SECONDARY — Burnished Gold (warm metallic contrast)
* Hue: ~52°. Warm brass/copper tones that complement the coolness
* of steel blue. The classic "metal on metal" contrast pairing —
* used for secondary actions, badges, and call-to-action highlights.
* =================================================================== */
--color-secondary-50: oklch(97.5% 0.055 56deg);
--color-secondary-100: oklch(93.5% 0.09 55deg);
--color-secondary-200: oklch(89.5% 0.12 54deg);
--color-secondary-300: oklch(85.5% 0.148 53deg);
--color-secondary-400: oklch(81.5% 0.162 52deg);
--color-secondary-500: oklch(76.5% 0.162 51deg);
--color-secondary-600: oklch(68.5% 0.152 50deg);
--color-secondary-700: oklch(60.5% 0.138 49deg);
--color-secondary-800: oklch(52% 0.122 48deg);
--color-secondary-900: oklch(43.5% 0.102 47deg);
--color-secondary-950: oklch(35% 0.084 46deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
--color-secondary-contrast-400: var(--color-secondary-contrast-dark);
--color-secondary-contrast-500: var(--color-secondary-contrast-dark);
--color-secondary-contrast-600: var(--color-secondary-contrast-light);
--color-secondary-contrast-700: var(--color-secondary-contrast-light);
--color-secondary-contrast-800: var(--color-secondary-contrast-light);
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
/* ===================================================================
* TERTIARY — Cobalt Navy
* Hue: ~229°. A deeper, richer blue for depth and dimension —
* like the heavy cobalt-blue depths under polished chrome.
* Used for accents, location chips, and depth elements.
* =================================================================== */
--color-tertiary-50: oklch(95.5% 0.025 232deg);
--color-tertiary-100: oklch(89.5% 0.048 231deg);
--color-tertiary-200: oklch(82.5% 0.072 230deg);
--color-tertiary-300: oklch(74.5% 0.095 229deg);
--color-tertiary-400: oklch(65.5% 0.12 229deg);
--color-tertiary-500: oklch(54.5% 0.135 230deg);
--color-tertiary-600: oklch(47% 0.132 230deg);
--color-tertiary-700: oklch(39.5% 0.122 229deg);
--color-tertiary-800: oklch(32% 0.108 228deg);
--color-tertiary-900: oklch(25% 0.09 227deg);
--color-tertiary-950: oklch(17.5% 0.072 226deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-400: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-500: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-600: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-700: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-800: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
/* ===================================================================
* SUCCESS — Bioluminescent Green
* Hue: ~152°. Consistent with AE_Firefly for recognizable semantic
* color meaning across OSIT themes.
* =================================================================== */
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-success-contrast-50: var(--color-success-contrast-dark);
--color-success-contrast-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: var(--color-success-contrast-dark);
--color-success-contrast-400: var(--color-success-contrast-dark);
--color-success-contrast-500: var(--color-success-contrast-dark);
--color-success-contrast-600: var(--color-success-contrast-dark);
--color-success-contrast-700: var(--color-success-contrast-light);
--color-success-contrast-800: var(--color-success-contrast-light);
--color-success-contrast-900: var(--color-success-contrast-light);
--color-success-contrast-950: var(--color-success-contrast-light);
/* ===================================================================
* WARNING — Amber Orange
* Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-warning-contrast-50: var(--color-warning-contrast-dark);
--color-warning-contrast-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: var(--color-warning-contrast-dark);
--color-warning-contrast-400: var(--color-warning-contrast-dark);
--color-warning-contrast-500: var(--color-warning-contrast-dark);
--color-warning-contrast-600: var(--color-warning-contrast-dark);
--color-warning-contrast-700: var(--color-warning-contrast-light);
--color-warning-contrast-800: var(--color-warning-contrast-light);
--color-warning-contrast-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: var(--color-warning-contrast-light);
/* ===================================================================
* ERROR — Soft Coral/Rose
* Consistent with AE_Firefly for recognizable semantic meaning.
* =================================================================== */
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-error-contrast-50: var(--color-error-contrast-dark);
--color-error-contrast-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: var(--color-error-contrast-dark);
--color-error-contrast-400: var(--color-error-contrast-light);
--color-error-contrast-500: var(--color-error-contrast-light);
--color-error-contrast-600: var(--color-error-contrast-light);
--color-error-contrast-700: var(--color-error-contrast-light);
--color-error-contrast-800: var(--color-error-contrast-light);
--color-error-contrast-900: var(--color-error-contrast-light);
--color-error-contrast-950: var(--color-error-contrast-light);
/* ===================================================================
* SURFACE — Chrome Silver
* A cool-blue-tinted neutral with slightly more chromatic presence
* than AE_Firefly's Moonlit Slate — leaning further toward the
* steel palette. Light mode: bright chrome. Dark mode: gunmetal.
*
* 50 → body-bg light: brilliant near-white with a chrome whisper
* 950 → body-bg dark: deep gunmetal with subtle cool-blue depth
* =================================================================== */
--color-surface-50: oklch(99% 0.004 220deg);
--color-surface-100: oklch(96.5% 0.008 218deg);
--color-surface-200: oklch(92.5% 0.012 217deg);
--color-surface-300: oklch(87% 0.016 216deg);
--color-surface-400: oklch(78.5% 0.02 215deg);
--color-surface-500: oklch(66.5% 0.022 217deg);
--color-surface-600: oklch(54.5% 0.025 220deg);
--color-surface-700: oklch(42.5% 0.028 223deg);
--color-surface-800: oklch(31% 0.032 226deg);
--color-surface-900: oklch(20.5% 0.035 228deg);
--color-surface-950: oklch(13% 0.04 232deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
--color-surface-contrast-50: var(--color-surface-contrast-dark);
--color-surface-contrast-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark);
--color-surface-contrast-400: var(--color-surface-contrast-dark);
--color-surface-contrast-500: var(--color-surface-contrast-dark);
--color-surface-contrast-600: var(--color-surface-contrast-light);
--color-surface-contrast-700: var(--color-surface-contrast-light);
--color-surface-contrast-800: var(--color-surface-contrast-light);
--color-surface-contrast-900: var(--color-surface-contrast-light);
--color-surface-contrast-950: var(--color-surface-contrast-light);
}

352
src/ae-firefly.css Normal file
View File

@@ -0,0 +1,352 @@
/*
* AE Firefly — "Shiny serenity, like a firefly."
* Aether Platform / One Sky IT, LLC — Design System Theme
*
* Aesthetic vision (Scott Idem, 2026-03-06):
* Calm, focused, softly luminous. A modern Section 508compliant
* theme for One Sky IT, LLC. Inspired by bioluminescence —
* the brief, cool glow of a firefly against a deep, serene night.
*
* Color philosophy:
* Primary — Luminescent Teal: cool bioluminescent shimmer
* Secondary — Warm Amber-Gold: the firefly's warm body glow
* Tertiary — Night-Sky Indigo: depth and serenity of the dark
* Surface — Moonlit Slate: barely-cool neutral; crisp white
* (light mode) → deep midnight blue-grey (dark mode)
*
* Section 508 / WCAG 2.1 AA:
* - Body text (surface-950 on surface-50 light): >15:1 contrast ✓
* - Primary-filled buttons meet ≥3:1 for interactive components ✓
* - Contrast crossover points calculated per OKLCH approximate RL
*
* Based on: Skeleton v4 theme CSS variable structure
* Reference: src/ae-osit-default.css, Nouveau preset
*/
html[data-theme='AE_Firefly'] {
--text-scaling: 1.067;
--background: var(--color-surface-50) !important;
--base-font-color: var(--color-surface-950);
--base-font-color-dark: var(--color-surface-50);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-color-dark: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
/* Anchors: teal in light, lighter teal in dark */
--anchor-font-color: var(--color-primary-600);
--anchor-font-color-dark: var(--color-primary-300);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
--radius-base: 0.375rem;
--radius-container: 0.875rem; /* slightly more rounded than default for modern feel */
--default-border-width: 1px;
}
/* --- Color ramps (light mode) copied from dark block so both modes have full ramps --- */
html[data-theme='AE_Firefly'] {
--color-primary-50: oklch(96.5% 0.025 192deg);
--color-primary-100: oklch(91% 0.05 190deg);
--color-primary-200: oklch(84.5% 0.078 188deg);
--color-primary-300: oklch(76.5% 0.105 186deg);
--color-primary-400: oklch(67.5% 0.125 185deg);
--color-primary-500: oklch(50.5% 0.13 184deg);
--color-primary-600: oklch(44% 0.125 183deg);
--color-primary-700: oklch(37.5% 0.115 182deg);
--color-primary-800: oklch(30.5% 0.105 181deg);
--color-primary-900: oklch(23.5% 0.09 180deg);
--color-primary-950: oklch(16% 0.075 179deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-secondary-50: oklch(97.5% 0.06 102deg);
--color-secondary-100: oklch(93.5% 0.095 100deg);
--color-secondary-200: oklch(89.5% 0.128 98deg);
--color-secondary-300: oklch(85.5% 0.155 95deg);
--color-secondary-400: oklch(81% 0.17 93deg);
--color-secondary-500: oklch(76% 0.17 90deg);
--color-secondary-600: oklch(68.5% 0.16 87deg);
--color-secondary-700: oklch(60.5% 0.145 85deg);
--color-secondary-800: oklch(52% 0.13 83deg);
--color-secondary-900: oklch(43.5% 0.11 81deg);
--color-secondary-950: oklch(35% 0.09 79deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-tertiary-50: oklch(95.5% 0.042 283deg);
--color-tertiary-100: oklch(89% 0.068 281deg);
--color-tertiary-200: oklch(81.5% 0.092 279deg);
--color-tertiary-300: oklch(73.5% 0.112 278deg);
--color-tertiary-400: oklch(65% 0.132 277deg);
--color-tertiary-500: oklch(55.5% 0.142 276deg);
--color-tertiary-600: oklch(48.5% 0.138 275deg);
--color-tertiary-700: oklch(41.5% 0.128 274deg);
--color-tertiary-800: oklch(34.5% 0.112 273deg);
--color-tertiary-900: oklch(27.5% 0.098 272deg);
--color-tertiary-950: oklch(20% 0.082 271deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-surface-50: oklch(99.2% 0.003 220deg);
--color-surface-100: oklch(97% 0.006 217deg);
--color-surface-200: oklch(93.5% 0.009 215deg);
--color-surface-300: oklch(88.5% 0.012 213deg);
--color-surface-400: oklch(81.5% 0.015 212deg);
--color-surface-500: oklch(70.5% 0.016 215deg);
--color-surface-600: oklch(59% 0.018 218deg);
--color-surface-700: oklch(47.5% 0.02 222deg);
--color-surface-800: oklch(30.5% 0.022 226deg);
--color-surface-900: oklch(24.5% 0.025 229deg);
--color-surface-950: oklch(15.5% 0.028 233deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
}
html.dark[data-theme='AE_Firefly'] {
--background: var(--color-surface-950) !important;
--default-divide-width: 1px;
--default-ring-width: 1px;
--body-background-color: var(--color-surface-50);
--body-background-color-dark: var(--color-surface-950);
/* ===================================================================
* PRIMARY — Luminescent Firefly Teal
* ...existing code...
*/
--color-primary-50: oklch(96.5% 0.025 192deg);
--color-primary-100: oklch(91% 0.05 190deg);
--color-primary-200: oklch(84.5% 0.078 188deg);
--color-primary-300: oklch(76.5% 0.105 186deg);
--color-primary-400: oklch(67.5% 0.125 185deg);
--color-primary-500: oklch(50.5% 0.13 184deg);
--color-primary-600: oklch(44% 0.125 183deg);
--color-primary-700: oklch(37.5% 0.115 182deg);
--color-primary-800: oklch(30.5% 0.105 181deg);
--color-primary-900: oklch(23.5% 0.09 180deg);
--color-primary-950: oklch(16% 0.075 179deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-primary-contrast-50: var(--color-primary-contrast-dark);
--color-primary-contrast-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: var(--color-primary-contrast-dark);
--color-primary-contrast-400: var(--color-primary-contrast-dark);
--color-primary-contrast-500: var(--color-primary-contrast-light);
--color-primary-contrast-600: var(--color-primary-contrast-light);
--color-primary-contrast-700: var(--color-primary-contrast-light);
--color-primary-contrast-800: var(--color-primary-contrast-light);
--color-primary-contrast-900: var(--color-primary-contrast-light);
--color-primary-contrast-950: var(--color-primary-contrast-light);
/* ...existing code for secondary, tertiary, success, warning, error, surface... */
--color-secondary-50: oklch(97.5% 0.06 102deg);
--color-secondary-100: oklch(93.5% 0.095 100deg);
--color-secondary-200: oklch(89.5% 0.128 98deg);
--color-secondary-300: oklch(85.5% 0.155 95deg);
--color-secondary-400: oklch(81% 0.17 93deg);
--color-secondary-500: oklch(76% 0.17 90deg);
--color-secondary-600: oklch(68.5% 0.16 87deg);
--color-secondary-700: oklch(60.5% 0.145 85deg);
--color-secondary-800: oklch(52% 0.13 83deg);
--color-secondary-900: oklch(43.5% 0.11 81deg);
--color-secondary-950: oklch(35% 0.09 79deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
--color-secondary-contrast-400: var(--color-secondary-contrast-dark);
--color-secondary-contrast-500: var(--color-secondary-contrast-dark);
--color-secondary-contrast-600: var(--color-secondary-contrast-light);
--color-secondary-contrast-700: var(--color-secondary-contrast-light);
--color-secondary-contrast-800: var(--color-secondary-contrast-light);
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
--color-tertiary-50: oklch(95.5% 0.042 283deg);
--color-tertiary-100: oklch(89% 0.068 281deg);
--color-tertiary-200: oklch(81.5% 0.092 279deg);
--color-tertiary-300: oklch(73.5% 0.112 278deg);
--color-tertiary-400: oklch(65% 0.132 277deg);
--color-tertiary-500: oklch(55.5% 0.142 276deg);
--color-tertiary-600: oklch(48.5% 0.138 275deg);
--color-tertiary-700: oklch(41.5% 0.128 274deg);
--color-tertiary-800: oklch(34.5% 0.112 273deg);
--color-tertiary-900: oklch(27.5% 0.098 272deg);
--color-tertiary-950: oklch(20% 0.082 271deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-400: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-500: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-600: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-700: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-800: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
--color-success-50: oklch(95.77% 0.05 152.69deg);
--color-success-100: oklch(91.59% 0.06 152deg);
--color-success-200: oklch(87.45% 0.08 152.08deg);
--color-success-300: oklch(83.57% 0.09 150.85deg);
--color-success-400: oklch(79.47% 0.11 150.71deg);
--color-success-500: oklch(75.38% 0.12 149.99deg);
--color-success-600: oklch(67.65% 0.11 149.94deg);
--color-success-700: oklch(59.71% 0.09 150.42deg);
--color-success-800: oklch(51.74% 0.08 150.24deg);
--color-success-900: oklch(43.2% 0.06 151.12deg);
--color-success-950: oklch(34.2% 0.04 151.44deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-success-contrast-50: var(--color-success-contrast-dark);
--color-success-contrast-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: var(--color-success-contrast-dark);
--color-success-contrast-400: var(--color-success-contrast-dark);
--color-success-contrast-500: var(--color-success-contrast-dark);
--color-success-contrast-600: var(--color-success-contrast-dark);
--color-success-contrast-700: var(--color-success-contrast-light);
--color-success-contrast-800: var(--color-success-contrast-light);
--color-success-contrast-900: var(--color-success-contrast-light);
--color-success-contrast-950: var(--color-success-contrast-light);
--color-warning-50: oklch(97.5% 0.065 78deg);
--color-warning-100: oklch(93.5% 0.09 75deg);
--color-warning-200: oklch(89.5% 0.12 73deg);
--color-warning-300: oklch(85.5% 0.145 70deg);
--color-warning-400: oklch(81.5% 0.16 67deg);
--color-warning-500: oklch(77% 0.165 65deg);
--color-warning-600: oklch(69.5% 0.155 64deg);
--color-warning-700: oklch(61.5% 0.14 63deg);
--color-warning-800: oklch(53.5% 0.125 62deg);
--color-warning-900: oklch(45% 0.105 61deg);
--color-warning-950: oklch(37% 0.088 60deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-warning-contrast-50: var(--color-warning-contrast-dark);
--color-warning-contrast-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: var(--color-warning-contrast-dark);
--color-warning-contrast-400: var(--color-warning-contrast-dark);
--color-warning-contrast-500: var(--color-warning-contrast-dark);
--color-warning-contrast-600: var(--color-warning-contrast-dark);
--color-warning-contrast-700: var(--color-warning-contrast-light);
--color-warning-contrast-800: var(--color-warning-contrast-light);
--color-warning-contrast-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: var(--color-warning-contrast-light);
--color-error-50: oklch(95% 0.04 18deg);
--color-error-100: oklch(88% 0.07 20deg);
--color-error-200: oklch(80% 0.105 21deg);
--color-error-300: oklch(72% 0.14 22deg);
--color-error-400: oklch(64.5% 0.17 23deg);
--color-error-500: oklch(57.5% 0.195 24deg);
--color-error-600: oklch(51.5% 0.182 25deg);
--color-error-700: oklch(45.5% 0.165 26deg);
--color-error-800: oklch(39.5% 0.148 27deg);
--color-error-900: oklch(33% 0.128 28deg);
--color-error-950: oklch(26.5% 0.108 29deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-error-contrast-50: var(--color-error-contrast-dark);
--color-error-contrast-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: var(--color-error-contrast-dark);
--color-error-contrast-400: var(--color-error-contrast-light);
--color-error-contrast-500: var(--color-error-contrast-light);
--color-error-contrast-600: var(--color-error-contrast-light);
--color-error-contrast-700: var(--color-error-contrast-light);
--color-error-contrast-800: var(--color-error-contrast-light);
--color-error-contrast-900: var(--color-error-contrast-light);
--color-error-contrast-950: var(--color-error-contrast-light);
--color-surface-50: oklch(99.2% 0.003 220deg);
--color-surface-100: oklch(97% 0.006 217deg);
--color-surface-200: oklch(93.5% 0.009 215deg);
--color-surface-300: oklch(88.5% 0.012 213deg);
--color-surface-400: oklch(81.5% 0.015 212deg);
--color-surface-500: oklch(70.5% 0.016 215deg);
--color-surface-600: oklch(59% 0.018 218deg);
--color-surface-700: oklch(47.5% 0.02 222deg);
--color-surface-800: oklch(35.5% 0.022 226deg);
--color-surface-900: oklch(24.5% 0.025 229deg);
--color-surface-950: oklch(15.5% 0.028 233deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
--color-surface-contrast-50: var(--color-surface-contrast-dark);
--color-surface-contrast-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark);
--color-surface-contrast-400: var(--color-surface-contrast-dark);
--color-surface-contrast-500: var(--color-surface-contrast-dark);
--color-surface-contrast-600: var(--color-surface-contrast-light);
--color-surface-contrast-700: var(--color-surface-contrast-light);
--color-surface-contrast-800: var(--color-surface-contrast-light);
--color-surface-contrast-900: var(--color-surface-contrast-light);
--color-surface-contrast-950: var(--color-surface-contrast-light);
}

205
src/ae-osit-default.css Normal file
View File

@@ -0,0 +1,205 @@
[data-theme='AE_OSIT_default'] {
--text-scaling: 1.067;
--base-font-color: var(--color-surface-950);
--base-font-color-dark: var(--color-surface-50);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-color-dark: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
--anchor-font-color: var(--color-primary-600);
--anchor-font-color-dark: var(--color-primary-400);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
--radius-base: 0.375rem;
--radius-container: 0.75rem;
--default-border-width: 1px;
--default-divide-width: 1px;
--default-ring-width: 1px;
--body-background-color: var(--color-surface-50);
--body-background-color-dark: var(--color-surface-950);
--color-primary-50: oklch(85.73% 0.07 251.8deg);
--color-primary-100: oklch(78.5% 0.09 252.03deg);
--color-primary-200: oklch(71.06% 0.1 253.6deg);
--color-primary-300: oklch(63.76% 0.12 253.85deg);
--color-primary-400: oklch(56.32% 0.14 255.25deg);
--color-primary-500: oklch(49.23% 0.15 256.36deg);
--color-primary-600: oklch(43.11% 0.14 258.86deg);
--color-primary-700: oklch(36.85% 0.14 261.54deg);
--color-primary-800: oklch(30.41% 0.13 263.99deg);
--color-primary-900: oklch(23.91% 0.12 265.91deg);
--color-primary-950: oklch(16.96% 0.12 264.05deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-primary-contrast-50: var(--color-primary-contrast-dark);
--color-primary-contrast-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: var(--color-primary-contrast-dark);
--color-primary-contrast-400: var(--color-primary-contrast-dark);
--color-primary-contrast-500: var(--color-primary-contrast-light);
--color-primary-contrast-600: var(--color-primary-contrast-light);
--color-primary-contrast-700: var(--color-primary-contrast-light);
--color-primary-contrast-800: var(--color-primary-contrast-light);
--color-primary-contrast-900: var(--color-primary-contrast-light);
--color-primary-contrast-950: var(--color-primary-contrast-light);
--color-secondary-50: oklch(96.26% 0.06 196.24deg);
--color-secondary-100: oklch(89.14% 0.07 220.79deg);
--color-secondary-200: oklch(82.13% 0.08 234.87deg);
--color-secondary-300: oklch(75.03% 0.11 245.33deg);
--color-secondary-400: oklch(68.15% 0.14 250.72deg);
--color-secondary-500: oklch(61.37% 0.16 255.34deg);
--color-secondary-600: oklch(55.1% 0.16 256.81deg);
--color-secondary-700: oklch(48.64% 0.15 258.4deg);
--color-secondary-800: oklch(41.84% 0.15 260.39deg);
--color-secondary-900: oklch(35.05% 0.14 262.03deg);
--color-secondary-950: oklch(28.12% 0.14 262.47deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: var(--color-secondary-contrast-dark);
--color-secondary-contrast-400: var(--color-secondary-contrast-dark);
--color-secondary-contrast-500: var(--color-secondary-contrast-dark);
--color-secondary-contrast-600: var(--color-secondary-contrast-light);
--color-secondary-contrast-700: var(--color-secondary-contrast-light);
--color-secondary-contrast-800: var(--color-secondary-contrast-light);
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
--color-tertiary-50: oklch(100% 0 none);
--color-tertiary-100: oklch(96.07% 0.01 251.15deg);
--color-tertiary-200: oklch(91.88% 0.03 252.69deg);
--color-tertiary-300: oklch(87.99% 0.05 253.24deg);
--color-tertiary-400: oklch(83.81% 0.06 253.57deg);
--color-tertiary-500: oklch(79.93% 0.08 253.32deg);
--color-tertiary-600: oklch(72.53% 0.08 251.75deg);
--color-tertiary-700: oklch(64.93% 0.08 249.75deg);
--color-tertiary-800: oklch(57.14% 0.09 247.99deg);
--color-tertiary-900: oklch(49.18% 0.09 246.55deg);
--color-tertiary-950: oklch(41.1% 0.09 246.54deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-400: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-500: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-600: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-700: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-800: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
--color-success-50: oklch(95.82% 0.06 184.52deg);
--color-success-100: oklch(91.55% 0.08 172.29deg);
--color-success-200: oklch(87.44% 0.11 165.22deg);
--color-success-300: oklch(83.26% 0.13 161.2deg);
--color-success-400: oklch(79.56% 0.16 157.13deg);
--color-success-500: oklch(76.12% 0.18 153.61deg);
--color-success-600: oklch(69.31% 0.17 151.81deg);
--color-success-700: oklch(62.07% 0.16 149.95deg);
--color-success-800: oklch(54.9% 0.15 147.65deg);
--color-success-900: oklch(47.26% 0.14 145.54deg);
--color-success-950: oklch(39.64% 0.13 143.79deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-success-contrast-50: var(--color-success-contrast-dark);
--color-success-contrast-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: var(--color-success-contrast-dark);
--color-success-contrast-400: var(--color-success-contrast-dark);
--color-success-contrast-500: var(--color-success-contrast-dark);
--color-success-contrast-600: var(--color-success-contrast-dark);
--color-success-contrast-700: var(--color-success-contrast-dark);
--color-success-contrast-800: var(--color-success-contrast-light);
--color-success-contrast-900: var(--color-success-contrast-light);
--color-success-contrast-950: var(--color-success-contrast-light);
--color-warning-50: oklch(98.26% 0.1 108.02deg);
--color-warning-100: oklch(95.84% 0.12 104.66deg);
--color-warning-200: oklch(93.48% 0.13 102.21deg);
--color-warning-300: oklch(91.49% 0.15 100.17deg);
--color-warning-400: oklch(89.28% 0.16 98.19deg);
--color-warning-500: oklch(87.14% 0.17 96.01deg);
--color-warning-600: oklch(79.88% 0.16 96.31deg);
--color-warning-700: oklch(72.35% 0.14 95.62deg);
--color-warning-800: oklch(64.73% 0.13 95.92deg);
--color-warning-900: oklch(56.77% 0.11 94.87deg);
--color-warning-950: oklch(48.63% 0.1 95.22deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-warning-contrast-50: var(--color-warning-contrast-dark);
--color-warning-contrast-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: var(--color-warning-contrast-dark);
--color-warning-contrast-400: var(--color-warning-contrast-dark);
--color-warning-contrast-500: var(--color-warning-contrast-dark);
--color-warning-contrast-600: var(--color-warning-contrast-dark);
--color-warning-contrast-700: var(--color-warning-contrast-dark);
--color-warning-contrast-800: var(--color-warning-contrast-light);
--color-warning-contrast-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: var(--color-warning-contrast-light);
--color-error-50: oklch(81.88% 0.1 38.14deg);
--color-error-100: oklch(75.88% 0.13 31.15deg);
--color-error-200: oklch(70.29% 0.16 27.32deg);
--color-error-300: oklch(65.15% 0.19 25.65deg);
--color-error-400: oklch(60.98% 0.21 25.56deg);
--color-error-500: oklch(57.86% 0.22 26.62deg);
--color-error-600: oklch(52.52% 0.2 26.86deg);
--color-error-700: oklch(46.81% 0.18 27.02deg);
--color-error-800: oklch(41.15% 0.16 27.63deg);
--color-error-900: oklch(35.01% 0.14 27.9deg);
--color-error-950: oklch(28.69% 0.12 29.23deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-error-contrast-50: var(--color-error-contrast-dark);
--color-error-contrast-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: var(--color-error-contrast-dark);
--color-error-contrast-400: var(--color-error-contrast-light);
--color-error-contrast-500: var(--color-error-contrast-light);
--color-error-contrast-600: var(--color-error-contrast-light);
--color-error-contrast-700: var(--color-error-contrast-light);
--color-error-contrast-800: var(--color-error-contrast-light);
--color-error-contrast-900: var(--color-error-contrast-light);
--color-error-contrast-950: var(--color-error-contrast-light);
--color-surface-50: oklch(100% 0 none);
--color-surface-100: oklch(93.98% 0 105.57deg);
--color-surface-200: oklch(87.66% 0 67.88deg);
--color-surface-300: oklch(81.35% 0 106.1deg);
--color-surface-400: oklch(74.79% 0 84.45deg);
--color-surface-500: oklch(68.29% 0 91.36deg);
--color-surface-600: oklch(60.99% 0 91.38deg);
--color-surface-700: oklch(53.5% 0 84.49deg);
--color-surface-800: oklch(46.03% 0 91.43deg);
--color-surface-900: oklch(37.94% 0 84.52deg);
--color-surface-950: oklch(29.34% 0 84.54deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
--color-surface-contrast-50: var(--color-surface-contrast-dark);
--color-surface-contrast-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark);
--color-surface-contrast-400: var(--color-surface-contrast-dark);
--color-surface-contrast-500: var(--color-surface-contrast-dark);
--color-surface-contrast-600: var(--color-surface-contrast-dark);
--color-surface-contrast-700: var(--color-surface-contrast-light);
--color-surface-contrast-800: var(--color-surface-contrast-light);
--color-surface-contrast-900: var(--color-surface-contrast-light);
--color-surface-contrast-950: var(--color-surface-contrast-light);
}

205
src/aeclci_v1.css Normal file
View File

@@ -0,0 +1,205 @@
[data-theme='aeclci'] {
--text-scaling: 1.067;
--base-font-color: var(--color-surface-950);
--base-font-color-dark: var(--color-surface-50);
--base-font-family: system-ui, sans-serif;
--base-font-size: inherit;
--base-line-height: inherit;
--base-font-weight: normal;
--base-font-style: normal;
--base-letter-spacing: 0em;
--heading-font-color: inherit;
--heading-font-color-dark: inherit;
--heading-font-family: inherit;
--heading-font-weight: bold;
--heading-font-style: normal;
--heading-letter-spacing: inherit;
--anchor-font-color: var(--color-primary-500);
--anchor-font-color-dark: var(--color-primary-500);
--anchor-font-family: inherit;
--anchor-font-size: inherit;
--anchor-line-height: inherit;
--anchor-font-weight: inherit;
--anchor-font-style: inherit;
--anchor-letter-spacing: inherit;
--anchor-text-decoration: none;
--anchor-text-decoration-hover: underline;
--anchor-text-decoration-active: none;
--anchor-text-decoration-focus: none;
--spacing: 0.25rem;
--radius-base: 0.375rem;
--radius-container: 0.75rem;
--default-border-width: 1px;
--default-divide-width: 1px;
--default-ring-width: 1px;
--body-background-color: var(--color-surface-50);
--body-background-color-dark: var(--color-surface-950);
--color-primary-50: oklch(85.1% 0.07 265.19deg);
--color-primary-100: oklch(77.89% 0.08 264.31deg);
--color-primary-200: oklch(70.32% 0.08 264.44deg);
--color-primary-300: oklch(62.86% 0.09 263.87deg);
--color-primary-400: oklch(54.96% 0.1 263.8deg);
--color-primary-500: oklch(47.12% 0.11 262.88deg);
--color-primary-600: oklch(40.9% 0.1 264.73deg);
--color-primary-700: oklch(34.53% 0.1 267.34deg);
--color-primary-800: oklch(28.16% 0.09 268.81deg);
--color-primary-900: oklch(21.29% 0.09 271.12deg);
--color-primary-950: oklch(12.88% 0.09 264.05deg);
--color-primary-contrast-dark: var(--color-primary-950);
--color-primary-contrast-light: var(--color-primary-50);
--color-primary-contrast-50: var(--color-primary-contrast-dark);
--color-primary-contrast-100: var(--color-primary-contrast-dark);
--color-primary-contrast-200: var(--color-primary-contrast-dark);
--color-primary-contrast-300: var(--color-primary-contrast-dark);
--color-primary-contrast-400: var(--color-primary-contrast-dark);
--color-primary-contrast-500: var(--color-primary-contrast-light);
--color-primary-contrast-600: var(--color-primary-contrast-light);
--color-primary-contrast-700: var(--color-primary-contrast-light);
--color-primary-contrast-800: var(--color-primary-contrast-light);
--color-primary-contrast-900: var(--color-primary-contrast-light);
--color-primary-contrast-950: var(--color-primary-contrast-light);
--color-secondary-50: oklch(73.24% 0.12 278.78deg);
--color-secondary-100: oklch(65.76% 0.12 276.12deg);
--color-secondary-200: oklch(58.15% 0.12 273.33deg);
--color-secondary-300: oklch(50.59% 0.12 270.28deg);
--color-secondary-400: oklch(42.65% 0.12 267.23deg);
--color-secondary-500: oklch(34.53% 0.12 264.22deg);
--color-secondary-600: oklch(30.3% 0.11 264.59deg);
--color-secondary-700: oklch(25.96% 0.09 265.69deg);
--color-secondary-800: oklch(21.25% 0.08 267.5deg);
--color-secondary-900: oklch(16.42% 0.06 269.55deg);
--color-secondary-950: oklch(8.85% 0.06 264.05deg);
--color-secondary-contrast-dark: var(--color-secondary-950);
--color-secondary-contrast-light: var(--color-secondary-50);
--color-secondary-contrast-50: var(--color-secondary-contrast-dark);
--color-secondary-contrast-100: var(--color-secondary-contrast-dark);
--color-secondary-contrast-200: var(--color-secondary-contrast-dark);
--color-secondary-contrast-300: var(--color-secondary-contrast-light);
--color-secondary-contrast-400: var(--color-secondary-contrast-light);
--color-secondary-contrast-500: var(--color-secondary-contrast-light);
--color-secondary-contrast-600: var(--color-secondary-contrast-light);
--color-secondary-contrast-700: var(--color-secondary-contrast-light);
--color-secondary-contrast-800: var(--color-secondary-contrast-light);
--color-secondary-contrast-900: var(--color-secondary-contrast-light);
--color-secondary-contrast-950: var(--color-secondary-contrast-light);
--color-tertiary-50: oklch(87.75% 0.12 326.52deg);
--color-tertiary-100: oklch(80.92% 0.13 323.93deg);
--color-tertiary-200: oklch(73.87% 0.14 321.55deg);
--color-tertiary-300: oklch(66.9% 0.15 319.41deg);
--color-tertiary-400: oklch(59.72% 0.16 317.25deg);
--color-tertiary-500: oklch(52.73% 0.17 315.13deg);
--color-tertiary-600: oklch(46.6% 0.16 314.18deg);
--color-tertiary-700: oklch(40.43% 0.14 312.8deg);
--color-tertiary-800: oklch(33.85% 0.13 309.88deg);
--color-tertiary-900: oklch(27.23% 0.12 306.83deg);
--color-tertiary-950: oklch(19.83% 0.1 302.7deg);
--color-tertiary-contrast-dark: var(--color-tertiary-950);
--color-tertiary-contrast-light: var(--color-tertiary-50);
--color-tertiary-contrast-50: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-100: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-200: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-300: var(--color-tertiary-contrast-dark);
--color-tertiary-contrast-400: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-500: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-600: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-700: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-800: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-900: var(--color-tertiary-contrast-light);
--color-tertiary-contrast-950: var(--color-tertiary-contrast-light);
--color-success-50: oklch(95.23% 0.07 195.99deg);
--color-success-100: oklch(90.22% 0.09 189.46deg);
--color-success-200: oklch(85.11% 0.1 186.03deg);
--color-success-300: oklch(80.35% 0.12 181.75deg);
--color-success-400: oklch(75.55% 0.12 178.92deg);
--color-success-500: oklch(71.19% 0.13 174.73deg);
--color-success-600: oklch(64.29% 0.12 173.65deg);
--color-success-700: oklch(57.46% 0.11 171.75deg);
--color-success-800: oklch(50.18% 0.1 170.68deg);
--color-success-900: oklch(42.87% 0.09 167.65deg);
--color-success-950: oklch(34.91% 0.07 164.42deg);
--color-success-contrast-dark: var(--color-success-950);
--color-success-contrast-light: var(--color-success-50);
--color-success-contrast-50: var(--color-success-contrast-dark);
--color-success-contrast-100: var(--color-success-contrast-dark);
--color-success-contrast-200: var(--color-success-contrast-dark);
--color-success-contrast-300: var(--color-success-contrast-dark);
--color-success-contrast-400: var(--color-success-contrast-dark);
--color-success-contrast-500: var(--color-success-contrast-dark);
--color-success-contrast-600: var(--color-success-contrast-dark);
--color-success-contrast-700: var(--color-success-contrast-light);
--color-success-contrast-800: var(--color-success-contrast-light);
--color-success-contrast-900: var(--color-success-contrast-light);
--color-success-contrast-950: var(--color-success-contrast-light);
--color-warning-50: oklch(95.67% 0.05 84.56deg);
--color-warning-100: oklch(92.83% 0.06 82.16deg);
--color-warning-200: oklch(90.12% 0.08 80.33deg);
--color-warning-300: oklch(87.59% 0.1 80.01deg);
--color-warning-400: oklch(85.03% 0.12 78.35deg);
--color-warning-500: oklch(82.46% 0.14 76.71deg);
--color-warning-600: oklch(76.34% 0.13 72.25deg);
--color-warning-700: oklch(70.34% 0.13 68.09deg);
--color-warning-800: oklch(63.99% 0.13 63.18deg);
--color-warning-900: oklch(57.91% 0.13 57.97deg);
--color-warning-950: oklch(51.69% 0.13 51.44deg);
--color-warning-contrast-dark: var(--color-warning-950);
--color-warning-contrast-light: var(--color-warning-50);
--color-warning-contrast-50: var(--color-warning-contrast-dark);
--color-warning-contrast-100: var(--color-warning-contrast-dark);
--color-warning-contrast-200: var(--color-warning-contrast-dark);
--color-warning-contrast-300: var(--color-warning-contrast-dark);
--color-warning-contrast-400: var(--color-warning-contrast-dark);
--color-warning-contrast-500: var(--color-warning-contrast-dark);
--color-warning-contrast-600: var(--color-warning-contrast-light);
--color-warning-contrast-700: var(--color-warning-contrast-light);
--color-warning-contrast-800: var(--color-warning-contrast-light);
--color-warning-contrast-900: var(--color-warning-contrast-light);
--color-warning-contrast-950: var(--color-warning-contrast-light);
--color-error-50: oklch(84.29% 0.09 46.91deg);
--color-error-100: oklch(78.63% 0.12 39.19deg);
--color-error-200: oklch(72.92% 0.14 34.35deg);
--color-error-300: oklch(67.88% 0.17 31.48deg);
--color-error-400: oklch(63.09% 0.19 30.02deg);
--color-error-500: oklch(59.32% 0.21 29.47deg);
--color-error-600: oklch(53.56% 0.19 29.25deg);
--color-error-700: oklch(47.75% 0.17 29.2deg);
--color-error-800: oklch(41.51% 0.15 28.7deg);
--color-error-900: oklch(35.35% 0.14 28.7deg);
--color-error-950: oklch(28.69% 0.12 29.23deg);
--color-error-contrast-dark: var(--color-error-950);
--color-error-contrast-light: var(--color-error-50);
--color-error-contrast-50: var(--color-error-contrast-dark);
--color-error-contrast-100: var(--color-error-contrast-dark);
--color-error-contrast-200: var(--color-error-contrast-dark);
--color-error-contrast-300: var(--color-error-contrast-dark);
--color-error-contrast-400: var(--color-error-contrast-dark);
--color-error-contrast-500: var(--color-error-contrast-light);
--color-error-contrast-600: var(--color-error-contrast-light);
--color-error-contrast-700: var(--color-error-contrast-light);
--color-error-contrast-800: var(--color-error-contrast-light);
--color-error-contrast-900: var(--color-error-contrast-light);
--color-error-contrast-950: var(--color-error-contrast-light);
--color-surface-50: oklch(100% 0 none);
--color-surface-100: oklch(97.02% 0 none);
--color-surface-200: oklch(94.01% 0 none);
--color-surface-300: oklch(91.12% 0 196.34deg);
--color-surface-400: oklch(88.07% 0 196.37deg);
--color-surface-500: oklch(84.99% 0 196.4deg);
--color-surface-600: oklch(77.78% 0 196.47deg);
--color-surface-700: oklch(70.09% 0 196.54deg);
--color-surface-800: oklch(62.51% 0 196.61deg);
--color-surface-900: oklch(54.34% 0 196.68deg);
--color-surface-950: oklch(46.22% 0 196.73deg);
--color-surface-contrast-dark: var(--color-surface-950);
--color-surface-contrast-light: var(--color-surface-50);
--color-surface-contrast-50: var(--color-surface-contrast-dark);
--color-surface-contrast-100: var(--color-surface-contrast-dark);
--color-surface-contrast-200: var(--color-surface-contrast-dark);
--color-surface-contrast-300: var(--color-surface-contrast-dark);
--color-surface-contrast-400: var(--color-surface-contrast-dark);
--color-surface-contrast-500: var(--color-surface-contrast-dark);
--color-surface-contrast-600: var(--color-surface-contrast-dark);
--color-surface-contrast-700: var(--color-surface-contrast-dark);
--color-surface-contrast-800: var(--color-surface-contrast-dark);
--color-surface-contrast-900: var(--color-surface-contrast-light);
--color-surface-contrast-950: var(--color-surface-contrast-light);
}

View File

@@ -1,38 +0,0 @@
import axios from 'axios';
console.log('*** api_update.js ***');
var axios_fastapi = axios.create({
//baseURL: 'https://dev-fastapi.oneskyit.com',
//baseURL: 'http://fastapi.localhost:5005',
baseURL: 'http://192.168.32.20:5005',
/* other custom settings */
});
axios_fastapi.defaults.headers['Access-Control-Allow-Origin'] = '*';
axios_fastapi.defaults.headers['content-type'] = 'application/json';
axios_fastapi.defaults.headers['x-aether-api-key'] = 'aaabbbcccxxxyyyzzz';
axios_fastapi.defaults.headers['x-aether-api-token'] = 'aaabbbcccxxxyyyzzz';
axios_fastapi.defaults.headers['x-aether-api-expire-on'] = '';
axios_fastapi.defaults.headers['x-account-id'] = 'xxxyyyzzz111222333';
async function delete_object({axios=axios_fastapi, endpoint='', data=[]}) {
console.log('*** delete_object() ***');
console.log(data)
// https://stackoverflow.com/questions/51069552/axios-delete-request-with-body-and-headers
let response_data = await axios.delete(endpoint, { 'data': data }) // not just data?
.then(function (response) {
console.log(response.data);
return response.data;
})
.catch(function (error) {
console.log(error);
return error;
});
return response_data;
}
export default delete_object;

View File

@@ -1,52 +0,0 @@
import axios from 'axios';
console.log('*** api_update.js ***');
var axios_fastapi = axios.create({
baseURL: 'https://dev-fastapi.oneskyit.com',
// baseURL: 'http://fastapi.localhost:5005',
// baseURL: 'http://192.168.32.20:5005',
/* other custom settings */
});
axios_fastapi.defaults.headers['Access-Control-Allow-Origin'] = '*';
axios_fastapi.defaults.headers['content-type'] = 'application/json';
axios_fastapi.defaults.headers['x-aether-api-key'] = 'aaabbbcccxxxyyyzzz';
axios_fastapi.defaults.headers['x-aether-api-token'] = 'aaabbbcccxxxyyyzzz';
axios_fastapi.defaults.headers['x-aether-api-expire-on'] = '';
axios_fastapi.defaults.headers['x-account-id'] = 'xxxyyyzzz111222333';
async function get_object({axios=axios_fastapi, endpoint='', params=[], data=[], return_meta=false, force_li=false}) {
console.log('*** get_object() ***');
//console.log(endpoint);
//console.log(params);
//console.log(data);
//console.log(return_meta);
//console.log(force_li);
let response_data = await axios.get(endpoint, { params: params })
.then(function (response) {
console.log(response.data);
if (!Array.isArray(response.data['data']) && force_li) {
console.log('Forcing return as a list');
let return_data = [];
return_data.push(response.data['data']);
return return_data;
} else {
return response.data['data'];
}
//return response.data;
})
.catch(function (error) {
console.log(error.response);
if (error.response.status === 404) {
return null;
}
return error;
});
return response_data;
}
export default get_object;

View File

@@ -1,45 +0,0 @@
import axios from 'axios';
console.log('*** api_update.js ***');
var axios_fastapi = axios.create({
// baseURL: 'https://dev-fastapi.oneskyit.com',
baseURL: 'http://fastapi.localhost:5005',
// baseURL: 'http://192.168.32.20:5005',
/* other custom settings */
});
axios_fastapi.defaults.headers['Access-Control-Allow-Origin'] = '*';
axios_fastapi.defaults.headers['content-type'] = 'application/json';
axios_fastapi.defaults.headers['x-aether-api-key'] = 'aaabbbcccxxxyyyzzz';
axios_fastapi.defaults.headers['x-aether-api-token'] = 'aaabbbcccxxxyyyzzz';
axios_fastapi.defaults.headers['x-aether-api-expire-on'] = '';
axios_fastapi.defaults.headers['x-account-id'] = 'xxxyyyzzz111222333';
async function patch_object({axios=axios_fastapi, endpoint='', params=[], record=[], return_meta=false}) {
console.log('*** patch_object() ***');
//console.log(endpoint);
//console.log(params);
console.log(record);
//console.log(return_meta);
//console.log(force_li);
let response_data = await axios.patch(endpoint, record, { params: params })
.then(function (response) {
console.log(response.data);
return response.data['data'];
//return response.data;
})
.catch(function (error) {
console.log(error.response);
if (error.response.status === 404) {
return null;
}
return error;
});
return response_data;
}
export default patch_object;

View File

@@ -1,45 +0,0 @@
import axios from 'axios';
console.log('*** api_post.js ***');
var axios_fastapi = axios.create({
// baseURL: 'https://dev-fastapi.oneskyit.com',
baseURL: 'http://fastapi.localhost:5005',
// baseURL: 'http://192.168.32.20:5005',
/* other custom settings */
});
axios_fastapi.defaults.headers['Access-Control-Allow-Origin'] = '*';
axios_fastapi.defaults.headers['content-type'] = 'application/json';
axios_fastapi.defaults.headers['x-aether-api-key'] = 'aaabbbcccxxxyyyzzz';
axios_fastapi.defaults.headers['x-aether-api-token'] = 'aaabbbcccxxxyyyzzz';
axios_fastapi.defaults.headers['x-aether-api-expire-on'] = '';
axios_fastapi.defaults.headers['x-account-id'] = 'xxxyyyzzz111222333';
async function post_object({axios=axios_fastapi, endpoint='', record=[], return_meta=false}) {
console.log('*** post_object() ***');
//console.log(endpoint);
//console.log(params);
console.log(record);
//console.log(return_meta);
//console.log(force_li);
let response_data = await axios.post(endpoint, record)
.then(function (response) {
console.log(response.data);
return response.data['data'];
//return response.data;
})
.catch(function (error) {
console.log(error.response);
if (error.response.status === 404) {
return null;
}
return error;
});
return response_data;
}
export default post_object;

Some files were not shown because too many files have changed in this diff Show More