refactor: site metadata

This commit is contained in:
Jason Rasmussen
2026-05-06 16:54:30 -04:00
parent 4369fdffbb
commit 5c3898ec41
30 changed files with 147 additions and 105 deletions
+1
View File
@@ -4,6 +4,7 @@ pnpm-lock.yaml
yarn.lock yarn.lock
src/lib/assets src/lib/assets
dist/ dist/
deployment/
CHANGELOG.md CHANGELOG.md
.svelte-kit/ .svelte-kit/
.release-please-manifest.json .release-please-manifest.json
@@ -13,6 +13,7 @@
ControlBarHeader, ControlBarHeader,
ControlBarOverflow, ControlBarOverflow,
getSiteProviders, getSiteProviders,
globalManager,
Logo, Logo,
ThemeSwitcher, ThemeSwitcher,
TooltipProvider, TooltipProvider,
@@ -26,6 +27,7 @@
const { children }: Props = $props(); const { children }: Props = $props();
globalManager.setSiteMetadata(siteMetadata);
commandPaletteManager.enable(); commandPaletteManager.enable();
</script> </script>
@@ -27,7 +27,7 @@
let filteredCategories = $derived(filterProjects(query)); let filteredCategories = $derived(filterProjects(query));
</script> </script>
<SiteMetadata site={siteMetadata} /> <SiteMetadata />
<Heading size="giant">{siteMetadata.title}</Heading> <Heading size="giant">{siteMetadata.title}</Heading>
<Text size="large" color="muted">{siteMetadata.description}</Text> <Text size="large" color="muted">{siteMetadata.description}</Text>
@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { siteMetadata } from '$lib';
import { Card, CardBody, Container, Heading, SiteMetadata, Text } from '@immich/ui'; import { Card, CardBody, Container, Heading, SiteMetadata, Text } from '@immich/ui';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
@@ -22,7 +21,7 @@
}); });
</script> </script>
<SiteMetadata site={siteMetadata} page={pageMetadata} /> <SiteMetadata page={pageMetadata} />
<Container size="medium" center> <Container size="medium" center>
<section class="flex flex-col gap-2"> <section class="flex flex-col gap-2">
-7
View File
@@ -4,13 +4,6 @@ import { defaultProvider, type ActionItem } from '@immich/ui';
import fm from 'front-matter'; import fm from 'front-matter';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
export const siteMetadata = {
title: 'Immich',
description:
'Self-hosted photo and video management solution. Easily back up, organize, and manage your photos on your own server. Immich helps you browse, search and organize your photos and videos with ease, without sacrificing your privacy.',
imageUrl: '/img/social-preview.png',
};
export const blogMetadata = { export const blogMetadata = {
title: 'Immich Blog', title: 'Immich Blog',
description: 'Latest updates, announcements, and stories from the Immich team.', description: 'Latest updates, announcements, and stories from the Immich team.',
@@ -15,6 +15,7 @@
CommandPaletteProvider, CommandPaletteProvider,
Constants, Constants,
getSiteProviders, getSiteProviders,
globalManager,
IconButton, IconButton,
Link, Link,
Logo, Logo,
@@ -77,6 +78,12 @@
return active ? 'primary' : 'secondary'; return active ? 'primary' : 'secondary';
}; };
globalManager.setSiteMetadata({
title: 'Immich',
description:
'Self-hosted photo and video management solution. Easily back up, organize, and manage your photos on your own server. Immich helps you browse, search and organize your photos and videos with ease, without sacrificing your privacy.',
imageUrl: '/img/social-preview.png',
});
commandPaletteManager.enable(); commandPaletteManager.enable();
</script> </script>
+1 -3
View File
@@ -4,7 +4,6 @@
import qrCodeLight from '$common/img/app-qr-code-light.svg'; import qrCodeLight from '$common/img/app-qr-code-light.svg';
import screenshotDark from '$common/img/screenshot-dark.webp'; import screenshotDark from '$common/img/screenshot-dark.webp';
import screenshotLight from '$common/img/screenshot-light.webp'; import screenshotLight from '$common/img/screenshot-light.webp';
import { siteMetadata } from '$lib';
import { import {
appStoreBadge, appStoreBadge,
Button, Button,
@@ -30,8 +29,7 @@
]; ];
</script> </script>
<!-- only SSR SiteMetadata --> <SiteMetadata />
<SiteMetadata site={siteMetadata} />
{#if browser} {#if browser}
<VStack gap={8} class="mt-4 text-center lg:mt-16"> <VStack gap={8} class="mt-4 text-center lg:mt-16">
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { TimelineItem } from '$lib'; import type { TimelineItem } from '$lib';
import { posts, siteMetadata } from '$lib'; import { posts } from '$lib';
import Timeline from '$lib/components/Timeline.svelte'; import Timeline from '$lib/components/Timeline.svelte';
import { asGithubLink, Heading, SiteMetadata, Stack, Text } from '@immich/ui'; import { asGithubLink, Heading, SiteMetadata, Stack, Text } from '@immich/ui';
import { import {
@@ -234,7 +234,7 @@
}; };
</script> </script>
<SiteMetadata site={siteMetadata} page={pageMetadata} /> <SiteMetadata page={pageMetadata} />
<Stack class="text-center" gap={4}> <Stack class="text-center" gap={4}>
<Heading size="title" tag="h1">{pageMetadata.title}</Heading> <Heading size="title" tag="h1">{pageMetadata.title}</Heading>
@@ -2,7 +2,6 @@
import portainerIcon from '$common/img/portainer.svg'; import portainerIcon from '$common/img/portainer.svg';
import truenasIcon from '$common/img/truenas-scale.svg'; import truenasIcon from '$common/img/truenas-scale.svg';
import unraidIcon from '$common/img/unraid.svg'; import unraidIcon from '$common/img/unraid.svg';
import { siteMetadata } from '$lib';
import { import {
appStoreBadge, appStoreBadge,
Button, Button,
@@ -30,7 +29,7 @@
}; };
</script> </script>
<SiteMetadata site={siteMetadata} page={pageMetadata} /> <SiteMetadata page={pageMetadata} />
<Heading size="title" class="mb-4" tag="h1">{pageMetadata.title}</Heading> <Heading size="title" class="mb-4" tag="h1">{pageMetadata.title}</Heading>
@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import { siteMetadata } from '$lib';
import { import {
Card, Card,
CardBody, CardBody,
@@ -256,7 +255,7 @@
}; };
</script> </script>
<SiteMetadata site={siteMetadata} page={pageMetadata} /> <SiteMetadata page={pageMetadata} />
<Stack gap={8}> <Stack gap={8}>
<Stack> <Stack>
@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { TimelineItem } from '$lib'; import type { TimelineItem } from '$lib';
import { siteMetadata } from '$lib';
import Timeline from '$lib/components/Timeline.svelte'; import Timeline from '$lib/components/Timeline.svelte';
import { Heading, SiteMetadata, Stack, Text } from '@immich/ui'; import { Heading, SiteMetadata, Stack, Text } from '@immich/ui';
import { import {
@@ -947,7 +946,7 @@
}; };
</script> </script>
<SiteMetadata site={siteMetadata} page={pageMetadata} /> <SiteMetadata page={pageMetadata} />
<Stack class="text-center" gap={4}> <Stack class="text-center" gap={4}>
<Heading size="title" tag="h1">Roadmap</Heading> <Heading size="title" tag="h1">Roadmap</Heading>
@@ -1,16 +1,16 @@
<script lang="ts"> <script lang="ts">
import MarkdownPage from '$lib/components/MarkdownPage.svelte'; import { Markdown, type ContainerSize } from '@immich/ui';
import { type ContainerSize } from '@immich/ui';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
type Props = { type Props = {
name: string; name: string;
description: string; description: string;
descriptionBody?: Snippet;
size?: ContainerSize; size?: ContainerSize;
children?: Snippet; children?: Snippet;
}; };
const { size, name, description, children }: Props = $props(); const { size, name, description, descriptionBody, children }: Props = $props();
</script> </script>
<MarkdownPage attributes={{ title: name, description }} {size} {children} /> <Markdown.Page attributes={{ title: name, description }} description={descriptionBody} {size} {children} />
@@ -1,31 +0,0 @@
<script lang="ts">
import PageDescription from '$lib/components/PageDescription.svelte';
import { siteMetadata } from '$lib/constants.js';
import { Container, Heading, SiteMetadata, type BreadcrumbItem, type ContainerSize } from '@immich/ui';
import type { Snippet } from 'svelte';
type Props = {
attributes: { title: string; description: string };
size?: ContainerSize;
breadcrumbs?: BreadcrumbItem[];
children?: Snippet;
};
const { attributes, size = 'medium', children }: Props = $props();
const { title, description } = $derived(attributes);
const page = $derived({ title, description });
</script>
<SiteMetadata site={siteMetadata} {page} />
<Container {size} center class="my-4 flex flex-col p-4 lg:my-8">
<section class="mb-8">
<Heading tag="h1" size="giant">{page.title}</Heading>
{#if page.description}
<PageDescription>{page.description}</PageDescription>
{/if}
</section>
{@render children?.()}
</Container>
@@ -1,12 +0,0 @@
<script lang="ts">
import { Text } from '@immich/ui';
import type { Snippet } from 'svelte';
type Props = {
children: Snippet;
};
const { children }: Props = $props();
</script>
<Text color="muted" size="large" class="mt-2" {children} />
-5
View File
@@ -106,11 +106,6 @@ export type ExampleItem = {
export type ExampleCardProps = ExampleItem; export type ExampleCardProps = ExampleItem;
export const siteMetadata = {
title: 'Immich UI',
description: 'A Svelte component library for Immich',
};
export type ComponentItem = { export type ComponentItem = {
name: string; name: string;
title?: string; title?: string;
@@ -1,14 +1,14 @@
<script lang="ts"> <script lang="ts">
import ComponentCard from '$lib/components/ComponentCard.svelte'; import ComponentCard from '$lib/components/ComponentCard.svelte';
import Grid from '$lib/components/Grid.svelte'; import Grid from '$lib/components/Grid.svelte';
import MarkdownPage from '$lib/components/MarkdownPage.svelte';
import { components } from '$lib/constants.js'; import { components } from '$lib/constants.js';
import { Markdown } from '@immich/ui';
</script> </script>
<MarkdownPage attributes={{ title: 'Components', description: 'A list of all the Immich UI components' }} size="large"> <Markdown.Page attributes={{ title: 'Components', description: 'A list of all the Immich UI components' }} size="large">
<Grid> <Grid>
{#each components as component (component.name)} {#each components as component (component.name)}
<ComponentCard {component} /> <ComponentCard {component} />
{/each} {/each}
</Grid> </Grid>
</MarkdownPage> </Markdown.Page>
@@ -2,7 +2,6 @@
import ComponentExamples from '$lib/components/ComponentExamples.svelte'; import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentLink from '$lib/components/ComponentLink.svelte'; import ComponentLink from '$lib/components/ComponentLink.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte'; import ComponentPage from '$lib/components/ComponentPage.svelte';
import PageDescription from '$lib/components/PageDescription.svelte';
import BasicExample from './BasicExample.svelte'; import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw'; import basicExample from './BasicExample.svelte?raw';
import ColorExample from './ColorExample.svelte'; import ColorExample from './ColorExample.svelte';
@@ -22,7 +21,9 @@
</script> </script>
<ComponentPage name="IconButton" description="An icon-only version of the Button component"> <ComponentPage name="IconButton" description="An icon-only version of the Button component">
<PageDescription>An icon-only version of the <ComponentLink name="Button" /> component</PageDescription> {#snippet descriptionBody()}
An icon-only version of the <ComponentLink name="Button" /> component
{/snippet}
<ComponentExamples <ComponentExamples
examples={[ examples={[
{ title: 'Basic', code: basicExample, component: BasicExample }, { title: 'Basic', code: basicExample, component: BasicExample },
@@ -1,9 +1,8 @@
<script lang="ts"> <script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte'; import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte'; import ComponentPage from '$lib/components/ComponentPage.svelte';
import PageDescription from '$lib/components/PageDescription.svelte';
import { asComponentHref } from '$lib/utilities.js'; import { asComponentHref } from '$lib/utilities.js';
import { Link } from '@immich/ui'; import { Link, Text } from '@immich/ui';
import BasicExample from './BasicExample.svelte'; import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw'; import basicExample from './BasicExample.svelte?raw';
import ExpandExample from './ExpandExample.svelte'; import ExpandExample from './ExpandExample.svelte';
@@ -15,10 +14,10 @@
</script> </script>
<ComponentPage name="Navbar" description="A component for creating navigation items in a sidebar"> <ComponentPage name="Navbar" description="A component for creating navigation items in a sidebar">
<PageDescription <Text>
>This component exports a NavbarItem for creating navigation menus. This is usually used in conjunction with the <Link This component exports a NavbarItem for creating navigation menus. This is usually used in conjunction with the <Link
href={asComponentHref('AppShell')}>AppSidebar</Link href={asComponentHref('AppShell')}>AppSidebar</Link
> component</PageDescription > component</Text
> >
<ComponentExamples <ComponentExamples
examples={[ examples={[
@@ -1,15 +1,13 @@
<script lang="ts"> <script lang="ts">
import ComponentExamples from '$lib/components/ComponentExamples.svelte'; import ComponentExamples from '$lib/components/ComponentExamples.svelte';
import ComponentPage from '$lib/components/ComponentPage.svelte'; import ComponentPage from '$lib/components/ComponentPage.svelte';
import PageDescription from '$lib/components/PageDescription.svelte';
import BasicExample from './BasicExample.svelte'; import BasicExample from './BasicExample.svelte';
import basicExample from './BasicExample.svelte?raw'; import basicExample from './BasicExample.svelte?raw';
</script> </script>
<ComponentPage name="Tooltip" description="A component for displaying tooltips on hover or focus"> <ComponentPage
<PageDescription> name="Tooltip"
Displays supplementary information when users hover over or interact with an element. description="Display supplementary information when users hover over or interact with an element"
</PageDescription> >
<ComponentExamples examples={[{ title: 'Basic', code: basicExample, component: BasicExample }]} /> <ComponentExamples examples={[{ title: 'Basic', code: basicExample, component: BasicExample }]} />
</ComponentPage> </ComponentPage>
+6 -1
View File
@@ -1,20 +1,25 @@
<script lang="ts"> <script lang="ts">
import '$lib/app.css';
import { getDocsProviders } from '$lib/constants.js'; import { getDocsProviders } from '$lib/constants.js';
import { import {
commandPaletteManager, commandPaletteManager,
CommandPaletteProvider, CommandPaletteProvider,
getSiteProviders, getSiteProviders,
globalManager,
ScreencastOverlay, ScreencastOverlay,
toastManager, toastManager,
TooltipProvider, TooltipProvider,
} from '@immich/ui'; } from '@immich/ui';
import '@immich/ui/theme/default.css'; import '@immich/ui/theme/default.css';
import '$lib/app.css';
let { children } = $props(); let { children } = $props();
toastManager.setOptions({ class: 'top-[58px]' }); toastManager.setOptions({ class: 'top-[58px]' });
globalManager.setSiteMetadata({
title: 'Immich UI',
description: 'A Svelte component library for Immich',
});
commandPaletteManager.enable(); commandPaletteManager.enable();
commandPaletteManager.setTranslations({ commandPaletteManager.setTranslations({
command_palette_prompt_default: 'Quickly find components, links, and commands', command_palette_prompt_default: 'Quickly find components, links, and commands',
+1 -2
View File
@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import imageUrl from '$lib/assets/design_placeholder.svg'; import imageUrl from '$lib/assets/design_placeholder.svg';
import { siteMetadata } from '$lib/constants.js';
import { import {
AppShell, AppShell,
AppShellHeader, AppShellHeader,
@@ -23,7 +22,7 @@
import { mdiCodeBraces, mdiOpenInNew } from '@mdi/js'; import { mdiCodeBraces, mdiOpenInNew } from '@mdi/js';
</script> </script>
<SiteMetadata site={siteMetadata} /> <SiteMetadata />
<AppShell> <AppShell>
<AppShellHeader class="w-full"> <AppShellHeader class="w-full">
+1 -1
View File
@@ -11,7 +11,7 @@ const config = {
preprocess: [ preprocess: [
svelteMarkdownPreprocess({ svelteMarkdownPreprocess({
layouts: { layouts: {
default: '$lib/components/MarkdownPage.svelte', default: { name: 'Markdown.Page', source: '@immich/ui', import: false },
}, },
}), }),
vitePreprocess(), vitePreprocess(),
+2 -1
View File
@@ -72,10 +72,11 @@ export default defineConfig([
'apps/**/backend/dist', 'apps/**/backend/dist',
'apps/**/.wrangler', 'apps/**/.wrangler',
'apps/**/backend/worker-configuration.d.ts', 'apps/**/backend/worker-configuration.d.ts',
'common/',
'deployment/**',
'packages/**/build/', 'packages/**/build/',
'packages/**/.svelte-kit/', 'packages/**/.svelte-kit/',
'packages/**/dist/', 'packages/**/dist/',
'common/',
], ],
}, },
]); ]);
@@ -9,7 +9,7 @@ export type FileWithContent = { filename: string; content: string };
export type FileWithFrontMatter = { filename: string; attributes: FrontMatterAttributes; body: string }; export type FileWithFrontMatter = { filename: string; attributes: FrontMatterAttributes; body: string };
export type FileWithScriptBody = FileWithFrontMatter & { scriptBody: string }; export type FileWithScriptBody = FileWithFrontMatter & { scriptBody: string };
export type FileWithMarkup = FileWithScriptBody & { markup: string }; export type FileWithMarkup = FileWithScriptBody & { markup: string };
export type FileWithLayout = FileWithMarkup & { layout?: string }; export type FileWithLayout = FileWithMarkup & { layout?: SvelteMarkdownImport };
export type FileWithSvelte = FileWithLayout & { svelte: string }; export type FileWithSvelte = FileWithLayout & { svelte: string };
export type FrontMatterAttributes = { export type FrontMatterAttributes = {
@@ -17,10 +17,14 @@ export type FrontMatterAttributes = {
[key: string]: unknown; [key: string]: unknown;
}; };
export type SvelteMarkdownLayoutHandler = (file: FileWithMarkup) => SvelteMarkdownImportLike;
export type SvelteMarkdownImportLike = SvelteMarkdownImport | string;
export type SvelteMarkdownImport = { name: string; import?: string | false; source: string };
export type SvelteMarkdownPreprocessLayouts = { export type SvelteMarkdownPreprocessLayouts = {
_?: string; _?: SvelteMarkdownImportLike | SvelteMarkdownLayoutHandler;
default?: string; default?: SvelteMarkdownImportLike | SvelteMarkdownLayoutHandler;
[key: string]: string | undefined; [key: string]: SvelteMarkdownImportLike | SvelteMarkdownLayoutHandler | undefined;
}; };
const SCRIPT_BODY_REGEX = /<script.*>(?<body>(.|\n)*?)<\/script>/; const SCRIPT_BODY_REGEX = /<script.*>(?<body>(.|\n)*?)<\/script>/;
@@ -97,7 +101,21 @@ export class SvelteMarkdownPreprocess {
parseLayout(file: FileWithMarkup): MaybePromise<FileWithLayout> { parseLayout(file: FileWithMarkup): MaybePromise<FileWithLayout> {
const layoutKey = file.attributes.layout; const layoutKey = file.attributes.layout;
const layout = layoutKey ? this.#layouts[layoutKey] : (this.#layouts.default ?? this.#layouts._);
let layout = layoutKey ? this.#layouts[layoutKey] : (this.#layouts.default ?? this.#layouts._);
if (layoutKey && !layout) {
throw new Error(`File references unknown layout key: ${file.filename} => ${layoutKey} => ?`);
}
if (typeof layout === 'function') {
layout = layout(file);
}
if (typeof layout === 'string') {
layout = { name: 'Layout', source: layout };
}
return { ...file, layout }; return { ...file, layout };
} }
@@ -108,10 +126,12 @@ export class SvelteMarkdownPreprocess {
return { ...file, svelte }; return { ...file, svelte };
} }
scriptImports(file: FileWithLayout): Array<string | undefined> { scriptImports({ layout }: FileWithLayout): Array<string | undefined> {
return [ return [
` import { Markdown } from '@immich/ui';`, ` import { Markdown } from '@immich/ui';`,
file.layout ? ` import Layout from '${file.layout}';` : undefined, layout && layout.import !== false
? ` import ${layout.import ?? layout.name} from '${layout.source}';`
: undefined,
]; ];
} }
@@ -132,7 +152,9 @@ export class SvelteMarkdownPreprocess {
} }
createSvelteTemplate(file: FileWithLayout): string { createSvelteTemplate(file: FileWithLayout): string {
return (file.layout ? [`<Layout {attributes}>`, file.markup, '</Layout>'] : [file.markup]).join('\n'); return (
file.layout ? [`<${file.layout.name} {attributes}>`, file.markup, `</${file.layout.name}>`] : [file.markup]
).join('\n');
} }
} }
@@ -0,0 +1,45 @@
<script lang="ts">
import Container from '$lib/components/Container/Container.svelte';
import Heading from '$lib/components/Heading/Heading.svelte';
import SiteMetadata from '$lib/components/SiteMetadata/SiteMetadata.svelte';
import Text from '$lib/components/Text/Text.svelte';
import { type BreadcrumbItem, type ContainerSize } from '$lib/types.js';
import type { Metadata } from '$lib/utilities/common.js';
import type { Snippet } from 'svelte';
type Props = {
attributes: Metadata;
size?: ContainerSize;
breadcrumbs?: BreadcrumbItem[];
title?: Snippet;
description?: Snippet;
children?: Snippet;
};
const { attributes: page, title, description, size = 'medium', children }: Props = $props();
</script>
<SiteMetadata {page} />
<Container {size} center class="my-4 flex flex-col p-4 lg:my-8">
<section class="mb-8">
<Heading tag="h1" size="giant">
{#if title}
{@render title()}
{:else}
{page.title}
{/if}
</Heading>
{#if page.description || description}
<Text color="muted" size="large" fontWeight="semi-bold" class="mt-2">
{#if typeof description === 'function'}
{@render description()}
{:else}
{page.description}
{/if}
</Text>
{/if}
</section>
{@render children?.()}
</Container>
@@ -7,6 +7,7 @@ import LineBreak from './LineBreak.svelte';
import Link from './Link.svelte'; import Link from './Link.svelte';
import List from './List.svelte'; import List from './List.svelte';
import ListItem from './ListItem.svelte'; import ListItem from './ListItem.svelte';
import Page from './Page.svelte';
import Paragraph from './Paragraph.svelte'; import Paragraph from './Paragraph.svelte';
import Space from './Space.svelte'; import Space from './Space.svelte';
import Table from './Table.svelte'; import Table from './Table.svelte';
@@ -27,6 +28,7 @@ export const Markdown = {
Image, Image,
ListItem, ListItem,
List, List,
Page,
Paragraph, Paragraph,
Table, Table,
TableHeader, TableHeader,
@@ -63,7 +63,7 @@
<button <button
type="button" type="button"
aria-label={expanded ? t('collapse') : t('expand')} aria-label={expanded ? t('collapse') : t('expand')}
class="hover:bg-subtle hover:text-primary absolute me-2 hidden h-full rounded-lg px-0.5 md:block" class="hover:bg-subtle hover:text-primary absolute me-2 h-full rounded-lg px-0.5 md:block"
onclick={() => (expanded = !expanded)} onclick={() => (expanded = !expanded)}
> >
<Icon <Icon
@@ -1,15 +1,16 @@
<script lang="ts"> <script lang="ts">
import { globalManager } from '$lib/services/global-manager.svelte.js';
import { resolveMetadata, resolveUrl, type ArticleMetadata, type Metadata } from '$lib/utilities/common.js'; import { resolveMetadata, resolveUrl, type ArticleMetadata, type Metadata } from '$lib/utilities/common.js';
type Props = { type Props = {
site: Metadata; site?: Metadata;
page?: Metadata; page?: Metadata;
article?: ArticleMetadata; article?: ArticleMetadata;
}; };
const { site, page, article }: Props = $props(); const { site, page, article }: Props = $props();
let resolved = $derived(resolveMetadata(site, page, article)); let resolved = $derived(resolveMetadata(site ?? globalManager.siteMetadata, page, article));
</script> </script>
<svelte:head> <svelte:head>
+1
View File
@@ -109,6 +109,7 @@ export { default as TooltipProvider } from '$lib/components/Tooltip/TooltipProvi
export * from '$lib/actions/shortcut.js'; export * from '$lib/actions/shortcut.js';
export * from '$lib/commands.js'; export * from '$lib/commands.js';
export * from '$lib/services/command-palette-manager.svelte.js'; export * from '$lib/services/command-palette-manager.svelte.js';
export * from '$lib/services/global-manager.svelte.js';
export * from '$lib/services/menu-manager.svelte.js'; export * from '$lib/services/menu-manager.svelte.js';
export * from '$lib/services/modal-manager.svelte.js'; export * from '$lib/services/modal-manager.svelte.js';
export * from '$lib/services/screencast-manager.svelte.js'; export * from '$lib/services/screencast-manager.svelte.js';
@@ -0,0 +1,19 @@
import { type Metadata } from '$lib/utilities/common.js';
class GlobalManager {
#siteMetadata = $state<Metadata>();
get siteMetadata() {
if (!this.#siteMetadata) {
throw new Error('No site metadata!');
}
return this.#siteMetadata;
}
setSiteMetadata(site: Metadata) {
this.#siteMetadata = site;
}
}
export const globalManager = new GlobalManager();