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>
This commit is contained in:
Scott Idem
2026-03-25 11:39:24 -04:00
parent 1c818e648b
commit 613e43114c
2 changed files with 77 additions and 69 deletions

View File

@@ -593,7 +593,8 @@ const theme_options = [
dark:border-gray-700/60 dark:bg-gray-900/30 dark:border-gray-700/60 dark:bg-gray-900/30
" "
class:border-primary-400={expand}> class:border-primary-400={expand}>
<!-- AUTH STATUS SHIELD ───────────────────────────────────────── --> <!-- AUTH STATUS SHIELD — hidden in iframe; host page manages auth context -->
{#if !$ae_loc?.iframe}
<button <button
type="button" type="button"
class=" class="
@@ -628,8 +629,10 @@ const theme_options = [
{access_label ?? 'Auth?'} {access_label ?? 'Auth?'}
</span> </span>
</button> </button>
{/if}
<!-- FONT SIZE CYCLER ─────────────────────────────────────────── --> <!-- FONT SIZE CYCLER — hidden in iframe; font choice belongs to the host page -->
{#if !$ae_loc?.iframe}
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-surface hover:preset-tonal-primary group/font transition-all duration-200" class="btn btn-sm preset-tonal-surface hover:preset-tonal-primary group/font transition-all duration-200"
@@ -640,8 +643,10 @@ const theme_options = [
class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/font:max-w-20 group-hover/font:opacity-100" class="btn-label max-w-0 overflow-hidden pl-1 text-xs opacity-0 transition-all duration-300 ease-in-out group-hover/font:max-w-20 group-hover/font:opacity-100"
>Font</span> >Font</span>
</button> </button>
{/if}
<!-- DARK / LIGHT TOGGLE ──────────────────────────────────────── --> <!-- DARK / LIGHT TOGGLE — hidden in iframe; theme is set by the host page -->
{#if !$ae_loc?.iframe}
<button <button
type="button" type="button"
class="btn btn-sm preset-tonal-surface hover:preset-tonal-secondary group/mode transition-all duration-200" class="btn btn-sm preset-tonal-surface hover:preset-tonal-secondary group/mode transition-all duration-200"
@@ -660,6 +665,7 @@ const theme_options = [
>Light</span> >Light</span>
{/if} {/if}
</button> </button>
{/if}
<!-- EDIT MODE TOGGLE (authenticated+ only) ───────────────────── --> <!-- EDIT MODE TOGGLE (authenticated+ only) ───────────────────── -->
{#if $ae_loc?.authenticated_access} {#if $ae_loc?.authenticated_access}

View File

@@ -218,13 +218,15 @@ $effect(() => {
// the iframe src to pass show_menu=true, so we watch for trusted_access and unhide it. // the iframe src to pass show_menu=true, so we watch for trusted_access and unhide it.
$effect(() => { $effect(() => {
if (browser && $ae_loc.iframe && $ae_loc.trusted_access) { if (browser && $ae_loc.iframe && $ae_loc.trusted_access) {
// WHY untrack: writing to $ae_loc inside an effect that reads $ae_loc causes // WHY the guard: ae_loc is a tracked dep here (reads above). Writing
// an infinite reactive loop (effect_update_depth_exceeded). The conditions we // $ae_loc.sys_menu.hide re-notifies this effect every run — infinite loop.
// want to react to ($ae_loc.iframe / trusted_access) are tracked above; the // untrack() does NOT help: it prevents new dep reads but the store still
// write is a side-effect that must not re-trigger this effect. // notifies already-subscribed effects after the write.
untrack(() => { // The idempotent write guard breaks the cycle: run 2 finds the value
// already false, skips the write, no further re-queue.
if ($ae_loc.sys_menu.hide !== false) {
$ae_loc.sys_menu.hide = false; $ae_loc.sys_menu.hide = false;
}); }
} }
}); });