refactor(launcher): standardize helper names and apply batch formatting

- Renamed internal 'preventDefault' to 'prevent_default' in launcher files.
- Fixed native 'event.preventDefault()' call in launcher_file_cont.svelte.
- Applied batch formatting (printWidth: 80) across the (launcher) module.
This commit is contained in:
Scott Idem
2026-02-06 14:48:44 -05:00
parent 7ce5e1f825
commit 969e5610bb
26 changed files with 1536 additions and 604 deletions

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
/** /**
* events/[event_id]/(launcher)/+layout.svelte * events/[event_id]/(launcher)/+layout.svelte
* Root layout for the launcher area. * Root layout for the launcher area.
* Ensures background sync runs globally regardless of active tab. * Ensures background sync runs globally regardless of active tab.
*/ */
import { ae_loc } from '$lib/stores/ae_stores'; import { ae_loc } from '$lib/stores/ae_stores';

View File

@@ -3,6 +3,7 @@
This directory contains the files for the new Event Launcher module (v3). Detailed documentation to follow. This directory contains the files for the new Event Launcher module (v3). Detailed documentation to follow.
This should be able to run in 3 modes: This should be able to run in 3 modes:
* Default - What most users will see for demo purposes
* Onsite - This is what is set onsite, usually just in the Speaker Ready Room - Default - What most users will see for demo purposes
* Native App - This is for the native app version of Aether and used on the MacBook laptops in each of the session rooms - Onsite - This is what is set onsite, usually just in the Speaker Ready Room
- Native App - This is for the native app version of Aether and used on the MacBook laptops in each of the session rooms

View File

