diff --git a/apps/manadeck/apps/web/src/routes/(app)/+layout.svelte b/apps/manadeck/apps/web/src/routes/(app)/+layout.svelte index 3c3251ea0..63e040442 100644 --- a/apps/manadeck/apps/web/src/routes/(app)/+layout.svelte +++ b/apps/manadeck/apps/web/src/routes/(app)/+layout.svelte @@ -9,7 +9,8 @@ isNavCollapsed as collapsedStore, } from '$lib/stores/navigation'; import { PillNavigation } from '@manacore/shared-ui'; - import type { PillNavItem } from '@manacore/shared-ui'; + import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui'; + import { THEME_DEFINITIONS } from '@manacore/shared-theme'; let { children } = $props(); @@ -28,6 +29,27 @@ { href: '/profile', label: 'Profile', icon: 'user' }, ]; + // Theme variant dropdown items + let themeVariantItems = $derived([ + ...theme.variants.map((variant) => ({ + id: variant, + label: `${THEME_DEFINITIONS[variant].emoji} ${THEME_DEFINITIONS[variant].label}`, + onClick: () => theme.setVariant(variant), + active: theme.variant === variant, + })), + { + id: 'all-themes', + label: '🎨 Alle Themes', + onClick: () => goto('/themes'), + active: false, + }, + ]); + + // Current theme variant label + let currentThemeVariantLabel = $derived( + `${THEME_DEFINITIONS[theme.variant].emoji} ${THEME_DEFINITIONS[theme.variant].label}` + ); + // Navigation shortcuts (Ctrl+1-5) const navRoutes = navItems.map((item) => item.href); @@ -37,6 +59,7 @@ return; } + // Ctrl+Number navigation if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) { const num = parseInt(event.key); if (num >= 1 && num <= navRoutes.length) { @@ -47,6 +70,14 @@ } } } + + // Single key shortcuts (no modifiers) + if (!event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey) { + if (event.key.toLowerCase() === 't') { + event.preventDefault(); + goto('/themes'); + } + } } function handleModeChange(isSidebar: boolean) { @@ -125,6 +156,9 @@ {isCollapsed} onCollapsedChange={handleCollapsedChange} showThemeToggle={true} + showThemeVariants={true} + {themeVariantItems} + {currentThemeVariantLabel} showLanguageSwitcher={false} primaryColor="#6366f1" /> diff --git a/apps/manadeck/apps/web/src/routes/(app)/themes/+page.svelte b/apps/manadeck/apps/web/src/routes/(app)/themes/+page.svelte new file mode 100644 index 000000000..d88bc94d9 --- /dev/null +++ b/apps/manadeck/apps/web/src/routes/(app)/themes/+page.svelte @@ -0,0 +1,19 @@ + + + + Themes | ManaDeck + + + theme.setVariant(v)} + showModeSelector={true} + currentMode={theme.mode} + onModeChange={(m) => theme.setMode(m)} + showBackButton={true} + onBack={() => goto('/decks')} +/> diff --git a/apps/picture/apps/web/src/routes/app/+layout.svelte b/apps/picture/apps/web/src/routes/app/+layout.svelte index f3a860fd2..9d2c91573 100644 --- a/apps/picture/apps/web/src/routes/app/+layout.svelte +++ b/apps/picture/apps/web/src/routes/app/+layout.svelte @@ -3,7 +3,8 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; import { PillNavigation } from '@manacore/shared-ui'; - import type { PillNavItem, PillNavElement } from '@manacore/shared-ui'; + import type { PillNavItem, PillNavElement, PillDropdownItem } from '@manacore/shared-ui'; + import { THEME_DEFINITIONS } from '@manacore/shared-theme'; import KeyboardShortcutsModal from '$lib/components/ui/KeyboardShortcutsModal.svelte'; import { theme } from '$lib/stores/theme'; import { isUIVisible, toggleUI, showKeyboardShortcuts } from '$lib/stores/ui'; @@ -71,6 +72,27 @@ { id: 'gridSmall', icon: 'gridSmall', title: 'Klein (3)' }, ]; + // Theme variant dropdown items + let themeVariantItems = $derived([ + ...theme.variants.map((variant) => ({ + id: variant, + label: `${THEME_DEFINITIONS[variant].emoji} ${THEME_DEFINITIONS[variant].label}`, + onClick: () => theme.setVariant(variant), + active: theme.variant === variant, + })), + { + id: 'all-themes', + label: '🎨 Alle Themes', + onClick: () => goto('/app/themes'), + active: false, + }, + ]); + + // Current theme variant label + let currentThemeVariantLabel = $derived( + `${THEME_DEFINITIONS[theme.variant].emoji} ${THEME_DEFINITIONS[theme.variant].label}` + ); + // Elements (divider + view mode tabs) let elements: PillNavElement[] = $derived([ { type: 'divider' as const }, @@ -129,6 +151,10 @@ e.preventDefault(); goto('/app/archive'); break; + case 't': + e.preventDefault(); + goto('/app/themes'); + break; case '1': e.preventDefault(); setViewMode('single'); @@ -174,6 +200,9 @@ {isCollapsed} onCollapsedChange={handleCollapsedChange} showThemeToggle={true} + showThemeVariants={true} + {themeVariantItems} + {currentThemeVariantLabel} showLanguageSwitcher={false} primaryColor="#3b82f6" /> diff --git a/apps/picture/apps/web/src/routes/app/themes/+page.svelte b/apps/picture/apps/web/src/routes/app/themes/+page.svelte new file mode 100644 index 000000000..fd61316e6 --- /dev/null +++ b/apps/picture/apps/web/src/routes/app/themes/+page.svelte @@ -0,0 +1,19 @@ + + + + Themes | Picture + + + theme.setVariant(v)} + showModeSelector={true} + currentMode={theme.mode} + onModeChange={(m) => theme.setMode(m)} + showBackButton={true} + onBack={() => goto('/app/gallery')} +/> diff --git a/apps/presi/apps/web/src/routes/+layout.svelte b/apps/presi/apps/web/src/routes/+layout.svelte index 1f506f883..59b88d299 100644 --- a/apps/presi/apps/web/src/routes/+layout.svelte +++ b/apps/presi/apps/web/src/routes/+layout.svelte @@ -20,14 +20,20 @@ let isCollapsed = $state(false); // Theme variant dropdown items - let themeVariantItems = $derived( - theme.variants.map((variant) => ({ + let themeVariantItems = $derived([ + ...theme.variants.map((variant) => ({ id: variant, label: `${THEME_DEFINITIONS[variant].emoji} ${THEME_DEFINITIONS[variant].label}`, onClick: () => theme.setVariant(variant), active: theme.variant === variant, - })) - ); + })), + { + id: 'all-themes', + label: '🎨 Alle Themes', + onClick: () => goto('/themes'), + active: false, + }, + ]); // Current theme variant label let currentThemeVariantLabel = $derived( @@ -76,6 +82,21 @@ goto('/login'); } + function handleKeydown(event: KeyboardEvent) { + const target = event.target as HTMLElement; + if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) { + return; + } + + // Single key shortcuts (no modifiers) + if (!event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey) { + if (event.key.toLowerCase() === 't') { + event.preventDefault(); + goto('/themes'); + } + } + } + // Redirect to login if not authenticated $effect(() => { if (!auth.isLoading && !auth.isAuthenticated && !publicRoutes.includes($page.url.pathname)) { @@ -110,6 +131,8 @@ }); + + Presi - Presentation Creator diff --git a/apps/presi/apps/web/src/routes/themes/+page.svelte b/apps/presi/apps/web/src/routes/themes/+page.svelte new file mode 100644 index 000000000..bbd586e38 --- /dev/null +++ b/apps/presi/apps/web/src/routes/themes/+page.svelte @@ -0,0 +1,19 @@ + + + + Themes | Presi + + + theme.setVariant(v)} + showModeSelector={true} + currentMode={theme.mode} + onModeChange={(m) => theme.setMode(m)} + showBackButton={true} + onBack={() => goto('/')} +/> diff --git a/packages/shared-ui/src/index.ts b/packages/shared-ui/src/index.ts index 182b408d3..0c1580b28 100644 --- a/packages/shared-ui/src/index.ts +++ b/packages/shared-ui/src/index.ts @@ -44,5 +44,6 @@ export type { KeyboardShortcut, PillNavItem, PillDropdownItem, + PillNavElement, PillNavigationProps, } from './navigation';