mirror of
https://github.com/immich-app/static-pages.git
synced 2026-09-30 21:27:54 +08:00
feat: features section (#681)
* feat: new homepage feature section * update screenshots * feat: new feature card * facial recognition screenshot --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
@@ -133,5 +133,5 @@
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Container>
|
||||
<SiteFooter />
|
||||
<SiteFooter class="mt-16" />
|
||||
</AppShell>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 320 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 185 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 516 KiB |
@@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
import type { Feature } from '$lib/types';
|
||||
import { Button, Heading, Icon, Link, Text } from '@immich/ui';
|
||||
import { tv } from 'tailwind-variants';
|
||||
|
||||
type Props = {
|
||||
feature: Feature;
|
||||
color?: 'primary' | 'secondary';
|
||||
};
|
||||
|
||||
const { feature, color = 'secondary' }: Props = $props();
|
||||
const { title, description, icons, links } = $derived(feature);
|
||||
|
||||
const styles = tv({
|
||||
base: 'flex flex-col justify-between rounded-2xl border-2 p-6 transition-all',
|
||||
variants: {
|
||||
color: {
|
||||
primary: 'border border-primary-300 bg-primary-50',
|
||||
secondary: 'bg-light-50',
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class={styles({ color })}>
|
||||
<div>
|
||||
<div class="mb-6 flex gap-2">
|
||||
{#each icons as icon, i (i)}
|
||||
{#if icon}
|
||||
{#if typeof icon === 'object' && 'href' in icon && icon.href}
|
||||
<Link href={icon.href}>
|
||||
<Icon {icon} size="2em" />
|
||||
</Link>
|
||||
{:else}
|
||||
<Icon {icon} size="2em" />
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<Heading size="medium">{title}</Heading>
|
||||
<Text color="muted" class="mt-2">{description}</Text>
|
||||
</div>
|
||||
{#if links && links.length > 0}
|
||||
<div class="mt-4">
|
||||
{#each links as link, i (i)}
|
||||
<Button size="small" {color} href={link.href}>{link.text}</Button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { cleanClass } from '$common';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import { Container, type ContainerSize } from '@immich/ui';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type Props = {
|
||||
children?: Snippet;
|
||||
color?: 'primary' | 'secondary';
|
||||
class?: string;
|
||||
size?: ContainerSize;
|
||||
};
|
||||
|
||||
const { size = 'large', color = 'secondary', class: className, children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<PageContent class={cleanClass(color === 'secondary' ? 'bg-light' : 'bg-primary-50')}>
|
||||
<Container {size} center>
|
||||
<div class={cleanClass('my-8 lg:h-112 lg:my-0', className)}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</Container>
|
||||
</PageContent>
|
||||
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { cleanClass } from '$common';
|
||||
import FeatureContainer from '$lib/components/FeatureContainer.svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type Props = {
|
||||
label: Snippet;
|
||||
media: Snippet;
|
||||
reverse?: boolean;
|
||||
};
|
||||
|
||||
const { reverse = false, label, media }: Props = $props();
|
||||
</script>
|
||||
|
||||
<FeatureContainer color={reverse ? 'secondary' : 'primary'} class="flex flex-col gap-12 lg:grid lg:grid-cols-2">
|
||||
<div class={cleanClass(reverse ? 'lg:order-2' : '', 'col-span-1 my-auto')}>
|
||||
{@render label?.()}
|
||||
</div>
|
||||
<div class="flex items-center justify-around overflow-hidden align-middle">
|
||||
{@render media?.()}
|
||||
</div>
|
||||
</FeatureContainer>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
import { cleanClass } from '$common';
|
||||
|
||||
type Props = {
|
||||
children?: Snippet;
|
||||
class?: string;
|
||||
};
|
||||
|
||||
const { children, class: className }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class={cleanClass('grow', className)}>
|
||||
<div class="size-full w-full p-4 md:p-6 lg:p-8">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { siteMetadata } from '$lib';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import { Card, CardBody, Container, Heading, SiteMetadata, Text } from '@immich/ui';
|
||||
import { DateTime } from 'luxon';
|
||||
import type { Snippet } from 'svelte';
|
||||
@@ -24,16 +25,18 @@
|
||||
|
||||
<SiteMetadata site={siteMetadata} page={pageMetadata} />
|
||||
|
||||
<Container size="medium" center>
|
||||
<section class="flex flex-col gap-2">
|
||||
<Heading tag="h1" size="title">{pageMetadata.title}</Heading>
|
||||
<Text color="muted">Last updated: {updatedAt.toLocaleString(DateTime.DATE_FULL)}</Text>
|
||||
<Text>{pageMetadata.description}</Text>
|
||||
</section>
|
||||
<PageContent>
|
||||
<Container size="medium" center>
|
||||
<section class="flex flex-col gap-2">
|
||||
<Heading tag="h1" size="title">{pageMetadata.title}</Heading>
|
||||
<Text color="muted">Last updated: {updatedAt.toLocaleString(DateTime.DATE_FULL)}</Text>
|
||||
<Text>{pageMetadata.description}</Text>
|
||||
</section>
|
||||
|
||||
<Card color="secondary" class="mt-8">
|
||||
<CardBody class="pt-0">
|
||||
{@render children?.()}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Container>
|
||||
<Card color="secondary" class="mt-8">
|
||||
<CardBody class="pt-0">
|
||||
{@render children?.()}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Container>
|
||||
</PageContent>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { Button, Heading, Icon, Text, type IconLike } from '@immich/ui';
|
||||
import { mdiOpenInNew } from '@mdi/js';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
children?: Snippet;
|
||||
icon: IconLike;
|
||||
href: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
const { title, icon, href, text, children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="group flex flex-col items-start gap-6 rounded-3xl border border-primary-300 bg-primary-50 p-10 transition-colors hover:border-primary-400 sm:flex-row sm:items-center sm:gap-8"
|
||||
>
|
||||
<Icon
|
||||
{icon}
|
||||
size="4rem"
|
||||
class="shrink-0 rotate-[-25deg] text-primary transition-transform duration-200 ease-out group-hover:scale-105 group-hover:rotate-[-10deg]"
|
||||
/>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-1.5 px-2">
|
||||
<Heading size="medium" tag="h3">{title}</Heading>
|
||||
<Text color="muted">{@render children?.()}</Text>
|
||||
</div>
|
||||
|
||||
<Button {href} size="large" shape="round" trailingIcon={mdiOpenInNew} class="shrink-0">
|
||||
{text}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import { Button, Heading, Icon, type IconLike } from '@immich/ui';
|
||||
import { mdiOpenInNew } from '@mdi/js';
|
||||
import { tv } from 'tailwind-variants';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
icon: IconLike;
|
||||
buttonIcon?: IconLike;
|
||||
href: string;
|
||||
text: string;
|
||||
color?: 'primary' | 'warning' | 'info';
|
||||
};
|
||||
|
||||
const { title, icon, buttonIcon, href, text, color = 'primary' }: Props = $props();
|
||||
|
||||
const iconStyles = tv({
|
||||
base: 'shrink-0',
|
||||
variants: {
|
||||
color: {
|
||||
primary: 'text-primary',
|
||||
warning: 'text-warning',
|
||||
info: 'text-info',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const buttonStyles = tv({
|
||||
base: 'shrink-0',
|
||||
variants: {
|
||||
color: {
|
||||
primary: 'hover:border-primary hover:text-primary',
|
||||
warning: 'hover:border-warning hover:text-warning',
|
||||
info: 'hover:border-info hover:text-info',
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-4 rounded-2xl border bg-light-50 p-6">
|
||||
<Icon {icon} size="1.75rem" class={iconStyles({ color })} />
|
||||
|
||||
<Heading size="small" tag="h3" class="flex-1">{title}</Heading>
|
||||
|
||||
<Button
|
||||
{href}
|
||||
size="small"
|
||||
shape="round"
|
||||
variant="outline"
|
||||
color="secondary"
|
||||
leadingIcon={buttonIcon}
|
||||
trailingIcon={mdiOpenInNew}
|
||||
class={buttonStyles({ color })}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { IconLike } from '@immich/ui';
|
||||
|
||||
export type Feature = {
|
||||
title: string;
|
||||
description: string;
|
||||
icons: Array<IconLike & { href?: string }>;
|
||||
links?: FeatureLink[];
|
||||
};
|
||||
|
||||
export type FeatureLink = {
|
||||
href: string;
|
||||
text: string;
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { afterNavigate, beforeNavigate } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import PageContent from '$common/components/PageContent.svelte';
|
||||
import { posts } from '$lib';
|
||||
import '$lib/app.css';
|
||||
import { getSearchProvider } from '$lib/search';
|
||||
@@ -21,6 +20,7 @@
|
||||
Logo,
|
||||
NavbarItem,
|
||||
ScreencastOverlay,
|
||||
SiteFooter,
|
||||
Text,
|
||||
ThemeSwitcher,
|
||||
TooltipProvider,
|
||||
@@ -160,8 +160,9 @@
|
||||
</div>
|
||||
</AppShellSidebar>
|
||||
|
||||
<PageContent class="mx-auto w-full max-w-(--breakpoint-lg)">
|
||||
<div class="mx-auto flex h-full flex-col">
|
||||
{@render children?.()}
|
||||
</PageContent>
|
||||
<SiteFooter />
|
||||
</div>
|
||||
</AppShell>
|
||||
</TooltipProvider>
|
||||
|
||||
@@ -5,13 +5,21 @@
|
||||
import screenshotDark from '$common/img/screenshot-dark.webp';
|
||||
import screenshotLight from '$common/img/screenshot-light.webp';
|
||||
import { siteMetadata } from '$lib';
|
||||
import featureFacialRecognition from '$lib/assets/img/feature-facial-recognition.webp';
|
||||
import featureMobile from '$lib/assets/img/feature-mobile.webp';
|
||||
import featureSharing from '$lib/assets/img/feature-sharing.webp';
|
||||
import FeatureContainer from '$lib/components/FeatureContainer.svelte';
|
||||
import FeatureHighlight from '$lib/components/FeatureHighlight.svelte';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import SupportCard from '$lib/components/SupportCard.svelte';
|
||||
import SupportLinkCard from '$lib/components/SupportLinkCard.svelte';
|
||||
import {
|
||||
appStoreBadge,
|
||||
Button,
|
||||
Constants,
|
||||
Container,
|
||||
Heading,
|
||||
Icon,
|
||||
Link,
|
||||
Logo,
|
||||
playStoreBadge,
|
||||
SiteMetadata,
|
||||
@@ -19,94 +27,141 @@
|
||||
themeManager,
|
||||
VStack,
|
||||
} from '@immich/ui';
|
||||
import { mdiKeyOutline, mdiOpenInNew, mdiShoppingOutline } from '@mdi/js';
|
||||
import { siDiscord, siGithub, siWeblate } from 'simple-icons';
|
||||
|
||||
const supportItems = [
|
||||
{ href: Constants.Sites.Buy, text: 'Buy Immich', icon: mdiKeyOutline },
|
||||
{ href: Constants.Sites.Store, text: 'Buy Merch', icon: mdiShoppingOutline },
|
||||
{ href: Constants.Socials.Github, text: 'View on GitHub', icon: siGithub.path },
|
||||
{ href: Constants.Socials.Weblate, text: 'Translate on Weblate', icon: siWeblate.path },
|
||||
];
|
||||
import { mdiKeyVariant, mdiOpenInNew, mdiSourcePull, mdiTshirtCrew } from '@mdi/js';
|
||||
import { siDiscord, siGithub } from 'simple-icons';
|
||||
</script>
|
||||
|
||||
<!-- only SSR SiteMetadata -->
|
||||
<SiteMetadata site={siteMetadata} />
|
||||
|
||||
{#if browser}
|
||||
<VStack gap={8} class="mt-4 text-center lg:mt-16">
|
||||
<Logo size="giant" variant="stacked-futo" />
|
||||
<PageContent>
|
||||
<Container size="large" center>
|
||||
<VStack gap={8} class="mt-4 text-center lg:mt-16">
|
||||
<Logo size="giant" variant="stacked-futo" />
|
||||
|
||||
<Heading size="title" tag="h1" fontWeight="extra-bold">
|
||||
Self-hosted <span class="text-primary">photo and<br class="hidden lg:block" /> video management</span> solution
|
||||
</Heading>
|
||||
<Heading size="title" tag="h1" fontWeight="extra-bold">
|
||||
Self-hosted <span class="text-primary">photo and<br class="hidden lg:block" /> video management</span> solution
|
||||
</Heading>
|
||||
|
||||
<Text size="large">
|
||||
Easily back up, organize, and manage your photos on your own server. Immich helps you<br
|
||||
class="hidden lg:block"
|
||||
<Text size="large">
|
||||
Easily back up, organize, and manage your photos on your own server. Immich helps you<br
|
||||
class="hidden lg:block"
|
||||
/>
|
||||
browse, search and organize your photos and videos with ease, without sacrificing your privacy.
|
||||
</Text>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col justify-center gap-4 sm:flex-row">
|
||||
<Button size="large" href="/download">Download</Button>
|
||||
<Button size="large" href={Constants.Sites.Demo} color="secondary">
|
||||
<span>Open Demo</span>
|
||||
<Icon icon={mdiOpenInNew} />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<Button href={Constants.Socials.Discord} size="large" variant="ghost" leadingIcon={siDiscord.path}>
|
||||
Join our Discord
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative -z-10">
|
||||
<img src={themeManager.value === 'dark' ? screenshotDark : screenshotLight} alt="Immich application" />
|
||||
|
||||
<div class="absolute top-[-55%] left-0 -z-10 h-[200%] w-full overflow-visible">
|
||||
<Logo size="giant" class="z-10 mb-2 size-full antialiased opacity-20 blur-3xl" />
|
||||
<div class="bg-immich-bg/90 absolute top-0 left-0 size-full backdrop-blur-xl dark:bg-transparent"></div>
|
||||
</div>
|
||||
</div>
|
||||
</VStack>
|
||||
</Container>
|
||||
</PageContent>
|
||||
|
||||
<FeatureHighlight>
|
||||
{#snippet label()}
|
||||
<Heading size="large" tag="h3" class="mb-2">Mobile app</Heading>
|
||||
<Text size="large">
|
||||
View and manage your photos and videos directly from the mobile app. Immich makes it easy to organize, explore
|
||||
and share your precious memories on both Apple and Android.
|
||||
</Text>
|
||||
|
||||
<div class="my-8 flex h-full justify-around gap-4 sm:justify-start lg:my-4">
|
||||
<div class="flex flex-col items-start justify-around gap-4">
|
||||
<a href={Constants.Get.Android}>
|
||||
<img src={playStoreBadge} alt="Playstore Badge" class="w-48" />
|
||||
</a>
|
||||
<a href={Constants.Get.iOS}>
|
||||
<img src={appStoreBadge} alt="AppStore Badge" class="w-48" />
|
||||
</a>
|
||||
</div>
|
||||
<img src={themeManager.value === 'dark' ? qrCodeDark : qrCodeLight} alt="QRCode" class="h-36 rounded-xl" />
|
||||
</div>
|
||||
{/snippet}
|
||||
{#snippet media()}
|
||||
<img src={featureMobile} alt="Mobile app" class="object-scale-down" />
|
||||
{/snippet}
|
||||
</FeatureHighlight>
|
||||
|
||||
<FeatureHighlight reverse>
|
||||
{#snippet label()}
|
||||
<Heading size="large" tag="h3" class="mb-2">Sharing</Heading>
|
||||
<Text size="large">
|
||||
Share with friends and family using shared albums, partner sharing, and shared links. Everyone has their own
|
||||
account and can collaborate together.
|
||||
</Text>
|
||||
{/snippet}
|
||||
{#snippet media()}
|
||||
<img src={featureSharing} alt="Sharing" class="object-scale-down" />
|
||||
{/snippet}
|
||||
</FeatureHighlight>
|
||||
|
||||
<FeatureHighlight>
|
||||
{#snippet label()}
|
||||
<Heading size="large" tag="h3" class="mb-2">Facial Recognition</Heading>
|
||||
<Text size="large">
|
||||
Immich automatically detects and groups similar faces together, providing another way to search, organize, or
|
||||
browse your photos and videos.
|
||||
</Text>
|
||||
{/snippet}
|
||||
{#snippet media()}
|
||||
<img
|
||||
src={featureFacialRecognition}
|
||||
alt="Face box around a detected person"
|
||||
class="rounded-xl object-scale-down"
|
||||
/>
|
||||
browse, search and organize your photos and videos with ease, without sacrificing your privacy.
|
||||
</Text>
|
||||
{/snippet}
|
||||
</FeatureHighlight>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col justify-center gap-4 sm:flex-row">
|
||||
<Button size="large" href="/download">Download</Button>
|
||||
<Button size="large" href={Constants.Sites.Demo} color="secondary">
|
||||
<span>Open Demo</span>
|
||||
<Icon icon={mdiOpenInNew} />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<Button href={Constants.Socials.Discord} size="large" variant="ghost" leadingIcon={siDiscord.path}>
|
||||
Join our Discord
|
||||
</Button>
|
||||
<FeatureContainer size="large" class="flex items-center">
|
||||
<div>
|
||||
<Heading size="large" tag="h2" class="mb-8 text-center">Support Immich</Heading>
|
||||
<div class="flex w-full flex-col gap-5">
|
||||
<SupportCard title="Product Key" icon={mdiKeyVariant} href={Constants.Sites.Buy} text="Buy a key">
|
||||
Building Immich takes a lot of time and effort. We have full-time engineers working on it to make it as good
|
||||
as we possibly can. Consider buying a product key to support the project.
|
||||
</SupportCard>
|
||||
|
||||
<div class="grid grid-cols-1 gap-5 md:grid-cols-2">
|
||||
<SupportLinkCard
|
||||
title="Merch Store"
|
||||
icon={mdiTshirtCrew}
|
||||
color="warning"
|
||||
href={Constants.Sites.Store}
|
||||
text="View store"
|
||||
/>
|
||||
<SupportLinkCard
|
||||
title="Contribute"
|
||||
icon={mdiSourcePull}
|
||||
color="info"
|
||||
buttonIcon={siGithub}
|
||||
href={Constants.Socials.Github}
|
||||
text="GitHub"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative -z-10">
|
||||
<img src={themeManager.value === 'dark' ? screenshotDark : screenshotLight} alt="Immich application" />
|
||||
|
||||
<div class="absolute top-[-55%] left-0 -z-10 h-[200%] w-full overflow-visible">
|
||||
<Logo size="giant" class="z-10 mb-2 size-full antialiased opacity-20 blur-3xl" />
|
||||
<div class="bg-immich-bg/90 absolute top-0 left-0 size-full backdrop-blur-xl dark:bg-transparent"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="m-2 w-full max-w-(--breakpoint-sm) border-t" />
|
||||
<Heading size="title" tag="h2">Download mobile app</Heading>
|
||||
|
||||
<Text size="large">Download Immich app and start backing up your photos and videos securely to your own server</Text
|
||||
>
|
||||
|
||||
<div class="flex flex-col gap-4 lg:flex-row">
|
||||
<Button href={Constants.Get.Android} variant="ghost" class="p-0">
|
||||
<img src={playStoreBadge} alt="Playstore Badge" class="h-16" />
|
||||
</Button>
|
||||
<Button href={Constants.Get.iOS} variant="ghost" class="p-0">
|
||||
<img src={appStoreBadge} alt="AppStore Badge" class="h-16" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<img src={themeManager.value === 'dark' ? qrCodeDark : qrCodeLight} alt="QRCode" class="h-36 rounded-xl" />
|
||||
|
||||
<hr class="m-8 w-full max-w-(--breakpoint-sm) border-t" />
|
||||
<Heading size="title" tag="h2">Support the project</Heading>
|
||||
|
||||
<Text size="large">
|
||||
Support Immich by purchasing a <Link href={Constants.Sites.Buy}>product key</Link>,
|
||||
<Link href={Constants.Sites.Store}>merch</Link>, or contributing on
|
||||
<Link href={Constants.Socials.Github}>GitHub</Link> or
|
||||
<Link href={Constants.Socials.Weblate}>Weblate</Link>
|
||||
</Text>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
{#each supportItems as action, i (i)}
|
||||
<Button href={action.href} size="large" variant="outline" color="secondary" leadingIcon={action.icon}>
|
||||
{action.text}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
</VStack>
|
||||
</FeatureContainer>
|
||||
{:else}
|
||||
<div class="h-screen w-full"></div>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { tv } from 'tailwind-variants';
|
||||
|
||||
@@ -22,6 +23,8 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class={styles({ releaseNotes: isReleaseNotePage })}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
<PageContent>
|
||||
<div class={styles({ releaseNotes: isReleaseNotePage })}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</PageContent>
|
||||
|
||||
@@ -30,8 +30,10 @@
|
||||
|
||||
<SiteMetadata site={blogMetadata} />
|
||||
|
||||
<Heading size="title" tag="h1" fontWeight="bold" class="mb-1">Blog</Heading>
|
||||
<Text color="muted" class="mt-4">{blogMetadata.description}</Text>
|
||||
<div>
|
||||
<Heading size="title" tag="h1" fontWeight="bold" class="mb-1">Blog</Heading>
|
||||
<Text color="muted" class="mt-4">{blogMetadata.description}</Text>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<a href={Routes.blog()}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { TimelineItem } from '$lib';
|
||||
import { posts, siteMetadata } from '$lib';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import Timeline from '$lib/components/Timeline.svelte';
|
||||
import { asGithubLink, Heading, SiteMetadata, Stack, Text } from '@immich/ui';
|
||||
import {
|
||||
@@ -236,15 +237,17 @@
|
||||
|
||||
<SiteMetadata site={siteMetadata} page={pageMetadata} />
|
||||
|
||||
<Stack class="text-center" gap={4}>
|
||||
<Heading size="title" tag="h1">{pageMetadata.title}</Heading>
|
||||
<Text>{pageMetadata.description}</Text>
|
||||
</Stack>
|
||||
<PageContent>
|
||||
<Stack class="text-center" gap={4}>
|
||||
<Heading size="title" tag="h1">{pageMetadata.title}</Heading>
|
||||
<Text>{pageMetadata.description}</Text>
|
||||
</Stack>
|
||||
|
||||
<div class="mx-auto mt-8 flex w-full max-w-(--breakpoint-md) justify-around">
|
||||
<Timeline
|
||||
items={items
|
||||
.toSorted((a, b) => b.date.getTime() - a.date.getTime())
|
||||
.map((item) => ({ ...item, getDateLabel: withLanguage(item.date) }))}
|
||||
/>
|
||||
</div>
|
||||
<div class="mx-auto mt-8 flex w-full max-w-(--breakpoint-md) justify-around">
|
||||
<Timeline
|
||||
items={items
|
||||
.toSorted((a, b) => b.date.getTime() - a.date.getTime())
|
||||
.map((item) => ({ ...item, getDateLabel: withLanguage(item.date) }))}
|
||||
/>
|
||||
</div>
|
||||
</PageContent>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import truenasIcon from '$common/img/truenas-scale.svg';
|
||||
import unraidIcon from '$common/img/unraid.svg';
|
||||
import { siteMetadata } from '$lib';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import {
|
||||
appStoreBadge,
|
||||
Button,
|
||||
@@ -13,6 +14,7 @@
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Constants,
|
||||
Container,
|
||||
fdroidBadge,
|
||||
Heading,
|
||||
Icon,
|
||||
@@ -31,109 +33,112 @@
|
||||
</script>
|
||||
|
||||
<SiteMetadata site={siteMetadata} page={pageMetadata} />
|
||||
<PageContent>
|
||||
<Container size="large" center>
|
||||
<Heading size="title" class="mb-4" tag="h1">{pageMetadata.title}</Heading>
|
||||
|
||||
<Heading size="title" class="mb-4" tag="h1">{pageMetadata.title}</Heading>
|
||||
<div class="mb-4">
|
||||
<Heading size="large" tag="h2">Mobile Apps</Heading>
|
||||
<Text color="muted">{pageMetadata.description}</Text>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<Heading size="large" tag="h2">Mobile Apps</Heading>
|
||||
<Text color="muted">{pageMetadata.description}</Text>
|
||||
</div>
|
||||
<div class="l mb-8 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2">
|
||||
<Card color="secondary">
|
||||
<CardHeader>
|
||||
<CardTitle>Android</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Stack gap={4}>
|
||||
<a href={Constants.Get.Android}>
|
||||
<img class="mx-auto h-16" alt="Get it on Google Play" src={playStoreBadge} />
|
||||
</a>
|
||||
<a href={Constants.Get.FDroid}>
|
||||
<img class="mx-auto h-16 rounded-xl border border-dark" alt="Get it on F-Droid" src={fdroidBadge} />
|
||||
</a>
|
||||
<Button
|
||||
class="light mx-auto h-16 border"
|
||||
size="giant"
|
||||
variant="filled"
|
||||
href={Constants.Get.GithubRelease}
|
||||
color="secondary"
|
||||
>
|
||||
<Icon icon={mdiAndroid} size="2em" />
|
||||
<Text>Download APK</Text>
|
||||
</Button>
|
||||
</Stack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
<div class="l mb-8 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2">
|
||||
<Card color="secondary">
|
||||
<CardHeader>
|
||||
<CardTitle>Android</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Stack gap={4}>
|
||||
<a href={Constants.Get.Android}>
|
||||
<img class="mx-auto h-16" alt="Get it on Google Play" src={playStoreBadge} />
|
||||
</a>
|
||||
<a href={Constants.Get.FDroid}>
|
||||
<img class="mx-auto h-16 rounded-xl border border-dark" alt="Get it on F-Droid" src={fdroidBadge} />
|
||||
</a>
|
||||
<Button
|
||||
class="light mx-auto h-16 border"
|
||||
size="giant"
|
||||
variant="filled"
|
||||
href={Constants.Get.GithubRelease}
|
||||
color="secondary"
|
||||
>
|
||||
<Icon icon={mdiAndroid} size="2em" />
|
||||
<Text>Download APK</Text>
|
||||
</Button>
|
||||
</Stack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Card color="secondary">
|
||||
<CardHeader>
|
||||
<CardTitle>iOS</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<a href={Constants.Get.iOS}>
|
||||
<img class="mx-auto mt-4 h-16" alt="Download on the App Store" src={appStoreBadge} />
|
||||
</a>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card color="secondary">
|
||||
<CardHeader>
|
||||
<CardTitle>iOS</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<a href={Constants.Get.iOS}>
|
||||
<img class="mx-auto mt-4 h-16" alt="Download on the App Store" src={appStoreBadge} />
|
||||
</a>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<Heading size="large" tag="h2">Server</Heading>
|
||||
<Text color="muted">Download, setup, and run Immich on your own server</Text>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<Heading size="large" tag="h2">Server</Heading>
|
||||
<Text color="muted">Download, setup, and run Immich on your own server</Text>
|
||||
</div>
|
||||
<section class="grid auto-rows-fr grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Card color="secondary" class="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Docker Compose</CardTitle>
|
||||
<CardDescription>Run Immich on <Link href="https://www.docker.com/">Docker</Link></CardDescription>
|
||||
</CardHeader>
|
||||
<CardBody class="flex items-center justify-center align-middle">
|
||||
<Icon icon={mdiDocker} size="6rem" color="#0db7ed" />
|
||||
</CardBody>
|
||||
<CardFooter class="flex flex-col gap-2">
|
||||
<Button fullWidth href="https://immich.app/docs/install/docker-compose">View Instructions</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<section class="grid auto-rows-fr grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Card color="secondary" class="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Docker Compose</CardTitle>
|
||||
<CardDescription>Run Immich on <Link href="https://www.docker.com/">Docker</Link></CardDescription>
|
||||
</CardHeader>
|
||||
<CardBody class="flex items-center justify-center align-middle">
|
||||
<Icon icon={mdiDocker} size="6rem" color="#0db7ed" />
|
||||
</CardBody>
|
||||
<CardFooter class="flex flex-col gap-2">
|
||||
<Button fullWidth href="https://immich.app/docs/install/docker-compose">View Instructions</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card color="secondary" class="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Portainer</CardTitle>
|
||||
<CardDescription>Run Immich on <Link href="https://www.portainer.io/">Portainer</Link></CardDescription>
|
||||
</CardHeader>
|
||||
<CardBody class="flex items-center justify-center align-middle">
|
||||
<img src={portainerIcon} class="w-20" alt="Portainer logo" />
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Button fullWidth href="https://immich.app/docs/install/portainer">View Instructions</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card color="secondary" class="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Unraid</CardTitle>
|
||||
<CardDescription>Run Immich on <Link href="https://unraid.net/">Unraid</Link></CardDescription>
|
||||
</CardHeader>
|
||||
<CardBody class="flex items-center justify-center align-middle">
|
||||
<img src={unraidIcon} class="w-24" alt="UnRaid logo" />
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Button fullWidth href="https://immich.app/docs/install/unraid">View Instructions</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<Card color="secondary" class="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Portainer</CardTitle>
|
||||
<CardDescription>Run Immich on <Link href="https://www.portainer.io/">Portainer</Link></CardDescription>
|
||||
</CardHeader>
|
||||
<CardBody class="flex items-center justify-center align-middle">
|
||||
<img src={portainerIcon} class="w-20" alt="Portainer logo" />
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Button fullWidth href="https://immich.app/docs/install/portainer">View Instructions</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<Card color="secondary" class="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Unraid</CardTitle>
|
||||
<CardDescription>Run Immich on <Link href="https://unraid.net/">Unraid</Link></CardDescription>
|
||||
</CardHeader>
|
||||
<CardBody class="flex items-center justify-center align-middle">
|
||||
<img src={unraidIcon} class="w-24" alt="UnRaid logo" />
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Button fullWidth href="https://immich.app/docs/install/unraid">View Instructions</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<Card color="secondary" class="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>TrueNAS SCALE</CardTitle>
|
||||
<CardDescription
|
||||
>Run Immich on <Link href="https://www.truenas.com/truenas-scale/">TrueNAS SCALE</Link></CardDescription
|
||||
>
|
||||
</CardHeader>
|
||||
<CardBody class="flex items-center justify-center align-middle">
|
||||
<img src={truenasIcon} class="w-24" alt="TrueNAS SCALE logo" />
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Button fullWidth href="https://immich.app/docs/install/truenas">View Instructions</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</section>
|
||||
<Card color="secondary" class="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>TrueNAS SCALE</CardTitle>
|
||||
<CardDescription
|
||||
>Run Immich on <Link href="https://www.truenas.com/truenas-scale/">TrueNAS SCALE</Link></CardDescription
|
||||
>
|
||||
</CardHeader>
|
||||
<CardBody class="flex items-center justify-center align-middle">
|
||||
<img src={truenasIcon} class="w-24" alt="TrueNAS SCALE logo" />
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Button fullWidth href="https://immich.app/docs/install/truenas">View Instructions</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</section>
|
||||
</Container>
|
||||
</PageContent>
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { siteMetadata } from '$lib';
|
||||
import {
|
||||
Card,
|
||||
CardBody,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Heading,
|
||||
Icon,
|
||||
SiteMetadata,
|
||||
Stack,
|
||||
Text,
|
||||
} from '@immich/ui';
|
||||
import FeatureCard from '$lib/components/FeatureCard.svelte';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import type { Feature } from '$lib/types';
|
||||
import { Container, Heading, SiteMetadata, Text } from '@immich/ui';
|
||||
import {
|
||||
mdiAccountCog,
|
||||
mdiAccountMultiple,
|
||||
mdiAccountMultipleOutline,
|
||||
mdiAccountPlusOutline,
|
||||
mdiAndroid,
|
||||
mdiApple,
|
||||
mdiArchiveOutline,
|
||||
mdiCalendarTodayOutline,
|
||||
mdiCellphone,
|
||||
mdiCast,
|
||||
mdiChartLine,
|
||||
mdiCloudUpload,
|
||||
mdiDatabaseOutline,
|
||||
mdiEmailOutline,
|
||||
mdiFaceRecognition,
|
||||
mdiFileMultiple,
|
||||
mdiFileOutline,
|
||||
mdiFileUploadOutline,
|
||||
mdiFolderMultiple,
|
||||
@@ -34,6 +28,8 @@
|
||||
mdiImageMultiple,
|
||||
mdiImageMultipleOutline,
|
||||
mdiKeyOutline,
|
||||
mdiLink,
|
||||
mdiLock,
|
||||
mdiMagnify,
|
||||
mdiMapMarkerOutline,
|
||||
mdiMapOutline,
|
||||
@@ -43,210 +39,225 @@
|
||||
mdiRaw,
|
||||
mdiRotate360,
|
||||
mdiSecurity,
|
||||
mdiShareVariant,
|
||||
mdiShare,
|
||||
mdiTagMultiple,
|
||||
mdiThemeLightDark,
|
||||
mdiTimelineOutline,
|
||||
mdiTrashCan,
|
||||
mdiTune,
|
||||
mdiWeb,
|
||||
} from '@mdi/js';
|
||||
|
||||
type Feature = {
|
||||
icon: string;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
const features: Feature[] = [
|
||||
{
|
||||
icons: [mdiApple, mdiAndroid],
|
||||
title: 'Mobile App',
|
||||
description: 'View your photos and videos on the go',
|
||||
icon: mdiCellphone,
|
||||
description: 'Backup photos and videos from your devices.',
|
||||
},
|
||||
{
|
||||
icons: [mdiShare],
|
||||
title: 'Sharing',
|
||||
description: 'Share your library with friends and family.',
|
||||
},
|
||||
{
|
||||
icons: [mdiFaceRecognition],
|
||||
title: 'Facial Recognition',
|
||||
description: 'Detect and group faces automatically.',
|
||||
},
|
||||
{
|
||||
icons: [mdiLink],
|
||||
title: 'Public Links',
|
||||
description: 'Create secure links for sharing specific collections.',
|
||||
},
|
||||
{
|
||||
icons: [mdiAccountMultiple],
|
||||
title: 'Multiple Users',
|
||||
description: 'Each user has their own account and devices.',
|
||||
},
|
||||
{
|
||||
icons: [mdiMotionPlayOutline],
|
||||
title: 'Motion Photos',
|
||||
description: 'Play Apple LivePhotos and Android Motion Photos.',
|
||||
},
|
||||
{
|
||||
icons: [mdiCast],
|
||||
title: 'Cast',
|
||||
description: 'Enjoy photos and videos on the big screen.',
|
||||
},
|
||||
{
|
||||
icons: [mdiLock],
|
||||
title: 'Locked Folder',
|
||||
description: 'Extra protection for sensitive media.',
|
||||
},
|
||||
{
|
||||
icons: [mdiTune],
|
||||
title: 'Built-in Editor',
|
||||
description: 'Easily adjust, crop, and rotate photos.',
|
||||
},
|
||||
{
|
||||
icons: [mdiFileMultiple],
|
||||
title: 'Duplicate Detection',
|
||||
description: 'Detect and group duplicates automatically.',
|
||||
},
|
||||
{
|
||||
icons: [mdiMagnify],
|
||||
title: 'Smart Search',
|
||||
description: 'Search using phrases like "sunset at the beach".',
|
||||
},
|
||||
{
|
||||
icons: [mdiSecurity],
|
||||
title: 'OAuth Integration',
|
||||
description: 'Integrate with 3rd party identity providers.',
|
||||
},
|
||||
{
|
||||
title: 'Timeline',
|
||||
description: 'View your photos and videos in a timeline',
|
||||
icon: mdiTimelineOutline,
|
||||
icons: [mdiTimelineOutline],
|
||||
},
|
||||
{
|
||||
title: 'Albums',
|
||||
description: 'Organize your photos and videos into albums and share them with other users',
|
||||
icon: mdiImageAlbum,
|
||||
},
|
||||
{
|
||||
title: 'Multi-User',
|
||||
description: 'Add friends and family to your instance, each with their own private set of photos and videos',
|
||||
icon: mdiAccountPlusOutline,
|
||||
},
|
||||
{
|
||||
title: 'Shared Links',
|
||||
description: 'Share your photos and videos with private, secure, and optionally password protected links',
|
||||
icon: mdiShareVariant,
|
||||
icons: [mdiImageAlbum],
|
||||
},
|
||||
{
|
||||
title: 'Partner Sharing',
|
||||
description: 'Share your photos and videos with one or more partners',
|
||||
icon: mdiAccountMultipleOutline,
|
||||
icons: [mdiAccountMultipleOutline],
|
||||
},
|
||||
{
|
||||
title: 'Dark Theme',
|
||||
description: 'View Immich using a dark theme',
|
||||
icon: mdiThemeLightDark,
|
||||
},
|
||||
{
|
||||
title: 'Facial Recognition',
|
||||
description: 'Detect faces in your photos and videos',
|
||||
icon: mdiFaceRecognition,
|
||||
icons: [mdiThemeLightDark],
|
||||
},
|
||||
{
|
||||
title: 'Map',
|
||||
description: 'View your photos and videos on a map',
|
||||
icon: mdiMapOutline,
|
||||
icons: [mdiMapOutline],
|
||||
},
|
||||
{
|
||||
title: 'Archive',
|
||||
description: 'Hide non-essential photos and videos from the main timeline',
|
||||
icon: mdiArchiveOutline,
|
||||
icons: [mdiArchiveOutline],
|
||||
},
|
||||
{
|
||||
title: 'Memories',
|
||||
description: 'View photos and videos taken on this day in past years',
|
||||
icon: mdiCalendarTodayOutline,
|
||||
icons: [mdiCalendarTodayOutline],
|
||||
},
|
||||
{
|
||||
title: 'Favorites',
|
||||
description: 'Keep track of your favorite photos and videos',
|
||||
icon: mdiHeartOutline,
|
||||
},
|
||||
{
|
||||
title: 'Smart Search',
|
||||
description: 'Search your photos and videos with natural language',
|
||||
icon: mdiMagnify,
|
||||
icons: [mdiHeartOutline],
|
||||
},
|
||||
{
|
||||
title: 'Slideshow',
|
||||
description: 'View your photos and videos as a slideshow',
|
||||
icon: mdiPlayBoxOutline,
|
||||
},
|
||||
{
|
||||
title: 'Live Photos',
|
||||
description: 'View Live Photos from Apple devices',
|
||||
icon: mdiMotionPlayOutline,
|
||||
},
|
||||
{
|
||||
title: 'Motion Photos',
|
||||
description: 'View Motion PHotos from Android devices',
|
||||
icon: mdiMotionPlayOutline,
|
||||
icons: [mdiPlayBoxOutline],
|
||||
},
|
||||
{
|
||||
title: '360° Photos',
|
||||
description: 'View your 360° photos in Immich',
|
||||
icon: mdiRotate360,
|
||||
icons: [mdiRotate360],
|
||||
},
|
||||
{
|
||||
title: 'Panoramas',
|
||||
description: 'View your panoramas in Immich',
|
||||
icon: mdiPanoramaOutline,
|
||||
icons: [mdiPanoramaOutline],
|
||||
},
|
||||
{
|
||||
title: 'RAW',
|
||||
description: 'View your raw, uncompressed photo and scans in Immich',
|
||||
icon: mdiRaw,
|
||||
icons: [mdiRaw],
|
||||
},
|
||||
{
|
||||
title: 'Stacks',
|
||||
description: 'Combine similar or related photos and videos into a stack',
|
||||
icon: mdiImageMultipleOutline,
|
||||
icons: [mdiImageMultipleOutline],
|
||||
},
|
||||
{
|
||||
title: 'Tags',
|
||||
description: 'Organize your photos and videos with hierarchal tags',
|
||||
icon: mdiTagMultiple,
|
||||
icons: [mdiTagMultiple],
|
||||
},
|
||||
{
|
||||
title: 'Drag and Drop',
|
||||
description: 'Drag and drop photos, videos, and screenshots into Immich',
|
||||
icon: mdiFileUploadOutline,
|
||||
icons: [mdiFileUploadOutline],
|
||||
},
|
||||
{
|
||||
title: 'Internationalization',
|
||||
description: 'View Immich in your preferred language and locale',
|
||||
icon: mdiWeb,
|
||||
icons: [mdiWeb],
|
||||
},
|
||||
{
|
||||
title: 'Email Notifications',
|
||||
description: 'Receive email notifications when new photos or videos are added to an album',
|
||||
icon: mdiEmailOutline,
|
||||
icons: [mdiEmailOutline],
|
||||
},
|
||||
{
|
||||
title: 'Trash',
|
||||
description: 'Automatically delete photos and videos from the trash after a period of time',
|
||||
icon: mdiTagMultiple,
|
||||
icons: [mdiTrashCan],
|
||||
},
|
||||
{
|
||||
title: 'Reverse Geocoding',
|
||||
description: 'View and search your photos and videos by city, state, and country names',
|
||||
icon: mdiMapMarkerOutline,
|
||||
icons: [mdiMapMarkerOutline],
|
||||
},
|
||||
{
|
||||
title: 'Activity & Comments',
|
||||
description: 'View activity and comments on your photos and videos',
|
||||
icon: mdiForumOutline,
|
||||
icons: [mdiForumOutline],
|
||||
},
|
||||
{
|
||||
title: 'External Libraries',
|
||||
description: 'Mount your photos and videos as read-only, external folder',
|
||||
icon: mdiImageMultiple,
|
||||
icons: [mdiImageMultiple],
|
||||
},
|
||||
{
|
||||
title: 'Folder View',
|
||||
description: 'View your photos and videos in a folder structure',
|
||||
icon: mdiFolderMultiple,
|
||||
icons: [mdiFolderMultiple],
|
||||
},
|
||||
{
|
||||
title: 'Storage Template',
|
||||
description: 'Customize how your photos and videos are stored on disk',
|
||||
icon: mdiTagMultiple,
|
||||
},
|
||||
|
||||
{
|
||||
title: 'OIDC',
|
||||
description: 'Login to Immich using OpenID Connect',
|
||||
icon: mdiSecurity,
|
||||
icons: [mdiTagMultiple],
|
||||
},
|
||||
{
|
||||
title: 'Hardware Transcoding',
|
||||
description: 'Use hardware acceleration to transcode your videos',
|
||||
icon: mdiAccountMultiple,
|
||||
icons: [mdiAccountMultiple],
|
||||
},
|
||||
{
|
||||
title: 'Database Backups',
|
||||
description: 'Automatically backup your Immich database',
|
||||
icon: mdiDatabaseOutline,
|
||||
icons: [mdiDatabaseOutline],
|
||||
},
|
||||
{
|
||||
title: 'CLI Upload',
|
||||
description: 'Import photos and videos into Immich using the CLI',
|
||||
icon: mdiCloudUpload,
|
||||
icons: [mdiCloudUpload],
|
||||
},
|
||||
{
|
||||
title: 'Api Keys',
|
||||
title: 'API Keys',
|
||||
description: 'Programmatically access your data or integrate Immich with third party services',
|
||||
icon: mdiKeyOutline,
|
||||
icons: [mdiKeyOutline],
|
||||
},
|
||||
{
|
||||
title: 'Monitoring',
|
||||
description: 'Monitor the health of your Immich instance via Prometheus',
|
||||
icon: mdiChartLine,
|
||||
icons: [mdiChartLine],
|
||||
},
|
||||
{
|
||||
title: 'User Settings',
|
||||
description: 'Customize your Immich experience',
|
||||
icon: mdiAccountCog,
|
||||
icons: [mdiAccountCog],
|
||||
},
|
||||
{
|
||||
title: 'XML Sidecars',
|
||||
description: 'Sync metadata to and from XMP sidecar files',
|
||||
icon: mdiFileOutline,
|
||||
icons: [mdiFileOutline],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -258,25 +269,17 @@
|
||||
|
||||
<SiteMetadata site={siteMetadata} page={pageMetadata} />
|
||||
|
||||
<Stack gap={8}>
|
||||
<Stack>
|
||||
<Heading size="title" tag="h1">{pageMetadata.title}</Heading>
|
||||
<Text>{pageMetadata.description}</Text>
|
||||
</Stack>
|
||||
<PageContent>
|
||||
<Container size="giant" center>
|
||||
<div class="mb-4">
|
||||
<Heading size="title" tag="h1" fontWeight="bold" class="mb-1">{pageMetadata.title}</Heading>
|
||||
<Text color="muted">{pageMetadata.description}</Text>
|
||||
</div>
|
||||
|
||||
<Stack gap={2}>
|
||||
<section class="grid auto-rows-fr grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div class="grid w-full grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 lg:gap-6">
|
||||
{#each features as feature, i (i)}
|
||||
<Card color="secondary" class="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>{feature.title}</CardTitle>
|
||||
<CardDescription>{feature.description}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardBody class="flex items-center justify-center align-middle text-primary">
|
||||
<Icon icon={feature.icon} size="3rem" class="m-5" />
|
||||
</CardBody>
|
||||
</Card>
|
||||
<FeatureCard {feature} />
|
||||
{/each}
|
||||
</section>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</div>
|
||||
</Container>
|
||||
</PageContent>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { TimelineItem } from '$lib';
|
||||
import { siteMetadata } from '$lib';
|
||||
import PageContent from '$lib/components/PageContent.svelte';
|
||||
import Timeline from '$lib/components/Timeline.svelte';
|
||||
import { Heading, SiteMetadata, Stack, Text } from '@immich/ui';
|
||||
import { Container, Heading, SiteMetadata, Stack, Text } from '@immich/ui';
|
||||
import {
|
||||
mdiAccountGroup,
|
||||
mdiAccountGroupOutline,
|
||||
@@ -985,10 +986,14 @@
|
||||
|
||||
<SiteMetadata site={siteMetadata} page={pageMetadata} />
|
||||
|
||||
<Stack class="text-center" gap={4}>
|
||||
<Heading size="title" tag="h1">Roadmap</Heading>
|
||||
<Text>{pageMetadata.description}</Text>
|
||||
</Stack>
|
||||
<div class="mx-auto mt-8 flex w-full max-w-(--breakpoint-md) justify-around">
|
||||
<Timeline items={[...roadmap, ...milestones]} />
|
||||
</div>
|
||||
<PageContent>
|
||||
<Container size="medium" center>
|
||||
<Stack class="text-center" gap={4}>
|
||||
<Heading size="title" tag="h1">Roadmap</Heading>
|
||||
<Text>{pageMetadata.description}</Text>
|
||||
</Stack>
|
||||
<div class="mx-auto mt-8 flex w-full max-w-(--breakpoint-md) justify-around">
|
||||
<Timeline items={[...roadmap, ...milestones]} />
|
||||
</div>
|
||||
</Container>
|
||||
</PageContent>
|
||||
|
||||
@@ -105,6 +105,6 @@
|
||||
<div class="grow">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
<SiteFooter />
|
||||
<SiteFooter class="mt-16" />
|
||||
</section>
|
||||
</AppShell>
|
||||
|
||||
Reference in New Issue
Block a user