- 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>
- 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>
- 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>
- 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>
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>
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>
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>
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>
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>
- 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
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>
- 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>
- 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>
- 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>
- 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>
- +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>
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>
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>
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>
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>
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>
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>
$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>
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
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
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>
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>
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>
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>
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>
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>
- 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>
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>