feat(shared-ui): unify ImmersiveModeToggle across Calendar, Contacts, and Todo apps

- Move ImmersiveModeToggle component from Calendar to shared-ui package
- Add immersiveModeEnabled setting to Contacts and Todo settings stores
- Update all three app layouts with F-key shortcut and conditional UI rendering
- Consistent glass-pill styling on hover for toggle button

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-12-15 03:34:15 +01:00
parent a6d439360f
commit 893c6ef0fb
7 changed files with 246 additions and 121 deletions

View file

@ -58,6 +58,10 @@ export interface TodoAppSettings {
dailyGoal: number | null;
/** Show productivity streak */
showStreak: boolean;
// Immersive Mode
/** Fullscreen mode - hides all UI elements */
immersiveModeEnabled: boolean;
}
const DEFAULT_SETTINGS: TodoAppSettings = {
@ -89,6 +93,9 @@ const DEFAULT_SETTINGS: TodoAppSettings = {
pomodoroEnabled: false,
dailyGoal: null,
showStreak: false,
// Immersive Mode
immersiveModeEnabled: false,
};
const STORAGE_KEY = 'todo-settings';
@ -198,6 +205,19 @@ export const todoSettings = {
return settings.showStreak;
},
// Immersive Mode
get immersiveModeEnabled() {
return settings.immersiveModeEnabled;
},
/**
* Toggle Immersive Mode (fullscreen, hide all UI)
*/
toggleImmersiveMode() {
settings = { ...settings, immersiveModeEnabled: !settings.immersiveModeEnabled };
saveSettings(settings);
},
/**
* Initialize settings from localStorage
*/

View file

@ -3,7 +3,7 @@
import { page } from '$app/stores';
import { onMount } from 'svelte';
import { locale } from 'svelte-i18n';
import { PillNavigation, QuickInputBar } from '@manacore/shared-ui';
import { PillNavigation, QuickInputBar, ImmersiveModeToggle } from '@manacore/shared-ui';
import {
SplitPaneContainer,
setSplitPanelContext,
@ -18,6 +18,7 @@
} from '@manacore/shared-ui';
import { authStore } from '$lib/stores/auth.svelte';
import { userSettings } from '$lib/stores/user-settings.svelte';
import { todoSettings } from '$lib/stores/settings.svelte';
import { projectsStore } from '$lib/stores/projects.svelte';
import { labelsStore } from '$lib/stores/labels.svelte';
import { tasksStore } from '$lib/stores/tasks.svelte';
@ -215,6 +216,18 @@
}
}
}
// F = Toggle Immersive Mode (no modifier keys)
if (
(event.key === 'f' || event.key === 'F') &&
!event.ctrlKey &&
!event.metaKey &&
!event.shiftKey &&
!event.altKey
) {
event.preventDefault();
todoSettings.toggleImmersiveMode();
}
}
function handleModeChange(isSidebar: boolean) {
@ -262,6 +275,9 @@
// Initialize split-panel from URL/localStorage
splitPanel.initialize();
// Initialize todo settings
todoSettings.initialize();
// Load data
await Promise.all([
projectsStore.fetchProjects(),
@ -328,66 +344,80 @@
<SplitPaneContainer>
<div class="layout-container">
<PillNavigation
items={navItems}
currentPath={$page.url.pathname}
appName="Todo"
homeRoute="/"
onToggleTheme={handleToggleTheme}
{isDark}
{isSidebarMode}
onModeChange={handleModeChange}
{isCollapsed}
onCollapsedChange={handleCollapsedChange}
desktopPosition={userSettings.nav.desktopPosition}
showThemeToggle={true}
showThemeVariants={true}
{themeVariantItems}
{currentThemeVariantLabel}
themeMode={theme.mode}
onThemeModeChange={handleThemeModeChange}
showLanguageSwitcher={true}
{languageItems}
{currentLanguageLabel}
showLogout={authStore.isAuthenticated}
onLogout={handleLogout}
loginHref="/login"
primaryColor="#8b5cf6"
showAppSwitcher={true}
{appItems}
{userEmail}
settingsHref="/settings"
manaHref="/mana"
profileHref="/profile"
allAppsHref="/apps"
onOpenInPanel={handleOpenInPanel}
<!-- UI Elements (hidden in immersive mode) -->
{#if !todoSettings.immersiveModeEnabled}
<PillNavigation
items={navItems}
currentPath={$page.url.pathname}
appName="Todo"
homeRoute="/"
onToggleTheme={handleToggleTheme}
{isDark}
{isSidebarMode}
onModeChange={handleModeChange}
{isCollapsed}
onCollapsedChange={handleCollapsedChange}
desktopPosition={userSettings.nav.desktopPosition}
showThemeToggle={true}
showThemeVariants={true}
{themeVariantItems}
{currentThemeVariantLabel}
themeMode={theme.mode}
onThemeModeChange={handleThemeModeChange}
showLanguageSwitcher={true}
{languageItems}
{currentLanguageLabel}
showLogout={authStore.isAuthenticated}
onLogout={handleLogout}
loginHref="/login"
primaryColor="#8b5cf6"
showAppSwitcher={true}
{appItems}
{userEmail}
settingsHref="/settings"
manaHref="/mana"
profileHref="/profile"
allAppsHref="/apps"
onOpenInPanel={handleOpenInPanel}
/>
<!-- Global Quick Input Bar -->
<QuickInputBar
onSearch={handleSearch}
onSelect={handleSelect}
{quickActions}
placeholder="Neue Aufgabe oder suchen..."
emptyText="Keine Aufgaben gefunden"
searchingText="Suche..."
onCreate={handleCreate}
onParseCreate={handleParseCreate}
createText="Erstellen"
appIcon="todo"
primaryColor="#8b5cf6"
autoFocus={true}
/>
{/if}
<!-- Immersive Mode Toggle (always visible) -->
<ImmersiveModeToggle
isImmersive={todoSettings.immersiveModeEnabled}
onToggle={() => todoSettings.toggleImmersiveMode()}
/>
<main
class="main-content bg-background"
class:sidebar-mode={isSidebarMode && !isCollapsed}
class:floating-mode={!isSidebarMode && !isCollapsed}
class:immersive={todoSettings.immersiveModeEnabled}
>
<div class="content-wrapper" class:full-width={$page.url.pathname === '/kanban'}>
<div
class="content-wrapper"
class:full-width={$page.url.pathname === '/kanban'}
class:immersive={todoSettings.immersiveModeEnabled}
>
{@render children()}
</div>
</main>
<!-- Global Quick Input Bar -->
<QuickInputBar
onSearch={handleSearch}
onSelect={handleSelect}
{quickActions}
placeholder="Neue Aufgabe oder suchen..."
emptyText="Keine Aufgaben gefunden"
searchingText="Suche..."
onCreate={handleCreate}
onParseCreate={handleParseCreate}
createText="Erstellen"
appIcon="todo"
primaryColor="#8b5cf6"
autoFocus={true}
/>
</div>
</SplitPaneContainer>
@ -414,6 +444,19 @@
padding-left: 180px;
}
/* Immersive mode - fullscreen, no padding */
.main-content.immersive {
padding: 0 !important;
height: 100vh;
width: 100vw;
}
.content-wrapper.immersive {
padding: 0;
max-width: none;
height: 100%;
}
.content-wrapper {
max-width: 900px;
margin-left: auto;