Prettier for Event Launcher
This commit is contained in:
@@ -44,7 +44,8 @@
|
|||||||
// Only send when connected so the UI button doesn't silently no-op.
|
// Only send when connected so the UI button doesn't silently no-op.
|
||||||
if (
|
if (
|
||||||
$events_loc.launcher.ws_connect &&
|
$events_loc.launcher.ws_connect &&
|
||||||
($events_loc.launcher.controller === 'local_push' || $events_loc.launcher.controller === 'remote')
|
($events_loc.launcher.controller === 'local_push' ||
|
||||||
|
$events_loc.launcher.controller === 'remote')
|
||||||
) {
|
) {
|
||||||
$events_sess.launcher.controller_cmd = `ae_mode:${mode}`;
|
$events_sess.launcher.controller_cmd = `ae_mode:${mode}`;
|
||||||
$events_sess.launcher.controller_trigger_send = 'trigger';
|
$events_sess.launcher.controller_trigger_send = 'trigger';
|
||||||
@@ -57,135 +58,126 @@
|
|||||||
icon={LayoutGrid}
|
icon={LayoutGrid}
|
||||||
bind:state={$events_loc.launcher.section_state__app_modes}
|
bind:state={$events_loc.launcher.section_state__app_modes}
|
||||||
{on_expand}
|
{on_expand}
|
||||||
description="Mode: {$events_loc.launcher.app_mode} | UI Layout"
|
description="Mode: {$events_loc.launcher.app_mode} | UI Layout">
|
||||||
>
|
|
||||||
<!-- Content omitted for brevity, preserved in file -->
|
<!-- Content omitted for brevity, preserved in file -->
|
||||||
<div class="col-span-full flex flex-col gap-3">
|
<div class="col-span-full flex flex-col gap-3">
|
||||||
<!-- 0. Oral / Poster Kiosk Mode Preset Toggle -->
|
<!-- 0. Oral / Poster Kiosk Mode Preset Toggle -->
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1">Session Mode Preset</p>
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
<div class="grid grid-cols-2 gap-1 bg-surface-500/5 p-1 rounded-lg">
|
Session Mode Preset
|
||||||
|
</p>
|
||||||
|
<div class="bg-surface-500/5 grid grid-cols-2 gap-1 rounded-lg p-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => apply_mode('oral')}
|
onclick={() => apply_mode('oral')}
|
||||||
class="btn btn-xs font-bold text-[10px]"
|
class="btn btn-xs text-[10px] font-bold"
|
||||||
class:preset-filled-secondary={!is_poster_mode}
|
class:preset-filled-secondary={!is_poster_mode}
|
||||||
class:preset-tonal-surface={is_poster_mode}
|
class:preset-tonal-surface={is_poster_mode}
|
||||||
title="Standard oral/presentation layout — menus and headers visible"
|
title="Standard oral/presentation layout — menus and headers visible">
|
||||||
>
|
|
||||||
<GraduationCap size="0.85em" class="mr-1 opacity-70" />
|
<GraduationCap size="0.85em" class="mr-1 opacity-70" />
|
||||||
Oral / Default
|
Oral / Default
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => apply_mode('poster')}
|
onclick={() => apply_mode('poster')}
|
||||||
class="btn btn-xs font-bold text-[10px]"
|
class="btn btn-xs text-[10px] font-bold"
|
||||||
class:preset-filled-primary={is_poster_mode}
|
class:preset-filled-primary={is_poster_mode}
|
||||||
class:preset-tonal-surface={!is_poster_mode}
|
class:preset-tonal-surface={!is_poster_mode}
|
||||||
title="Digital Poster kiosk — hides site chrome, menu, header & footer"
|
title="Digital Poster kiosk — hides site chrome, menu, header & footer">
|
||||||
>
|
|
||||||
<IdCard size="0.85em" class="mr-1 opacity-70" />
|
<IdCard size="0.85em" class="mr-1 opacity-70" />
|
||||||
Poster Kiosk
|
Poster Kiosk
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{#if $events_loc.launcher.ws_connect && ($events_loc.launcher.controller === 'local_push' || $events_loc.launcher.controller === 'remote')}
|
{#if $events_loc.launcher.ws_connect && ($events_loc.launcher.controller === 'local_push' || $events_loc.launcher.controller === 'remote')}
|
||||||
<p class="text-[8px] opacity-40 italic ml-1">Applies to all connected WS devices</p>
|
<p class="ml-1 text-[8px] italic opacity-40">
|
||||||
|
Applies to all connected WS devices
|
||||||
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 1. App Mode Selection -->
|
<!-- 1. App Mode Selection -->
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
>Operational Environment</p
|
Operational Environment
|
||||||
>
|
</p>
|
||||||
<div class="grid grid-cols-3 gap-1 bg-surface-500/5 p-1 rounded-lg">
|
<div class="bg-surface-500/5 grid grid-cols-3 gap-1 rounded-lg p-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => ($events_loc.launcher.app_mode = 'default')}
|
onclick={() => ($events_loc.launcher.app_mode = 'default')}
|
||||||
class="btn btn-xs text-[9px] font-bold"
|
class="btn btn-xs text-[9px] font-bold"
|
||||||
class:preset-filled-primary={$events_loc.launcher
|
class:preset-filled-primary={$events_loc.launcher
|
||||||
.app_mode === 'default'}
|
.app_mode === 'default'}
|
||||||
class:preset-tonal-surface={$events_loc.launcher.app_mode !==
|
class:preset-tonal-surface={$events_loc.launcher
|
||||||
'default'}
|
.app_mode !== 'default'}
|
||||||
title="Default standard web browser (Chromium, Firefox, Safari based) launcher, for remote presenters and testing before being onsite">
|
title="Default standard web browser (Chromium, Firefox, Safari based) launcher, for remote presenters and testing before being onsite">
|
||||||
Web</button
|
Web</button>
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => ($events_loc.launcher.app_mode = 'native')}
|
onclick={() => ($events_loc.launcher.app_mode = 'native')}
|
||||||
class="btn btn-xs text-[9px] font-bold"
|
class="btn btn-xs text-[9px] font-bold"
|
||||||
class:preset-filled-primary={$events_loc.launcher
|
class:preset-filled-primary={$events_loc.launcher
|
||||||
.app_mode === 'native'}
|
.app_mode === 'native'}
|
||||||
class:preset-tonal-surface={$events_loc.launcher.app_mode !==
|
class:preset-tonal-surface={$events_loc.launcher
|
||||||
'native'}
|
.app_mode !== 'native'}
|
||||||
title="Native Electron based app launcher, for onsite presenters in session rooms">App</button
|
title="Native Electron based app launcher, for onsite presenters in session rooms"
|
||||||
>
|
>App</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => ($events_loc.launcher.app_mode = 'onsite')}
|
onclick={() => ($events_loc.launcher.app_mode = 'onsite')}
|
||||||
class="btn btn-xs text-[9px] font-bold"
|
class="btn btn-xs text-[9px] font-bold"
|
||||||
class:preset-filled-primary={$events_loc.launcher
|
class:preset-filled-primary={$events_loc.launcher
|
||||||
.app_mode === 'onsite'}
|
.app_mode === 'onsite'}
|
||||||
class:preset-tonal-surface={$events_loc.launcher.app_mode !==
|
class:preset-tonal-surface={$events_loc.launcher
|
||||||
'onsite'}
|
.app_mode !== 'onsite'}
|
||||||
title="Customized onsite OS and web browser (Chromium or Firefox based) launcher, for onsite presenters in for practice and onsite backup">Onsite</button
|
title="Customized onsite OS and web browser (Chromium or Firefox based) launcher, for onsite presenters in for practice and onsite backup"
|
||||||
>
|
>Onsite</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 2. UI Layout Toggles -->
|
<!-- 2. UI Layout Toggles -->
|
||||||
<div
|
<div
|
||||||
class="flex flex-col gap-1 border-t border-surface-500/10 pt-2 mt-1"
|
class="border-surface-500/10 mt-1 flex flex-col gap-1 border-t pt-2">
|
||||||
>
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
Interface Visibility
|
||||||
>Interface Visibility</p
|
</p>
|
||||||
>
|
|
||||||
<div class="grid grid-cols-2 gap-2 p-1">
|
<div class="grid grid-cols-2 gap-2 p-1">
|
||||||
<label class="flex items-center gap-2 cursor-pointer group">
|
<label class="group flex cursor-pointer items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={
|
bind:checked={
|
||||||
$events_loc.launcher.hide__launcher_header
|
$events_loc.launcher.hide__launcher_header
|
||||||
}
|
}
|
||||||
class="checkbox checkbox-sm"
|
class="checkbox checkbox-sm" />
|
||||||
/>
|
<span class="group-hover:text-primary-500 text-xs"
|
||||||
<span class="text-xs group-hover:text-primary-500"
|
>Hide Header</span>
|
||||||
>Hide Header</span
|
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
<label class="flex items-center gap-2 cursor-pointer group">
|
<label class="group flex cursor-pointer items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={$events_loc.launcher.hide__launcher_menu}
|
bind:checked={$events_loc.launcher.hide__launcher_menu}
|
||||||
class="checkbox checkbox-sm"
|
class="checkbox checkbox-sm" />
|
||||||
/>
|
<span class="group-hover:text-primary-500 text-xs"
|
||||||
<span class="text-xs group-hover:text-primary-500"
|
>Hide Menu</span>
|
||||||
>Hide Menu</span
|
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
<label class="flex items-center gap-2 cursor-pointer group">
|
<label class="group flex cursor-pointer items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={
|
bind:checked={
|
||||||
$events_loc.launcher.hide__launcher_footer
|
$events_loc.launcher.hide__launcher_footer
|
||||||
}
|
}
|
||||||
class="checkbox checkbox-sm"
|
class="checkbox checkbox-sm" />
|
||||||
/>
|
<span class="group-hover:text-primary-500 text-xs"
|
||||||
<span class="text-xs group-hover:text-primary-500"
|
>Hide Footer</span>
|
||||||
>Hide Footer</span
|
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
<label class="flex items-center gap-2 cursor-pointer group">
|
<label class="group flex cursor-pointer items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={
|
bind:checked={
|
||||||
$events_loc.launcher.hide__session_datetimes
|
$events_loc.launcher.hide__session_datetimes
|
||||||
}
|
}
|
||||||
class="checkbox checkbox-sm"
|
class="checkbox checkbox-sm" />
|
||||||
/>
|
<span class="group-hover:text-primary-500 text-xs"
|
||||||
<span class="text-xs group-hover:text-primary-500"
|
>Hide Times</span>
|
||||||
>Hide Times</span
|
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -202,8 +194,7 @@
|
|||||||
$events_loc.launcher.time_hours = 12;
|
$events_loc.launcher.time_hours = 12;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
class="btn btn-xs preset-tonal-surface w-full text-[10px]"
|
class="btn btn-xs preset-tonal-surface w-full text-[10px]">
|
||||||
>
|
|
||||||
<Clock size="0.85em" class="mr-1 opacity-50" />
|
<Clock size="0.85em" class="mr-1 opacity-50" />
|
||||||
Clock Format:
|
Clock Format:
|
||||||
<strong>{$events_loc.launcher.time_hours}-hour</strong>
|
<strong>{$events_loc.launcher.time_hours}-hour</strong>
|
||||||
@@ -212,35 +203,30 @@
|
|||||||
<!-- 4. Advanced Toggles (Edit Mode Only) -->
|
<!-- 4. Advanced Toggles (Edit Mode Only) -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div
|
<div
|
||||||
class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-2"
|
class="border-surface-500/20 col-span-full mt-1 flex flex-col gap-2 border-t pt-3">
|
||||||
>
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
Technical Layout
|
||||||
>Technical Layout</p
|
</p>
|
||||||
>
|
|
||||||
<div class="grid grid-cols-1 gap-2 p-1">
|
<div class="grid grid-cols-1 gap-2 p-1">
|
||||||
<label class="flex items-center gap-2 cursor-pointer group">
|
<label class="group flex cursor-pointer items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={$events_loc.launcher.hide__ws_element}
|
bind:checked={$events_loc.launcher.hide__ws_element}
|
||||||
class="checkbox checkbox-sm"
|
class="checkbox checkbox-sm" />
|
||||||
/>
|
|
||||||
<span
|
<span
|
||||||
class="text-xs group-hover:text-primary-500 italic"
|
class="group-hover:text-primary-500 text-xs italic"
|
||||||
>Hide WebSocket Debugger</span
|
>Hide WebSocket Debugger</span>
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
<label class="flex items-center gap-2 cursor-pointer group">
|
<label class="group flex cursor-pointer items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={
|
bind:checked={
|
||||||
$events_loc.launcher.hide__modal_header_title
|
$events_loc.launcher.hide__modal_header_title
|
||||||
}
|
}
|
||||||
class="checkbox checkbox-sm"
|
class="checkbox checkbox-sm" />
|
||||||
/>
|
|
||||||
<span
|
<span
|
||||||
class="text-xs group-hover:text-primary-500 italic"
|
class="group-hover:text-primary-500 text-xs italic"
|
||||||
>Hide Poster Modal Title</span
|
>Hide Poster Modal Title</span>
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,15 @@
|
|||||||
import { ae_loc } from '$lib/stores/ae_stores';
|
import { ae_loc } from '$lib/stores/ae_stores';
|
||||||
import { events_loc, events_sess } from '$lib/stores/ae_events_stores';
|
import { events_loc, events_sess } from '$lib/stores/ae_events_stores';
|
||||||
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
||||||
import { Gamepad2, Link, Lock, LockOpen, Plug, RefreshCw, Unlink } from '@lucide/svelte';
|
import {
|
||||||
|
Gamepad2,
|
||||||
|
Link,
|
||||||
|
Lock,
|
||||||
|
LockOpen,
|
||||||
|
Plug,
|
||||||
|
RefreshCw,
|
||||||
|
Unlink
|
||||||
|
} from '@lucide/svelte';
|
||||||
interface Props {
|
interface Props {
|
||||||
on_expand?: () => void;
|
on_expand?: () => void;
|
||||||
}
|
}
|
||||||
@@ -20,41 +28,35 @@
|
|||||||
{on_expand}
|
{on_expand}
|
||||||
description="Mode: {$events_loc.launcher?.controller} | WS: {ws_connected
|
description="Mode: {$events_loc.launcher?.controller} | WS: {ws_connected
|
||||||
? 'Connected'
|
? 'Connected'
|
||||||
: 'Offline'}"
|
: 'Offline'}">
|
||||||
>
|
|
||||||
<!-- Content omitted for brevity, preserved in file -->
|
<!-- Content omitted for brevity, preserved in file -->
|
||||||
<div class="col-span-full flex flex-col gap-3">
|
<div class="col-span-full flex flex-col gap-3">
|
||||||
<!-- 1. Connection Status Badge -->
|
<!-- 1. Connection Status Badge -->
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between bg-surface-500/5 p-2 rounded border border-surface-500/10"
|
class="bg-surface-500/5 border-surface-500/10 flex items-center justify-between rounded border p-2">
|
||||||
>
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Plug size="1em" class="opacity-50" />
|
<Plug size="1em" class="opacity-50" />
|
||||||
<span class="text-[10px] font-bold uppercase tracking-wider"
|
<span class="text-[10px] font-bold tracking-wider uppercase"
|
||||||
>WebSocket Link</span
|
>WebSocket Link</span>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
{#if ws_connected}
|
{#if ws_connected}
|
||||||
<span
|
<span
|
||||||
class="badge preset-filled-success text-[8px] animate-pulse"
|
class="badge preset-filled-success animate-pulse text-[8px]"
|
||||||
>Connected</span
|
>Connected</span>
|
||||||
>
|
|
||||||
{:else}
|
{:else}
|
||||||
<span class="badge preset-filled-error text-[8px]"
|
<span class="badge preset-filled-error text-[8px]"
|
||||||
>Disconnected</span
|
>Disconnected</span>
|
||||||
>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 2. Controller Mode Selection -->
|
<!-- 2. Controller Mode Selection -->
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
>Controller Strategy</p
|
Controller Strategy
|
||||||
>
|
</p>
|
||||||
<select
|
<select
|
||||||
bind:value={$events_loc.launcher.controller}
|
bind:value={$events_loc.launcher.controller}
|
||||||
class="select select-sm text-xs preset-tonal-surface h-8"
|
class="select select-sm preset-tonal-surface h-8 text-xs">
|
||||||
>
|
|
||||||
<option value="local">Local Only</option>
|
<option value="local">Local Only</option>
|
||||||
<option value="remote">Remotely WS Controlled</option>
|
<option value="remote">Remotely WS Controlled</option>
|
||||||
<option value="local_push">Local and WS Controller</option>
|
<option value="local_push">Local and WS Controller</option>
|
||||||
@@ -62,7 +64,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 3. Connection Actions -->
|
<!-- 3. Connection Actions -->
|
||||||
<div class="grid grid-cols-2 gap-2 mt-1">
|
<div class="mt-1 grid grid-cols-2 gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
@@ -76,8 +78,7 @@
|
|||||||
}}
|
}}
|
||||||
class="btn btn-sm text-[10px] font-bold transition-all"
|
class="btn btn-sm text-[10px] font-bold transition-all"
|
||||||
class:preset-tonal-error={$events_loc.launcher.ws_connect}
|
class:preset-tonal-error={$events_loc.launcher.ws_connect}
|
||||||
class:preset-tonal-success={!$events_loc.launcher.ws_connect}
|
class:preset-tonal-success={!$events_loc.launcher.ws_connect}>
|
||||||
>
|
|
||||||
{#if $events_loc.launcher.ws_connect}
|
{#if $events_loc.launcher.ws_connect}
|
||||||
<Unlink size="0.85em" class="mr-1" /> Disconnect
|
<Unlink size="0.85em" class="mr-1" /> Disconnect
|
||||||
{:else}
|
{:else}
|
||||||
@@ -92,8 +93,7 @@
|
|||||||
$events_sess.launcher.controller_trigger_send = 'trigger';
|
$events_sess.launcher.controller_trigger_send = 'trigger';
|
||||||
}}
|
}}
|
||||||
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500 text-[10px] font-bold"
|
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500 text-[10px] font-bold"
|
||||||
disabled={!ws_connected}
|
disabled={!ws_connected}>
|
||||||
>
|
|
||||||
<RefreshCw size="0.85em" class="mr-1" /> Group Reload
|
<RefreshCw size="0.85em" class="mr-1" /> Group Reload
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -101,21 +101,19 @@
|
|||||||
<!-- 4. Technical Config (Edit Mode Only) -->
|
<!-- 4. Technical Config (Edit Mode Only) -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div
|
<div
|
||||||
class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-2"
|
class="border-surface-500/20 col-span-full mt-1 flex flex-col gap-2 border-t pt-3">
|
||||||
>
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
Channel Configuration
|
||||||
>Channel Configuration</p
|
</p>
|
||||||
>
|
|
||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
<input
|
<input
|
||||||
bind:value={$events_loc.launcher.controller_group_code}
|
bind:value={$events_loc.launcher.controller_group_code}
|
||||||
placeholder="Group Code"
|
placeholder="Group Code"
|
||||||
class="input input-sm grow text-[10px] h-7 preset-tonal-surface font-mono"
|
class="input input-sm preset-tonal-surface h-7 grow font-mono text-[10px]"
|
||||||
readonly={!$events_sess.launcher
|
readonly={!$events_sess.launcher
|
||||||
.controller_unlock_group_code}
|
.controller_unlock_group_code}
|
||||||
ondblclick={() =>
|
ondblclick={() =>
|
||||||
($events_sess.launcher.controller_unlock_group_code = true)}
|
($events_sess.launcher.controller_unlock_group_code = true)} />
|
||||||
/>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() =>
|
onclick={() =>
|
||||||
@@ -123,8 +121,7 @@
|
|||||||
!$events_sess.launcher
|
!$events_sess.launcher
|
||||||
.controller_unlock_group_code)}
|
.controller_unlock_group_code)}
|
||||||
class="btn btn-xs preset-tonal-surface"
|
class="btn btn-xs preset-tonal-surface"
|
||||||
title="Toggle Unlock"
|
title="Toggle Unlock">
|
||||||
>
|
|
||||||
{#if $events_sess.launcher.controller_unlock_group_code}
|
{#if $events_sess.launcher.controller_unlock_group_code}
|
||||||
<LockOpen size="0.85em" class="text-primary-500" />
|
<LockOpen size="0.85em" class="text-primary-500" />
|
||||||
{:else}
|
{:else}
|
||||||
@@ -132,7 +129,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-[8px] opacity-40 italic ml-1">
|
<p class="ml-1 text-[8px] italic opacity-40">
|
||||||
Double-click input to unlock editing. Changing code triggers
|
Double-click input to unlock editing. Changing code triggers
|
||||||
reconnect.
|
reconnect.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -47,53 +47,48 @@
|
|||||||
bind:state={$events_loc.launcher.section_state__health}
|
bind:state={$events_loc.launcher.section_state__health}
|
||||||
{on_expand}
|
{on_expand}
|
||||||
description="Heartbeat: {$events_sess.launcher.heartbeat_info
|
description="Heartbeat: {$events_sess.launcher.heartbeat_info
|
||||||
.last_timestamp || 'Pending'}"
|
.last_timestamp || 'Pending'}">
|
||||||
>
|
|
||||||
<!-- Content omitted for brevity in instruction, but preserved in file -->
|
<!-- Content omitted for brevity in instruction, but preserved in file -->
|
||||||
<!-- Telemetry Dashboard -->
|
<!-- Telemetry Dashboard -->
|
||||||
<div
|
<div
|
||||||
class="col-span-full flex flex-col gap-3 bg-surface-500/5 p-3 rounded-lg border border-surface-500/10"
|
class="bg-surface-500/5 border-surface-500/10 col-span-full flex flex-col gap-3 rounded-lg border p-3">
|
||||||
>
|
|
||||||
<!-- CPU Usage (Mock Logic if load not available yet) -->
|
<!-- CPU Usage (Mock Logic if load not available yet) -->
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<div
|
<div
|
||||||
class="flex justify-between text-[9px] uppercase font-bold opacity-60"
|
class="flex justify-between text-[9px] font-bold uppercase opacity-60">
|
||||||
>
|
|
||||||
<span
|
<span
|
||||||
>CPU Architecture: {$ae_loc.native_device?.meta_json
|
>CPU Architecture: {$ae_loc.native_device?.meta_json
|
||||||
?.arch || '...'}</span
|
?.arch || '...'}</span>
|
||||||
>
|
|
||||||
<span>Load: {cpu_load_pct}%</span>
|
<span>Load: {cpu_load_pct}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="w-full h-1.5 bg-surface-500/20 rounded-full overflow-hidden"
|
class="bg-surface-500/20 h-1.5 w-full overflow-hidden rounded-full">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="h-full transition-all duration-1000 {get_usage_color(cpu_load_pct)}"
|
class="h-full transition-all duration-1000 {get_usage_color(
|
||||||
style="width: {cpu_load_pct}%"
|
cpu_load_pct
|
||||||
></div>
|
)}"
|
||||||
|
style="width: {cpu_load_pct}%">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- RAM Usage -->
|
<!-- RAM Usage -->
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<div
|
<div
|
||||||
class="flex justify-between text-[9px] uppercase font-bold opacity-60"
|
class="flex justify-between text-[9px] font-bold uppercase opacity-60">
|
||||||
>
|
|
||||||
<span>Memory (RAM)</span>
|
<span>Memory (RAM)</span>
|
||||||
<span>{ram_usage_pct}% Used</span>
|
<span>{ram_usage_pct}% Used</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="w-full h-1.5 bg-surface-500/20 rounded-full overflow-hidden"
|
class="bg-surface-500/20 h-1.5 w-full overflow-hidden rounded-full">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="h-full transition-all duration-1000 {get_usage_color(
|
class="h-full transition-all duration-1000 {get_usage_color(
|
||||||
ram_usage_pct
|
ram_usage_pct
|
||||||
)}"
|
)}"
|
||||||
style="width: {ram_usage_pct}%"
|
style="width: {ram_usage_pct}%">
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="text-[8px] opacity-40 text-right italic">
|
</div>
|
||||||
|
<div class="text-right text-[8px] italic opacity-40">
|
||||||
Free: {$ae_loc.native_device?.meta_json?.free_mem || '...'} / {$ae_loc
|
Free: {$ae_loc.native_device?.meta_json?.free_mem || '...'} / {$ae_loc
|
||||||
.native_device?.meta_json?.total_mem || '...'}
|
.native_device?.meta_json?.total_mem || '...'}
|
||||||
</div>
|
</div>
|
||||||
@@ -101,26 +96,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Heartbeat & Sync Info -->
|
<!-- Heartbeat & Sync Info -->
|
||||||
<div class="grid grid-cols-2 gap-x-2 gap-y-2 w-full text-[10px] p-1">
|
<div class="grid w-full grid-cols-2 gap-x-2 gap-y-2 p-1 text-[10px]">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="opacity-50 text-[8px] uppercase font-bold"
|
<span class="text-[8px] font-bold uppercase opacity-50"
|
||||||
>Last Heartbeat</span
|
>Last Heartbeat</span>
|
||||||
>
|
|
||||||
<span
|
<span
|
||||||
class="font-mono {$events_sess.launcher.heartbeat_info
|
class="font-mono {$events_sess.launcher.heartbeat_info
|
||||||
.status === 'success'
|
.status === 'success'
|
||||||
? 'text-success-500'
|
? 'text-success-500'
|
||||||
: 'text-error-500'}"
|
: 'text-error-500'}">
|
||||||
>
|
|
||||||
{$events_sess.launcher.heartbeat_info.last_timestamp ||
|
{$events_sess.launcher.heartbeat_info.last_timestamp ||
|
||||||
'Pending...'}
|
'Pending...'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col text-right">
|
<div class="flex flex-col text-right">
|
||||||
<span class="opacity-50 text-[8px] uppercase font-bold"
|
<span class="text-[8px] font-bold uppercase opacity-50"
|
||||||
>Local File Cache</span
|
>Local File Cache</span>
|
||||||
>
|
|
||||||
<span class="font-mono">
|
<span class="font-mono">
|
||||||
{$events_sess.launcher.sync_stats.cached} / {$events_sess
|
{$events_sess.launcher.sync_stats.cached} / {$events_sess
|
||||||
.launcher.sync_stats.total}
|
.launcher.sync_stats.total}
|
||||||
@@ -129,19 +121,18 @@
|
|||||||
|
|
||||||
{#if $events_sess.launcher.sync_stats.currently_syncing}
|
{#if $events_sess.launcher.sync_stats.currently_syncing}
|
||||||
<div
|
<div
|
||||||
class="col-span-full bg-primary-500/10 p-2 rounded border border-primary-500/20 animate-pulse mt-1"
|
class="bg-primary-500/10 border-primary-500/20 col-span-full mt-1 animate-pulse rounded border p-2">
|
||||||
>
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<RefreshCw size="1em" class="animate-spin text-primary-500" />
|
<RefreshCw
|
||||||
|
size="1em"
|
||||||
|
class="text-primary-500 animate-spin" />
|
||||||
<div class="flex flex-col truncate">
|
<div class="flex flex-col truncate">
|
||||||
<span
|
<span
|
||||||
class="text-[8px] uppercase font-bold text-primary-500"
|
class="text-primary-500 text-[8px] font-bold uppercase"
|
||||||
>Syncing File...</span
|
>Syncing File...</span>
|
||||||
>
|
|
||||||
<span class="truncate italic opacity-80"
|
<span class="truncate italic opacity-80"
|
||||||
>{$events_sess.launcher.sync_stats
|
>{$events_sess.launcher.sync_stats
|
||||||
.currently_syncing}</span
|
.currently_syncing}</span>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -151,26 +142,22 @@
|
|||||||
<!-- Device Metadata (Edit Mode Only) -->
|
<!-- Device Metadata (Edit Mode Only) -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div
|
<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"
|
class="border-surface-500/10 col-span-full mt-1 flex flex-col gap-1 border-t px-1 pt-2 text-[9px] opacity-60">
|
||||||
>
|
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<span>Hostname:</span>
|
<span>Hostname:</span>
|
||||||
<span class="font-mono"
|
<span class="font-mono"
|
||||||
>{$ae_loc.native_device.info_hostname || '...'}</span
|
>{$ae_loc.native_device.info_hostname || '...'}</span>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between gap-4">
|
<div class="flex justify-between gap-4">
|
||||||
<span>IP Addresses:</span>
|
<span>IP Addresses:</span>
|
||||||
<span class="font-mono truncate"
|
<span class="truncate font-mono"
|
||||||
>{$ae_loc.native_device.info_ip_list || '...'}</span
|
>{$ae_loc.native_device.info_ip_list || '...'}</span>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 opacity-40">
|
<div class="mt-2 opacity-40">
|
||||||
<span class="text-[8px] uppercase font-bold"
|
<span class="text-[8px] font-bold uppercase"
|
||||||
>Raw Device JSON</span
|
>Raw Device JSON</span>
|
||||||
>
|
|
||||||
<pre
|
<pre
|
||||||
class="text-[7px] max-h-32 overflow-y-auto bg-black/20 p-1 rounded mt-1">
|
class="mt-1 max-h-32 overflow-y-auto rounded bg-black/20 p-1 text-[7px]">
|
||||||
{JSON.stringify($ae_loc.native_device, null, 2)}
|
{JSON.stringify($ae_loc.native_device, null, 2)}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,11 +14,20 @@
|
|||||||
|
|
||||||
async function handle_cleanup_now() {
|
async function handle_cleanup_now() {
|
||||||
const cache_root = $ae_loc.local_file_cache_path;
|
const cache_root = $ae_loc.local_file_cache_path;
|
||||||
if (!cache_root) { cleanup_status = 'Error: Cache path not set.'; return; }
|
if (!cache_root) {
|
||||||
|
cleanup_status = 'Error: Cache path not set.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
const max_age_hours = $events_loc.launcher.cleanup_tmp_max_age_hours ?? 24;
|
const max_age_hours = $events_loc.launcher.cleanup_tmp_max_age_hours ?? 24;
|
||||||
cleanup_status = 'Cleaning...';
|
cleanup_status = 'Cleaning...';
|
||||||
const result = await cleanup_tmp_files({ cache_root, max_age_minutes: max_age_hours * 60 });
|
const result = await cleanup_tmp_files({
|
||||||
cleanup_status = (result as any).success !== false ? 'Done.' : `Error: ${(result as any).error}`;
|
cache_root,
|
||||||
|
max_age_minutes: max_age_hours * 60
|
||||||
|
});
|
||||||
|
cleanup_status =
|
||||||
|
(result as any).success !== false
|
||||||
|
? 'Done.'
|
||||||
|
: `Error: ${(result as any).error}`;
|
||||||
setTimeout(() => (cleanup_status = ''), 4000);
|
setTimeout(() => (cleanup_status = ''), 4000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,9 +36,7 @@
|
|||||||
|
|
||||||
if (val == 'delete_idbs') {
|
if (val == 'delete_idbs') {
|
||||||
if (
|
if (
|
||||||
confirm(
|
confirm('Are you sure you want to delete ALL IndexedDB databases?')
|
||||||
'Are you sure you want to delete ALL IndexedDB databases?'
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
indexedDB.deleteDatabase('ae_archives_db');
|
indexedDB.deleteDatabase('ae_archives_db');
|
||||||
indexedDB.deleteDatabase('ae_core_db');
|
indexedDB.deleteDatabase('ae_core_db');
|
||||||
@@ -37,9 +44,7 @@
|
|||||||
indexedDB.deleteDatabase('ae_journals_db');
|
indexedDB.deleteDatabase('ae_journals_db');
|
||||||
indexedDB.deleteDatabase('ae_posts_db');
|
indexedDB.deleteDatabase('ae_posts_db');
|
||||||
indexedDB.deleteDatabase('ae_sponsorships_db');
|
indexedDB.deleteDatabase('ae_sponsorships_db');
|
||||||
alert(
|
alert('All IndexedDB databases deleted. Please reload the app.');
|
||||||
'All IndexedDB databases deleted. Please reload the app.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if (val == 'delete_idbs_events') {
|
} else if (val == 'delete_idbs_events') {
|
||||||
if (
|
if (
|
||||||
@@ -48,9 +53,7 @@
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
indexedDB.deleteDatabase('ae_events_db');
|
indexedDB.deleteDatabase('ae_events_db');
|
||||||
alert(
|
alert('Events IndexedDB database deleted. Please reload the app.');
|
||||||
'Events IndexedDB database deleted. Please reload the app.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if (val == 'delete_local') {
|
} else if (val == 'delete_local') {
|
||||||
if (confirm('Are you sure you want to delete ALL local config?')) {
|
if (confirm('Are you sure you want to delete ALL local config?')) {
|
||||||
@@ -79,44 +82,43 @@
|
|||||||
icon={Wrench}
|
icon={Wrench}
|
||||||
bind:state={$events_loc.launcher.section_state__local_actions}
|
bind:state={$events_loc.launcher.section_state__local_actions}
|
||||||
{on_expand}
|
{on_expand}
|
||||||
description="Cache wiping and global menu toggles"
|
description="Cache wiping and global menu toggles">
|
||||||
>
|
|
||||||
<div class="col-span-full flex flex-col gap-3">
|
<div class="col-span-full flex flex-col gap-3">
|
||||||
<!-- 1. Reset Actions -->
|
<!-- 1. Reset Actions -->
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span
|
<span
|
||||||
class="text-[9px] font-bold uppercase opacity-50 ml-1 text-error-500"
|
class="text-error-500 ml-1 text-[9px] font-bold uppercase opacity-50"
|
||||||
>Maintenance & Resets</span
|
>Maintenance & Resets</span>
|
||||||
>
|
|
||||||
<select
|
<select
|
||||||
bind:value={selected_reset}
|
bind:value={selected_reset}
|
||||||
onchange={(e) =>
|
onchange={(e) =>
|
||||||
handle_reset_action((e.target as HTMLSelectElement).value)}
|
handle_reset_action((e.target as HTMLSelectElement).value)}
|
||||||
class="select select-sm text-xs preset-tonal-surface h-8 text-error-500 border-error-500/20"
|
class="select select-sm preset-tonal-surface text-error-500 border-error-500/20 h-8 text-xs">
|
||||||
>
|
|
||||||
<option value="">-- Select a reset action --</option>
|
<option value="">-- Select a reset action --</option>
|
||||||
<option value="delete_idbs">Delete ALL Databases</option>
|
<option value="delete_idbs">Delete ALL Databases</option>
|
||||||
<option value="delete_idbs_events">Delete Events DB Only</option
|
<option value="delete_idbs_events"
|
||||||
>
|
>Delete Events DB Only</option>
|
||||||
<option value="delete_local">Wipe ALL Local Storage</option>
|
<option value="delete_local">Wipe ALL Local Storage</option>
|
||||||
<option value="delete_local_events"
|
<option value="delete_local_events"
|
||||||
>Wipe Events Storage Only</option
|
>Wipe Events Storage Only</option>
|
||||||
>
|
|
||||||
</select>
|
</select>
|
||||||
<span class="text-[8px] opacity-40 italic ml-1 leading-tight">
|
<span class="ml-1 text-[8px] leading-tight italic opacity-40">
|
||||||
* Destructive actions require browser confirmation.
|
* Destructive actions require browser confirmation.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 2. UI Toggles -->
|
<!-- 2. UI Toggles -->
|
||||||
<div class="grid grid-cols-2 gap-2 mt-1">
|
<div class="mt-1 grid grid-cols-2 gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => ($ae_loc.sys_menu.hide = !$ae_loc.sys_menu.hide)}
|
onclick={() => ($ae_loc.sys_menu.hide = !$ae_loc.sys_menu.hide)}
|
||||||
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
|
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
|
||||||
title="Show/Hide Aether global system menu"
|
title="Show/Hide Aether global system menu">
|
||||||
>
|
{#if $ae_loc.sys_menu.hide}<Eye
|
||||||
{#if $ae_loc.sys_menu.hide}<Eye size="1em" class="mr-2" />{:else}<EyeOff size="1em" class="mr-2" />{/if}
|
size="1em"
|
||||||
|
class="mr-2" />{:else}<EyeOff
|
||||||
|
size="1em"
|
||||||
|
class="mr-2" />{/if}
|
||||||
{$ae_loc.sys_menu.hide ? 'Show' : 'Hide'} Sys Menu
|
{$ae_loc.sys_menu.hide ? 'Show' : 'Hide'} Sys Menu
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -125,41 +127,51 @@
|
|||||||
onclick={() =>
|
onclick={() =>
|
||||||
($ae_loc.debug_menu.hide = !$ae_loc.debug_menu.hide)}
|
($ae_loc.debug_menu.hide = !$ae_loc.debug_menu.hide)}
|
||||||
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
|
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
|
||||||
title="Show/Hide Aether global debug menu"
|
title="Show/Hide Aether global debug menu">
|
||||||
>
|
{#if $ae_loc.debug_menu.hide}<Bug
|
||||||
{#if $ae_loc.debug_menu.hide}<Bug size="1em" class="mr-2" />{:else}<BugOff size="1em" class="mr-2" />{/if}
|
size="1em"
|
||||||
|
class="mr-2" />{:else}<BugOff
|
||||||
|
size="1em"
|
||||||
|
class="mr-2" />{/if}
|
||||||
{$ae_loc.debug_menu.hide ? 'Show' : 'Hide'} Debug
|
{$ae_loc.debug_menu.hide ? 'Show' : 'Hide'} Debug
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 3. Cache .tmp Cleanup (Native Only) -->
|
<!-- 3. Cache .tmp Cleanup (Native Only) -->
|
||||||
{#if $ae_loc.is_native && $ae_loc.local_file_cache_path}
|
{#if $ae_loc.is_native && $ae_loc.local_file_cache_path}
|
||||||
<div class="flex flex-col gap-1 border-t border-surface-500/20 pt-2 mt-1">
|
<div
|
||||||
<span class="text-[9px] font-bold uppercase opacity-50 ml-1">Cache Maintenance</span>
|
class="border-surface-500/20 mt-1 flex flex-col gap-1 border-t pt-2">
|
||||||
|
<span class="ml-1 text-[9px] font-bold uppercase opacity-50"
|
||||||
|
>Cache Maintenance</span>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<label for="cleanup_max_age" class="text-[10px] opacity-70 whitespace-nowrap">Max age (hrs):</label>
|
<label
|
||||||
|
for="cleanup_max_age"
|
||||||
|
class="text-[10px] whitespace-nowrap opacity-70"
|
||||||
|
>Max age (hrs):</label>
|
||||||
<input
|
<input
|
||||||
id="cleanup_max_age"
|
id="cleanup_max_age"
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
max="168"
|
max="168"
|
||||||
bind:value={$events_loc.launcher.cleanup_tmp_max_age_hours}
|
bind:value={
|
||||||
class="input input-sm w-16 h-7 text-xs text-center preset-tonal-surface"
|
$events_loc.launcher.cleanup_tmp_max_age_hours
|
||||||
placeholder="24"
|
}
|
||||||
/>
|
class="input input-sm preset-tonal-surface h-7 w-16 text-center text-xs"
|
||||||
|
placeholder="24" />
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={handle_cleanup_now}
|
onclick={handle_cleanup_now}
|
||||||
class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 grow"
|
class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 grow">
|
||||||
>
|
|
||||||
<Eraser size="0.85em" class="mr-1" /> Clean .tmp Now
|
<Eraser size="0.85em" class="mr-1" /> Clean .tmp Now
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{#if cleanup_status}
|
{#if cleanup_status}
|
||||||
<span class="text-[9px] italic opacity-60 ml-1">{cleanup_status}</span>
|
<span class="ml-1 text-[9px] italic opacity-60"
|
||||||
|
>{cleanup_status}</span>
|
||||||
{/if}
|
{/if}
|
||||||
<span class="text-[8px] opacity-40 italic ml-1 leading-tight">
|
<span class="ml-1 text-[8px] leading-tight italic opacity-40">
|
||||||
Removes stale in-progress download artifacts. Auto-runs on startup.
|
Removes stale in-progress download artifacts. Auto-runs on
|
||||||
|
startup.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -167,14 +179,12 @@
|
|||||||
<!-- 4. Connection Summary (Edit Mode Only) -->
|
<!-- 4. Connection Summary (Edit Mode Only) -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div
|
<div
|
||||||
class="col-span-full border-t border-surface-500/20 pt-2 mt-1 flex flex-col gap-1"
|
class="border-surface-500/20 col-span-full mt-1 flex flex-col gap-1 border-t pt-2">
|
||||||
>
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
API Context
|
||||||
>API Context</p
|
</p>
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="bg-black/10 p-2 rounded text-[9px] font-mono opacity-60 break-all leading-tight"
|
class="rounded bg-black/10 p-2 font-mono text-[9px] leading-tight break-all opacity-60">
|
||||||
>
|
|
||||||
Endpoint: {$ae_api.base_url}<br />
|
Endpoint: {$ae_api.base_url}<br />
|
||||||
Account: {$ae_loc.account_id}
|
Account: {$ae_loc.account_id}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,21 @@
|
|||||||
import { events_loc, events_sess } from '$lib/stores/ae_events_stores';
|
import { events_loc, events_sess } from '$lib/stores/ae_events_stores';
|
||||||
import * as native from '$lib/electron/electron_relay';
|
import * as native from '$lib/electron/electron_relay';
|
||||||
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
||||||
import { Code, Columns2, FlaskConical, FolderOpen, Image, Maximize2, Monitor, Play, Power, RefreshCw, SkipBack, SkipForward, Square } from '@lucide/svelte';
|
import {
|
||||||
|
Code,
|
||||||
|
Columns2,
|
||||||
|
FlaskConical,
|
||||||
|
FolderOpen,
|
||||||
|
Image,
|
||||||
|
Maximize2,
|
||||||
|
Monitor,
|
||||||
|
Play,
|
||||||
|
Power,
|
||||||
|
RefreshCw,
|
||||||
|
SkipBack,
|
||||||
|
SkipForward,
|
||||||
|
Square
|
||||||
|
} from '@lucide/svelte';
|
||||||
interface Props {
|
interface Props {
|
||||||
on_expand?: () => void;
|
on_expand?: () => void;
|
||||||
}
|
}
|
||||||
@@ -42,9 +56,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Modal state for dangerous actions
|
// Modal state for dangerous actions
|
||||||
let show_power_confirm = $state<{ action: string; label: string } | null>(
|
let show_power_confirm = $state<{ action: string; label: string } | null>(null);
|
||||||
null
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Launcher_Cfg_Section
|
<Launcher_Cfg_Section
|
||||||
@@ -53,51 +65,50 @@
|
|||||||
bind:state={$events_loc.launcher.section_state__native_os}
|
bind:state={$events_loc.launcher.section_state__native_os}
|
||||||
{on_expand}
|
{on_expand}
|
||||||
description="OS: {$ae_loc.native_device?.meta_json?.platform ||
|
description="OS: {$ae_loc.native_device?.meta_json?.platform ||
|
||||||
'...'} | Kiosk & Apps"
|
'...'} | Kiosk & Apps">
|
||||||
>
|
|
||||||
<!-- Dev preview banner: shown when edit_mode is on but not running in Electron.
|
<!-- Dev preview banner: shown when edit_mode is on but not running in Electron.
|
||||||
electron_relay functions all return null when native is absent — no errors. -->
|
electron_relay functions all return null when native is absent — no errors. -->
|
||||||
{#if $ae_loc.edit_mode && !$ae_loc.is_native}
|
{#if $ae_loc.edit_mode && !$ae_loc.is_native}
|
||||||
<div class="flex items-center gap-2 px-2 py-1.5 rounded-lg bg-warning-500/10 border border-warning-500/30 mb-1">
|
<div
|
||||||
|
class="bg-warning-500/10 border-warning-500/30 mb-1 flex items-center gap-2 rounded-lg border px-2 py-1.5">
|
||||||
<FlaskConical size="0.75em" class="text-warning-500" />
|
<FlaskConical size="0.75em" class="text-warning-500" />
|
||||||
<span class="text-[9px] text-warning-500 font-bold uppercase tracking-wide">Dev Preview — controls visible but non-functional without Electron</span>
|
<span
|
||||||
|
class="text-warning-500 text-[9px] font-bold tracking-wide uppercase"
|
||||||
|
>Dev Preview — controls visible but non-functional without
|
||||||
|
Electron</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if system_status}
|
{#if system_status}
|
||||||
<div
|
<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"
|
class="bg-surface-500/10 text-primary-500 border-primary-500/20 col-span-full animate-pulse rounded border py-1 text-center text-[10px] italic">
|
||||||
>
|
|
||||||
{system_status}
|
{system_status}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- 1. Window & Folders (Common) -->
|
<!-- 1. Window & Folders (Common) -->
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
>Folders & View</p
|
Folders & View
|
||||||
>
|
</p>
|
||||||
<div class="grid grid-cols-2 gap-1">
|
<div class="grid grid-cols-2 gap-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() =>
|
onclick={() =>
|
||||||
native.open_folder($ae_loc.local_file_cache_path)}
|
native.open_folder($ae_loc.local_file_cache_path)}
|
||||||
class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start"
|
class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start">
|
||||||
>
|
|
||||||
<FolderOpen size="0.85em" class="mr-1 shrink-0" /> Cache
|
<FolderOpen size="0.85em" class="mr-1 shrink-0" /> Cache
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => native.open_folder($ae_loc.host_file_temp_path)}
|
onclick={() => native.open_folder($ae_loc.host_file_temp_path)}
|
||||||
class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start"
|
class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start">
|
||||||
>
|
|
||||||
<FolderOpen size="0.85em" class="mr-1 shrink-0" /> Temp
|
<FolderOpen size="0.85em" class="mr-1 shrink-0" /> Temp
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => native.window_control({ action: 'maximize' })}
|
onclick={() => native.window_control({ action: 'maximize' })}
|
||||||
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
|
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500">
|
||||||
>
|
|
||||||
<Maximize2 size="0.85em" class="mr-1" /> Maximize
|
<Maximize2 size="0.85em" class="mr-1" /> Maximize
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@@ -107,8 +118,7 @@
|
|||||||
native.window_control({ action: 'kiosk', value: true }),
|
native.window_control({ action: 'kiosk', value: true }),
|
||||||
'Kiosk Mode'
|
'Kiosk Mode'
|
||||||
)}
|
)}
|
||||||
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500"
|
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500">
|
||||||
>
|
|
||||||
<Monitor size="0.85em" class="mr-1" /> Kiosk
|
<Monitor size="0.85em" class="mr-1" /> Kiosk
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -116,14 +126,13 @@
|
|||||||
|
|
||||||
<!-- 2. Presentation Remote Control (Common) -->
|
<!-- 2. Presentation Remote Control (Common) -->
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex flex-row justify-between items-center px-1">
|
<div class="flex flex-row items-center justify-between px-1">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50"
|
<p class="text-[9px] font-bold uppercase opacity-50">
|
||||||
>Remote Control</p
|
Remote Control
|
||||||
>
|
</p>
|
||||||
<select
|
<select
|
||||||
bind:value={remote_app}
|
bind:value={remote_app}
|
||||||
class="select select-sm py-0 h-5 text-[9px] w-24 preset-tonal-surface"
|
class="select select-sm preset-tonal-surface h-5 w-24 py-0 text-[9px]">
|
||||||
>
|
|
||||||
<option value="powerpoint">PowerPoint</option>
|
<option value="powerpoint">PowerPoint</option>
|
||||||
<option value="keynote">Keynote</option>
|
<option value="keynote">Keynote</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -134,39 +143,34 @@
|
|||||||
type="button"
|
type="button"
|
||||||
onclick={() => handle_remote_control('prev')}
|
onclick={() => handle_remote_control('prev')}
|
||||||
class="btn btn-sm preset-tonal-secondary"
|
class="btn btn-sm preset-tonal-secondary"
|
||||||
title="Previous Slide"
|
title="Previous Slide">
|
||||||
>
|
|
||||||
<SkipBack size="1em" />
|
<SkipBack size="1em" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => handle_remote_control('start')}
|
onclick={() => handle_remote_control('start')}
|
||||||
class="btn btn-sm preset-tonal-success"
|
class="btn btn-sm preset-tonal-success"
|
||||||
title="Start/Resume Slideshow"
|
title="Start/Resume Slideshow">
|
||||||
>
|
|
||||||
<Play size="1em" />
|
<Play size="1em" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => handle_remote_control('stop')}
|
onclick={() => handle_remote_control('stop')}
|
||||||
class="btn btn-sm preset-tonal-error"
|
class="btn btn-sm preset-tonal-error"
|
||||||
title="Stop Slideshow"
|
title="Stop Slideshow">
|
||||||
>
|
|
||||||
<Square size="1em" />
|
<Square size="1em" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => handle_remote_control('next')}
|
onclick={() => handle_remote_control('next')}
|
||||||
class="btn btn-sm preset-tonal-secondary"
|
class="btn btn-sm preset-tonal-secondary"
|
||||||
title="Next Slide"
|
title="Next Slide">
|
||||||
>
|
|
||||||
<SkipForward size="1em" />
|
<SkipForward size="1em" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{#if remote_status}
|
{#if remote_status}
|
||||||
<div
|
<div
|
||||||
class="text-[9px] text-center italic animate-pulse text-primary-500"
|
class="text-primary-500 animate-pulse text-center text-[9px] italic">
|
||||||
>
|
|
||||||
{remote_status}
|
{remote_status}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -175,13 +179,12 @@
|
|||||||
<!-- 3. Technical Management (Edit Mode Only) -->
|
<!-- 3. Technical Management (Edit Mode Only) -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div
|
<div
|
||||||
class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-3"
|
class="border-surface-500/20 col-span-full mt-1 flex flex-col gap-3 border-t pt-3">
|
||||||
>
|
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50"
|
<p class="text-[9px] font-bold uppercase opacity-50">
|
||||||
>System Actions</p
|
System Actions
|
||||||
>
|
</p>
|
||||||
<div class="grid grid-cols-1 gap-1">
|
<div class="grid grid-cols-1 gap-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -192,9 +195,9 @@
|
|||||||
}),
|
}),
|
||||||
'Extend Display'
|
'Extend Display'
|
||||||
)}
|
)}
|
||||||
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500 justify-start"
|
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500 justify-start">
|
||||||
>
|
<Columns2 size="0.85em" class="mr-1 shrink-0" /> Extend
|
||||||
<Columns2 size="0.85em" class="mr-1 shrink-0" /> Extend Mode
|
Mode
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -206,17 +209,15 @@
|
|||||||
'Wallpaper'
|
'Wallpaper'
|
||||||
)}
|
)}
|
||||||
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500 justify-start"
|
class="btn btn-xs preset-tonal-surface hover:preset-filled-primary-500 justify-start"
|
||||||
disabled={!$ae_loc.site_header_image_path}
|
disabled={!$ae_loc.site_header_image_path}>
|
||||||
>
|
|
||||||
<Image size="0.85em" class="mr-1 shrink-0" /> Reset Wallpaper
|
<Image size="0.85em" class="mr-1 shrink-0" /> Reset Wallpaper
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span
|
<span
|
||||||
class="text-[9px] font-bold uppercase opacity-50 text-error-500"
|
class="text-error-500 text-[9px] font-bold uppercase opacity-50"
|
||||||
>Power</span
|
>Power</span>
|
||||||
>
|
|
||||||
<div class="grid grid-cols-1 gap-1">
|
<div class="grid grid-cols-1 gap-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -225,8 +226,7 @@
|
|||||||
action: 'reboot',
|
action: 'reboot',
|
||||||
label: 'Reboot Laptop'
|
label: 'Reboot Laptop'
|
||||||
})}
|
})}
|
||||||
class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 justify-start"
|
class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 justify-start">
|
||||||
>
|
|
||||||
<RefreshCw size="0.85em" class="mr-1 shrink-0" /> Reboot
|
<RefreshCw size="0.85em" class="mr-1 shrink-0" /> Reboot
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@@ -236,8 +236,7 @@
|
|||||||
action: 'shutdown',
|
action: 'shutdown',
|
||||||
label: 'Shutdown Laptop'
|
label: 'Shutdown Laptop'
|
||||||
})}
|
})}
|
||||||
class="btn btn-xs preset-tonal-error hover:preset-filled-error-500 justify-start"
|
class="btn btn-xs preset-tonal-error hover:preset-filled-error-500 justify-start">
|
||||||
>
|
|
||||||
<Power size="0.85em" class="mr-1 shrink-0" /> Shutdown
|
<Power size="0.85em" class="mr-1 shrink-0" /> Shutdown
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -245,16 +244,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
>Terminal Access</p
|
Terminal Access
|
||||||
>
|
</p>
|
||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={$events_sess.launcher.manual_cmd}
|
bind:value={$events_sess.launcher.manual_cmd}
|
||||||
placeholder="ls -la"
|
placeholder="ls -la"
|
||||||
class="input input-sm grow text-[10px] preset-tonal-surface h-7"
|
class="input input-sm preset-tonal-surface h-7 grow text-[10px]" />
|
||||||
/>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
@@ -268,13 +266,12 @@
|
|||||||
(res as any).error ||
|
(res as any).error ||
|
||||||
'No Output';
|
'No Output';
|
||||||
}}
|
}}
|
||||||
class="btn btn-sm preset-filled-secondary hover:preset-filled-primary-500 text-[10px] h-7"
|
class="btn btn-sm preset-filled-secondary hover:preset-filled-primary-500 h-7 text-[10px]"
|
||||||
>Run</button
|
>Run</button>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
{#if test_cmd_result}
|
{#if 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>
|
class="border-surface-500/50 mt-1 max-h-24 overflow-x-auto rounded border bg-black p-2 text-[8px] text-green-500 shadow-inner">{test_cmd_result}</pre>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -284,25 +281,21 @@
|
|||||||
<!-- Power Confirmation Modal -->
|
<!-- Power Confirmation Modal -->
|
||||||
{#if show_power_confirm}
|
{#if show_power_confirm}
|
||||||
<div
|
<div
|
||||||
class="fixed inset-0 z-[1000] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
|
class="fixed inset-0 z-[1000] flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="card p-6 w-full max-w-sm preset-filled-surface-100-900 border border-error-500 shadow-2xl animate-in zoom-in-95 duration-200"
|
class="card preset-filled-surface-100-900 border-error-500 animate-in zoom-in-95 w-full max-w-sm border p-6 shadow-2xl duration-200">
|
||||||
>
|
<h4 class="h4 text-error-500 mb-2 font-bold">
|
||||||
<h4 class="h4 text-error-500 font-bold mb-2">
|
|
||||||
Confirm System Action
|
Confirm System Action
|
||||||
</h4>
|
</h4>
|
||||||
<p class="text-sm opacity-80 mb-6">
|
<p class="mb-6 text-sm opacity-80">
|
||||||
Are you sure you want to <strong
|
Are you sure you want to <strong
|
||||||
>{show_power_confirm.action}</strong
|
>{show_power_confirm.action}</strong> this host machine?
|
||||||
> this host machine?
|
|
||||||
</p>
|
</p>
|
||||||
<div class="flex justify-end gap-2">
|
<div class="flex justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => (show_power_confirm = null)}
|
onclick={() => (show_power_confirm = null)}
|
||||||
class="btn btn-sm preset-tonal-surface">Cancel</button
|
class="btn btn-sm preset-tonal-surface">Cancel</button>
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
@@ -314,8 +307,7 @@
|
|||||||
action
|
action
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
class="btn btn-sm preset-filled-error"
|
class="btn btn-sm preset-filled-error">
|
||||||
>
|
|
||||||
Confirm {show_power_confirm.action}
|
Confirm {show_power_confirm.action}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,62 +16,54 @@
|
|||||||
{on_expand}
|
{on_expand}
|
||||||
description="Idle: {($events_loc.launcher.idle_timer / 60000).toFixed(
|
description="Idle: {($events_loc.launcher.idle_timer / 60000).toFixed(
|
||||||
1
|
1
|
||||||
)}m | Auto-Posters"
|
)}m | Auto-Posters">
|
||||||
>
|
|
||||||
<!-- Content omitted for brevity, preserved in file -->
|
<!-- Content omitted for brevity, preserved in file -->
|
||||||
<div class="col-span-full flex flex-col gap-3">
|
<div class="col-span-full flex flex-col gap-3">
|
||||||
<!-- 1. Technical Timers (Edit Mode Only) -->
|
<!-- 1. Technical Timers (Edit Mode Only) -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
>Screen Saver Timers (ms)</p
|
Screen Saver Timers (ms)
|
||||||
>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="grid grid-cols-1 gap-2 bg-surface-500/5 p-2 rounded border border-surface-500/10"
|
class="bg-surface-500/5 border-surface-500/10 grid grid-cols-1 gap-2 rounded border p-2">
|
||||||
>
|
<div class="flex items-center justify-between gap-4">
|
||||||
<div class="flex justify-between items-center gap-4">
|
|
||||||
<span class="text-[10px] opacity-60">Idle Wait</span>
|
<span class="text-[10px] opacity-60">Idle Wait</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min={3000}
|
min={3000}
|
||||||
bind:value={$events_loc.launcher.idle_timer}
|
bind:value={$events_loc.launcher.idle_timer}
|
||||||
class="input input-sm text-[10px] h-7 w-24 text-right preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 w-24 text-right text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center gap-4">
|
<div class="flex items-center justify-between gap-4">
|
||||||
<span class="text-[10px] opacity-60">Cycle Check</span>
|
<span class="text-[10px] opacity-60">Cycle Check</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min={500}
|
min={500}
|
||||||
bind:value={$events_loc.launcher.idle_cycle}
|
bind:value={$events_loc.launcher.idle_cycle}
|
||||||
class="input input-sm text-[10px] h-7 w-24 text-right preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 w-24 text-right text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center gap-4">
|
<div class="flex items-center justify-between gap-4">
|
||||||
<span class="text-[10px] opacity-60"
|
<span class="text-[10px] opacity-60"
|
||||||
>Image Rotation</span
|
>Image Rotation</span>
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min={750}
|
min={750}
|
||||||
bind:value={$events_loc.launcher.idle_loop_period}
|
bind:value={$events_loc.launcher.idle_loop_period}
|
||||||
class="input input-sm text-[10px] h-7 w-24 text-right preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 w-24 text-right text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- 2. Read Only Summary (Normal Mode) -->
|
<!-- 2. Read Only Summary (Normal Mode) -->
|
||||||
<div
|
<div
|
||||||
class="bg-surface-500/5 p-3 rounded-lg border border-surface-500/10 flex flex-col gap-2"
|
class="bg-surface-500/5 border-surface-500/10 flex flex-col gap-2 rounded-lg border p-3">
|
||||||
>
|
<div class="flex items-center justify-between text-xs">
|
||||||
<div class="flex justify-between items-center text-xs">
|
|
||||||
<span class="opacity-60">Active Idle Timeout:</span>
|
<span class="opacity-60">Active Idle Timeout:</span>
|
||||||
<span class="font-bold text-primary-500"
|
<span class="text-primary-500 font-bold"
|
||||||
>{($events_loc.launcher.idle_timer / 60000).toFixed(1)} minutes</span
|
>{($events_loc.launcher.idle_timer / 60000).toFixed(1)} minutes</span>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="text-[9px] opacity-40 italic">
|
<p class="text-[9px] italic opacity-40">
|
||||||
The screen saver automatically rotates digital posters when
|
The screen saver automatically rotates digital posters when
|
||||||
no activity is detected for the specified time.
|
no activity is detected for the specified time.
|
||||||
</p>
|
</p>
|
||||||
@@ -79,7 +71,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<p class="text-[8px] opacity-40 italic uppercase tracking-tighter">
|
<p class="text-[8px] tracking-tighter uppercase italic opacity-40">
|
||||||
Applies to "Poster" session types only
|
Applies to "Poster" session types only
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,44 +54,39 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
class="w-full transition-all duration-300 border rounded-lg overflow-hidden mb-2 {!is_open
|
class="mb-2 w-full overflow-hidden rounded-lg border transition-all duration-300 {!is_open
|
||||||
? 'preset-outlined-surface-300-700'
|
? 'preset-outlined-surface-300-700'
|
||||||
: ''} {state === 'auto'
|
: ''} {state === 'auto'
|
||||||
? 'preset-outlined-primary-500 shadow-xl'
|
? 'preset-outlined-primary-500 shadow-xl'
|
||||||
: ''} {state === 'pinned'
|
: ''} {state === 'pinned'
|
||||||
? 'preset-outlined-warning-500 shadow-xl'
|
? 'preset-outlined-warning-500 shadow-xl'
|
||||||
: ''}"
|
: ''}">
|
||||||
>
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<header
|
<header
|
||||||
class="flex flex-row items-center justify-between p-2 cursor-pointer transition-colors {!is_open
|
class="flex cursor-pointer flex-row items-center justify-between p-2 transition-colors {!is_open
|
||||||
? 'bg-surface-500/5'
|
? 'bg-surface-500/5'
|
||||||
: ''} {state === 'auto' ? 'bg-primary-500/10' : ''} {state ===
|
: ''} {state === 'auto' ? 'bg-primary-500/10' : ''} {state ===
|
||||||
'pinned'
|
'pinned'
|
||||||
? 'bg-warning-500/10'
|
? 'bg-warning-500/10'
|
||||||
: ''}"
|
: ''}"
|
||||||
onclick={toggle_expand}
|
onclick={toggle_expand}>
|
||||||
>
|
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<Icon
|
<Icon
|
||||||
size="1em"
|
size="1em"
|
||||||
class="w-5 text-center opacity-70 {state === 'auto'
|
class="w-5 text-center opacity-70 {state === 'auto'
|
||||||
? 'text-primary-500'
|
? 'text-primary-500'
|
||||||
: ''} {state === 'pinned' ? 'text-warning-500' : ''}"
|
: ''} {state === 'pinned' ? 'text-warning-500' : ''}" />
|
||||||
/>
|
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span
|
<span
|
||||||
class="text-sm font-bold tracking-tight uppercase {!is_open
|
class="text-sm font-bold tracking-tight uppercase {!is_open
|
||||||
? 'opacity-50'
|
? 'opacity-50'
|
||||||
: ''}">{title}</span
|
: ''}">{title}</span>
|
||||||
>
|
|
||||||
{#if description && !is_open}
|
{#if description && !is_open}
|
||||||
<span
|
<span
|
||||||
class="text-[9px] opacity-40 italic truncate max-w-[180px]"
|
class="max-w-[180px] truncate text-[9px] italic opacity-40"
|
||||||
>{description}</span
|
>{description}</span>
|
||||||
>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -106,16 +101,19 @@
|
|||||||
class:text-warning-500={state === 'pinned'}
|
class:text-warning-500={state === 'pinned'}
|
||||||
title={state === 'pinned'
|
title={state === 'pinned'
|
||||||
? 'Unpin Section'
|
? 'Unpin Section'
|
||||||
: 'Pin Section (Stay open)'}
|
: 'Pin Section (Stay open)'}>
|
||||||
>
|
|
||||||
<Pin size="0.7em" />
|
<Pin size="0.7em" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Collapse Icon -->
|
<!-- Collapse Icon -->
|
||||||
{#if is_open}
|
{#if is_open}
|
||||||
<ChevronDown size="1em" class="transition-transform duration-300 opacity-30" />
|
<ChevronDown
|
||||||
|
size="1em"
|
||||||
|
class="opacity-30 transition-transform duration-300" />
|
||||||
{:else}
|
{:else}
|
||||||
<ChevronRight size="1em" class="transition-transform duration-300 opacity-30" />
|
<ChevronRight
|
||||||
|
size="1em"
|
||||||
|
class="opacity-30 transition-transform duration-300" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -124,27 +122,22 @@
|
|||||||
{#if is_open}
|
{#if is_open}
|
||||||
<div
|
<div
|
||||||
transition:slide={{ duration: 300 }}
|
transition:slide={{ duration: 300 }}
|
||||||
class="p-3 bg-white/5 dark:bg-black/5"
|
class="bg-white/5 p-3 dark:bg-black/5">
|
||||||
>
|
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div class="mb-2 flex justify-between items-center px-1">
|
<div class="mb-2 flex items-center justify-between px-1">
|
||||||
<span
|
<span
|
||||||
class="text-[8px] uppercase font-bold tracking-widest text-primary-500/60 flex items-center gap-1"
|
class="text-primary-500/60 flex items-center gap-1 text-[8px] font-bold tracking-widest uppercase">
|
||||||
>
|
|
||||||
<Pencil size="0.7em" /> Technical Mode
|
<Pencil size="0.7em" /> Technical Mode
|
||||||
</span>
|
</span>
|
||||||
{#if state === 'pinned'}
|
{#if state === 'pinned'}
|
||||||
<span
|
<span
|
||||||
class="badge preset-filled-warning text-[8px] uppercase"
|
class="badge preset-filled-warning text-[8px] uppercase"
|
||||||
>Pinned</span
|
>Pinned</span>
|
||||||
>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div
|
<div class="grid grid-cols-1 gap-3">
|
||||||
class="grid grid-cols-1 gap-3"
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,21 +15,24 @@
|
|||||||
bind:state={$events_loc.launcher.section_state__sync_timers}
|
bind:state={$events_loc.launcher.section_state__sync_timers}
|
||||||
{on_expand}
|
{on_expand}
|
||||||
description="Prefix: {$ae_loc.native_device?.hash_prefix_length ||
|
description="Prefix: {$ae_loc.native_device?.hash_prefix_length ||
|
||||||
2} | Loops: Active"
|
2} | Loops: Active">
|
||||||
>
|
|
||||||
<!-- Content omitted for brevity, preserved in file -->
|
<!-- Content omitted for brevity, preserved in file -->
|
||||||
<!-- Pause toggle: always visible — useful during testing or onsite troubleshooting -->
|
<!-- Pause toggle: always visible — useful during testing or onsite troubleshooting -->
|
||||||
<div class="flex items-center justify-between mb-2 p-2 rounded border border-surface-500/10 bg-surface-500/5">
|
<div
|
||||||
<span class="text-[10px] font-bold uppercase tracking-wider opacity-70">
|
class="border-surface-500/10 bg-surface-500/5 mb-2 flex items-center justify-between rounded border p-2">
|
||||||
{$events_loc.launcher.sync_paused ? '⏸ Sync Paused' : '▶ Sync Active'}
|
<span class="text-[10px] font-bold tracking-wider uppercase opacity-70">
|
||||||
|
{$events_loc.launcher.sync_paused
|
||||||
|
? '⏸ Sync Paused'
|
||||||
|
: '▶ Sync Active'}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => ($events_loc.launcher.sync_paused = !$events_loc.launcher.sync_paused)}
|
onclick={() =>
|
||||||
|
($events_loc.launcher.sync_paused =
|
||||||
|
!$events_loc.launcher.sync_paused)}
|
||||||
class="btn btn-xs transition-all"
|
class="btn btn-xs transition-all"
|
||||||
class:preset-tonal-warning={$events_loc.launcher.sync_paused}
|
class:preset-tonal-warning={$events_loc.launcher.sync_paused}
|
||||||
class:preset-tonal-success={!$events_loc.launcher.sync_paused}
|
class:preset-tonal-success={!$events_loc.launcher.sync_paused}>
|
||||||
>
|
|
||||||
{#if $events_loc.launcher.sync_paused}
|
{#if $events_loc.launcher.sync_paused}
|
||||||
<Play size="0.85em" class="mr-1" /> Resume
|
<Play size="0.85em" class="mr-1" /> Resume
|
||||||
{:else}
|
{:else}
|
||||||
@@ -43,92 +46,80 @@
|
|||||||
<!-- Technical Timers (Edit Mode Only) -->
|
<!-- Technical Timers (Edit Mode Only) -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
Polling Periods (ms)
|
||||||
>Polling Periods (ms)</p
|
</p>
|
||||||
>
|
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[8px] opacity-60">Event Data</span
|
<span class="text-[8px] opacity-60"
|
||||||
>
|
>Event Data</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
bind:value={
|
bind:value={
|
||||||
$events_loc.launcher.sync_intervals.event
|
$events_loc.launcher.sync_intervals.event
|
||||||
}
|
}
|
||||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[8px] opacity-60"
|
<span class="text-[8px] opacity-60"
|
||||||
>Device Config</span
|
>Device Config</span>
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
bind:value={
|
bind:value={
|
||||||
$events_loc.launcher.sync_intervals.device
|
$events_loc.launcher.sync_intervals.device
|
||||||
}
|
}
|
||||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[8px] opacity-60"
|
<span class="text-[8px] opacity-60"
|
||||||
>Room/Location</span
|
>Room/Location</span>
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
bind:value={
|
bind:value={
|
||||||
$events_loc.launcher.sync_intervals.location
|
$events_loc.launcher.sync_intervals.location
|
||||||
}
|
}
|
||||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[8px] opacity-60"
|
<span class="text-[8px] opacity-60"
|
||||||
>Session Loop</span
|
>Session Loop</span>
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
bind:value={
|
bind:value={
|
||||||
$events_loc.launcher.sync_intervals.session
|
$events_loc.launcher.sync_intervals.session
|
||||||
}
|
}
|
||||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[8px] opacity-60"
|
<span class="text-[8px] opacity-60"
|
||||||
>Presentation Loop</span
|
>Presentation Loop</span>
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
bind:value={
|
bind:value={
|
||||||
$events_loc.launcher.sync_intervals.presentation
|
$events_loc.launcher.sync_intervals
|
||||||
|
.presentation
|
||||||
}
|
}
|
||||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[8px] opacity-60"
|
<span class="text-[8px] opacity-60"
|
||||||
>Presenter Loop</span
|
>Presenter Loop</span>
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
bind:value={
|
bind:value={
|
||||||
$events_loc.launcher.sync_intervals.presenter
|
$events_loc.launcher.sync_intervals
|
||||||
|
.presenter
|
||||||
}
|
}
|
||||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-col gap-1 mt-1 border-t border-surface-500/10 pt-2"
|
class="border-surface-500/10 mt-1 flex flex-col gap-1 border-t pt-2">
|
||||||
>
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
<p
|
Cache Structure
|
||||||
class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
</p>
|
||||||
>Cache Structure</p
|
|
||||||
>
|
|
||||||
<div class="flex items-center justify-between px-1">
|
<div class="flex items-center justify-between px-1">
|
||||||
<span class="text-[9px]">Hash Prefix Length</span>
|
<span class="text-[9px]">Hash Prefix Length</span>
|
||||||
{#if $ae_loc.native_device}
|
{#if $ae_loc.native_device}
|
||||||
@@ -136,17 +127,17 @@
|
|||||||
bind:value={
|
bind:value={
|
||||||
$ae_loc.native_device.hash_prefix_length
|
$ae_loc.native_device.hash_prefix_length
|
||||||
}
|
}
|
||||||
class="select select-sm h-6 py-0 text-[10px] w-16 preset-tonal-surface"
|
class="select select-sm preset-tonal-surface h-6 w-16 py-0 text-[10px]">
|
||||||
>
|
|
||||||
<option value={1}>1 char</option>
|
<option value={1}>1 char</option>
|
||||||
<option value={2}>2 chars</option>
|
<option value={2}>2 chars</option>
|
||||||
<option value={3}>3 chars</option>
|
<option value={3}>3 chars</option>
|
||||||
</select>
|
</select>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="text-[9px] opacity-50 italic">loading…</span>
|
<span class="text-[9px] italic opacity-50"
|
||||||
|
>loading…</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<p class="text-[8px] opacity-40 italic mt-1">
|
<p class="mt-1 text-[8px] italic opacity-40">
|
||||||
* Prefix change requires a full app reload to take
|
* Prefix change requires a full app reload to take
|
||||||
effect.
|
effect.
|
||||||
</p>
|
</p>
|
||||||
@@ -154,47 +145,40 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<!-- Read Only Summary (Normal Mode) -->
|
<!-- Read Only Summary (Normal Mode) -->
|
||||||
<div
|
<div
|
||||||
class="bg-surface-500/5 p-2 rounded border border-surface-500/10 flex flex-col gap-1"
|
class="bg-surface-500/5 border-surface-500/10 flex flex-col gap-1 rounded border p-2">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="flex justify-between text-[9px] opacity-60 font-mono"
|
class="flex justify-between font-mono text-[9px] opacity-60">
|
||||||
>
|
|
||||||
<span>Event Sync:</span>
|
<span>Event Sync:</span>
|
||||||
<span
|
<span
|
||||||
>{(
|
>{(
|
||||||
$events_loc.launcher.sync_intervals.event /
|
$events_loc.launcher.sync_intervals.event / 1000
|
||||||
1000
|
).toFixed(1)}s</span>
|
||||||
).toFixed(1)}s</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex justify-between text-[9px] opacity-60 font-mono"
|
class="flex justify-between font-mono text-[9px] opacity-60">
|
||||||
>
|
|
||||||
<span>Room Monitor:</span>
|
<span>Room Monitor:</span>
|
||||||
<span
|
<span
|
||||||
>{(
|
>{(
|
||||||
$events_loc.launcher.sync_intervals.location / 1000
|
$events_loc.launcher.sync_intervals.location /
|
||||||
).toFixed(1)}s</span
|
1000
|
||||||
>
|
).toFixed(1)}s</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="flex justify-between text-[9px] opacity-60 font-mono border-t border-surface-500/10 pt-1"
|
class="border-surface-500/10 flex justify-between border-t pt-1 font-mono text-[9px] opacity-60">
|
||||||
>
|
|
||||||
<span>Prefix Sharding:</span>
|
<span>Prefix Sharding:</span>
|
||||||
<span
|
<span
|
||||||
>{$ae_loc.native_device?.hash_prefix_length || 2} chars</span
|
>{$ae_loc.native_device?.hash_prefix_length || 2} chars</span>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<p class="text-[8px] opacity-40 italic">
|
<p class="text-[8px] italic opacity-40">
|
||||||
Enable Edit Mode to adjust polling intervals.
|
Enable Edit Mode to adjust polling intervals.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="text-center p-4 opacity-50 italic text-xs">
|
<div class="p-4 text-center text-xs italic opacity-50">
|
||||||
Device configuration not loaded.
|
Device configuration not loaded.
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -7,7 +7,14 @@
|
|||||||
import { ae_loc, ae_api, ae_sess } from '$lib/stores/ae_stores';
|
import { ae_loc, ae_api, ae_sess } from '$lib/stores/ae_stores';
|
||||||
import { events_loc, events_sess } from '$lib/stores/ae_events_stores';
|
import { events_loc, events_sess } from '$lib/stores/ae_events_stores';
|
||||||
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
import Launcher_Cfg_Section from './launcher_cfg_section.svelte';
|
||||||
import { AlertTriangle, Boxes, RefreshCw, Settings, Trash2, Zap } from '@lucide/svelte';
|
import {
|
||||||
|
AlertTriangle,
|
||||||
|
Boxes,
|
||||||
|
RefreshCw,
|
||||||
|
Settings,
|
||||||
|
Trash2,
|
||||||
|
Zap
|
||||||
|
} from '@lucide/svelte';
|
||||||
interface Props {
|
interface Props {
|
||||||
on_expand?: () => void;
|
on_expand?: () => void;
|
||||||
}
|
}
|
||||||
@@ -43,100 +50,88 @@
|
|||||||
icon={Boxes}
|
icon={Boxes}
|
||||||
bind:state={$events_loc.launcher.section_state__template}
|
bind:state={$events_loc.launcher.section_state__template}
|
||||||
{on_expand}
|
{on_expand}
|
||||||
description="Kitchen Sink Scaffold | Demo Only"
|
description="Kitchen Sink Scaffold | Demo Only">
|
||||||
>
|
|
||||||
<!-- A. TOP STATUS BAR (Optional) -->
|
<!-- A. TOP STATUS BAR (Optional) -->
|
||||||
{#if action_status}
|
{#if action_status}
|
||||||
<div
|
<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"
|
class="bg-primary-500/10 text-primary-500 border-primary-500/20 col-span-full animate-pulse rounded border py-1 text-center text-[10px] italic">
|
||||||
>
|
|
||||||
{action_status}
|
{action_status}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- B. COMMON GRID SECTION (Read Only / High Level) -->
|
<!-- B. COMMON GRID SECTION (Read Only / High Level) -->
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
>Standard Actions</p
|
Standard Actions
|
||||||
>
|
</p>
|
||||||
|
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<div class="grid grid-cols-2 gap-1">
|
<div class="grid grid-cols-2 gap-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => handle_test_action('Primary')}
|
onclick={() => handle_test_action('Primary')}
|
||||||
class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start"
|
class="btn btn-xs preset-tonal-primary hover:preset-filled-primary-500 justify-start">
|
||||||
>
|
|
||||||
<Zap size="0.85em" class="mr-1 shrink-0" /> Primary
|
<Zap size="0.85em" class="mr-1 shrink-0" /> Primary
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => handle_test_action('Secondary')}
|
onclick={() => handle_test_action('Secondary')}
|
||||||
class="btn btn-xs preset-tonal-secondary hover:preset-filled-secondary-500 justify-start"
|
class="btn btn-xs preset-tonal-secondary hover:preset-filled-secondary-500 justify-start">
|
||||||
>
|
|
||||||
<Settings size="0.85em" class="mr-1 shrink-0" /> Secondary
|
<Settings size="0.85em" class="mr-1 shrink-0" /> Secondary
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Toggles & Checkboxes -->
|
<!-- Toggles & Checkboxes -->
|
||||||
<div class="flex flex-col gap-1 mt-1 bg-surface-500/5 p-2 rounded">
|
<div class="bg-surface-500/5 mt-1 flex flex-col gap-1 rounded p-2">
|
||||||
<label class="flex items-center gap-2 cursor-pointer group">
|
<label class="group flex cursor-pointer items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={toggle_val}
|
bind:checked={toggle_val}
|
||||||
class="checkbox checkbox-sm"
|
class="checkbox checkbox-sm" />
|
||||||
/>
|
|
||||||
<span
|
<span
|
||||||
class="text-xs group-hover:text-primary-500 transition-colors"
|
class="group-hover:text-primary-500 text-xs transition-colors"
|
||||||
>Toggle Feature Alpha</span
|
>Toggle Feature Alpha</span>
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
<label class="flex items-center gap-2 cursor-pointer group">
|
<label class="group flex cursor-pointer items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="demo"
|
name="demo"
|
||||||
value="a"
|
value="a"
|
||||||
class="radio radio-sm"
|
class="radio radio-sm" />
|
||||||
/>
|
|
||||||
<span
|
<span
|
||||||
class="text-xs group-hover:text-primary-500 transition-colors"
|
class="group-hover:text-primary-500 text-xs transition-colors"
|
||||||
>Mode A</span
|
>Mode A</span>
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- C. STATUS & GAUGES SECTION -->
|
<!-- C. STATUS & GAUGES SECTION -->
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
>Current Status</p
|
Current Status
|
||||||
>
|
</p>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-col gap-2 p-2 border border-surface-500/10 rounded-lg"
|
class="border-surface-500/10 flex flex-col gap-2 rounded-lg border p-2">
|
||||||
>
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex justify-between items-center">
|
|
||||||
<span class="text-[10px] font-medium">Engine Health</span>
|
<span class="text-[10px] font-medium">Engine Health</span>
|
||||||
<span class="badge preset-filled-success text-[8px] uppercase"
|
<span class="badge preset-filled-success text-[8px] uppercase"
|
||||||
>Stable</span
|
>Stable</span>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Progress / Gauge Example -->
|
<!-- Progress / Gauge Example -->
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<div
|
<div
|
||||||
class="flex justify-between text-[8px] uppercase opacity-60"
|
class="flex justify-between text-[8px] uppercase opacity-60">
|
||||||
>
|
|
||||||
<span>Processing Load</span>
|
<span>Processing Load</span>
|
||||||
<span>45%</span>
|
<span>45%</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="w-full h-1.5 bg-surface-500/20 rounded-full overflow-hidden"
|
class="bg-surface-500/20 h-1.5 w-full overflow-hidden rounded-full">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="h-full bg-success-500 transition-all duration-1000"
|
class="bg-success-500 h-full transition-all duration-1000"
|
||||||
style="width: 45%"
|
style="width: 45%">
|
||||||
></div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -144,9 +139,10 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => handle_test_action('Refresh')}
|
onclick={() => handle_test_action('Refresh')}
|
||||||
class="btn btn-xs preset-outlined-surface-500 w-full text-[10px]"
|
class="btn btn-xs preset-outlined-surface-500 w-full text-[10px]">
|
||||||
>
|
<RefreshCw
|
||||||
<RefreshCw size="0.85em" class="mr-1 {is_loading ? 'animate-spin' : ''}" />
|
size="0.85em"
|
||||||
|
class="mr-1 {is_loading ? 'animate-spin' : ''}" />
|
||||||
Refresh State
|
Refresh State
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -154,32 +150,28 @@
|
|||||||
<!-- D. TECHNICAL SECTION (Edit Mode Only) -->
|
<!-- D. TECHNICAL SECTION (Edit Mode Only) -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div
|
<div
|
||||||
class="col-span-full border-t border-surface-500/20 pt-3 mt-1 flex flex-col gap-3"
|
class="border-surface-500/20 col-span-full mt-1 flex flex-col gap-3 border-t pt-3">
|
||||||
>
|
|
||||||
<!-- Dangerous Actions -->
|
<!-- Dangerous Actions -->
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span
|
<span
|
||||||
class="text-[9px] font-bold uppercase opacity-50 text-warning-500"
|
class="text-warning-500 text-[9px] font-bold uppercase opacity-50"
|
||||||
>System Config</span
|
>System Config</span>
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => (show_confirm = true)}
|
onclick={() => (show_confirm = true)}
|
||||||
class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 justify-start"
|
class="btn btn-xs preset-tonal-warning hover:preset-filled-warning-500 justify-start">
|
||||||
>
|
<AlertTriangle size="0.85em" class="mr-1 shrink-0" /> Reset
|
||||||
<AlertTriangle size="0.85em" class="mr-1 shrink-0" /> Reset All
|
All
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span
|
<span
|
||||||
class="text-[9px] font-bold uppercase opacity-50 text-error-500"
|
class="text-error-500 text-[9px] font-bold uppercase opacity-50"
|
||||||
>Danger Zone</span
|
>Danger Zone</span>
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-xs preset-tonal-error hover:preset-filled-error-500 justify-start"
|
class="btn btn-xs preset-tonal-error hover:preset-filled-error-500 justify-start">
|
||||||
>
|
|
||||||
<Trash2 size="0.85em" class="mr-1 shrink-0" /> Wipe Cache
|
<Trash2 size="0.85em" class="mr-1 shrink-0" /> Wipe Cache
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -188,21 +180,17 @@
|
|||||||
<!-- Form Inputs -->
|
<!-- Form Inputs -->
|
||||||
<div class="grid grid-cols-1 gap-2">
|
<div class="grid grid-cols-1 gap-2">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span
|
<span class="ml-1 text-[9px] font-bold uppercase opacity-50"
|
||||||
class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
>Raw Settings</span>
|
||||||
>Raw Settings</span
|
|
||||||
>
|
|
||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={text_input}
|
bind:value={text_input}
|
||||||
placeholder="Enter string parameter..."
|
placeholder="Enter string parameter..."
|
||||||
class="input input-sm grow text-[10px] preset-tonal-surface h-7"
|
class="input input-sm preset-tonal-surface h-7 grow text-[10px]" />
|
||||||
/>
|
|
||||||
<select
|
<select
|
||||||
bind:value={select_val}
|
bind:value={select_val}
|
||||||
class="select select-sm h-7 py-0 text-[10px] w-24 preset-tonal-surface"
|
class="select select-sm preset-tonal-surface h-7 w-24 py-0 text-[10px]">
|
||||||
>
|
|
||||||
<option value="option1">Global</option>
|
<option value="option1">Global</option>
|
||||||
<option value="option2">Local</option>
|
<option value="option2">Local</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -210,24 +198,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<span class="text-[8px] opacity-60 ml-1"
|
<span class="ml-1 text-[8px] opacity-60"
|
||||||
>Threshold (ms)</span
|
>Threshold (ms)</span>
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
bind:value={number_input}
|
bind:value={number_input}
|
||||||
class="input input-sm text-[10px] h-7 preset-tonal-surface"
|
class="input input-sm preset-tonal-surface h-7 text-[10px]" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Terminal / Output Log -->
|
<!-- Terminal / Output Log -->
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50 ml-1"
|
<p class="ml-1 text-[9px] font-bold uppercase opacity-50">
|
||||||
>Debug Output</p
|
Debug Output
|
||||||
>
|
</p>
|
||||||
<pre
|
<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">
|
class="border-surface-500/50 max-h-24 overflow-x-auto rounded border bg-black p-2 text-[8px] text-green-500 shadow-inner">
|
||||||
[LOG] System Initialized
|
[LOG] System Initialized
|
||||||
[INFO] Store synced with IndexedDB
|
[INFO] Store synced with IndexedDB
|
||||||
[DEBUG] active_tab: template
|
[DEBUG] active_tab: template
|
||||||
@@ -241,13 +227,11 @@
|
|||||||
<!-- Confirmation Modal Demo -->
|
<!-- Confirmation Modal Demo -->
|
||||||
{#if show_confirm}
|
{#if show_confirm}
|
||||||
<div
|
<div
|
||||||
class="fixed inset-0 z-[1000] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
|
class="fixed inset-0 z-[1000] flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="card p-6 w-full max-w-sm preset-filled-surface-100-900 border border-warning-500 shadow-2xl animate-in zoom-in-95 duration-200"
|
class="card preset-filled-surface-100-900 border-warning-500 animate-in zoom-in-95 w-full max-w-sm border p-6 shadow-2xl duration-200">
|
||||||
>
|
<h4 class="h4 text-warning-500 mb-2 font-bold">Confirm Action</h4>
|
||||||
<h4 class="h4 text-warning-500 font-bold mb-2">Confirm Action</h4>
|
<p class="mb-6 text-sm opacity-80">
|
||||||
<p class="text-sm opacity-80 mb-6">
|
|
||||||
Are you sure you want to perform this test operation? This
|
Are you sure you want to perform this test operation? This
|
||||||
demonstrate the standard confirmation pattern.
|
demonstrate the standard confirmation pattern.
|
||||||
</p>
|
</p>
|
||||||
@@ -255,16 +239,14 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => (show_confirm = false)}
|
onclick={() => (show_confirm = false)}
|
||||||
class="btn btn-sm preset-tonal-surface">Cancel</button
|
class="btn btn-sm preset-tonal-surface">Cancel</button>
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
show_confirm = false;
|
show_confirm = false;
|
||||||
handle_test_action('Confirm');
|
handle_test_action('Confirm');
|
||||||
}}
|
}}
|
||||||
class="btn btn-sm preset-filled-warning"
|
class="btn btn-sm preset-filled-warning">
|
||||||
>
|
|
||||||
Yes, Proceed
|
Yes, Proceed
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,9 +13,7 @@
|
|||||||
let update_path = $state(
|
let update_path = $state(
|
||||||
'~/OSIT/Speaker Ready System/Admin Share/Custom Applications/osit_binaries/'
|
'~/OSIT/Speaker Ready System/Admin Share/Custom Applications/osit_binaries/'
|
||||||
);
|
);
|
||||||
let update_url = $state(
|
let update_url = $state('https://dev-demo.oneskyit.com/updates/ae_native.zip');
|
||||||
'https://dev-demo.oneskyit.com/updates/ae_native.zip'
|
|
||||||
);
|
|
||||||
|
|
||||||
let update_status = $state('');
|
let update_status = $state('');
|
||||||
let is_checking = $state(false);
|
let is_checking = $state(false);
|
||||||
@@ -59,35 +57,31 @@
|
|||||||
icon={CloudDownload}
|
icon={CloudDownload}
|
||||||
bind:state={$events_loc.launcher.section_state__updates}
|
bind:state={$events_loc.launcher.section_state__updates}
|
||||||
{on_expand}
|
{on_expand}
|
||||||
description="v1.0.0 | Source: {update_source}"
|
description="v1.0.0 | Source: {update_source}">
|
||||||
>
|
|
||||||
<!-- Content omitted for brevity, preserved in file -->
|
<!-- Content omitted for brevity, preserved in file -->
|
||||||
<div class="col-span-full flex flex-col gap-2">
|
<div class="col-span-full flex flex-col gap-2">
|
||||||
<!-- TECHNICAL: Source Config (Edit Mode Only) -->
|
<!-- TECHNICAL: Source Config (Edit Mode Only) -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div
|
<div
|
||||||
class="flex flex-col gap-2 bg-surface-500/5 p-2 rounded border border-surface-500/10 mb-1"
|
class="bg-surface-500/5 border-surface-500/10 mb-1 flex flex-col gap-2 rounded border p-2">
|
||||||
>
|
<div class="flex flex-row items-center justify-between px-1">
|
||||||
<div class="flex flex-row justify-between items-center px-1">
|
<p class="text-[9px] font-bold uppercase opacity-50">
|
||||||
<p class="text-[9px] font-bold uppercase opacity-50"
|
Source Type
|
||||||
>Source Type</p
|
</p>
|
||||||
>
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<label class="flex items-center gap-1 text-[10px]">
|
<label class="flex items-center gap-1 text-[10px]">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
bind:group={update_source}
|
bind:group={update_source}
|
||||||
value="file"
|
value="file"
|
||||||
class="radio radio-sm"
|
class="radio radio-sm" /> Local
|
||||||
/> Local
|
|
||||||
</label>
|
</label>
|
||||||
<label class="flex items-center gap-1 text-[10px]">
|
<label class="flex items-center gap-1 text-[10px]">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
bind:group={update_source}
|
bind:group={update_source}
|
||||||
value="url"
|
value="url"
|
||||||
class="radio radio-sm"
|
class="radio radio-sm" /> Web
|
||||||
/> Web
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,15 +91,13 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={update_path}
|
bind:value={update_path}
|
||||||
placeholder="Path to update package"
|
placeholder="Path to update package"
|
||||||
class="input input-sm text-[10px] preset-tonal-surface h-7 w-full"
|
class="input input-sm preset-tonal-surface h-7 w-full text-[10px]" />
|
||||||
/>
|
|
||||||
{:else}
|
{:else}
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={update_url}
|
bind:value={update_url}
|
||||||
placeholder="URL to update package"
|
placeholder="URL to update package"
|
||||||
class="input input-sm text-[10px] preset-tonal-surface h-7 w-full"
|
class="input input-sm preset-tonal-surface h-7 w-full text-[10px]" />
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -115,10 +107,9 @@
|
|||||||
type="button"
|
type="button"
|
||||||
onclick={handle_check_update}
|
onclick={handle_check_update}
|
||||||
disabled={is_checking}
|
disabled={is_checking}
|
||||||
class="btn btn-sm preset-filled-tertiary hover:preset-filled-primary-500 text-[10px] w-full"
|
class="btn btn-sm preset-filled-tertiary hover:preset-filled-primary-500 w-full text-[10px]">
|
||||||
>
|
|
||||||
{#if is_checking}
|
{#if is_checking}
|
||||||
<LoaderCircle size="0.85em" class="animate-spin mr-1" /> Checking...
|
<LoaderCircle size="0.85em" class="mr-1 animate-spin" /> Checking...
|
||||||
{:else}
|
{:else}
|
||||||
<Search size="0.85em" class="mr-1" /> Check for Updates
|
<Search size="0.85em" class="mr-1" /> Check for Updates
|
||||||
{/if}
|
{/if}
|
||||||
@@ -126,8 +117,7 @@
|
|||||||
|
|
||||||
{#if update_status}
|
{#if update_status}
|
||||||
<div
|
<div
|
||||||
class="text-[9px] text-center italic p-1 border border-surface-500/20 rounded bg-surface-500/5 mt-1"
|
class="border-surface-500/20 bg-surface-500/5 mt-1 rounded border p-1 text-center text-[9px] italic">
|
||||||
>
|
|
||||||
{update_status}
|
{update_status}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -136,8 +126,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={handle_install}
|
onclick={handle_install}
|
||||||
class="btn btn-sm preset-filled-success hover:preset-filled-primary-500 text-[10px] w-full animate-bounce mt-2 shadow-lg"
|
class="btn btn-sm preset-filled-success hover:preset-filled-primary-500 mt-2 w-full animate-bounce text-[10px] shadow-lg">
|
||||||
>
|
|
||||||
<Wand2 size="0.85em" class="mr-1" /> Install & Relaunch
|
<Wand2 size="0.85em" class="mr-1" /> Install & Relaunch
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -132,7 +132,9 @@
|
|||||||
const param_launcher_footer = data.url.searchParams.get('launcher_footer');
|
const param_launcher_footer = data.url.searchParams.get('launcher_footer');
|
||||||
|
|
||||||
if (log_lvl > 1) {
|
if (log_lvl > 1) {
|
||||||
console.log(`[Launcher Sync] URL Change: event=${path_event_id}, loc=${path_location_id}, sess=${url_session_id}`);
|
console.log(
|
||||||
|
`[Launcher Sync] URL Change: event=${path_event_id}, loc=${path_location_id}, sess=${url_session_id}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
@@ -144,7 +146,10 @@
|
|||||||
}
|
}
|
||||||
// CRITICAL: Ensure session_id is synced to store so LiveQueries react
|
// CRITICAL: Ensure session_id is synced to store so LiveQueries react
|
||||||
if ($events_slct.event_session_id !== url_session_id) {
|
if ($events_slct.event_session_id !== url_session_id) {
|
||||||
if (log_lvl) console.log(`[Launcher Sync] Updating store session_id: ${url_session_id}`);
|
if (log_lvl)
|
||||||
|
console.log(
|
||||||
|
`[Launcher Sync] Updating store session_id: ${url_session_id}`
|
||||||
|
);
|
||||||
$events_slct.event_session_id = url_session_id;
|
$events_slct.event_session_id = url_session_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,14 +157,20 @@
|
|||||||
if (param_iframe === 'true') $ae_loc.iframe = true;
|
if (param_iframe === 'true') $ae_loc.iframe = true;
|
||||||
else if (param_iframe === 'false') $ae_loc.iframe = false;
|
else if (param_iframe === 'false') $ae_loc.iframe = false;
|
||||||
|
|
||||||
if (param_launcher_menu === 'hide') $events_loc.launcher.hide__launcher_menu = true;
|
if (param_launcher_menu === 'hide')
|
||||||
else if (param_launcher_menu === 'show') $events_loc.launcher.hide__launcher_menu = false;
|
$events_loc.launcher.hide__launcher_menu = true;
|
||||||
|
else if (param_launcher_menu === 'show')
|
||||||
|
$events_loc.launcher.hide__launcher_menu = false;
|
||||||
|
|
||||||
if (param_launcher_header === 'hide') $events_loc.launcher.hide__launcher_header = true;
|
if (param_launcher_header === 'hide')
|
||||||
else if (param_launcher_header === 'show') $events_loc.launcher.hide__launcher_header = false;
|
$events_loc.launcher.hide__launcher_header = true;
|
||||||
|
else if (param_launcher_header === 'show')
|
||||||
|
$events_loc.launcher.hide__launcher_header = false;
|
||||||
|
|
||||||
if (param_launcher_footer === 'hide') $events_loc.launcher.hide__launcher_footer = true;
|
if (param_launcher_footer === 'hide')
|
||||||
else if (param_launcher_footer === 'show') $events_loc.launcher.hide__launcher_footer = false;
|
$events_loc.launcher.hide__launcher_footer = true;
|
||||||
|
else if (param_launcher_footer === 'show')
|
||||||
|
$events_loc.launcher.hide__launcher_footer = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Strip launcher display params from the URL after applying them — same pattern
|
// Strip launcher display params from the URL after applying them — same pattern
|
||||||
@@ -196,7 +207,9 @@
|
|||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (ae_acct) {
|
if (ae_acct) {
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
const new_location_obj_li = ae_acct.slct.event_location_obj_li ?? [''];
|
const new_location_obj_li = ae_acct.slct.event_location_obj_li ?? [
|
||||||
|
''
|
||||||
|
];
|
||||||
// Compare by extracting IDs only — object identity (===) won't work for
|
// Compare by extracting IDs only — object identity (===) won't work for
|
||||||
// plain JS objects from the store. Joining IDs is cheap and avoids a full
|
// plain JS objects from the store. Joining IDs is cheap and avoids a full
|
||||||
// JSON.stringify of potentially large location objects on every navigation.
|
// JSON.stringify of potentially large location objects on every navigation.
|
||||||
@@ -210,9 +223,13 @@
|
|||||||
$events_slct.event_location_obj_li = new_location_obj_li;
|
$events_slct.event_location_obj_li = new_location_obj_li;
|
||||||
}
|
}
|
||||||
|
|
||||||
const new_id_li__event_location = ae_acct.slct.id_li__event_location ?? [''];
|
const new_id_li__event_location = ae_acct.slct
|
||||||
|
.id_li__event_location ?? [''];
|
||||||
// ID list contains plain strings — join-compare is O(n) and avoids JSON.stringify.
|
// ID list contains plain strings — join-compare is O(n) and avoids JSON.stringify.
|
||||||
if (($events_slct.id_li__event_location ?? []).join(',') !== new_id_li__event_location.join(',')) {
|
if (
|
||||||
|
($events_slct.id_li__event_location ?? []).join(',') !==
|
||||||
|
new_id_li__event_location.join(',')
|
||||||
|
) {
|
||||||
$events_slct.id_li__event_location = new_id_li__event_location;
|
$events_slct.id_li__event_location = new_id_li__event_location;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -240,10 +257,7 @@
|
|||||||
let lq__event_event_file_obj_li = liveQuery(async () => {
|
let lq__event_event_file_obj_li = liveQuery(async () => {
|
||||||
const id = $events_slct.event_id;
|
const id = $events_slct.event_id;
|
||||||
if (!id) return [];
|
if (!id) return [];
|
||||||
return await db_events.file
|
return await db_events.file.where('for_id').equals(id).sortBy('filename');
|
||||||
.where('for_id')
|
|
||||||
.equals(id)
|
|
||||||
.sortBy('filename');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Event File - For Location
|
// Event File - For Location
|
||||||
@@ -274,10 +288,7 @@
|
|||||||
let lq__event_location_obj_li = liveQuery(async () => {
|
let lq__event_location_obj_li = liveQuery(async () => {
|
||||||
const id = $events_slct.event_id;
|
const id = $events_slct.event_id;
|
||||||
if (!id) return [];
|
if (!id) return [];
|
||||||
return await db_events.location
|
return await db_events.location.where('event_id').equals(id).sortBy('name');
|
||||||
.where('event_id')
|
|
||||||
.equals(id)
|
|
||||||
.sortBy('name');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// $derived.by: must recreate when event_location_id changes (see comment above).
|
// $derived.by: must recreate when event_location_id changes (see comment above).
|
||||||
@@ -311,8 +322,10 @@
|
|||||||
const result = $lq__event_obj;
|
const result = $lq__event_obj;
|
||||||
if (result) {
|
if (result) {
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
if (result.updated_on !== $events_slct.event_obj?.updated_on ||
|
if (
|
||||||
result.id !== $events_slct.event_obj?.id) {
|
result.updated_on !== $events_slct.event_obj?.updated_on ||
|
||||||
|
result.id !== $events_slct.event_obj?.id
|
||||||
|
) {
|
||||||
$events_slct.event_obj = { ...result };
|
$events_slct.event_obj = { ...result };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -323,8 +336,11 @@
|
|||||||
const result = $lq__event_device_obj;
|
const result = $lq__event_device_obj;
|
||||||
if (result) {
|
if (result) {
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
if (result.updated_on !== $events_slct.event_device_obj?.updated_on ||
|
if (
|
||||||
result.id !== $events_slct.event_device_obj?.id) {
|
result.updated_on !==
|
||||||
|
$events_slct.event_device_obj?.updated_on ||
|
||||||
|
result.id !== $events_slct.event_device_obj?.id
|
||||||
|
) {
|
||||||
$events_slct.event_device_obj = { ...result };
|
$events_slct.event_device_obj = { ...result };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -337,8 +353,12 @@
|
|||||||
untrack(() => {
|
untrack(() => {
|
||||||
const current = $events_slct.event_session_obj_li ?? [];
|
const current = $events_slct.event_session_obj_li ?? [];
|
||||||
// Compare by joining IDs — O(n) string compare vs O(n*m) JSON.stringify.
|
// Compare by joining IDs — O(n) string compare vs O(n*m) JSON.stringify.
|
||||||
const new_ids = (results as any[]).map((r: any) => r.id ?? r.event_session_id).join(',');
|
const new_ids = (results as any[])
|
||||||
const cur_ids = current.map((r: any) => r.id ?? r.event_session_id).join(',');
|
.map((r: any) => r.id ?? r.event_session_id)
|
||||||
|
.join(',');
|
||||||
|
const cur_ids = current
|
||||||
|
.map((r: any) => r.id ?? r.event_session_id)
|
||||||
|
.join(',');
|
||||||
if (new_ids !== cur_ids) {
|
if (new_ids !== cur_ids) {
|
||||||
$events_slct.event_session_obj_li = [...(results as any[])];
|
$events_slct.event_session_obj_li = [...(results as any[])];
|
||||||
}
|
}
|
||||||
@@ -522,16 +542,13 @@
|
|||||||
const keys = Object.keys(
|
const keys = Object.keys(
|
||||||
$events_loc.launcher.screen_saver_img_kv
|
$events_loc.launcher.screen_saver_img_kv
|
||||||
);
|
);
|
||||||
const rand_index = Math.floor(
|
const rand_index = Math.floor(Math.random() * keys.length);
|
||||||
Math.random() * keys.length
|
|
||||||
);
|
|
||||||
let event_file_obj =
|
let event_file_obj =
|
||||||
$events_loc.launcher.screen_saver_img_kv[
|
$events_loc.launcher.screen_saver_img_kv[
|
||||||
keys[rand_index]
|
keys[rand_index]
|
||||||
];
|
];
|
||||||
|
|
||||||
$events_slct.event_file_id =
|
$events_slct.event_file_id = event_file_obj.event_file_id;
|
||||||
event_file_obj.event_file_id;
|
|
||||||
$events_slct.event_file_obj = event_file_obj;
|
$events_slct.event_file_obj = event_file_obj;
|
||||||
$events_sess.launcher.modal__open_event_file_id = null;
|
$events_sess.launcher.modal__open_event_file_id = null;
|
||||||
$events_sess.launcher.modal__title =
|
$events_sess.launcher.modal__title =
|
||||||
@@ -587,81 +604,74 @@
|
|||||||
class="
|
class="
|
||||||
static
|
static
|
||||||
m-auto
|
m-auto
|
||||||
border-x border-gray-200 dark:border-gray-600
|
mb-16 h-full w-full
|
||||||
mb-16 sm:mb-12
|
max-w-7xl border-x
|
||||||
h-full
|
border-gray-200
|
||||||
w-full max-w-7xl
|
transition-all sm:mb-12
|
||||||
transition-all
|
dark:border-gray-600
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<header
|
<header
|
||||||
id="Main-Header"
|
id="Main-Header"
|
||||||
class:hidden={$events_loc.launcher.hide__launcher_header}
|
class:hidden={$events_loc.launcher.hide__launcher_header}
|
||||||
class="
|
class="
|
||||||
z-20
|
absolute
|
||||||
absolute top-0 left-0 right-0
|
top-0 right-0 left-0 z-20
|
||||||
w-full max-w-7xl
|
m-auto flex
|
||||||
h-12
|
h-12
|
||||||
p-1 px-12 m-auto
|
w-full max-w-7xl flex-row
|
||||||
|
|
||||||
flex flex-row items-center justify-around sm:justify-between
|
items-center justify-around bg-slate-200 p-1 px-12
|
||||||
|
|
||||||
text-sm
|
text-sm
|
||||||
|
|
||||||
bg-slate-200 dark:bg-slate-800
|
opacity-95 transition-colors
|
||||||
|
|
||||||
opacity-95 hover:opacity-100
|
duration-300 hover:opacity-100
|
||||||
transition-colors duration-300
|
sm:justify-between dark:bg-slate-800
|
||||||
"
|
">
|
||||||
>
|
<h3 class="h4 text-surface-600-400 text-center italic">
|
||||||
<h3 class="h4 text-center italic text-surface-600-400">
|
|
||||||
<!-- Menu toggle: needs a real tap target for tablet/touch operators -->
|
<!-- Menu toggle: needs a real tap target for tablet/touch operators -->
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="px-2 py-1 rounded hover:bg-surface-500/10 transition-colors"
|
class="hover:bg-surface-500/10 rounded px-2 py-1 transition-colors"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
$events_loc.launcher.hide__launcher_menu =
|
$events_loc.launcher.hide__launcher_menu =
|
||||||
!$events_loc.launcher.hide__launcher_menu;
|
!$events_loc.launcher.hide__launcher_menu;
|
||||||
}}
|
}}
|
||||||
title="Toggle Launcher menu"
|
title="Toggle Launcher menu">
|
||||||
>
|
<Satellite class="mx-1 inline-block text-base text-gray-500" />
|
||||||
<Satellite class="text-base mx-1 inline-block text-gray-500" />
|
|
||||||
<abbr title="Aether - Events Module Launcher">
|
<abbr title="Aether - Events Module Launcher">
|
||||||
Æ Launcher
|
Æ Launcher
|
||||||
<span
|
<span
|
||||||
class="text-xs align-super font-normal"
|
class="align-super text-xs font-normal"
|
||||||
title="Version 3">v3</span
|
title="Version 3">v3</span>
|
||||||
>
|
|
||||||
</abbr>
|
</abbr>
|
||||||
</button>
|
</button>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{#if $lq__event_obj}
|
{#if $lq__event_obj}
|
||||||
<h2
|
<h2
|
||||||
class="hidden md:inline-block h3 text-center text-surface-600-400"
|
class="h3 text-surface-600-400 hidden text-center md:inline-block">
|
||||||
>
|
|
||||||
{$lq__event_obj.cfg_json?.short_name}
|
{$lq__event_obj.cfg_json?.short_name}
|
||||||
</h2>
|
</h2>
|
||||||
<h3
|
<h3
|
||||||
class="h4 text-center italic text-surface-600-400"
|
class="h4 text-surface-600-400 text-center italic"
|
||||||
title="Location ID: {$lq__event_location_obj?.event_location_id} Name: {$lq__event_location_obj?.name}"
|
title="Location ID: {$lq__event_location_obj?.event_location_id} Name: {$lq__event_location_obj?.name}">
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="text-base"
|
class="text-base"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
$ae_loc.edit_mode = !$ae_loc.edit_mode;
|
$ae_loc.edit_mode = !$ae_loc.edit_mode;
|
||||||
}}
|
}}
|
||||||
title="Toggle Edit Mode to show location options and more"
|
title="Toggle Edit Mode to show location options and more">
|
||||||
>
|
|
||||||
<MapPin size="1em" />
|
<MapPin size="1em" />
|
||||||
<span class="sr-only">Location:</span>
|
<span class="sr-only">Location:</span>
|
||||||
</button>
|
</button>
|
||||||
{$lq__event_location_obj?.name}
|
{$lq__event_location_obj?.name}
|
||||||
</h3>
|
</h3>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex flex-row gap-1 items-center justify-center">
|
<div class="flex flex-row items-center justify-center gap-1">
|
||||||
<LoaderCircle size="1em" class="animate-spin mx-1" />
|
<LoaderCircle size="1em" class="mx-1 animate-spin" />
|
||||||
<span>Loading event...</span>
|
<span>Loading event...</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -669,30 +679,28 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
h-full min-w-full w-full max-w-full
|
flex h-full w-full max-w-full
|
||||||
flex flex-col sm:flex-row flex-wrap sm:flex-nowrap gap-0
|
min-w-full flex-col flex-wrap items-center justify-start gap-0
|
||||||
items-center
|
bg-gray-100
|
||||||
justify-start sm:justify-center
|
px-0.5 py-1
|
||||||
py-1 px-0.5
|
sm:flex-row sm:flex-nowrap
|
||||||
bg-gray-100 dark:bg-gray-900
|
sm:justify-center dark:bg-gray-900
|
||||||
|
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<section
|
<section
|
||||||
id="Main-Nav-Menu"
|
id="Main-Nav-Menu"
|
||||||
class="event_launcher_menu
|
class="event_launcher_menu
|
||||||
|
flex
|
||||||
h-full
|
h-full
|
||||||
basis-1/5
|
max-w-xs min-w-56 basis-1/5
|
||||||
min-w-56 md:min-w-64 lg:min-w-72
|
flex-col
|
||||||
max-w-xs
|
items-center justify-start
|
||||||
pt-0.5 pr-0.5
|
gap-1 overflow-y-auto border-r border-gray-200 pt-0.5
|
||||||
flex flex-col gap-1 items-center justify-start
|
pr-0.5
|
||||||
overflow-y-auto
|
|
||||||
|
|
||||||
border-r border-gray-200 dark:border-gray-700
|
md:min-w-64 lg:min-w-72 dark:border-gray-700
|
||||||
"
|
"
|
||||||
class:hidden={$events_loc.launcher.hide__launcher_menu}
|
class:hidden={$events_loc.launcher.hide__launcher_menu}>
|
||||||
>
|
|
||||||
<Launcher_menu
|
<Launcher_menu
|
||||||
{lq__event_obj}
|
{lq__event_obj}
|
||||||
{lq__event_event_file_obj_li}
|
{lq__event_event_file_obj_li}
|
||||||
@@ -717,28 +725,25 @@
|
|||||||
}
|
}
|
||||||
bind:trigger_reload__event_location_obj_li={
|
bind:trigger_reload__event_location_obj_li={
|
||||||
$events_sess.launcher.trigger_reload__event_location_obj_li
|
$events_sess.launcher.trigger_reload__event_location_obj_li
|
||||||
}
|
}></Launcher_menu>
|
||||||
></Launcher_menu>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
id="Main-Content"
|
id="Main-Content"
|
||||||
class="event_launcher_main
|
class="event_launcher_main
|
||||||
|
flex
|
||||||
h-full
|
h-full
|
||||||
min-w-xs
|
|
||||||
max-w-full
|
max-w-full
|
||||||
py-1 px-0.5
|
min-w-xs basis-4/5
|
||||||
basis-4/5
|
flex-col
|
||||||
flex flex-col gap-1
|
items-center justify-center gap-1
|
||||||
items-center
|
|
||||||
justify-center
|
|
||||||
overflow-y-auto
|
overflow-y-auto
|
||||||
"
|
px-0.5
|
||||||
>
|
py-1
|
||||||
|
">
|
||||||
{#if !$events_slct.event_location_id}
|
{#if !$events_slct.event_location_id}
|
||||||
<div
|
<div
|
||||||
class="flex flex-row items-center justify-center p-8 opacity-50"
|
class="flex flex-row items-center justify-center p-8 opacity-50">
|
||||||
>
|
|
||||||
<MapPin size="1.5em" class="mx-2" />
|
<MapPin size="1.5em" class="mx-2" />
|
||||||
<span>Please select a location from the menu</span>
|
<span>Please select a location from the menu</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -751,9 +756,8 @@
|
|||||||
{:else if $events_slct.event_location_id}
|
{:else if $events_slct.event_location_id}
|
||||||
<!-- Location selected but no session chosen yet — prompt operator -->
|
<!-- Location selected but no session chosen yet — prompt operator -->
|
||||||
<div
|
<div
|
||||||
class="flex flex-col items-center justify-center p-8 opacity-50"
|
class="flex flex-col items-center justify-center p-8 opacity-50">
|
||||||
>
|
<LoaderCircle class="mb-2 animate-spin" />
|
||||||
<LoaderCircle class="animate-spin mb-2" />
|
|
||||||
<span>Select a session from the menu</span>
|
<span>Select a session from the menu</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -767,35 +771,32 @@
|
|||||||
id="Main-Footer"
|
id="Main-Footer"
|
||||||
class:hidden={$events_loc.launcher.hide__launcher_footer}
|
class:hidden={$events_loc.launcher.hide__launcher_footer}
|
||||||
class="
|
class="
|
||||||
z-20
|
absolute
|
||||||
absolute bottom-0 left-0 right-0
|
right-0 bottom-0 left-0 z-20
|
||||||
|
m-auto flex
|
||||||
w-full max-w-7xl
|
w-full max-w-7xl
|
||||||
p-1 m-auto
|
|
||||||
|
|
||||||
flex flex-row items-center justify-between
|
flex-row items-center justify-between border-t
|
||||||
|
|
||||||
text-xs
|
border-gray-300
|
||||||
|
|
||||||
bg-gray-200 border-t border-gray-300
|
bg-gray-200 p-1 text-xs
|
||||||
dark:bg-gray-800 dark:border-gray-600
|
opacity-70 transition-opacity
|
||||||
|
|
||||||
opacity-70 hover:opacity-100
|
duration-500 hover:opacity-100
|
||||||
transition-opacity duration-500
|
dark:border-gray-600 dark:bg-gray-800
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="slct_location_name transition-colors duration-300"
|
class="slct_location_name transition-colors duration-300"
|
||||||
title="Location ID: {$lq__event_location_obj?.event_location_id} Name: {$lq__event_location_obj?.name} | Device ID: {$lq__event_device_obj?.event_device_id} Name: {$lq__event_device_obj?.name}"
|
title="Location ID: {$lq__event_location_obj?.event_location_id} Name: {$lq__event_location_obj?.name} | Device ID: {$lq__event_device_obj?.event_device_id} Name: {$lq__event_device_obj?.name}">
|
||||||
>
|
|
||||||
<!-- Edit mode toggle: needs tap target for tablet operators -->
|
<!-- Edit mode toggle: needs tap target for tablet operators -->
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="px-1.5 py-1 rounded hover:bg-surface-500/10 transition-colors"
|
class="hover:bg-surface-500/10 rounded px-1.5 py-1 transition-colors"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
$ae_loc.edit_mode = !$ae_loc.edit_mode;
|
$ae_loc.edit_mode = !$ae_loc.edit_mode;
|
||||||
}}
|
}}
|
||||||
title="Toggle Edit Mode to show location options and more"
|
title="Toggle Edit Mode to show location options and more">
|
||||||
>
|
|
||||||
<span class="sr-only">Location:</span>
|
<span class="sr-only">Location:</span>
|
||||||
<MapPin size="1em" />
|
<MapPin size="1em" />
|
||||||
</button>
|
</button>
|
||||||
@@ -809,9 +810,8 @@
|
|||||||
<span
|
<span
|
||||||
class:preset-tonal-warning={!$idle}
|
class:preset-tonal-warning={!$idle}
|
||||||
class:preset-tonal-success={$idle}
|
class:preset-tonal-success={$idle}
|
||||||
class="group px-2 py-0.5 rounded-md transition-colors duration-300"
|
class="group rounded-md px-2 py-0.5 transition-colors duration-300"
|
||||||
title="The user is currently {$idle ? 'idle' : 'active'}"
|
title="The user is currently {$idle ? 'idle' : 'active'}">
|
||||||
>
|
|
||||||
{#if $idle}
|
{#if $idle}
|
||||||
<BedDouble size="1em" class="mx-1" />
|
<BedDouble size="1em" class="mx-1" />
|
||||||
<span class="hidden group-hover:inline"> Idle </span>
|
<span class="hidden group-hover:inline"> Idle </span>
|
||||||
@@ -822,9 +822,8 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="group px-2 py-0.5 rounded-md transition-colors duration-300"
|
class="group rounded-md px-2 py-0.5 transition-colors duration-300"
|
||||||
title="Online status = {online?.current}"
|
title="Online status = {online?.current}">
|
||||||
>
|
|
||||||
<Wifi size="1em" class="mx-1" />
|
<Wifi size="1em" class="mx-1" />
|
||||||
{online?.current ? '' : 'Offline!'}
|
{online?.current ? '' : 'Offline!'}
|
||||||
</span>
|
</span>
|
||||||
@@ -835,12 +834,11 @@
|
|||||||
'connected'}
|
'connected'}
|
||||||
class:preset-tonal-success={$events_sess.launcher.ws_connect_status ==
|
class:preset-tonal-success={$events_sess.launcher.ws_connect_status ==
|
||||||
'connected'}
|
'connected'}
|
||||||
class="group px-2 py-0.5 rounded-md transition-colors duration-300"
|
class="group rounded-md px-2 py-0.5 transition-colors duration-300"
|
||||||
title="WebSocket is {$events_sess.launcher.ws_connect_status ==
|
title="WebSocket is {$events_sess.launcher.ws_connect_status ==
|
||||||
'connected'
|
'connected'
|
||||||
? 'connected'
|
? 'connected'
|
||||||
: 'disconnected'} API: {$ae_api?.base_url}"
|
: 'disconnected'} API: {$ae_api?.base_url}">
|
||||||
>
|
|
||||||
{#if $events_sess.launcher.ws_connect_status == 'connected'}
|
{#if $events_sess.launcher.ws_connect_status == 'connected'}
|
||||||
<Network size="1em" class="mx-1 text-green-700" />
|
<Network size="1em" class="mx-1 text-green-700" />
|
||||||
<span class="hidden group-hover:inline"> WS Connected </span>
|
<span class="hidden group-hover:inline"> WS Connected </span>
|
||||||
@@ -860,9 +858,9 @@
|
|||||||
else if (mode === 'larger') $ae_loc.font_size_mode = 'smaller';
|
else if (mode === 'larger') $ae_loc.font_size_mode = 'smaller';
|
||||||
else $ae_loc.font_size_mode = 'default';
|
else $ae_loc.font_size_mode = 'default';
|
||||||
}}
|
}}
|
||||||
class="group px-2 py-0.5 rounded-md font-mono font-bold hover:bg-surface-500/10 transition-colors duration-200"
|
class="group hover:bg-surface-500/10 rounded-md px-2 py-0.5 font-mono font-bold transition-colors duration-200"
|
||||||
title="Font size: {$ae_loc.font_size_mode ?? 'default'} — tap to cycle (default → larger → smaller)"
|
title="Font size: {$ae_loc.font_size_mode ??
|
||||||
>
|
'default'} — tap to cycle (default → larger → smaller)">
|
||||||
{#if $ae_loc.font_size_mode === 'larger'}
|
{#if $ae_loc.font_size_mode === 'larger'}
|
||||||
<span>A+</span>
|
<span>A+</span>
|
||||||
{:else if $ae_loc.font_size_mode === 'smaller'}
|
{:else if $ae_loc.font_size_mode === 'smaller'}
|
||||||
@@ -873,8 +871,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="current_datetime font-mono px-2 hover:font-bold hover:bg-white dark:hover:bg-slate-700 transition-colors"
|
class="current_datetime px-2 font-mono transition-colors hover:bg-white hover:font-bold dark:hover:bg-slate-700">
|
||||||
>
|
|
||||||
<span class="hidden md:inline">
|
<span class="hidden md:inline">
|
||||||
<CalendarDays size="1em" />
|
<CalendarDays size="1em" />
|
||||||
{ae_util.iso_datetime_formatter($time, 'date_full_no_year')}
|
{ae_util.iso_datetime_formatter($time, 'date_full_no_year')}
|
||||||
@@ -894,10 +891,9 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => ($events_loc.launcher.hide_drawer__cfg = false)}
|
onclick={() => ($events_loc.launcher.hide_drawer__cfg = false)}
|
||||||
class="btn btn-sm p-3 preset-tonal-error hover:preset-filled-error-500 transition-colors duration-300"
|
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 p-3 transition-colors duration-300"
|
||||||
class:opacity-25={!$ae_loc.trusted_access}
|
class:opacity-25={!$ae_loc.trusted_access}
|
||||||
class:hover:opacity-75={!$ae_loc.trusted_access}
|
class:hover:opacity-75={!$ae_loc.trusted_access}>
|
||||||
>
|
|
||||||
<Biohazard size="1em" />
|
<Biohazard size="1em" />
|
||||||
<span class="hidden">Launcher Config</span>
|
<span class="hidden">Launcher Config</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -906,7 +902,7 @@
|
|||||||
<Drawer
|
<Drawer
|
||||||
dismissable={false}
|
dismissable={false}
|
||||||
onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)}
|
onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)}
|
||||||
class="bg-orange-50 dark:bg-slate-800 opacity-90 hover:opacity-97 transition-all duration-300 border border-gray-300 dark:border-gray-600 w-full md:w-96 lg:w-[32rem]"
|
class="w-full border border-gray-300 bg-orange-50 opacity-90 transition-all duration-300 hover:opacity-97 md:w-96 lg:w-[32rem] dark:border-gray-600 dark:bg-slate-800"
|
||||||
placement="left"
|
placement="left"
|
||||||
{...{
|
{...{
|
||||||
transitionType: 'fly',
|
transitionType: 'fly',
|
||||||
@@ -917,8 +913,7 @@
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
bind:hidden={$events_loc.launcher.hide_drawer__cfg}
|
bind:hidden={$events_loc.launcher.hide_drawer__cfg}
|
||||||
id="sidebar1"
|
id="sidebar1">
|
||||||
>
|
|
||||||
<!-- Stop-propagation wrapper: prevents clicks inside the visual panel from
|
<!-- Stop-propagation wrapper: prevents clicks inside the visual panel from
|
||||||
bubbling up to the <dialog> element. The onclick on the <Drawer> above
|
bubbling up to the <dialog> element. The onclick on the <Drawer> above
|
||||||
is spread through to the native <dialog> by Flowbite, overriding its
|
is spread through to the native <dialog> by Flowbite, overriding its
|
||||||
@@ -931,20 +926,17 @@
|
|||||||
<hr class="my-2 border-gray-300 dark:border-gray-600" />
|
<hr class="my-2 border-gray-300 dark:border-gray-600" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-row flex-wrap gap-0.5 items-center justify-center max-w-md"
|
class="flex max-w-md flex-row flex-wrap items-center justify-center gap-0.5">
|
||||||
>
|
|
||||||
<a
|
<a
|
||||||
href="/events/{$events_slct.event_id}"
|
href="/events/{$events_slct.event_id}"
|
||||||
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500"
|
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500">
|
||||||
>
|
|
||||||
<Search size="1em" class="m-1" />
|
<Search size="1em" class="m-1" />
|
||||||
Session Search
|
Session Search
|
||||||
</a>
|
</a>
|
||||||
{#if $events_slct?.event_location_id}
|
{#if $events_slct?.event_location_id}
|
||||||
<a
|
<a
|
||||||
href="/events/{$events_slct.event_id}/location/{$events_slct.event_location_id}"
|
href="/events/{$events_slct.event_id}/location/{$events_slct.event_location_id}"
|
||||||
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500"
|
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500">
|
||||||
>
|
|
||||||
<MapPin size="1em" class="m-1" />
|
<MapPin size="1em" class="m-1" />
|
||||||
View Selected Location
|
View Selected Location
|
||||||
</a>
|
</a>
|
||||||
@@ -952,8 +944,7 @@
|
|||||||
{#if $events_slct?.event_session_id}
|
{#if $events_slct?.event_session_id}
|
||||||
<a
|
<a
|
||||||
href="/events/{$events_slct.event_id}/session/{$events_slct.event_session_id}"
|
href="/events/{$events_slct.event_id}/session/{$events_slct.event_session_id}"
|
||||||
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500"
|
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500">
|
||||||
>
|
|
||||||
<GraduationCap size="1em" class="m-1" />
|
<GraduationCap size="1em" class="m-1" />
|
||||||
View Selected Session
|
View Selected Session
|
||||||
</a>
|
</a>
|
||||||
@@ -964,7 +955,7 @@
|
|||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
activateClickOutside={false}
|
activateClickOutside={false}
|
||||||
class="bg-red-50 dark:bg-slate-900 opacity-75 hover:opacity-95 transition-all duration-300"
|
class="bg-red-50 opacity-75 transition-all duration-300 hover:opacity-95 dark:bg-slate-900"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
{...{
|
{...{
|
||||||
transitionType: 'fly',
|
transitionType: 'fly',
|
||||||
@@ -975,19 +966,16 @@
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
bind:hidden={$events_loc.launcher.hide_drawer__debug}
|
bind:hidden={$events_loc.launcher.hide_drawer__debug}
|
||||||
id="sidebar2"
|
id="sidebar2">
|
||||||
>
|
|
||||||
<div class="flex flex-row items-center justify-between">
|
<div class="flex flex-row items-center justify-between">
|
||||||
<h2
|
<h2
|
||||||
class="text-center mb-4 text-base font-semibold text-gray-500 dark:text-gray-400"
|
class="mb-4 text-center text-base font-semibold text-gray-500 dark:text-gray-400">
|
||||||
>
|
|
||||||
Debug
|
Debug
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => ($events_loc.launcher.hide_drawer__debug = true)}
|
onclick={() => ($events_loc.launcher.hide_drawer__debug = true)}
|
||||||
class="mb-4 dark:text-white"
|
class="mb-4 dark:text-white">
|
||||||
>
|
|
||||||
<X size="1em" />
|
<X size="1em" />
|
||||||
<span class="hidden">Close Debug Drawer</span>
|
<span class="hidden">Close Debug Drawer</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -1009,33 +997,30 @@
|
|||||||
autoclose={false}
|
autoclose={false}
|
||||||
placement="top-center"
|
placement="top-center"
|
||||||
class="
|
class="
|
||||||
bg-gray-500/90 dark:bg-gray-800/90 text-gray-800 dark:text-gray-200
|
relative flex flex-col items-center
|
||||||
rounded-lg border-gray-200 dark:border-gray-700
|
justify-center divide-y divide-gray-200
|
||||||
divide-y divide-gray-200 dark:divide-gray-700 shadow-md
|
rounded-lg border-gray-200 bg-gray-500/90 text-gray-800
|
||||||
relative
|
shadow-md
|
||||||
flex flex-col items-center justify-center
|
dark:divide-gray-700 dark:border-gray-700 dark:bg-gray-800/90 dark:text-gray-200
|
||||||
{$events_loc.launcher.controller == 'remote' ? 'min-h-full' : ''}
|
{$events_loc.launcher.controller == 'remote' ? 'min-h-full' : ''}
|
||||||
min-w-full
|
min-w-full
|
||||||
"
|
"
|
||||||
bodyClass="p-0 space-y-0 overflow-auto flex flex-col gap-1 items-center justify-center pb-14"
|
bodyClass="p-0 space-y-0 overflow-auto flex flex-col gap-1 items-center justify-center pb-14"
|
||||||
headerClass={`fixed top-0 right-0 left-0 p-1 md:p-2 flex flex-row items-center ${$events_loc.launcher.controller == 'remote' ? 'hidden' : ''} bg-white dark:bg-gray-800 opacity-50 ${$events_loc.launcher.hide__modal_header_title ? 'justify-center' : 'justify-between'}`}
|
headerClass={`fixed top-0 right-0 left-0 p-1 md:p-2 flex flex-row items-center ${$events_loc.launcher.controller == 'remote' ? 'hidden' : ''} bg-white dark:bg-gray-800 opacity-50 ${$events_loc.launcher.hide__modal_header_title ? 'justify-center' : 'justify-between'}`}
|
||||||
footerClass="text-center hidden"
|
footerClass="text-center hidden">
|
||||||
>
|
|
||||||
{#snippet header()}
|
{#snippet header()}
|
||||||
<h3
|
<h3
|
||||||
class:hidden={$events_loc.launcher.hide__modal_header_title}
|
class:hidden={$events_loc.launcher.hide__modal_header_title}
|
||||||
class="text-lg font-semibold opacity-20 hover:opacity-100 transition-all"
|
class="text-lg font-semibold opacity-20 transition-all hover:opacity-100">
|
||||||
>
|
|
||||||
{$events_sess.launcher?.modal__title ?? 'Digital Poster Display'}
|
{$events_sess.launcher?.modal__title ?? 'Digital Poster Display'}
|
||||||
</h3>
|
</h3>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn flex-row-reverse group transition-all justify-self-end"
|
class="btn group flex-row-reverse justify-self-end transition-all"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
$events_sess.launcher.modal__open_event_file_id = null;
|
$events_sess.launcher.modal__open_event_file_id = null;
|
||||||
}}
|
}}
|
||||||
title="Close Modal"
|
title="Close Modal">
|
||||||
>
|
|
||||||
<X size="1em" class="my-1.5" />
|
<X size="1em" class="my-1.5" />
|
||||||
<span class="hidden group-hover:inline"> Close</span>
|
<span class="hidden group-hover:inline"> Close</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -1047,25 +1032,28 @@
|
|||||||
and 'natural size' mode where the operator can pan/scroll and pinch-zoom freely
|
and 'natural size' mode where the operator can pan/scroll and pinch-zoom freely
|
||||||
for closer inspection or accessibility accommodation. -->
|
for closer inspection or accessibility accommodation. -->
|
||||||
<div
|
<div
|
||||||
class="w-full flex-1 flex items-center justify-center"
|
class="flex w-full flex-1 items-center justify-center"
|
||||||
class:overflow-auto={!modal_zoom_fit}
|
class:overflow-auto={!modal_zoom_fit}
|
||||||
class:overflow-hidden={modal_zoom_fit}
|
class:overflow-hidden={modal_zoom_fit}>
|
||||||
>
|
|
||||||
{#if $events_sess.launcher.modal__event_file_obj?.hosted_file_id}
|
{#if $events_sess.launcher.modal__event_file_obj?.hosted_file_id}
|
||||||
<!-- WHY: Use hosted_file endpoint (not event_file) — the event_file download
|
<!-- WHY: Use hosted_file endpoint (not event_file) — the event_file download
|
||||||
endpoint requires auth headers that a plain <img> tag cannot send (→ 403).
|
endpoint requires auth headers that a plain <img> tag cannot send (→ 403).
|
||||||
The hosted_file endpoint accepts key=account_id as a query param and is
|
The hosted_file endpoint accepts key=account_id as a query param and is
|
||||||
the proven browser-compatible path for direct file display. -->
|
the proven browser-compatible path for direct file display. -->
|
||||||
<img
|
<img
|
||||||
src="{$ae_api.base_url}/v3/action/hosted_file/{$events_sess.launcher
|
src="{$ae_api.base_url}/v3/action/hosted_file/{$events_sess
|
||||||
.modal__event_file_obj.hosted_file_id}/download?return_file=true&filename={encodeURIComponent(
|
.launcher.modal__event_file_obj
|
||||||
|
.hosted_file_id}/download?return_file=true&filename={encodeURIComponent(
|
||||||
$events_sess.launcher.modal__event_file_obj.filename ?? ''
|
$events_sess.launcher.modal__event_file_obj.filename ?? ''
|
||||||
)}&key={$ae_api.account_id}"
|
)}&key={$ae_api.account_id}"
|
||||||
alt="Poster: {$events_sess.launcher.modal__title}"
|
alt="Poster: {$events_sess.launcher.modal__title}"
|
||||||
ondblclick={() => {
|
ondblclick={() => {
|
||||||
modal_zoom_fit = !modal_zoom_fit;
|
modal_zoom_fit = !modal_zoom_fit;
|
||||||
// Sync zoom state to the remote display when acting as controller.
|
// Sync zoom state to the remote display when acting as controller.
|
||||||
if ($events_loc.launcher.controller == 'local_push' && $events_sess.launcher.ws_connect_status == 'connected') {
|
if (
|
||||||
|
$events_loc.launcher.controller == 'local_push' &&
|
||||||
|
$events_sess.launcher.ws_connect_status == 'connected'
|
||||||
|
) {
|
||||||
$events_sess.launcher.controller_cmd = `ae_zoom:${modal_zoom_fit ? 'fit' : 'zoom'}`;
|
$events_sess.launcher.controller_cmd = `ae_zoom:${modal_zoom_fit ? 'fit' : 'zoom'}`;
|
||||||
$events_sess.launcher.controller_trigger_send = true;
|
$events_sess.launcher.controller_trigger_send = true;
|
||||||
}
|
}
|
||||||
@@ -1078,8 +1066,7 @@
|
|||||||
class:object-contain={modal_zoom_fit}
|
class:object-contain={modal_zoom_fit}
|
||||||
class:cursor-zoom-in={modal_zoom_fit}
|
class:cursor-zoom-in={modal_zoom_fit}
|
||||||
class:cursor-zoom-out={!modal_zoom_fit}
|
class:cursor-zoom-out={!modal_zoom_fit}
|
||||||
style="touch-action: pinch-zoom;"
|
style="touch-action: pinch-zoom;" />
|
||||||
/>
|
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex flex-row items-center justify-center p-4">
|
<div class="flex flex-row items-center justify-center p-4">
|
||||||
<Info size="1em" class="mx-1" />
|
<Info size="1em" class="mx-1" />
|
||||||
@@ -1092,13 +1079,12 @@
|
|||||||
<!-- WHY: pb-14 on bodyClass reserves space so these buttons don't obscure poster content. -->
|
<!-- WHY: pb-14 on bodyClass reserves space so these buttons don't obscure poster content. -->
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
absolute bottom-0 left-0 right-0
|
absolute right-0 bottom-0 left-0
|
||||||
flex flex-row items-center justify-between gap-2
|
flex flex-row items-center justify-between gap-2
|
||||||
p-1.5
|
bg-black/30
|
||||||
bg-black/30 backdrop-blur-sm
|
p-1.5 backdrop-blur-sm
|
||||||
"
|
"
|
||||||
class:hidden={$events_loc.launcher.controller == 'remote'}
|
class:hidden={$events_loc.launcher.controller == 'remote'}>
|
||||||
>
|
|
||||||
<!-- Zoom / Fit toggle: accessibility accommodation — lets operators and general
|
<!-- Zoom / Fit toggle: accessibility accommodation — lets operators and general
|
||||||
public zoom in to read details, pinch on mobile, or double-tap the image. -->
|
public zoom in to read details, pinch on mobile, or double-tap the image. -->
|
||||||
<button
|
<button
|
||||||
@@ -1106,16 +1092,18 @@
|
|||||||
onclick={() => {
|
onclick={() => {
|
||||||
modal_zoom_fit = !modal_zoom_fit;
|
modal_zoom_fit = !modal_zoom_fit;
|
||||||
// Sync zoom state to the remote display when acting as controller.
|
// Sync zoom state to the remote display when acting as controller.
|
||||||
if ($events_loc.launcher.controller == 'local_push' && $events_sess.launcher.ws_connect_status == 'connected') {
|
if (
|
||||||
|
$events_loc.launcher.controller == 'local_push' &&
|
||||||
|
$events_sess.launcher.ws_connect_status == 'connected'
|
||||||
|
) {
|
||||||
$events_sess.launcher.controller_cmd = `ae_zoom:${modal_zoom_fit ? 'fit' : 'zoom'}`;
|
$events_sess.launcher.controller_cmd = `ae_zoom:${modal_zoom_fit ? 'fit' : 'zoom'}`;
|
||||||
$events_sess.launcher.controller_trigger_send = true;
|
$events_sess.launcher.controller_trigger_send = true;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
class="btn btn-sm preset-tonal-surface opacity-80 hover:opacity-100 transition-opacity"
|
class="btn btn-sm preset-tonal-surface opacity-80 transition-opacity hover:opacity-100"
|
||||||
title={modal_zoom_fit
|
title={modal_zoom_fit
|
||||||
? 'Pan / Zoom mode — pinch or double-tap image to zoom'
|
? 'Pan / Zoom mode — pinch or double-tap image to zoom'
|
||||||
: 'Fit image to screen'}
|
: 'Fit image to screen'}>
|
||||||
>
|
|
||||||
{#if modal_zoom_fit}
|
{#if modal_zoom_fit}
|
||||||
<ZoomIn size="1em" class="mr-1" />
|
<ZoomIn size="1em" class="mr-1" />
|
||||||
<span class="hidden sm:inline">Zoom</span>
|
<span class="hidden sm:inline">Zoom</span>
|
||||||
@@ -1136,14 +1124,13 @@
|
|||||||
$events_sess.launcher.modal__open_event_file_id = null;
|
$events_sess.launcher.modal__open_event_file_id = null;
|
||||||
$events_sess.launcher.modal__event_file_obj = null;
|
$events_sess.launcher.modal__event_file_obj = null;
|
||||||
}}
|
}}
|
||||||
class="btn btn-sm preset-tonal-error opacity-80 hover:opacity-100 transition-all"
|
class="btn btn-sm preset-tonal-error opacity-80 transition-all hover:opacity-100"
|
||||||
class:hidden={$events_loc.launcher.controller != 'local_push' ||
|
class:hidden={$events_loc.launcher.controller != 'local_push' ||
|
||||||
$events_sess.launcher.ws_connect_status != 'connected'}
|
$events_sess.launcher.ws_connect_status != 'connected'}
|
||||||
title="Close poster on this device and on the remote display (screensaver resumes)"
|
title="Close poster on this device and on the remote display (screensaver resumes)">
|
||||||
>
|
|
||||||
<Monitor size="1em" class="mr-1" />
|
<Monitor size="1em" class="mr-1" />
|
||||||
<X size="1em" />
|
<X size="1em" />
|
||||||
<span class="hidden sm:inline ml-1">Close Both</span>
|
<span class="ml-1 hidden sm:inline">Close Both</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Back to List: dismisses this controller's view only.
|
<!-- Back to List: dismisses this controller's view only.
|
||||||
@@ -1155,12 +1142,11 @@
|
|||||||
$events_sess.launcher.modal__open_event_file_id = null;
|
$events_sess.launcher.modal__open_event_file_id = null;
|
||||||
$events_sess.launcher.modal__event_file_obj = null;
|
$events_sess.launcher.modal__event_file_obj = null;
|
||||||
}}
|
}}
|
||||||
class="btn btn-sm preset-tonal-surface border border-surface-400/50 opacity-80 hover:opacity-100 transition-all"
|
class="btn btn-sm preset-tonal-surface border-surface-400/50 border opacity-80 transition-all hover:opacity-100"
|
||||||
class:hidden={!$ae_loc.trusted_access &&
|
class:hidden={!$ae_loc.trusted_access &&
|
||||||
($events_loc.launcher.controller != 'local_push' ||
|
($events_loc.launcher.controller != 'local_push' ||
|
||||||
$events_sess.launcher.ws_connect_status != 'connected')}
|
$events_sess.launcher.ws_connect_status != 'connected')}
|
||||||
title="Close poster on this device only — remote display keeps showing"
|
title="Close poster on this device only — remote display keeps showing">
|
||||||
>
|
|
||||||
<List size="1em" class="mr-1" />
|
<List size="1em" class="mr-1" />
|
||||||
<span class="hidden sm:inline">Back to List</span>
|
<span class="hidden sm:inline">Back to List</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -1187,6 +1173,5 @@
|
|||||||
bind:hide__ws_commands={$events_loc.launcher.hide__ws_commands}
|
bind:hide__ws_commands={$events_loc.launcher.hide__ws_commands}
|
||||||
bind:ws_conn_status={trigger_handle_ws_conn}
|
bind:ws_conn_status={trigger_handle_ws_conn}
|
||||||
bind:ws_recv_status={trigger_handle_ws_recv}
|
bind:ws_recv_status={trigger_handle_ws_recv}
|
||||||
bind:ws_sent_status={trigger_handle_ws_sent}
|
bind:ws_sent_status={trigger_handle_ws_sent} />
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -9,15 +9,11 @@
|
|||||||
|
|
||||||
// Imports
|
// Imports
|
||||||
import { untrack } from 'svelte';
|
import { untrack } from 'svelte';
|
||||||
import {
|
import { ae_loc, ae_sess, ae_api } from '$lib/stores/ae_stores';
|
||||||
ae_loc,
|
|
||||||
ae_sess,
|
|
||||||
ae_api,
|
|
||||||
} from '$lib/stores/ae_stores';
|
|
||||||
import {
|
import {
|
||||||
events_loc,
|
events_loc,
|
||||||
events_sess,
|
events_sess,
|
||||||
events_slct,
|
events_slct
|
||||||
} from '$lib/stores/ae_events_stores';
|
} from '$lib/stores/ae_events_stores';
|
||||||
|
|
||||||
// NOTE: Derived from data.account_id (prop) instead of $slct.account_id (store)
|
// NOTE: Derived from data.account_id (prop) instead of $slct.account_id (store)
|
||||||
@@ -41,9 +37,12 @@
|
|||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (ae_acct) {
|
if (ae_acct) {
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
$events_slct.event_location_obj_li = ae_acct.slct.event_location_obj_li ?? [''];
|
$events_slct.event_location_obj_li = ae_acct.slct
|
||||||
$events_slct.id_li__event_location = ae_acct.slct.id_li__event_location ?? [''];
|
.event_location_obj_li ?? [''];
|
||||||
$events_slct.event_session_obj_li = ae_acct.slct.event_session_obj_li ?? [''];
|
$events_slct.id_li__event_location = ae_acct.slct
|
||||||
|
.id_li__event_location ?? [''];
|
||||||
|
$events_slct.event_session_obj_li = ae_acct.slct
|
||||||
|
.event_session_obj_li ?? [''];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -71,7 +71,8 @@ export async function load({ params, parent, url }) {
|
|||||||
|
|
||||||
const session_id = url.searchParams.get('session_id');
|
const session_id = url.searchParams.get('session_id');
|
||||||
if (browser && session_id) {
|
if (browser && session_id) {
|
||||||
if (log_lvl) console.log(`Triggering deep load for session_id: ${session_id}`);
|
if (log_lvl)
|
||||||
|
console.log(`Triggering deep load for session_id: ${session_id}`);
|
||||||
events_func.load_ae_obj_id__event_session({
|
events_func.load_ae_obj_id__event_session({
|
||||||
api_cfg: ae_acct.api,
|
api_cfg: ae_acct.api,
|
||||||
event_session_id: session_id,
|
event_session_id: session_id,
|
||||||
|
|||||||
@@ -6,7 +6,11 @@
|
|||||||
*/
|
*/
|
||||||
import { onMount, onDestroy } from 'svelte';
|
import { onMount, onDestroy } from 'svelte';
|
||||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||||
import { events_loc, events_slct, events_sess } from '$lib/stores/ae_events_stores';
|
import {
|
||||||
|
events_loc,
|
||||||
|
events_slct,
|
||||||
|
events_sess
|
||||||
|
} from '$lib/stores/ae_events_stores';
|
||||||
import { events_func } from '$lib/ae_events/ae_events_functions';
|
import { events_func } from '$lib/ae_events/ae_events_functions';
|
||||||
import { ae_util } from '$lib/ae_utils/ae_utils';
|
import { ae_util } from '$lib/ae_utils/ae_utils';
|
||||||
import { db_events } from '$lib/ae_events/db_events';
|
import { db_events } from '$lib/ae_events/db_events';
|
||||||
@@ -16,9 +20,7 @@
|
|||||||
let { log_lvl = 1 } = $props();
|
let { log_lvl = 1 } = $props();
|
||||||
|
|
||||||
let currently_syncing: string | null = $state(null);
|
let currently_syncing: string | null = $state(null);
|
||||||
let sync_results: Record<string, 'success' | 'error' | 'pending'> = $state(
|
let sync_results: Record<string, 'success' | 'error' | 'pending'> = $state({});
|
||||||
{}
|
|
||||||
);
|
|
||||||
let sync_stats = $state({ total: 0, cached: 0, missing: 0 });
|
let sync_stats = $state({ total: 0, cached: 0, missing: 0 });
|
||||||
let last_heartbeat: string | null = $state(null);
|
let last_heartbeat: string | null = $state(null);
|
||||||
|
|
||||||
@@ -79,22 +81,40 @@
|
|||||||
// Load timings from persistent config, with fallbacks to device config or defaults.
|
// Load timings from persistent config, with fallbacks to device config or defaults.
|
||||||
// Fallback values here must match the loop_info $state defaults above — keep in sync.
|
// Fallback values here must match the loop_info $state defaults above — keep in sync.
|
||||||
loop_info.event = cfg.event || dev.check_event_loop_period || 90000;
|
loop_info.event = cfg.event || dev.check_event_loop_period || 90000;
|
||||||
loop_info.device = cfg.device || dev.check_event_device_loop_period || 60000;
|
loop_info.device =
|
||||||
loop_info.location = cfg.location || dev.check_event_location_loop_period || 60000;
|
cfg.device || dev.check_event_device_loop_period || 60000;
|
||||||
loop_info.session = cfg.session || dev.check_event_session_loop_period || 60000;
|
loop_info.location =
|
||||||
loop_info.presentation = cfg.presentation || dev.check_event_presentation_loop_period || 120000;
|
cfg.location || dev.check_event_location_loop_period || 60000;
|
||||||
loop_info.presenter = cfg.presenter || dev.check_event_presenter_loop_period || 120000;
|
loop_info.session =
|
||||||
loop_info.file_sync = cfg.file_sync || dev.check_file_sync_loop_period || 30000;
|
cfg.session || dev.check_event_session_loop_period || 60000;
|
||||||
|
loop_info.presentation =
|
||||||
|
cfg.presentation || dev.check_event_presentation_loop_period || 120000;
|
||||||
|
loop_info.presenter =
|
||||||
|
cfg.presenter || dev.check_event_presenter_loop_period || 120000;
|
||||||
|
loop_info.file_sync =
|
||||||
|
cfg.file_sync || dev.check_file_sync_loop_period || 30000;
|
||||||
|
|
||||||
// 1. Structural/Metadata Loops
|
// 1. Structural/Metadata Loops
|
||||||
timer__event = setInterval(() => refresh_event_data(), loop_info.event);
|
timer__event = setInterval(() => refresh_event_data(), loop_info.event);
|
||||||
timer__device = setInterval(() => run_device_heartbeat(), loop_info.device);
|
timer__device = setInterval(() => run_device_heartbeat(), loop_info.device);
|
||||||
timer__location = setInterval(() => refresh_location_config(), loop_info.location);
|
timer__location = setInterval(
|
||||||
|
() => refresh_location_config(),
|
||||||
|
loop_info.location
|
||||||
|
);
|
||||||
|
|
||||||
// 2. Room Content Refresh Loops (API -> Dexie)
|
// 2. Room Content Refresh Loops (API -> Dexie)
|
||||||
timer__session = setInterval(() => refresh_session_data(), loop_info.session);
|
timer__session = setInterval(
|
||||||
timer__presentation = setInterval(() => refresh_presentation_data(), loop_info.presentation);
|
() => refresh_session_data(),
|
||||||
timer__presenter = setInterval(() => refresh_presenter_data(), loop_info.presenter);
|
loop_info.session
|
||||||
|
);
|
||||||
|
timer__presentation = setInterval(
|
||||||
|
() => refresh_presentation_data(),
|
||||||
|
loop_info.presentation
|
||||||
|
);
|
||||||
|
timer__presenter = setInterval(
|
||||||
|
() => refresh_presenter_data(),
|
||||||
|
loop_info.presenter
|
||||||
|
);
|
||||||
|
|
||||||
// 3. Native File Sync Loop (Dexie -> Disk)
|
// 3. Native File Sync Loop (Dexie -> Disk)
|
||||||
timer__file_sync = setInterval(() => run_sync_cycle(), loop_info.file_sync);
|
timer__file_sync = setInterval(() => run_sync_cycle(), loop_info.file_sync);
|
||||||
@@ -113,8 +133,12 @@
|
|||||||
// Age threshold is user-configurable (cfg → General → Cache Maintenance), default 24h.
|
// Age threshold is user-configurable (cfg → General → Cache Maintenance), default 24h.
|
||||||
const cache_root = $ae_loc.local_file_cache_path;
|
const cache_root = $ae_loc.local_file_cache_path;
|
||||||
if ($ae_loc.is_native && cache_root) {
|
if ($ae_loc.is_native && cache_root) {
|
||||||
const max_age_hours = $events_loc.launcher.cleanup_tmp_max_age_hours ?? 24;
|
const max_age_hours =
|
||||||
cleanup_tmp_files({ cache_root, max_age_minutes: max_age_hours * 60 }).then((result) => {
|
$events_loc.launcher.cleanup_tmp_max_age_hours ?? 24;
|
||||||
|
cleanup_tmp_files({
|
||||||
|
cache_root,
|
||||||
|
max_age_minutes: max_age_hours * 60
|
||||||
|
}).then((result) => {
|
||||||
if (log_lvl) console.log('Sync: .tmp cleanup complete.', result);
|
if (log_lvl) console.log('Sync: .tmp cleanup complete.', result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -157,7 +181,10 @@
|
|||||||
const location_id = $events_slct.event_location_id;
|
const location_id = $events_slct.event_location_id;
|
||||||
if (!location_id) return;
|
if (!location_id) return;
|
||||||
try {
|
try {
|
||||||
if (log_lvl > 1) console.log(`Sync: Refreshing sessions for location: ${location_id}`);
|
if (log_lvl > 1)
|
||||||
|
console.log(
|
||||||
|
`Sync: Refreshing sessions for location: ${location_id}`
|
||||||
|
);
|
||||||
await events_func.load_ae_obj_li__event_session({
|
await events_func.load_ae_obj_li__event_session({
|
||||||
api_cfg: $ae_api,
|
api_cfg: $ae_api,
|
||||||
for_obj_type: 'event_location',
|
for_obj_type: 'event_location',
|
||||||
@@ -178,7 +205,10 @@
|
|||||||
const session_id = $events_slct.event_session_id;
|
const session_id = $events_slct.event_session_id;
|
||||||
if (!session_id) return;
|
if (!session_id) return;
|
||||||
try {
|
try {
|
||||||
if (log_lvl > 1) console.log(`Sync: Refreshing presentations for session: ${session_id}`);
|
if (log_lvl > 1)
|
||||||
|
console.log(
|
||||||
|
`Sync: Refreshing presentations for session: ${session_id}`
|
||||||
|
);
|
||||||
await events_func.load_ae_obj_li__event_presentation({
|
await events_func.load_ae_obj_li__event_presentation({
|
||||||
api_cfg: $ae_api,
|
api_cfg: $ae_api,
|
||||||
for_obj_type: 'event_session',
|
for_obj_type: 'event_session',
|
||||||
@@ -197,7 +227,10 @@
|
|||||||
const session_id = $events_slct.event_session_id;
|
const session_id = $events_slct.event_session_id;
|
||||||
if (!session_id) return;
|
if (!session_id) return;
|
||||||
try {
|
try {
|
||||||
if (log_lvl > 1) console.log(`Sync: Refreshing presenters for session: ${session_id}`);
|
if (log_lvl > 1)
|
||||||
|
console.log(
|
||||||
|
`Sync: Refreshing presenters for session: ${session_id}`
|
||||||
|
);
|
||||||
await events_func.load_ae_obj_li__event_presenter({
|
await events_func.load_ae_obj_li__event_presenter({
|
||||||
api_cfg: $ae_api,
|
api_cfg: $ae_api,
|
||||||
for_obj_type: 'event_session',
|
for_obj_type: 'event_session',
|
||||||
@@ -214,8 +247,7 @@
|
|||||||
const cache_root = $ae_loc.local_file_cache_path;
|
const cache_root = $ae_loc.local_file_cache_path;
|
||||||
const prefix_len = $ae_loc.native_device?.hash_prefix_length || 2;
|
const prefix_len = $ae_loc.native_device?.hash_prefix_length || 2;
|
||||||
|
|
||||||
if (!location_id || !cache_root || is_syncing || !$ae_loc.is_native)
|
if (!location_id || !cache_root || is_syncing || !$ae_loc.is_native) return;
|
||||||
return;
|
|
||||||
|
|
||||||
is_syncing = true;
|
is_syncing = true;
|
||||||
try {
|
try {
|
||||||
@@ -362,15 +394,16 @@
|
|||||||
update_payload.info_hostname = info.hostname;
|
update_payload.info_hostname = info.hostname;
|
||||||
// Safely handle IP list (bridge may return ip_addresses or networkInterfaces)
|
// Safely handle IP list (bridge may return ip_addresses or networkInterfaces)
|
||||||
const ips = info.ip_addresses || [];
|
const ips = info.ip_addresses || [];
|
||||||
update_payload.info_ip_list = Array.isArray(ips) ? ips.join(', ') : 'Unknown';
|
update_payload.info_ip_list = Array.isArray(ips)
|
||||||
|
? ips.join(', ')
|
||||||
|
: 'Unknown';
|
||||||
|
|
||||||
update_payload.meta_json = {
|
update_payload.meta_json = {
|
||||||
platform: info.platform,
|
platform: info.platform,
|
||||||
release: info.release,
|
release: info.release,
|
||||||
arch: info.arch,
|
arch: info.arch,
|
||||||
cpus: info.cpus,
|
cpus: info.cpus,
|
||||||
total_mem:
|
total_mem: Math.round(info.total_mem / (1024 * 1024)) + 'MB',
|
||||||
Math.round(info.total_mem / (1024 * 1024)) + 'MB',
|
|
||||||
free_mem: Math.round(info.free_mem / (1024 * 1024)) + 'MB'
|
free_mem: Math.round(info.free_mem / (1024 * 1024)) + 'MB'
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -424,81 +457,76 @@
|
|||||||
and the sys bar (bottom-12 right-2). Panel grows upward from the status chip. -->
|
and the sys bar (bottom-12 right-2). Panel grows upward from the status chip. -->
|
||||||
{#if $events_loc.launcher.app_mode === 'native' || $ae_loc.is_native}
|
{#if $events_loc.launcher.app_mode === 'native' || $ae_loc.is_native}
|
||||||
<div
|
<div
|
||||||
class="fixed bottom-20 left-4 z-[9999] flex flex-col items-start gap-2 pointer-events-none"
|
class="pointer-events-none fixed bottom-20 left-4 z-[9999] flex flex-col items-start gap-2">
|
||||||
>
|
|
||||||
{#if show_monitor}
|
{#if show_monitor}
|
||||||
<div
|
<div
|
||||||
class="bg-surface-50/95 dark:bg-surface-900/95 text-surface-800 dark:text-surface-100 p-3 rounded-lg border border-surface-200 dark:border-primary-700 shadow-2xl text-[10px] font-mono min-w-52 pointer-events-auto backdrop-blur-sm"
|
class="bg-surface-50/95 dark:bg-surface-900/95 text-surface-800 dark:text-surface-100 border-surface-200 dark:border-primary-700 pointer-events-auto min-w-52 rounded-lg border p-3 font-mono text-[10px] shadow-2xl backdrop-blur-sm">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="flex justify-between border-b border-surface-200 dark:border-primary-700 pb-1 mb-2"
|
class="border-surface-200 dark:border-primary-700 mb-2 flex justify-between border-b pb-1">
|
||||||
>
|
<span
|
||||||
<span class="font-bold text-primary-600 dark:text-primary-400"
|
class="text-primary-600 dark:text-primary-400 font-bold"
|
||||||
>NATIVE SYNC MONITOR</span
|
>NATIVE SYNC MONITOR</span>
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => (show_monitor = false)}
|
onclick={() => (show_monitor = false)}
|
||||||
class="text-error-500 hover:text-error-400 ml-2">×</button
|
class="text-error-500 hover:text-error-400 ml-2"
|
||||||
>
|
>×</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-x-4 gap-y-1 mb-2">
|
<div class="mb-2 grid grid-cols-2 gap-x-4 gap-y-1">
|
||||||
<span class="opacity-60 text-primary-700 dark:text-primary-300">Room Status:</span>
|
<span
|
||||||
|
class="text-primary-700 dark:text-primary-300 opacity-60"
|
||||||
|
>Room Status:</span>
|
||||||
<span class="text-right"
|
<span class="text-right"
|
||||||
>{sync_stats.cached} / {sync_stats.total} Files</span
|
>{sync_stats.cached} / {sync_stats.total} Files</span>
|
||||||
>
|
|
||||||
|
|
||||||
<span class="opacity-60 text-primary-700 dark:text-primary-300">Prefix Len:</span>
|
<span
|
||||||
|
class="text-primary-700 dark:text-primary-300 opacity-60"
|
||||||
|
>Prefix Len:</span>
|
||||||
<span class="text-right"
|
<span class="text-right"
|
||||||
>{$ae_loc.native_device?.hash_prefix_length || 2} chars</span
|
>{$ae_loc.native_device?.hash_prefix_length || 2} chars</span>
|
||||||
>
|
|
||||||
|
|
||||||
<span class="opacity-60 text-primary-700 dark:text-primary-300">Heartbeat:</span>
|
<span
|
||||||
|
class="text-primary-700 dark:text-primary-300 opacity-60"
|
||||||
|
>Heartbeat:</span>
|
||||||
<span
|
<span
|
||||||
class="text-right {last_heartbeat
|
class="text-right {last_heartbeat
|
||||||
? 'text-success-600 dark:text-success-400'
|
? 'text-success-600 dark:text-success-400'
|
||||||
: 'text-error-600 dark:text-error-400'}"
|
: 'text-error-600 dark:text-error-400'}">
|
||||||
>
|
|
||||||
{last_heartbeat || 'Pending...'}
|
{last_heartbeat || 'Pending...'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="border-t border-surface-200 dark:border-surface-700 pt-2 flex flex-col gap-1">
|
<div
|
||||||
<div class="flex justify-between items-center">
|
class="border-surface-200 dark:border-surface-700 flex flex-col gap-1 border-t pt-2">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
<span class:text-primary-500={timer__event}
|
<span class:text-primary-500={timer__event}
|
||||||
>Event Loop:</span
|
>Event Loop:</span>
|
||||||
>
|
|
||||||
<span>{loop_info.event / 1000}s</span>
|
<span>{loop_info.event / 1000}s</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex items-center justify-between">
|
||||||
<span class:text-primary-500={timer__device}
|
<span class:text-primary-500={timer__device}
|
||||||
>Device Loop:</span
|
>Device Loop:</span>
|
||||||
>
|
|
||||||
<span>{loop_info.device / 1000}s</span>
|
<span>{loop_info.device / 1000}s</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex items-center justify-between">
|
||||||
<span class:text-primary-500={timer__location}
|
<span class:text-primary-500={timer__location}
|
||||||
>Location Loop:</span
|
>Location Loop:</span>
|
||||||
>
|
|
||||||
<span>{loop_info.location / 1000}s</span>
|
<span>{loop_info.location / 1000}s</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex items-center justify-between">
|
||||||
<span class:text-primary-500={timer__session}
|
<span class:text-primary-500={timer__session}
|
||||||
>Session Loop:</span
|
>Session Loop:</span>
|
||||||
>
|
|
||||||
<span>{loop_info.session / 1000}s</span>
|
<span>{loop_info.session / 1000}s</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex items-center justify-between">
|
||||||
<span class:text-primary-500={timer__presentation}
|
<span class:text-primary-500={timer__presentation}
|
||||||
>Pres Loop:</span
|
>Pres Loop:</span>
|
||||||
>
|
|
||||||
<span>{loop_info.presentation / 1000}s</span>
|
<span>{loop_info.presentation / 1000}s</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex items-center justify-between">
|
||||||
<span class:text-primary-500={timer__presenter}
|
<span class:text-primary-500={timer__presenter}
|
||||||
>Speaker Loop:</span
|
>Speaker Loop:</span>
|
||||||
>
|
|
||||||
<span>{loop_info.presenter / 1000}s</span>
|
<span>{loop_info.presenter / 1000}s</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -512,25 +540,36 @@
|
|||||||
type="button"
|
type="button"
|
||||||
onclick={() => (show_monitor = !show_monitor)}
|
onclick={() => (show_monitor = !show_monitor)}
|
||||||
class="
|
class="
|
||||||
flex items-center gap-1.5
|
pointer-events-auto flex items-center
|
||||||
px-2 py-1.5 rounded-lg
|
gap-1.5 rounded-lg px-2
|
||||||
text-[10px] font-mono
|
py-1.5 font-mono
|
||||||
pointer-events-auto
|
text-[10px]
|
||||||
transition-all
|
transition-all
|
||||||
{currently_syncing
|
{currently_syncing
|
||||||
? 'bg-primary-500/15 dark:bg-primary-500/25 border border-primary-500 text-primary-700 dark:text-primary-300 shadow-md animate-pulse'
|
? 'bg-primary-500/15 dark:bg-primary-500/25 border-primary-500 text-primary-700 dark:text-primary-300 animate-pulse border shadow-md'
|
||||||
: 'bg-surface-100/90 dark:bg-surface-800/80 border border-surface-300 dark:border-surface-600 text-surface-600 dark:text-surface-300 opacity-60 hover:opacity-100 shadow-sm'}
|
: 'bg-surface-100/90 dark:bg-surface-800/80 border-surface-300 dark:border-surface-600 text-surface-600 dark:text-surface-300 border opacity-60 shadow-sm hover:opacity-100'}
|
||||||
"
|
"
|
||||||
title="Native Sync Monitor · {sync_stats.cached}/{sync_stats.total} files · click to {show_monitor ? 'close' : 'open'}"
|
title="Native Sync Monitor · {sync_stats.cached}/{sync_stats.total} files · click to {show_monitor
|
||||||
>
|
? 'close'
|
||||||
{#if currently_syncing}<RefreshCw size="1em" class="text-[9px] animate-spin text-primary-500" />{:else}<Cpu size="1em" class="text-[9px] opacity-50" />{/if}
|
: 'open'}">
|
||||||
|
{#if currently_syncing}<RefreshCw
|
||||||
|
size="1em"
|
||||||
|
class="text-primary-500 animate-spin text-[9px]" />{:else}<Cpu
|
||||||
|
size="1em"
|
||||||
|
class="text-[9px] opacity-50" />{/if}
|
||||||
{#if currently_syncing}
|
{#if currently_syncing}
|
||||||
<span class="truncate max-w-40 text-left">{currently_syncing}</span>
|
<span class="max-w-40 truncate text-left"
|
||||||
|
>{currently_syncing}</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span>Native Sync</span>
|
<span>Native Sync</span>
|
||||||
<span class="opacity-50 ml-0.5">{sync_stats.cached}/{sync_stats.total}</span>
|
<span class="ml-0.5 opacity-50"
|
||||||
|
>{sync_stats.cached}/{sync_stats.total}</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if show_monitor}<ChevronDown size="1em" class="text-[7px] opacity-40 ml-0.5" />{:else}<ChevronUp size="1em" class="text-[7px] opacity-40 ml-0.5" />{/if}
|
{#if show_monitor}<ChevronDown
|
||||||
|
size="1em"
|
||||||
|
class="ml-0.5 text-[7px] opacity-40" />{:else}<ChevronUp
|
||||||
|
size="1em"
|
||||||
|
class="ml-0.5 text-[7px] opacity-40" />{/if}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -32,7 +32,16 @@
|
|||||||
import Launcher_Cfg_Screen_Saver from './cfg_components/launcher_cfg_screen_saver.svelte';
|
import Launcher_Cfg_Screen_Saver from './cfg_components/launcher_cfg_screen_saver.svelte';
|
||||||
import Launcher_Cfg_App_Modes from './cfg_components/launcher_cfg_app_modes.svelte';
|
import Launcher_Cfg_App_Modes from './cfg_components/launcher_cfg_app_modes.svelte';
|
||||||
import Launcher_Cfg_Local_Actions from './cfg_components/launcher_cfg_local_actions.svelte';
|
import Launcher_Cfg_Local_Actions from './cfg_components/launcher_cfg_local_actions.svelte';
|
||||||
import { Bug, Code, Monitor, Pencil, RefreshCw, Settings, SlidersHorizontal, X } from '@lucide/svelte';
|
import {
|
||||||
|
Bug,
|
||||||
|
Code,
|
||||||
|
Monitor,
|
||||||
|
Pencil,
|
||||||
|
RefreshCw,
|
||||||
|
Settings,
|
||||||
|
SlidersHorizontal,
|
||||||
|
X
|
||||||
|
} from '@lucide/svelte';
|
||||||
// UI Tab State
|
// UI Tab State
|
||||||
// Tabs are audience-oriented:
|
// Tabs are audience-oriented:
|
||||||
// setup — what every onsite operator needs (mode preset, display, WS, screen saver)
|
// setup — what every onsite operator needs (mode preset, display, WS, screen saver)
|
||||||
@@ -63,16 +72,13 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
w-full max-w-full
|
flex w-full
|
||||||
flex flex-col gap-4 items-center justify-start
|
max-w-full flex-col items-center justify-start gap-4
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="w-full flex flex-row items-center justify-between border-b border-surface-500/20 pb-2"
|
class="border-surface-500/20 flex w-full flex-row items-center justify-between border-b pb-2">
|
||||||
>
|
|
||||||
<h2
|
<h2
|
||||||
class="text-center text-lg font-bold text-gray-700 dark:text-gray-200"
|
class="text-center text-lg font-bold text-gray-700 dark:text-gray-200">
|
||||||
>
|
|
||||||
<Settings size="1em" class="mr-2 opacity-50" />
|
<Settings size="1em" class="mr-2 opacity-50" />
|
||||||
Launcher Configuration
|
Launcher Configuration
|
||||||
</h2>
|
</h2>
|
||||||
@@ -88,8 +94,7 @@
|
|||||||
class:text-primary-500={$ae_loc.edit_mode}
|
class:text-primary-500={$ae_loc.edit_mode}
|
||||||
class:opacity-20={!$ae_loc.edit_mode}
|
class:opacity-20={!$ae_loc.edit_mode}
|
||||||
class:hover:opacity-60={!$ae_loc.edit_mode}
|
class:hover:opacity-60={!$ae_loc.edit_mode}
|
||||||
title="{$ae_loc.edit_mode ? 'Disable' : 'Enable'} Edit Mode"
|
title="{$ae_loc.edit_mode ? 'Disable' : 'Enable'} Edit Mode">
|
||||||
>
|
|
||||||
<Pencil size="0.75em" />
|
<Pencil size="0.75em" />
|
||||||
<span class="sr-only">Toggle Edit Mode</span>
|
<span class="sr-only">Toggle Edit Mode</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -97,8 +102,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)}
|
onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)}
|
||||||
class="btn btn-icon dark:text-white hover:bg-surface-500/10 transition-colors"
|
class="btn btn-icon hover:bg-surface-500/10 transition-colors dark:text-white">
|
||||||
>
|
|
||||||
<X size="1em" />
|
<X size="1em" />
|
||||||
<span class="sr-only">Close Config</span>
|
<span class="sr-only">Close Config</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -110,88 +114,80 @@
|
|||||||
for onsite operators who never need those tools. Edit Mode is toggled via
|
for onsite operators who never need those tools. Edit Mode is toggled via
|
||||||
the pencil icon in the header above. -->
|
the pencil icon in the header above. -->
|
||||||
<div
|
<div
|
||||||
class="w-full gap-1 bg-surface-500/10 p-1 rounded-lg"
|
class="bg-surface-500/10 w-full gap-1 rounded-lg p-1"
|
||||||
class:grid={true}
|
class:grid={true}
|
||||||
class:grid-cols-2={!$ae_loc.edit_mode}
|
class:grid-cols-2={!$ae_loc.edit_mode}
|
||||||
class:grid-cols-3={$ae_loc.edit_mode}
|
class:grid-cols-3={$ae_loc.edit_mode}>
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => (active_tab = 'setup')}
|
onclick={() => (active_tab = 'setup')}
|
||||||
class="btn btn-sm text-[10px] uppercase font-bold transition-all"
|
class="btn btn-sm text-[10px] font-bold uppercase transition-all"
|
||||||
class:preset-filled-primary={active_tab === 'setup'}
|
class:preset-filled-primary={active_tab === 'setup'}
|
||||||
class:preset-tonal-surface={active_tab !== 'setup'}
|
class:preset-tonal-surface={active_tab !== 'setup'}
|
||||||
title="Display presets, interface toggles, WS controller, screen saver"
|
title="Display presets, interface toggles, WS controller, screen saver">
|
||||||
>
|
|
||||||
<SlidersHorizontal size="0.85em" class="mr-1" /> Setup
|
<SlidersHorizontal size="0.85em" class="mr-1" /> Setup
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => (active_tab = 'device')}
|
onclick={() => (active_tab = 'device')}
|
||||||
class="btn btn-sm text-[10px] uppercase font-bold transition-all"
|
class="btn btn-sm text-[10px] font-bold uppercase transition-all"
|
||||||
class:preset-filled-primary={active_tab === 'device'}
|
class:preset-filled-primary={active_tab === 'device'}
|
||||||
class:preset-tonal-surface={active_tab !== 'device'}
|
class:preset-tonal-surface={active_tab !== 'device'}
|
||||||
title="Sync engine, device health & native OS controls"
|
title="Sync engine, device health & native OS controls">
|
||||||
>
|
|
||||||
<Monitor size="0.85em" class="mr-1" /> Device
|
<Monitor size="0.85em" class="mr-1" /> Device
|
||||||
</button>
|
</button>
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => (active_tab = 'dev')}
|
onclick={() => (active_tab = 'dev')}
|
||||||
class="btn btn-sm text-[10px] uppercase font-bold transition-all"
|
class="btn btn-sm text-[10px] font-bold uppercase transition-all"
|
||||||
class:preset-filled-warning={active_tab === 'dev'}
|
class:preset-filled-warning={active_tab === 'dev'}
|
||||||
class:preset-tonal-surface={active_tab !== 'dev'}
|
class:preset-tonal-surface={active_tab !== 'dev'}
|
||||||
title="Developer & debug tools"
|
title="Developer & debug tools">
|
||||||
>
|
|
||||||
<Code size="0.85em" class="mr-1" /> Dev
|
<Code size="0.85em" class="mr-1" /> Dev
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tab Content -->
|
<!-- Tab Content -->
|
||||||
<div class="w-full flex flex-col gap-2 min-h-[400px]">
|
<div class="flex min-h-[400px] w-full flex-col gap-2">
|
||||||
|
|
||||||
<!-- SETUP: everything onsite operators need day-to-day -->
|
<!-- SETUP: everything onsite operators need day-to-day -->
|
||||||
{#if active_tab === 'setup'}
|
{#if active_tab === 'setup'}
|
||||||
<div class="animate-in fade-in slide-in-from-left-2 duration-300 flex flex-col gap-2">
|
<div
|
||||||
|
class="animate-in fade-in slide-in-from-left-2 flex flex-col gap-2 duration-300">
|
||||||
<!-- Mode preset is the #1 onsite action — give it prominent placement -->
|
<!-- Mode preset is the #1 onsite action — give it prominent placement -->
|
||||||
<Launcher_Cfg_App_Modes
|
<Launcher_Cfg_App_Modes
|
||||||
on_expand={() => handle_section_expand('app_modes')}
|
on_expand={() => handle_section_expand('app_modes')} />
|
||||||
/>
|
|
||||||
<Launcher_Cfg_Controller
|
<Launcher_Cfg_Controller
|
||||||
on_expand={() => handle_section_expand('controller')}
|
on_expand={() => handle_section_expand('controller')} />
|
||||||
/>
|
|
||||||
<Launcher_Cfg_Screen_Saver
|
<Launcher_Cfg_Screen_Saver
|
||||||
on_expand={() => handle_section_expand('screen_saver')}
|
on_expand={() => handle_section_expand('screen_saver')} />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- DEVICE: sync engine first (all devices) + native OS controls (native or edit_mode preview) -->
|
<!-- DEVICE: sync engine first (all devices) + native OS controls (native or edit_mode preview) -->
|
||||||
{#if active_tab === 'device'}
|
{#if active_tab === 'device'}
|
||||||
<div class="animate-in fade-in slide-in-from-bottom-2 duration-300 flex flex-col gap-2">
|
<div
|
||||||
|
class="animate-in fade-in slide-in-from-bottom-2 flex flex-col gap-2 duration-300">
|
||||||
<!-- Sync pause/timers — relevant to every device, not just native -->
|
<!-- Sync pause/timers — relevant to every device, not just native -->
|
||||||
<Launcher_Cfg_Sync_Timers
|
<Launcher_Cfg_Sync_Timers
|
||||||
on_expand={() => handle_section_expand('sync_timers')}
|
on_expand={() => handle_section_expand('sync_timers')} />
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Native sections: always in Electron; visible in edit_mode for dev preview.
|
<!-- Native sections: always in Electron; visible in edit_mode for dev preview.
|
||||||
electron_relay.ts guards all calls — safe to import/render without Electron. -->
|
electron_relay.ts guards all calls — safe to import/render without Electron. -->
|
||||||
{#if $ae_loc.is_native || $ae_loc.edit_mode}
|
{#if $ae_loc.is_native || $ae_loc.edit_mode}
|
||||||
<Launcher_Cfg_Health
|
<Launcher_Cfg_Health
|
||||||
on_expand={() => handle_section_expand('health')}
|
on_expand={() => handle_section_expand('health')} />
|
||||||
/>
|
|
||||||
<Launcher_Cfg_Native_OS
|
<Launcher_Cfg_Native_OS
|
||||||
on_expand={() => handle_section_expand('native_os')}
|
on_expand={() => handle_section_expand('native_os')} />
|
||||||
/>
|
|
||||||
{#if $ae_loc.is_native}
|
{#if $ae_loc.is_native}
|
||||||
<Launcher_Cfg_Updates
|
<Launcher_Cfg_Updates
|
||||||
on_expand={() => handle_section_expand('updates')}
|
on_expand={() =>
|
||||||
/>
|
handle_section_expand('updates')} />
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="py-3 text-center opacity-40 italic text-xs flex flex-col gap-1 items-center">
|
<div
|
||||||
|
class="flex flex-col items-center gap-1 py-3 text-center text-xs italic opacity-40">
|
||||||
<Monitor size="1.2em" class="opacity-30" />
|
<Monitor size="1.2em" class="opacity-30" />
|
||||||
<p>Native OS controls available in Aether Desktop.</p>
|
<p>Native OS controls available in Aether Desktop.</p>
|
||||||
<p class="text-[9px]">Enable Edit Mode to preview.</p>
|
<p class="text-[9px]">Enable Edit Mode to preview.</p>
|
||||||
@@ -202,27 +198,24 @@
|
|||||||
|
|
||||||
<!-- DEV: developer/debug tools — only reachable when Edit Mode is on -->
|
<!-- DEV: developer/debug tools — only reachable when Edit Mode is on -->
|
||||||
{#if active_tab === 'dev' && $ae_loc.edit_mode}
|
{#if active_tab === 'dev' && $ae_loc.edit_mode}
|
||||||
<div class="animate-in fade-in slide-in-from-right-2 duration-300 flex flex-col gap-2">
|
<div
|
||||||
|
class="animate-in fade-in slide-in-from-right-2 flex flex-col gap-2 duration-300">
|
||||||
<Launcher_Cfg_Local_Actions
|
<Launcher_Cfg_Local_Actions
|
||||||
on_expand={() => handle_section_expand('local_actions')}
|
on_expand={() => handle_section_expand('local_actions')} />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Global Actions Footer -->
|
<!-- Global Actions Footer -->
|
||||||
<div
|
<div
|
||||||
class="w-full flex flex-col gap-2 border-t border-surface-500/20 pt-4 mt-auto"
|
class="border-surface-500/20 mt-auto flex w-full flex-col gap-2 border-t pt-4">
|
||||||
>
|
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
<!-- Close button — always visible in lower-left as a second dismissal point.
|
<!-- Close button — always visible in lower-left as a second dismissal point.
|
||||||
Useful in kiosk/iframe mode where the top-right close btn may scroll out of view. -->
|
Useful in kiosk/iframe mode where the top-right close btn may scroll out of view. -->
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)}
|
onclick={() => ($events_loc.launcher.hide_drawer__cfg = true)}
|
||||||
class="btn btn-sm preset-tonal-surface hover:preset-filled-surface-500 transition-all"
|
class="btn btn-sm preset-tonal-surface hover:preset-filled-surface-500 transition-all">
|
||||||
>
|
|
||||||
<X size="0.85em" class="mr-1" />
|
<X size="0.85em" class="mr-1" />
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
@@ -230,8 +223,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => location.reload()}
|
onclick={() => location.reload()}
|
||||||
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition-all"
|
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition-all">
|
||||||
>
|
|
||||||
<RefreshCw size="0.85em" class="mr-1" />
|
<RefreshCw size="0.85em" class="mr-1" />
|
||||||
Reload
|
Reload
|
||||||
</button>
|
</button>
|
||||||
@@ -242,16 +234,14 @@
|
|||||||
type="button"
|
type="button"
|
||||||
onclick={() =>
|
onclick={() =>
|
||||||
($events_loc.launcher.hide_drawer__debug = false)}
|
($events_loc.launcher.hide_drawer__debug = false)}
|
||||||
class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500 transition-all w-full"
|
class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500 w-full transition-all">
|
||||||
>
|
|
||||||
<Bug size="0.85em" class="mr-1" />
|
<Bug size="0.85em" class="mr-1" />
|
||||||
Debug Panel
|
Debug Panel
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<p
|
<p
|
||||||
class="text-[9px] text-center opacity-40 uppercase font-bold tracking-widest mt-2"
|
class="mt-2 text-center text-[9px] font-bold tracking-widest uppercase opacity-40">
|
||||||
>
|
|
||||||
Aether Platform • Events Launcher v3.0
|
Aether Platform • Events Launcher v3.0
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -63,7 +63,18 @@
|
|||||||
events_slct
|
events_slct
|
||||||
} from '$lib/stores/ae_events_stores';
|
} from '$lib/stores/ae_events_stores';
|
||||||
import { events_func } from '$lib/ae_events/ae_events_functions';
|
import { events_func } from '$lib/ae_events/ae_events_functions';
|
||||||
import { AlertCircle, AlertTriangle, BarChart2, CalendarDays, FolderOpen, Laptop, LoaderCircle, Monitor, Save, Send } from '@lucide/svelte';
|
import {
|
||||||
|
AlertCircle,
|
||||||
|
AlertTriangle,
|
||||||
|
BarChart2,
|
||||||
|
CalendarDays,
|
||||||
|
FolderOpen,
|
||||||
|
Laptop,
|
||||||
|
LoaderCircle,
|
||||||
|
Monitor,
|
||||||
|
Save,
|
||||||
|
Send
|
||||||
|
} from '@lucide/svelte';
|
||||||
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
|
||||||
|
|
||||||
// Import the relay
|
// Import the relay
|
||||||
@@ -227,24 +238,22 @@
|
|||||||
class:hidden={hide_draft &&
|
class:hidden={hide_draft &&
|
||||||
(event_file_obj.file_purpose == 'outline' ||
|
(event_file_obj.file_purpose == 'outline' ||
|
||||||
event_file_obj.file_purpose == 'draft')}
|
event_file_obj.file_purpose == 'draft')}
|
||||||
class="event_launcher_file_cont grow flex flex-col md:flex-row flex-wrap gap-1 items-center justify-center max-w-full transition-all"
|
class="event_launcher_file_cont flex max-w-full grow flex-col flex-wrap items-center justify-center gap-1 transition-all md:flex-row">
|
||||||
>
|
|
||||||
{#if open_file_clicked}
|
{#if open_file_clicked}
|
||||||
<div
|
<div
|
||||||
class="open_file_clicked alert"
|
class="open_file_clicked alert"
|
||||||
in:fade={{ duration: 250 }}
|
in:fade={{ duration: 250 }}
|
||||||
out:fade={{ duration: 2000 }}
|
out:fade={{ duration: 2000 }}>
|
||||||
>
|
|
||||||
<div class="alert_msg_pulse">
|
<div class="alert_msg_pulse">
|
||||||
<strong
|
<strong
|
||||||
>*** {open_file_status_message ||
|
>*** {open_file_status_message ||
|
||||||
'Please wait while this file downloads...'} ***</strong
|
'Please wait while this file downloads...'} ***</strong>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
{#if $ae_loc.is_native && $events_loc.launcher.app_mode === 'native'}
|
{#if $ae_loc.is_native && $events_loc.launcher.app_mode === 'native'}
|
||||||
<p>Most files will automatically be opened full screen.</p>
|
<p>Most files will automatically be opened full screen.</p>
|
||||||
<p>
|
<p>
|
||||||
PowerPoint or KeyNote will attempt to display in presenter view.
|
PowerPoint or KeyNote will attempt to display in presenter
|
||||||
|
view.
|
||||||
</p>
|
</p>
|
||||||
<p>Please close the file when finished.</p>
|
<p>Please close the file when finished.</p>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -252,8 +261,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="event_file_action grow max-w-full flex flex-row flex-wrap gap-1 items-center justify-center"
|
class="event_file_action flex max-w-full grow flex-row flex-wrap items-center justify-center gap-1">
|
||||||
>
|
|
||||||
{#if session_type == 'poster' || open_method == 'modal'}
|
{#if session_type == 'poster' || open_method == 'modal'}
|
||||||
<AE_Comp_Hosted_Files_Download_Button
|
<AE_Comp_Hosted_Files_Download_Button
|
||||||
hosted_file_id={event_file_id}
|
hosted_file_id={event_file_id}
|
||||||
@@ -267,17 +275,24 @@
|
|||||||
$events_slct.event_file_id = event_file_id;
|
$events_slct.event_file_id = event_file_id;
|
||||||
$events_slct.event_file_obj = event_file_obj;
|
$events_slct.event_file_obj = event_file_obj;
|
||||||
// Push the open command to the remote display when in local_push mode
|
// Push the open command to the remote display when in local_push mode
|
||||||
if ($events_loc.launcher.controller == 'local_push' && $events_sess.launcher.ws_connect_status == 'connected') {
|
if (
|
||||||
|
$events_loc.launcher.controller == 'local_push' &&
|
||||||
|
$events_sess.launcher.ws_connect_status == 'connected'
|
||||||
|
) {
|
||||||
$events_sess.launcher.controller_cmd = `ae_open:event_file=${event_file_id}`;
|
$events_sess.launcher.controller_cmd = `ae_open:event_file=${event_file_id}`;
|
||||||
$events_sess.launcher.controller_trigger_send = true;
|
$events_sess.launcher.controller_trigger_send = true;
|
||||||
}
|
}
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
{#snippet label()}
|
{#snippet label()}
|
||||||
{#if screen_saver_exts.includes(event_file_obj.extension)}
|
{#if screen_saver_exts.includes(event_file_obj.extension)}
|
||||||
<BarChart2 size="1em" class="{hide_launch_icon ? 'hidden' : ''} m-1" /> Open Poster
|
<BarChart2
|
||||||
|
size="1em"
|
||||||
|
class="{hide_launch_icon ? 'hidden' : ''} m-1" /> Open
|
||||||
|
Poster
|
||||||
{:else}
|
{:else}
|
||||||
<Send size="1em" class="{hide_launch_icon ? 'hidden' : ''} m-1" />
|
<Send
|
||||||
|
size="1em"
|
||||||
|
class="{hide_launch_icon ? 'hidden' : ''} m-1" />
|
||||||
{ae_util.shorten_filename({
|
{ae_util.shorten_filename({
|
||||||
filename: event_file_obj.filename,
|
filename: event_file_obj.filename,
|
||||||
max_length: max_filename_length
|
max_length: max_filename_length
|
||||||
@@ -291,13 +306,14 @@
|
|||||||
hosted_file_obj={event_file_obj}
|
hosted_file_obj={event_file_obj}
|
||||||
require_auth={false}
|
require_auth={false}
|
||||||
classes="btn {btn_size} gap-1 justify-between min-w-full w-full max-w-96 preset-tonal-primary border border-primary-500"
|
classes="btn {btn_size} gap-1 justify-between min-w-full w-full max-w-96 preset-tonal-primary border border-primary-500"
|
||||||
click={handle_open_file}
|
click={handle_open_file}>
|
||||||
>
|
|
||||||
{#snippet label()}
|
{#snippet label()}
|
||||||
{@const file_id = event_file_obj.hosted_file_id}
|
{@const file_id = event_file_obj.hosted_file_id}
|
||||||
<span class="shrink text-xs border-r border-gray-400 pr-1">
|
<span class="shrink border-r border-gray-400 pr-1 text-xs">
|
||||||
{#await ae_promises[event_file_id]}
|
{#await ae_promises[event_file_id]}
|
||||||
<LoaderCircle size="1em" class="inline animate-spin mx-0.5" />
|
<LoaderCircle
|
||||||
|
size="1em"
|
||||||
|
class="mx-0.5 inline animate-spin" />
|
||||||
<span>
|
<span>
|
||||||
{#if $ae_sess.api_download_kv[file_id]}
|
{#if $ae_sess.api_download_kv[file_id]}
|
||||||
{$ae_sess.api_download_kv[file_id]
|
{$ae_sess.api_download_kv[file_id]
|
||||||
@@ -307,24 +323,28 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
{:then result}
|
{:then result}
|
||||||
{@const FileIcon = ae_util.file_extension_icon_lucide(event_file_obj.extension)}
|
{@const FileIcon =
|
||||||
<FileIcon size="1em" class="inline mx-0.5" />
|
ae_util.file_extension_icon_lucide(
|
||||||
|
event_file_obj.extension
|
||||||
|
)}
|
||||||
|
<FileIcon size="1em" class="mx-0.5 inline" />
|
||||||
{event_file_obj.extension}
|
{event_file_obj.extension}
|
||||||
{#if result === null || result === false}
|
{#if result === null || result === false}
|
||||||
<span class="text-error-500"
|
<span class="text-error-500"
|
||||||
><AlertTriangle size="1em" class="inline mx-1" />Failed!</span
|
><AlertTriangle
|
||||||
>
|
size="1em"
|
||||||
|
class="mx-1 inline" />Failed!</span>
|
||||||
{/if}
|
{/if}
|
||||||
{:catch error}
|
{:catch error}
|
||||||
<span class="text-error-500" title={error?.message}
|
<span class="text-error-500" title={error?.message}
|
||||||
><AlertCircle size="1em" class="inline mx-0.5" />Error!</span
|
><AlertCircle
|
||||||
>
|
size="1em"
|
||||||
|
class="mx-0.5 inline" />Error!</span>
|
||||||
{/await}
|
{/await}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="grow {text_size} {text_size_md} w-full max-w-full overflow-hidden text-ellipsis {btn_text_align}"
|
class="grow {text_size} {text_size_md} w-full max-w-full overflow-hidden text-ellipsis {btn_text_align}">
|
||||||
>
|
|
||||||
{ae_util.shorten_string({
|
{ae_util.shorten_string({
|
||||||
string: event_file_obj.filename_no_ext,
|
string: event_file_obj.filename_no_ext,
|
||||||
begin_length: 45,
|
begin_length: 45,
|
||||||
@@ -333,9 +353,8 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="badge my-0 py-0.5 preset-tonal-success hover:preset-filled-success-500 text-xs xl:text-sm"
|
class="badge preset-tonal-success hover:preset-filled-success-500 my-0 py-0.5 text-xs xl:text-sm"
|
||||||
class:hidden={!event_file_obj.file_purpose}
|
class:hidden={!event_file_obj.file_purpose}>
|
||||||
>
|
|
||||||
{event_file_obj.file_purpose}
|
{event_file_obj.file_purpose}
|
||||||
</span>
|
</span>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@@ -344,9 +363,8 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="event_file_meta grow text-sm text-gray-500 flex flex-col sm:flex-row gap-1 wrap items-center justify-between w-64 max-w-80 font-mono"
|
class="event_file_meta wrap flex w-64 max-w-80 grow flex-col items-center justify-between gap-1 font-mono text-sm text-gray-500 sm:flex-row"
|
||||||
class:hidden={hide_meta}
|
class:hidden={hide_meta}>
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
@@ -366,33 +384,33 @@
|
|||||||
log_lvl
|
log_lvl
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
class="btn btn-sm transition-all group"
|
class="btn btn-sm group transition-all"
|
||||||
class:preset-tonal-success={event_file_obj?.open_in_os == 'win'}
|
class:preset-tonal-success={event_file_obj?.open_in_os == 'win'}
|
||||||
class:preset-tonal-warning={event_file_obj?.open_in_os == 'mac'}
|
class:preset-tonal-warning={event_file_obj?.open_in_os == 'mac'}
|
||||||
disabled={!$ae_loc.trusted_access}
|
disabled={!$ae_loc.trusted_access}>
|
||||||
>
|
{#if event_file_obj?.open_in_os == 'win'}<Monitor
|
||||||
{#if event_file_obj?.open_in_os == 'win'}<Monitor size="1em" class="m-1" />
|
size="1em"
|
||||||
{:else if event_file_obj?.open_in_os == 'mac'}<Laptop size="1em" class="m-1" />
|
class="m-1" />
|
||||||
|
{:else if event_file_obj?.open_in_os == 'mac'}<Laptop
|
||||||
|
size="1em"
|
||||||
|
class="m-1" />
|
||||||
{:else}<FolderOpen size="1em" class="m-1" />{/if}
|
{:else}<FolderOpen size="1em" class="m-1" />{/if}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<span
|
<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="event_file_created_on preset-filled-surface-100-900 flex w-24 flex-row items-center justify-end gap-1 rounded px-1 py-0.5 text-center text-xs md:w-44"
|
||||||
class:hidden={hide_created_on}
|
class:hidden={hide_created_on}>
|
||||||
>
|
|
||||||
<CalendarDays size="0.85em" class="inline" />
|
<CalendarDays size="0.85em" class="inline" />
|
||||||
<span class="w-18"
|
<span class="w-18"
|
||||||
>{ae_util.iso_datetime_formatter(
|
>{ae_util.iso_datetime_formatter(
|
||||||
event_file_obj.created_on,
|
event_file_obj.created_on,
|
||||||
'date_short'
|
'date_short'
|
||||||
)}</span
|
)}</span>
|
||||||
>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="event_file_size text-xs text-center flex flex-row gap-1 items-center justify-end preset-filled-surface-100-900 w-22 max-w-28 rounded py-0.5"
|
class="event_file_size preset-filled-surface-100-900 flex w-22 max-w-28 flex-row items-center justify-end gap-1 rounded py-0.5 text-center text-xs"
|
||||||
class:hidden={hide_size}
|
class:hidden={hide_size}>
|
||||||
>
|
|
||||||
<Save size="0.85em" class="inline" />
|
<Save size="0.85em" class="inline" />
|
||||||
{#if event_file_obj.file_size}{ae_util.format_bytes(
|
{#if event_file_obj.file_size}{ae_util.format_bytes(
|
||||||
event_file_obj.file_size
|
event_file_obj.file_size
|
||||||
|
|||||||
@@ -146,15 +146,14 @@
|
|||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
event_launcher_menu
|
event_launcher_menu
|
||||||
shrink h-full w-full max-w-full
|
flex h-full w-full max-w-full
|
||||||
flex flex-col flex-wrap gap-1 items-center justify-start
|
shrink flex-col flex-wrap items-center justify-start gap-1
|
||||||
|
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<!-- overflow-x-clip -->
|
<!-- overflow-x-clip -->
|
||||||
|
|
||||||
{#if $lq__event_event_file_obj_li}
|
{#if $lq__event_event_file_obj_li}
|
||||||
<div class="w-full flex flex-col gap-0.5 overflow-y-auto">
|
<div class="flex w-full flex-col gap-0.5 overflow-y-auto">
|
||||||
<!-- <div class="text-xs text-neutral-800/80">
|
<!-- <div class="text-xs text-neutral-800/80">
|
||||||
<strong>
|
<strong>
|
||||||
Event Files:
|
Event Files:
|
||||||
@@ -191,8 +190,7 @@
|
|||||||
}
|
}
|
||||||
bind:modal__event_file_obj={
|
bind:modal__event_file_obj={
|
||||||
$events_sess.launcher.modal__event_file_obj
|
$events_sess.launcher.modal__event_file_obj
|
||||||
}
|
} />
|
||||||
/>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -201,12 +199,11 @@
|
|||||||
<Menu_location_list_menu
|
<Menu_location_list_menu
|
||||||
{lq__event_location_obj_li}
|
{lq__event_location_obj_li}
|
||||||
slct_event_location_id={$events_slct.event_location_id}
|
slct_event_location_id={$events_slct.event_location_id}
|
||||||
bind:loading__session_li_status
|
bind:loading__session_li_status />
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $lq__location_event_file_obj_li}
|
{#if $lq__location_event_file_obj_li}
|
||||||
<div class="w-full flex flex-col gap-0.5">
|
<div class="flex w-full flex-col gap-0.5">
|
||||||
{#each $lq__location_event_file_obj_li as event_file_obj, index (event_file_obj.event_file_id)}
|
{#each $lq__location_event_file_obj_li as event_file_obj, index (event_file_obj.event_file_id)}
|
||||||
<Event_launcher_file_cont
|
<Event_launcher_file_cont
|
||||||
event_file_id={event_file_obj.event_file_id}
|
event_file_id={event_file_obj.event_file_id}
|
||||||
@@ -235,8 +232,7 @@
|
|||||||
}
|
}
|
||||||
bind:modal__event_file_obj={
|
bind:modal__event_file_obj={
|
||||||
$events_sess.launcher.modal__event_file_obj
|
$events_sess.launcher.modal__event_file_obj
|
||||||
}
|
} />
|
||||||
/>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -246,8 +242,7 @@
|
|||||||
bind:slct__event_session_id
|
bind:slct__event_session_id
|
||||||
bind:loading__session_id_status
|
bind:loading__session_id_status
|
||||||
{lq__event_session_obj_li}
|
{lq__event_session_obj_li}
|
||||||
bind:trigger_reload__event_session_obj_id
|
bind:trigger_reload__event_session_obj_id />
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Menu_launcher_controls />
|
<Menu_launcher_controls />
|
||||||
|
|||||||
@@ -53,17 +53,15 @@
|
|||||||
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
|
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
|
||||||
<section class="event_presentation_file_list my-1">
|
<section class="event_presentation_file_list my-1">
|
||||||
<div
|
<div
|
||||||
class="text-[10px] text-surface-600-400 uppercase font-bold tracking-wider opacity-70"
|
class="text-surface-600-400 text-[10px] font-bold tracking-wider uppercase opacity-70">
|
||||||
>
|
|
||||||
Presentation Files:
|
Presentation Files:
|
||||||
</div>
|
</div>
|
||||||
<ul class="space-y-1">
|
<ul class="space-y-1">
|
||||||
{#each $lq__event_file_obj_li as event_file_obj (event_file_obj.event_file_id)}
|
{#each $lq__event_file_obj_li as event_file_obj (event_file_obj.event_file_id)}
|
||||||
<li
|
<li
|
||||||
class="flex flex-col md:flex-row flex-wrap gap-1 items-center justify-start"
|
class="flex flex-col flex-wrap items-center justify-start gap-1 md:flex-row"
|
||||||
class:hidden={!$events_loc.launcher
|
class:hidden={!$events_loc.launcher
|
||||||
.show_content__hidden_files && event_file_obj.hide}
|
.show_content__hidden_files && event_file_obj.hide}>
|
||||||
>
|
|
||||||
<Event_launcher_file_cont
|
<Event_launcher_file_cont
|
||||||
event_file_id={event_file_obj.event_file_id}
|
event_file_id={event_file_obj.event_file_id}
|
||||||
{event_file_obj}
|
{event_file_obj}
|
||||||
@@ -82,8 +80,7 @@
|
|||||||
}
|
}
|
||||||
bind:modal__event_file_obj={
|
bind:modal__event_file_obj={
|
||||||
$events_sess.launcher.modal__event_file_obj
|
$events_sess.launcher.modal__event_file_obj
|
||||||
}
|
} />
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
</strong>
|
</strong>
|
||||||
|
|
||||||
{#if !lq__event_presenter_obj?.file_count}
|
{#if !lq__event_presenter_obj?.file_count}
|
||||||
<p class="text-sm text-center text-gray-400">
|
<p class="text-center text-sm text-gray-400">
|
||||||
<!-- <span class="fas fa-exclamation"></span> -->
|
<!-- <span class="fas fa-exclamation"></span> -->
|
||||||
No files to show for this presenter at this time.
|
No files to show for this presenter at this time.
|
||||||
<!-- <span class="fas fa-exclamation"></span> -->
|
<!-- <span class="fas fa-exclamation"></span> -->
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
|
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
|
||||||
<section class="event_session_file_list">
|
<section class="event_session_file_list">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs text-surface-600-400">
|
<div class="text-surface-600-400 text-xs">
|
||||||
<strong>
|
<strong>
|
||||||
<Archive size="1em" class="inline" />
|
<Archive size="1em" class="inline" />
|
||||||
Presenter Files:
|
Presenter Files:
|
||||||
@@ -89,10 +89,9 @@
|
|||||||
<ul class="space-y-1">
|
<ul class="space-y-1">
|
||||||
{#each $lq__event_file_obj_li as event_file_obj, index (event_file_obj.event_file_id)}
|
{#each $lq__event_file_obj_li as event_file_obj, index (event_file_obj.event_file_id)}
|
||||||
<li
|
<li
|
||||||
class="flex flex-col md:flex-row flex-wrap gap-1 items-center justify-start"
|
class="flex flex-col flex-wrap items-center justify-start gap-1 md:flex-row"
|
||||||
class:hidden={!$events_loc.launcher
|
class:hidden={!$events_loc.launcher
|
||||||
.show_content__hidden_files && event_file_obj.hide}
|
.show_content__hidden_files && event_file_obj.hide}>
|
||||||
>
|
|
||||||
<Event_launcher_file_cont
|
<Event_launcher_file_cont
|
||||||
event_file_id={event_file_obj.event_file_id}
|
event_file_id={event_file_obj.event_file_id}
|
||||||
{event_file_obj}
|
{event_file_obj}
|
||||||
@@ -110,8 +109,7 @@
|
|||||||
}
|
}
|
||||||
bind:modal__event_file_obj={
|
bind:modal__event_file_obj={
|
||||||
$events_sess.launcher.modal__event_file_obj
|
$events_sess.launcher.modal__event_file_obj
|
||||||
}
|
} />
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -83,10 +83,9 @@
|
|||||||
<ul class="space-y-1">
|
<ul class="space-y-1">
|
||||||
{#each $lq__event_file_obj_li as event_file_obj, index (event_file_obj.event_file_id)}
|
{#each $lq__event_file_obj_li as event_file_obj, index (event_file_obj.event_file_id)}
|
||||||
<li
|
<li
|
||||||
class="flex flex-col md:flex-row wrap gap items-center justify-center"
|
class="wrap gap flex flex-col items-center justify-center md:flex-row"
|
||||||
class:hidden={!$events_loc.launcher
|
class:hidden={!$events_loc.launcher
|
||||||
.show_content__hidden_files && event_file_obj.hide}
|
.show_content__hidden_files && event_file_obj.hide}>
|
||||||
>
|
|
||||||
<Event_launcher_file_cont
|
<Event_launcher_file_cont
|
||||||
event_file_id={event_file_obj.event_file_id}
|
event_file_id={event_file_obj.event_file_id}
|
||||||
{event_file_obj}
|
{event_file_obj}
|
||||||
@@ -102,8 +101,7 @@
|
|||||||
}
|
}
|
||||||
bind:modal__event_file_obj={
|
bind:modal__event_file_obj={
|
||||||
$events_sess.launcher.modal__event_file_obj
|
$events_sess.launcher.modal__event_file_obj
|
||||||
}
|
} />
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -39,7 +39,16 @@
|
|||||||
events_trigger
|
events_trigger
|
||||||
} from '$lib/stores/ae_events_stores';
|
} from '$lib/stores/ae_events_stores';
|
||||||
import { events_func } from '$lib/ae_events/ae_events_functions';
|
import { events_func } from '$lib/ae_events/ae_events_functions';
|
||||||
import { AlertTriangle, Archive, Barcode, Image, LoaderCircle, Monitor, User, Users } from '@lucide/svelte';
|
import {
|
||||||
|
AlertTriangle,
|
||||||
|
Archive,
|
||||||
|
Barcode,
|
||||||
|
Image,
|
||||||
|
LoaderCircle,
|
||||||
|
Monitor,
|
||||||
|
User,
|
||||||
|
Users
|
||||||
|
} from '@lucide/svelte';
|
||||||
// Event Session (Main View Trigger)
|
// Event Session (Main View Trigger)
|
||||||
// WHY: We use a simple derived observable. The template handles the $ prefix.
|
// WHY: We use a simple derived observable. The template handles the $ prefix.
|
||||||
let lq__event_session_obj = $derived(
|
let lq__event_session_obj = $derived(
|
||||||
@@ -60,7 +69,9 @@
|
|||||||
if (!slct__event_session_id) return [];
|
if (!slct__event_session_id) return [];
|
||||||
|
|
||||||
if (log_lvl > 1) {
|
if (log_lvl > 1) {
|
||||||
console.log(`[LQ] Fetching files for session: ${slct__event_session_id}`);
|
console.log(
|
||||||
|
`[LQ] Fetching files for session: ${slct__event_session_id}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await db_events.file
|
return await db_events.file
|
||||||
@@ -77,7 +88,9 @@
|
|||||||
if (!slct__event_session_id) return [];
|
if (!slct__event_session_id) return [];
|
||||||
|
|
||||||
if (log_lvl > 1) {
|
if (log_lvl > 1) {
|
||||||
console.log(`[LQ] Fetching presentations for session: ${slct__event_session_id}`);
|
console.log(
|
||||||
|
`[LQ] Fetching presentations for session: ${slct__event_session_id}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sort_by = 'start_datetime';
|
let sort_by = 'start_datetime';
|
||||||
@@ -97,7 +110,9 @@
|
|||||||
if (!slct__event_session_id) return [];
|
if (!slct__event_session_id) return [];
|
||||||
|
|
||||||
if (log_lvl > 1) {
|
if (log_lvl > 1) {
|
||||||
console.log(`[LQ] Fetching presenters for session: ${slct__event_session_id}`);
|
console.log(
|
||||||
|
`[LQ] Fetching presenters for session: ${slct__event_session_id}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await db_events.presenter
|
return await db_events.presenter
|
||||||
@@ -146,15 +161,14 @@
|
|||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
event_launcher_session_view
|
event_launcher_session_view
|
||||||
grow h-full w-full
|
relative h-full w-full
|
||||||
|
grow
|
||||||
space-y-1
|
space-y-1
|
||||||
relative
|
">
|
||||||
"
|
|
||||||
>
|
|
||||||
<!-- <slot name="event_session_message">event session message</slot> -->
|
<!-- <slot name="event_session_message">event session message</slot> -->
|
||||||
|
|
||||||
{#if $events_sess.launcher.loading__session_id_status}
|
{#if $events_sess.launcher.loading__session_id_status}
|
||||||
<span class="absolute top-0 right-0 text-sm text-center text-gray-400">
|
<span class="absolute top-0 right-0 text-center text-sm text-gray-400">
|
||||||
<LoaderCircle size="1em" class="inline animate-spin" />
|
<LoaderCircle size="1em" class="inline animate-spin" />
|
||||||
Loading session information...
|
Loading session information...
|
||||||
</span>
|
</span>
|
||||||
@@ -176,35 +190,33 @@
|
|||||||
collapse the header below that height. Zero layout shift between sessions.
|
collapse the header below that height. Zero layout shift between sessions.
|
||||||
-->
|
-->
|
||||||
<header
|
<header
|
||||||
class="event_session_about border-b-2 border-gray-400 dark:border-gray-600 flex flex-col gap-0.5 items-stretch"
|
class="event_session_about flex flex-col items-stretch gap-0.5 border-b-2 border-gray-400 dark:border-gray-600">
|
||||||
>
|
|
||||||
<h3
|
<h3
|
||||||
class:hidden={!$lq__event_session_obj?.start_datetime ||
|
class:hidden={!$lq__event_session_obj?.start_datetime ||
|
||||||
$events_loc.launcher.hide__session_datetimes}
|
$events_loc.launcher.hide__session_datetimes}
|
||||||
class="event_session_datetimes text-sm text-center"
|
class="event_session_datetimes text-center text-sm">
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (
|
if (
|
||||||
$events_loc.launcher.time_format == 'time_12_short'
|
$events_loc.launcher.time_format ==
|
||||||
|
'time_12_short'
|
||||||
) {
|
) {
|
||||||
// $events_loc.launcher.datetime_format = 'datetime_long';
|
// $events_loc.launcher.datetime_format = 'datetime_long';
|
||||||
$events_loc.launcher.time_format = 'time_short';
|
$events_loc.launcher.time_format = 'time_short';
|
||||||
$events_loc.launcher.time_hours = 24;
|
$events_loc.launcher.time_hours = 24;
|
||||||
} else {
|
} else {
|
||||||
$events_loc.launcher.time_format = 'time_12_short';
|
$events_loc.launcher.time_format =
|
||||||
|
'time_12_short';
|
||||||
// $events_loc.launcher.datetime_format = 'datetime_12_long';
|
// $events_loc.launcher.datetime_format = 'datetime_12_long';
|
||||||
$events_loc.launcher.time_hours = 12;
|
$events_loc.launcher.time_hours = 12;
|
||||||
}
|
}
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
<strong
|
<strong
|
||||||
>{ae_util.iso_datetime_formatter(
|
>{ae_util.iso_datetime_formatter(
|
||||||
$lq__event_session_obj.start_datetime,
|
$lq__event_session_obj.start_datetime,
|
||||||
'week_long'
|
'week_long'
|
||||||
)}</strong
|
)}</strong>
|
||||||
>
|
|
||||||
<span class="font-normal">
|
<span class="font-normal">
|
||||||
{ae_util.iso_datetime_formatter(
|
{ae_util.iso_datetime_formatter(
|
||||||
$lq__event_session_obj.start_datetime,
|
$lq__event_session_obj.start_datetime,
|
||||||
@@ -215,8 +227,7 @@
|
|||||||
>{ae_util.iso_datetime_formatter(
|
>{ae_util.iso_datetime_formatter(
|
||||||
$lq__event_session_obj.start_datetime,
|
$lq__event_session_obj.start_datetime,
|
||||||
$events_loc.launcher.time_format
|
$events_loc.launcher.time_format
|
||||||
)}</strong
|
)}</strong>
|
||||||
>
|
|
||||||
<span class="font-normal">
|
<span class="font-normal">
|
||||||
–
|
–
|
||||||
{ae_util.iso_datetime_formatter(
|
{ae_util.iso_datetime_formatter(
|
||||||
@@ -228,21 +239,18 @@
|
|||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="w-full flex flex-row gap-2 items-center justify-between"
|
class="flex w-full flex-row items-center justify-between gap-2">
|
||||||
>
|
|
||||||
<!-- grow + line-clamp-2 = stable 2-line max; title provides full text for screen readers + hover -->
|
<!-- grow + line-clamp-2 = stable 2-line max; title provides full text for screen readers + hover -->
|
||||||
<h2
|
<h2
|
||||||
class="grow text-xl line-clamp-2 min-w-0"
|
class="line-clamp-2 min-w-0 grow text-xl"
|
||||||
title={`Name: ${$lq__event_session_obj.name}\nType: ${$lq__event_session_obj.type_code} \nCode: ${$lq__event_session_obj.code} \nID: ${$lq__event_session_obj.event_session_id} \nStart Date/Time: ${ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, 'week_long')} ${ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, $events_loc.launcher.time_format)} \nEnd Date/Time: ${ae_util.iso_datetime_formatter($lq__event_session_obj.end_datetime, $events_loc.launcher.time_format)}`}
|
title={`Name: ${$lq__event_session_obj.name}\nType: ${$lq__event_session_obj.type_code} \nCode: ${$lq__event_session_obj.code} \nID: ${$lq__event_session_obj.event_session_id} \nStart Date/Time: ${ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, 'week_long')} ${ae_util.iso_datetime_formatter($lq__event_session_obj.start_datetime, $events_loc.launcher.time_format)} \nEnd Date/Time: ${ae_util.iso_datetime_formatter($lq__event_session_obj.end_datetime, $events_loc.launcher.time_format)}`}>
|
||||||
>
|
|
||||||
{$lq__event_session_obj?.name}
|
{$lq__event_session_obj?.name}
|
||||||
</h2>
|
</h2>
|
||||||
{#if $lq__event_session_obj?.code}
|
{#if $lq__event_session_obj?.code}
|
||||||
<!-- shrink-0: code badge never gets squeezed by a long name -->
|
<!-- shrink-0: code badge never gets squeezed by a long name -->
|
||||||
<span
|
<span
|
||||||
class="shrink-0 text-base text-gray-500 font-normal p-1"
|
class="shrink-0 p-1 text-base font-normal text-gray-500"
|
||||||
title="Session code {$lq__event_session_obj.code}"
|
title="Session code {$lq__event_session_obj.code}">
|
||||||
>
|
|
||||||
<Barcode size="1em" class="inline" />
|
<Barcode size="1em" class="inline" />
|
||||||
{$lq__event_session_obj?.code}
|
{$lq__event_session_obj?.code}
|
||||||
</span>
|
</span>
|
||||||
@@ -255,7 +263,7 @@
|
|||||||
</section> -->
|
</section> -->
|
||||||
|
|
||||||
{#if $lq__event_session_obj?.file_count_all === 0}
|
{#if $lq__event_session_obj?.file_count_all === 0}
|
||||||
<p class="text-2xl text-center text-red-500 font-bold">
|
<p class="text-center text-2xl font-bold text-red-500">
|
||||||
<AlertTriangle size="1em" class="inline" />
|
<AlertTriangle size="1em" class="inline" />
|
||||||
Warning
|
Warning
|
||||||
<AlertTriangle size="1em" class="inline" />
|
<AlertTriangle size="1em" class="inline" />
|
||||||
@@ -267,15 +275,14 @@
|
|||||||
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
|
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
|
||||||
<section class="event_session_file_list">
|
<section class="event_session_file_list">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs text-surface-600-400">
|
<div class="text-surface-600-400 text-xs">
|
||||||
<strong>
|
<strong>
|
||||||
<Archive size="1em" class="inline" />
|
<Archive size="1em" class="inline" />
|
||||||
Session Files:
|
Session Files:
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class:hidden={!$ae_loc.trusted_access ||
|
class:hidden={!$ae_loc.trusted_access ||
|
||||||
!$ae_loc.edit_mode}
|
!$ae_loc.edit_mode}>
|
||||||
>
|
|
||||||
({$lq__event_file_obj_li?.length}×)
|
({$lq__event_file_obj_li?.length}×)
|
||||||
</span>
|
</span>
|
||||||
</strong>
|
</strong>
|
||||||
@@ -291,11 +298,10 @@
|
|||||||
<ul class="space-y-1">
|
<ul class="space-y-1">
|
||||||
{#each $lq__event_file_obj_li as event_file_obj (event_file_obj.event_file_id)}
|
{#each $lq__event_file_obj_li as event_file_obj (event_file_obj.event_file_id)}
|
||||||
<li
|
<li
|
||||||
class="flex flex-row flex-wrap gap-1 items-center justify-center"
|
class="flex flex-row flex-wrap items-center justify-center gap-1"
|
||||||
class:hidden={!$events_loc.launcher
|
class:hidden={!$events_loc.launcher
|
||||||
.show_content__hidden_files &&
|
.show_content__hidden_files &&
|
||||||
event_file_obj.hide}
|
event_file_obj.hide}>
|
||||||
>
|
|
||||||
<Event_launcher_file_cont
|
<Event_launcher_file_cont
|
||||||
event_file_id={event_file_obj.event_file_id}
|
event_file_id={event_file_obj.event_file_id}
|
||||||
{event_file_obj}
|
{event_file_obj}
|
||||||
@@ -303,7 +309,9 @@
|
|||||||
show_bak_download={$ae_loc.trusted_access &&
|
show_bak_download={$ae_loc.trusted_access &&
|
||||||
$ae_loc.edit_mode}
|
$ae_loc.edit_mode}
|
||||||
session_type={type_code || 'oral'}
|
session_type={type_code || 'oral'}
|
||||||
open_method={type_code == 'poster' ? 'modal' : null}
|
open_method={type_code == 'poster'
|
||||||
|
? 'modal'
|
||||||
|
: null}
|
||||||
modal_title={$lq__event_session_obj?.name}
|
modal_title={$lq__event_session_obj?.name}
|
||||||
bind:modal__title={
|
bind:modal__title={
|
||||||
$events_sess.launcher.modal__title
|
$events_sess.launcher.modal__title
|
||||||
@@ -313,9 +321,9 @@
|
|||||||
.modal__open_event_file_id
|
.modal__open_event_file_id
|
||||||
}
|
}
|
||||||
bind:modal__event_file_obj={
|
bind:modal__event_file_obj={
|
||||||
$events_sess.launcher.modal__event_file_obj
|
$events_sess.launcher
|
||||||
}
|
.modal__event_file_obj
|
||||||
/>
|
} />
|
||||||
|
|
||||||
<!-- <Launcher_file_cont {event_file_obj} hide_created_on={false} show_bak_download={($ae_loc.trusted_access || $events_loc.launcher.trusted_access)} open_file_as={$lq__event_session_obj.type_code} poster_title={$lq__event_session_obj.title} /> -->
|
<!-- <Launcher_file_cont {event_file_obj} hide_created_on={false} show_bak_download={($ae_loc.trusted_access || $events_loc.launcher.trusted_access)} open_file_as={$lq__event_session_obj.type_code} poster_title={$lq__event_session_obj.title} /> -->
|
||||||
|
|
||||||
@@ -342,7 +350,7 @@
|
|||||||
<!-- {$lq__event_session_obj?.event_presentation_li?.length ?? 'loading...?'} -->
|
<!-- {$lq__event_session_obj?.event_presentation_li?.length ?? 'loading...?'} -->
|
||||||
|
|
||||||
{#if $lq__event_presentation_obj_li}
|
{#if $lq__event_presentation_obj_li}
|
||||||
<div class="text-xs text-surface-600-400">
|
<div class="text-surface-600-400 text-xs">
|
||||||
<strong>
|
<strong>
|
||||||
{#if type_code == 'poster'}
|
{#if type_code == 'poster'}
|
||||||
<Image size="1em" class="inline" />
|
<Image size="1em" class="inline" />
|
||||||
@@ -361,27 +369,24 @@
|
|||||||
<ul class="event_presentation_list max-w-full space-y-2">
|
<ul class="event_presentation_list max-w-full space-y-2">
|
||||||
{#each $lq__event_presentation_obj_li as event_presentation_obj (event_presentation_obj.event_presentation_id)}
|
{#each $lq__event_presentation_obj_li as event_presentation_obj (event_presentation_obj.event_presentation_id)}
|
||||||
<li
|
<li
|
||||||
class="border-b-2 border-gray-300 dark:border-gray-700 my-1 py-1 text-center md:text-left"
|
class="my-1 border-b-2 border-gray-300 py-1 text-center md:text-left dark:border-gray-700">
|
||||||
>
|
|
||||||
<!-- The presentation information -->
|
<!-- The presentation information -->
|
||||||
<div
|
<div
|
||||||
class="event_presentation_datetime_name flex flex-row justify-evenly gap-4"
|
class="event_presentation_datetime_name flex flex-row justify-evenly gap-4">
|
||||||
>
|
|
||||||
<!-- <div class="event_presentation_datetime_name"> -->
|
<!-- <div class="event_presentation_datetime_name"> -->
|
||||||
{#if event_presentation_obj?.start_datetime}
|
{#if event_presentation_obj?.start_datetime}
|
||||||
<span class="event_presentation_datetime"
|
<span
|
||||||
|
class="event_presentation_datetime"
|
||||||
><strong
|
><strong
|
||||||
>{ae_util.iso_datetime_formatter(
|
>{ae_util.iso_datetime_formatter(
|
||||||
event_presentation_obj?.start_datetime,
|
event_presentation_obj?.start_datetime,
|
||||||
'time_12_short_no_leading'
|
'time_12_short_no_leading'
|
||||||
)}</strong
|
)}</strong
|
||||||
></span
|
></span>
|
||||||
>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<span class="event_presentation_name grow"
|
<span class="event_presentation_name grow"
|
||||||
>{event_presentation_obj?.name}</span
|
>{event_presentation_obj?.name}</span>
|
||||||
>
|
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
|
|
||||||
<!-- Yes, this is kind of inefficient, but it works for now. -->
|
<!-- Yes, this is kind of inefficient, but it works for now. -->
|
||||||
@@ -389,15 +394,18 @@
|
|||||||
{#each $lq__event_presenter_obj_li as event_presenter_obj, index (event_presenter_obj.event_presenter_id)}
|
{#each $lq__event_presenter_obj_li as event_presenter_obj, index (event_presenter_obj.event_presenter_id)}
|
||||||
{#if event_presenter_obj.event_presentation_id == event_presentation_obj.event_presentation_id}
|
{#if event_presenter_obj.event_presentation_id == event_presentation_obj.event_presentation_id}
|
||||||
<span
|
<span
|
||||||
class="event_presentation_single_presenter italic text-sm text-gray-500"
|
class="event_presentation_single_presenter text-sm text-gray-500 italic">
|
||||||
>
|
|
||||||
{#if $lq__event_presenter_obj_li[index]?.given_name && $lq__event_presenter_obj_li[index]?.given_name != 'Group'}
|
{#if $lq__event_presenter_obj_li[index]?.given_name && $lq__event_presenter_obj_li[index]?.given_name != 'Group'}
|
||||||
<User size="0.85em" class="inline" />
|
<User
|
||||||
|
size="0.85em"
|
||||||
|
class="inline" />
|
||||||
{$lq__event_presenter_obj_li[
|
{$lq__event_presenter_obj_li[
|
||||||
index
|
index
|
||||||
]?.full_name}
|
]?.full_name}
|
||||||
{:else if $lq__event_presenter_obj_li[index]?.given_name == 'Group'}
|
{:else if $lq__event_presenter_obj_li[index]?.given_name == 'Group'}
|
||||||
<Users size="0.85em" class="inline" />
|
<Users
|
||||||
|
size="0.85em"
|
||||||
|
class="inline" />
|
||||||
{$lq__event_presenter_obj_li[
|
{$lq__event_presenter_obj_li[
|
||||||
index
|
index
|
||||||
]?.affiliations}
|
]?.affiliations}
|
||||||
@@ -413,8 +421,7 @@
|
|||||||
<!-- Presentation-level files -->
|
<!-- Presentation-level files -->
|
||||||
<Launcher_presentation_view
|
<Launcher_presentation_view
|
||||||
lq__event_presentation_obj={event_presentation_obj}
|
lq__event_presentation_obj={event_presentation_obj}
|
||||||
session_type={type_code}
|
session_type={type_code} />
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- The presenter list -->
|
<!-- The presenter list -->
|
||||||
<!-- WHY: In poster mode, presenter names are already shown inline
|
<!-- WHY: In poster mode, presenter names are already shown inline
|
||||||
@@ -426,29 +433,27 @@
|
|||||||
so this has no visual cost for events that use presentation-level files. -->
|
so this has no visual cost for events that use presentation-level files. -->
|
||||||
|
|
||||||
{#if $lq__event_presenter_obj_li && $lq__event_presenter_obj_li.length}
|
{#if $lq__event_presenter_obj_li && $lq__event_presenter_obj_li.length}
|
||||||
<ul class="event_presentation_presenter_list">
|
<ul
|
||||||
|
class="event_presentation_presenter_list">
|
||||||
{#each $lq__event_presenter_obj_li as event_presenter_obj (event_presenter_obj.event_presenter_id)}
|
{#each $lq__event_presenter_obj_li as event_presenter_obj (event_presenter_obj.event_presenter_id)}
|
||||||
{#if event_presenter_obj.event_presentation_id == event_presentation_obj.event_presentation_id}
|
{#if event_presenter_obj.event_presentation_id == event_presentation_obj.event_presentation_id}
|
||||||
<li
|
<li
|
||||||
class="
|
class="
|
||||||
border border-transparent
|
hover:bg-surface-100-900 hover:border-surface-400-600
|
||||||
rounded-lg
|
rounded-lg
|
||||||
hover:bg-surface-100-900
|
border
|
||||||
hover:border-surface-400-600
|
border-transparent
|
||||||
p-1
|
p-1
|
||||||
transition-all
|
transition-all
|
||||||
"
|
">
|
||||||
>
|
|
||||||
{#if type_code == 'poster'}
|
{#if type_code == 'poster'}
|
||||||
<Launcher_presenter_view_posters
|
<Launcher_presenter_view_posters
|
||||||
lq__event_presenter_obj={event_presenter_obj}
|
lq__event_presenter_obj={event_presenter_obj}
|
||||||
hide_name={true}
|
hide_name={true} />
|
||||||
/>
|
|
||||||
{:else}
|
{:else}
|
||||||
<Launcher_presenter_view
|
<Launcher_presenter_view
|
||||||
lq__event_presenter_obj={event_presenter_obj}
|
lq__event_presenter_obj={event_presenter_obj}
|
||||||
session_type={type_code}
|
session_type={type_code} />
|
||||||
/>
|
|
||||||
{/if}
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -80,43 +80,42 @@
|
|||||||
The outer div mirrors the grow/h-full/w-full contract expected by
|
The outer div mirrors the grow/h-full/w-full contract expected by
|
||||||
+layout.svelte so this slots in as a drop-in replacement for the oral view.
|
+layout.svelte so this slots in as a drop-in replacement for the oral view.
|
||||||
-->
|
-->
|
||||||
<div class="poster_session_view flex flex-col gap-0 w-full h-full overflow-hidden">
|
<div
|
||||||
|
class="poster_session_view flex h-full w-full flex-col gap-0 overflow-hidden">
|
||||||
{#if $events_sess.launcher?.loading__session_id_status}
|
{#if $events_sess.launcher?.loading__session_id_status}
|
||||||
<span class="absolute top-0 right-0 text-sm text-gray-400 flex items-center gap-1 p-1 z-10">
|
<span
|
||||||
|
class="absolute top-0 right-0 z-10 flex items-center gap-1 p-1 text-sm text-gray-400">
|
||||||
<LoaderCircle size="1em" class="animate-spin" />
|
<LoaderCircle size="1em" class="animate-spin" />
|
||||||
Loading...
|
Loading...
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $lq__event_session_obj && $lq__event_session_obj.event_session_id}
|
{#if $lq__event_session_obj && $lq__event_session_obj.event_session_id}
|
||||||
|
|
||||||
<!-- ── Compact session identity strip ──────────────────────────────── -->
|
<!-- ── Compact session identity strip ──────────────────────────────── -->
|
||||||
<header
|
<header
|
||||||
class="
|
class="
|
||||||
poster_session_header
|
poster_session_header
|
||||||
flex flex-row gap-2 items-center justify-between
|
border-surface-300 dark:border-surface-600 bg-surface-100/60 dark:bg-surface-800/60 flex
|
||||||
px-2 py-1.5
|
shrink-0 flex-row
|
||||||
border-b-2 border-surface-300 dark:border-surface-600
|
items-center justify-between gap-2
|
||||||
bg-surface-100/60 dark:bg-surface-800/60
|
border-b-2 px-2
|
||||||
shrink-0
|
py-1.5
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<h2
|
<h2
|
||||||
class="text-base font-bold line-clamp-1 grow min-w-0"
|
class="line-clamp-1 min-w-0 grow text-base font-bold"
|
||||||
title={$lq__event_session_obj.name}
|
title={$lq__event_session_obj.name}>
|
||||||
>
|
<Images
|
||||||
<Images size="1em" class="inline mr-1.5 text-primary-500 opacity-70" />
|
size="1em"
|
||||||
|
class="text-primary-500 mr-1.5 inline opacity-70" />
|
||||||
{$lq__event_session_obj.name}
|
{$lq__event_session_obj.name}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<span class="flex flex-row gap-1.5 items-center shrink-0">
|
<span class="flex shrink-0 flex-row items-center gap-1.5">
|
||||||
<!-- Poster count badge -->
|
<!-- Poster count badge -->
|
||||||
{#if poster_count > 0}
|
{#if poster_count > 0}
|
||||||
<span
|
<span
|
||||||
class="text-xs font-mono font-semibold text-surface-500 bg-surface-200 dark:bg-surface-700 px-2 py-0.5 rounded-full"
|
class="text-surface-500 bg-surface-200 dark:bg-surface-700 rounded-full px-2 py-0.5 font-mono text-xs font-semibold"
|
||||||
title="Number of posters in this session"
|
title="Number of posters in this session">
|
||||||
>
|
|
||||||
{poster_count}×
|
{poster_count}×
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -124,9 +123,8 @@
|
|||||||
<!-- Session code -->
|
<!-- Session code -->
|
||||||
{#if $lq__event_session_obj.code}
|
{#if $lq__event_session_obj.code}
|
||||||
<span
|
<span
|
||||||
class="text-xs font-mono font-bold text-surface-400 bg-surface-100 dark:bg-surface-800 px-2 py-0.5 rounded border border-surface-300 dark:border-surface-600"
|
class="text-surface-400 bg-surface-100 dark:bg-surface-800 border-surface-300 dark:border-surface-600 rounded border px-2 py-0.5 font-mono text-xs font-bold"
|
||||||
title="Session code: {$lq__event_session_obj.code}"
|
title="Session code: {$lq__event_session_obj.code}">
|
||||||
>
|
|
||||||
{$lq__event_session_obj.code}
|
{$lq__event_session_obj.code}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -135,16 +133,18 @@
|
|||||||
|
|
||||||
<!-- ── Session-level files (rarely present — program, schedule, etc.) ── -->
|
<!-- ── Session-level files (rarely present — program, schedule, etc.) ── -->
|
||||||
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
|
{#if $lq__event_file_obj_li && $lq__event_file_obj_li.length}
|
||||||
<section class="session_resources_strip px-2 pb-2 pt-1 border-b border-surface-200 dark:border-surface-700 shrink-0">
|
<section
|
||||||
<p class="text-[10px] text-surface-500 uppercase font-bold tracking-wider mb-1 opacity-60">
|
class="session_resources_strip border-surface-200 dark:border-surface-700 shrink-0 border-b px-2 pt-1 pb-2">
|
||||||
|
<p
|
||||||
|
class="text-surface-500 mb-1 text-[10px] font-bold tracking-wider uppercase opacity-60">
|
||||||
Session Resources:
|
Session Resources:
|
||||||
</p>
|
</p>
|
||||||
<ul class="flex flex-row flex-wrap gap-2">
|
<ul class="flex flex-row flex-wrap gap-2">
|
||||||
{#each $lq__event_file_obj_li as event_file_obj (event_file_obj.event_file_id)}
|
{#each $lq__event_file_obj_li as event_file_obj (event_file_obj.event_file_id)}
|
||||||
<li
|
<li
|
||||||
class:hidden={!$events_loc.launcher.show_content__hidden_files &&
|
class:hidden={!$events_loc.launcher
|
||||||
event_file_obj.hide}
|
.show_content__hidden_files &&
|
||||||
>
|
event_file_obj.hide}>
|
||||||
<Event_launcher_file_cont
|
<Event_launcher_file_cont
|
||||||
event_file_id={event_file_obj.event_file_id}
|
event_file_id={event_file_obj.event_file_id}
|
||||||
{event_file_obj}
|
{event_file_obj}
|
||||||
@@ -163,8 +163,7 @@
|
|||||||
}
|
}
|
||||||
bind:modal__event_file_obj={
|
bind:modal__event_file_obj={
|
||||||
$events_sess.launcher.modal__event_file_obj
|
$events_sess.launcher.modal__event_file_obj
|
||||||
}
|
} />
|
||||||
/>
|
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -174,18 +173,20 @@
|
|||||||
<!-- ── Poster card grid ────────────────────────────────────────────── -->
|
<!-- ── Poster card grid ────────────────────────────────────────────── -->
|
||||||
{#if $lq__event_presentation_obj_li === undefined}
|
{#if $lq__event_presentation_obj_li === undefined}
|
||||||
<!-- Still resolving from Dexie -->
|
<!-- Still resolving from Dexie -->
|
||||||
<div class="flex items-center justify-center gap-2 p-10 opacity-40 grow">
|
<div
|
||||||
|
class="flex grow items-center justify-center gap-2 p-10 opacity-40">
|
||||||
<LoaderCircle size="2em" class="animate-spin" />
|
<LoaderCircle size="2em" class="animate-spin" />
|
||||||
<span>Loading posters…</span>
|
<span>Loading posters…</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{:else if $lq__event_presentation_obj_li.length === 0}
|
{:else if $lq__event_presentation_obj_li.length === 0}
|
||||||
<!-- Loaded but empty -->
|
<!-- Loaded but empty -->
|
||||||
<div class="flex flex-col items-center justify-center gap-3 p-12 opacity-40 text-center grow">
|
<div
|
||||||
|
class="flex grow flex-col items-center justify-center gap-3 p-12 text-center opacity-40">
|
||||||
<Image size="3em" />
|
<Image size="3em" />
|
||||||
<p class="text-lg font-medium">No posters in this session yet.</p>
|
<p class="text-lg font-medium">
|
||||||
|
No posters in this session yet.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{:else}
|
{:else}
|
||||||
<!--
|
<!--
|
||||||
Grid: 1 col on phone, 2 on tablet (sm), 3 on large desktop (xl).
|
Grid: 1 col on phone, 2 on tablet (sm), 3 on large desktop (xl).
|
||||||
@@ -197,15 +198,14 @@
|
|||||||
class="
|
class="
|
||||||
poster_card_grid
|
poster_card_grid
|
||||||
grid
|
grid
|
||||||
|
grow
|
||||||
grid-cols-1
|
grid-cols-1
|
||||||
|
gap-3
|
||||||
|
overflow-y-auto
|
||||||
|
p-3
|
||||||
sm:grid-cols-2
|
sm:grid-cols-2
|
||||||
xl:grid-cols-3
|
xl:grid-cols-3
|
||||||
gap-3
|
">
|
||||||
p-3
|
|
||||||
overflow-y-auto
|
|
||||||
grow
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{#each $lq__event_presentation_obj_li as presentation, i (presentation.event_presentation_id)}
|
{#each $lq__event_presentation_obj_li as presentation, i (presentation.event_presentation_id)}
|
||||||
{@const presenters_for_this = (
|
{@const presenters_for_this = (
|
||||||
$lq__event_presenter_obj_li ?? []
|
$lq__event_presenter_obj_li ?? []
|
||||||
@@ -223,18 +223,17 @@
|
|||||||
<li
|
<li
|
||||||
class="
|
class="
|
||||||
poster_card
|
poster_card
|
||||||
relative flex flex-col gap-2
|
border-surface-200 dark:border-surface-700 dark:bg-surface-900 hover:border-primary-400
|
||||||
rounded-xl
|
dark:hover:border-primary-500
|
||||||
border border-surface-200 dark:border-surface-700
|
relative flex min-h-40
|
||||||
bg-white dark:bg-surface-900
|
flex-col gap-2
|
||||||
hover:border-primary-400 dark:hover:border-primary-500
|
rounded-xl border
|
||||||
active:scale-[0.98] active:shadow-sm
|
bg-white p-3
|
||||||
transition-all duration-150
|
shadow-sm transition-all
|
||||||
shadow-sm hover:shadow-md
|
duration-150 hover:shadow-md
|
||||||
p-3
|
active:scale-[0.98]
|
||||||
min-h-40
|
active:shadow-sm
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<!--
|
<!--
|
||||||
Top-right badge: prefer the presentation code (e.g. "P-042")
|
Top-right badge: prefer the presentation code (e.g. "P-042")
|
||||||
as it matches physical poster board numbers; fall back to
|
as it matches physical poster board numbers; fall back to
|
||||||
@@ -242,18 +241,17 @@
|
|||||||
-->
|
-->
|
||||||
<span
|
<span
|
||||||
class="
|
class="
|
||||||
absolute top-2 right-2
|
text-primary-600 dark:text-primary-400 bg-primary-50
|
||||||
text-xs font-mono font-bold leading-tight
|
dark:bg-primary-950/60 border-primary-200 dark:border-primary-800 absolute
|
||||||
text-primary-600 dark:text-primary-400
|
top-2 right-2
|
||||||
bg-primary-50 dark:bg-primary-950/60
|
rounded-full border
|
||||||
border border-primary-200 dark:border-primary-800
|
px-2 py-0.5 font-mono
|
||||||
px-2 py-0.5
|
text-xs leading-tight
|
||||||
rounded-full
|
font-bold
|
||||||
"
|
"
|
||||||
title="{presentation.code
|
title={presentation.code
|
||||||
? 'Poster code: ' + presentation.code
|
? 'Poster code: ' + presentation.code
|
||||||
: 'Poster #' + (i + 1)}"
|
: 'Poster #' + (i + 1)}>
|
||||||
>
|
|
||||||
{presentation.code || '#' + (i + 1)}
|
{presentation.code || '#' + (i + 1)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -266,15 +264,14 @@
|
|||||||
<h3
|
<h3
|
||||||
class="
|
class="
|
||||||
poster_title
|
poster_title
|
||||||
text-base md:text-lg
|
|
||||||
font-bold leading-snug
|
|
||||||
line-clamp-3
|
|
||||||
text-surface-950 dark:text-surface-50
|
text-surface-950 dark:text-surface-50
|
||||||
|
line-clamp-3 grow
|
||||||
pr-14
|
pr-14
|
||||||
grow
|
text-base leading-snug
|
||||||
|
font-bold
|
||||||
|
md:text-lg
|
||||||
"
|
"
|
||||||
title={presentation.name}
|
title={presentation.name}>
|
||||||
>
|
|
||||||
{presentation.name}
|
{presentation.name}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
@@ -286,37 +283,38 @@
|
|||||||
"Group" presenter whose full name is stored in affiliations.
|
"Group" presenter whose full name is stored in affiliations.
|
||||||
-->
|
-->
|
||||||
{#if presenters_for_this.length}
|
{#if presenters_for_this.length}
|
||||||
<div class="presenter_info space-y-0.5 shrink-0">
|
<div class="presenter_info shrink-0 space-y-0.5">
|
||||||
{#each presenters_for_this as presenter (presenter.event_presenter_id)}
|
{#each presenters_for_this as presenter (presenter.event_presenter_id)}
|
||||||
<p
|
<p
|
||||||
class="
|
class="
|
||||||
flex flex-row flex-wrap items-baseline gap-x-1.5
|
text-surface-500 dark:text-surface-400 flex flex-row flex-wrap
|
||||||
text-sm text-surface-500 dark:text-surface-400
|
items-baseline gap-x-1.5 text-sm
|
||||||
leading-snug
|
leading-snug
|
||||||
"
|
">
|
||||||
>
|
|
||||||
{#if presenter.given_name && presenter.given_name !== 'Group'}
|
{#if presenter.given_name && presenter.given_name !== 'Group'}
|
||||||
<User size="0.7em" class="opacity-50 shrink-0 mt-px" />
|
<User
|
||||||
|
size="0.7em"
|
||||||
|
class="mt-px shrink-0 opacity-50" />
|
||||||
<span
|
<span
|
||||||
class="font-medium text-surface-700 dark:text-surface-300"
|
class="text-surface-700 dark:text-surface-300 font-medium"
|
||||||
>{presenter.full_name}</span
|
>{presenter.full_name}</span>
|
||||||
>
|
|
||||||
{#if presenter.affiliations}
|
{#if presenter.affiliations}
|
||||||
<span
|
<span
|
||||||
class="italic text-xs opacity-70 line-clamp-1 min-w-0"
|
class="line-clamp-1 min-w-0 text-xs italic opacity-70"
|
||||||
title={presenter.affiliations}
|
title={presenter.affiliations}>
|
||||||
>
|
|
||||||
— {presenter.affiliations}
|
— {presenter.affiliations}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if presenter.given_name === 'Group'}
|
{:else if presenter.given_name === 'Group'}
|
||||||
<Users size="0.7em" class="opacity-50 shrink-0 mt-px" />
|
<Users
|
||||||
|
size="0.7em"
|
||||||
|
class="mt-px shrink-0 opacity-50" />
|
||||||
<span
|
<span
|
||||||
class="font-medium text-surface-700 dark:text-surface-300"
|
class="text-surface-700 dark:text-surface-300 font-medium"
|
||||||
>{presenter.affiliations}</span
|
>{presenter.affiliations}</span>
|
||||||
>
|
|
||||||
{:else}
|
{:else}
|
||||||
<span class="opacity-40 text-xs">—</span>
|
<span class="text-xs opacity-40"
|
||||||
|
>—</span>
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
</p>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -329,12 +327,12 @@
|
|||||||
presenter level, or both — render both sub-components so
|
presenter level, or both — render both sub-components so
|
||||||
neither source is missed.
|
neither source is missed.
|
||||||
-->
|
-->
|
||||||
<div class="poster_actions flex flex-col gap-1 mt-auto pt-1 shrink-0">
|
<div
|
||||||
|
class="poster_actions mt-auto flex shrink-0 flex-col gap-1 pt-1">
|
||||||
<!-- Presentation-level files (the most common attachment point) -->
|
<!-- Presentation-level files (the most common attachment point) -->
|
||||||
<Launcher_presentation_view
|
<Launcher_presentation_view
|
||||||
lq__event_presentation_obj={presentation}
|
lq__event_presentation_obj={presentation}
|
||||||
session_type="poster"
|
session_type="poster" />
|
||||||
/>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Presenter-level files (some events attach the PDF here instead).
|
Presenter-level files (some events attach the PDF here instead).
|
||||||
@@ -344,18 +342,16 @@
|
|||||||
{#each presenters_for_this as presenter (presenter.event_presenter_id)}
|
{#each presenters_for_this as presenter (presenter.event_presenter_id)}
|
||||||
<Launcher_presenter_view_posters
|
<Launcher_presenter_view_posters
|
||||||
lq__event_presenter_obj={presenter}
|
lq__event_presenter_obj={presenter}
|
||||||
hide_name={true}
|
hide_name={true} />
|
||||||
/>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{:else}
|
{:else}
|
||||||
<!-- No session selected or still loading -->
|
<!-- No session selected or still loading -->
|
||||||
<div class="flex items-center justify-center gap-2 p-8 opacity-40 grow">
|
<div class="flex grow items-center justify-center gap-2 p-8 opacity-40">
|
||||||
<LoaderCircle size="1em" class="animate-spin" />
|
<LoaderCircle size="1em" class="animate-spin" />
|
||||||
<span>No session selected</span>
|
<span>No session selected</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,10 +30,11 @@
|
|||||||
let { log_lvl = $bindable(0) }: Props = $props();
|
let { log_lvl = $bindable(0) }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full max-w-full flex flex-col gap-1 items-center justify-center">
|
<div class="flex w-full max-w-full flex-col items-center justify-center gap-1">
|
||||||
<!-- ── Visibility toggles — edit mode only ── -->
|
<!-- ── Visibility toggles — edit mode only ── -->
|
||||||
{#if $ae_loc.edit_mode}
|
{#if $ae_loc.edit_mode}
|
||||||
<div class="w-full max-w-full flex flex-row gap-1 items-center justify-center">
|
<div
|
||||||
|
class="flex w-full max-w-full flex-row items-center justify-center gap-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
@@ -46,13 +47,12 @@
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
class="
|
class="
|
||||||
btn btn-sm text-xs
|
btn btn-sm preset-tonal-tertiary
|
||||||
w-1/2 max-w-1/2
|
hover:preset-filled-tertiary-500 w-1/2
|
||||||
preset-tonal-tertiary hover:preset-filled-tertiary-500
|
max-w-1/2 text-xs
|
||||||
transition-all
|
transition-all
|
||||||
"
|
"
|
||||||
title="Toggle visibility of hidden and draft files in the launcher file list."
|
title="Toggle visibility of hidden and draft files in the launcher file list.">
|
||||||
>
|
|
||||||
{#if $events_loc.launcher.show_content__hidden_files}
|
{#if $events_loc.launcher.show_content__hidden_files}
|
||||||
<EyeOff size="0.85em" class="m-1 text-neutral-800/80" />
|
<EyeOff size="0.85em" class="m-1 text-neutral-800/80" />
|
||||||
Hide Files
|
Hide Files
|
||||||
@@ -69,13 +69,12 @@
|
|||||||
!$events_loc.launcher.show_content__hidden_sessions;
|
!$events_loc.launcher.show_content__hidden_sessions;
|
||||||
}}
|
}}
|
||||||
class="
|
class="
|
||||||
btn btn-sm text-xs
|
btn btn-sm preset-tonal-tertiary
|
||||||
w-1/2 max-w-1/2
|
hover:preset-filled-tertiary-500 w-1/2
|
||||||
preset-tonal-tertiary hover:preset-filled-tertiary-500
|
max-w-1/2 text-xs
|
||||||
transition-all
|
transition-all
|
||||||
"
|
"
|
||||||
title="Toggle visibility of hidden and cancelled sessions in the launcher session list."
|
title="Toggle visibility of hidden and cancelled sessions in the launcher session list.">
|
||||||
>
|
|
||||||
{#if $events_loc.launcher.show_content__hidden_sessions}
|
{#if $events_loc.launcher.show_content__hidden_sessions}
|
||||||
<EyeOff size="0.85em" class="m-1 text-neutral-800/80" />
|
<EyeOff size="0.85em" class="m-1 text-neutral-800/80" />
|
||||||
Hide Sessions
|
Hide Sessions
|
||||||
@@ -88,7 +87,8 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- ── Accessibility controls — always visible ── -->
|
<!-- ── Accessibility controls — always visible ── -->
|
||||||
<div class="w-full max-w-full flex flex-row gap-1 items-center justify-center">
|
<div
|
||||||
|
class="flex w-full max-w-full flex-row items-center justify-center gap-1">
|
||||||
<!-- Font size cycler: default → larger → smaller → default -->
|
<!-- Font size cycler: default → larger → smaller → default -->
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -103,22 +103,28 @@
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
class="
|
class="
|
||||||
btn btn-sm text-xs
|
btn btn-sm preset-tonal-tertiary
|
||||||
|
hover:preset-filled-tertiary-500 group
|
||||||
w-1/2 max-w-1/2
|
w-1/2 max-w-1/2
|
||||||
preset-tonal-tertiary hover:preset-filled-tertiary-500
|
text-xs transition-all
|
||||||
transition-all group
|
|
||||||
"
|
"
|
||||||
title="Cycle font size (default → larger → smaller). Current: {$ae_loc.font_size_mode ?? 'default'}"
|
title="Cycle font size (default → larger → smaller). Current: {$ae_loc.font_size_mode ??
|
||||||
>
|
'default'}">
|
||||||
{#if !$ae_loc.font_size_mode || $ae_loc.font_size_mode === 'default'}
|
{#if !$ae_loc.font_size_mode || $ae_loc.font_size_mode === 'default'}
|
||||||
<span class="font-bold text-sm font-mono leading-none m-1">A</span>
|
<span class="m-1 font-mono text-sm leading-none font-bold"
|
||||||
<span class="hidden group-hover:inline-block text-xs">Font: Normal</span>
|
>A</span>
|
||||||
|
<span class="hidden text-xs group-hover:inline-block"
|
||||||
|
>Font: Normal</span>
|
||||||
{:else if $ae_loc.font_size_mode === 'larger'}
|
{:else if $ae_loc.font_size_mode === 'larger'}
|
||||||
<span class="font-bold text-base font-mono leading-none m-1">A+</span>
|
<span class="m-1 font-mono text-base leading-none font-bold"
|
||||||
<span class="hidden group-hover:inline-block text-xs">Font: Larger</span>
|
>A+</span>
|
||||||
|
<span class="hidden text-xs group-hover:inline-block"
|
||||||
|
>Font: Larger</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="font-bold text-xs font-mono leading-none m-1">A−</span>
|
<span class="m-1 font-mono text-xs leading-none font-bold"
|
||||||
<span class="hidden group-hover:inline-block text-xs">Font: Smaller</span>
|
>A−</span>
|
||||||
|
<span class="hidden text-xs group-hover:inline-block"
|
||||||
|
>Font: Smaller</span>
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -126,16 +132,17 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
$ae_loc.theme_mode = $ae_loc.theme_mode === 'dark' ? 'light' : 'dark';
|
$ae_loc.theme_mode =
|
||||||
|
$ae_loc.theme_mode === 'dark' ? 'light' : 'dark';
|
||||||
}}
|
}}
|
||||||
class="
|
class="
|
||||||
btn btn-sm text-xs
|
btn btn-sm preset-tonal-tertiary
|
||||||
|
hover:preset-filled-tertiary-500 group
|
||||||
w-1/2 max-w-1/2
|
w-1/2 max-w-1/2
|
||||||
preset-tonal-tertiary hover:preset-filled-tertiary-500
|
text-xs transition-all
|
||||||
transition-all group
|
|
||||||
"
|
"
|
||||||
title="Toggle light/dark display mode. Current: {$ae_loc.theme_mode ?? 'light'}"
|
title="Toggle light/dark display mode. Current: {$ae_loc.theme_mode ??
|
||||||
>
|
'light'}">
|
||||||
{#if $ae_loc.theme_mode === 'dark'}
|
{#if $ae_loc.theme_mode === 'dark'}
|
||||||
<Moon class="m-1 inline-block" size="1em" />
|
<Moon class="m-1 inline-block" size="1em" />
|
||||||
<span class="hidden group-hover:inline-block">Dark Mode</span>
|
<span class="hidden group-hover:inline-block">Dark Mode</span>
|
||||||
|
|||||||
@@ -148,23 +148,24 @@
|
|||||||
<!-- text-neutral-800/80 -->
|
<!-- text-neutral-800/80 -->
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
w-full max-w-full
|
flex w-full
|
||||||
flex flex-col md:flex-row flex-wrap gap-1 items-center justify-center
|
max-w-full flex-col flex-wrap items-center justify-center gap-1 md:flex-row
|
||||||
"
|
">
|
||||||
>
|
|
||||||
{#if $lq__event_location_obj_li && $lq__event_location_obj_li.length > 0}
|
{#if $lq__event_location_obj_li && $lq__event_location_obj_li.length > 0}
|
||||||
<div class="text-xs text-surface-600-400">
|
<div class="text-surface-600-400 text-xs">
|
||||||
<strong>
|
<strong>
|
||||||
Location:
|
Location:
|
||||||
<span
|
<span
|
||||||
class:hidden={!$ae_loc.trusted_access || !$ae_loc.edit_mode}
|
class:hidden={!$ae_loc.trusted_access ||
|
||||||
>
|
!$ae_loc.edit_mode}>
|
||||||
({$lq__event_location_obj_li?.length}×)
|
({$lq__event_location_obj_li?.length}×)
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- This should fade out once the data is loaded. -->
|
<!-- This should fade out once the data is loaded. -->
|
||||||
{#await ae_promises[slct_event_location_id ?? '']}
|
{#await ae_promises[slct_event_location_id ?? '']}
|
||||||
<LoaderCircle size="0.85em" class="inline animate-spin text-blue-500" />
|
<LoaderCircle
|
||||||
|
size="0.85em"
|
||||||
|
class="inline animate-spin text-blue-500" />
|
||||||
{:then result}
|
{:then result}
|
||||||
<Check size="0.85em" class="inline text-green-500/80" />
|
<Check size="0.85em" class="inline text-green-500/80" />
|
||||||
{/await}
|
{/await}
|
||||||
@@ -172,7 +173,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
class="select text-xs p-1 max-w-42"
|
class="select max-w-42 p-1 text-xs"
|
||||||
bind:value={slct_event_location_id}
|
bind:value={slct_event_location_id}
|
||||||
onchange={async () => {
|
onchange={async () => {
|
||||||
// console.log(`slct_event_location_id:`, slct_event_location_id);
|
// console.log(`slct_event_location_id:`, slct_event_location_id);
|
||||||
@@ -222,9 +223,8 @@
|
|||||||
loading__session_li_status = null;
|
loading__session_li_status = null;
|
||||||
// goto(new_url, {replaceState: true}); // Updates the URL without reloading the page
|
// goto(new_url, {replaceState: true}); // Updates the URL without reloading the page
|
||||||
goto(new_url, { replaceState: false }); // Updates the URL history without reloading the page
|
goto(new_url, { replaceState: false }); // Updates the URL history without reloading the page
|
||||||
}}
|
}}>
|
||||||
>
|
<option value="" class="text-surface-800-200 italic">
|
||||||
<option value="" class="italic text-surface-800-200">
|
|
||||||
-- select --
|
-- select --
|
||||||
</option>
|
</option>
|
||||||
{#each $lq__event_location_obj_li as event_location_obj (event_location_obj.event_location_id)}
|
{#each $lq__event_location_obj_li as event_location_obj (event_location_obj.event_location_id)}
|
||||||
|
|||||||
@@ -91,7 +91,14 @@
|
|||||||
events_trigger
|
events_trigger
|
||||||
} from '$lib/stores/ae_events_stores';
|
} from '$lib/stores/ae_events_stores';
|
||||||
import { events_func } from '$lib/ae_events/ae_events_functions';
|
import { events_func } from '$lib/ae_events/ae_events_functions';
|
||||||
import { CalendarCheck, CalendarDays, Check, EyeOff, Image, LoaderCircle } from '@lucide/svelte';
|
import {
|
||||||
|
CalendarCheck,
|
||||||
|
CalendarDays,
|
||||||
|
Check,
|
||||||
|
EyeOff,
|
||||||
|
Image,
|
||||||
|
LoaderCircle
|
||||||
|
} from '@lucide/svelte';
|
||||||
// export let slct__event_session_id: any;
|
// export let slct__event_session_id: any;
|
||||||
|
|
||||||
// *** Functions and Logic
|
// *** Functions and Logic
|
||||||
@@ -121,7 +128,9 @@
|
|||||||
const event_session_id = String(trigger_reload__event_session_obj_id);
|
const event_session_id = String(trigger_reload__event_session_obj_id);
|
||||||
|
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(`[UI Trace] trigger_reload changed to: ${event_session_id}`);
|
console.log(
|
||||||
|
`[UI Trace] trigger_reload changed to: ${event_session_id}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
@@ -154,7 +163,9 @@
|
|||||||
keepFocus: true
|
keepFocus: true
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if (log_lvl)
|
if (log_lvl)
|
||||||
console.log(`🏁 [Trace] Navigation Roundtrip: ${(performance.now() - start).toFixed(2)}ms`);
|
console.log(
|
||||||
|
`🏁 [Trace] Navigation Roundtrip: ${(performance.now() - start).toFixed(2)}ms`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -202,23 +213,24 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
w-full max-w-80
|
flex w-full
|
||||||
flex flex-col flex-wrap gap-1 items-center justify-start md:justify-center
|
max-w-80 flex-col flex-wrap items-center justify-start gap-1 md:justify-center
|
||||||
"
|
">
|
||||||
>
|
|
||||||
{#if $lq__event_session_obj_li && $lq__event_session_obj_li.length > 0}
|
{#if $lq__event_session_obj_li && $lq__event_session_obj_li.length > 0}
|
||||||
<div class="text-xs text-surface-600-400">
|
<div class="text-surface-600-400 text-xs">
|
||||||
<strong>
|
<strong>
|
||||||
Sessions:
|
Sessions:
|
||||||
<span
|
<span
|
||||||
class:hidden={!$ae_loc.trusted_access || !$ae_loc.edit_mode}
|
class:hidden={!$ae_loc.trusted_access ||
|
||||||
>
|
!$ae_loc.edit_mode}>
|
||||||
({$lq__event_session_obj_li?.length}×)
|
({$lq__event_session_obj_li?.length}×)
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- This should fade out once the data is loaded. -->
|
<!-- This should fade out once the data is loaded. -->
|
||||||
{#await ae_promises.slct__event_session_id}
|
{#await ae_promises.slct__event_session_id}
|
||||||
<LoaderCircle size="0.85em" class="inline animate-spin text-blue-500" />
|
<LoaderCircle
|
||||||
|
size="0.85em"
|
||||||
|
class="inline animate-spin text-blue-500" />
|
||||||
{:then result}
|
{:then result}
|
||||||
<Check size="0.85em" class="inline text-green-500/80" />
|
<Check size="0.85em" class="inline text-green-500/80" />
|
||||||
{/await}
|
{/await}
|
||||||
@@ -227,22 +239,20 @@
|
|||||||
|
|
||||||
<ul
|
<ul
|
||||||
class="
|
class="
|
||||||
|
m-0 flex
|
||||||
w-full max-w-full
|
w-full max-w-full
|
||||||
p-0 m-0
|
flex-col items-start justify-start gap-0 p-0
|
||||||
flex flex-col gap-0 items-start justify-start
|
">
|
||||||
"
|
|
||||||
>
|
|
||||||
{#each $lq__event_session_obj_li as event_session_obj (event_session_obj.event_session_id)}
|
{#each $lq__event_session_obj_li as event_session_obj (event_session_obj.event_session_id)}
|
||||||
<li
|
<li
|
||||||
class="
|
class="
|
||||||
session-item
|
session-item
|
||||||
relative
|
relative
|
||||||
p-0 m-0
|
m-0 w-full
|
||||||
w-full max-w-full
|
max-w-full p-0
|
||||||
"
|
"
|
||||||
class:session-active={slct__event_session_id ===
|
class:session-active={slct__event_session_id ===
|
||||||
event_session_obj?.id}
|
event_session_obj?.id}>
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onmouseenter={() => {
|
onmouseenter={() => {
|
||||||
@@ -272,17 +282,17 @@
|
|||||||
class="
|
class="
|
||||||
session-btn
|
session-btn
|
||||||
btn btn-sm
|
btn btn-sm
|
||||||
focus-visible:ring-2 focus-visible:ring-primary-400 focus-visible:ring-offset-1
|
focus-visible:ring-primary-400 m-0 flex
|
||||||
|
|
||||||
text-sm
|
w-full
|
||||||
w-full max-w-full
|
max-w-full flex-row
|
||||||
text-left
|
items-center
|
||||||
m-0
|
justify-start
|
||||||
px-1.5 py-1
|
rounded-md px-1.5
|
||||||
|
|
||||||
rounded-md
|
py-1
|
||||||
flex flex-row items-center justify-start
|
text-left text-sm transition-colors duration-200
|
||||||
transition-colors duration-200
|
focus-visible:ring-2 focus-visible:ring-offset-1
|
||||||
"
|
"
|
||||||
class:preset-filled-primary={slct__event_session_id ===
|
class:preset-filled-primary={slct__event_session_id ===
|
||||||
event_session_obj?.id}
|
event_session_obj?.id}
|
||||||
@@ -298,8 +308,7 @@
|
|||||||
event_session_obj?.hide_event_launcher)}
|
event_session_obj?.hide_event_launcher)}
|
||||||
class:opacity-40={event_session_obj?.hide ||
|
class:opacity-40={event_session_obj?.hide ||
|
||||||
event_session_obj?.hide_event_launcher}
|
event_session_obj?.hide_event_launcher}
|
||||||
title={`Session: ${event_session_obj?.name}\nID: ${event_session_obj?.id} | ${ae_util.iso_datetime_formatter(event_session_obj?.start_datetime, $events_loc.launcher.time_format)}`}
|
title={`Session: ${event_session_obj?.name}\nID: ${event_session_obj?.id} | ${ae_util.iso_datetime_formatter(event_session_obj?.start_datetime, $events_loc.launcher.time_format)}`}>
|
||||||
>
|
|
||||||
<!-- Session row layout: [date column | session name]
|
<!-- Session row layout: [date column | session name]
|
||||||
Date column is fixed-width (shrink-0) so name column always
|
Date column is fixed-width (shrink-0) so name column always
|
||||||
gets consistent space regardless of date string length.
|
gets consistent space regardless of date string length.
|
||||||
@@ -311,8 +320,7 @@
|
|||||||
When revealed, dimmed (opacity-40) with eye-slash icon. -->
|
When revealed, dimmed (opacity-40) with eye-slash icon. -->
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="border-r border-surface-400-600 pr-1 min-w-20 shrink-0"
|
class="border-surface-400-600 min-w-20 shrink-0 border-r pr-1">
|
||||||
>
|
|
||||||
{#if slct__event_session_id === event_session_obj?.id}
|
{#if slct__event_session_id === event_session_obj?.id}
|
||||||
<CalendarCheck size="0.85em" class="inline" />
|
<CalendarCheck size="0.85em" class="inline" />
|
||||||
{:else}
|
{:else}
|
||||||
@@ -321,8 +329,7 @@
|
|||||||
<span
|
<span
|
||||||
class="text-xs"
|
class="text-xs"
|
||||||
class:hidden={slct__event_session_id ===
|
class:hidden={slct__event_session_id ===
|
||||||
event_session_obj?.id}
|
event_session_obj?.id}>
|
||||||
>
|
|
||||||
{ae_util.iso_datetime_formatter(
|
{ae_util.iso_datetime_formatter(
|
||||||
event_session_obj?.start_datetime,
|
event_session_obj?.start_datetime,
|
||||||
'week_medium'
|
'week_medium'
|
||||||
@@ -339,20 +346,28 @@
|
|||||||
<span
|
<span
|
||||||
class="
|
class="
|
||||||
session-name
|
session-name
|
||||||
grow text-sm
|
min-w-0 grow
|
||||||
min-w-0
|
text-sm
|
||||||
"
|
">
|
||||||
>
|
|
||||||
{#if event_session_obj?.type_code == 'poster'}
|
{#if event_session_obj?.type_code == 'poster'}
|
||||||
<span title="Digital Poster Session"><Image size="0.85em" class="inline mr-1 text-primary-500" /></span>
|
<span title="Digital Poster Session"
|
||||||
|
><Image
|
||||||
|
size="0.85em"
|
||||||
|
class="text-primary-500 mr-1 inline" /></span>
|
||||||
{/if}
|
{/if}
|
||||||
<!-- Distinct icon styles distinguish the two hidden states:
|
<!-- Distinct icon styles distinguish the two hidden states:
|
||||||
amber = hide (globally hidden — draft, cancelled, or admin-only)
|
amber = hide (globally hidden — draft, cancelled, or admin-only)
|
||||||
muted = hide_event_launcher (suppressed in Launcher view only) -->
|
muted = hide_event_launcher (suppressed in Launcher view only) -->
|
||||||
{#if event_session_obj?.hide}
|
{#if event_session_obj?.hide}
|
||||||
<span title="Hidden session"><EyeOff size="0.85em" class="inline mr-1 text-warning-600" /></span>
|
<span title="Hidden session"
|
||||||
|
><EyeOff
|
||||||
|
size="0.85em"
|
||||||
|
class="text-warning-600 mr-1 inline" /></span>
|
||||||
{:else if event_session_obj?.hide_event_launcher}
|
{:else if event_session_obj?.hide_event_launcher}
|
||||||
<span title="Hidden from Launcher"><EyeOff size="0.85em" class="inline mr-1 opacity-60" /></span>
|
<span title="Hidden from Launcher"
|
||||||
|
><EyeOff
|
||||||
|
size="0.85em"
|
||||||
|
class="mr-1 inline opacity-60" /></span>
|
||||||
{/if}
|
{/if}
|
||||||
{event_session_obj?.name}
|
{event_session_obj?.name}
|
||||||
</span>
|
</span>
|
||||||
@@ -453,7 +468,9 @@
|
|||||||
background-color: #f1f5f9; /* slate-100 — solid light surface */
|
background-color: #f1f5f9; /* slate-100 — solid light surface */
|
||||||
border-left: 3px solid rgb(var(--color-primary-500, 99 102 241));
|
border-left: 3px solid rgb(var(--color-primary-500, 99 102 241));
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15), 0 1px 4px rgba(0, 0, 0, 0.08);
|
box-shadow:
|
||||||
|
0 4px 20px rgba(0, 0, 0, 0.15),
|
||||||
|
0 1px 4px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark mode overlay — solid dark surface, light readable text */
|
/* Dark mode overlay — solid dark surface, light readable text */
|
||||||
@@ -461,7 +478,9 @@
|
|||||||
:global(.dark) .session-item:not(.session-active):focus-within .session-btn {
|
:global(.dark) .session-item:not(.session-active):focus-within .session-btn {
|
||||||
background-color: #1e293b; /* slate-800 */
|
background-color: #1e293b; /* slate-800 */
|
||||||
color: #f1f5f9; /* slate-100 */
|
color: #f1f5f9; /* slate-100 */
|
||||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.6), 0 1px 4px rgba(0, 0, 0, 0.4);
|
box-shadow:
|
||||||
|
0 4px 24px rgba(0, 0, 0, 0.6),
|
||||||
|
0 1px 4px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Session name: single-line truncated at rest ── */
|
/* ── Session name: single-line truncated at rest ── */
|
||||||
|
|||||||
Reference in New Issue
Block a user