[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
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
|
||||
### [Launcher] Active features (identified 2026-03-06)
|
||||
|
||||
- **Font size cycler (Launcher sidebar):** Staff onsite may not have access to the system menu, so the launcher sidebar needs its own font size cycler. Add `font_size_step: number` to `$events_loc.launcher` store. Add a cycle button in `launcher_menu.svelte` alongside the "All Files"/"All Sessions" show/hide buttons. Three steps: compact (`text-xs`) → default (`text-sm`) → large (`text-base`). Apply the class to the launcher sidebar root container `<div>`.
|
||||
- [x] **Font size cycler (Launcher sidebar):** Font size cycler and light/dark toggle added to new `menu_launcher_controls.svelte` component; wired into `launcher_menu.svelte`. Visibility toggles (All Files / All Sessions) moved to same component and restyled to `preset-tonal-tertiary`. (2026-03-11)
|
||||
|
||||
- **Minor Svelte warning:** `slct_event_location_id` prop in `menu_location_list.svelte` is not `$bindable()` but `bind:value={slct_event_location_id}` is used. Functionally fine since `onchange` writes directly to `$events_slct.event_location_id`.
|
||||
|
||||
@@ -27,9 +27,9 @@ Key questions before starting: which routes, does the Electron app scan, what do
|
||||
lead record look like in the DB?
|
||||
|
||||
### [DevOps] Remaining deployment items
|
||||
- [ ] **Wire AE_APP_REPLICAS:** `docker-compose.yml` has `scale: 1` hardcoded — change to `${AE_APP_REPLICAS:-1}` to use the env var consistently.
|
||||
- [ ] **Archive ae_env_node_app:** The old Red/Green/Blue node env has been superseded by the unified `aether_container_env`. Archive or move that directory once confirmed nothing depends on it.
|
||||
- [ ] **Build Optimization:** Explore using a private container registry to separate build from deploy (build once, deploy anywhere).
|
||||
- [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)
|
||||
|
||||
|
||||
### [General]
|
||||
|
||||
@@ -1,4 +1,39 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* launcher_menu.svelte — Aether Launcher: Sidebar Menu Container
|
||||
*
|
||||
* PURPOSE:
|
||||
* The main sidebar panel for the Aether Events Launcher. Composes all menu
|
||||
* sub-components into a single vertical column and passes data down from the
|
||||
* parent layout. This is intentionally a thin coordinator — business logic lives
|
||||
* in the individual sub-components and the layout's liveQuery / effect layer.
|
||||
*
|
||||
* STRUCTURE (top to bottom):
|
||||
* 1. Event-level files (lq__event_event_file_obj_li)
|
||||
* Files attached directly to the event (e.g. a shared opening slide deck).
|
||||
* Shown regardless of which room/session is selected.
|
||||
*
|
||||
* 2. Location selector (Menu_location_list_menu — edit mode only)
|
||||
* Dropdown that lets operators switch the active room. Triggers a session
|
||||
* list reload and navigates the URL to /launcher/{location_id}.
|
||||
*
|
||||
* 3. Location-level files (lq__location_event_file_obj_li)
|
||||
* Files attached to the selected room — e.g. A/V setup guides, room schedules.
|
||||
* Hidden until a room is selected.
|
||||
*
|
||||
* 4. Session list (Menu_session_list_menu)
|
||||
* Compact button list of all sessions in the selected room. Operators click
|
||||
* to switch the active session; hover pre-loads after a delay timer.
|
||||
*
|
||||
* 5. Launcher controls (Menu_launcher_controls)
|
||||
* Bottom bar for accessibility and visibility settings: font size cycler,
|
||||
* light/dark toggle, and (edit mode only) show/hide draft files/sessions.
|
||||
*
|
||||
* DATA FLOW:
|
||||
* All liveQuery stores (lq__*) are passed in from +layout.svelte and originate
|
||||
* from Dexie IndexedDB — never fetched directly here. Selection state is
|
||||
* coordinated via $events_slct / $events_loc stores.
|
||||
*/
|
||||
interface Props {
|
||||
lq__event_obj: any;
|
||||
|
||||
@@ -84,6 +119,7 @@
|
||||
import Event_launcher_file_cont from './launcher_file_cont.svelte';
|
||||
import Menu_location_list_menu from './menu_location_list.svelte';
|
||||
import Menu_session_list_menu from './menu_session_list.svelte';
|
||||
import Menu_launcher_controls from './menu_launcher_controls.svelte';
|
||||
|
||||
// *** Functions and Logic
|
||||
|
||||
@@ -214,67 +250,5 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if $ae_loc.edit_mode}
|
||||
<div
|
||||
class="
|
||||
w-full max-w-full
|
||||
flex flex-row gap-1 items-center justify-center
|
||||
"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
if ($events_loc.launcher.show_content__hidden_files) {
|
||||
$events_loc.launcher.show_content__hidden_files = false;
|
||||
$events_loc.launcher.hide_content__draft_files = true;
|
||||
} else {
|
||||
$events_loc.launcher.show_content__hidden_files = true;
|
||||
$events_loc.launcher.hide_content__draft_files = false;
|
||||
}
|
||||
}}
|
||||
class="
|
||||
btn btn-sm
|
||||
text-xs
|
||||
w-1/2 max-w-1/2
|
||||
preset-tonal-warning
|
||||
hover:preset-tonal-success
|
||||
transition-all
|
||||
"
|
||||
title="Toggle showing hidden files. Including files marked as 'draft'."
|
||||
>
|
||||
{#if $events_loc.launcher.show_content__hidden_files}
|
||||
<span class="fas fa-eye-slash m-1 text-neutral-800/80"
|
||||
></span>
|
||||
Hide Files
|
||||
{:else}
|
||||
<span class="fas fa-eye m-1 text-neutral-800/80"></span>
|
||||
All Files
|
||||
{/if}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$events_loc.launcher.show_content__hidden_sessions =
|
||||
!$events_loc.launcher.show_content__hidden_sessions;
|
||||
}}
|
||||
class="
|
||||
btn btn-sm
|
||||
text-xs
|
||||
w-1/2 max-w-1/2
|
||||
preset-tonal-warning
|
||||
hover:preset-tonal-success
|
||||
transition-all
|
||||
"
|
||||
>
|
||||
{#if $events_loc.launcher.show_content__hidden_sessions}
|
||||
<span class="fas fa-eye-slash m-1 text-neutral-800/80"
|
||||
></span>
|
||||
Hide Sessions
|
||||
{:else}
|
||||
<span class="fas fa-eye m-1 text-neutral-800/80"></span>
|
||||
All Sessions
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
<Menu_launcher_controls />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* menu_launcher_controls.svelte — Aether Launcher: Bottom Control Bar
|
||||
*
|
||||
* PURPOSE:
|
||||
* Accessibility and visibility controls for the Launcher sidebar. Placed at the
|
||||
* bottom of launcher_menu.svelte so presenters and operators can adjust display
|
||||
* settings without needing access to the global AE System Menu (which may be
|
||||
* unavailable in kiosk, locked, or podium setups).
|
||||
*
|
||||
* SECTIONS:
|
||||
* 1. Visibility toggles (edit mode only) — show/hide draft files and hidden sessions
|
||||
* 2. Accessibility controls (always visible) — font size cycler and light/dark toggle
|
||||
*
|
||||
* WHY ALWAYS-VISIBLE ACCESSIBILITY CONTROLS:
|
||||
* Projector-connected screens, tablets, and phones at conference venues vary wildly
|
||||
* in lighting. Operators and presenters need quick one-tap access to font size and
|
||||
* theme mode without hunting through the system menu or requiring admin access.
|
||||
*/
|
||||
|
||||
import { Moon, Sun } from '@lucide/svelte';
|
||||
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
import { events_loc } from '$lib/stores/ae_events_stores';
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
}
|
||||
|
||||
let { log_lvl = $bindable(0) }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="w-full max-w-full flex flex-col gap-1 items-center justify-center">
|
||||
<!-- ── Visibility toggles — edit mode only ── -->
|
||||
{#if $ae_loc.edit_mode}
|
||||
<div class="w-full max-w-full flex flex-row gap-1 items-center justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
if ($events_loc.launcher.show_content__hidden_files) {
|
||||
$events_loc.launcher.show_content__hidden_files = false;
|
||||
$events_loc.launcher.hide_content__draft_files = true;
|
||||
} else {
|
||||
$events_loc.launcher.show_content__hidden_files = true;
|
||||
$events_loc.launcher.hide_content__draft_files = false;
|
||||
}
|
||||
}}
|
||||
class="
|
||||
btn btn-sm text-xs
|
||||
w-1/2 max-w-1/2
|
||||
preset-tonal-tertiary hover:preset-filled-tertiary-500
|
||||
transition-all
|
||||
"
|
||||
title="Toggle visibility of hidden and draft files in the launcher file list."
|
||||
>
|
||||
{#if $events_loc.launcher.show_content__hidden_files}
|
||||
<span class="fas fa-eye-slash m-1 text-neutral-800/80"></span>
|
||||
Hide Files
|
||||
{:else}
|
||||
<span class="fas fa-eye m-1 text-neutral-800/80"></span>
|
||||
All Files
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$events_loc.launcher.show_content__hidden_sessions =
|
||||
!$events_loc.launcher.show_content__hidden_sessions;
|
||||
}}
|
||||
class="
|
||||
btn btn-sm text-xs
|
||||
w-1/2 max-w-1/2
|
||||
preset-tonal-tertiary hover:preset-filled-tertiary-500
|
||||
transition-all
|
||||
"
|
||||
title="Toggle visibility of hidden and cancelled sessions in the launcher session list."
|
||||
>
|
||||
{#if $events_loc.launcher.show_content__hidden_sessions}
|
||||
<span class="fas fa-eye-slash m-1 text-neutral-800/80"></span>
|
||||
Hide Sessions
|
||||
{:else}
|
||||
<span class="fas fa-eye m-1 text-neutral-800/80"></span>
|
||||
All Sessions
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- ── Accessibility controls — always visible ── -->
|
||||
<div class="w-full max-w-full flex flex-row gap-1 items-center justify-center">
|
||||
<!-- Font size cycler: default → larger → smaller → default -->
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
const mode = $ae_loc.font_size_mode;
|
||||
if (!mode || mode === 'default') {
|
||||
$ae_loc.font_size_mode = 'larger';
|
||||
} else if (mode === 'larger') {
|
||||
$ae_loc.font_size_mode = 'smaller';
|
||||
} else {
|
||||
$ae_loc.font_size_mode = 'default';
|
||||
}
|
||||
}}
|
||||
class="
|
||||
btn btn-sm text-xs
|
||||
w-1/2 max-w-1/2
|
||||
preset-tonal-tertiary hover:preset-filled-tertiary-500
|
||||
transition-all group
|
||||
"
|
||||
title="Cycle font size (default → larger → smaller). Current: {$ae_loc.font_size_mode ?? 'default'}"
|
||||
>
|
||||
{#if !$ae_loc.font_size_mode || $ae_loc.font_size_mode === 'default'}
|
||||
<span class="font-bold text-sm font-mono leading-none m-1">A</span>
|
||||
<span class="hidden group-hover:inline-block text-xs">Font: Normal</span>
|
||||
{:else if $ae_loc.font_size_mode === 'larger'}
|
||||
<span class="font-bold text-base font-mono leading-none m-1">A+</span>
|
||||
<span class="hidden group-hover:inline-block text-xs">Font: Larger</span>
|
||||
{:else}
|
||||
<span class="font-bold text-xs font-mono leading-none m-1">A−</span>
|
||||
<span class="hidden group-hover:inline-block text-xs">Font: Smaller</span>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<!-- Light/dark mode toggle. DOM sync handled by +layout.svelte $effect on theme_mode. -->
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$ae_loc.theme_mode = $ae_loc.theme_mode === 'dark' ? 'light' : 'dark';
|
||||
}}
|
||||
class="
|
||||
btn btn-sm text-xs
|
||||
w-1/2 max-w-1/2
|
||||
preset-tonal-tertiary hover:preset-filled-tertiary-500
|
||||
transition-all group
|
||||
"
|
||||
title="Toggle light/dark display mode. Current: {$ae_loc.theme_mode ?? 'light'}"
|
||||
>
|
||||
{#if $ae_loc.theme_mode === 'dark'}
|
||||
<Moon class="m-1 inline-block" size="1em" />
|
||||
<span class="hidden group-hover:inline-block">Dark Mode</span>
|
||||
{:else}
|
||||
<Sun class="m-1 inline-block" size="1em" />
|
||||
<span class="hidden group-hover:inline-block">Light Mode</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,4 +1,31 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* menu_location_list.svelte — Aether Launcher: Room / Location Selector
|
||||
*
|
||||
* PURPOSE:
|
||||
* Provides a dropdown for operators to switch between venue rooms (event_location
|
||||
* objects). Selecting a room triggers a session list reload for that room and
|
||||
* navigates the URL to /launcher/{location_id} so the back button works correctly.
|
||||
*
|
||||
* VISIBILITY:
|
||||
* Rendered only when $ae_loc.edit_mode is true (operator-level access). Regular
|
||||
* attendees and kiosk displays do not see the room selector — they are taken
|
||||
* directly to a URL-encoded room and session without UI controls.
|
||||
*
|
||||
* DATA FLOW:
|
||||
* lq__event_location_obj_li (Dexie liveQuery, passed from launcher_menu.svelte)
|
||||
* → rendered as <select> options
|
||||
* → onchange writes to $events_slct.event_location_id and $events_loc.launcher.slct
|
||||
* → calls handle_load_ae_obj_li__event_session() to fetch sessions for the new room
|
||||
* → navigates to /launcher/{location_id} via goto()
|
||||
*
|
||||
* NOTE — slct_event_location_id binding:
|
||||
* This prop is NOT declared $bindable() but is used with bind:value on the <select>.
|
||||
* This is intentional: the onchange handler writes directly to the canonical stores
|
||||
* ($events_slct.event_location_id) rather than propagating through a prop binding.
|
||||
* The bind:value keeps the select's visual state in sync with the store value passed
|
||||
* in from the parent without requiring a two-way prop binding.
|
||||
*/
|
||||
interface Props {
|
||||
loading__session_li_status?: null | boolean | string;
|
||||
lq__event_location_obj_li: any;
|
||||
|
||||
Reference in New Issue
Block a user