feat(launcher): add Clear Cache and Reload Launcher buttons to controls bar
Fills in two new buttons added to menu_launcher_controls.svelte:
- Clear Cache: removes 'ae_events_loc' from localStorage and deletes the
ae_events_db IndexedDB database, then reloads — clears stale launch state
without touching downloaded file cache or user prefs (theme/font size).
- Reload Launcher: calls native.window_control({ action: 'reload' }) in
Electron, falls back to window.location.reload() in browser mode.
Also fixes a stray 'lucide-svelte' import (merged Recycle into '@lucide/svelte')
and separates cache_status from reset_apps_status so button labels stay correct
when multiple actions fire.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
* mode or navigating the config menu. This button is intentionally prominent.
|
||||
*/
|
||||
|
||||
import { Moon, Sun, Eye, EyeOff, Columns2, Copy, XCircle } from '@lucide/svelte';
|
||||
import { Moon, Sun, Eye, EyeOff, Columns2, Copy, XCircle, Trash, Recycle } from '@lucide/svelte';
|
||||
|
||||
import { ae_loc } from '$lib/stores/ae_stores';
|
||||
import { events_loc } from '$lib/stores/ae_events_stores';
|
||||
@@ -69,6 +69,7 @@ const DEFAULT_KILL_LIST = [
|
||||
];
|
||||
|
||||
let reset_apps_status = $state('');
|
||||
let cache_status = $state('');
|
||||
|
||||
async function handle_reset_apps() {
|
||||
const native_device = ($ae_loc as any).native_device ?? null;
|
||||
@@ -79,77 +80,100 @@ async function handle_reset_apps() {
|
||||
reset_apps_status = 'Done';
|
||||
setTimeout(() => (reset_apps_status = ''), 3000);
|
||||
}
|
||||
|
||||
async function handle_cache_cleanup() {
|
||||
cache_status = 'Clearing...';
|
||||
try {
|
||||
localStorage.removeItem('ae_events_loc');
|
||||
indexedDB.deleteDatabase('ae_events_db');
|
||||
cache_status = 'Done — reloading...';
|
||||
setTimeout(() => window.location.reload(), 800);
|
||||
} catch {
|
||||
cache_status = 'Error';
|
||||
setTimeout(() => (cache_status = ''), 3000);
|
||||
}
|
||||
}
|
||||
|
||||
function handle_reload_launcher() {
|
||||
if ($ae_loc.is_native) {
|
||||
native.window_control({ action: 'reload' });
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex w-full max-w-full flex-col items-center justify-center gap-1">
|
||||
<!-- ── Edit mode controls ── -->
|
||||
{#if $ae_loc.edit_mode}
|
||||
<!-- All Files / All Sessions toggles.
|
||||
Warning color when showing hidden content — signals non-default state to operators. -->
|
||||
<div class="flex w-full max-w-full flex-row items-center justify-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
if ($events_loc.launcher.show_content__hidden_files) {
|
||||
$events_loc.launcher.show_content__hidden_files = false;
|
||||
$events_loc.launcher.show_content__internal_files = false;
|
||||
} else {
|
||||
$events_loc.launcher.show_content__hidden_files = true;
|
||||
$events_loc.launcher.show_content__internal_files = true;
|
||||
}
|
||||
}}
|
||||
class="btn btn-sm w-1/2 max-w-1/2 text-xs transition-all"
|
||||
class:preset-tonal-warning={$events_loc.launcher.show_content__hidden_files}
|
||||
class:hover:preset-filled-warning-500={$events_loc.launcher
|
||||
.show_content__hidden_files}
|
||||
class:preset-tonal-tertiary={!$events_loc.launcher.show_content__hidden_files}
|
||||
class:hover:preset-filled-tertiary-500={!$events_loc.launcher
|
||||
.show_content__hidden_files}
|
||||
title={$events_loc.launcher.show_content__hidden_files
|
||||
? 'Showing all files including hidden and draft. Tap to hide them again.'
|
||||
: 'Showing only public files. Tap to show all files including hidden and draft.'}>
|
||||
{#if $events_loc.launcher.show_content__hidden_files}
|
||||
<EyeOff size="0.85em" class="m-1" />
|
||||
Hide Files
|
||||
{:else}
|
||||
<Eye size="0.85em" class="m-1" />
|
||||
All Files
|
||||
{/if}
|
||||
</button>
|
||||
<div class="flex flex-row flex-wrap w-full max-w-full items-center justify-center gap-0.5">
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$events_loc.launcher.show_content__hidden_sessions =
|
||||
!$events_loc.launcher.show_content__hidden_sessions;
|
||||
}}
|
||||
class="btn btn-sm w-1/2 max-w-1/2 text-xs transition-all"
|
||||
class:preset-tonal-warning={$events_loc.launcher.show_content__hidden_sessions}
|
||||
class:hover:preset-filled-warning-500={$events_loc.launcher
|
||||
.show_content__hidden_sessions}
|
||||
class:preset-tonal-tertiary={!$events_loc.launcher.show_content__hidden_sessions}
|
||||
class:hover:preset-filled-tertiary-500={!$events_loc.launcher
|
||||
.show_content__hidden_sessions}
|
||||
title={$events_loc.launcher.show_content__hidden_sessions
|
||||
? 'Showing all sessions including cancelled and hidden. Tap to hide them again.'
|
||||
: 'Showing only active sessions. Tap to show all sessions including hidden and cancelled.'}>
|
||||
{#if $events_loc.launcher.show_content__hidden_sessions}
|
||||
<EyeOff size="0.85em" class="m-1" />
|
||||
Hide Sessions
|
||||
{:else}
|
||||
<Eye size="0.85em" class="m-1" />
|
||||
All Sessions
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
<!-- All Files / All Sessions toggles.
|
||||
Warning color when showing hidden content — signals non-default state to operators. -->
|
||||
<div class="min-w-32 flex flex-row flex-wrap items-center justify-center gap-0.5">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
if ($events_loc.launcher.show_content__hidden_files) {
|
||||
$events_loc.launcher.show_content__hidden_files = false;
|
||||
$events_loc.launcher.show_content__internal_files = false;
|
||||
} else {
|
||||
$events_loc.launcher.show_content__hidden_files = true;
|
||||
$events_loc.launcher.show_content__internal_files = true;
|
||||
}
|
||||
}}
|
||||
class="btn btn-sm min-w-34 w-34 max-w-1/2 text-xs transition-all"
|
||||
class:preset-tonal-warning={$events_loc.launcher.show_content__hidden_files}
|
||||
class:hover:preset-filled-warning-500={$events_loc.launcher
|
||||
.show_content__hidden_files}
|
||||
class:preset-tonal-tertiary={!$events_loc.launcher.show_content__hidden_files}
|
||||
class:hover:preset-filled-tertiary-500={!$events_loc.launcher
|
||||
.show_content__hidden_files}
|
||||
title={$events_loc.launcher.show_content__hidden_files
|
||||
? 'Showing all files including hidden and draft. Tap to hide them again.'
|
||||
: 'Showing only public files. Tap to show all files including hidden and draft.'}>
|
||||
{#if $events_loc.launcher.show_content__hidden_files}
|
||||
<EyeOff size="0.85em" class="m-1" />
|
||||
Hide Files
|
||||
{:else}
|
||||
<Eye size="0.85em" class="m-1" />
|
||||
All Files
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
{#if $ae_loc.edit_mode}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$events_loc.launcher.show_content__hidden_sessions =
|
||||
!$events_loc.launcher.show_content__hidden_sessions;
|
||||
}}
|
||||
class="btn btn-sm min-w-34 w-34 max-w-1/2 text-xs transition-all"
|
||||
class:preset-tonal-warning={$events_loc.launcher.show_content__hidden_sessions}
|
||||
class:hover:preset-filled-warning-500={$events_loc.launcher
|
||||
.show_content__hidden_sessions}
|
||||
class:preset-tonal-tertiary={!$events_loc.launcher.show_content__hidden_sessions}
|
||||
class:hover:preset-filled-tertiary-500={!$events_loc.launcher
|
||||
.show_content__hidden_sessions}
|
||||
title={$events_loc.launcher.show_content__hidden_sessions
|
||||
? 'Showing all sessions including cancelled and hidden. Tap to hide them again.'
|
||||
: 'Showing only active sessions. Tap to show all sessions including hidden and cancelled.'}>
|
||||
{#if $events_loc.launcher.show_content__hidden_sessions}
|
||||
<EyeOff size="0.85em" class="shrink-0" />
|
||||
Hide Sessions
|
||||
{:else}
|
||||
<Eye size="0.85em" class="shrink-0" />
|
||||
All Sessions
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="min-w-32 flex flex-row flex-wrap items-center justify-center gap-0.5">
|
||||
<!-- Display mode toggle — warning color when mirroring (non-default operator setting).
|
||||
Tooltip describes the CURRENT state and what pressing will do, so operators know
|
||||
which way they are switching before they tap. -->
|
||||
Tooltip describes the CURRENT state and what pressing will do, so operators know
|
||||
which way they are switching before they tap. -->
|
||||
<button
|
||||
type="button"
|
||||
onclick={toggle_display_mode}
|
||||
class="btn btn-sm w-full text-xs transition-all"
|
||||
class="btn btn-sm min-w-34 w-34 max-w-1/2 text-xs transition-all"
|
||||
class:preset-tonal-tertiary={quick_display_mode === 'extend'}
|
||||
class:hover:preset-filled-tertiary-500={quick_display_mode === 'extend'}
|
||||
class:preset-tonal-warning={quick_display_mode === 'mirror'}
|
||||
@@ -158,29 +182,55 @@ async function handle_reset_apps() {
|
||||
? 'Screens are extended: laptop can show notes while projector shows slides. Tap to mirror — make both screens show the same content.'
|
||||
: 'Screens are mirrored: both screens show the same content. Tap to extend — allow the laptop and projector to show different content.'}>
|
||||
{#if quick_display_mode === 'extend'}
|
||||
<Columns2 size="0.85em" class="m-1" />
|
||||
<Columns2 size="0.85em" class="shrink-0" />
|
||||
Display: Extend
|
||||
{:else}
|
||||
<Copy size="0.85em" class="m-1" />
|
||||
<Copy size="0.85em" class="shrink-0" />
|
||||
Display: Mirror
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ── Always-visible controls ── -->
|
||||
<div class="w-full flex flex-row flex-wrap items-center justify-center gap-0.5">
|
||||
|
||||
<!-- Reset Apps: force-closes hung presentation apps without requiring edit mode.
|
||||
Essential recovery tool for presenters stuck on stage with a frozen app. -->
|
||||
<button
|
||||
type="button"
|
||||
onclick={handle_reset_apps}
|
||||
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 w-full text-xs transition-all"
|
||||
title="Close all presentation apps (PowerPoint, Keynote, Adobe Acrobat, VLC). Use this if a presentation is frozen or stuck.">
|
||||
<XCircle size="0.85em" class="m-1 shrink-0" />
|
||||
{reset_apps_status || 'Reset Apps'}
|
||||
</button>
|
||||
{#if $ae_loc.edit_mode}
|
||||
<button
|
||||
type="button"
|
||||
onclick={handle_cache_cleanup}
|
||||
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 w-34 max-w-1/2 text-xs transition-all"
|
||||
title="Clear localStorage and IDB caches used by the Launcher. Does *not* delete cached files."
|
||||
>
|
||||
<Trash size="0.85em" class="shrink-0" />
|
||||
{cache_status || 'Clear Cache'}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<div class="flex w-full max-w-full flex-row items-center justify-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onclick={handle_reload_launcher}
|
||||
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 w-34 max-w-1/2 text-xs transition-all"
|
||||
title="Reload the Launcher interface."
|
||||
>
|
||||
<Recycle size="0.85em" class="shrink-0" />
|
||||
Reload Launcher
|
||||
</button>
|
||||
|
||||
|
||||
<!-- Reset Apps: force-closes hung presentation apps without requiring edit mode.
|
||||
Essential recovery tool for presenters stuck on stage with a frozen app. -->
|
||||
<button
|
||||
type="button"
|
||||
onclick={handle_reset_apps}
|
||||
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 w-34 max-w-1/2 text-xs transition-all"
|
||||
title="Close all presentation apps (PowerPoint, Keynote, Adobe Acrobat, VLC). Use this if a presentation is frozen or stuck.">
|
||||
<XCircle size="0.85em" class="shrink-0" />
|
||||
{reset_apps_status || 'Reset Apps'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="w-full flex flex-row flex-wrap items-center justify-center gap-0.5">
|
||||
<!-- Font size cycler: default → larger → smaller → default -->
|
||||
<button
|
||||
type="button"
|
||||
@@ -194,7 +244,7 @@ async function handle_reset_apps() {
|
||||
$ae_loc.font_size_mode = 'default';
|
||||
}
|
||||
}}
|
||||
class="btn btn-sm preset-tonal-tertiary hover:preset-filled-tertiary-500 group w-1/2 max-w-1/2 text-xs transition-all"
|
||||
class="btn btn-sm preset-tonal-tertiary hover:preset-filled-tertiary-500 group min-w-32 max-w-1/2 text-xs transition-all"
|
||||
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'}
|
||||
@@ -215,7 +265,7 @@ async function handle_reset_apps() {
|
||||
onclick={() => {
|
||||
$ae_loc.theme_mode = $ae_loc.theme_mode === 'dark' ? 'light' : 'dark';
|
||||
}}
|
||||
class="btn btn-sm preset-tonal-tertiary hover:preset-filled-tertiary-500 group w-1/2 max-w-1/2 text-xs transition-all"
|
||||
class="btn btn-sm preset-tonal-tertiary hover:preset-filled-tertiary-500 group min-w-32 max-w-1/2 text-xs transition-all"
|
||||
title="Toggle light/dark display mode. Current: {$ae_loc.theme_mode ?? 'light'}">
|
||||
{#if $ae_loc.theme_mode === 'dark'}
|
||||
<Moon class="m-1 inline-block" size="1em" />
|
||||
|
||||
Reference in New Issue
Block a user