@@ -9,66 +9,111 @@
let { on_expand }: Props = $props(); let { on_expand }: Props = $props();
</script> </script>
<Launcher_Cfg_Section <Launcher_Cfg_Section
title="Display & App Modes" title="Display & App Modes"
icon="fa-object-group" icon="fa-object-group"
bind:state={$events_loc.launcher.section_state__app_modes} bind:state={$events_loc.launcher.section_state__app_modes}
{on_expand} {on_expand}
description="Mode: {$events_loc.launcher.app_mode} | UI Layout" description="Mode: {$events_loc.launcher.app_mode} | UI Layout"
> >
<!-- Content omitted for brevity, preserved in file --> <!-- Content omitted for brevity, preserved in file -->
<div class="col-span-full flex flex-col gap-3"> <div class="col-span-full flex flex-col gap-3">
<!-- 1. App Mode Selection --> <!-- 1. App Mode Selection -->
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Operational Environment</label> <label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Operational Environment</label
>
<div class="grid grid-cols-3 gap-1 bg-surface-500/5 p-1 rounded-lg"> <div class="grid grid-cols-3 gap-1 bg-surface-500/5 p-1 rounded-lg">
<button type="button" <button
onclick={() => $events_loc.launcher.app_mode = 'default'} type="button"
onclick={() => ($events_loc.launcher.app_mode = 'default')}
class="btn btn-xs text-[9px] font-bold" class="btn btn-xs text-[9px] font-bold"
class:preset-filled-primary-500={$events_loc.launcher.app_mode === 'default'} class:preset-filled-primary-500={$events_loc.launcher
class:opacity-40={$events_loc.launcher.app_mode !== 'default'} .app_mode === 'default'}
>Web</button> class:opacity-40={$events_loc.launcher.app_mode !==
<button type="button" 'default'}>Web</button
onclick={() => $events_loc.launcher.app_mode = 'native'} >
<button
type="button"
onclick={() => ($events_loc.launcher.app_mode = 'native')}
class="btn btn-xs text-[9px] font-bold" class="btn btn-xs text-[9px] font-bold"
class:preset-filled-primary-500={$events_loc.launcher.app_mode === 'native'} class:preset-filled-primary-500={$events_loc.launcher
class:opacity-40={$events_loc.launcher.app_mode !== 'native'} .app_mode === 'native'}
>App</button> class:opacity-40={$events_loc.launcher.app_mode !==
<button type="button" 'native'}>App</button
onclick={() => $events_loc.launcher.app_mode = 'onsite'} >
<button
type="button"
onclick={() => ($events_loc.launcher.app_mode = 'onsite')}
class="btn btn-xs text-[9px] font-bold" class="btn btn-xs text-[9px] font-bold"
class:preset-filled-primary-500={$events_loc.launcher.app_mode === 'onsite'} class:preset-filled-primary-500={$events_loc.launcher
class:opacity-40={$events_loc.launcher.app_mode !== 'onsite'} .app_mode === 'onsite'}
>Onsite</button> class:opacity-40={$events_loc.launcher.app_mode !==
'onsite'}>Onsite</button
>
</div> </div>
</div> </div>
<!-- 2. UI Layout Toggles --> <!-- 2. UI Layout Toggles -->
<div class="flex flex-col gap-1 border-t border-surface-500/10 pt-2 mt-1"> <div
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Interface Visibility</label> class="flex flex-col gap-1 border-t border-surface-500/10 pt-2 mt-1"
>
<label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Interface Visibility</label
>
<div class="grid grid-cols-2 gap-2 p-1"> <div class="grid grid-cols-2 gap-2 p-1">
<label class="flex items-center gap-2 cursor-pointer group"> <label class="flex items-center gap-2 cursor-pointer group">
<input type="checkbox" bind:checked={$events_loc.launcher.hide__launcher_header} class="checkbox checkbox-sm" /> <input
<span class="text-xs group-hover:text-primary-500">Hide Header</span> type="checkbox"
bind:checked={
$events_loc.launcher.hide__launcher_header
}
class="checkbox checkbox-sm"
/>
<span class="text-xs group-hover:text-primary-500"
>Hide Header</span
>
</label> </label>
<label class="flex items-center gap-2 cursor-pointer group"> <label class="flex items-center gap-2 cursor-pointer group">
<input type="checkbox" bind:checked={$events_loc.launcher.hide__launcher_menu} class="checkbox checkbox-sm" /> <input
<span class="text-xs group-hover:text-primary-500">Hide Menu</span> type="checkbox"
bind:checked={$events_loc.launcher.hide__launcher_menu}
class="checkbox checkbox-sm"
/>
<span class="text-xs group-hover:text-primary-500"
>Hide Menu</span
>
</label> </label>
<label class="flex items-center gap-2 cursor-pointer group"> <label class="flex items-center gap-2 cursor-pointer group">
<input type="checkbox" bind:checked={$events_loc.launcher.hide__launcher_footer} class="checkbox checkbox-sm" /> <input
<span class="text-xs group-hover:text-primary-500">Hide Footer</span> type="checkbox"
bind:checked={
$events_loc.launcher.hide__launcher_footer
}
class="checkbox checkbox-sm"
/>
<span class="text-xs group-hover:text-primary-500"
>Hide Footer</span
>
</label> </label>
<label class="flex items-center gap-2 cursor-pointer group"> <label class="flex items-center gap-2 cursor-pointer group">
<input type="checkbox" bind:checked={$events_loc.launcher.hide__session_datetimes} class="checkbox checkbox-sm" /> <input
<span class="text-xs group-hover:text-primary-500">Hide Times</span> type="checkbox"
bind:checked={
$events_loc.launcher.hide__session_datetimes
}
class="checkbox checkbox-sm"
/>
<span class="text-xs group-hover:text-primary-500"
>Hide Times</span
>
</label> </label>
</div> </div>
</div> </div>
<!-- 3. Time Format Toggle --> <!-- 3. Time Format Toggle -->
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
if ($events_loc.launcher.time_format == 'time_12_short') { if ($events_loc.launcher.time_format == 'time_12_short') {
$events_loc.launcher.time_format = 'time_short'; $events_loc.launcher.time_format = 'time_short';
@@ -81,21 +126,42 @@
class="btn btn-xs preset-tonal-surface w-full text-[10px]" class="btn btn-xs preset-tonal-surface w-full text-[10px]"
> >
<span class="fas fa-clock mr-2 opacity-50"></span> <span class="fas fa-clock mr-2 opacity-50"></span>
Clock Format: <strong>{$events_loc.launcher.time_hours}-hour</strong> Clock Format:
<strong>{$events_loc.launcher.time_hours}-hour</strong>
</button> </button>
<!-- 4. Advanced Toggles (Edit Mode Only) --> <!-- 4. Advanced Toggles (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-2"> <div
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Technical Layout</label> class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-2"
>
<label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Technical Layout</label
>
<div class="grid grid-cols-1 gap-2 p-1"> <div class="grid grid-cols-1 gap-2 p-1">
<label class="flex items-center gap-2 cursor-pointer group"> <label class="flex items-center gap-2 cursor-pointer group">
<input type="checkbox" bind:checked={$events_loc.launcher.hide__ws_element} class="checkbox checkbox-sm" /> <input
<span class="text-xs group-hover:text-primary-500 italic">Hide WebSocket Debugger</span> type="checkbox"
bind:checked={$events_loc.launcher.hide__ws_element}
class="checkbox checkbox-sm"
/>
<span
class="text-xs group-hover:text-primary-500 italic"
>Hide WebSocket Debugger</span
>
</label> </label>
<label class="flex items-center gap-2 cursor-pointer group"> <label class="flex items-center gap-2 cursor-pointer group">
<input type="checkbox" bind:checked={$events_loc.launcher.hide__modal_header_title} class="checkbox checkbox-sm" /> <input
<span class="text-xs group-hover:text-primary-500 italic">Hide Poster Modal Title</span> type="checkbox"
bind:checked={
$events_loc.launcher.hide__modal_header_title
}
class="checkbox checkbox-sm"
/>
<span
class="text-xs group-hover:text-primary-500 italic"
>Hide Poster Modal Title</span
>
</label> </label>
</div> </div>
</div> </div>

View File

@@ -8,35 +8,49 @@
} }
let { on_expand }: Props = $props(); let { on_expand }: Props = $props();
const ws_connected = $derived($events_sess.launcher.ws_connect_status === 'connected'); const ws_connected = $derived(
$events_sess.launcher.ws_connect_status === 'connected'
);
</script> </script>
<Launcher_Cfg_Section <Launcher_Cfg_Section
title="Remote Controller" title="Remote Controller"
icon="fa-gamepad" icon="fa-gamepad"
bind:state={$events_loc.launcher.section_state__controller} bind:state={$events_loc.launcher.section_state__controller}
{on_expand} {on_expand}
description="Mode: {$events_loc.launcher?.controller} | WS: {ws_connected ? 'Connected' : 'Offline'}" description="Mode: {$events_loc.launcher?.controller} | WS: {ws_connected
? 'Connected'
: 'Offline'}"
> >
<!-- Content omitted for brevity, preserved in file --> <!-- Content omitted for brevity, preserved in file -->
<div class="col-span-full flex flex-col gap-3"> <div class="col-span-full flex flex-col gap-3">
<!-- 1. Connection Status Badge --> <!-- 1. Connection Status Badge -->
<div class="flex items-center justify-between bg-surface-500/5 p-2 rounded border border-surface-500/10"> <div
class="flex items-center justify-between bg-surface-500/5 p-2 rounded border border-surface-500/10"
>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="fas fa-plug opacity-50"></span> <span class="fas fa-plug opacity-50"></span>
<span class="text-[10px] font-bold uppercase tracking-wider">WebSocket Link</span> <span class="text-[10px] font-bold uppercase tracking-wider"
>WebSocket Link</span
>
</div> </div>
{#if ws_connected} {#if ws_connected}
<span class="badge variant-filled-success text-[8px] animate-pulse">Connected</span> <span
class="badge variant-filled-success text-[8px] animate-pulse"
>Connected</span
>
{:else} {:else}
<span class="badge variant-filled-error text-[8px]">Disconnected</span> <span class="badge variant-filled-error text-[8px]"
>Disconnected</span
>
{/if} {/if}
</div> </div>
<!-- 2. Controller Mode Selection --> <!-- 2. Controller Mode Selection -->
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Controller Strategy</label> <label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Controller Strategy</label
>
<select <select
bind:value={$events_loc.launcher.controller} bind:value={$events_loc.launcher.controller}
class="select select-sm text-xs preset-tonal-surface h-8" class="select select-sm text-xs preset-tonal-surface h-8"
@@ -49,7 +63,8 @@
<!-- 3. Connection Actions --> <!-- 3. Connection Actions -->
<div class="grid grid-cols-2 gap-2 mt-1"> <div class="grid grid-cols-2 gap-2 mt-1">
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
if ($events_loc.launcher.ws_connect) { if ($events_loc.launcher.ws_connect) {
$events_sess.launcher.trigger__ws_disconnect = true; $events_sess.launcher.trigger__ws_disconnect = true;
@@ -70,7 +85,8 @@
{/if} {/if}
</button> </button>
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
$events_sess.launcher.controller_cmd = 'ae_refresh:now'; $events_sess.launcher.controller_cmd = 'ae_refresh:now';
$events_sess.launcher.controller_trigger_send = 'trigger'; $events_sess.launcher.controller_trigger_send = 'trigger';
@@ -84,25 +100,43 @@
<!-- 4. Technical Config (Edit Mode Only) --> <!-- 4. Technical Config (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-2"> <div
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Channel Configuration</label> class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-2"
>
<label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Channel Configuration</label
>
<div class="flex gap-1"> <div class="flex gap-1">
<input <input
bind:value={$events_loc.launcher.controller_group_code} bind:value={$events_loc.launcher.controller_group_code}
placeholder="Group Code" placeholder="Group Code"
class="input input-sm grow text-[10px] h-7 preset-tonal-surface font-mono" class="input input-sm grow text-[10px] h-7 preset-tonal-surface font-mono"
readonly={!$events_sess.launcher.controller_unlock_group_code} readonly={!$events_sess.launcher
ondblclick={() => $events_sess.launcher.controller_unlock_group_code = true} .controller_unlock_group_code}
ondblclick={() =>
($events_sess.launcher.controller_unlock_group_code = true)}
/> />
<button type="button" <button
onclick={() => $events_sess.launcher.controller_unlock_group_code = !$events_sess.launcher.controller_unlock_group_code} type="button"
onclick={() =>
($events_sess.launcher.controller_unlock_group_code =
!$events_sess.launcher
.controller_unlock_group_code)}
class="btn btn-xs preset-tonal-surface" class="btn btn-xs preset-tonal-surface"
title="Toggle Unlock" title="Toggle Unlock"
> >
<span class="fas {$events_sess.launcher.controller_unlock_group_code ? 'fa-lock-open text-primary-500' : 'fa-lock'}"></span> <span
class="fas {$events_sess.launcher
.controller_unlock_group_code
? 'fa-lock-open text-primary-500'
: 'fa-lock'}"
></span>
</button> </button>
</div> </div>
<p class="text-[8px] opacity-40 italic ml-1">Double-click input to unlock editing. Changing code triggers reconnect.</p> <p class="text-[8px] opacity-40 italic ml-1">
Double-click input to unlock editing. Changing code triggers
reconnect.
</p>
</div> </div>
{/if} {/if}
</div> </div>

View File

@@ -12,12 +12,12 @@
let ram_usage_pct = $derived.by(() => { let ram_usage_pct = $derived.by(() => {
const meta = $ae_loc.native_device?.meta_json; const meta = $ae_loc.native_device?.meta_json;
if (!meta?.total_mem || !meta?.free_mem) return 0; if (!meta?.total_mem || !meta?.free_mem) return 0;
// Parse "16384MB" strings // Parse "16384MB" strings
const total = parseInt(meta.total_mem); const total = parseInt(meta.total_mem);
const free = parseInt(meta.free_mem); const free = parseInt(meta.free_mem);
if (isNaN(total) || isNaN(free)) return 0; if (isNaN(total) || isNaN(free)) return 0;
return Math.round(((total - free) / total) * 100); return Math.round(((total - free) / total) * 100);
}); });
@@ -29,39 +29,61 @@
} }
</script> </script>
<Launcher_Cfg_Section <Launcher_Cfg_Section
title="System & Sync Health" title="System & Sync Health"
icon="fa-heartbeat" icon="fa-heartbeat"
bind:state={$events_loc.launcher.section_state__health} bind:state={$events_loc.launcher.section_state__health}
{on_expand} {on_expand}
description="Heartbeat: {$events_sess.launcher.heartbeat_info.last_timestamp || 'Pending'}" description="Heartbeat: {$events_sess.launcher.heartbeat_info
.last_timestamp || 'Pending'}"
> >
<!-- Content omitted for brevity in instruction, but preserved in file --> <!-- Content omitted for brevity in instruction, but preserved in file -->
<!-- Telemetry Dashboard --> <!-- Telemetry Dashboard -->
<div class="col-span-full flex flex-col gap-3 bg-surface-500/5 p-3 rounded-lg border border-surface-500/10"> <div
class="col-span-full flex flex-col gap-3 bg-surface-500/5 p-3 rounded-lg border border-surface-500/10"
>
<!-- CPU Usage (Mock Logic if load not available yet) --> <!-- CPU Usage (Mock Logic if load not available yet) -->
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<div class="flex justify-between text-[9px] uppercase font-bold opacity-60"> <div
<span>CPU Architecture: {$ae_loc.native_device?.meta_json?.arch || '...'}</span> class="flex justify-between text-[9px] uppercase font-bold opacity-60"
>
<span
>CPU Architecture: {$ae_loc.native_device?.meta_json
?.arch || '...'}</span
>
<span>Load: Healthy</span> <span>Load: Healthy</span>
</div> </div>
<div class="w-full h-1.5 bg-surface-500/20 rounded-full overflow-hidden"> <div
<div class="h-full bg-primary-500 transition-all duration-1000" style="width: 15%"></div> class="w-full h-1.5 bg-surface-500/20 rounded-full overflow-hidden"
>
<div
class="h-full bg-primary-500 transition-all duration-1000"
style="width: 15%"
></div>
</div> </div>
</div> </div>
<!-- RAM Usage --> <!-- RAM Usage -->
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<div class="flex justify-between text-[9px] uppercase font-bold opacity-60"> <div
class="flex justify-between text-[9px] uppercase font-bold opacity-60"
>
<span>Memory (RAM)</span> <span>Memory (RAM)</span>
<span>{ram_usage_pct}% Used</span> <span>{ram_usage_pct}% Used</span>
</div> </div>
<div class="w-full h-1.5 bg-surface-500/20 rounded-full overflow-hidden"> <div
<div class="h-full transition-all duration-1000 {get_usage_color(ram_usage_pct)}" style="width: {ram_usage_pct}%"></div> class="w-full h-1.5 bg-surface-500/20 rounded-full overflow-hidden"
>
<div
class="h-full transition-all duration-1000 {get_usage_color(
ram_usage_pct
)}"
style="width: {ram_usage_pct}%"
></div>
</div> </div>
<div class="text-[8px] opacity-40 text-right italic"> <div class="text-[8px] opacity-40 text-right italic">
Free: {$ae_loc.native_device?.meta_json?.free_mem || '...'} / {$ae_loc.native_device?.meta_json?.total_mem || '...'} Free: {$ae_loc.native_device?.meta_json?.free_mem || '...'} / {$ae_loc
.native_device?.meta_json?.total_mem || '...'}
</div> </div>
</div> </div>
</div> </div>
@@ -69,26 +91,45 @@
<!-- Heartbeat & Sync Info --> <!-- Heartbeat & Sync Info -->
<div class="grid grid-cols-2 gap-x-2 gap-y-2 w-full text-[10px] p-1"> <div class="grid grid-cols-2 gap-x-2 gap-y-2 w-full text-[10px] p-1">
<div class="flex flex-col"> <div class="flex flex-col">
<span class="opacity-50 text-[8px] uppercase font-bold">Last Heartbeat</span> <span class="opacity-50 text-[8px] uppercase font-bold"
<span class="font-mono {$events_sess.launcher.heartbeat_info.status === 'success' ? 'text-success-500' : 'text-error-500'}"> >Last Heartbeat</span
{$events_sess.launcher.heartbeat_info.last_timestamp || 'Pending...'} >
<span
class="font-mono {$events_sess.launcher.heartbeat_info
.status === 'success'
? 'text-success-500'
: 'text-error-500'}"
>
{$events_sess.launcher.heartbeat_info.last_timestamp ||
'Pending...'}
</span> </span>
</div> </div>
<div class="flex flex-col text-right"> <div class="flex flex-col text-right">
<span class="opacity-50 text-[8px] uppercase font-bold">Local File Cache</span> <span class="opacity-50 text-[8px] uppercase font-bold"
>Local File Cache</span
>
<span class="font-mono"> <span class="font-mono">
{$events_sess.launcher.sync_stats.cached} / {$events_sess.launcher.sync_stats.total} {$events_sess.launcher.sync_stats.cached} / {$events_sess
.launcher.sync_stats.total}
</span> </span>
</div> </div>
{#if $events_sess.launcher.sync_stats.currently_syncing} {#if $events_sess.launcher.sync_stats.currently_syncing}
<div class="col-span-full bg-primary-500/10 p-2 rounded border border-primary-500/20 animate-pulse mt-1"> <div
class="col-span-full bg-primary-500/10 p-2 rounded border border-primary-500/20 animate-pulse mt-1"
>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="fas fa-sync fa-spin text-primary-500"></span> <span class="fas fa-sync fa-spin text-primary-500"></span>
<div class="flex flex-col truncate"> <div class="flex flex-col truncate">
<span class="text-[8px] uppercase font-bold text-primary-500">Syncing File...</span> <span
<span class="truncate italic opacity-80">{$events_sess.launcher.sync_stats.currently_syncing}</span> class="text-[8px] uppercase font-bold text-primary-500"
>Syncing File...</span
>
<span class="truncate italic opacity-80"
>{$events_sess.launcher.sync_stats
.currently_syncing}</span
>
</div> </div>
</div> </div>
</div> </div>
@@ -97,18 +138,27 @@
<!-- Device Metadata (Edit Mode Only) --> <!-- Device Metadata (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="col-span-full mt-1 pt-2 border-t border-surface-500/10 flex flex-col gap-1 text-[9px] opacity-60 px-1"> <div
class="col-span-full mt-1 pt-2 border-t border-surface-500/10 flex flex-col gap-1 text-[9px] opacity-60 px-1"
>
<div class="flex justify-between"> <div class="flex justify-between">
<span>Hostname:</span> <span>Hostname:</span>
<span class="font-mono">{$ae_loc.native_device.info_hostname || '...'}</span> <span class="font-mono"
>{$ae_loc.native_device.info_hostname || '...'}</span
>
</div> </div>
<div class="flex justify-between gap-4"> <div class="flex justify-between gap-4">
<span>IP Addresses:</span> <span>IP Addresses:</span>
<span class="font-mono truncate">{$ae_loc.native_device.info_ip_list || '...'}</span> <span class="font-mono truncate"
>{$ae_loc.native_device.info_ip_list || '...'}</span
>
</div> </div>
<div class="mt-2 opacity-40"> <div class="mt-2 opacity-40">
<label class="text-[8px] uppercase font-bold">Raw Device JSON</label> <label class="text-[8px] uppercase font-bold"
<pre class="text-[7px] max-h-32 overflow-y-auto bg-black/20 p-1 rounded mt-1"> >Raw Device JSON</label
>
<pre
class="text-[7px] max-h-32 overflow-y-auto bg-black/20 p-1 rounded mt-1">
{JSON.stringify($ae_loc.native_device, null, 2)} {JSON.stringify($ae_loc.native_device, null, 2)}
</pre> </pre>
</div> </div>

View File

@@ -12,21 +12,33 @@
function handle_reset_action(val: string) { function handle_reset_action(val: string) {
if (!val) return; if (!val) return;
if (val == 'delete_idbs') { if (val == 'delete_idbs') {
if (confirm('Are you sure you want to delete ALL IndexedDB databases?')) { if (
confirm(
'Are you sure you want to delete ALL IndexedDB databases?'
)
) {
indexedDB.deleteDatabase('ae_archives_db'); indexedDB.deleteDatabase('ae_archives_db');
indexedDB.deleteDatabase('ae_core_db'); indexedDB.deleteDatabase('ae_core_db');
indexedDB.deleteDatabase('ae_events_db'); indexedDB.deleteDatabase('ae_events_db');
indexedDB.deleteDatabase('ae_journals_db'); indexedDB.deleteDatabase('ae_journals_db');
indexedDB.deleteDatabase('ae_posts_db'); indexedDB.deleteDatabase('ae_posts_db');
indexedDB.deleteDatabase('ae_sponsorships_db'); indexedDB.deleteDatabase('ae_sponsorships_db');
alert('All IndexedDB databases deleted. Please reload the app.'); alert(
'All IndexedDB databases deleted. Please reload the app.'
);
} }
} else if (val == 'delete_idbs_events') { } else if (val == 'delete_idbs_events') {
if (confirm('Are you sure you want to delete ONLY the Events IndexedDB database?')) { if (
confirm(
'Are you sure you want to delete ONLY the Events IndexedDB database?'
)
) {
indexedDB.deleteDatabase('ae_events_db'); indexedDB.deleteDatabase('ae_events_db');
alert('Events IndexedDB database deleted. Please reload the app.'); alert(
'Events IndexedDB database deleted. Please reload the app.'
);
} }
} else if (val == 'delete_local') { } else if (val == 'delete_local') {
if (confirm('Are you sure you want to delete ALL local config?')) { if (confirm('Are you sure you want to delete ALL local config?')) {
@@ -37,7 +49,11 @@
location.reload(); location.reload();
} }
} else if (val == 'delete_local_events') { } else if (val == 'delete_local_events') {
if (confirm('Are you sure you want to delete ONLY the Events local config?')) { if (
confirm(
'Are you sure you want to delete ONLY the Events local config?'
)
) {
localStorage.removeItem('ae_events_loc'); localStorage.removeItem('ae_events_loc');
location.reload(); location.reload();
} }
@@ -46,28 +62,34 @@
} }
</script> </script>
<Launcher_Cfg_Section <Launcher_Cfg_Section
title="Local Reset & Actions" title="Local Reset & Actions"
icon="fa-tools" icon="fa-tools"
bind:state={$events_loc.launcher.section_state__local_actions} bind:state={$events_loc.launcher.section_state__local_actions}
{on_expand} {on_expand}
description="Cache wiping and global menu toggles" description="Cache wiping and global menu toggles"
> >
<div class="col-span-full flex flex-col gap-3"> <div class="col-span-full flex flex-col gap-3">
<!-- 1. Reset Actions --> <!-- 1. Reset Actions -->
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1 text-error-500">Maintenance & Resets</label> <label
class="text-[9px] font-bold uppercase opacity-50 ml-1 text-error-500"
>Maintenance & Resets</label
>
<select <select
bind:value={selected_reset} bind:value={selected_reset}
onchange={(e) => handle_reset_action((e.target as HTMLSelectElement).value)} onchange={(e) =>
handle_reset_action((e.target as HTMLSelectElement).value)}
class="select select-sm text-xs preset-tonal-surface h-8 text-error-500 border-error-500/20" class="select select-sm text-xs preset-tonal-surface h-8 text-error-500 border-error-500/20"
> >
<option value="">-- Select a reset action --</option> <option value="">-- Select a reset action --</option>
<option value="delete_idbs">Delete ALL Databases</option> <option value="delete_idbs">Delete ALL Databases</option>
<option value="delete_idbs_events">Delete Events DB Only</option> <option value="delete_idbs_events">Delete Events DB Only</option
>
<option value="delete_local">Wipe ALL Local Storage</option> <option value="delete_local">Wipe ALL Local Storage</option>
<option value="delete_local_events">Wipe Events Storage Only</option> <option value="delete_local_events"
>Wipe Events Storage Only</option
>
</select> </select>
<span class="text-[8px] opacity-40 italic ml-1 leading-tight"> <span class="text-[8px] opacity-40 italic ml-1 leading-tight">
* Destructive actions require browser confirmation. * Destructive actions require browser confirmation.
@@ -76,31 +98,48 @@
<!-- 2. UI Toggles --> <!-- 2. UI Toggles -->
<div class="grid grid-cols-2 gap-2 mt-1"> <div class="grid grid-cols-2 gap-2 mt-1">
<button type="button" <button
type="button"
onclick={() => ($ae_loc.sys_menu.hide = !$ae_loc.sys_menu.hide)} onclick={() => ($ae_loc.sys_menu.hide = !$ae_loc.sys_menu.hide)}
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500" class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
title="Show/Hide Aether global system menu" title="Show/Hide Aether global system menu"
> >
<span class="fas {$ae_loc.sys_menu.hide ? 'fa-eye' : 'fa-eye-slash'} mr-2"></span> <span
class="fas {$ae_loc.sys_menu.hide
? 'fa-eye'
: 'fa-eye-slash'} mr-2"
></span>
{$ae_loc.sys_menu.hide ? 'Show' : 'Hide'} Sys Menu {$ae_loc.sys_menu.hide ? 'Show' : 'Hide'} Sys Menu
</button> </button>
<button type="button" <button
onclick={() => ($ae_loc.debug_menu.hide = !$ae_loc.debug_menu.hide)} type="button"
onclick={() =>
($ae_loc.debug_menu.hide = !$ae_loc.debug_menu.hide)}
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500" class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
title="Show/Hide Aether global debug menu" title="Show/Hide Aether global debug menu"
> >
<span class="fas {$ae_loc.debug_menu.hide ? 'fa-bug' : 'fa-bug-slash'} mr-2"></span> <span
class="fas {$ae_loc.debug_menu.hide
? 'fa-bug'
: 'fa-bug-slash'} mr-2"
></span>
{$ae_loc.debug_menu.hide ? 'Show' : 'Hide'} Debug {$ae_loc.debug_menu.hide ? 'Show' : 'Hide'} Debug
</button> </button>
</div> </div>
<!-- 3. Connection Summary (Edit Mode Only) --> <!-- 3. Connection Summary (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="col-span-full border-t border-surface-500/20 pt-2 mt-1 flex flex-col gap-1"> <div
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">API Context</label> class="col-span-full border-t border-surface-500/20 pt-2 mt-1 flex flex-col gap-1"
<div class="bg-black/10 p-2 rounded text-[9px] font-mono opacity-60 break-all leading-tight"> >
Endpoint: {$ae_api.base_url}<br/> <label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>API Context</label
>
<div
class="bg-black/10 p-2 rounded text-[9px] font-mono opacity-60 break-all leading-tight"
>
Endpoint: {$ae_api.base_url}<br />
Account: {$ae_loc.account_id} Account: {$ae_loc.account_id}
</div> </div>
</div> </div>

View File

@@ -14,15 +14,20 @@
let remote_status = $state(''); let remote_status = $state('');
let system_status = $state(''); let system_status = $state('');
async function handle_remote_control(action: 'next' | 'prev' | 'start' | 'stop') { async function handle_remote_control(
action: 'next' | 'prev' | 'start' | 'stop'
) {
remote_status = `Sending ${action}...`; remote_status = `Sending ${action}...`;
const res = await native.control_presentation({ app: remote_app, action }); const res = await native.control_presentation({
app: remote_app,
action
});
if (res.success) { if (res.success) {
remote_status = `Success: ${action}`; remote_status = `Success: ${action}`;
} else { } else {
remote_status = `Error: ${res.error}`; remote_status = `Error: ${res.error}`;
} }
setTimeout(() => remote_status = '', 3000); setTimeout(() => (remote_status = ''), 3000);
} }
async function handle_system_action(promise: Promise<any>, label: string) { async function handle_system_action(promise: Promise<any>, label: string) {
@@ -33,51 +38,67 @@
} else { } else {
system_status = `Error: ${res.error || 'Unknown error'}`; system_status = `Error: ${res.error || 'Unknown error'}`;
} }
setTimeout(() => system_status = '', 3000); setTimeout(() => (system_status = ''), 3000);
} }
// Modal state for dangerous actions // Modal state for dangerous actions
let show_power_confirm = $state<{ action: string, label: string } | null>(null); let show_power_confirm = $state<{ action: string; label: string } | null>(
null
);
</script> </script>
<Launcher_Cfg_Section <Launcher_Cfg_Section
title="Native OS Management" title="Native OS Management"
icon="fa-laptop-code" icon="fa-laptop-code"
bind:state={$events_loc.launcher.section_state__native_os} bind:state={$events_loc.launcher.section_state__native_os}
{on_expand} {on_expand}
description="OS: {$ae_loc.native_device?.meta_json?.platform || '...'} | Kiosk & Apps" description="OS: {$ae_loc.native_device?.meta_json?.platform ||
'...'} | Kiosk & Apps"
> >
<!-- Content omitted for brevity, preserved in file --> <!-- Content omitted for brevity, preserved in file -->
{#if system_status} {#if system_status}
<div class="col-span-full text-[10px] text-center italic bg-surface-500/10 py-1 rounded animate-pulse text-primary-500 border border-primary-500/20"> <div
class="col-span-full text-[10px] text-center italic bg-surface-500/10 py-1 rounded animate-pulse text-primary-500 border border-primary-500/20"
>
{system_status} {system_status}
</div> </div>
{/if} {/if}
<!-- 1. Window & Folders (Common) --> <!-- 1. Window & Folders (Common) -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Folders & View</label> <label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Folders & View</label
>
<div class="grid grid-cols-2 gap-1"> <div class="grid grid-cols-2 gap-1">
<button type="button" <button
onclick={() => native.open_folder($ae_loc.local_file_cache_path)} type="button"
onclick={() =>
native.open_folder($ae_loc.local_file_cache_path)}
class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start" class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start"
> >
<span class="fas fa-folder-open mr-2 w-3"></span> Cache <span class="fas fa-folder-open mr-2 w-3"></span> Cache
</button> </button>
<button type="button" <button
type="button"
onclick={() => native.open_folder($ae_loc.host_file_temp_path)} onclick={() => native.open_folder($ae_loc.host_file_temp_path)}
class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start" class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start"
> >
<span class="fas fa-folder-open mr-2 w-3"></span> Temp <span class="fas fa-folder-open mr-2 w-3"></span> Temp
</button> </button>
<button type="button" <button
type="button"
onclick={() => native.window_control({ action: 'maximize' })} onclick={() => native.window_control({ action: 'maximize' })}
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500" class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
> >
<span class="fas fa-expand mr-1"></span> Maximize <span class="fas fa-expand mr-1"></span> Maximize
</button> </button>
<button type="button" <button
onclick={() => handle_system_action(native.window_control({ action: 'kiosk', value: true }), 'Kiosk Mode')} type="button"
onclick={() =>
handle_system_action(
native.window_control({ action: 'kiosk', value: true }),
'Kiosk Mode'
)}
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500" class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
> >
<span class="fas fa-desktop mr-1"></span> Kiosk <span class="fas fa-desktop mr-1"></span> Kiosk
@@ -88,47 +109,94 @@
<!-- 2. Presentation Remote Control (Common) --> <!-- 2. Presentation Remote Control (Common) -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex flex-row justify-between items-center px-1"> <div class="flex flex-row justify-between items-center px-1">
<label class="text-[9px] font-bold uppercase opacity-50">Remote Control</label> <label class="text-[9px] font-bold uppercase opacity-50"
<select bind:value={remote_app} class="select select-sm py-0 h-5 text-[9px] w-24 preset-tonal-surface"> >Remote Control</label
>
<select
bind:value={remote_app}
class="select select-sm py-0 h-5 text-[9px] w-24 preset-tonal-surface"
>
<option value="powerpoint">PowerPoint</option> <option value="powerpoint">PowerPoint</option>
<option value="keynote">Keynote</option> <option value="keynote">Keynote</option>
</select> </select>
</div> </div>
<div class="grid grid-cols-4 gap-1"> <div class="grid grid-cols-4 gap-1">
<button type="button" onclick={() => handle_remote_control('prev')} class="btn btn-sm preset-tonal-secondary" title="Previous Slide"> <button
type="button"
onclick={() => handle_remote_control('prev')}
class="btn btn-sm preset-tonal-secondary"
title="Previous Slide"
>
<span class="fas fa-step-backward"></span> <span class="fas fa-step-backward"></span>
</button> </button>
<button type="button" onclick={() => handle_remote_control('start')} class="btn btn-sm preset-tonal-success" title="Start/Resume Slideshow"> <button
type="button"
onclick={() => handle_remote_control('start')}
class="btn btn-sm preset-tonal-success"
title="Start/Resume Slideshow"
>
<span class="fas fa-play"></span> <span class="fas fa-play"></span>
</button> </button>
<button type="button" onclick={() => handle_remote_control('stop')} class="btn btn-sm preset-tonal-error" title="Stop Slideshow"> <button
type="button"
onclick={() => handle_remote_control('stop')}
class="btn btn-sm preset-tonal-error"
title="Stop Slideshow"
>
<span class="fas fa-stop"></span> <span class="fas fa-stop"></span>
</button> </button>
<button type="button" onclick={() => handle_remote_control('next')} class="btn btn-sm preset-tonal-secondary" title="Next Slide"> <button
type="button"
onclick={() => handle_remote_control('next')}
class="btn btn-sm preset-tonal-secondary"
title="Next Slide"
>
<span class="fas fa-step-forward"></span> <span class="fas fa-step-forward"></span>
</button> </button>
</div> </div>
{#if remote_status} {#if remote_status}
<div class="text-[9px] text-center italic animate-pulse text-primary-500">{remote_status}</div> <div
class="text-[9px] text-center italic animate-pulse text-primary-500"
>
{remote_status}
</div>
{/if} {/if}
</div> </div>
<!-- 3. Technical Management (Edit Mode Only) --> <!-- 3. Technical Management (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-3"> <div
class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-3"
>
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50">System Actions</label> <label class="text-[9px] font-bold uppercase opacity-50"
>System Actions</label
>
<div class="grid grid-cols-1 gap-1"> <div class="grid grid-cols-1 gap-1">
<button type="button" <button
onclick={() => handle_system_action(native.set_display_layout({ mode: 'extend' }), 'Extend Display')} type="button"
onclick={() =>
handle_system_action(
native.set_display_layout({
mode: 'extend'
}),
'Extend Display'
)}
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500 justify-start" class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500 justify-start"
> >
<span class="fas fa-columns mr-2 w-3"></span> Extend Mode <span class="fas fa-columns mr-2 w-3"></span> Extend Mode
</button> </button>
<button type="button" <button
onclick={() => handle_system_action(native.set_wallpaper({ path: $ae_loc.site_header_image_path }), 'Wallpaper')} type="button"
onclick={() =>
handle_system_action(
native.set_wallpaper({
path: $ae_loc.site_header_image_path
}),
'Wallpaper'
)}
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500 justify-start" class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500 justify-start"
disabled={!$ae_loc.site_header_image_path} disabled={!$ae_loc.site_header_image_path}
> >
@@ -137,16 +205,29 @@
</div> </div>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50 text-error-500">Power</label> <label
class="text-[9px] font-bold uppercase opacity-50 text-error-500"
>Power</label
>
<div class="grid grid-cols-1 gap-1"> <div class="grid grid-cols-1 gap-1">
<button type="button" <button
onclick={() => show_power_confirm = { action: 'reboot', label: 'Reboot Laptop' }} type="button"
onclick={() =>
(show_power_confirm = {
action: 'reboot',
label: 'Reboot Laptop'
})}
class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 justify-start" class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 justify-start"
> >
<span class="fas fa-sync-alt mr-2 w-3"></span> Reboot <span class="fas fa-sync-alt mr-2 w-3"></span> Reboot
</button> </button>
<button type="button" <button
onclick={() => show_power_confirm = { action: 'shutdown', label: 'Shutdown Laptop' }} type="button"
onclick={() =>
(show_power_confirm = {
action: 'shutdown',
label: 'Shutdown Laptop'
})}
class="btn btn-xs preset-tonal-error hover:preset-filled-error-500 justify-start" class="btn btn-xs preset-tonal-error hover:preset-filled-error-500 justify-start"
> >
<span class="fas fa-power-off mr-2 w-3"></span> Shutdown <span class="fas fa-power-off mr-2 w-3"></span> Shutdown
@@ -156,25 +237,36 @@
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Terminal Access</label> <label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Terminal Access</label
>
<div class="flex gap-1"> <div class="flex gap-1">
<input <input
type="text" type="text"
bind:value={$events_sess.launcher.manual_cmd} bind:value={$events_sess.launcher.manual_cmd}
placeholder="ls -la" placeholder="ls -la"
class="input input-sm grow text-[10px] preset-tonal-surface h-7" class="input input-sm grow text-[10px] preset-tonal-surface h-7"
/> />
<button type="button" <button
type="button"
onclick={async () => { onclick={async () => {
test_cmd_result = 'Running...'; test_cmd_result = 'Running...';
const res = await native.run_cmd({ cmd: $events_sess.launcher.manual_cmd || 'uptime' }); const res = await native.run_cmd({
test_cmd_result = (res as any).stdout || (res as any).error || 'No Output'; cmd:
$events_sess.launcher.manual_cmd || 'uptime'
});
test_cmd_result =
(res as any).stdout ||
(res as any).error ||
'No Output';
}} }}
class="btn btn-sm preset-filled-secondary hover:preset-filled-primary-500 text-[10px] h-7" class="btn btn-sm preset-filled-secondary hover:preset-filled-primary-500 text-[10px] h-7"
>Run</button> >Run</button
>
</div> </div>
{#if test_cmd_result} {#if test_cmd_result}
<pre class="text-[8px] bg-black text-green-500 p-2 mt-1 overflow-x-auto rounded border border-surface-500/50 max-h-24 shadow-inner">{test_cmd_result}</pre> <pre
class="text-[8px] bg-black text-green-500 p-2 mt-1 overflow-x-auto rounded border border-surface-500/50 max-h-24 shadow-inner">{test_cmd_result}</pre>
{/if} {/if}
</div> </div>
</div> </div>
@@ -183,20 +275,37 @@
<!-- Power Confirmation Modal --> <!-- Power Confirmation Modal -->
{#if show_power_confirm} {#if show_power_confirm}
<div class="fixed inset-0 z-[1000] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"> <div
<div class="card p-6 w-full max-w-sm preset-filled-surface-100-900 border border-error-500 shadow-2xl animate-in zoom-in-95 duration-200"> class="fixed inset-0 z-[1000] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
<h4 class="h4 text-error-500 font-bold mb-2">Confirm System Action</h4> >
<div
class="card p-6 w-full max-w-sm preset-filled-surface-100-900 border border-error-500 shadow-2xl animate-in zoom-in-95 duration-200"
>
<h4 class="h4 text-error-500 font-bold mb-2">
Confirm System Action
</h4>
<p class="text-sm opacity-80 mb-6"> <p class="text-sm opacity-80 mb-6">
Are you sure you want to <strong>{show_power_confirm.action}</strong> this host machine? Are you sure you want to <strong
>{show_power_confirm.action}</strong
> this host machine?
</p> </p>
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">
<button type="button" onclick={() => show_power_confirm = null} class="btn btn-sm preset-tonal-surface">Cancel</button> <button
<button type="button" type="button"
onclick={() => (show_power_confirm = null)}
class="btn btn-sm preset-tonal-surface">Cancel</button
>
<button
type="button"
onclick={() => { onclick={() => {
const action = show_power_confirm?.action; const action = show_power_confirm?.action;
show_power_confirm = null; show_power_confirm = null;
if (action) handle_system_action(native.power_control({ action: action as any }), action); if (action)
}} handle_system_action(
native.power_control({ action: action as any }),
action
);
}}
class="btn btn-sm preset-filled-error" class="btn btn-sm preset-filled-error"
> >
Confirm {show_power_confirm.action} Confirm {show_power_confirm.action}

View File

@@ -9,59 +9,71 @@
let { on_expand }: Props = $props(); let { on_expand }: Props = $props();
</script> </script>
<Launcher_Cfg_Section <Launcher_Cfg_Section
title="Digital Screen Saver" title="Digital Screen Saver"
icon="fa-id-badge" icon="fa-id-badge"
bind:state={$events_loc.launcher.section_state__screen_saver} bind:state={$events_loc.launcher.section_state__screen_saver}
{on_expand} {on_expand}
description="Idle: {($events_loc.launcher.idle_timer / 60000).toFixed(1)}m | Auto-Posters" description="Idle: {($events_loc.launcher.idle_timer / 60000).toFixed(
1
)}m | Auto-Posters"
> >
<!-- Content omitted for brevity, preserved in file --> <!-- Content omitted for brevity, preserved in file -->
<div class="col-span-full flex flex-col gap-3"> <div class="col-span-full flex flex-col gap-3">
<!-- 1. Technical Timers (Edit Mode Only) --> <!-- 1. Technical Timers (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Screen Saver Timers (ms)</label> <label class="text-[9px] font-bold uppercase opacity-50 ml-1"
<div class="grid grid-cols-1 gap-2 bg-surface-500/5 p-2 rounded border border-surface-500/10"> >Screen Saver Timers (ms)</label
>
<div
class="grid grid-cols-1 gap-2 bg-surface-500/5 p-2 rounded border border-surface-500/10"
>
<div class="flex justify-between items-center gap-4"> <div class="flex justify-between items-center gap-4">
<span class="text-[10px] opacity-60">Idle Wait</span> <span class="text-[10px] opacity-60">Idle Wait</span>
<input <input
type="number" type="number"
min={3000} min={3000}
bind:value={$events_loc.launcher.idle_timer} bind:value={$events_loc.launcher.idle_timer}
class="input input-sm text-[10px] h-7 w-24 text-right preset-tonal-surface" class="input input-sm text-[10px] h-7 w-24 text-right preset-tonal-surface"
/> />
</div> </div>
<div class="flex justify-between items-center gap-4"> <div class="flex justify-between items-center gap-4">
<span class="text-[10px] opacity-60">Cycle Check</span> <span class="text-[10px] opacity-60">Cycle Check</span>
<input <input
type="number" type="number"
min={500} min={500}
bind:value={$events_loc.launcher.idle_cycle} bind:value={$events_loc.launcher.idle_cycle}
class="input input-sm text-[10px] h-7 w-24 text-right preset-tonal-surface" class="input input-sm text-[10px] h-7 w-24 text-right preset-tonal-surface"
/> />
</div> </div>
<div class="flex justify-between items-center gap-4"> <div class="flex justify-between items-center gap-4">
<span class="text-[10px] opacity-60">Image Rotation</span> <span class="text-[10px] opacity-60"
<input >Image Rotation</span
type="number" >
<input
type="number"
min={750} min={750}
bind:value={$events_loc.launcher.idle_loop_period} bind:value={$events_loc.launcher.idle_loop_period}
class="input input-sm text-[10px] h-7 w-24 text-right preset-tonal-surface" class="input input-sm text-[10px] h-7 w-24 text-right preset-tonal-surface"
/> />
</div> </div>
</div> </div>
</div> </div>
{:else} {:else}
<!-- 2. Read Only Summary (Normal Mode) --> <!-- 2. Read Only Summary (Normal Mode) -->
<div class="bg-surface-500/5 p-3 rounded-lg border border-surface-500/10 flex flex-col gap-2"> <div
class="bg-surface-500/5 p-3 rounded-lg border border-surface-500/10 flex flex-col gap-2"
>
<div class="flex justify-between items-center text-xs"> <div class="flex justify-between items-center text-xs">
<span class="opacity-60">Active Idle Timeout:</span> <span class="opacity-60">Active Idle Timeout:</span>
<span class="font-bold text-primary-500">{($events_loc.launcher.idle_timer / 60000).toFixed(1)} minutes</span> <span class="font-bold text-primary-500"
>{($events_loc.launcher.idle_timer / 60000).toFixed(1)} minutes</span
>
</div> </div>
<p class="text-[9px] opacity-40 italic"> <p class="text-[9px] opacity-40 italic">
The screen saver automatically rotates digital posters when no activity is detected for the specified time. The screen saver automatically rotates digital posters when
no activity is detected for the specified time.
</p> </p>
</div> </div>
{/if} {/if}

View File

@@ -13,7 +13,15 @@
on_toggle?: (new_state: 'collapsed' | 'auto' | 'pinned') => void; on_toggle?: (new_state: 'collapsed' | 'auto' | 'pinned') => void;
} }
let { title, icon, state = $bindable(), description, children, on_expand, on_toggle }: Props = $props(); let {
title,
icon,
state = $bindable(),
description,
children,
on_expand,
on_toggle
}: Props = $props();
function toggle_expand() { function toggle_expand() {
if (state === 'collapsed') { if (state === 'collapsed') {
@@ -40,40 +48,66 @@
</script> </script>
<section <section
class="w-full transition-all duration-300 border rounded-lg overflow-hidden mb-2 { !is_open ? 'preset-outlined-surface-300-700' : '' } { state === 'auto' ? 'preset-outlined-primary-500 shadow-xl' : '' } { state === 'pinned' ? 'preset-outlined-warning-500 shadow-xl' : '' }" class="w-full transition-all duration-300 border rounded-lg overflow-hidden mb-2 {!is_open
? 'preset-outlined-surface-300-700'
: ''} {state === 'auto'
? 'preset-outlined-primary-500 shadow-xl'
: ''} {state === 'pinned'
? 'preset-outlined-warning-500 shadow-xl'
: ''}"
> >
<!-- Header --> <!-- Header -->
<!-- svelte-ignore a11y_click_events_have_key_events --> <!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions --> <!-- svelte-ignore a11y_no_static_element_interactions -->
<header <header
class="flex flex-row items-center justify-between p-2 cursor-pointer transition-colors { !is_open ? 'bg-surface-500/5' : '' } { state === 'auto' ? 'bg-primary-500/10' : '' } { state === 'pinned' ? 'bg-warning-500/10' : '' }" class="flex flex-row items-center justify-between p-2 cursor-pointer transition-colors {!is_open
? 'bg-surface-500/5'
: ''} {state === 'auto' ? 'bg-primary-500/10' : ''} {state ===
'pinned'
? 'bg-warning-500/10'
: ''}"
onclick={toggle_expand} onclick={toggle_expand}
> >
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span class="fas {icon} w-5 text-center opacity-70 { state === 'auto' ? 'text-primary-500' : '' } { state === 'pinned' ? 'text-warning-500' : '' }"></span> <span
class="fas {icon} w-5 text-center opacity-70 {state === 'auto'
? 'text-primary-500'
: ''} {state === 'pinned' ? 'text-warning-500' : ''}"
></span>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-sm font-bold tracking-tight uppercase { !is_open ? 'opacity-50' : '' }">{title}</span> <span
class="text-sm font-bold tracking-tight uppercase {!is_open
? 'opacity-50'
: ''}">{title}</span
>
{#if description && !is_open} {#if description && !is_open}
<span class="text-[9px] opacity-40 italic truncate max-w-[180px]">{description}</span> <span
class="text-[9px] opacity-40 italic truncate max-w-[180px]"
>{description}</span
>
{/if} {/if}
</div> </div>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<!-- Pin Toggle --> <!-- Pin Toggle -->
<button type="button" <button
type="button"
onclick={toggle_pin} onclick={toggle_pin}
class="btn btn-icon btn-xs transition-all hover:scale-110" class="btn btn-icon btn-xs transition-all hover:scale-110"
class:opacity-20={state !== 'pinned'} class:opacity-20={state !== 'pinned'}
class:text-warning-500={state === 'pinned'} class:text-warning-500={state === 'pinned'}
title={state === 'pinned' ? 'Unpin Section' : 'Pin Section (Stay open)'} title={state === 'pinned'
? 'Unpin Section'
: 'Pin Section (Stay open)'}
> >
<span class="fas fa-thumbtack text-[10px]"></span> <span class="fas fa-thumbtack text-[10px]"></span>
</button> </button>
<!-- Collapse Icon --> <!-- Collapse Icon -->
<span class="fas transition-transform duration-300 opacity-30" <span
class:fa-chevron-right={!is_open} class="fas transition-transform duration-300 opacity-30"
class:fa-chevron-right={!is_open}
class:fa-chevron-down={is_open} class:fa-chevron-down={is_open}
class:rotate-180={is_open} class:rotate-180={is_open}
></span> ></span>
@@ -82,19 +116,29 @@
<!-- Content --> <!-- Content -->
{#if is_open} {#if is_open}
<div transition:slide={{ duration: 300 }} class="p-3 bg-white/5 dark:bg-black/5"> <div
transition:slide={{ duration: 300 }}
class="p-3 bg-white/5 dark:bg-black/5"
>
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="mb-2 flex justify-between items-center px-1"> <div class="mb-2 flex justify-between items-center px-1">
<span class="text-[8px] uppercase font-bold tracking-widest text-primary-500/60 flex items-center gap-1"> <span
class="text-[8px] uppercase font-bold tracking-widest text-primary-500/60 flex items-center gap-1"
>
<span class="fas fa-edit"></span> Technical Mode <span class="fas fa-edit"></span> Technical Mode
</span> </span>
{#if state === 'pinned'} {#if state === 'pinned'}
<span class="badge variant-filled-warning text-[8px] uppercase">Pinned</span> <span
class="badge variant-filled-warning text-[8px] uppercase"
>Pinned</span
>
{/if} {/if}
</div> </div>
{/if} {/if}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-1 gap-4 lg:gap-3"> <div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-1 gap-4 lg:gap-3"
>
{@render children?.()} {@render children?.()}
</div> </div>
</div> </div>

View File

@@ -9,63 +9,92 @@
let { on_expand }: Props = $props(); let { on_expand }: Props = $props();
</script> </script>
<Launcher_Cfg_Section <Launcher_Cfg_Section
title="Sync Engine & Timers" title="Sync Engine & Timers"
icon="fa-sync" icon="fa-sync"
bind:state={$events_loc.launcher.section_state__sync_timers} bind:state={$events_loc.launcher.section_state__sync_timers}
{on_expand} {on_expand}
description="Prefix: {$ae_loc.native_device?.hash_prefix_length || 2} | Loops: Active" description="Prefix: {$ae_loc.native_device?.hash_prefix_length ||
2} | Loops: Active"
> >
<!-- Content omitted for brevity, preserved in file --> <!-- Content omitted for brevity, preserved in file -->
{#if $ae_loc.native_device} {#if $ae_loc.native_device}
<div class="grid grid-cols-1 gap-3"> <div class="grid grid-cols-1 gap-3">
<!-- Technical Timers (Edit Mode Only) --> <!-- Technical Timers (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Polling Periods (ms)</label> <label
class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Polling Periods (ms)</label
>
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<span class="text-[8px] opacity-60">Event Data</span> <span class="text-[8px] opacity-60">Event Data</span
<input >
type="number" <input
bind:value={$ae_loc.native_device.check_event_loop_period} type="number"
class="input input-sm text-[10px] h-7 preset-tonal-surface" bind:value={
$ae_loc.native_device
.check_event_loop_period
}
class="input input-sm text-[10px] h-7 preset-tonal-surface"
/> />
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<span class="text-[8px] opacity-60">Device Config</span> <span class="text-[8px] opacity-60"
<input >Device Config</span
type="number" >
bind:value={$ae_loc.native_device.check_event_device_loop_period} <input
class="input input-sm text-[10px] h-7 preset-tonal-surface" type="number"
bind:value={
$ae_loc.native_device
.check_event_device_loop_period
}
class="input input-sm text-[10px] h-7 preset-tonal-surface"
/> />
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<span class="text-[8px] opacity-60">Room/Location</span> <span class="text-[8px] opacity-60"
<input >Room/Location</span
type="number" >
bind:value={$ae_loc.native_device.check_event_location_loop_period} <input
class="input input-sm text-[10px] h-7 preset-tonal-surface" type="number"
bind:value={
$ae_loc.native_device
.check_event_location_loop_period
}
class="input input-sm text-[10px] h-7 preset-tonal-surface"
/> />
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<span class="text-[8px] opacity-60">Session Loop</span> <span class="text-[8px] opacity-60"
<input >Session Loop</span
type="number" >
bind:value={$ae_loc.native_device.check_event_session_loop_period} <input
class="input input-sm text-[10px] h-7 preset-tonal-surface" type="number"
bind:value={
$ae_loc.native_device
.check_event_session_loop_period
}
class="input input-sm text-[10px] h-7 preset-tonal-surface"
/> />
</div> </div>
</div> </div>
</div> </div>
<div class="flex flex-col gap-1 mt-1 border-t border-surface-500/10 pt-2"> <div
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Cache Structure</label> class="flex flex-col gap-1 mt-1 border-t border-surface-500/10 pt-2"
>
<label
class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Cache Structure</label
>
<div class="flex items-center justify-between px-1"> <div class="flex items-center justify-between px-1">
<span class="text-[9px]">Hash Prefix Length</span> <span class="text-[9px]">Hash Prefix Length</span>
<select <select
bind:value={$ae_loc.native_device.hash_prefix_length} bind:value={
$ae_loc.native_device.hash_prefix_length
}
class="select select-sm h-6 py-0 text-[10px] w-16 preset-tonal-surface" class="select select-sm h-6 py-0 text-[10px] w-16 preset-tonal-surface"
> >
<option value={1}>1 char</option> <option value={1}>1 char</option>
@@ -73,26 +102,51 @@
<option value={3}>3 chars</option> <option value={3}>3 chars</option>
</select> </select>
</div> </div>
<p class="text-[8px] opacity-40 italic mt-1">* Prefix change requires a full app reload to take effect.</p> <p class="text-[8px] opacity-40 italic mt-1">
* Prefix change requires a full app reload to take
effect.
</p>
</div> </div>
{:else} {:else}
<!-- Read Only Summary (Normal Mode) --> <!-- Read Only Summary (Normal Mode) -->
<div class="bg-surface-500/5 p-2 rounded border border-surface-500/10 flex flex-col gap-1"> <div
<div class="flex justify-between text-[9px] opacity-60 font-mono"> class="bg-surface-500/5 p-2 rounded border border-surface-500/10 flex flex-col gap-1"
>
<div
class="flex justify-between text-[9px] opacity-60 font-mono"
>
<span>Event Sync:</span> <span>Event Sync:</span>
<span>{($ae_loc.native_device.check_event_loop_period / 1000).toFixed(1)}s</span> <span
>{(
$ae_loc.native_device.check_event_loop_period /
1000
).toFixed(1)}s</span
>
</div> </div>
<div class="flex justify-between text-[9px] opacity-60 font-mono"> <div
class="flex justify-between text-[9px] opacity-60 font-mono"
>
<span>Room Monitor:</span> <span>Room Monitor:</span>
<span>{($ae_loc.native_device.check_event_location_loop_period / 1000).toFixed(1)}s</span> <span
>{(
$ae_loc.native_device
.check_event_location_loop_period / 1000
).toFixed(1)}s</span
>
</div> </div>
<div class="flex justify-between text-[9px] opacity-60 font-mono border-t border-surface-500/10 pt-1"> <div
class="flex justify-between text-[9px] opacity-60 font-mono border-t border-surface-500/10 pt-1"
>
<span>Prefix Sharding:</span> <span>Prefix Sharding:</span>
<span>{$ae_loc.native_device.hash_prefix_length || 2} chars</span> <span
>{$ae_loc.native_device.hash_prefix_length || 2} chars</span
>
</div> </div>
</div> </div>
<div class="text-center"> <div class="text-center">
<p class="text-[8px] opacity-40 italic">Enable Edit Mode to adjust polling intervals.</p> <p class="text-[8px] opacity-40 italic">
Enable Edit Mode to adjust polling intervals.
</p>
</div> </div>
{/if} {/if}
</div> </div>

View File

@@ -25,46 +25,52 @@
async function handle_test_action(label: string) { async function handle_test_action(label: string) {
is_loading = true; is_loading = true;
action_status = `Executing ${label}...`; action_status = `Executing ${label}...`;
// Simulate async work // Simulate async work
await new Promise(r => setTimeout(r, 1500)); await new Promise((r) => setTimeout(r, 1500));
is_loading = false; is_loading = false;
action_status = `Finished: ${label}`; action_status = `Finished: ${label}`;
setTimeout(() => action_status = '', 3000); setTimeout(() => (action_status = ''), 3000);
} }
// Modal state for destructive actions // Modal state for destructive actions
let show_confirm = $state(false); let show_confirm = $state(false);
</script> </script>
<Launcher_Cfg_Section <Launcher_Cfg_Section
title="Template Section" title="Template Section"
icon="fa-cubes" icon="fa-cubes"
bind:state={$events_loc.launcher.section_state__template} bind:state={$events_loc.launcher.section_state__template}
{on_expand} {on_expand}
description="Kitchen Sink Scaffold | Demo Only" description="Kitchen Sink Scaffold | Demo Only"
> >
<!-- A. TOP STATUS BAR (Optional) --> <!-- A. TOP STATUS BAR (Optional) -->
{#if action_status} {#if action_status}
<div class="col-span-full text-[10px] text-center italic bg-primary-500/10 py-1 rounded animate-pulse text-primary-500 border border-primary-500/20"> <div
class="col-span-full text-[10px] text-center italic bg-primary-500/10 py-1 rounded animate-pulse text-primary-500 border border-primary-500/20"
>
{action_status} {action_status}
</div> </div>
{/if} {/if}
<!-- B. COMMON GRID SECTION (Read Only / High Level) --> <!-- B. COMMON GRID SECTION (Read Only / High Level) -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Standard Actions</label> <label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Standard Actions</label
>
<!-- Action Buttons --> <!-- Action Buttons -->
<div class="grid grid-cols-2 gap-1"> <div class="grid grid-cols-2 gap-1">
<button type="button" <button
type="button"
onclick={() => handle_test_action('Primary')} onclick={() => handle_test_action('Primary')}
class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start" class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start"
> >
<span class="fas fa-bolt mr-2 w-3 text-center"></span> Primary <span class="fas fa-bolt mr-2 w-3 text-center"></span> Primary
</button> </button>
<button type="button" <button
type="button"
onclick={() => handle_test_action('Secondary')} onclick={() => handle_test_action('Secondary')}
class="btn btn-xs preset-tonal-secondary hover:preset-filled-secondary-500 justify-start" class="btn btn-xs preset-tonal-secondary hover:preset-filled-secondary-500 justify-start"
> >
@@ -75,39 +81,68 @@
<!-- Toggles & Checkboxes --> <!-- Toggles & Checkboxes -->
<div class="flex flex-col gap-1 mt-1 bg-surface-500/5 p-2 rounded"> <div class="flex flex-col gap-1 mt-1 bg-surface-500/5 p-2 rounded">
<label class="flex items-center gap-2 cursor-pointer group"> <label class="flex items-center gap-2 cursor-pointer group">
<input type="checkbox" bind:checked={toggle_val} class="checkbox checkbox-sm" /> <input
<span class="text-xs group-hover:text-primary-500 transition-colors">Toggle Feature Alpha</span> type="checkbox"
bind:checked={toggle_val}
class="checkbox checkbox-sm"
/>
<span
class="text-xs group-hover:text-primary-500 transition-colors"
>Toggle Feature Alpha</span
>
</label> </label>
<label class="flex items-center gap-2 cursor-pointer group"> <label class="flex items-center gap-2 cursor-pointer group">
<input type="radio" name="demo" value="a" class="radio radio-sm" /> <input
<span class="text-xs group-hover:text-primary-500 transition-colors">Mode A</span> type="radio"
name="demo"
value="a"
class="radio radio-sm"
/>
<span
class="text-xs group-hover:text-primary-500 transition-colors"
>Mode A</span
>
</label> </label>
</div> </div>
</div> </div>
<!-- C. STATUS & GAUGES SECTION --> <!-- C. STATUS & GAUGES SECTION -->
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Current Status</label> <label class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Current Status</label
<div class="flex flex-col gap-2 p-2 border border-surface-500/10 rounded-lg"> >
<div
class="flex flex-col gap-2 p-2 border border-surface-500/10 rounded-lg"
>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class="text-[10px] font-medium">Engine Health</span> <span class="text-[10px] font-medium">Engine Health</span>
<span class="badge variant-filled-success text-[8px] uppercase">Stable</span> <span class="badge variant-filled-success text-[8px] uppercase"
>Stable</span
>
</div> </div>
<!-- Progress / Gauge Example --> <!-- Progress / Gauge Example -->
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<div class="flex justify-between text-[8px] uppercase opacity-60"> <div
class="flex justify-between text-[8px] uppercase opacity-60"
>
<span>Processing Load</span> <span>Processing Load</span>
<span>45%</span> <span>45%</span>
</div> </div>
<div class="w-full h-1.5 bg-surface-500/20 rounded-full overflow-hidden"> <div
<div class="h-full bg-success-500 transition-all duration-1000" style="width: 45%"></div> class="w-full h-1.5 bg-surface-500/20 rounded-full overflow-hidden"
>
<div
class="h-full bg-success-500 transition-all duration-1000"
style="width: 45%"
></div>
</div> </div>
</div> </div>
</div> </div>
<button type="button" <button
type="button"
onclick={() => handle_test_action('Refresh')} onclick={() => handle_test_action('Refresh')}
class="btn btn-xs preset-outlined-surface-500 w-full text-[10px]" class="btn btn-xs preset-outlined-surface-500 w-full text-[10px]"
> >
@@ -118,22 +153,34 @@
<!-- D. TECHNICAL SECTION (Edit Mode Only) --> <!-- D. TECHNICAL SECTION (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-3"> <div
class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-3"
>
<!-- Dangerous Actions --> <!-- Dangerous Actions -->
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50 text-warning-500">System Config</label> <label
<button type="button" class="text-[9px] font-bold uppercase opacity-50 text-warning-500"
onclick={() => show_confirm = true} >System Config</label
>
<button
type="button"
onclick={() => (show_confirm = true)}
class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 justify-start" class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 justify-start"
> >
<span class="fas fa-exclamation-triangle mr-2 w-3"></span> Reset All <span class="fas fa-exclamation-triangle mr-2 w-3"
></span> Reset All
</button> </button>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50 text-error-500">Danger Zone</label> <label
<button type="button" class="btn btn-xs preset-tonal-error hover:preset-filled-error-500 justify-start"> class="text-[9px] font-bold uppercase opacity-50 text-error-500"
>Danger Zone</label
>
<button
type="button"
class="btn btn-xs preset-tonal-error hover:preset-filled-error-500 justify-start"
>
<span class="fas fa-trash-alt mr-2 w-3"></span> Wipe Cache <span class="fas fa-trash-alt mr-2 w-3"></span> Wipe Cache
</button> </button>
</div> </div>
@@ -142,35 +189,46 @@
<!-- Form Inputs --> <!-- Form Inputs -->
<div class="grid grid-cols-1 gap-2"> <div class="grid grid-cols-1 gap-2">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Raw Settings</label> <label
class="text-[9px] font-bold uppercase opacity-50 ml-1"
>Raw Settings</label
>
<div class="flex gap-1"> <div class="flex gap-1">
<input <input
type="text" type="text"
bind:value={text_input} bind:value={text_input}
placeholder="Enter string parameter..." placeholder="Enter string parameter..."
class="input input-sm grow text-[10px] preset-tonal-surface h-7" class="input input-sm grow text-[10px] preset-tonal-surface h-7"
/> />
<select bind:value={select_val} class="select select-sm h-7 py-0 text-[10px] w-24 preset-tonal-surface"> <select
bind:value={select_val}
class="select select-sm h-7 py-0 text-[10px] w-24 preset-tonal-surface"
>
<option value="option1">Global</option> <option value="option1">Global</option>
<option value="option2">Local</option> <option value="option2">Local</option>
</select> </select>
</div> </div>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<span class="text-[8px] opacity-60 ml-1">Threshold (ms)</span> <span class="text-[8px] opacity-60 ml-1"
<input >Threshold (ms)</span
type="number" >
<input
type="number"
bind:value={number_input} bind:value={number_input}
class="input input-sm text-[10px] h-7 preset-tonal-surface" class="input input-sm text-[10px] h-7 preset-tonal-surface"
/> />
</div> </div>
</div> </div>
<!-- Terminal / Output Log --> <!-- Terminal / Output Log -->
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Debug Output</label> <label class="text-[9px] font-bold uppercase opacity-50 ml-1"
<pre class="text-[8px] bg-black text-green-500 p-2 overflow-x-auto rounded border border-surface-500/50 max-h-24 shadow-inner"> >Debug Output</label
>
<pre
class="text-[8px] bg-black text-green-500 p-2 overflow-x-auto rounded border border-surface-500/50 max-h-24 shadow-inner">
[LOG] System Initialized [LOG] System Initialized
[INFO] Store synced with IndexedDB [INFO] Store synced with IndexedDB
[DEBUG] active_tab: template [DEBUG] active_tab: template
@@ -183,16 +241,29 @@
<!-- Confirmation Modal Demo --> <!-- Confirmation Modal Demo -->
{#if show_confirm} {#if show_confirm}
<div class="fixed inset-0 z-[1000] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"> <div
<div class="card p-6 w-full max-w-sm preset-filled-surface-100-900 border border-warning-500 shadow-2xl animate-in zoom-in-95 duration-200"> class="fixed inset-0 z-[1000] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
>
<div
class="card p-6 w-full max-w-sm preset-filled-surface-100-900 border border-warning-500 shadow-2xl animate-in zoom-in-95 duration-200"
>
<h4 class="h4 text-warning-500 font-bold mb-2">Confirm Action</h4> <h4 class="h4 text-warning-500 font-bold mb-2">Confirm Action</h4>
<p class="text-sm opacity-80 mb-6"> <p class="text-sm opacity-80 mb-6">
Are you sure you want to perform this test operation? This demonstrate the standard confirmation pattern. Are you sure you want to perform this test operation? This
demonstrate the standard confirmation pattern.
</p> </p>
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">
<button type="button" onclick={() => show_confirm = false} class="btn btn-sm preset-tonal-surface">Cancel</button> <button
<button type="button" type="button"
onclick={() => { show_confirm = false; handle_test_action('Confirm'); }} onclick={() => (show_confirm = false)}
class="btn btn-sm preset-tonal-surface">Cancel</button
>
<button
type="button"
onclick={() => {
show_confirm = false;
handle_test_action('Confirm');
}}
class="btn btn-sm preset-filled-warning" class="btn btn-sm preset-filled-warning"
> >
Yes, Proceed Yes, Proceed

View File

@@ -10,9 +10,13 @@
let { on_expand }: Props = $props(); let { on_expand }: Props = $props();
let update_source: 'url' | 'file' = $state('file'); let update_source: 'url' | 'file' = $state('file');
let update_path = $state('~/OSIT/Speaker Ready System/Admin Share/Custom Applications/osit_binaries/'); let update_path = $state(
let update_url = $state('https://dev-demo.oneskyit.com/updates/ae_native.zip'); '~/OSIT/Speaker Ready System/Admin Share/Custom Applications/osit_binaries/'
);
let update_url = $state(
'https://dev-demo.oneskyit.com/updates/ae_native.zip'
);
let update_status = $state(''); let update_status = $state('');
let is_checking = $state(false); let is_checking = $state(false);
let download_result = $state<any>(null); let download_result = $state<any>(null);
@@ -20,14 +24,15 @@
async function handle_check_update() { async function handle_check_update() {
is_checking = true; is_checking = true;
update_status = 'Checking for updates...'; update_status = 'Checking for updates...';
try { try {
const args = update_source === 'url' const args =
? { source: 'url' as const, url: update_url } update_source === 'url'
: { source: 'file' as const, path: update_path }; ? { source: 'url' as const, url: update_url }
: { source: 'file' as const, path: update_path };
const res = await native.update_app(args); const res = await native.update_app(args);
if (res.success) { if (res.success) {
download_result = res; download_result = res;
update_status = 'Update located/downloaded. Ready to install.'; update_status = 'Update located/downloaded. Ready to install.';
@@ -43,45 +48,60 @@
async function handle_install() { async function handle_install() {
update_status = 'Initiating installation...'; update_status = 'Initiating installation...';
alert('Installation logic is OS-specific. This will typically swap the application bundle and restart.'); alert(
'Installation logic is OS-specific. This will typically swap the application bundle and restart.'
);
} }
</script> </script>
<Launcher_Cfg_Section <Launcher_Cfg_Section
title="Application Updates" title="Application Updates"
icon="fa-cloud-download-alt" icon="fa-cloud-download-alt"
bind:state={$events_loc.launcher.section_state__updates} bind:state={$events_loc.launcher.section_state__updates}
{on_expand} {on_expand}
description="v1.0.0 | Source: {update_source}" description="v1.0.0 | Source: {update_source}"
> >
<!-- Content omitted for brevity, preserved in file --> <!-- Content omitted for brevity, preserved in file -->
<div class="col-span-full flex flex-col gap-2"> <div class="col-span-full flex flex-col gap-2">
<!-- TECHNICAL: Source Config (Edit Mode Only) --> <!-- TECHNICAL: Source Config (Edit Mode Only) -->
{#if $ae_loc.edit_mode} {#if $ae_loc.edit_mode}
<div class="flex flex-col gap-2 bg-surface-500/5 p-2 rounded border border-surface-500/10 mb-1"> <div
class="flex flex-col gap-2 bg-surface-500/5 p-2 rounded border border-surface-500/10 mb-1"
>
<div class="flex flex-row justify-between items-center px-1"> <div class="flex flex-row justify-between items-center px-1">
<label class="text-[9px] font-bold uppercase opacity-50">Source Type</label> <label class="text-[9px] font-bold uppercase opacity-50"
>Source Type</label
>
<div class="flex gap-2"> <div class="flex gap-2">
<label class="flex items-center gap-1 text-[10px]"> <label class="flex items-center gap-1 text-[10px]">
<input type="radio" bind:group={update_source} value="file" class="radio radio-sm" /> Local <input
type="radio"
bind:group={update_source}
value="file"
class="radio radio-sm"
/> Local
</label> </label>
<label class="flex items-center gap-1 text-[10px]"> <label class="flex items-center gap-1 text-[10px]">
<input type="radio" bind:group={update_source} value="url" class="radio radio-sm" /> Web <input
type="radio"
bind:group={update_source}
value="url"
class="radio radio-sm"
/> Web
</label> </label>
</div> </div>
</div> </div>
{#if update_source === 'file'} {#if update_source === 'file'}
<input <input
type="text" type="text"
bind:value={update_path} bind:value={update_path}
placeholder="Path to update package" placeholder="Path to update package"
class="input input-sm text-[10px] preset-tonal-surface h-7 w-full" class="input input-sm text-[10px] preset-tonal-surface h-7 w-full"
/> />
{:else} {:else}
<input <input
type="text" type="text"
bind:value={update_url} bind:value={update_url}
placeholder="URL to update package" placeholder="URL to update package"
class="input input-sm text-[10px] preset-tonal-surface h-7 w-full" class="input input-sm text-[10px] preset-tonal-surface h-7 w-full"
@@ -91,7 +111,8 @@
{/if} {/if}
<!-- COMMON: Check Button --> <!-- COMMON: Check Button -->
<button type="button" <button
type="button"
onclick={handle_check_update} onclick={handle_check_update}
disabled={is_checking} disabled={is_checking}
class="btn btn-sm preset-filled-tertiary hover:preset-filled-primary-500 text-[10px] w-full" class="btn btn-sm preset-filled-tertiary hover:preset-filled-primary-500 text-[10px] w-full"
@@ -104,13 +125,16 @@
</button> </button>
{#if update_status} {#if update_status}
<div class="text-[9px] text-center italic p-1 border border-surface-500/20 rounded bg-surface-500/5 mt-1"> <div
class="text-[9px] text-center italic p-1 border border-surface-500/20 rounded bg-surface-500/5 mt-1"
>
{update_status} {update_status}
</div> </div>
{/if} {/if}
{#if download_result} {#if download_result}
<button type="button" <button
type="button"
onclick={handle_install} onclick={handle_install}
class="btn btn-sm preset-filled-success hover:preset-filled-primary-500 text-[10px] w-full animate-bounce mt-2 shadow-lg" class="btn btn-sm preset-filled-success hover:preset-filled-primary-500 text-[10px] w-full animate-bounce mt-2 shadow-lg"
> >

View File

@@ -58,9 +58,7 @@
// *** Set initial variables // *** Set initial variables
let ae_acct = data[$slct.account_id]; let ae_acct = data[$slct.account_id];
import { import { online } from 'svelte/reactivity/window';
online
} from 'svelte/reactivity/window';
$ae_sess.disable_sys_nav = true; $ae_sess.disable_sys_nav = true;
$ae_sess.disable_sys_header = true; $ae_sess.disable_sys_header = true;
@@ -80,7 +78,9 @@
if (log_lvl) { if (log_lvl) {
console.log(`event_id: ${data.params.event_id}`); console.log(`event_id: ${data.params.event_id}`);
console.log(`event_location_id: ${data.params.event_location_id}`); console.log(`event_location_id: ${data.params.event_location_id}`);
console.log(`event_session_id: ${data.url.searchParams.get('session_id')}`); console.log(
`event_session_id: ${data.url.searchParams.get('session_id')}`
);
} }
$events_slct.event_id = data.params.event_id; $events_slct.event_id = data.params.event_id;
$events_slct.event_location_id = data.params.event_location_id; $events_slct.event_location_id = data.params.event_location_id;
@@ -89,11 +89,19 @@
// String-Only ID Vision: Sync the device ID from the native environment // String-Only ID Vision: Sync the device ID from the native environment
const native_dev = $ae_loc.native_device; const native_dev = $ae_loc.native_device;
if (native_dev) { if (native_dev) {
$events_slct.event_device_id = native_dev.event_device_id || native_dev.id || native_dev.event_device_id_random || native_dev.id_random; $events_slct.event_device_id =
native_dev.event_device_id ||
native_dev.id ||
native_dev.event_device_id_random ||
native_dev.id_random;
} }
$events_slct.event_location_obj_li = ae_acct.slct.event_location_obj_li ?? ['']; $events_slct.event_location_obj_li = ae_acct.slct.event_location_obj_li ?? [
$events_slct.id_li__event_location = ae_acct.slct.id_li__event_location ?? ['']; ''
];
$events_slct.id_li__event_location = ae_acct.slct.id_li__event_location ?? [
''
];
// *** Functions and Logic // *** Functions and Logic
@@ -103,9 +111,12 @@
if (!id) return null; if (!id) return null;
if (log_lvl > 1) console.log(`lq__event_obj: event_id = ${id}`); if (log_lvl > 1) console.log(`lq__event_obj: event_id = ${id}`);
let results = await db_events.event.get(id); let results = await db_events.event.get(id);
if ($events_slct.event_obj && results) { if ($events_slct.event_obj && results) {
if (JSON.stringify($events_slct.event_obj) !== JSON.stringify(results)) { if (
JSON.stringify($events_slct.event_obj) !==
JSON.stringify(results)
) {
$events_slct.event_obj = { ...results }; $events_slct.event_obj = { ...results };
} }
} }
@@ -118,7 +129,10 @@
if (!id) return null; if (!id) return null;
let results = await db_events.device.get(id); let results = await db_events.device.get(id);
if ($events_slct.event_device_obj && results) { if ($events_slct.event_device_obj && results) {
if (JSON.stringify($events_slct.event_device_obj) !== JSON.stringify(results)) { if (
JSON.stringify($events_slct.event_device_obj) !==
JSON.stringify(results)
) {
$events_slct.event_device_obj = { ...results }; $events_slct.event_device_obj = { ...results };
} }
} }
@@ -129,14 +143,20 @@
let lq__event_event_file_obj_li = liveQuery(async () => { let lq__event_event_file_obj_li = liveQuery(async () => {
const id = $events_slct.event_id; const id = $events_slct.event_id;
if (!id) return []; if (!id) return [];
return await db_events.file.where('for_id').equals(id).sortBy('filename'); return await db_events.file
.where('for_id')
.equals(id)
.sortBy('filename');
}); });
// Event File - For Location // Event File - For Location
let lq__location_event_file_obj_li = liveQuery(async () => { let lq__location_event_file_obj_li = liveQuery(async () => {
const id = $events_slct.event_location_id; const id = $events_slct.event_location_id;
if (!id) return []; if (!id) return [];
return await db_events.file.where('for_id').equals(id).sortBy('filename'); return await db_events.file
.where('for_id')
.equals(id)
.sortBy('filename');
}); });
// Event Location // Event Location
@@ -149,7 +169,10 @@
let lq__event_location_obj_li = liveQuery(async () => { let lq__event_location_obj_li = liveQuery(async () => {
const id = $events_slct.event_id; const id = $events_slct.event_id;
if (!id) return []; if (!id) return [];
return await db_events.location.where('event_id').equals(id).sortBy('name'); return await db_events.location
.where('event_id')
.equals(id)
.sortBy('name');
}); });
// Event Session (Main View Trigger) // Event Session (Main View Trigger)
@@ -157,24 +180,37 @@
let lq__event_session_obj = liveQuery(async () => { let lq__event_session_obj = liveQuery(async () => {
const id = $events_slct.event_session_id; const id = $events_slct.event_session_id;
if (!id) return null; if (!id) return null;
if (log_lvl) console.log(`🔍 [Trace] Launcher Layout LQ: Fetching session_id=${id}`); if (log_lvl)
console.log(
`🔍 [Trace] Launcher Layout LQ: Fetching session_id=${id}`
);
const start = performance.now(); const start = performance.now();
let results = await db_events.session.get(id); let results = await db_events.session.get(id);
if (log_lvl) console.log(`📦 [Trace] Launcher Layout LQ: Result obtained in ${(performance.now() - start).toFixed(2)}ms (Result=${results?.name || 'NOT FOUND'})`); if (log_lvl)
console.log(
`📦 [Trace] Launcher Layout LQ: Result obtained in ${(performance.now() - start).toFixed(2)}ms (Result=${results?.name || 'NOT FOUND'})`
);
return results; return results;
}); });
let lq__event_session_obj_li = liveQuery(async () => { let lq__event_session_obj_li = liveQuery(async () => {
const id = $events_slct.event_location_id; const id = $events_slct.event_location_id;
if (!id) return []; if (!id) return [];
if (log_lvl > 1) console.log(`LQ - Using default sort for Event Session list location_id: ${id}`); if (log_lvl > 1)
console.log(
`LQ - Using default sort for Event Session list location_id: ${id}`
);
let results = await db_events.session let results = await db_events.session
.where('event_location_id') .where('event_location_id')
.equals(id) .equals(id)
.reverse() .reverse()
.sortBy('name'); .sortBy('name');
if ($events_slct.event_session_obj_li && JSON.stringify($events_slct.event_session_obj_li) !== JSON.stringify(results)) { if (
$events_slct.event_session_obj_li &&
JSON.stringify($events_slct.event_session_obj_li) !==
JSON.stringify(results)
) {
$events_slct.event_session_obj_li = [...(results || [])]; $events_slct.event_session_obj_li = [...(results || [])];
} }
return results; return results;
@@ -210,7 +246,10 @@
$events_slct.event_session_id = obj_id; $events_slct.event_session_id = obj_id;
let new_url = new URL(data.url); let new_url = new URL(data.url);
new_url.pathname = `/events/${$lq__event_session_obj?.event_id}/launcher/${$lq__event_session_obj?.event_location_id}`; new_url.pathname = `/events/${$lq__event_session_obj?.event_id}/launcher/${$lq__event_session_obj?.event_location_id}`;
new_url.searchParams.set('session_id', $events_slct.event_session_id); new_url.searchParams.set(
'session_id',
$events_slct.event_session_id
);
goto(new_url.toString(), { replaceState: false }); goto(new_url.toString(), { replaceState: false });
} }
} else if (cmd.startsWith('ae_download:')) { } else if (cmd.startsWith('ae_download:')) {
@@ -283,9 +322,12 @@
} }
}); });
if (!$events_loc.launcher.idle_timer) $events_loc.launcher.idle_timer = 5 * 60 * 1000; if (!$events_loc.launcher.idle_timer)
if (!$events_loc.launcher.idle_cycle) $events_loc.launcher.idle_cycle = 5 * 1000; $events_loc.launcher.idle_timer = 5 * 60 * 1000;
if (!$events_loc.launcher.idle_loop_period) $events_loc.launcher.idle_loop_period = 3 * 60 * 1000; if (!$events_loc.launcher.idle_cycle)
$events_loc.launcher.idle_cycle = 5 * 1000;
if (!$events_loc.launcher.idle_loop_period)
$events_loc.launcher.idle_loop_period = 3 * 60 * 1000;
listen({ listen({
timer: $events_loc.launcher.idle_timer, timer: $events_loc.launcher.idle_timer,
@@ -296,26 +338,43 @@
let saver_looping: boolean = $state(false); let saver_looping: boolean = $state(false);
function handle_idle_client() { function handle_idle_client() {
if ($lq__event_session_obj && $lq__event_session_obj?.type_code == 'poster') { if (
$lq__event_session_obj &&
$lq__event_session_obj?.type_code == 'poster'
) {
if (saver_looping) return false; if (saver_looping) return false;
saver_looping = true; saver_looping = true;
idle_timer_interval = setInterval(() => { idle_timer_interval = setInterval(
if ($events_loc.launcher.screen_saver_img_kv) { () => {
const keys = Object.keys($events_loc.launcher.screen_saver_img_kv); if ($events_loc.launcher.screen_saver_img_kv) {
const rand_index = Math.floor(Math.random() * keys.length); const keys = Object.keys(
let event_file_obj = $events_loc.launcher.screen_saver_img_kv[keys[rand_index]]; $events_loc.launcher.screen_saver_img_kv
);
const rand_index = Math.floor(
Math.random() * keys.length
);
let event_file_obj =
$events_loc.launcher.screen_saver_img_kv[
keys[rand_index]
];
$events_slct.event_file_id = event_file_obj.event_file_id; $events_slct.event_file_id =
$events_slct.event_file_obj = event_file_obj; event_file_obj.event_file_id;
$events_sess.launcher.modal__open_event_file_id = null; $events_slct.event_file_obj = event_file_obj;
$events_sess.launcher.modal__title = event_file_obj.filename ?? '*'; $events_sess.launcher.modal__open_event_file_id = null;
$events_sess.launcher.modal__open_event_file_id = $events_slct.event_file_id; $events_sess.launcher.modal__title =
$events_sess.launcher.modal__event_file_obj = event_file_obj; event_file_obj.filename ?? '*';
return true; $events_sess.launcher.modal__open_event_file_id =
} $events_slct.event_file_id;
return false; $events_sess.launcher.modal__event_file_obj =
}, $events_loc.launcher.idle_loop_period ?? 2 * 60 * 1000); event_file_obj;
return true;
}
return false;
},
$events_loc.launcher.idle_loop_period ?? 2 * 60 * 1000
);
} else { } else {
saver_looping = false; saver_looping = false;
return false; return false;
@@ -376,7 +435,8 @@
" "
> >
<h3 class="h4 text-center italic text-surface-600-400"> <h3 class="h4 text-center italic text-surface-600-400">
<button type="button" <button
type="button"
class="" class=""
onclick={() => { onclick={() => {
$events_loc.launcher.hide__launcher_menu = $events_loc.launcher.hide__launcher_menu =
@@ -387,20 +447,26 @@
<Satellite class="text-base mx-1 inline-block text-gray-500" /> <Satellite class="text-base mx-1 inline-block text-gray-500" />
<abbr title="Aether - Events Module Launcher"> <abbr title="Aether - Events Module Launcher">
Æ Launcher Æ Launcher
<span class="text-xs align-super font-normal" title="Version 3">v3</span> <span
class="text-xs align-super font-normal"
title="Version 3">v3</span
>
</abbr> </abbr>
</button> </button>
</h3> </h3>
{#if $lq__event_obj} {#if $lq__event_obj}
<h2 class="hidden md:inline-block h3 text-center text-surface-600-400"> <h2
class="hidden md:inline-block h3 text-center text-surface-600-400"
>
{$lq__event_obj.cfg_json?.short_name} {$lq__event_obj.cfg_json?.short_name}
</h2> </h2>
<h3 <h3
class="h4 text-center italic text-surface-600-400" class="h4 text-center italic text-surface-600-400"
title="Location ID: {$lq__event_location_obj?.event_location_id} Name: {$lq__event_location_obj?.name}" title="Location ID: {$lq__event_location_obj?.event_location_id} Name: {$lq__event_location_obj?.name}"
> >
<button type="button" <button
type="button"
class="text-base" class="text-base"
onclick={() => { onclick={() => {
$ae_loc.edit_mode = !$ae_loc.edit_mode; $ae_loc.edit_mode = !$ae_loc.edit_mode;
@@ -455,9 +521,13 @@
{lq__event_location_obj_li} {lq__event_location_obj_li}
{lq__event_location_obj} {lq__event_location_obj}
slct__event_location_id={$events_slct.event_location_id} slct__event_location_id={$events_slct.event_location_id}
bind:loading__session_li_status={$events_sess.launcher.loading__session_li_status} bind:loading__session_li_status={
$events_sess.launcher.loading__session_li_status
}
{lq__event_session_obj_li} {lq__event_session_obj_li}
bind:loading__session_id_status={$events_sess.launcher.loading__session_id_status} bind:loading__session_id_status={
$events_sess.launcher.loading__session_id_status
}
{lq__event_session_obj} {lq__event_session_obj}
bind:slct__event_session_id={$events_slct.event_session_id} bind:slct__event_session_id={$events_slct.event_session_id}
bind:trigger_reload__event_session_obj_id={ bind:trigger_reload__event_session_obj_id={
@@ -487,7 +557,9 @@
" "
> >
{#if !$events_slct.event_location_id} {#if !$events_slct.event_location_id}
<div class="flex flex-row items-center justify-center p-8 opacity-50"> <div
class="flex flex-row items-center justify-center p-8 opacity-50"
>
<span class="fas fa-map-marker-alt mx-2 text-2xl"></span> <span class="fas fa-map-marker-alt mx-2 text-2xl"></span>
<span>Please select a location from the menu</span> <span>Please select a location from the menu</span>
</div> </div>
@@ -500,7 +572,9 @@
bind:type_code={$lq__event_session_obj.type_code} bind:type_code={$lq__event_session_obj.type_code}
></Launcher_session_view> ></Launcher_session_view>
{:else if $events_slct.event_session_id} {:else if $events_slct.event_session_id}
<div class="flex flex-col items-center justify-center p-8 opacity-50"> <div
class="flex flex-col items-center justify-center p-8 opacity-50"
>
<LoaderCircle class="animate-spin mb-2" /> <LoaderCircle class="animate-spin mb-2" />
<span>Loading session details...</span> <span>Loading session details...</span>
</div> </div>
@@ -537,7 +611,8 @@
class="slct_location_name transition-all duration-1000" class="slct_location_name transition-all duration-1000"
title="Location ID: {$lq__event_location_obj?.event_location_id} Name: {$lq__event_location_obj?.name} | Device ID: {$lq__event_device_obj?.event_device_id} Name: {$lq__event_device_obj?.name}" title="Location ID: {$lq__event_location_obj?.event_location_id} Name: {$lq__event_location_obj?.name} | Device ID: {$lq__event_device_obj?.event_device_id} Name: {$lq__event_device_obj?.name}"
> >
<button type="button" <button
type="button"
class="" class=""
onclick={() => { onclick={() => {
$ae_loc.edit_mode = !$ae_loc.edit_mode; $ae_loc.edit_mode = !$ae_loc.edit_mode;
@@ -579,10 +654,13 @@
<span <span
class:hidden={!$events_loc.launcher.ws_connect} class:hidden={!$events_loc.launcher.ws_connect}
class:preset-tonal-warning={$events_sess.launcher.ws_connect_status != 'connected'} class:preset-tonal-warning={$events_sess.launcher.ws_connect_status !=
class:preset-tonal-success={$events_sess.launcher.ws_connect_status == 'connected'} 'connected'}
class:preset-tonal-success={$events_sess.launcher.ws_connect_status ==
'connected'}
class="group px-1 rounded-md transition-all duration-1000" class="group px-1 rounded-md transition-all duration-1000"
title="WebSocket is {$events_sess.launcher.ws_connect_status == 'connected' title="WebSocket is {$events_sess.launcher.ws_connect_status ==
'connected'
? 'connected' ? 'connected'
: 'disconnected'} API: {$ae_api?.base_url}" : 'disconnected'} API: {$ae_api?.base_url}"
> >
@@ -595,7 +673,9 @@
{/if} {/if}
</span> </span>
<div class="current_datetime font-mono px-2 hover:font-bold hover:bg-white transition-all"> <div
class="current_datetime font-mono px-2 hover:font-bold hover:bg-white transition-all"
>
<span class="hidden md:inline"> <span class="hidden md:inline">
<span class="fas fa-calendar-alt"></span> <span class="fas fa-calendar-alt"></span>
{ae_util.iso_datetime_formatter($time, 'date_full_no_year')} {ae_util.iso_datetime_formatter($time, 'date_full_no_year')}
@@ -612,7 +692,8 @@
</footer> </footer>
<div class="absolute top-0 left-0 z-20 text-center"> <div class="absolute top-0 left-0 z-20 text-center">
<button type="button" <button
type="button"
onclick={() => ($events_loc.launcher.hide_drawer__cfg = false)} onclick={() => ($events_loc.launcher.hide_drawer__cfg = false)}
class="btn btn-sm p-2.5 preset-tonal-error hover:preset-filled-error-500 transition-all duration-1000" class="btn btn-sm p-2.5 preset-tonal-error hover:preset-filled-error-500 transition-all duration-1000"
class:opacity-25={!$ae_loc.trusted_access} class:opacity-25={!$ae_loc.trusted_access}
@@ -639,7 +720,9 @@
<hr class="my-2 border-gray-300 dark:border-gray-600" /> <hr class="my-2 border-gray-300 dark:border-gray-600" />
<div class="flex flex-row flex-wrap gap-0.5 items-center justify-center max-w-md"> <div
class="flex flex-row flex-wrap gap-0.5 items-center justify-center max-w-md"
>
<a <a
href="/events/{$events_slct.event_id}" href="/events/{$events_slct.event_id}"
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500" class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500"
@@ -682,10 +765,13 @@
id="sidebar2" id="sidebar2"
> >
<div class="flex flex-row items-center justify-between"> <div class="flex flex-row items-center justify-between">
<h2 class="text-center mb-4 text-base font-semibold text-gray-500 dark:text-gray-400"> <h2
class="text-center mb-4 text-base font-semibold text-gray-500 dark:text-gray-400"
>
Debug Debug
</h2> </h2>
<button type="button" <button
type="button"
onclick={() => ($events_loc.launcher.hide_drawer__debug = true)} onclick={() => ($events_loc.launcher.hide_drawer__debug = true)}
class="mb-4 dark:text-white" class="mb-4 dark:text-white"
> >
@@ -729,7 +815,8 @@
> >
{$events_sess.launcher?.modal__title ?? 'Digital Poster Display'} {$events_sess.launcher?.modal__title ?? 'Digital Poster Display'}
</h3> </h3>
<button type="button" <button
type="button"
class="btn flex-row-reverse group transition-all justify-self-end" class="btn flex-row-reverse group transition-all justify-self-end"
onclick={() => { onclick={() => {
$events_sess.launcher.modal__open_event_file_id = null; $events_sess.launcher.modal__open_event_file_id = null;
@@ -741,7 +828,8 @@
</button> </button>
{/snippet} {/snippet}
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
$events_sess.launcher.controller_cmd = `ae_close:event_file_modal`; $events_sess.launcher.controller_cmd = `ae_close:event_file_modal`;
$events_sess.launcher.controller_trigger_send = true; $events_sess.launcher.controller_trigger_send = true;
@@ -766,8 +854,8 @@
{#if $events_sess.launcher.modal__open_event_file_id} {#if $events_sess.launcher.modal__open_event_file_id}
<img <img
src="{$ae_api.base_url}/v3/action/event_file/{$events_sess.launcher src="{$ae_api.base_url}/v3/action/event_file/{$events_sess.launcher
.modal__open_event_file_id}/download?filename={$events_slct.event_file_obj .modal__open_event_file_id}/download?filename={$events_slct
.filename}&key={$ae_api.account_id}" .event_file_obj.filename}&key={$ae_api.account_id}"
alt="Poster" alt="Poster"
class="min-h-28 min-w-md max-h-full max-w-full" class="min-h-28 min-w-md max-h-full max-w-full"
/> />
@@ -778,7 +866,8 @@
</div> </div>
{/if} {/if}
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
$events_sess.launcher.controller_cmd = `ae_close:event_file_modal`; $events_sess.launcher.controller_cmd = `ae_close:event_file_modal`;
$events_sess.launcher.controller_trigger_send = true; $events_sess.launcher.controller_trigger_send = true;
@@ -800,7 +889,8 @@
Close Remote Poster Display Only Close Remote Poster Display Only
</button> </button>
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
$events_sess.launcher.modal__title = ''; $events_sess.launcher.modal__title = '';
$events_sess.launcher.modal__open_event_file_id = null; $events_sess.launcher.modal__open_event_file_id = null;
@@ -846,4 +936,4 @@
bind:ws_recv_status={trigger_handle_ws_recv} bind:ws_recv_status={trigger_handle_ws_recv}
bind:ws_sent_status={trigger_handle_ws_sent} bind:ws_sent_status={trigger_handle_ws_sent}
/> />
{/if} {/if}

View File

@@ -17,7 +17,9 @@ export async function load({ params, parent, url }) {
let ae_acct = data[account_id]; let ae_acct = data[account_id];
if (!ae_acct) { if (!ae_acct) {
console.warn(`ae Events - [event_id] launcher +layout.ts: Account ${account_id} not found in data. Initializing ghost acct.`); console.warn(
`ae Events - [event_id] launcher +layout.ts: Account ${account_id} not found in data. Initializing ghost acct.`
);
ae_acct = { ae_acct = {
api: data.ae_api || {}, api: data.ae_api || {},
slct: { slct: {
@@ -30,10 +32,13 @@ export async function load({ params, parent, url }) {
const event_id = params.event_id; const event_id = params.event_id;
if (browser) { if (browser) {
if (log_lvl) console.log(`ae_events Launcher - [event_id] launcher +layout.ts (Non-Blocking)`); if (log_lvl)
console.log(
`ae_events Launcher - [event_id] launcher +layout.ts (Non-Blocking)`
);
// OPTIMIZATION: Fire these in the background without 'await'. // OPTIMIZATION: Fire these in the background without 'await'.
// The UI components (menu, session_view) already use LiveQuery/Dexie // The UI components (menu, session_view) already use LiveQuery/Dexie
// and will automatically update when these background tasks save to IDB. // and will automatically update when these background tasks save to IDB.
events_func.load_ae_obj_li__event_device({ events_func.load_ae_obj_li__event_device({
api_cfg: ae_acct.api, api_cfg: ae_acct.api,
@@ -51,4 +56,4 @@ export async function load({ params, parent, url }) {
} }
return data; return data;
} }

View File

@@ -49,12 +49,21 @@
$events_slct.event_id = data.params.event_id; $events_slct.event_id = data.params.event_id;
$events_slct.event_location_id = data.params.event_location_id; $events_slct.event_location_id = data.params.event_location_id;
$events_slct.event_location_obj_li = ae_acct.slct.event_location_obj_li ?? ['']; $events_slct.event_location_obj_li = ae_acct.slct.event_location_obj_li ?? [
$events_slct.id_li__event_location = ae_acct.slct.id_li__event_location ?? ['']; ''
];
$events_slct.id_li__event_location = ae_acct.slct.id_li__event_location ?? [
''
];
if (log_lvl) { if (log_lvl) {
console.log(`$events_slct.event_location_obj_li:`, $events_slct.event_location_obj_li); console.log(
`$events_slct.event_location_obj_li:`,
$events_slct.event_location_obj_li
);
} }
$events_slct.event_session_obj_li = ae_acct.slct.event_session_obj_li ?? ['']; $events_slct.event_session_obj_li = ae_acct.slct.event_session_obj_li ?? [
''
];
// Set the local storage values // Set the local storage values
if (!$events_loc.launcher) { if (!$events_loc.launcher) {
@@ -135,7 +144,8 @@
ni_data['name'] = network_interface; ni_data['name'] = network_interface;
let ni_details = result.networkInterfaces[network_interface]; let ni_details =
result.networkInterfaces[network_interface];
// console.log(ni_details); // console.log(ni_details);
for (let i = 0; i < ni_details.length; i++) { for (let i = 0; i < ni_details.length; i++) {
@@ -155,12 +165,13 @@
event_device_data['info_ip_list'] = ip_address_li.join('; '); event_device_data['info_ip_list'] = ip_address_li.join('; ');
console.log(event_device_data['info_ip_list']); console.log(event_device_data['info_ip_list']);
let event_device_obj_up_promise = events_func.update_ae_obj__event_device({ let event_device_obj_up_promise = events_func
api_cfg: $ae_api, .update_ae_obj__event_device({
event_device_id: event_device_id, api_cfg: $ae_api,
data_kv: event_device_data, event_device_id: event_device_id,
log_lvl: 0 data_kv: event_device_data,
}) log_lvl: 0
})
.then(function (up_event_device_result) { .then(function (up_event_device_result) {
// console.log('UPDATED DEVICE INFO!!! UPDATED DEVICE INFO!!! UPDATED DEVICE INFO!!!'); // console.log('UPDATED DEVICE INFO!!! UPDATED DEVICE INFO!!! UPDATED DEVICE INFO!!!');
if (up_event_device_result) { if (up_event_device_result) {
@@ -177,6 +188,4 @@
} }
</script> </script>
<div class="hidden"> <div class="hidden">This is for forcing data loading.</div>
This is for forcing data loading.
</div>

View File

@@ -17,7 +17,9 @@ export async function load({ params, parent, url }) {
let ae_acct = data[account_id]; let ae_acct = data[account_id];
if (!ae_acct) { if (!ae_acct) {
console.warn(`ae Events - [event_id] launcher [event_location_id] +page.ts: Account ${account_id} not found in data. Initializing ghost acct.`); console.warn(
`ae Events - [event_id] launcher [event_location_id] +page.ts: Account ${account_id} not found in data. Initializing ghost acct.`
);
ae_acct = { ae_acct = {
api: data.ae_api || {}, api: data.ae_api || {},
slct: { slct: {
@@ -43,9 +45,9 @@ export async function load({ params, parent, url }) {
event_location_id event_location_id
); );
} }
// OPTIMIZATION: Fire the session list refresh in the background. // OPTIMIZATION: Fire the session list refresh in the background.
// The Launcher components already use liveQuery to watch Dexie, // The Launcher components already use liveQuery to watch Dexie,
// so they will update automatically once this background task finishes. // so they will update automatically once this background task finishes.
events_func.load_ae_obj_li__event_session({ events_func.load_ae_obj_li__event_session({
api_cfg: ae_acct.api, api_cfg: ae_acct.api,
@@ -61,7 +63,9 @@ export async function load({ params, parent, url }) {
log_lvl: 0 log_lvl: 0
}); });
} else { } else {
console.log(`ae pres_mgmt launcher [slug] +page.ts: browser = false or location_id missing`); console.log(
`ae pres_mgmt launcher [slug] +page.ts: browser = false or location_id missing`
);
} }
// WARNING: Precaution against shared data between sites and sessions. // WARNING: Precaution against shared data between sites and sessions.

View File

@@ -14,7 +14,9 @@
let { log_lvl = 1 } = $props(); let { log_lvl = 1 } = $props();
let currently_syncing: string | null = $state(null); let currently_syncing: string | null = $state(null);
let sync_results: Record<string, 'success' | 'error' | 'pending'> = $state({}); let sync_results: Record<string, 'success' | 'error' | 'pending'> = $state(
{}
);
let sync_stats = $state({ total: 0, cached: 0, missing: 0 }); let sync_stats = $state({ total: 0, cached: 0, missing: 0 });
let last_heartbeat: string | null = $state(null); let last_heartbeat: string | null = $state(null);
@@ -49,7 +51,10 @@
$ae_loc.native_device.home_directory = info.home_directory; $ae_loc.native_device.home_directory = info.home_directory;
$ae_loc.native_device.tmp_directory = info.tmp_directory; $ae_loc.native_device.tmp_directory = info.tmp_directory;
if (log_lvl) console.log('Sync: Native OS metadata hydrated.', { home: info.home_directory }); if (log_lvl)
console.log('Sync: Native OS metadata hydrated.', {
home: info.home_directory
});
} }
} catch (err) { } catch (err) {
console.error('Sync: Metadata hydration FAILED:', err); console.error('Sync: Metadata hydration FAILED:', err);
@@ -66,8 +71,14 @@
// Timers run in both browser and native for dev/testing // Timers run in both browser and native for dev/testing
timer__event = setInterval(() => run_sync_cycle(), loop_info.event); timer__event = setInterval(() => run_sync_cycle(), loop_info.event);
timer__device = setInterval(() => run_device_heartbeat(), loop_info.device); timer__device = setInterval(
timer__location = setInterval(() => refresh_location_config(), loop_info.location); () => run_device_heartbeat(),
loop_info.device
);
timer__location = setInterval(
() => refresh_location_config(),
loop_info.location
);
timer__session = setInterval(() => run_sync_cycle(), loop_info.session); timer__session = setInterval(() => run_sync_cycle(), loop_info.session);
// Immediate first run // Immediate first run
@@ -88,22 +99,42 @@
const cache_root = $ae_loc.local_file_cache_path; const cache_root = $ae_loc.local_file_cache_path;
const prefix_len = $ae_loc.native_device?.hash_prefix_length || 2; const prefix_len = $ae_loc.native_device?.hash_prefix_length || 2;
if (!location_id || !cache_root || is_syncing || !$ae_loc.is_native) return; if (!location_id || !cache_root || is_syncing || !$ae_loc.is_native)
return;
is_syncing = true; is_syncing = true;
try { try {
const sessions = await db_events.session.where('event_location_id').equals(location_id).toArray(); const sessions = await db_events.session
const session_ids = sessions.map(s => s.event_session_id); .where('event_location_id')
.equals(location_id)
.toArray();
const session_ids = sessions.map((s) => s.event_session_id);
if (session_ids.length === 0) return; if (session_ids.length === 0) return;
const presentations = await db_events.presentation.where('event_session_id').anyOf(session_ids).toArray(); const presentations = await db_events.presentation
const presentation_ids = presentations.map(p => p.event_presentation_id); .where('event_session_id')
.anyOf(session_ids)
.toArray();
const presentation_ids = presentations.map(
(p) => p.event_presentation_id
);
const presenters = await db_events.presenter.where('event_session_id').anyOf(session_ids).toArray(); const presenters = await db_events.presenter
const presenter_ids = presenters.map(p => p.event_presenter_id); .where('event_session_id')
.anyOf(session_ids)
.toArray();
const presenter_ids = presenters.map((p) => p.event_presenter_id);
const all_for_ids = [...session_ids, ...presentation_ids, ...presenter_ids, $events_slct.event_id]; const all_for_ids = [
const files = await db_events.file.where('for_id').anyOf(all_for_ids).toArray(); ...session_ids,
...presentation_ids,
...presenter_ids,
$events_slct.event_id
];
const files = await db_events.file
.where('for_id')
.anyOf(all_for_ids)
.toArray();
sync_stats.total = files.length; sync_stats.total = files.length;
let cached_count = 0; let cached_count = 0;
@@ -111,7 +142,10 @@
for (const file_obj of files) { for (const file_obj of files) {
if (!file_obj.hash_sha256) continue; if (!file_obj.hash_sha256) continue;
if (sync_results[file_obj.event_file_id] === 'success') { cached_count++; continue; } if (sync_results[file_obj.event_file_id] === 'success') {
cached_count++;
continue;
}
const exists = await native.check_hash_file_cache({ const exists = await native.check_hash_file_cache({
cache_root, cache_root,
@@ -127,17 +161,22 @@
missing_count++; missing_count++;
currently_syncing = file_obj.filename; currently_syncing = file_obj.filename;
$events_sess.launcher.sync_stats.currently_syncing = currently_syncing; $events_sess.launcher.sync_stats.currently_syncing =
currently_syncing;
// Use the PROVEN endpoint path from api.ts that is known to work in Default Mode. // Use the PROVEN endpoint path from api.ts that is known to work in Default Mode.
const url = `${$ae_api.base_url}/v3/action/hosted_file/${file_obj.hosted_file_id}/download?return_file=true&filename=${encodeURIComponent(file_obj.filename)}&key=${$ae_api.account_id}`; const url = `${$ae_api.base_url}/v3/action/hosted_file/${file_obj.hosted_file_id}/download?return_file=true&filename=${encodeURIComponent(file_obj.filename)}&key=${$ae_api.account_id}`;
const result = await native.download_to_cache({ const result = await native.download_to_cache({
url, cache_root, hash: file_obj.hash_sha256, url,
api_key: $ae_api.api_secret_key, account_id: $ae_api.account_id, cache_root,
hash: file_obj.hash_sha256,
api_key: $ae_api.api_secret_key,
account_id: $ae_api.account_id,
hash_prefix_length: prefix_len hash_prefix_length: prefix_len
}); });
if (result.success) sync_results[file_obj.event_file_id] = 'success'; if (result.success)
sync_results[file_obj.event_file_id] = 'success';
} }
sync_stats.cached = cached_count; sync_stats.cached = cached_count;
sync_stats.missing = missing_count; sync_stats.missing = missing_count;
@@ -165,15 +204,23 @@
async function run_device_heartbeat() { async function run_device_heartbeat() {
const dev = $ae_loc.native_device; const dev = $ae_loc.native_device;
// String-Only ID Vision: Prioritize semantic string IDs, then generic, then legacy random strings // String-Only ID Vision: Prioritize semantic string IDs, then generic, then legacy random strings
const device_id = dev?.event_device_id || dev?.id || dev?.event_device_id_random || dev?.id_random; const device_id =
dev?.event_device_id ||
dev?.id ||
dev?.event_device_id_random ||
dev?.id_random;
if (!device_id) { if (!device_id) {
if (log_lvl) console.warn('Sync: Heartbeat skipped, no device_id found in $ae_loc.native_device.'); if (log_lvl)
console.warn(
'Sync: Heartbeat skipped, no device_id found in $ae_loc.native_device.'
);
$events_sess.launcher.heartbeat_info.status = 'error'; $events_sess.launcher.heartbeat_info.status = 'error';
return; return;
} }
if (log_lvl > 1) console.log(`Sync: Running heartbeat for device: ${device_id}`); if (log_lvl > 1)
console.log(`Sync: Running heartbeat for device: ${device_id}`);
try { try {
let info = null; let info = null;
@@ -183,7 +230,10 @@
const update_payload: any = { const update_payload: any = {
// Use standard SQL format YYYY-MM-DD HH:mm:ss for MySQL compatibility // Use standard SQL format YYYY-MM-DD HH:mm:ss for MySQL compatibility
heartbeat: ae_util.iso_datetime_formatter(new Date(), 'datetime_iso') heartbeat: ae_util.iso_datetime_formatter(
new Date(),
'datetime_iso'
)
}; };
if (info) { if (info) {
@@ -194,7 +244,8 @@
release: info.release, release: info.release,
arch: info.arch, arch: info.arch,
cpus: info.cpus, cpus: info.cpus,
total_mem: Math.round(info.total_mem / (1024 * 1024)) + 'MB', total_mem:
Math.round(info.total_mem / (1024 * 1024)) + 'MB',
free_mem: Math.round(info.free_mem / (1024 * 1024)) + 'MB' free_mem: Math.round(info.free_mem / (1024 * 1024)) + 'MB'
}; };
} else { } else {
@@ -243,42 +294,70 @@
</script> </script>
<!-- Monitor Overlay (Hidden by default, triggered by config modal or secret gesture) --> <!-- Monitor Overlay (Hidden by default, triggered by config modal or secret gesture) -->
<div class="fixed bottom-4 right-4 z-[9999] flex flex-col items-end gap-2 pointer-events-none"> <div
class="fixed bottom-4 right-4 z-[9999] flex flex-col items-end gap-2 pointer-events-none"
>
{#if show_monitor} {#if show_monitor}
<div class="bg-black/90 text-white p-3 rounded-lg border border-primary-500 shadow-2xl text-[10px] font-mono min-w-48 pointer-events-auto"> <div
<div class="flex justify-between border-b border-primary-500 pb-1 mb-2"> class="bg-black/90 text-white p-3 rounded-lg border border-primary-500 shadow-2xl text-[10px] font-mono min-w-48 pointer-events-auto"
<span class="font-bold text-primary-400">NATIVE SYNC MONITOR</span> >
<button type="button" onclick={() => show_monitor = false} class="text-error-500 hover:text-error-400">×</button> <div
class="flex justify-between border-b border-primary-500 pb-1 mb-2"
>
<span class="font-bold text-primary-400"
>NATIVE SYNC MONITOR</span
>
<button
type="button"
onclick={() => (show_monitor = false)}
class="text-error-500 hover:text-error-400">×</button
>
</div> </div>
<div class="grid grid-cols-2 gap-x-4 gap-y-1 mb-2"> <div class="grid grid-cols-2 gap-x-4 gap-y-1 mb-2">
<span class="opacity-70 text-primary-300">Room Status:</span> <span class="opacity-70 text-primary-300">Room Status:</span>
<span class="text-right">{sync_stats.cached} / {sync_stats.total} Files</span> <span class="text-right"
>{sync_stats.cached} / {sync_stats.total} Files</span
>
<span class="opacity-70 text-primary-300">Prefix Len:</span> <span class="opacity-70 text-primary-300">Prefix Len:</span>
<span class="text-right">{$ae_loc.native_device?.hash_prefix_length || 2} chars</span> <span class="text-right"
>{$ae_loc.native_device?.hash_prefix_length || 2} chars</span
>
<span class="opacity-70 text-primary-300">Heartbeat:</span> <span class="opacity-70 text-primary-300">Heartbeat:</span>
<span class="text-right {last_heartbeat ? 'text-success-500' : 'text-error-500'}"> <span
class="text-right {last_heartbeat
? 'text-success-500'
: 'text-error-500'}"
>
{last_heartbeat || 'Pending...'} {last_heartbeat || 'Pending...'}
</span> </span>
</div> </div>
<div class="border-t border-white/10 pt-2 flex flex-col gap-1"> <div class="border-t border-white/10 pt-2 flex flex-col gap-1">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class:text-primary-500={timer__event}>Event Loop:</span> <span class:text-primary-500={timer__event}
>Event Loop:</span
>
<span>{loop_info.event / 1000}s</span> <span>{loop_info.event / 1000}s</span>
</div> </div>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class:text-primary-500={timer__device}>Device Loop:</span> <span class:text-primary-500={timer__device}
>Device Loop:</span
>
<span>{loop_info.device / 1000}s</span> <span>{loop_info.device / 1000}s</span>
</div> </div>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class:text-primary-500={timer__location}>Location Loop:</span> <span class:text-primary-500={timer__location}
>Location Loop:</span
>
<span>{loop_info.location / 1000}s</span> <span>{loop_info.location / 1000}s</span>
</div> </div>
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class:text-primary-500={timer__session}>Session Loop:</span> <span class:text-primary-500={timer__session}
>Session Loop:</span
>
<span>{loop_info.session / 1000}s</span> <span>{loop_info.session / 1000}s</span>
</div> </div>
</div> </div>
@@ -286,27 +365,34 @@
{/if} {/if}
{#if currently_syncing} {#if currently_syncing}
<button type="button" <button
onclick={() => show_monitor = !show_monitor} type="button"
onclick={() => (show_monitor = !show_monitor)}
class="bg-black/80 text-white p-2 rounded-lg text-[10px] border border-primary-500 animate-pulse shadow-2xl pointer-events-auto transition-transform active:scale-95" class="bg-black/80 text-white p-2 rounded-lg text-[10px] border border-primary-500 animate-pulse shadow-2xl pointer-events-auto transition-transform active:scale-95"
> >
<div class="flex items-center gap-2 text-left"> <div class="flex items-center gap-2 text-left">
<span class="fas fa-sync fa-spin text-primary-500 text-sm"></span> <span class="fas fa-sync fa-spin text-primary-500 text-sm"
></span>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="font-bold">Syncing Room Files...</span> <span class="font-bold">Syncing Room Files...</span>
<span class="opacity-70 truncate max-w-48">{currently_syncing}</span> <span class="opacity-70 truncate max-w-48"
<span class="text-[8px] mt-1 text-primary-300">Monitor Ready (Click to View)</span> >{currently_syncing}</span
>
<span class="text-[8px] mt-1 text-primary-300"
>Monitor Ready (Click to View)</span
>
</div> </div>
</div> </div>
</button> </button>
{:else} {:else}
<!-- Secret button area to toggle monitor when not syncing --> <!-- Secret button area to toggle monitor when not syncing -->
<button type="button" <button
onclick={() => show_monitor = !show_monitor} type="button"
onclick={() => (show_monitor = !show_monitor)}
class="w-8 h-8 opacity-0 hover:opacity-20 bg-primary-500 rounded-full pointer-events-auto flex items-center justify-center transition-opacity" class="w-8 h-8 opacity-0 hover:opacity-20 bg-primary-500 rounded-full pointer-events-auto flex items-center justify-center transition-opacity"
title="Toggle Sync Monitor" title="Toggle Sync Monitor"
> >
<span class="fas fa-microchip text-white text-[10px]"></span> <span class="fas fa-microchip text-white text-[10px]"></span>
</button> </button>
{/if} {/if}
</div> </div>

View File

@@ -43,8 +43,11 @@
*/ */
function handle_section_expand(current_key: string) { function handle_section_expand(current_key: string) {
const launcher = $events_loc.launcher; const launcher = $events_loc.launcher;
Object.keys(launcher).forEach(key => { Object.keys(launcher).forEach((key) => {
if (key.startsWith('section_state__') && key !== `section_state__${current_key}`) { if (
key.startsWith('section_state__') &&
key !== `section_state__${current_key}`
) {
if (launcher[key] === 'auto') { if (launcher[key] === 'auto') {
launcher[key] = 'collapsed'; launcher[key] = 'collapsed';
} }
@@ -60,13 +63,18 @@
flex flex-col gap-4 items-center justify-start flex flex-col gap-4 items-center justify-start
" "
> >
<div class="w-full flex flex-row items-center justify-between border-b border-surface-500/20 pb-2"> <div
<h2 class="text-center text-lg font-bold text-gray-700 dark:text-gray-200"> class="w-full flex flex-row items-center justify-between border-b border-surface-500/20 pb-2"
>
<h2
class="text-center text-lg font-bold text-gray-700 dark:text-gray-200"
>
<span class="fas fa-cog mr-2 opacity-50"></span> <span class="fas fa-cog mr-2 opacity-50"></span>
Launcher Configuration Launcher Configuration
</h2> </h2>
<button type="button" <button
type="button"
onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)} onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)}
class="btn btn-icon dark:text-white hover:bg-surface-500/10 transition-colors" class="btn btn-icon dark:text-white hover:bg-surface-500/10 transition-colors"
> >
@@ -77,24 +85,27 @@
<!-- Category Tabs --> <!-- Category Tabs -->
<div class="w-full grid grid-cols-3 gap-1 bg-surface-500/10 p-1 rounded-lg"> <div class="w-full grid grid-cols-3 gap-1 bg-surface-500/10 p-1 rounded-lg">
<button type="button" <button
onclick={() => active_tab = 'system'} type="button"
onclick={() => (active_tab = 'system')}
class="btn btn-sm text-[10px] uppercase font-bold transition-all" class="btn btn-sm text-[10px] uppercase font-bold transition-all"
class:preset-filled-primary-500={active_tab === 'system'} class:preset-filled-primary-500={active_tab === 'system'}
class:opacity-50={active_tab !== 'system'} class:opacity-50={active_tab !== 'system'}
> >
<span class="fas fa-microchip mr-1"></span> System <span class="fas fa-microchip mr-1"></span> System
</button> </button>
<button type="button" <button
onclick={() => active_tab = 'sync'} type="button"
onclick={() => (active_tab = 'sync')}
class="btn btn-sm text-[10px] uppercase font-bold transition-all" class="btn btn-sm text-[10px] uppercase font-bold transition-all"
class:preset-filled-primary-500={active_tab === 'sync'} class:preset-filled-primary-500={active_tab === 'sync'}
class:opacity-50={active_tab !== 'sync'} class:opacity-50={active_tab !== 'sync'}
> >
<span class="fas fa-sync mr-1"></span> Sync <span class="fas fa-sync mr-1"></span> Sync
</button> </button>
<button type="button" <button
onclick={() => active_tab = 'general'} type="button"
onclick={() => (active_tab = 'general')}
class="btn btn-sm text-[10px] uppercase font-bold transition-all" class="btn btn-sm text-[10px] uppercase font-bold transition-all"
class:preset-filled-primary-500={active_tab === 'general'} class:preset-filled-primary-500={active_tab === 'general'}
class:opacity-50={active_tab !== 'general'} class:opacity-50={active_tab !== 'general'}
@@ -105,16 +116,22 @@
<!-- Tab Content --> <!-- Tab Content -->
<div class="w-full flex flex-col gap-2 min-h-[400px]"> <div class="w-full flex flex-col gap-2 min-h-[400px]">
{#if active_tab === 'system'} {#if active_tab === 'system'}
<div class="animate-in fade-in slide-in-from-left-2 duration-300"> <div class="animate-in fade-in slide-in-from-left-2 duration-300">
{#if $ae_loc.is_native} {#if $ae_loc.is_native}
<Launcher_Cfg_Health on_expand={() => handle_section_expand('health')} /> <Launcher_Cfg_Health
<Launcher_Cfg_Native_OS on_expand={() => handle_section_expand('native_os')} /> on_expand={() => handle_section_expand('health')}
<Launcher_Cfg_Updates on_expand={() => handle_section_expand('updates')} /> />
<Launcher_Cfg_Native_OS
on_expand={() => handle_section_expand('native_os')}
/>
<Launcher_Cfg_Updates
on_expand={() => handle_section_expand('updates')}
/>
{:else} {:else}
<div class="card p-8 text-center opacity-50 italic text-sm"> <div class="card p-8 text-center opacity-50 italic text-sm">
Native OS features are only available when running in the Aether Desktop app. Native OS features are only available when running in
the Aether Desktop app.
</div> </div>
{/if} {/if}
</div> </div>
@@ -122,33 +139,47 @@
{#if active_tab === 'sync'} {#if active_tab === 'sync'}
<div class="animate-in fade-in slide-in-from-bottom-2 duration-300"> <div class="animate-in fade-in slide-in-from-bottom-2 duration-300">
<Launcher_Cfg_Sync_Timers on_expand={() => handle_section_expand('sync_timers')} /> <Launcher_Cfg_Sync_Timers
on_expand={() => handle_section_expand('sync_timers')}
/>
</div> </div>
{/if} {/if}
{#if active_tab === 'general'} {#if active_tab === 'general'}
<div class="animate-in fade-in slide-in-from-right-2 duration-300"> <div class="animate-in fade-in slide-in-from-right-2 duration-300">
<Launcher_Cfg_Controller on_expand={() => handle_section_expand('controller')} /> <Launcher_Cfg_Controller
<Launcher_Cfg_App_Modes on_expand={() => handle_section_expand('app_modes')} /> on_expand={() => handle_section_expand('controller')}
<Launcher_Cfg_Screen_Saver on_expand={() => handle_section_expand('screen_saver')} /> />
<Launcher_Cfg_Local_Actions on_expand={() => handle_section_expand('local_actions')} /> <Launcher_Cfg_App_Modes
on_expand={() => handle_section_expand('app_modes')}
/>
<Launcher_Cfg_Screen_Saver
on_expand={() => handle_section_expand('screen_saver')}
/>
<Launcher_Cfg_Local_Actions
on_expand={() => handle_section_expand('local_actions')}
/>
</div> </div>
{/if} {/if}
</div> </div>
<!-- Global Actions Footer --> <!-- Global Actions Footer -->
<div class="w-full flex flex-col gap-2 border-t border-surface-500/20 pt-4 mt-auto"> <div
class="w-full flex flex-col gap-2 border-t border-surface-500/20 pt-4 mt-auto"
>
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
<button type="button" <button
onclick={() => ($events_loc.launcher.hide_drawer__debug = false)} type="button"
onclick={() =>
($events_loc.launcher.hide_drawer__debug = false)}
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 transition-all" class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 transition-all"
> >
<span class="fas fa-bug mr-2"></span> <span class="fas fa-bug mr-2"></span>
Open Debug Open Debug
</button> </button>
<button type="button" <button
type="button"
onclick={() => location.reload()} onclick={() => location.reload()}
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition-all" class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition-all"
> >
@@ -156,9 +187,11 @@
Reload Page Reload Page
</button> </button>
</div> </div>
<p class="text-[9px] text-center opacity-40 uppercase font-bold tracking-widest mt-2"> <p
class="text-[9px] text-center opacity-40 uppercase font-bold tracking-widest mt-2"
>
Aether Platform &bull; Events Launcher v3.0 Aether Platform &bull; Events Launcher v3.0
</p> </p>
</div> </div>
</div> </div>

View File

@@ -57,12 +57,16 @@
import { api } from '$lib/api/api'; import { api } from '$lib/api/api';
import { ae_loc, ae_api, ae_sess, slct } from '$lib/stores/ae_stores'; import { ae_loc, ae_api, ae_sess, slct } from '$lib/stores/ae_stores';
import { core_func } from '$lib/ae_core/ae_core_functions'; import { core_func } from '$lib/ae_core/ae_core_functions';
import { events_loc, events_sess, events_slct } from '$lib/stores/ae_events_stores'; import {
events_loc,
events_sess,
events_slct
} from '$lib/stores/ae_events_stores';
import { events_func } from '$lib/ae_events_functions'; import { events_func } from '$lib/ae_events_functions';
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte'; import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
import Element_ae_crud from '$lib/elements/element_ae_crud.svelte'; import Element_ae_crud from '$lib/elements/element_ae_crud.svelte';
// Import the relay // Import the relay
import * as native from '$lib/electron/electron_relay'; import * as native from '$lib/electron/electron_relay';
@@ -78,14 +82,17 @@
onMount(() => { onMount(() => {
if (screen_saver_exts.includes(event_file_obj.extension)) { if (screen_saver_exts.includes(event_file_obj.extension)) {
if (!$events_loc.launcher.screen_saver_img_kv) $events_loc.launcher.screen_saver_img_kv = {}; if (!$events_loc.launcher.screen_saver_img_kv)
$events_loc.launcher.screen_saver_img_kv[event_file_id] = { ...event_file_obj }; $events_loc.launcher.screen_saver_img_kv = {};
$events_loc.launcher.screen_saver_img_kv[event_file_id] = {
...event_file_obj
};
} }
}); });
async function handle_open_file() { async function handle_open_file() {
if (log_lvl) console.log('*** handle_open_file() ***'); if (log_lvl) console.log('*** handle_open_file() ***');
$events_slct.event_file_id = event_file_id; $events_slct.event_file_id = event_file_id;
$events_slct.event_file_obj = event_file_obj; $events_slct.event_file_obj = event_file_obj;
@@ -98,15 +105,18 @@
open_file_status = 'checking_cache'; open_file_status = 'checking_cache';
open_file_status_message = 'Checking local cache...'; open_file_status_message = 'Checking local cache...';
const exists = await native.check_hash_file_cache({ cache_root, hash: event_file_obj.hash_sha256 }); const exists = await native.check_hash_file_cache({
cache_root,
hash: event_file_obj.hash_sha256
});
if (!exists) { if (!exists) {
open_file_status = 'downloading_file'; open_file_status = 'downloading_file';
open_file_status_message = 'Downloading file to cache...'; open_file_status_message = 'Downloading file to cache...';
// Use the PROVEN endpoint path from api.ts that is known to work in Default Mode. // Use the PROVEN endpoint path from api.ts that is known to work in Default Mode.
const url = `${$ae_api.base_url}/v3/action/hosted_file/${event_file_obj.hosted_file_id}/download?return_file=true&filename=${encodeURIComponent(event_file_obj.filename)}&key=${$ae_api.account_id}`; const url = `${$ae_api.base_url}/v3/action/hosted_file/${event_file_obj.hosted_file_id}/download?return_file=true&filename=${encodeURIComponent(event_file_obj.filename)}&key=${$ae_api.account_id}`;
const dl_result = await native.download_to_cache({ const dl_result = await native.download_to_cache({
url, url,
cache_root, cache_root,
@@ -118,7 +128,7 @@
if (!dl_result.success) { if (!dl_result.success) {
open_file_status = 'error'; open_file_status = 'error';
open_file_status_message = `Download failed: ${dl_result.error}`; open_file_status_message = `Download failed: ${dl_result.error}`;
setTimeout(() => open_file_clicked = false, 5000); setTimeout(() => (open_file_clicked = false), 5000);
return false; return false;
} }
} }
@@ -127,7 +137,7 @@
open_file_status_message = 'Opening Application'; open_file_status_message = 'Opening Application';
// Phase 2/5: Use the atomic copy-and-launch operation. // Phase 2/5: Use the atomic copy-and-launch operation.
// The main process handler (file_handlers.ts) now handles the // The main process handler (file_handlers.ts) now handles the
// specialized LibreOffice/AppleScript logic internally after copying. // specialized LibreOffice/AppleScript logic internally after copying.
const launch_result = await native.launch_from_cache({ const launch_result = await native.launch_from_cache({
cache_root, cache_root,
@@ -141,9 +151,9 @@
open_file_status_message = `Failed to open: ${launch_result.error}`; open_file_status_message = `Failed to open: ${launch_result.error}`;
} }
setTimeout(() => open_file_clicked = false, 5000); setTimeout(() => (open_file_clicked = false), 5000);
return launch_result.success; return launch_result.success;
} }
// 2. ONSITE MODE (Browser with Modified Extensions) // 2. ONSITE MODE (Browser with Modified Extensions)
else if ($events_loc.launcher.app_mode === 'onsite') { else if ($events_loc.launcher.app_mode === 'onsite') {
open_file_clicked = true; open_file_clicked = true;
@@ -151,7 +161,11 @@
open_file_status_message = 'Downloading (Onsite Mode)...'; open_file_status_message = 'Downloading (Onsite Mode)...';
let filename = event_file_obj.filename; let filename = event_file_obj.filename;
if ((event_file_obj.extension === 'ppt' || event_file_obj.extension === 'pptx') && event_file_obj.open_in_os === 'win') { if (
(event_file_obj.extension === 'ppt' ||
event_file_obj.extension === 'pptx') &&
event_file_obj.open_in_os === 'win'
) {
filename = event_file_obj.filename + 'win'; filename = event_file_obj.filename + 'win';
} }
@@ -167,7 +181,7 @@
log_lvl: 1 log_lvl: 1
}); });
setTimeout(() => open_file_clicked = false, 5000); setTimeout(() => (open_file_clicked = false), 5000);
return dl_promise; return dl_promise;
} }
// 3. DEFAULT MODE (Standard Browser) // 3. DEFAULT MODE (Standard Browser)
@@ -175,7 +189,7 @@
open_file_clicked = true; open_file_clicked = true;
open_file_status = 'downloading_default'; open_file_status = 'downloading_default';
open_file_status_message = 'Downloading...'; open_file_status_message = 'Downloading...';
const dl_promise = api.get_object({ const dl_promise = api.get_object({
api_cfg: $ae_api, api_cfg: $ae_api,
endpoint: `/v3/action/hosted_file/${event_file_obj.hosted_file_id}/download`, endpoint: `/v3/action/hosted_file/${event_file_obj.hosted_file_id}/download`,
@@ -193,14 +207,14 @@
$events_sess.launcher.controller_trigger_send = true; $events_sess.launcher.controller_trigger_send = true;
} }
setTimeout(() => open_file_clicked = false, 5000); setTimeout(() => (open_file_clicked = false), 5000);
return dl_promise; return dl_promise;
} }
} }
function prevent_default<T extends Event>(fn: (event: T) => void) { function prevent_default<T extends Event>(fn: (event: T) => void) {
return function (event: T) { return function (event: T) {
event.prevent_default(); event.preventDefault();
fn(event); fn(event);
}; };
} }
@@ -209,21 +223,34 @@
<div <div
class:justify-between={!hide_meta} class:justify-between={!hide_meta}
class:justify-center={hide_meta} class:justify-center={hide_meta}
class:hidden={hide_draft && (event_file_obj.file_purpose == 'outline' || event_file_obj.file_purpose == 'draft')} class:hidden={hide_draft &&
(event_file_obj.file_purpose == 'outline' ||
event_file_obj.file_purpose == 'draft')}
class="event_launcher_file_cont grow flex flex-col md:flex-row flex-wrap gap-1 items-center justify-center max-w-full transition-all" class="event_launcher_file_cont grow flex flex-col md:flex-row flex-wrap gap-1 items-center justify-center max-w-full transition-all"
> >
{#if open_file_clicked} {#if open_file_clicked}
<div class="open_file_clicked alert" in:fade={{ duration: 250 }} out:fade={{ duration: 2000 }}> <div
class="open_file_clicked alert"
in:fade={{ duration: 250 }}
out:fade={{ duration: 2000 }}
>
<div class="alert_msg_pulse"> <div class="alert_msg_pulse">
<strong>*** {open_file_status_message || 'Please wait while this file downloads...'} ***</strong> <strong
>*** {open_file_status_message ||
'Please wait while this file downloads...'} ***</strong
>
</div> </div>
<p>Most files will automatically be opened full screen.</p> <p>Most files will automatically be opened full screen.</p>
<p>PowerPoint or KeyNote will attempt to display in presenter view.</p> <p>
PowerPoint or KeyNote will attempt to display in presenter view.
</p>
<p>Please close the file when finished.</p> <p>Please close the file when finished.</p>
</div> </div>
{/if} {/if}
<span class="event_file_action grow max-w-full flex flex-row flex-wrap gap-1 items-center justify-center"> <span
class="event_file_action grow max-w-full flex flex-row flex-wrap gap-1 items-center justify-center"
>
{#if session_type == 'poster' || open_method == 'modal'} {#if session_type == 'poster' || open_method == 'modal'}
<AE_Comp_Hosted_Files_Download_Button <AE_Comp_Hosted_Files_Download_Button
hosted_file_id={event_file_id} hosted_file_id={event_file_id}
@@ -239,10 +266,19 @@
> >
{#snippet label()} {#snippet label()}
{#if screen_saver_exts.includes(event_file_obj.extension)} {#if screen_saver_exts.includes(event_file_obj.extension)}
<span class="fas fa-chart-bar m-1" class:hidden={hide_launch_icon}></span> Open Poster <span
class="fas fa-chart-bar m-1"
class:hidden={hide_launch_icon}
></span> Open Poster
{:else} {:else}
<span class="fas fa-paper-plane m-1" class:hidden={hide_launch_icon}></span> <span
{ae_util.shorten_filename({ filename: event_file_obj.filename, max_length: max_filename_length })} class="fas fa-paper-plane m-1"
class:hidden={hide_launch_icon}
></span>
{ae_util.shorten_filename({
filename: event_file_obj.filename,
max_length: max_filename_length
})}
{/if} {/if}
{/snippet} {/snippet}
</AE_Comp_Hosted_Files_Download_Button> </AE_Comp_Hosted_Files_Download_Button>
@@ -260,27 +296,48 @@
<span class="fas fa-spinner fa-spin mx-0.5"></span> <span class="fas fa-spinner fa-spin mx-0.5"></span>
<span> <span>
{#if $ae_sess.api_download_kv[file_id]} {#if $ae_sess.api_download_kv[file_id]}
{$ae_sess.api_download_kv[file_id].percent_completed}% {$ae_sess.api_download_kv[file_id]
.percent_completed}%
{:else} {:else}
... ...
{/if} {/if}
</span> </span>
{:then result} {:then result}
<span class="fas fa-{ae_util.file_extension_icon(event_file_obj.extension)} mx-0.5"></span> <span
class="fas fa-{ae_util.file_extension_icon(
event_file_obj.extension
)} mx-0.5"
></span>
{event_file_obj.extension} {event_file_obj.extension}
{#if result === null || result === false} {#if result === null || result === false}
<span class="text-error-500"><span class="fas fa-exclamation-triangle mx-1"></span>Failed!</span> <span class="text-error-500"
><span
class="fas fa-exclamation-triangle mx-1"
></span>Failed!</span
>
{/if} {/if}
{:catch error} {:catch error}
<span class="text-error-500" title={error?.message}><span class="fas fa-exclamation-circle mx-0.5"></span>Error!</span> <span class="text-error-500" title={error?.message}
><span class="fas fa-exclamation-circle mx-0.5"
></span>Error!</span
>
{/await} {/await}
</span> </span>
<span class="grow {text_size} {text_size_md} w-full max-w-full overflow-hidden text-ellipsis {btn_text_align}"> <span
{ae_util.shorten_string({ string: event_file_obj.filename_no_ext, begin_length: 45, max_length: 65 })} class="grow {text_size} {text_size_md} w-full max-w-full overflow-hidden text-ellipsis {btn_text_align}"
>
{ae_util.shorten_string({
string: event_file_obj.filename_no_ext,
begin_length: 45,
max_length: 65
})}
</span> </span>
<span class="badge my-0 py-0.5 preset-tonal-success hover:preset-filled-success-500 text-xs xl:text-sm" class:hidden={!event_file_obj.file_purpose}> <span
class="badge my-0 py-0.5 preset-tonal-success hover:preset-filled-success-500 text-xs xl:text-sm"
class:hidden={!event_file_obj.file_purpose}
>
{event_file_obj.file_purpose} {event_file_obj.file_purpose}
</span> </span>
{/snippet} {/snippet}
@@ -288,7 +345,10 @@
{/if} {/if}
</span> </span>
<span class="event_file_meta grow text-sm text-gray-500 flex flex-col sm:flex-row gap-1 wrap items-center justify-between w-64 max-w-80 font-mono" class:hidden={hide_meta}> <span
class="event_file_meta grow text-sm text-gray-500 flex flex-col sm:flex-row gap-1 wrap items-center justify-between w-64 max-w-80 font-mono"
class:hidden={hide_meta}
>
<Element_ae_crud <Element_ae_crud
trigger_patch={ae_triggers.open_in_os} trigger_patch={ae_triggers.open_in_os}
api_cfg={$ae_api} api_cfg={$ae_api}
@@ -298,13 +358,20 @@
field_type={'button'} field_type={'button'}
field_value={ae_tmp.value__open_in_os} field_value={ae_tmp.value__open_in_os}
on:ae_crud_updated={() => { on:ae_crud_updated={() => {
events_func.load_ae_obj_id__event_file({ api_cfg: $ae_api, event_file_id: event_file_obj?.event_file_id, log_lvl }); events_func.load_ae_obj_id__event_file({
api_cfg: $ae_api,
event_file_id: event_file_obj?.event_file_id,
log_lvl
});
}} }}
> >
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
if (!event_file_obj?.open_in_os) ae_tmp.value__open_in_os = 'win'; if (!event_file_obj?.open_in_os)
else if (event_file_obj?.open_in_os == 'win') ae_tmp.value__open_in_os = 'mac'; ae_tmp.value__open_in_os = 'win';
else if (event_file_obj?.open_in_os == 'win')
ae_tmp.value__open_in_os = 'mac';
else ae_tmp.value__open_in_os = null; else ae_tmp.value__open_in_os = null;
ae_triggers.open_in_os = true; ae_triggers.open_in_os = true;
}} }}
@@ -313,20 +380,37 @@
class:preset-tonal-warning={event_file_obj?.open_in_os == 'mac'} class:preset-tonal-warning={event_file_obj?.open_in_os == 'mac'}
disabled={!$ae_loc.trusted_access} disabled={!$ae_loc.trusted_access}
> >
{#if event_file_obj?.open_in_os == 'win'}<span class="fab fa-windows m-1"></span> {#if event_file_obj?.open_in_os == 'win'}<span
{:else if event_file_obj?.open_in_os == 'mac'}<span class="fab fa-apple m-1"></span> class="fab fa-windows m-1"
></span>
{:else if event_file_obj?.open_in_os == 'mac'}<span
class="fab fa-apple m-1"
></span>
{:else}<span class="fas fa-folder-open m-1"></span>{/if} {:else}<span class="fas fa-folder-open m-1"></span>{/if}
</button> </button>
</Element_ae_crud> </Element_ae_crud>
<span class="event_file_created_on text-xs text-center flex flex-row gap-1 items-center justify-end w-24 md:w-44 preset-filled-surface-100-900 rounded px-1 py-0.5" class:hidden={hide_created_on}> <span
class="event_file_created_on text-xs text-center flex flex-row gap-1 items-center justify-end w-24 md:w-44 preset-filled-surface-100-900 rounded px-1 py-0.5"
class:hidden={hide_created_on}
>
<span class="fas fa-calendar-day"></span> <span class="fas fa-calendar-day"></span>
<span class="w-18">{ae_util.iso_datetime_formatter(event_file_obj.created_on, 'date_short')}</span> <span class="w-18"
>{ae_util.iso_datetime_formatter(
event_file_obj.created_on,
'date_short'
)}</span
>
</span> </span>
<span class="event_file_size text-xs text-center flex flex-row gap-1 items-center justify-end preset-filled-surface-100-900 w-22 max-w-28 rounded py-0.5" class:hidden={hide_size}> <span
class="event_file_size text-xs text-center flex flex-row gap-1 items-center justify-end preset-filled-surface-100-900 w-22 max-w-28 rounded py-0.5"
class:hidden={hide_size}
>
<span class="fas fa-save"></span> <span class="fas fa-save"></span>
{#if event_file_obj.file_size}{ae_util.format_bytes(event_file_obj.file_size)}{/if} {#if event_file_obj.file_size}{ae_util.format_bytes(
event_file_obj.file_size
)}{/if}
</span> </span>
</span> </span>
</div> </div>

View File

@@ -141,19 +141,26 @@
hide_created_on={true} hide_created_on={true}
hide_os={true} hide_os={true}
hide_size={true} hide_size={true}
show_bak_download={$ae_loc.trusted_access && $ae_loc.edit_mode} show_bak_download={$ae_loc.trusted_access &&
$ae_loc.edit_mode}
btn_size={'btn-sm'} btn_size={'btn-sm'}
btn_text_align={'text-center'} btn_text_align={'text-center'}
text_size={'text-xs'} text_size={'text-xs'}
text_size_md={'text-xs'} text_size_md={'text-xs'}
session_type={event_file_obj?.event_session_type_code ?? 'oral'} session_type={event_file_obj?.event_session_type_code ??
open_method={event_file_obj?.event_session_type_code == 'poster' 'oral'}
open_method={event_file_obj?.event_session_type_code ==
'poster'
? 'modal' ? 'modal'
: null} : null}
modal_title={$lq__event_session_obj?.name} modal_title={$lq__event_session_obj?.name}
bind:modal__title={$events_sess.launcher.modal__title} bind:modal__title={$events_sess.launcher.modal__title}
bind:modal__open_event_file_id={$events_sess.launcher.modal__open_event_file_id} bind:modal__open_event_file_id={
bind:modal__event_file_obj={$events_sess.launcher.modal__event_file_obj} $events_sess.launcher.modal__open_event_file_id
}
bind:modal__event_file_obj={
$events_sess.launcher.modal__event_file_obj
}
/> />
{/each} {/each}
</div> </div>
@@ -179,19 +186,26 @@
hide_created_on={true} hide_created_on={true}
hide_os={true} hide_os={true}
hide_size={true} hide_size={true}
show_bak_download={$ae_loc.trusted_access && $ae_loc.edit_mode} show_bak_download={$ae_loc.trusted_access &&
$ae_loc.edit_mode}
btn_size={'btn-sm'} btn_size={'btn-sm'}
btn_text_align={'text-center'} btn_text_align={'text-center'}
text_size={'text-xs'} text_size={'text-xs'}
text_size_md={'text-xs'} text_size_md={'text-xs'}
session_type={event_file_obj?.event_session_type_code ?? 'oral'} session_type={event_file_obj?.event_session_type_code ??
open_method={event_file_obj?.event_session_type_code == 'poster' 'oral'}
open_method={event_file_obj?.event_session_type_code ==
'poster'
? 'modal' ? 'modal'
: null} : null}
modal_title={$lq__event_session_obj?.name} modal_title={$lq__event_session_obj?.name}
bind:modal__title={$events_sess.launcher.modal__title} bind:modal__title={$events_sess.launcher.modal__title}
bind:modal__open_event_file_id={$events_sess.launcher.modal__open_event_file_id} bind:modal__open_event_file_id={
bind:modal__event_file_obj={$events_sess.launcher.modal__event_file_obj} $events_sess.launcher.modal__open_event_file_id
}
bind:modal__event_file_obj={
$events_sess.launcher.modal__event_file_obj
}
/> />
{/each} {/each}
</div> </div>
@@ -214,7 +228,8 @@
flex flex-row gap-1 items-center justify-center flex flex-row gap-1 items-center justify-center
" "
> >
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
if ($events_loc.launcher.show_content__hidden_files) { if ($events_loc.launcher.show_content__hidden_files) {
$events_loc.launcher.show_content__hidden_files = false; $events_loc.launcher.show_content__hidden_files = false;
@@ -235,14 +250,16 @@
title="Toggle showing hidden files. Including files marked as 'draft'." title="Toggle showing hidden files. Including files marked as 'draft'."
> >
{#if $events_loc.launcher.show_content__hidden_files} {#if $events_loc.launcher.show_content__hidden_files}
<span class="fas fa-eye-slash m-1 text-neutral-800/80"></span> <span class="fas fa-eye-slash m-1 text-neutral-800/80"
></span>
Hide Files Hide Files
{:else} {:else}
<span class="fas fa-eye m-1 text-neutral-800/80"></span> <span class="fas fa-eye m-1 text-neutral-800/80"></span>
All Files All Files
{/if} {/if}
</button> </button>
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
$events_loc.launcher.show_content__hidden_sessions = $events_loc.launcher.show_content__hidden_sessions =
!$events_loc.launcher.show_content__hidden_sessions; !$events_loc.launcher.show_content__hidden_sessions;
@@ -257,7 +274,8 @@
" "
> >
{#if $events_loc.launcher.show_content__hidden_sessions} {#if $events_loc.launcher.show_content__hidden_sessions}
<span class="fas fa-eye-slash m-1 text-neutral-800/80"></span> <span class="fas fa-eye-slash m-1 text-neutral-800/80"
></span>
Hide Sessions Hide Sessions
{:else} {:else}
<span class="fas fa-eye m-1 text-neutral-800/80"></span> <span class="fas fa-eye m-1 text-neutral-800/80"></span>

View File

@@ -27,24 +27,30 @@
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length} {#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
<section class="event_presentation_file_list my-1"> <section class="event_presentation_file_list my-1">
<div class="text-[10px] text-surface-600-400 uppercase font-bold tracking-wider opacity-70"> <div
class="text-[10px] text-surface-600-400 uppercase font-bold tracking-wider opacity-70"
>
Presentation Files: Presentation Files:
</div> </div>
<ul class="space-y-1"> <ul class="space-y-1">
{#each $lq__event_file_obj_li as event_file_obj} {#each $lq__event_file_obj_li as event_file_obj}
<li <li
class="flex flex-col md:flex-row flex-wrap gap-1 items-center justify-start" class="flex flex-col md:flex-row flex-wrap gap-1 items-center justify-start"
class:hidden={!$events_loc.launcher.show_content__hidden_files && class:hidden={!$events_loc.launcher
event_file_obj.hide} .show_content__hidden_files && event_file_obj.hide}
> >
<Event_launcher_file_cont <Event_launcher_file_cont
event_file_id={event_file_obj.event_file_id} event_file_id={event_file_obj.event_file_id}
{event_file_obj} {event_file_obj}
hide_created_on={false} hide_created_on={false}
bind:hide_draft={$events_loc.launcher.hide_content__draft_files} bind:hide_draft={
$events_loc.launcher.hide_content__draft_files
}
show_bak_download={$ae_loc.trusted_access} show_bak_download={$ae_loc.trusted_access}
session_type={event_file_obj?.event_session_type_code ?? 'oral'} session_type={event_file_obj?.event_session_type_code ??
open_method={event_file_obj?.event_session_type_code == 'poster' 'oral'}
open_method={event_file_obj?.event_session_type_code ==
'poster'
? 'modal' ? 'modal'
: null} : null}
modal_title={lq__event_presentation_obj?.name} modal_title={lq__event_presentation_obj?.name}
@@ -52,7 +58,9 @@
bind:modal__open_event_file_id={ bind:modal__open_event_file_id={
$events_sess.launcher.modal__open_event_file_id $events_sess.launcher.modal__open_event_file_id
} }
bind:modal__event_file_obj={$events_sess.launcher.modal__event_file_obj} bind:modal__event_file_obj={
$events_sess.launcher.modal__event_file_obj
}
/> />
</li> </li>
{/each} {/each}

View File

@@ -89,17 +89,21 @@
{#each $lq__event_file_obj_li as event_file_obj, index} {#each $lq__event_file_obj_li as event_file_obj, index}
<li <li
class="flex flex-col md:flex-row flex-wrap gap-1 items-center justify-start" class="flex flex-col md:flex-row flex-wrap gap-1 items-center justify-start"
class:hidden={!$events_loc.launcher.show_content__hidden_files && class:hidden={!$events_loc.launcher
event_file_obj.hide} .show_content__hidden_files && event_file_obj.hide}
> >
<Event_launcher_file_cont <Event_launcher_file_cont
event_file_id={event_file_obj.event_file_id} event_file_id={event_file_obj.event_file_id}
{event_file_obj} {event_file_obj}
hide_created_on={false} hide_created_on={false}
bind:hide_draft={$events_loc.launcher.hide_content__draft_files} bind:hide_draft={
$events_loc.launcher.hide_content__draft_files
}
show_bak_download={$ae_loc.trusted_access} show_bak_download={$ae_loc.trusted_access}
session_type={event_file_obj?.event_session_type_code ?? 'oral'} session_type={event_file_obj?.event_session_type_code ??
open_method={event_file_obj?.event_session_type_code == 'poster' 'oral'}
open_method={event_file_obj?.event_session_type_code ==
'poster'
? 'modal' ? 'modal'
: null} : null}
modal_title={lq__event_presenter_obj?.event_presentation_name} modal_title={lq__event_presenter_obj?.event_presentation_name}
@@ -107,7 +111,9 @@
bind:modal__open_event_file_id={ bind:modal__open_event_file_id={
$events_sess.launcher.modal__open_event_file_id $events_sess.launcher.modal__open_event_file_id
} }
bind:modal__event_file_obj={$events_sess.launcher.modal__event_file_obj} bind:modal__event_file_obj={
$events_sess.launcher.modal__event_file_obj
}
/> />
</li> </li>
{/each} {/each}

View File

@@ -88,8 +88,8 @@
{#each $lq__event_file_obj_li as event_file_obj, index} {#each $lq__event_file_obj_li as event_file_obj, index}
<li <li
class="flex flex-col md:flex-row wrap gap items-center justify-center" class="flex flex-col md:flex-row wrap gap items-center justify-center"
class:hidden={!$events_loc.launcher.show_content__hidden_files && class:hidden={!$events_loc.launcher
event_file_obj.hide} .show_content__hidden_files && event_file_obj.hide}
> >
<Event_launcher_file_cont <Event_launcher_file_cont
event_file_id={event_file_obj.event_file_id} event_file_id={event_file_obj.event_file_id}
@@ -97,8 +97,10 @@
hide_created_on={false} hide_created_on={false}
hide_meta={true} hide_meta={true}
show_bak_download={$ae_loc.trusted_access} show_bak_download={$ae_loc.trusted_access}
session_type={event_file_obj?.event_session_type_code ?? 'oral'} session_type={event_file_obj?.event_session_type_code ??
open_method={event_file_obj?.event_session_type_code == 'poster' 'oral'}
open_method={event_file_obj?.event_session_type_code ==
'poster'
? 'modal' ? 'modal'
: null} : null}
modal_title={lq__event_presenter_obj?.event_presentation_name} modal_title={lq__event_presenter_obj?.event_presentation_name}
@@ -106,7 +108,9 @@
bind:modal__open_event_file_id={ bind:modal__open_event_file_id={
$events_sess.launcher.modal__open_event_file_id $events_sess.launcher.modal__open_event_file_id
} }
bind:modal__event_file_obj={$events_sess.launcher.modal__event_file_obj} bind:modal__event_file_obj={
$events_sess.launcher.modal__event_file_obj
}
/> />
</li> </li>
{/each} {/each}

View File

@@ -69,7 +69,9 @@
let lq__event_file_obj_li = $derived( let lq__event_file_obj_li = $derived(
liveQuery(async () => { liveQuery(async () => {
if (log_lvl) { if (log_lvl) {
console.log(`lq__event_file_obj: event_file_id = ${$events_slct?.event_file_id}`); console.log(
`lq__event_file_obj: event_file_id = ${$events_slct?.event_file_id}`
);
} }
let results = await db_events.file let results = await db_events.file
// .where('event_session_id') // .where('event_session_id')
@@ -83,7 +85,10 @@
// Check if results are different than the current $events_slct.event_file_obj_li // Check if results are different than the current $events_slct.event_file_obj_li
if ($events_slct.event_file_obj_li && results) { if ($events_slct.event_file_obj_li && results) {
if (JSON.stringify($events_slct.event_file_obj_li) !== JSON.stringify(results)) { if (
JSON.stringify($events_slct.event_file_obj_li) !==
JSON.stringify(results)
) {
$events_slct.event_file_obj_li = [...results]; $events_slct.event_file_obj_li = [...results];
if (log_lvl) { if (log_lvl) {
console.log( console.log(
@@ -136,7 +141,10 @@
.sortBy(sort_by); .sortBy(sort_by);
// .sortBy('name') // .sortBy('name')
if (log_lvl) { if (log_lvl) {
console.log(`lq__event_presentation_obj_li: results = `, results); console.log(
`lq__event_presentation_obj_li: results = `,
results
);
} }
// Check if results are different than the current $events_slct.event_presentation_obj_li // Check if results are different than the current $events_slct.event_presentation_obj_li
@@ -189,7 +197,8 @@
// Check if results are different than the current $events_slct.event_presenter_obj_li // Check if results are different than the current $events_slct.event_presenter_obj_li
if ($events_slct.event_presenter_obj_li && results) { if ($events_slct.event_presenter_obj_li && results) {
if ( if (
JSON.stringify($events_slct.event_presenter_obj_li) !== JSON.stringify(results) JSON.stringify($events_slct.event_presenter_obj_li) !==
JSON.stringify(results)
) { ) {
$events_slct.event_presenter_obj_li = { ...results }; $events_slct.event_presenter_obj_li = { ...results };
if (log_lvl) { if (log_lvl) {
@@ -277,9 +286,12 @@
$events_loc.launcher.hide__session_datetimes} $events_loc.launcher.hide__session_datetimes}
class="shrink event_session_datetimes" class="shrink event_session_datetimes"
> >
<button type="button" <button
type="button"
onclick={() => { onclick={() => {
if ($events_loc.launcher.time_format == 'time_12_short') { if (
$events_loc.launcher.time_format == 'time_12_short'
) {
// $events_loc.launcher.datetime_format = 'datetime_long'; // $events_loc.launcher.datetime_format = 'datetime_long';
$events_loc.launcher.time_format = 'time_short'; $events_loc.launcher.time_format = 'time_short';
$events_loc.launcher.time_hours = 24; $events_loc.launcher.time_hours = 24;
@@ -319,8 +331,10 @@
</h3> </h3>
<span <span
class:justify-between={$events_loc.launcher.hide__session_datetimes} class:justify-between={$events_loc.launcher
class:justify-end={!$events_loc.launcher.hide__session_datetimes} .hide__session_datetimes}
class:justify-end={!$events_loc.launcher
.hide__session_datetimes}
class="grow flex flex-row gap-2 items-center" class="grow flex flex-row gap-2 items-center"
> >
<h2 <h2
@@ -363,7 +377,10 @@
<span class="fas fa-file-archive"></span> <span class="fas fa-file-archive"></span>
Session Files: Session Files:
<span class:hidden={!$ae_loc.trusted_access || !$ae_loc.edit_mode}> <span
class:hidden={!$ae_loc.trusted_access ||
!$ae_loc.edit_mode}
>
({$lq__event_file_obj_li?.length}&times;) ({$lq__event_file_obj_li?.length}&times;)
</span> </span>
</strong> </strong>
@@ -384,22 +401,29 @@
{#each $lq__event_file_obj_li as event_file_obj, index} {#each $lq__event_file_obj_li as event_file_obj, index}
<li <li
class="flex flex-row flex-wrap gap-1 items-center justify-center" class="flex flex-row flex-wrap gap-1 items-center justify-center"
class:hidden={!$events_loc.launcher.show_content__hidden_files && class:hidden={!$events_loc.launcher
.show_content__hidden_files &&
event_file_obj.hide} event_file_obj.hide}
> >
<Event_launcher_file_cont <Event_launcher_file_cont
event_file_id={event_file_obj.event_file_id} event_file_id={event_file_obj.event_file_id}
{event_file_obj} {event_file_obj}
hide_created_on={true} hide_created_on={true}
show_bak_download={$ae_loc.trusted_access && $ae_loc.edit_mode} show_bak_download={$ae_loc.trusted_access &&
session_type={event_file_obj?.event_session_type_code ?? 'oral'} $ae_loc.edit_mode}
open_method={event_file_obj?.event_session_type_code == 'poster' session_type={event_file_obj?.event_session_type_code ??
'oral'}
open_method={event_file_obj?.event_session_type_code ==
'poster'
? 'modal' ? 'modal'
: null} : null}
modal_title={$lq__event_session_obj?.name} modal_title={$lq__event_session_obj?.name}
bind:modal__title={$events_sess.launcher.modal__title} bind:modal__title={
$events_sess.launcher.modal__title
}
bind:modal__open_event_file_id={ bind:modal__open_event_file_id={
$events_sess.launcher.modal__open_event_file_id $events_sess.launcher
.modal__open_event_file_id
} }
bind:modal__event_file_obj={ bind:modal__event_file_obj={
$events_sess.launcher.modal__event_file_obj $events_sess.launcher.modal__event_file_obj
@@ -449,7 +473,9 @@
<!-- Maybe set max with? max-w-(--breakpoint-md) --> <!-- Maybe set max with? max-w-(--breakpoint-md) -->
<ul class="event_presentation_list max-w-full space-y-2"> <ul class="event_presentation_list max-w-full space-y-2">
{#each $lq__event_presentation_obj_li as event_presentation_obj} {#each $lq__event_presentation_obj_li as event_presentation_obj}
<li class="border-b-2 border-gray-300 my-1 py-1 text-center md:text-left"> <li
class="border-b-2 border-gray-300 my-1 py-1 text-center md:text-left"
>
<!-- The presentation information --> <!-- The presentation information -->
<div <div
class="event_presentation_datetime_name flex flex-row justify-evenly gap-4" class="event_presentation_datetime_name flex flex-row justify-evenly gap-4"
@@ -479,12 +505,17 @@
class="event_presentation_single_presenter italic text-sm text-gray-500" class="event_presentation_single_presenter italic text-sm text-gray-500"
> >
{#if $lq__event_presenter_obj_li[index]?.given_name && $lq__event_presenter_obj_li[index]?.given_name != 'Group'} {#if $lq__event_presenter_obj_li[index]?.given_name && $lq__event_presenter_obj_li[index]?.given_name != 'Group'}
<span class="fas fa-user"></span> <span class="fas fa-user"
{$lq__event_presenter_obj_li[index]?.full_name} ></span>
{$lq__event_presenter_obj_li[
index
]?.full_name}
{:else if $lq__event_presenter_obj_li[index]?.given_name == 'Group'} {:else if $lq__event_presenter_obj_li[index]?.given_name == 'Group'}
<span class="fas fa-users"></span> <span class="fas fa-users"
{$lq__event_presenter_obj_li[index] ></span>
?.affiliations} {$lq__event_presenter_obj_li[
index
]?.affiliations}
{:else} {:else}
--not set-- --not set--
{/if} {/if}
@@ -495,7 +526,9 @@
</div> </div>
<!-- Presentation-level files --> <!-- Presentation-level files -->
<Launcher_presentation_view lq__event_presentation_obj={event_presentation_obj} /> <Launcher_presentation_view
lq__event_presentation_obj={event_presentation_obj}
/>
<!-- The presenter list --> <!-- The presenter list -->

View File

@@ -69,7 +69,9 @@
); );
} }
if (!event_location_id) { if (!event_location_id) {
console.warn(`handle_load_ae_obj_li__event_session: No event_location_id provided.`); console.warn(
`handle_load_ae_obj_li__event_session: No event_location_id provided.`
);
return; return;
} }
@@ -84,8 +86,12 @@
inc_all_file_li: false, // Also include files under presentations and presenters as well? inc_all_file_li: false, // Also include files under presentations and presenters as well?
inc_presentation_li: true, inc_presentation_li: true,
inc_presenter_li: true, inc_presenter_li: true,
enabled: $events_loc.launcher.show_content__enabled_sessions ? 'all' : 'enabled', enabled: $events_loc.launcher.show_content__enabled_sessions
hidden: $events_loc.launcher.show_content__hidden_sessions ? 'all' : 'not_hidden', ? 'all'
: 'enabled',
hidden: $events_loc.launcher.show_content__hidden_sessions
? 'all'
: 'not_hidden',
limit: 49, limit: 49,
try_cache: true, try_cache: true,
log_lvl: 1 log_lvl: 1
@@ -126,7 +132,9 @@
<div class="text-xs text-surface-600-400"> <div class="text-xs text-surface-600-400">
<strong> <strong>
Location: Location:
<span class:hidden={!$ae_loc.trusted_access || !$ae_loc.edit_mode}> <span
class:hidden={!$ae_loc.trusted_access || !$ae_loc.edit_mode}
>
({$lq__event_location_obj_li?.length}&times;) ({$lq__event_location_obj_li?.length}&times;)
</span> </span>
@@ -161,11 +169,14 @@
if (slct_event_location_id) { if (slct_event_location_id) {
loading__session_li_status = 'loading'; loading__session_li_status = 'loading';
$events_loc.launcher.slct.event_location_id = slct_event_location_id; $events_loc.launcher.slct.event_location_id =
slct_event_location_id;
$events_slct.event_location_id = slct_event_location_id; $events_slct.event_location_id = slct_event_location_id;
// Load the sessions for this location // Load the sessions for this location
await handle_load_ae_obj_li__event_session(slct_event_location_id); await handle_load_ae_obj_li__event_session(
slct_event_location_id
);
new_url = `/events/${$events_slct.event_id}/launcher/${slct_event_location_id}`; new_url = `/events/${$events_slct.event_id}/launcher/${slct_event_location_id}`;
@@ -188,7 +199,9 @@
goto(new_url, { replaceState: false }); // Updates the URL history without reloading the page goto(new_url, { replaceState: false }); // Updates the URL history without reloading the page
}} }}
> >
<option value="" class="italic text-surface-800-200"> -- select -- </option> <option value="" class="italic text-surface-800-200">
-- select --
</option>
{#each $lq__event_location_obj_li as event_location_obj} {#each $lq__event_location_obj_li as event_location_obj}
<option value={event_location_obj?.id}> <option value={event_location_obj?.id}>
{event_location_obj?.name} {event_location_obj?.name}

View File

@@ -73,17 +73,23 @@
} }
let event_session_id = String(trigger_reload__event_session_obj_id); let event_session_id = String(trigger_reload__event_session_obj_id);
trigger_reload__event_session_obj_id = false; trigger_reload__event_session_obj_id = false;
// OPTIMIZATION: Update slct__event_session_id prop (which is bound to the store) // OPTIMIZATION: Update slct__event_session_id prop (which is bound to the store)
// BEFORE calling goto. This triggers the LiveQuery in the layout instantly. // BEFORE calling goto. This triggers the LiveQuery in the layout instantly.
slct__event_session_id = event_session_id; slct__event_session_id = event_session_id;
if (log_lvl) console.log(`[UI Trace] UI setting slct__event_session_id = ${slct__event_session_id} (+${(performance.now() - start).toFixed(2)}ms)`); if (log_lvl)
console.log(
`[UI Trace] UI setting slct__event_session_id = ${slct__event_session_id} (+${(performance.now() - start).toFixed(2)}ms)`
);
handle_load_ae_obj_id__event_session(event_session_id); handle_load_ae_obj_id__event_session(event_session_id);
if ($events_loc.launcher.controller == 'local_push') { if ($events_loc.launcher.controller == 'local_push') {
if (log_lvl) console.log(`[UI Trace] Local Push Controller Command: ae_load:event_session=${event_session_id}`); if (log_lvl)
console.log(
`[UI Trace] Local Push Controller Command: ae_load:event_session=${event_session_id}`
);
$events_sess.launcher.controller_cmd = `ae_load:event_session=${event_session_id}`; $events_sess.launcher.controller_cmd = `ae_load:event_session=${event_session_id}`;
$events_sess.launcher.controller_trigger_send = true; $events_sess.launcher.controller_trigger_send = true;
} }
@@ -91,11 +97,21 @@
data_url.searchParams.set('session_id', event_session_id); data_url.searchParams.set('session_id', event_session_id);
let new_url = data_url.toString(); let new_url = data_url.toString();
if (log_lvl) console.log(`[UI Trace] Initiating SvelteKit goto... (+${(performance.now() - start).toFixed(2)}ms)`); if (log_lvl)
console.log(
`[UI Trace] Initiating SvelteKit goto... (+${(performance.now() - start).toFixed(2)}ms)`
);
// Use noscroll and keepfocus to speed up the transition // Use noscroll and keepfocus to speed up the transition
goto(new_url, { replaceState: false, noscroll: true, keepfocus: true }).then(() => { goto(new_url, {
if (log_lvl) console.log(`🏁 [Trace] Total Navigation Roundtrip: ${(performance.now() - start).toFixed(2)}ms`); replaceState: false,
noscroll: true,
keepfocus: true
}).then(() => {
if (log_lvl)
console.log(
`🏁 [Trace] Total Navigation Roundtrip: ${(performance.now() - start).toFixed(2)}ms`
);
}); });
} }
}); });
@@ -121,12 +137,17 @@
log_lvl: log_lvl log_lvl: log_lvl
}) })
.then(async (load_results) => { .then(async (load_results) => {
if (log_lvl) console.log(`[UI Trace] handle_load_ae_obj_id: Library returned results in ${(performance.now() - start).toFixed(2)}ms.`); if (log_lvl)
console.log(
`[UI Trace] handle_load_ae_obj_id: Library returned results in ${(performance.now() - start).toFixed(2)}ms.`
);
if (load_results) { if (load_results) {
$events_slct.event_session_obj = load_results; $events_slct.event_session_obj = load_results;
$events_slct.event_file_obj_li = load_results.event_file_li ?? []; $events_slct.event_file_obj_li =
$events_slct.event_presentation_obj_li = load_results.event_presentation_li ?? []; load_results.event_file_li ?? [];
$events_slct.event_presentation_obj_li =
load_results.event_presentation_li ?? [];
} }
}) })
.finally(() => { .finally(() => {
@@ -145,7 +166,9 @@
<div class="text-xs text-surface-600-400"> <div class="text-xs text-surface-600-400">
<strong> <strong>
Sessions: Sessions:
<span class:hidden={!$ae_loc.trusted_access || !$ae_loc.edit_mode}> <span
class:hidden={!$ae_loc.trusted_access || !$ae_loc.edit_mode}
>
({$lq__event_session_obj_li?.length}&times;) ({$lq__event_session_obj_li?.length}&times;)
</span> </span>
@@ -177,22 +200,28 @@
transition-all transition-all
" "
> >
<button type="button" <button
type="button"
onmouseenter={() => { onmouseenter={() => {
// Start a 750 ms timer to prevent changing the session too quickly. // Start a 750 ms timer to prevent changing the session too quickly.
hover_timer = setTimeout(async () => { hover_timer = setTimeout(async () => {
// Only run if the session ID has changed // Only run if the session ID has changed
if (slct__event_session_id === event_session_obj?.id) { if (
slct__event_session_id ===
event_session_obj?.id
) {
return; return;
} }
trigger_reload__event_session_obj_id = event_session_obj?.id; trigger_reload__event_session_obj_id =
event_session_obj?.id;
}, hover_timer_wait); }, hover_timer_wait);
}} }}
onmouseleave={() => { onmouseleave={() => {
clearTimeout(hover_timer); clearTimeout(hover_timer);
}} }}
onclick={async () => { onclick={async () => {
trigger_reload__event_session_obj_id = event_session_obj?.id; trigger_reload__event_session_obj_id =
event_session_obj?.id;
$events_slct.event_file_id = null; $events_slct.event_file_id = null;
$events_slct.event_file_obj = null; $events_slct.event_file_obj = null;
@@ -214,11 +243,14 @@
" "
class:preset-filled-primary-500={slct__event_session_id === class:preset-filled-primary-500={slct__event_session_id ===
event_session_obj?.id} event_session_obj?.id}
class:preset-tonal-secondary={slct__event_session_id != class:preset-tonal-secondary={slct__event_session_id !=
event_session_obj?.id} event_session_obj?.id}
class:border-secondary-500={slct__event_session_id != event_session_obj?.id} class:border-secondary-500={slct__event_session_id !=
class:font-bold={slct__event_session_id === event_session_obj?.id} event_session_obj?.id}
class:hidden={!$events_loc.launcher.show_content__hidden_sessions && class:font-bold={slct__event_session_id ===
event_session_obj?.id}
class:hidden={!$events_loc.launcher
.show_content__hidden_sessions &&
event_session_obj?.hide} event_session_obj?.hide}
class:dim={event_session_obj?.hide} class:dim={event_session_obj?.hide}
title={`Session: ${event_session_obj?.name}\nID: ${event_session_obj?.id} | ${ae_util.iso_datetime_formatter(event_session_obj?.start_datetime, $events_loc.launcher.time_format)}`} title={`Session: ${event_session_obj?.name}\nID: ${event_session_obj?.id} | ${ae_util.iso_datetime_formatter(event_session_obj?.start_datetime, $events_loc.launcher.time_format)}`}
@@ -226,7 +258,9 @@
<!-- hover:scale-115 --> <!-- hover:scale-115 -->
<!-- hover:z-index-10 --> <!-- hover:z-index-10 -->
<span class="border-r border-surface-400-600 pr-1 min-w-28"> <span
class="border-r border-surface-400-600 pr-1 min-w-28"
>
{#if slct__event_session_id === event_session_obj?.id} {#if slct__event_session_id === event_session_obj?.id}
<span class="fas fa-calendar-check"></span> <span class="fas fa-calendar-check"></span>
{:else} {:else}
@@ -234,7 +268,8 @@
{/if} {/if}
<span <span
class="text-xs" class="text-xs"
class:hidden={slct__event_session_id === event_session_obj?.id} class:hidden={slct__event_session_id ===
event_session_obj?.id}
> >
{ae_util.iso_datetime_formatter( {ae_util.iso_datetime_formatter(
event_session_obj?.start_datetime, event_session_obj?.start_datetime,
@@ -266,4 +301,4 @@
{:else} {:else}
<div class="text-sm">No sessions found.</div> <div class="text-sm">No sessions found.</div>
{/if} {/if}
</div> </div>