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:
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
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:
|
||||
* Default - What most users will see for demo purposes
|
||||
* 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
|
||||
|
||||
- Default - What most users will see for demo purposes
|
||||
- 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
|
||||
|
||||
@@ -9,66 +9,111 @@
|
||||
let { on_expand }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Launcher_Cfg_Section
|
||||
title="Display & App Modes"
|
||||
icon="fa-object-group"
|
||||
<Launcher_Cfg_Section
|
||||
title="Display & App Modes"
|
||||
icon="fa-object-group"
|
||||
bind:state={$events_loc.launcher.section_state__app_modes}
|
||||
{on_expand}
|
||||
description="Mode: {$events_loc.launcher.app_mode} | UI Layout"
|
||||
>
|
||||
<!-- Content omitted for brevity, preserved in file -->
|
||||
<div class="col-span-full flex flex-col gap-3">
|
||||
|
||||
<!-- 1. App Mode Selection -->
|
||||
<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">
|
||||
<button type="button"
|
||||
onclick={() => $events_loc.launcher.app_mode = 'default'}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => ($events_loc.launcher.app_mode = 'default')}
|
||||
class="btn btn-xs text-[9px] font-bold"
|
||||
class:preset-filled-primary-500={$events_loc.launcher.app_mode === 'default'}
|
||||
class:opacity-40={$events_loc.launcher.app_mode !== 'default'}
|
||||
>Web</button>
|
||||
<button type="button"
|
||||
onclick={() => $events_loc.launcher.app_mode = 'native'}
|
||||
class:preset-filled-primary-500={$events_loc.launcher
|
||||
.app_mode === 'default'}
|
||||
class:opacity-40={$events_loc.launcher.app_mode !==
|
||||
'default'}>Web</button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => ($events_loc.launcher.app_mode = 'native')}
|
||||
class="btn btn-xs text-[9px] font-bold"
|
||||
class:preset-filled-primary-500={$events_loc.launcher.app_mode === 'native'}
|
||||
class:opacity-40={$events_loc.launcher.app_mode !== 'native'}
|
||||
>App</button>
|
||||
<button type="button"
|
||||
onclick={() => $events_loc.launcher.app_mode = 'onsite'}
|
||||
class:preset-filled-primary-500={$events_loc.launcher
|
||||
.app_mode === 'native'}
|
||||
class:opacity-40={$events_loc.launcher.app_mode !==
|
||||
'native'}>App</button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => ($events_loc.launcher.app_mode = 'onsite')}
|
||||
class="btn btn-xs text-[9px] font-bold"
|
||||
class:preset-filled-primary-500={$events_loc.launcher.app_mode === 'onsite'}
|
||||
class:opacity-40={$events_loc.launcher.app_mode !== 'onsite'}
|
||||
>Onsite</button>
|
||||
class:preset-filled-primary-500={$events_loc.launcher
|
||||
.app_mode === 'onsite'}
|
||||
class:opacity-40={$events_loc.launcher.app_mode !==
|
||||
'onsite'}>Onsite</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. UI Layout Toggles -->
|
||||
<div 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="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">
|
||||
<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" />
|
||||
<span class="text-xs group-hover:text-primary-500">Hide Header</span>
|
||||
<input
|
||||
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 class="flex items-center gap-2 cursor-pointer group">
|
||||
<input 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>
|
||||
<input
|
||||
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 class="flex items-center gap-2 cursor-pointer group">
|
||||
<input 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>
|
||||
<input
|
||||
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 class="flex items-center gap-2 cursor-pointer group">
|
||||
<input 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>
|
||||
<input
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Time Format Toggle -->
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
if ($events_loc.launcher.time_format == 'time_12_short') {
|
||||
$events_loc.launcher.time_format = 'time_short';
|
||||
@@ -81,21 +126,42 @@
|
||||
class="btn btn-xs preset-tonal-surface w-full text-[10px]"
|
||||
>
|
||||
<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>
|
||||
|
||||
<!-- 4. Advanced Toggles (Edit Mode Only) -->
|
||||
{#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">
|
||||
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Technical Layout</label>
|
||||
<div
|
||||
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">
|
||||
<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" />
|
||||
<span class="text-xs group-hover:text-primary-500 italic">Hide WebSocket Debugger</span>
|
||||
<input
|
||||
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 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" />
|
||||
<span class="text-xs group-hover:text-primary-500 italic">Hide Poster Modal Title</span>
|
||||
<input
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,35 +8,49 @@
|
||||
}
|
||||
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>
|
||||
|
||||
<Launcher_Cfg_Section
|
||||
title="Remote Controller"
|
||||
icon="fa-gamepad"
|
||||
<Launcher_Cfg_Section
|
||||
title="Remote Controller"
|
||||
icon="fa-gamepad"
|
||||
bind:state={$events_loc.launcher.section_state__controller}
|
||||
{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 -->
|
||||
<div class="col-span-full flex flex-col gap-3">
|
||||
|
||||
<!-- 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">
|
||||
<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>
|
||||
{#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}
|
||||
<span class="badge variant-filled-error text-[8px]">Disconnected</span>
|
||||
<span class="badge variant-filled-error text-[8px]"
|
||||
>Disconnected</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- 2. Controller Mode Selection -->
|
||||
<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
|
||||
bind:value={$events_loc.launcher.controller}
|
||||
class="select select-sm text-xs preset-tonal-surface h-8"
|
||||
@@ -49,7 +63,8 @@
|
||||
|
||||
<!-- 3. Connection Actions -->
|
||||
<div class="grid grid-cols-2 gap-2 mt-1">
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
if ($events_loc.launcher.ws_connect) {
|
||||
$events_sess.launcher.trigger__ws_disconnect = true;
|
||||
@@ -70,7 +85,8 @@
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$events_sess.launcher.controller_cmd = 'ae_refresh:now';
|
||||
$events_sess.launcher.controller_trigger_send = 'trigger';
|
||||
@@ -84,25 +100,43 @@
|
||||
|
||||
<!-- 4. Technical Config (Edit Mode Only) -->
|
||||
{#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">
|
||||
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">Channel Configuration</label>
|
||||
<div
|
||||
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">
|
||||
<input
|
||||
bind:value={$events_loc.launcher.controller_group_code}
|
||||
placeholder="Group Code"
|
||||
class="input input-sm grow text-[10px] h-7 preset-tonal-surface font-mono"
|
||||
readonly={!$events_sess.launcher.controller_unlock_group_code}
|
||||
ondblclick={() => $events_sess.launcher.controller_unlock_group_code = true}
|
||||
readonly={!$events_sess.launcher
|
||||
.controller_unlock_group_code}
|
||||
ondblclick={() =>
|
||||
($events_sess.launcher.controller_unlock_group_code = true)}
|
||||
/>
|
||||
<button type="button"
|
||||
onclick={() => $events_sess.launcher.controller_unlock_group_code = !$events_sess.launcher.controller_unlock_group_code}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() =>
|
||||
($events_sess.launcher.controller_unlock_group_code =
|
||||
!$events_sess.launcher
|
||||
.controller_unlock_group_code)}
|
||||
class="btn btn-xs preset-tonal-surface"
|
||||
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>
|
||||
</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>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
let ram_usage_pct = $derived.by(() => {
|
||||
const meta = $ae_loc.native_device?.meta_json;
|
||||
if (!meta?.total_mem || !meta?.free_mem) return 0;
|
||||
|
||||
|
||||
// Parse "16384MB" strings
|
||||
const total = parseInt(meta.total_mem);
|
||||
const free = parseInt(meta.free_mem);
|
||||
if (isNaN(total) || isNaN(free)) return 0;
|
||||
|
||||
|
||||
return Math.round(((total - free) / total) * 100);
|
||||
});
|
||||
|
||||
@@ -29,39 +29,61 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Launcher_Cfg_Section
|
||||
title="System & Sync Health"
|
||||
icon="fa-heartbeat"
|
||||
<Launcher_Cfg_Section
|
||||
title="System & Sync Health"
|
||||
icon="fa-heartbeat"
|
||||
bind:state={$events_loc.launcher.section_state__health}
|
||||
{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 -->
|
||||
<!-- 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) -->
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex justify-between text-[9px] uppercase font-bold opacity-60">
|
||||
<span>CPU Architecture: {$ae_loc.native_device?.meta_json?.arch || '...'}</span>
|
||||
<div
|
||||
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>
|
||||
</div>
|
||||
<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
|
||||
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>
|
||||
|
||||
<!-- RAM Usage -->
|
||||
<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>{ram_usage_pct}% Used</span>
|
||||
</div>
|
||||
<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
|
||||
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 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>
|
||||
@@ -69,26 +91,45 @@
|
||||
<!-- Heartbeat & Sync Info -->
|
||||
<div class="grid grid-cols-2 gap-x-2 gap-y-2 w-full text-[10px] p-1">
|
||||
<div class="flex flex-col">
|
||||
<span class="opacity-50 text-[8px] uppercase font-bold">Last Heartbeat</span>
|
||||
<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 class="opacity-50 text-[8px] uppercase font-bold"
|
||||
>Last Heartbeat</span
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
|
||||
<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">
|
||||
{$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>
|
||||
</div>
|
||||
|
||||
{#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">
|
||||
<span class="fas fa-sync fa-spin text-primary-500"></span>
|
||||
<div class="flex flex-col truncate">
|
||||
<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>
|
||||
<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>
|
||||
@@ -97,18 +138,27 @@
|
||||
|
||||
<!-- Device Metadata (Edit Mode Only) -->
|
||||
{#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">
|
||||
<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 class="flex justify-between gap-4">
|
||||
<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 class="mt-2 opacity-40">
|
||||
<label class="text-[8px] uppercase font-bold">Raw Device JSON</label>
|
||||
<pre class="text-[7px] max-h-32 overflow-y-auto bg-black/20 p-1 rounded mt-1">
|
||||
<label class="text-[8px] uppercase font-bold"
|
||||
>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)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
@@ -12,21 +12,33 @@
|
||||
|
||||
function handle_reset_action(val: string) {
|
||||
if (!val) return;
|
||||
|
||||
|
||||
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_core_db');
|
||||
indexedDB.deleteDatabase('ae_events_db');
|
||||
indexedDB.deleteDatabase('ae_journals_db');
|
||||
indexedDB.deleteDatabase('ae_posts_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') {
|
||||
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');
|
||||
alert('Events IndexedDB database deleted. Please reload the app.');
|
||||
alert(
|
||||
'Events IndexedDB database deleted. Please reload the app.'
|
||||
);
|
||||
}
|
||||
} else if (val == 'delete_local') {
|
||||
if (confirm('Are you sure you want to delete ALL local config?')) {
|
||||
@@ -37,7 +49,11 @@
|
||||
location.reload();
|
||||
}
|
||||
} 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');
|
||||
location.reload();
|
||||
}
|
||||
@@ -46,28 +62,34 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Launcher_Cfg_Section
|
||||
title="Local Reset & Actions"
|
||||
icon="fa-tools"
|
||||
<Launcher_Cfg_Section
|
||||
title="Local Reset & Actions"
|
||||
icon="fa-tools"
|
||||
bind:state={$events_loc.launcher.section_state__local_actions}
|
||||
{on_expand}
|
||||
description="Cache wiping and global menu toggles"
|
||||
>
|
||||
<div class="col-span-full flex flex-col gap-3">
|
||||
|
||||
<!-- 1. Reset Actions -->
|
||||
<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
|
||||
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"
|
||||
>
|
||||
<option value="">-- Select a reset action --</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_events">Wipe Events Storage Only</option>
|
||||
<option value="delete_local_events"
|
||||
>Wipe Events Storage Only</option
|
||||
>
|
||||
</select>
|
||||
<span class="text-[8px] opacity-40 italic ml-1 leading-tight">
|
||||
* Destructive actions require browser confirmation.
|
||||
@@ -76,31 +98,48 @@
|
||||
|
||||
<!-- 2. UI Toggles -->
|
||||
<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)}
|
||||
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
|
||||
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
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
onclick={() => ($ae_loc.debug_menu.hide = !$ae_loc.debug_menu.hide)}
|
||||
<button
|
||||
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"
|
||||
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
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 3. Connection Summary (Edit Mode Only) -->
|
||||
{#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">
|
||||
<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/>
|
||||
<div
|
||||
class="col-span-full border-t border-surface-500/20 pt-2 mt-1 flex flex-col gap-1"
|
||||
>
|
||||
<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}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,15 +14,20 @@
|
||||
let remote_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}...`;
|
||||
const res = await native.control_presentation({ app: remote_app, action });
|
||||
const res = await native.control_presentation({
|
||||
app: remote_app,
|
||||
action
|
||||
});
|
||||
if (res.success) {
|
||||
remote_status = `Success: ${action}`;
|
||||
} else {
|
||||
remote_status = `Error: ${res.error}`;
|
||||
}
|
||||
setTimeout(() => remote_status = '', 3000);
|
||||
setTimeout(() => (remote_status = ''), 3000);
|
||||
}
|
||||
|
||||
async function handle_system_action(promise: Promise<any>, label: string) {
|
||||
@@ -33,51 +38,67 @@
|
||||
} else {
|
||||
system_status = `Error: ${res.error || 'Unknown error'}`;
|
||||
}
|
||||
setTimeout(() => system_status = '', 3000);
|
||||
setTimeout(() => (system_status = ''), 3000);
|
||||
}
|
||||
|
||||
// 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>
|
||||
|
||||
<Launcher_Cfg_Section
|
||||
title="Native OS Management"
|
||||
icon="fa-laptop-code"
|
||||
<Launcher_Cfg_Section
|
||||
title="Native OS Management"
|
||||
icon="fa-laptop-code"
|
||||
bind:state={$events_loc.launcher.section_state__native_os}
|
||||
{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 -->
|
||||
{#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}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- 1. Window & Folders (Common) -->
|
||||
<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">
|
||||
<button type="button"
|
||||
onclick={() => native.open_folder($ae_loc.local_file_cache_path)}
|
||||
<button
|
||||
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"
|
||||
>
|
||||
<span class="fas fa-folder-open mr-2 w-3"></span> Cache
|
||||
</button>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => native.open_folder($ae_loc.host_file_temp_path)}
|
||||
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
|
||||
</button>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => native.window_control({ action: 'maximize' })}
|
||||
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
|
||||
>
|
||||
<span class="fas fa-expand mr-1"></span> Maximize
|
||||
</button>
|
||||
<button type="button"
|
||||
onclick={() => handle_system_action(native.window_control({ action: 'kiosk', value: true }), 'Kiosk Mode')}
|
||||
<button
|
||||
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"
|
||||
>
|
||||
<span class="fas fa-desktop mr-1"></span> Kiosk
|
||||
@@ -88,47 +109,94 @@
|
||||
<!-- 2. Presentation Remote Control (Common) -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-row justify-between items-center px-1">
|
||||
<label class="text-[9px] font-bold uppercase opacity-50">Remote Control</label>
|
||||
<select bind:value={remote_app} class="select select-sm py-0 h-5 text-[9px] w-24 preset-tonal-surface">
|
||||
<label class="text-[9px] font-bold uppercase opacity-50"
|
||||
>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="keynote">Keynote</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</button>
|
||||
</div>
|
||||
{#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}
|
||||
</div>
|
||||
|
||||
<!-- 3. Technical Management (Edit Mode Only) -->
|
||||
{#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="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">
|
||||
<button type="button"
|
||||
onclick={() => handle_system_action(native.set_display_layout({ mode: 'extend' }), 'Extend Display')}
|
||||
<button
|
||||
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"
|
||||
>
|
||||
<span class="fas fa-columns mr-2 w-3"></span> Extend Mode
|
||||
</button>
|
||||
<button type="button"
|
||||
onclick={() => handle_system_action(native.set_wallpaper({ path: $ae_loc.site_header_image_path }), 'Wallpaper')}
|
||||
<button
|
||||
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"
|
||||
disabled={!$ae_loc.site_header_image_path}
|
||||
>
|
||||
@@ -137,16 +205,29 @@
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<button type="button"
|
||||
onclick={() => show_power_confirm = { action: 'reboot', label: 'Reboot Laptop' }}
|
||||
<button
|
||||
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"
|
||||
>
|
||||
<span class="fas fa-sync-alt mr-2 w-3"></span> Reboot
|
||||
</button>
|
||||
<button type="button"
|
||||
onclick={() => show_power_confirm = { action: 'shutdown', label: 'Shutdown Laptop' }}
|
||||
<button
|
||||
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"
|
||||
>
|
||||
<span class="fas fa-power-off mr-2 w-3"></span> Shutdown
|
||||
@@ -156,25 +237,36 @@
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={$events_sess.launcher.manual_cmd}
|
||||
<input
|
||||
type="text"
|
||||
bind:value={$events_sess.launcher.manual_cmd}
|
||||
placeholder="ls -la"
|
||||
class="input input-sm grow text-[10px] preset-tonal-surface h-7"
|
||||
/>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={async () => {
|
||||
test_cmd_result = 'Running...';
|
||||
const res = await native.run_cmd({ cmd: $events_sess.launcher.manual_cmd || 'uptime' });
|
||||
test_cmd_result = (res as any).stdout || (res as any).error || 'No Output';
|
||||
const res = await native.run_cmd({
|
||||
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"
|
||||
>Run</button>
|
||||
>Run</button
|
||||
>
|
||||
</div>
|
||||
{#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}
|
||||
</div>
|
||||
</div>
|
||||
@@ -183,20 +275,37 @@
|
||||
|
||||
<!-- Power Confirmation Modal -->
|
||||
{#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 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>
|
||||
<div
|
||||
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-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">
|
||||
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>
|
||||
<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 type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (show_power_confirm = null)}
|
||||
class="btn btn-sm preset-tonal-surface">Cancel</button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
const action = show_power_confirm?.action;
|
||||
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"
|
||||
>
|
||||
Confirm {show_power_confirm.action}
|
||||
|
||||
@@ -9,59 +9,71 @@
|
||||
let { on_expand }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Launcher_Cfg_Section
|
||||
title="Digital Screen Saver"
|
||||
icon="fa-id-badge"
|
||||
<Launcher_Cfg_Section
|
||||
title="Digital Screen Saver"
|
||||
icon="fa-id-badge"
|
||||
bind:state={$events_loc.launcher.section_state__screen_saver}
|
||||
{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 -->
|
||||
<div class="col-span-full flex flex-col gap-3">
|
||||
|
||||
<!-- 1. Technical Timers (Edit Mode Only) -->
|
||||
{#if $ae_loc.edit_mode}
|
||||
<div class="flex flex-col gap-2">
|
||||
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">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">
|
||||
<label class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
||||
>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">
|
||||
<span class="text-[10px] opacity-60">Idle Wait</span>
|
||||
<input
|
||||
type="number"
|
||||
<input
|
||||
type="number"
|
||||
min={3000}
|
||||
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 class="flex justify-between items-center gap-4">
|
||||
<span class="text-[10px] opacity-60">Cycle Check</span>
|
||||
<input
|
||||
type="number"
|
||||
<input
|
||||
type="number"
|
||||
min={500}
|
||||
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 class="flex justify-between items-center gap-4">
|
||||
<span class="text-[10px] opacity-60">Image Rotation</span>
|
||||
<input
|
||||
type="number"
|
||||
<span class="text-[10px] opacity-60"
|
||||
>Image Rotation</span
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
min={750}
|
||||
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>
|
||||
{:else}
|
||||
<!-- 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">
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -13,7 +13,15 @@
|
||||
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() {
|
||||
if (state === 'collapsed') {
|
||||
@@ -40,40 +48,66 @@
|
||||
</script>
|
||||
|
||||
<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 -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<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' : '' }"
|
||||
<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'
|
||||
: ''}"
|
||||
onclick={toggle_expand}
|
||||
>
|
||||
<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">
|
||||
<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}
|
||||
<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}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- Pin Toggle -->
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={toggle_pin}
|
||||
class="btn btn-icon btn-xs transition-all hover:scale-110"
|
||||
class:opacity-20={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>
|
||||
</button>
|
||||
|
||||
<!-- Collapse Icon -->
|
||||
<span class="fas transition-transform duration-300 opacity-30"
|
||||
class:fa-chevron-right={!is_open}
|
||||
<span
|
||||
class="fas transition-transform duration-300 opacity-30"
|
||||
class:fa-chevron-right={!is_open}
|
||||
class:fa-chevron-down={is_open}
|
||||
class:rotate-180={is_open}
|
||||
></span>
|
||||
@@ -82,19 +116,29 @@
|
||||
|
||||
<!-- Content -->
|
||||
{#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}
|
||||
<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>
|
||||
{#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}
|
||||
</div>
|
||||
{/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?.()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,63 +9,92 @@
|
||||
let { on_expand }: Props = $props();
|
||||
</script>
|
||||
|
||||
<Launcher_Cfg_Section
|
||||
title="Sync Engine & Timers"
|
||||
icon="fa-sync"
|
||||
<Launcher_Cfg_Section
|
||||
title="Sync Engine & Timers"
|
||||
icon="fa-sync"
|
||||
bind:state={$events_loc.launcher.section_state__sync_timers}
|
||||
{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 -->
|
||||
{#if $ae_loc.native_device}
|
||||
<div class="grid grid-cols-1 gap-3">
|
||||
|
||||
<!-- Technical Timers (Edit Mode Only) -->
|
||||
{#if $ae_loc.edit_mode}
|
||||
<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="flex flex-col gap-1">
|
||||
<span class="text-[8px] opacity-60">Event Data</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={$ae_loc.native_device.check_event_loop_period}
|
||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
||||
<span class="text-[8px] opacity-60">Event Data</span
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={
|
||||
$ae_loc.native_device
|
||||
.check_event_loop_period
|
||||
}
|
||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-[8px] opacity-60">Device Config</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={$ae_loc.native_device.check_event_device_loop_period}
|
||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
||||
<span class="text-[8px] opacity-60"
|
||||
>Device Config</span
|
||||
>
|
||||
<input
|
||||
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 class="flex flex-col gap-1">
|
||||
<span class="text-[8px] opacity-60">Room/Location</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={$ae_loc.native_device.check_event_location_loop_period}
|
||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
||||
<span class="text-[8px] opacity-60"
|
||||
>Room/Location</span
|
||||
>
|
||||
<input
|
||||
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 class="flex flex-col gap-1">
|
||||
<span class="text-[8px] opacity-60">Session Loop</span>
|
||||
<input
|
||||
type="number"
|
||||
bind:value={$ae_loc.native_device.check_event_session_loop_period}
|
||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
||||
<span class="text-[8px] opacity-60"
|
||||
>Session Loop</span
|
||||
>
|
||||
<input
|
||||
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 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 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">
|
||||
<span class="text-[9px]">Hash Prefix Length</span>
|
||||
<select
|
||||
bind:value={$ae_loc.native_device.hash_prefix_length}
|
||||
<select
|
||||
bind:value={
|
||||
$ae_loc.native_device.hash_prefix_length
|
||||
}
|
||||
class="select select-sm h-6 py-0 text-[10px] w-16 preset-tonal-surface"
|
||||
>
|
||||
<option value={1}>1 char</option>
|
||||
@@ -73,26 +102,51 @@
|
||||
<option value={3}>3 chars</option>
|
||||
</select>
|
||||
</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>
|
||||
{:else}
|
||||
<!-- 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 class="flex justify-between text-[9px] opacity-60 font-mono">
|
||||
<div
|
||||
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>{($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 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>{($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 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>{$ae_loc.native_device.hash_prefix_length || 2} chars</span>
|
||||
<span
|
||||
>{$ae_loc.native_device.hash_prefix_length || 2} chars</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -25,46 +25,52 @@
|
||||
async function handle_test_action(label: string) {
|
||||
is_loading = true;
|
||||
action_status = `Executing ${label}...`;
|
||||
|
||||
|
||||
// Simulate async work
|
||||
await new Promise(r => setTimeout(r, 1500));
|
||||
|
||||
await new Promise((r) => setTimeout(r, 1500));
|
||||
|
||||
is_loading = false;
|
||||
action_status = `Finished: ${label}`;
|
||||
setTimeout(() => action_status = '', 3000);
|
||||
setTimeout(() => (action_status = ''), 3000);
|
||||
}
|
||||
|
||||
// Modal state for destructive actions
|
||||
let show_confirm = $state(false);
|
||||
</script>
|
||||
|
||||
<Launcher_Cfg_Section
|
||||
title="Template Section"
|
||||
icon="fa-cubes"
|
||||
<Launcher_Cfg_Section
|
||||
title="Template Section"
|
||||
icon="fa-cubes"
|
||||
bind:state={$events_loc.launcher.section_state__template}
|
||||
{on_expand}
|
||||
description="Kitchen Sink Scaffold | Demo Only"
|
||||
>
|
||||
<!-- A. TOP STATUS BAR (Optional) -->
|
||||
{#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}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- B. COMMON GRID SECTION (Read Only / High Level) -->
|
||||
<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 -->
|
||||
<div class="grid grid-cols-2 gap-1">
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handle_test_action('Primary')}
|
||||
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
|
||||
</button>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handle_test_action('Secondary')}
|
||||
class="btn btn-xs preset-tonal-secondary hover:preset-filled-secondary-500 justify-start"
|
||||
>
|
||||
@@ -75,39 +81,68 @@
|
||||
<!-- Toggles & Checkboxes -->
|
||||
<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">
|
||||
<input 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>
|
||||
<input
|
||||
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 class="flex items-center gap-2 cursor-pointer group">
|
||||
<input type="radio" name="demo" value="a" class="radio radio-sm" />
|
||||
<span class="text-xs group-hover:text-primary-500 transition-colors">Mode A</span>
|
||||
<input
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- C. STATUS & GAUGES SECTION -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<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">
|
||||
<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 justify-between items-center">
|
||||
<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>
|
||||
|
||||
|
||||
<!-- Progress / Gauge Example -->
|
||||
<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>45%</span>
|
||||
</div>
|
||||
<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
|
||||
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>
|
||||
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => handle_test_action('Refresh')}
|
||||
class="btn btn-xs preset-outlined-surface-500 w-full text-[10px]"
|
||||
>
|
||||
@@ -118,22 +153,34 @@
|
||||
|
||||
<!-- D. TECHNICAL SECTION (Edit Mode Only) -->
|
||||
{#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 -->
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label class="text-[9px] font-bold uppercase opacity-50 text-warning-500">System Config</label>
|
||||
<button type="button"
|
||||
onclick={() => show_confirm = true}
|
||||
<label
|
||||
class="text-[9px] font-bold uppercase opacity-50 text-warning-500"
|
||||
>System Config</label
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (show_confirm = true)}
|
||||
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>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label 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">
|
||||
<label
|
||||
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
|
||||
</button>
|
||||
</div>
|
||||
@@ -142,35 +189,46 @@
|
||||
<!-- Form Inputs -->
|
||||
<div class="grid grid-cols-1 gap-2">
|
||||
<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">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={text_input}
|
||||
<input
|
||||
type="text"
|
||||
bind:value={text_input}
|
||||
placeholder="Enter string parameter..."
|
||||
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="option2">Local</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-[8px] opacity-60 ml-1">Threshold (ms)</span>
|
||||
<input
|
||||
type="number"
|
||||
<span class="text-[8px] opacity-60 ml-1"
|
||||
>Threshold (ms)</span
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
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>
|
||||
|
||||
<!-- Terminal / Output Log -->
|
||||
<div class="flex flex-col gap-1">
|
||||
<label class="text-[9px] font-bold uppercase opacity-50 ml-1">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">
|
||||
<label class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
||||
>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
|
||||
[INFO] Store synced with IndexedDB
|
||||
[DEBUG] active_tab: template
|
||||
@@ -183,16 +241,29 @@
|
||||
|
||||
<!-- Confirmation Modal Demo -->
|
||||
{#if show_confirm}
|
||||
<div 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">
|
||||
<div
|
||||
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>
|
||||
<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>
|
||||
<div class="flex justify-end gap-2">
|
||||
<button type="button" onclick={() => show_confirm = false} class="btn btn-sm preset-tonal-surface">Cancel</button>
|
||||
<button type="button"
|
||||
onclick={() => { show_confirm = false; handle_test_action('Confirm'); }}
|
||||
<button
|
||||
type="button"
|
||||
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"
|
||||
>
|
||||
Yes, Proceed
|
||||
|
||||
@@ -10,9 +10,13 @@
|
||||
let { on_expand }: Props = $props();
|
||||
|
||||
let update_source: 'url' | 'file' = $state('file');
|
||||
let update_path = $state('~/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_path = $state(
|
||||
'~/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 is_checking = $state(false);
|
||||
let download_result = $state<any>(null);
|
||||
@@ -20,14 +24,15 @@
|
||||
async function handle_check_update() {
|
||||
is_checking = true;
|
||||
update_status = 'Checking for updates...';
|
||||
|
||||
|
||||
try {
|
||||
const args = update_source === 'url'
|
||||
? { source: 'url' as const, url: update_url }
|
||||
: { source: 'file' as const, path: update_path };
|
||||
|
||||
const args =
|
||||
update_source === 'url'
|
||||
? { source: 'url' as const, url: update_url }
|
||||
: { source: 'file' as const, path: update_path };
|
||||
|
||||
const res = await native.update_app(args);
|
||||
|
||||
|
||||
if (res.success) {
|
||||
download_result = res;
|
||||
update_status = 'Update located/downloaded. Ready to install.';
|
||||
@@ -43,45 +48,60 @@
|
||||
|
||||
async function handle_install() {
|
||||
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>
|
||||
|
||||
<Launcher_Cfg_Section
|
||||
title="Application Updates"
|
||||
icon="fa-cloud-download-alt"
|
||||
<Launcher_Cfg_Section
|
||||
title="Application Updates"
|
||||
icon="fa-cloud-download-alt"
|
||||
bind:state={$events_loc.launcher.section_state__updates}
|
||||
{on_expand}
|
||||
description="v1.0.0 | Source: {update_source}"
|
||||
>
|
||||
<!-- Content omitted for brevity, preserved in file -->
|
||||
<div class="col-span-full flex flex-col gap-2">
|
||||
|
||||
<!-- TECHNICAL: Source Config (Edit Mode Only) -->
|
||||
{#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">
|
||||
<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">
|
||||
<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 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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if update_source === 'file'}
|
||||
<input
|
||||
type="text"
|
||||
<input
|
||||
type="text"
|
||||
bind:value={update_path}
|
||||
placeholder="Path to update package"
|
||||
class="input input-sm text-[10px] preset-tonal-surface h-7 w-full"
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
<input
|
||||
type="text"
|
||||
bind:value={update_url}
|
||||
placeholder="URL to update package"
|
||||
class="input input-sm text-[10px] preset-tonal-surface h-7 w-full"
|
||||
@@ -91,7 +111,8 @@
|
||||
{/if}
|
||||
|
||||
<!-- COMMON: Check Button -->
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={handle_check_update}
|
||||
disabled={is_checking}
|
||||
class="btn btn-sm preset-filled-tertiary hover:preset-filled-primary-500 text-[10px] w-full"
|
||||
@@ -104,13 +125,16 @@
|
||||
</button>
|
||||
|
||||
{#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}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if download_result}
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
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"
|
||||
>
|
||||
|
||||
@@ -58,9 +58,7 @@
|
||||
// *** Set initial variables
|
||||
let ae_acct = data[$slct.account_id];
|
||||
|
||||
import {
|
||||
online
|
||||
} from 'svelte/reactivity/window';
|
||||
import { online } from 'svelte/reactivity/window';
|
||||
|
||||
$ae_sess.disable_sys_nav = true;
|
||||
$ae_sess.disable_sys_header = true;
|
||||
@@ -80,7 +78,9 @@
|
||||
if (log_lvl) {
|
||||
console.log(`event_id: ${data.params.event_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_location_id = data.params.event_location_id;
|
||||
@@ -89,11 +89,19 @@
|
||||
// String-Only ID Vision: Sync the device ID from the native environment
|
||||
const native_dev = $ae_loc.native_device;
|
||||
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.id_li__event_location = ae_acct.slct.id_li__event_location ?? [''];
|
||||
$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 ?? [
|
||||
''
|
||||
];
|
||||
|
||||
// *** Functions and Logic
|
||||
|
||||
@@ -103,9 +111,12 @@
|
||||
if (!id) return null;
|
||||
if (log_lvl > 1) console.log(`lq__event_obj: event_id = ${id}`);
|
||||
let results = await db_events.event.get(id);
|
||||
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@@ -118,7 +129,10 @@
|
||||
if (!id) return null;
|
||||
let results = await db_events.device.get(id);
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@@ -129,14 +143,20 @@
|
||||
let lq__event_event_file_obj_li = liveQuery(async () => {
|
||||
const id = $events_slct.event_id;
|
||||
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
|
||||
let lq__location_event_file_obj_li = liveQuery(async () => {
|
||||
const id = $events_slct.event_location_id;
|
||||
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
|
||||
@@ -149,7 +169,10 @@
|
||||
let lq__event_location_obj_li = liveQuery(async () => {
|
||||
const id = $events_slct.event_id;
|
||||
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)
|
||||
@@ -157,24 +180,37 @@
|
||||
let lq__event_session_obj = liveQuery(async () => {
|
||||
const id = $events_slct.event_session_id;
|
||||
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();
|
||||
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;
|
||||
});
|
||||
|
||||
let lq__event_session_obj_li = liveQuery(async () => {
|
||||
const id = $events_slct.event_location_id;
|
||||
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
|
||||
.where('event_location_id')
|
||||
.equals(id)
|
||||
.reverse()
|
||||
.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 || [])];
|
||||
}
|
||||
return results;
|
||||
@@ -210,7 +246,10 @@
|
||||
$events_slct.event_session_id = obj_id;
|
||||
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.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 });
|
||||
}
|
||||
} 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_cycle) $events_loc.launcher.idle_cycle = 5 * 1000;
|
||||
if (!$events_loc.launcher.idle_loop_period) $events_loc.launcher.idle_loop_period = 3 * 60 * 1000;
|
||||
if (!$events_loc.launcher.idle_timer)
|
||||
$events_loc.launcher.idle_timer = 5 * 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({
|
||||
timer: $events_loc.launcher.idle_timer,
|
||||
@@ -296,26 +338,43 @@
|
||||
let saver_looping: boolean = $state(false);
|
||||
|
||||
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;
|
||||
saver_looping = true;
|
||||
|
||||
idle_timer_interval = setInterval(() => {
|
||||
if ($events_loc.launcher.screen_saver_img_kv) {
|
||||
const keys = Object.keys($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]];
|
||||
idle_timer_interval = setInterval(
|
||||
() => {
|
||||
if ($events_loc.launcher.screen_saver_img_kv) {
|
||||
const keys = Object.keys(
|
||||
$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_obj = event_file_obj;
|
||||
$events_sess.launcher.modal__open_event_file_id = null;
|
||||
$events_sess.launcher.modal__title = event_file_obj.filename ?? '*';
|
||||
$events_sess.launcher.modal__open_event_file_id = $events_slct.event_file_id;
|
||||
$events_sess.launcher.modal__event_file_obj = event_file_obj;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, $events_loc.launcher.idle_loop_period ?? 2 * 60 * 1000);
|
||||
$events_slct.event_file_id =
|
||||
event_file_obj.event_file_id;
|
||||
$events_slct.event_file_obj = event_file_obj;
|
||||
$events_sess.launcher.modal__open_event_file_id = null;
|
||||
$events_sess.launcher.modal__title =
|
||||
event_file_obj.filename ?? '*';
|
||||
$events_sess.launcher.modal__open_event_file_id =
|
||||
$events_slct.event_file_id;
|
||||
$events_sess.launcher.modal__event_file_obj =
|
||||
event_file_obj;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
$events_loc.launcher.idle_loop_period ?? 2 * 60 * 1000
|
||||
);
|
||||
} else {
|
||||
saver_looping = false;
|
||||
return false;
|
||||
@@ -376,7 +435,8 @@
|
||||
"
|
||||
>
|
||||
<h3 class="h4 text-center italic text-surface-600-400">
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
class=""
|
||||
onclick={() => {
|
||||
$events_loc.launcher.hide__launcher_menu =
|
||||
@@ -387,20 +447,26 @@
|
||||
<Satellite class="text-base mx-1 inline-block text-gray-500" />
|
||||
<abbr title="Aether - Events Module 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>
|
||||
</button>
|
||||
</h3>
|
||||
|
||||
{#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}
|
||||
</h2>
|
||||
<h3
|
||||
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}"
|
||||
>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
class="text-base"
|
||||
onclick={() => {
|
||||
$ae_loc.edit_mode = !$ae_loc.edit_mode;
|
||||
@@ -455,9 +521,13 @@
|
||||
{lq__event_location_obj_li}
|
||||
{lq__event_location_obj}
|
||||
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}
|
||||
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}
|
||||
bind:slct__event_session_id={$events_slct.event_session_id}
|
||||
bind:trigger_reload__event_session_obj_id={
|
||||
@@ -487,7 +557,9 @@
|
||||
"
|
||||
>
|
||||
{#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>Please select a location from the menu</span>
|
||||
</div>
|
||||
@@ -500,7 +572,9 @@
|
||||
bind:type_code={$lq__event_session_obj.type_code}
|
||||
></Launcher_session_view>
|
||||
{: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" />
|
||||
<span>Loading session details...</span>
|
||||
</div>
|
||||
@@ -537,7 +611,8 @@
|
||||
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}"
|
||||
>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
class=""
|
||||
onclick={() => {
|
||||
$ae_loc.edit_mode = !$ae_loc.edit_mode;
|
||||
@@ -579,10 +654,13 @@
|
||||
|
||||
<span
|
||||
class:hidden={!$events_loc.launcher.ws_connect}
|
||||
class:preset-tonal-warning={$events_sess.launcher.ws_connect_status != 'connected'}
|
||||
class:preset-tonal-success={$events_sess.launcher.ws_connect_status == 'connected'}
|
||||
class:preset-tonal-warning={$events_sess.launcher.ws_connect_status !=
|
||||
'connected'}
|
||||
class:preset-tonal-success={$events_sess.launcher.ws_connect_status ==
|
||||
'connected'}
|
||||
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'
|
||||
: 'disconnected'} API: {$ae_api?.base_url}"
|
||||
>
|
||||
@@ -595,7 +673,9 @@
|
||||
{/if}
|
||||
</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="fas fa-calendar-alt"></span>
|
||||
{ae_util.iso_datetime_formatter($time, 'date_full_no_year')}
|
||||
@@ -612,7 +692,8 @@
|
||||
</footer>
|
||||
|
||||
<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)}
|
||||
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}
|
||||
@@ -639,7 +720,9 @@
|
||||
|
||||
<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
|
||||
href="/events/{$events_slct.event_id}"
|
||||
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500"
|
||||
@@ -682,10 +765,13 @@
|
||||
id="sidebar2"
|
||||
>
|
||||
<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
|
||||
</h2>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => ($events_loc.launcher.hide_drawer__debug = true)}
|
||||
class="mb-4 dark:text-white"
|
||||
>
|
||||
@@ -729,7 +815,8 @@
|
||||
>
|
||||
{$events_sess.launcher?.modal__title ?? 'Digital Poster Display'}
|
||||
</h3>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
class="btn flex-row-reverse group transition-all justify-self-end"
|
||||
onclick={() => {
|
||||
$events_sess.launcher.modal__open_event_file_id = null;
|
||||
@@ -741,7 +828,8 @@
|
||||
</button>
|
||||
{/snippet}
|
||||
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$events_sess.launcher.controller_cmd = `ae_close:event_file_modal`;
|
||||
$events_sess.launcher.controller_trigger_send = true;
|
||||
@@ -766,8 +854,8 @@
|
||||
{#if $events_sess.launcher.modal__open_event_file_id}
|
||||
<img
|
||||
src="{$ae_api.base_url}/v3/action/event_file/{$events_sess.launcher
|
||||
.modal__open_event_file_id}/download?filename={$events_slct.event_file_obj
|
||||
.filename}&key={$ae_api.account_id}"
|
||||
.modal__open_event_file_id}/download?filename={$events_slct
|
||||
.event_file_obj.filename}&key={$ae_api.account_id}"
|
||||
alt="Poster"
|
||||
class="min-h-28 min-w-md max-h-full max-w-full"
|
||||
/>
|
||||
@@ -778,7 +866,8 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$events_sess.launcher.controller_cmd = `ae_close:event_file_modal`;
|
||||
$events_sess.launcher.controller_trigger_send = true;
|
||||
@@ -800,7 +889,8 @@
|
||||
Close Remote Poster Display Only
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$events_sess.launcher.modal__title = '';
|
||||
$events_sess.launcher.modal__open_event_file_id = null;
|
||||
@@ -846,4 +936,4 @@
|
||||
bind:ws_recv_status={trigger_handle_ws_recv}
|
||||
bind:ws_sent_status={trigger_handle_ws_sent}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -17,7 +17,9 @@ export async function load({ params, parent, url }) {
|
||||
let ae_acct = data[account_id];
|
||||
|
||||
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 = {
|
||||
api: data.ae_api || {},
|
||||
slct: {
|
||||
@@ -30,10 +32,13 @@ export async function load({ params, parent, url }) {
|
||||
const event_id = params.event_id;
|
||||
|
||||
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'.
|
||||
// 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.
|
||||
events_func.load_ae_obj_li__event_device({
|
||||
api_cfg: ae_acct.api,
|
||||
@@ -51,4 +56,4 @@ export async function load({ params, parent, url }) {
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +49,21 @@
|
||||
$events_slct.event_id = data.params.event_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.id_li__event_location = ae_acct.slct.id_li__event_location ?? [''];
|
||||
$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 ?? [
|
||||
''
|
||||
];
|
||||
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
|
||||
if (!$events_loc.launcher) {
|
||||
@@ -135,7 +144,8 @@
|
||||
|
||||
ni_data['name'] = network_interface;
|
||||
|
||||
let ni_details = result.networkInterfaces[network_interface];
|
||||
let ni_details =
|
||||
result.networkInterfaces[network_interface];
|
||||
// console.log(ni_details);
|
||||
|
||||
for (let i = 0; i < ni_details.length; i++) {
|
||||
@@ -155,12 +165,13 @@
|
||||
event_device_data['info_ip_list'] = ip_address_li.join('; ');
|
||||
console.log(event_device_data['info_ip_list']);
|
||||
|
||||
let event_device_obj_up_promise = events_func.update_ae_obj__event_device({
|
||||
api_cfg: $ae_api,
|
||||
event_device_id: event_device_id,
|
||||
data_kv: event_device_data,
|
||||
log_lvl: 0
|
||||
})
|
||||
let event_device_obj_up_promise = events_func
|
||||
.update_ae_obj__event_device({
|
||||
api_cfg: $ae_api,
|
||||
event_device_id: event_device_id,
|
||||
data_kv: event_device_data,
|
||||
log_lvl: 0
|
||||
})
|
||||
.then(function (up_event_device_result) {
|
||||
// console.log('UPDATED DEVICE INFO!!! UPDATED DEVICE INFO!!! UPDATED DEVICE INFO!!!');
|
||||
if (up_event_device_result) {
|
||||
@@ -177,6 +188,4 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="hidden">
|
||||
This is for forcing data loading.
|
||||
</div>
|
||||
<div class="hidden">This is for forcing data loading.</div>
|
||||
|
||||
@@ -17,7 +17,9 @@ export async function load({ params, parent, url }) {
|
||||
let ae_acct = data[account_id];
|
||||
|
||||
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 = {
|
||||
api: data.ae_api || {},
|
||||
slct: {
|
||||
@@ -43,9 +45,9 @@ export async function load({ params, parent, url }) {
|
||||
event_location_id
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
events_func.load_ae_obj_li__event_session({
|
||||
api_cfg: ae_acct.api,
|
||||
@@ -61,7 +63,9 @@ export async function load({ params, parent, url }) {
|
||||
log_lvl: 0
|
||||
});
|
||||
} 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.
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
let { log_lvl = 1 } = $props();
|
||||
|
||||
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 last_heartbeat: string | null = $state(null);
|
||||
|
||||
@@ -49,7 +51,10 @@
|
||||
$ae_loc.native_device.home_directory = info.home_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) {
|
||||
console.error('Sync: Metadata hydration FAILED:', err);
|
||||
@@ -66,8 +71,14 @@
|
||||
|
||||
// Timers run in both browser and native for dev/testing
|
||||
timer__event = setInterval(() => run_sync_cycle(), loop_info.event);
|
||||
timer__device = setInterval(() => run_device_heartbeat(), loop_info.device);
|
||||
timer__location = setInterval(() => refresh_location_config(), loop_info.location);
|
||||
timer__device = setInterval(
|
||||
() => run_device_heartbeat(),
|
||||
loop_info.device
|
||||
);
|
||||
timer__location = setInterval(
|
||||
() => refresh_location_config(),
|
||||
loop_info.location
|
||||
);
|
||||
timer__session = setInterval(() => run_sync_cycle(), loop_info.session);
|
||||
|
||||
// Immediate first run
|
||||
@@ -88,22 +99,42 @@
|
||||
const cache_root = $ae_loc.local_file_cache_path;
|
||||
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;
|
||||
try {
|
||||
const sessions = await db_events.session.where('event_location_id').equals(location_id).toArray();
|
||||
const session_ids = sessions.map(s => s.event_session_id);
|
||||
const sessions = await db_events.session
|
||||
.where('event_location_id')
|
||||
.equals(location_id)
|
||||
.toArray();
|
||||
const session_ids = sessions.map((s) => s.event_session_id);
|
||||
if (session_ids.length === 0) return;
|
||||
|
||||
const presentations = await db_events.presentation.where('event_session_id').anyOf(session_ids).toArray();
|
||||
const presentation_ids = presentations.map(p => p.event_presentation_id);
|
||||
const presentations = await db_events.presentation
|
||||
.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 presenter_ids = presenters.map(p => p.event_presenter_id);
|
||||
const presenters = await db_events.presenter
|
||||
.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 files = await db_events.file.where('for_id').anyOf(all_for_ids).toArray();
|
||||
const all_for_ids = [
|
||||
...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;
|
||||
let cached_count = 0;
|
||||
@@ -111,7 +142,10 @@
|
||||
|
||||
for (const file_obj of files) {
|
||||
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({
|
||||
cache_root,
|
||||
@@ -127,17 +161,22 @@
|
||||
|
||||
missing_count++;
|
||||
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.
|
||||
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({
|
||||
url, cache_root, hash: file_obj.hash_sha256,
|
||||
api_key: $ae_api.api_secret_key, account_id: $ae_api.account_id,
|
||||
url,
|
||||
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
|
||||
});
|
||||
|
||||
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.missing = missing_count;
|
||||
@@ -165,15 +204,23 @@
|
||||
async function run_device_heartbeat() {
|
||||
const dev = $ae_loc.native_device;
|
||||
// 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 (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';
|
||||
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 {
|
||||
let info = null;
|
||||
@@ -183,7 +230,10 @@
|
||||
|
||||
const update_payload: any = {
|
||||
// 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) {
|
||||
@@ -194,7 +244,8 @@
|
||||
release: info.release,
|
||||
arch: info.arch,
|
||||
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'
|
||||
};
|
||||
} else {
|
||||
@@ -243,42 +294,70 @@
|
||||
</script>
|
||||
|
||||
<!-- 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}
|
||||
<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 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
|
||||
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
|
||||
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 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="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="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="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...'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-white/10 pt-2 flex flex-col gap-1">
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -286,27 +365,34 @@
|
||||
{/if}
|
||||
|
||||
{#if currently_syncing}
|
||||
<button type="button"
|
||||
onclick={() => show_monitor = !show_monitor}
|
||||
<button
|
||||
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"
|
||||
>
|
||||
<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">
|
||||
<span class="font-bold">Syncing Room Files...</span>
|
||||
<span class="opacity-70 truncate max-w-48">{currently_syncing}</span>
|
||||
<span class="text-[8px] mt-1 text-primary-300">Monitor Ready (Click to View)</span>
|
||||
<span class="opacity-70 truncate max-w-48"
|
||||
>{currently_syncing}</span
|
||||
>
|
||||
<span class="text-[8px] mt-1 text-primary-300"
|
||||
>Monitor Ready (Click to View)</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{:else}
|
||||
<!-- Secret button area to toggle monitor when not syncing -->
|
||||
<button type="button"
|
||||
onclick={() => show_monitor = !show_monitor}
|
||||
<button
|
||||
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"
|
||||
title="Toggle Sync Monitor"
|
||||
>
|
||||
<span class="fas fa-microchip text-white text-[10px]"></span>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -43,8 +43,11 @@
|
||||
*/
|
||||
function handle_section_expand(current_key: string) {
|
||||
const launcher = $events_loc.launcher;
|
||||
Object.keys(launcher).forEach(key => {
|
||||
if (key.startsWith('section_state__') && key !== `section_state__${current_key}`) {
|
||||
Object.keys(launcher).forEach((key) => {
|
||||
if (
|
||||
key.startsWith('section_state__') &&
|
||||
key !== `section_state__${current_key}`
|
||||
) {
|
||||
if (launcher[key] === 'auto') {
|
||||
launcher[key] = 'collapsed';
|
||||
}
|
||||
@@ -60,13 +63,18 @@
|
||||
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">
|
||||
<h2 class="text-center text-lg font-bold text-gray-700 dark:text-gray-200">
|
||||
<div
|
||||
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>
|
||||
Launcher Configuration
|
||||
</h2>
|
||||
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)}
|
||||
class="btn btn-icon dark:text-white hover:bg-surface-500/10 transition-colors"
|
||||
>
|
||||
@@ -77,24 +85,27 @@
|
||||
|
||||
<!-- Category Tabs -->
|
||||
<div class="w-full grid grid-cols-3 gap-1 bg-surface-500/10 p-1 rounded-lg">
|
||||
<button type="button"
|
||||
onclick={() => active_tab = 'system'}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (active_tab = 'system')}
|
||||
class="btn btn-sm text-[10px] uppercase font-bold transition-all"
|
||||
class:preset-filled-primary-500={active_tab === 'system'}
|
||||
class:opacity-50={active_tab !== 'system'}
|
||||
>
|
||||
<span class="fas fa-microchip mr-1"></span> System
|
||||
</button>
|
||||
<button type="button"
|
||||
onclick={() => active_tab = 'sync'}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (active_tab = 'sync')}
|
||||
class="btn btn-sm text-[10px] uppercase font-bold transition-all"
|
||||
class:preset-filled-primary-500={active_tab === 'sync'}
|
||||
class:opacity-50={active_tab !== 'sync'}
|
||||
>
|
||||
<span class="fas fa-sync mr-1"></span> Sync
|
||||
</button>
|
||||
<button type="button"
|
||||
onclick={() => active_tab = 'general'}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (active_tab = 'general')}
|
||||
class="btn btn-sm text-[10px] uppercase font-bold transition-all"
|
||||
class:preset-filled-primary-500={active_tab === 'general'}
|
||||
class:opacity-50={active_tab !== 'general'}
|
||||
@@ -105,16 +116,22 @@
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="w-full flex flex-col gap-2 min-h-[400px]">
|
||||
|
||||
{#if active_tab === 'system'}
|
||||
<div class="animate-in fade-in slide-in-from-left-2 duration-300">
|
||||
{#if $ae_loc.is_native}
|
||||
<Launcher_Cfg_Health on_expand={() => handle_section_expand('health')} />
|
||||
<Launcher_Cfg_Native_OS on_expand={() => handle_section_expand('native_os')} />
|
||||
<Launcher_Cfg_Updates on_expand={() => handle_section_expand('updates')} />
|
||||
<Launcher_Cfg_Health
|
||||
on_expand={() => handle_section_expand('health')}
|
||||
/>
|
||||
<Launcher_Cfg_Native_OS
|
||||
on_expand={() => handle_section_expand('native_os')}
|
||||
/>
|
||||
<Launcher_Cfg_Updates
|
||||
on_expand={() => handle_section_expand('updates')}
|
||||
/>
|
||||
{:else}
|
||||
<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>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -122,33 +139,47 @@
|
||||
|
||||
{#if active_tab === 'sync'}
|
||||
<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>
|
||||
{/if}
|
||||
|
||||
{#if active_tab === 'general'}
|
||||
<div class="animate-in fade-in slide-in-from-right-2 duration-300">
|
||||
<Launcher_Cfg_Controller on_expand={() => handle_section_expand('controller')} />
|
||||
<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')} />
|
||||
<Launcher_Cfg_Controller
|
||||
on_expand={() => handle_section_expand('controller')}
|
||||
/>
|
||||
<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>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 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">
|
||||
<button type="button"
|
||||
onclick={() => ($events_loc.launcher.hide_drawer__debug = false)}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() =>
|
||||
($events_loc.launcher.hide_drawer__debug = false)}
|
||||
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 transition-all"
|
||||
>
|
||||
<span class="fas fa-bug mr-2"></span>
|
||||
Open Debug
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => location.reload()}
|
||||
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition-all"
|
||||
>
|
||||
@@ -156,9 +187,11 @@
|
||||
Reload Page
|
||||
</button>
|
||||
</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 • Events Launcher v3.0
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -57,12 +57,16 @@
|
||||
import { api } from '$lib/api/api';
|
||||
import { ae_loc, ae_api, ae_sess, slct } from '$lib/stores/ae_stores';
|
||||
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 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 the relay
|
||||
import * as native from '$lib/electron/electron_relay';
|
||||
|
||||
@@ -78,14 +82,17 @@
|
||||
|
||||
onMount(() => {
|
||||
if (screen_saver_exts.includes(event_file_obj.extension)) {
|
||||
if (!$events_loc.launcher.screen_saver_img_kv) $events_loc.launcher.screen_saver_img_kv = {};
|
||||
$events_loc.launcher.screen_saver_img_kv[event_file_id] = { ...event_file_obj };
|
||||
if (!$events_loc.launcher.screen_saver_img_kv)
|
||||
$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() {
|
||||
if (log_lvl) console.log('*** handle_open_file() ***');
|
||||
|
||||
|
||||
$events_slct.event_file_id = event_file_id;
|
||||
$events_slct.event_file_obj = event_file_obj;
|
||||
|
||||
@@ -98,15 +105,18 @@
|
||||
open_file_status = 'checking_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) {
|
||||
open_file_status = 'downloading_file';
|
||||
open_file_status_message = 'Downloading file to cache...';
|
||||
|
||||
|
||||
// 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 dl_result = await native.download_to_cache({
|
||||
url,
|
||||
cache_root,
|
||||
@@ -118,7 +128,7 @@
|
||||
if (!dl_result.success) {
|
||||
open_file_status = 'error';
|
||||
open_file_status_message = `Download failed: ${dl_result.error}`;
|
||||
setTimeout(() => open_file_clicked = false, 5000);
|
||||
setTimeout(() => (open_file_clicked = false), 5000);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +137,7 @@
|
||||
open_file_status_message = 'Opening Application';
|
||||
|
||||
// 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.
|
||||
const launch_result = await native.launch_from_cache({
|
||||
cache_root,
|
||||
@@ -141,9 +151,9 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 2. ONSITE MODE (Browser with Modified Extensions)
|
||||
else if ($events_loc.launcher.app_mode === 'onsite') {
|
||||
open_file_clicked = true;
|
||||
@@ -151,7 +161,11 @@
|
||||
open_file_status_message = 'Downloading (Onsite Mode)...';
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
@@ -167,7 +181,7 @@
|
||||
log_lvl: 1
|
||||
});
|
||||
|
||||
setTimeout(() => open_file_clicked = false, 5000);
|
||||
setTimeout(() => (open_file_clicked = false), 5000);
|
||||
return dl_promise;
|
||||
}
|
||||
// 3. DEFAULT MODE (Standard Browser)
|
||||
@@ -175,7 +189,7 @@
|
||||
open_file_clicked = true;
|
||||
open_file_status = 'downloading_default';
|
||||
open_file_status_message = 'Downloading...';
|
||||
|
||||
|
||||
const dl_promise = api.get_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: `/v3/action/hosted_file/${event_file_obj.hosted_file_id}/download`,
|
||||
@@ -193,14 +207,14 @@
|
||||
$events_sess.launcher.controller_trigger_send = true;
|
||||
}
|
||||
|
||||
setTimeout(() => open_file_clicked = false, 5000);
|
||||
setTimeout(() => (open_file_clicked = false), 5000);
|
||||
return dl_promise;
|
||||
}
|
||||
}
|
||||
|
||||
function prevent_default<T extends Event>(fn: (event: T) => void) {
|
||||
return function (event: T) {
|
||||
event.prevent_default();
|
||||
event.preventDefault();
|
||||
fn(event);
|
||||
};
|
||||
}
|
||||
@@ -209,21 +223,34 @@
|
||||
<div
|
||||
class:justify-between={!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"
|
||||
>
|
||||
{#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">
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
{/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'}
|
||||
<AE_Comp_Hosted_Files_Download_Button
|
||||
hosted_file_id={event_file_id}
|
||||
@@ -239,10 +266,19 @@
|
||||
>
|
||||
{#snippet label()}
|
||||
{#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}
|
||||
<span 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 })}
|
||||
<span
|
||||
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}
|
||||
{/snippet}
|
||||
</AE_Comp_Hosted_Files_Download_Button>
|
||||
@@ -260,27 +296,48 @@
|
||||
<span class="fas fa-spinner fa-spin mx-0.5"></span>
|
||||
<span>
|
||||
{#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}
|
||||
...
|
||||
{/if}
|
||||
</span>
|
||||
{: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}
|
||||
{#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}
|
||||
{: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}
|
||||
</span>
|
||||
|
||||
<span 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
|
||||
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 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}
|
||||
</span>
|
||||
{/snippet}
|
||||
@@ -288,7 +345,10 @@
|
||||
{/if}
|
||||
</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
|
||||
trigger_patch={ae_triggers.open_in_os}
|
||||
api_cfg={$ae_api}
|
||||
@@ -298,13 +358,20 @@
|
||||
field_type={'button'}
|
||||
field_value={ae_tmp.value__open_in_os}
|
||||
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={() => {
|
||||
if (!event_file_obj?.open_in_os) ae_tmp.value__open_in_os = 'win';
|
||||
else if (event_file_obj?.open_in_os == 'win') ae_tmp.value__open_in_os = 'mac';
|
||||
if (!event_file_obj?.open_in_os)
|
||||
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;
|
||||
ae_triggers.open_in_os = true;
|
||||
}}
|
||||
@@ -313,20 +380,37 @@
|
||||
class:preset-tonal-warning={event_file_obj?.open_in_os == 'mac'}
|
||||
disabled={!$ae_loc.trusted_access}
|
||||
>
|
||||
{#if event_file_obj?.open_in_os == 'win'}<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>
|
||||
{#if event_file_obj?.open_in_os == 'win'}<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}
|
||||
</button>
|
||||
</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="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 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>
|
||||
{#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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -141,19 +141,26 @@
|
||||
hide_created_on={true}
|
||||
hide_os={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_text_align={'text-center'}
|
||||
text_size={'text-xs'}
|
||||
text_size_md={'text-xs'}
|
||||
session_type={event_file_obj?.event_session_type_code ?? 'oral'}
|
||||
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'
|
||||
: null}
|
||||
modal_title={$lq__event_session_obj?.name}
|
||||
bind:modal__title={$events_sess.launcher.modal__title}
|
||||
bind: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__open_event_file_id={
|
||||
$events_sess.launcher.modal__open_event_file_id
|
||||
}
|
||||
bind:modal__event_file_obj={
|
||||
$events_sess.launcher.modal__event_file_obj
|
||||
}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -179,19 +186,26 @@
|
||||
hide_created_on={true}
|
||||
hide_os={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_text_align={'text-center'}
|
||||
text_size={'text-xs'}
|
||||
text_size_md={'text-xs'}
|
||||
session_type={event_file_obj?.event_session_type_code ?? 'oral'}
|
||||
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'
|
||||
: null}
|
||||
modal_title={$lq__event_session_obj?.name}
|
||||
bind:modal__title={$events_sess.launcher.modal__title}
|
||||
bind: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__open_event_file_id={
|
||||
$events_sess.launcher.modal__open_event_file_id
|
||||
}
|
||||
bind:modal__event_file_obj={
|
||||
$events_sess.launcher.modal__event_file_obj
|
||||
}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -214,7 +228,8 @@
|
||||
flex flex-row gap-1 items-center justify-center
|
||||
"
|
||||
>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
if ($events_loc.launcher.show_content__hidden_files) {
|
||||
$events_loc.launcher.show_content__hidden_files = false;
|
||||
@@ -235,14 +250,16 @@
|
||||
title="Toggle showing hidden files. Including files marked as 'draft'."
|
||||
>
|
||||
{#if $events_loc.launcher.show_content__hidden_files}
|
||||
<span class="fas fa-eye-slash m-1 text-neutral-800/80"></span>
|
||||
<span class="fas fa-eye-slash m-1 text-neutral-800/80"
|
||||
></span>
|
||||
Hide Files
|
||||
{:else}
|
||||
<span class="fas fa-eye m-1 text-neutral-800/80"></span>
|
||||
All Files
|
||||
{/if}
|
||||
</button>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$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}
|
||||
<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
|
||||
{:else}
|
||||
<span class="fas fa-eye m-1 text-neutral-800/80"></span>
|
||||
|
||||
@@ -27,24 +27,30 @@
|
||||
|
||||
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
|
||||
<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:
|
||||
</div>
|
||||
<ul class="space-y-1">
|
||||
{#each $lq__event_file_obj_li as event_file_obj}
|
||||
<li
|
||||
class="flex flex-col md:flex-row flex-wrap gap-1 items-center justify-start"
|
||||
class:hidden={!$events_loc.launcher.show_content__hidden_files &&
|
||||
event_file_obj.hide}
|
||||
class:hidden={!$events_loc.launcher
|
||||
.show_content__hidden_files && event_file_obj.hide}
|
||||
>
|
||||
<Event_launcher_file_cont
|
||||
event_file_id={event_file_obj.event_file_id}
|
||||
{event_file_obj}
|
||||
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}
|
||||
session_type={event_file_obj?.event_session_type_code ?? 'oral'}
|
||||
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'
|
||||
: null}
|
||||
modal_title={lq__event_presentation_obj?.name}
|
||||
@@ -52,7 +58,9 @@
|
||||
bind: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>
|
||||
{/each}
|
||||
|
||||
@@ -89,17 +89,21 @@
|
||||
{#each $lq__event_file_obj_li as event_file_obj, index}
|
||||
<li
|
||||
class="flex flex-col md:flex-row flex-wrap gap-1 items-center justify-start"
|
||||
class:hidden={!$events_loc.launcher.show_content__hidden_files &&
|
||||
event_file_obj.hide}
|
||||
class:hidden={!$events_loc.launcher
|
||||
.show_content__hidden_files && event_file_obj.hide}
|
||||
>
|
||||
<Event_launcher_file_cont
|
||||
event_file_id={event_file_obj.event_file_id}
|
||||
{event_file_obj}
|
||||
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}
|
||||
session_type={event_file_obj?.event_session_type_code ?? 'oral'}
|
||||
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'
|
||||
: null}
|
||||
modal_title={lq__event_presenter_obj?.event_presentation_name}
|
||||
@@ -107,7 +111,9 @@
|
||||
bind: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>
|
||||
{/each}
|
||||
|
||||
@@ -88,8 +88,8 @@
|
||||
{#each $lq__event_file_obj_li as event_file_obj, index}
|
||||
<li
|
||||
class="flex flex-col md:flex-row wrap gap items-center justify-center"
|
||||
class:hidden={!$events_loc.launcher.show_content__hidden_files &&
|
||||
event_file_obj.hide}
|
||||
class:hidden={!$events_loc.launcher
|
||||
.show_content__hidden_files && event_file_obj.hide}
|
||||
>
|
||||
<Event_launcher_file_cont
|
||||
event_file_id={event_file_obj.event_file_id}
|
||||
@@ -97,8 +97,10 @@
|
||||
hide_created_on={false}
|
||||
hide_meta={true}
|
||||
show_bak_download={$ae_loc.trusted_access}
|
||||
session_type={event_file_obj?.event_session_type_code ?? 'oral'}
|
||||
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'
|
||||
: null}
|
||||
modal_title={lq__event_presenter_obj?.event_presentation_name}
|
||||
@@ -106,7 +108,9 @@
|
||||
bind: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>
|
||||
{/each}
|
||||
|
||||
@@ -69,7 +69,9 @@
|
||||
let lq__event_file_obj_li = $derived(
|
||||
liveQuery(async () => {
|
||||
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
|
||||
// .where('event_session_id')
|
||||
@@ -83,7 +85,10 @@
|
||||
|
||||
// Check if results are different than the current $events_slct.event_file_obj_li
|
||||
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];
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
@@ -136,7 +141,10 @@
|
||||
.sortBy(sort_by);
|
||||
// .sortBy('name')
|
||||
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
|
||||
@@ -189,7 +197,8 @@
|
||||
// Check if results are different than the current $events_slct.event_presenter_obj_li
|
||||
if ($events_slct.event_presenter_obj_li && results) {
|
||||
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 };
|
||||
if (log_lvl) {
|
||||
@@ -277,9 +286,12 @@
|
||||
$events_loc.launcher.hide__session_datetimes}
|
||||
class="shrink event_session_datetimes"
|
||||
>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
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.time_format = 'time_short';
|
||||
$events_loc.launcher.time_hours = 24;
|
||||
@@ -319,8 +331,10 @@
|
||||
</h3>
|
||||
|
||||
<span
|
||||
class:justify-between={$events_loc.launcher.hide__session_datetimes}
|
||||
class:justify-end={!$events_loc.launcher.hide__session_datetimes}
|
||||
class:justify-between={$events_loc.launcher
|
||||
.hide__session_datetimes}
|
||||
class:justify-end={!$events_loc.launcher
|
||||
.hide__session_datetimes}
|
||||
class="grow flex flex-row gap-2 items-center"
|
||||
>
|
||||
<h2
|
||||
@@ -363,7 +377,10 @@
|
||||
<span class="fas fa-file-archive"></span>
|
||||
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}×)
|
||||
</span>
|
||||
</strong>
|
||||
@@ -384,22 +401,29 @@
|
||||
{#each $lq__event_file_obj_li as event_file_obj, index}
|
||||
<li
|
||||
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_launcher_file_cont
|
||||
event_file_id={event_file_obj.event_file_id}
|
||||
{event_file_obj}
|
||||
hide_created_on={true}
|
||||
show_bak_download={$ae_loc.trusted_access && $ae_loc.edit_mode}
|
||||
session_type={event_file_obj?.event_session_type_code ?? 'oral'}
|
||||
open_method={event_file_obj?.event_session_type_code == 'poster'
|
||||
show_bak_download={$ae_loc.trusted_access &&
|
||||
$ae_loc.edit_mode}
|
||||
session_type={event_file_obj?.event_session_type_code ??
|
||||
'oral'}
|
||||
open_method={event_file_obj?.event_session_type_code ==
|
||||
'poster'
|
||||
? 'modal'
|
||||
: null}
|
||||
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
|
||||
$events_sess.launcher
|
||||
.modal__open_event_file_id
|
||||
}
|
||||
bind:modal__event_file_obj={
|
||||
$events_sess.launcher.modal__event_file_obj
|
||||
@@ -449,7 +473,9 @@
|
||||
<!-- Maybe set max with? max-w-(--breakpoint-md) -->
|
||||
<ul class="event_presentation_list max-w-full space-y-2">
|
||||
{#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 -->
|
||||
<div
|
||||
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"
|
||||
>
|
||||
{#if $lq__event_presenter_obj_li[index]?.given_name && $lq__event_presenter_obj_li[index]?.given_name != 'Group'}
|
||||
<span class="fas fa-user"></span>
|
||||
{$lq__event_presenter_obj_li[index]?.full_name}
|
||||
<span class="fas fa-user"
|
||||
></span>
|
||||
{$lq__event_presenter_obj_li[
|
||||
index
|
||||
]?.full_name}
|
||||
{:else if $lq__event_presenter_obj_li[index]?.given_name == 'Group'}
|
||||
<span class="fas fa-users"></span>
|
||||
{$lq__event_presenter_obj_li[index]
|
||||
?.affiliations}
|
||||
<span class="fas fa-users"
|
||||
></span>
|
||||
{$lq__event_presenter_obj_li[
|
||||
index
|
||||
]?.affiliations}
|
||||
{:else}
|
||||
--not set--
|
||||
{/if}
|
||||
@@ -495,7 +526,9 @@
|
||||
</div>
|
||||
|
||||
<!-- 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 -->
|
||||
|
||||
|
||||
@@ -69,7 +69,9 @@
|
||||
);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -84,8 +86,12 @@
|
||||
inc_all_file_li: false, // Also include files under presentations and presenters as well?
|
||||
inc_presentation_li: true,
|
||||
inc_presenter_li: true,
|
||||
enabled: $events_loc.launcher.show_content__enabled_sessions ? 'all' : 'enabled',
|
||||
hidden: $events_loc.launcher.show_content__hidden_sessions ? 'all' : 'not_hidden',
|
||||
enabled: $events_loc.launcher.show_content__enabled_sessions
|
||||
? 'all'
|
||||
: 'enabled',
|
||||
hidden: $events_loc.launcher.show_content__hidden_sessions
|
||||
? 'all'
|
||||
: 'not_hidden',
|
||||
limit: 49,
|
||||
try_cache: true,
|
||||
log_lvl: 1
|
||||
@@ -126,7 +132,9 @@
|
||||
<div class="text-xs text-surface-600-400">
|
||||
<strong>
|
||||
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}×)
|
||||
</span>
|
||||
|
||||
@@ -161,11 +169,14 @@
|
||||
if (slct_event_location_id) {
|
||||
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;
|
||||
|
||||
// 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}`;
|
||||
|
||||
@@ -188,7 +199,9 @@
|
||||
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}
|
||||
<option value={event_location_obj?.id}>
|
||||
{event_location_obj?.name}
|
||||
|
||||
@@ -73,17 +73,23 @@
|
||||
}
|
||||
let event_session_id = String(trigger_reload__event_session_obj_id);
|
||||
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.
|
||||
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);
|
||||
|
||||
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_trigger_send = true;
|
||||
}
|
||||
@@ -91,11 +97,21 @@
|
||||
data_url.searchParams.set('session_id', event_session_id);
|
||||
|
||||
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
|
||||
goto(new_url, { replaceState: false, noscroll: true, keepfocus: true }).then(() => {
|
||||
if (log_lvl) console.log(`🏁 [Trace] Total Navigation Roundtrip: ${(performance.now() - start).toFixed(2)}ms`);
|
||||
goto(new_url, {
|
||||
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
|
||||
})
|
||||
.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) {
|
||||
$events_slct.event_session_obj = load_results;
|
||||
$events_slct.event_file_obj_li = load_results.event_file_li ?? [];
|
||||
$events_slct.event_presentation_obj_li = load_results.event_presentation_li ?? [];
|
||||
$events_slct.event_file_obj_li =
|
||||
load_results.event_file_li ?? [];
|
||||
$events_slct.event_presentation_obj_li =
|
||||
load_results.event_presentation_li ?? [];
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -145,7 +166,9 @@
|
||||
<div class="text-xs text-surface-600-400">
|
||||
<strong>
|
||||
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}×)
|
||||
</span>
|
||||
|
||||
@@ -177,22 +200,28 @@
|
||||
transition-all
|
||||
"
|
||||
>
|
||||
<button type="button"
|
||||
<button
|
||||
type="button"
|
||||
onmouseenter={() => {
|
||||
// Start a 750 ms timer to prevent changing the session too quickly.
|
||||
hover_timer = setTimeout(async () => {
|
||||
// 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;
|
||||
}
|
||||
trigger_reload__event_session_obj_id = event_session_obj?.id;
|
||||
trigger_reload__event_session_obj_id =
|
||||
event_session_obj?.id;
|
||||
}, hover_timer_wait);
|
||||
}}
|
||||
onmouseleave={() => {
|
||||
clearTimeout(hover_timer);
|
||||
}}
|
||||
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_obj = null;
|
||||
@@ -214,11 +243,14 @@
|
||||
"
|
||||
class:preset-filled-primary-500={slct__event_session_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}
|
||||
class:border-secondary-500={slct__event_session_id != event_session_obj?.id}
|
||||
class:font-bold={slct__event_session_id === event_session_obj?.id}
|
||||
class:hidden={!$events_loc.launcher.show_content__hidden_sessions &&
|
||||
class:border-secondary-500={slct__event_session_id !=
|
||||
event_session_obj?.id}
|
||||
class:font-bold={slct__event_session_id ===
|
||||
event_session_obj?.id}
|
||||
class:hidden={!$events_loc.launcher
|
||||
.show_content__hidden_sessions &&
|
||||
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)}`}
|
||||
@@ -226,7 +258,9 @@
|
||||
<!-- hover:scale-115 -->
|
||||
<!-- 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}
|
||||
<span class="fas fa-calendar-check"></span>
|
||||
{:else}
|
||||
@@ -234,7 +268,8 @@
|
||||
{/if}
|
||||
<span
|
||||
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(
|
||||
event_session_obj?.start_datetime,
|
||||
@@ -266,4 +301,4 @@
|
||||
{:else}
|
||||
<div class="text-sm">No sessions found.</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user