mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 03:41:10 +02:00
feat(matrix): add PillNavigation and theming integration
- Add PillNavigation component from @manacore/shared-ui - Create theme store with purple color scheme - Add i18n support (DE/EN) with svelte-i18n - Integrate theme switching, language selector, app switcher - Add glassmorphic utility classes to app.css - Update layouts to match other apps' navigation pattern Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1e5175e522
commit
cc130ccb24
10 changed files with 349 additions and 78 deletions
|
|
@ -35,10 +35,12 @@
|
|||
"buffer": "^6.0.3",
|
||||
"events": "^3.3.0",
|
||||
"@manacore/shared-auth": "workspace:*",
|
||||
"@manacore/shared-theme": "workspace:*",
|
||||
"@manacore/shared-tailwind": "workspace:*",
|
||||
"@manacore/shared-branding": "workspace:*",
|
||||
"@manacore/shared-i18n": "workspace:*",
|
||||
"@manacore/shared-icons": "workspace:*",
|
||||
"@manacore/shared-tailwind": "workspace:*",
|
||||
"@manacore/shared-theme": "workspace:*",
|
||||
"@manacore/shared-ui": "workspace:*",
|
||||
"date-fns": "^4.1.0",
|
||||
"svelte-i18n": "^4.0.1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
@source '../../../../../packages/shared-icons/src';
|
||||
@source '../../../../../packages/shared-auth-ui/src';
|
||||
@source '../../../../../packages/shared-theme-ui/src';
|
||||
@source '../../../../../packages/shared-branding/src';
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
|
|
|
|||
49
apps/matrix/apps/web/src/lib/i18n/index.ts
Normal file
49
apps/matrix/apps/web/src/lib/i18n/index.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { browser } from '$app/environment';
|
||||
import { init, register, locale, waitLocale } from 'svelte-i18n';
|
||||
|
||||
// List of supported locales
|
||||
export const supportedLocales = ['de', 'en'] as const;
|
||||
export type SupportedLocale = (typeof supportedLocales)[number];
|
||||
|
||||
// Default locale
|
||||
const defaultLocale = 'de';
|
||||
|
||||
// Register all available locales
|
||||
register('de', () => import('./locales/de.json'));
|
||||
register('en', () => import('./locales/en.json'));
|
||||
|
||||
// Get initial locale from browser or localStorage
|
||||
function getInitialLocale(): SupportedLocale {
|
||||
if (browser) {
|
||||
// Check localStorage first
|
||||
const stored = localStorage.getItem('matrix_locale');
|
||||
if (stored && supportedLocales.includes(stored as SupportedLocale)) {
|
||||
return stored as SupportedLocale;
|
||||
}
|
||||
|
||||
// Fall back to browser language
|
||||
const browserLang = navigator.language.split('-')[0];
|
||||
if (supportedLocales.includes(browserLang as SupportedLocale)) {
|
||||
return browserLang as SupportedLocale;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultLocale;
|
||||
}
|
||||
|
||||
// Initialize i18n at module scope (required for SSR)
|
||||
init({
|
||||
fallbackLocale: defaultLocale,
|
||||
initialLocale: getInitialLocale(),
|
||||
});
|
||||
|
||||
// Set locale and persist to localStorage
|
||||
export function setLocale(newLocale: SupportedLocale) {
|
||||
locale.set(newLocale);
|
||||
if (browser) {
|
||||
localStorage.setItem('matrix_locale', newLocale);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for locale to be loaded (useful for SSR)
|
||||
export { waitLocale };
|
||||
25
apps/matrix/apps/web/src/lib/i18n/locales/de.json
Normal file
25
apps/matrix/apps/web/src/lib/i18n/locales/de.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"app": {
|
||||
"name": "Mana Matrix",
|
||||
"description": "Sicherer Matrix-Chat"
|
||||
},
|
||||
"nav": {
|
||||
"chat": "Chat",
|
||||
"settings": "Einstellungen"
|
||||
},
|
||||
"auth": {
|
||||
"login": "Anmelden",
|
||||
"logout": "Abmelden",
|
||||
"connecting": "Verbinde mit Matrix...",
|
||||
"connectionFailed": "Verbindung fehlgeschlagen",
|
||||
"retry": "Erneut versuchen"
|
||||
},
|
||||
"chat": {
|
||||
"newChat": "Neuer Chat",
|
||||
"createRoom": "Raum erstellen",
|
||||
"sendMessage": "Nachricht senden",
|
||||
"typeMessage": "Nachricht schreiben...",
|
||||
"noRooms": "Noch keine Räume",
|
||||
"noMessages": "Noch keine Nachrichten"
|
||||
}
|
||||
}
|
||||
25
apps/matrix/apps/web/src/lib/i18n/locales/en.json
Normal file
25
apps/matrix/apps/web/src/lib/i18n/locales/en.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"app": {
|
||||
"name": "Mana Matrix",
|
||||
"description": "Secure Matrix chat"
|
||||
},
|
||||
"nav": {
|
||||
"chat": "Chat",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"auth": {
|
||||
"login": "Sign in",
|
||||
"logout": "Sign out",
|
||||
"connecting": "Connecting to Matrix...",
|
||||
"connectionFailed": "Connection failed",
|
||||
"retry": "Retry"
|
||||
},
|
||||
"chat": {
|
||||
"newChat": "New Chat",
|
||||
"createRoom": "Create Room",
|
||||
"sendMessage": "Send message",
|
||||
"typeMessage": "Type a message...",
|
||||
"noRooms": "No rooms yet",
|
||||
"noMessages": "No messages yet"
|
||||
}
|
||||
}
|
||||
4
apps/matrix/apps/web/src/lib/stores/navigation.ts
Normal file
4
apps/matrix/apps/web/src/lib/stores/navigation.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import { writable } from 'svelte/store';
|
||||
|
||||
export const isSidebarMode = writable(false);
|
||||
export const isNavCollapsed = writable(false);
|
||||
10
apps/matrix/apps/web/src/lib/stores/theme.ts
Normal file
10
apps/matrix/apps/web/src/lib/stores/theme.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { createThemeStore } from '@manacore/shared-theme';
|
||||
|
||||
export const theme = createThemeStore({
|
||||
appId: 'matrix',
|
||||
defaultVariant: 'purple',
|
||||
primaryColor: {
|
||||
light: '270 70% 60%', // Purple/violet
|
||||
dark: '270 70% 60%',
|
||||
},
|
||||
});
|
||||
|
|
@ -1,9 +1,30 @@
|
|||
<script lang="ts">
|
||||
import { matrixStore } from '$lib/matrix';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { locale } from 'svelte-i18n';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { CircleNotch, WarningCircle, ArrowsClockwise } from '@manacore/shared-icons';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import {
|
||||
THEME_DEFINITIONS,
|
||||
DEFAULT_THEME_VARIANTS,
|
||||
EXTENDED_THEME_VARIANTS,
|
||||
} from '@manacore/shared-theme';
|
||||
import type { ThemeVariant } from '@manacore/shared-theme';
|
||||
import {
|
||||
isSidebarMode as sidebarModeStore,
|
||||
isNavCollapsed as collapsedStore,
|
||||
} from '$lib/stores/navigation';
|
||||
import { PillNavigation } from '@manacore/shared-ui';
|
||||
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
|
||||
import { setLocale, supportedLocales } from '$lib/i18n';
|
||||
|
||||
// App switcher items
|
||||
const appItems = getPillAppItems('matrix');
|
||||
|
||||
interface Props {
|
||||
children: Snippet;
|
||||
|
|
@ -13,14 +34,116 @@
|
|||
let loading = $state(true);
|
||||
let initError = $state<string | null>(null);
|
||||
|
||||
// Navigation state
|
||||
let isSidebarMode = $state(false);
|
||||
let isCollapsed = $state(false);
|
||||
|
||||
// Theme state
|
||||
let isDark = $derived(theme.isDark);
|
||||
|
||||
// Theme variant dropdown items (default themes only for now)
|
||||
let themeVariantItems = $derived<PillDropdownItem[]>([
|
||||
...DEFAULT_THEME_VARIANTS.map((variant) => ({
|
||||
id: variant,
|
||||
label: THEME_DEFINITIONS[variant].label,
|
||||
icon: THEME_DEFINITIONS[variant].icon,
|
||||
onClick: () => theme.setVariant(variant),
|
||||
active: theme.variant === variant,
|
||||
})),
|
||||
]);
|
||||
|
||||
let currentThemeVariantLabel = $derived(THEME_DEFINITIONS[theme.variant]?.label || 'Theme');
|
||||
|
||||
// Language selector
|
||||
let currentLocale = $derived($locale || 'de');
|
||||
function handleLocaleChange(newLocale: string) {
|
||||
setLocale(newLocale as any);
|
||||
}
|
||||
let languageItems = $derived(
|
||||
getLanguageDropdownItems(supportedLocales, currentLocale, handleLocaleChange)
|
||||
);
|
||||
let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale));
|
||||
|
||||
// Navigation items for Matrix
|
||||
const navItems: PillNavItem[] = [
|
||||
{ href: '/chat', label: 'Chat', icon: 'home' },
|
||||
{ href: '/settings', label: 'Einstellungen', icon: 'settings' },
|
||||
];
|
||||
|
||||
// User info from Matrix
|
||||
let userEmail = $derived(matrixStore.userId || undefined);
|
||||
|
||||
// Keyboard shortcuts
|
||||
const navRoutes = navItems.map((item) => item.href);
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
const target = event.target as HTMLElement;
|
||||
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
|
||||
return;
|
||||
}
|
||||
if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey) {
|
||||
const num = parseInt(event.key);
|
||||
if (num >= 1 && num <= navRoutes.length) {
|
||||
event.preventDefault();
|
||||
const route = navRoutes[num - 1];
|
||||
if (route) {
|
||||
goto(route);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleModeChange(isSidebar: boolean) {
|
||||
isSidebarMode = isSidebar;
|
||||
sidebarModeStore.set(isSidebar);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('matrix-nav-sidebar', String(isSidebar));
|
||||
}
|
||||
}
|
||||
|
||||
function handleCollapsedChange(collapsed: boolean) {
|
||||
isCollapsed = collapsed;
|
||||
collapsedStore.set(collapsed);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('matrix-nav-collapsed', String(collapsed));
|
||||
}
|
||||
}
|
||||
|
||||
function handleToggleTheme() {
|
||||
theme.toggleMode();
|
||||
}
|
||||
|
||||
function handleThemeModeChange(mode: 'light' | 'dark' | 'system') {
|
||||
theme.setMode(mode);
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
matrixStore.logout();
|
||||
goto('/login');
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
// Initialize sidebar mode from localStorage
|
||||
const savedSidebar = localStorage.getItem('matrix-nav-sidebar');
|
||||
if (savedSidebar === 'true') {
|
||||
isSidebarMode = true;
|
||||
sidebarModeStore.set(true);
|
||||
}
|
||||
|
||||
// Initialize collapsed state from localStorage
|
||||
const savedCollapsed = localStorage.getItem('matrix-nav-collapsed');
|
||||
if (savedCollapsed === 'true') {
|
||||
isCollapsed = true;
|
||||
collapsedStore.set(true);
|
||||
}
|
||||
|
||||
// Check if already initialized
|
||||
if (matrixStore.isReady) {
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to initialize
|
||||
// Try to initialize Matrix
|
||||
const success = await matrixStore.initialize();
|
||||
|
||||
if (!success) {
|
||||
|
|
@ -38,7 +161,6 @@
|
|||
|
||||
onDestroy(() => {
|
||||
// Don't destroy on navigation within app routes
|
||||
// matrixStore.destroy();
|
||||
});
|
||||
|
||||
async function retry() {
|
||||
|
|
@ -50,20 +172,17 @@
|
|||
}
|
||||
loading = false;
|
||||
}
|
||||
|
||||
function logout() {
|
||||
matrixStore.logout();
|
||||
goto('/login');
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={handleKeydown} />
|
||||
|
||||
{#if loading}
|
||||
<!-- Loading State -->
|
||||
<div class="flex h-screen flex-col items-center justify-center gap-4">
|
||||
<div class="flex h-screen flex-col items-center justify-center gap-4 bg-background">
|
||||
<CircleNotch class="h-12 w-12 animate-spin text-primary" />
|
||||
<div class="text-center">
|
||||
<p class="font-medium">Connecting to Matrix...</p>
|
||||
<p class="text-sm text-base-content/60">
|
||||
<p class="font-medium text-foreground">Connecting to Matrix...</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{#if matrixStore.syncState === 'PREPARED'}
|
||||
Preparing sync...
|
||||
{:else if matrixStore.syncState === 'SYNCING'}
|
||||
|
|
@ -78,28 +197,97 @@
|
|||
</div>
|
||||
{:else if initError}
|
||||
<!-- Error State -->
|
||||
<div class="flex h-screen flex-col items-center justify-center gap-4 p-4">
|
||||
<div class="rounded-full bg-error/10 p-4">
|
||||
<WarningCircle class="h-12 w-12 text-error" />
|
||||
<div class="flex h-screen flex-col items-center justify-center gap-4 p-4 bg-background">
|
||||
<div class="rounded-full bg-red-500/10 p-4">
|
||||
<WarningCircle class="h-12 w-12 text-red-500" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h2 class="text-xl font-semibold">Connection Failed</h2>
|
||||
<p class="mt-2 max-w-md text-base-content/60">{initError}</p>
|
||||
<h2 class="text-xl font-semibold text-foreground">Connection Failed</h2>
|
||||
<p class="mt-2 max-w-md text-muted-foreground">{initError}</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-primary" onclick={retry}>
|
||||
<button
|
||||
class="px-4 py-2 rounded-xl bg-gradient-to-r from-violet-500 to-purple-600 text-white font-medium shadow-md hover:shadow-lg transition-all flex items-center gap-2"
|
||||
onclick={retry}
|
||||
>
|
||||
<ArrowsClockwise class="h-4 w-4" />
|
||||
Retry
|
||||
</button>
|
||||
<button class="btn btn-ghost" onclick={logout}> Sign Out </button>
|
||||
<button class="px-4 py-2 rounded-xl glass-button font-medium" onclick={handleLogout}>
|
||||
Sign Out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else if matrixStore.isReady}
|
||||
<!-- Ready - Render children -->
|
||||
{@render children()}
|
||||
<!-- Ready - Show navigation and content -->
|
||||
<div class="layout-container">
|
||||
<!-- PillNavigation -->
|
||||
<PillNavigation
|
||||
items={navItems}
|
||||
currentPath={$page.url.pathname}
|
||||
appName="Mana Matrix"
|
||||
homeRoute="/chat"
|
||||
onToggleTheme={handleToggleTheme}
|
||||
{isDark}
|
||||
{isSidebarMode}
|
||||
onModeChange={handleModeChange}
|
||||
{isCollapsed}
|
||||
onCollapsedChange={handleCollapsedChange}
|
||||
desktopPosition="bottom"
|
||||
showThemeToggle={true}
|
||||
showThemeVariants={true}
|
||||
{themeVariantItems}
|
||||
{currentThemeVariantLabel}
|
||||
themeMode={theme.mode}
|
||||
onThemeModeChange={handleThemeModeChange}
|
||||
showLanguageSwitcher={true}
|
||||
{languageItems}
|
||||
{currentLanguageLabel}
|
||||
showLogout={true}
|
||||
onLogout={handleLogout}
|
||||
primaryColor="#8b5cf6"
|
||||
showAppSwitcher={true}
|
||||
{appItems}
|
||||
{userEmail}
|
||||
settingsHref="/settings"
|
||||
allAppsHref="https://mana.how"
|
||||
/>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main
|
||||
class="main-content bg-background"
|
||||
class:sidebar-mode={isSidebarMode && !isCollapsed}
|
||||
class:floating-mode={!isSidebarMode && !isCollapsed}
|
||||
>
|
||||
{@render children()}
|
||||
</main>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Unknown state - redirect to login -->
|
||||
<div class="flex h-screen items-center justify-center">
|
||||
<p class="text-base-content/60">Redirecting...</p>
|
||||
<div class="flex h-screen items-center justify-center bg-background">
|
||||
<p class="text-muted-foreground">Redirecting...</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.layout-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
transition: all 300ms ease;
|
||||
}
|
||||
|
||||
/* Floating nav mode - add top padding for fixed nav */
|
||||
.main-content.floating-mode {
|
||||
padding-top: 80px;
|
||||
}
|
||||
|
||||
/* Sidebar mode - add left padding for sidebar nav */
|
||||
.main-content.sidebar-mode {
|
||||
padding-left: 180px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
import { RoomList, RoomHeader, Timeline, MessageInput } from '$lib/components/chat';
|
||||
import CreateRoomDialog from '$lib/components/chat/CreateRoomDialog.svelte';
|
||||
import RoomSettingsPanel from '$lib/components/chat/RoomSettingsPanel.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Gear, SignOut, ChatCircle, Plus } from '@manacore/shared-icons';
|
||||
import { ChatCircle, Plus } from '@manacore/shared-icons';
|
||||
|
||||
let sidebarOpen = $state(true);
|
||||
let showCreateRoom = $state(false);
|
||||
|
|
@ -18,11 +17,6 @@
|
|||
sidebarOpen = !sidebarOpen;
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
matrixStore.logout();
|
||||
goto('/login');
|
||||
}
|
||||
|
||||
function handleReply(message: SimpleMessage) {
|
||||
editMessage = null;
|
||||
replyTo = message;
|
||||
|
|
@ -38,65 +32,25 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="flex h-screen overflow-hidden bg-background">
|
||||
<div class="flex h-full overflow-hidden bg-background">
|
||||
<!-- Sidebar -->
|
||||
<aside
|
||||
class="flex w-80 flex-shrink-0 flex-col border-r border-black/10 dark:border-white/10 bg-white/50 dark:bg-white/5 backdrop-blur-sm transition-all duration-300 ease-in-out"
|
||||
class:hidden={!sidebarOpen}
|
||||
class:lg:flex={true}
|
||||
>
|
||||
<!-- Sidebar Header -->
|
||||
<header
|
||||
class="flex items-center justify-between border-b border-black/10 dark:border-white/10 p-4"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="p-1.5 rounded-lg bg-gradient-to-br from-violet-500 to-purple-600">
|
||||
<ChatCircle class="h-5 w-5 text-white" weight="fill" />
|
||||
</div>
|
||||
<h1 class="text-lg font-bold">Mana Matrix</h1>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<!-- User Info / Status Bar -->
|
||||
<div class="border-b border-black/10 dark:border-white/10 px-4 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<p class="truncate text-sm font-medium">{matrixStore.userId}</p>
|
||||
<button
|
||||
class="p-2 rounded-lg hover:bg-black/5 dark:hover:bg-white/10 transition-colors"
|
||||
class="p-1.5 rounded-lg hover:bg-black/5 dark:hover:bg-white/10 transition-colors"
|
||||
title="Neuer Chat"
|
||||
onclick={() => (showCreateRoom = true)}
|
||||
>
|
||||
<Plus class="h-5 w-5" />
|
||||
<Plus class="h-4 w-4" />
|
||||
</button>
|
||||
<div class="dropdown dropdown-end">
|
||||
<button
|
||||
tabindex="0"
|
||||
class="p-2 rounded-lg hover:bg-black/5 dark:hover:bg-white/10 transition-colors"
|
||||
>
|
||||
<Gear class="h-5 w-5" />
|
||||
</button>
|
||||
<ul tabindex="0" class="dropdown-content z-50 w-52 rounded-xl glass p-2 shadow-xl mt-2">
|
||||
<li>
|
||||
<a
|
||||
href="/settings"
|
||||
class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-black/5 dark:hover:bg-white/10 transition-colors"
|
||||
>
|
||||
<Gear class="h-4 w-4" />
|
||||
Einstellungen
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
onclick={handleLogout}
|
||||
class="flex items-center gap-2 w-full px-3 py-2 rounded-lg hover:bg-red-500/10 text-red-500 transition-colors"
|
||||
>
|
||||
<SignOut class="h-4 w-4" />
|
||||
Abmelden
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- User Info -->
|
||||
<div class="border-b border-black/10 dark:border-white/10 px-4 py-3">
|
||||
<p class="truncate text-sm font-medium">{matrixStore.userId}</p>
|
||||
<p class="flex items-center gap-1.5 text-xs text-muted-foreground mt-0.5">
|
||||
<span class="h-2 w-2 rounded-full bg-green-500"></span>
|
||||
{matrixStore.syncState === 'SYNCING' ? 'Verbunden' : matrixStore.syncState}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { onMount } from 'svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import { ToastContainer } from '@manacore/shared-ui';
|
||||
|
||||
interface Props {
|
||||
children: Snippet;
|
||||
}
|
||||
|
||||
let { children }: Props = $props();
|
||||
|
||||
onMount(() => {
|
||||
const cleanup = theme.initialize();
|
||||
return cleanup;
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
@ -14,4 +22,9 @@
|
|||
<meta name="description" content="Self-hosted Matrix chat client" />
|
||||
</svelte:head>
|
||||
|
||||
{@render children()}
|
||||
<div class="min-h-screen bg-background text-foreground">
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
<!-- Global Toast notifications -->
|
||||
<ToastContainer />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue