- 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>
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>
- 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.
- 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)
- 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
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)
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.
- 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
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>
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>
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>
- 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>
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>
- 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>
- 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>
- 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
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.
- 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>
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).
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.
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.
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.
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.
- 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
- 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.
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