477c0e4d52
- Add svelte-i18n with 224 translation keys (English + Russian) - Language switcher in header (EN/RU toggle, persists to localStorage) - Extract all hardcoded strings from 37 component/page files - Add 4 new widget types: Bookmark, Note (markdown), Embed (iframe), Status - WidgetRenderer dispatches by type, WidgetGrid supports full-width widgets - Type-specific config forms in board editor - Install marked for markdown rendering
69 lines
1.6 KiB
Svelte
69 lines
1.6 KiB
Svelte
<script lang="ts">
|
|
import { t } from 'svelte-i18n';
|
|
import type { Snippet } from 'svelte';
|
|
import Sidebar from './Sidebar.svelte';
|
|
import Header from './Header.svelte';
|
|
import AmbientBackground from '$lib/components/background/AmbientBackground.svelte';
|
|
import SearchDialog from '$lib/components/search/SearchDialog.svelte';
|
|
import { ui } from '$lib/stores/ui.svelte.js';
|
|
|
|
interface BoardLink {
|
|
id: string;
|
|
name: string;
|
|
icon: string | null;
|
|
}
|
|
|
|
interface UserInfo {
|
|
displayName: string;
|
|
email: string;
|
|
role: string;
|
|
avatarUrl?: string | null;
|
|
}
|
|
|
|
interface Props {
|
|
user: UserInfo | null;
|
|
boards: BoardLink[];
|
|
children: Snippet;
|
|
}
|
|
|
|
let { user, boards, children }: Props = $props();
|
|
|
|
const isAdmin = $derived(user?.role === 'admin');
|
|
</script>
|
|
|
|
<!-- Ambient Background (fixed, behind everything) -->
|
|
<AmbientBackground />
|
|
|
|
<div class="relative z-10 flex h-screen overflow-hidden">
|
|
<!-- Mobile overlay -->
|
|
{#if ui.isMobile && !ui.sidebarHidden}
|
|
<button
|
|
type="button"
|
|
class="fixed inset-0 z-30 bg-black/50"
|
|
onclick={() => ui.closeMobileSidebar()}
|
|
aria-label={$t('sidebar.close')}
|
|
></button>
|
|
{/if}
|
|
|
|
<!-- Sidebar -->
|
|
{#if !ui.sidebarHidden || !ui.isMobile}
|
|
<div
|
|
class="shrink-0 {ui.isMobile ? 'fixed left-0 top-0 z-40 h-full' : 'relative'}"
|
|
>
|
|
<Sidebar {boards} {isAdmin} collapsed={ui.isMobile ? false : ui.sidebarCollapsed} />
|
|
</div>
|
|
{/if}
|
|
|
|
<!-- Main content area -->
|
|
<div class="flex min-w-0 flex-1 flex-col overflow-hidden">
|
|
<Header {user} />
|
|
|
|
<main class="flex-1 overflow-y-auto">
|
|
{@render children()}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Dialog (modal, z-50) -->
|
|
<SearchDialog />
|