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>
This commit is contained in:
Scott Idem
2026-03-13 16:59:04 -04:00
parent 50bfe2f64b
commit 0b9272e9f9
3 changed files with 108 additions and 183 deletions

View File

@@ -15,6 +15,7 @@
import {
Bug,
CircleX,
LogOut,
Menu,
Moon,
Sun,
@@ -91,7 +92,10 @@
$ae_sess.sys_menu.expand = true;
$ae_loc.app_cfg.show_element__access_type = true;
if ($ae_loc?.access_type === 'anonymous') {
if (!$ae_loc?.access_type || $ae_loc?.access_type === 'anonymous') {
// onDestroy in Element_access_type resets show_element__passcode_input to false
// on panel close, so we must restore it here for menu-button opens too.
$ae_sess.app_cfg.show_element__passcode_input = true;
$ae_sess.sys_menu.focus_passcode_input = true;
} else {
$ae_loc.sys_menu.expand_user = false;
@@ -139,8 +143,8 @@
$ae_loc.sys_menu.expand_access_type = true;
$ae_sess.app_cfg.show_element__passcode_input = true;
$ae_sess.sys_menu.focus_passcode_input = true;
const to_focus = document.getElementById('access_passcode_input');
to_focus?.focus();
// Note: focus is handled reactively by the focus_input binding in Element_access_type.
// Direct getElementById here would fail — the panel DOM doesn't exist yet at this point.
}
}
@@ -265,9 +269,17 @@
class="w-full flex items-center justify-between px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
onclick={() => sec_signin = !sec_signin}
>
<span class="flex items-center gap-1">
<span class="fas fa-sign-in-alt opacity-60"></span>
Sign In / Out
<span class="flex items-center gap-1.5 min-w-0">
{#if $ae_loc?.person_id && $ae_loc?.user_id}
<LogOut size="0.85em" class="opacity-60 shrink-0" />
<span>Sign Out</span>
<span class="normal-case font-normal opacity-70 truncate">
{$ae_loc?.user?.username ?? person_display ?? ''}
</span>
{:else}
<ShieldUser size="0.85em" class="opacity-60 shrink-0" />
<span>User Sign In</span>
{/if}
</span>
<span class="opacity-50">{sec_signin ? '▲' : '▼'}</span>
</button>
@@ -290,11 +302,20 @@
class="w-full flex items-center justify-between px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
onclick={() => sec_access = !sec_access}
>
<span class="flex items-center gap-1">
<span class="fas fa-shield-alt opacity-60"></span>
Access / Passcode
{#if access_label}
<span class="normal-case font-normal text-primary-600 dark:text-primary-400 ml-1">({access_label})</span>
<span class="flex items-center gap-1.5 min-w-0">
{#if $ae_loc?.access_type && $ae_loc?.access_type !== 'anonymous'}
{#if $ae_loc?.user_access_type && $ae_loc?.access_type !== $ae_loc?.user_access_type}
<ShieldMinus size="0.85em" class="opacity-60 shrink-0" />
<span>Elevated Access</span>
<span class="normal-case font-normal text-primary-600 dark:text-primary-400 truncate">({access_label})</span>
{:else}
<ShieldEllipsis size="0.85em" class="opacity-60 shrink-0" />
<span>Access</span>
<span class="normal-case font-normal text-primary-600 dark:text-primary-400 truncate">({access_label})</span>
{/if}
{:else}
<ShieldUser size="0.85em" class="opacity-60 shrink-0" />
<span>Enter Passcode</span>
{/if}
</span>
<span class="opacity-50">{sec_access ? '▲' : '▼'}</span>
@@ -330,29 +351,18 @@
<span class="opacity-50">{sec_appearance ? '▲' : '▼'}</span>
</button>
{#if sec_appearance}
<div class="px-3 pb-3 space-y-3">
<div class="px-3 pb-3 pt-1 space-y-3">
<!-- Theme name -->
<div class="space-y-1">
<div class="text-xs text-gray-400 dark:text-gray-500 uppercase tracking-wide">Theme</div>
<select
bind:value={$ae_loc.theme_name}
onchange={(e) => document.documentElement.setAttribute('data-theme', (e.target as HTMLSelectElement).value)}
class="select select-sm w-full text-xs"
>
{#each theme_options as opt}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
</div>
<!-- Dark / Light mode + Font size (side by side) -->
<div class="flex items-center justify-between gap-2">
<!-- Quick actions row: mode + font (most commonly used) -->
<div class="flex gap-2">
<button
type="button"
class="btn btn-sm flex-1 preset-tonal-secondary hover:preset-filled-secondary-500 transition-all"
class="btn btn-sm flex-1 transition-all
{$ae_loc?.theme_mode === 'dark'
? 'preset-tonal-secondary hover:preset-filled-secondary-500'
: 'preset-tonal-warning hover:preset-filled-warning-500'}"
onclick={toggle_theme_mode}
title="Toggle light/dark mode"
title="Toggle light/dark mode (currently: {$ae_loc?.theme_mode ?? 'unknown'})"
>
{#if $ae_loc?.theme_mode === 'dark'}
<Moon size="1em" class="shrink-0" />
@@ -369,10 +379,24 @@
title={font_title}
>
<span class="font-bold text-sm leading-none">{font_label}</span>
<span class="text-xs ml-1">Font</span>
<span class="text-xs ml-1 opacity-70">Font</span>
</button>
</div>
<!-- Theme name select -->
<div class="space-y-1.5">
<div class="text-xs text-gray-400 dark:text-gray-500 font-medium">Theme</div>
<select
bind:value={$ae_loc.theme_name}
onchange={(e) => document.documentElement.setAttribute('data-theme', (e.target as HTMLSelectElement).value)}
class="select w-full text-sm"
>
{#each theme_options as opt (opt.value)}
<option value={opt.value}>{opt.label}</option>
{/each}
</select>
</div>
</div>
{/if}
</div>