feat(theme): add themes page to picture, manadeck, and presi apps

- Add dedicated /themes page with ThemePage component to all three apps
- Add theme variant dropdown with "Alle Themes" link in navigation
- Add keyboard shortcut 'T' for quick access to themes page
- Export PillNavElement type from shared-ui package

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-11-29 09:14:12 +01:00
parent 54383bf7c2
commit 8a4cc298f6
7 changed files with 150 additions and 6 deletions

View file

@ -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<PillDropdownItem[]>([
...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"
/>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { ThemePage } from '@manacore/shared-theme-ui';
import { theme } from '$lib/stores/theme';
</script>
<svelte:head>
<title>Themes | ManaDeck</title>
</svelte:head>
<ThemePage
currentVariant={theme.variant}
onSelectTheme={(v) => theme.setVariant(v)}
showModeSelector={true}
currentMode={theme.mode}
onModeChange={(m) => theme.setMode(m)}
showBackButton={true}
onBack={() => goto('/decks')}
/>

View file

@ -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<PillDropdownItem[]>([
...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"
/>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { ThemePage } from '@manacore/shared-theme-ui';
import { theme } from '$lib/stores/theme';
</script>
<svelte:head>
<title>Themes | Picture</title>
</svelte:head>
<ThemePage
currentVariant={theme.variant}
onSelectTheme={(v) => theme.setVariant(v)}
showModeSelector={true}
currentMode={theme.mode}
onModeChange={(m) => theme.setMode(m)}
showBackButton={true}
onBack={() => goto('/app/gallery')}
/>

View file

@ -20,14 +20,20 @@
let isCollapsed = $state(false);
// Theme variant dropdown items
let themeVariantItems = $derived<PillDropdownItem[]>(
theme.variants.map((variant) => ({
let themeVariantItems = $derived<PillDropdownItem[]>([
...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 @@
});
</script>
<svelte:window onkeydown={handleKeydown} />
<svelte:head>
<title>Presi - Presentation Creator</title>
</svelte:head>

View file

@ -0,0 +1,19 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { ThemePage } from '@manacore/shared-theme-ui';
import { theme } from '$lib/stores/theme';
</script>
<svelte:head>
<title>Themes | Presi</title>
</svelte:head>
<ThemePage
currentVariant={theme.variant}
onSelectTheme={(v) => theme.setVariant(v)}
showModeSelector={true}
currentMode={theme.mode}
onModeChange={(m) => theme.setMode(m)}
showBackButton={true}
onBack={() => goto('/')}
/>

View file

@ -44,5 +44,6 @@ export type {
KeyboardShortcut,
PillNavItem,
PillDropdownItem,
PillNavElement,
PillNavigationProps,
} from './navigation';