@immich/ui 0.30.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +9 -17
  2. package/dist/actions/shortcut.js +1 -1
  3. package/dist/components/Alert/Alert.svelte +92 -94
  4. package/dist/components/AppShell/AppShell.svelte +26 -26
  5. package/dist/components/AppShell/AppShellHeader.svelte +8 -8
  6. package/dist/components/AppShell/AppShellSidebar.svelte +20 -20
  7. package/dist/components/AppShell/PageLayout.svelte +29 -35
  8. package/dist/components/Avatar/Avatar.svelte +45 -55
  9. package/dist/components/Button/Button.svelte +3 -3
  10. package/dist/components/Card/Card.svelte +131 -135
  11. package/dist/components/Card/CardBody.svelte +9 -9
  12. package/dist/components/Card/CardDescription.svelte +9 -9
  13. package/dist/components/Card/CardFooter.svelte +9 -9
  14. package/dist/components/Card/CardHeader.svelte +9 -9
  15. package/dist/components/Card/CardTitle.svelte +14 -14
  16. package/dist/components/Checkbox/Checkbox.svelte +109 -110
  17. package/dist/components/CloseButton/CloseButton.svelte +12 -17
  18. package/dist/components/Code/Code.svelte +58 -65
  19. package/dist/components/CommandPalette/CommandPalette.svelte +126 -131
  20. package/dist/components/CommandPalette/CommandPaletteItem.svelte +61 -61
  21. package/dist/components/ConfirmModal/ConfirmModal.svelte +48 -48
  22. package/dist/components/Container/Container.svelte +25 -25
  23. package/dist/components/Field/Field.svelte +21 -21
  24. package/dist/components/FormatBytes/FormatBytes.svelte +9 -9
  25. package/dist/components/Heading/Heading.svelte +41 -47
  26. package/dist/components/HelperText/HelperText.svelte +17 -17
  27. package/dist/components/Icon/Icon.svelte +37 -42
  28. package/dist/components/IconButton/IconButton.svelte +6 -13
  29. package/dist/components/Input/Input.svelte +149 -152
  30. package/dist/components/Kbd/Kbd.svelte +25 -25
  31. package/dist/components/Label/Label.svelte +6 -6
  32. package/dist/components/Link/Link.svelte +18 -25
  33. package/dist/components/LoadingSpinner/LoadingSpinner.svelte +46 -46
  34. package/dist/components/Logo/Logo.svelte +53 -53
  35. package/dist/components/Modal/Modal.svelte +110 -114
  36. package/dist/components/Modal/ModalBody.svelte +8 -8
  37. package/dist/components/Modal/ModalFooter.svelte +8 -8
  38. package/dist/components/Modal/ModalHeader.svelte +8 -8
  39. package/dist/components/MultiSelect/MultiSelect.svelte +7 -7
  40. package/dist/components/Navbar/NavbarGroup.svelte +5 -5
  41. package/dist/components/Navbar/NavbarItem.svelte +59 -61
  42. package/dist/components/PasswordInput/PasswordInput.svelte +29 -31
  43. package/dist/components/Scrollable/Scrollable.svelte +49 -49
  44. package/dist/components/Select/Select.svelte +9 -9
  45. package/dist/components/Stack/HStack.svelte +4 -4
  46. package/dist/components/Stack/Stack.svelte +62 -62
  47. package/dist/components/Stack/VStack.svelte +4 -4
  48. package/dist/components/SupporterBadge/SupporterBadge.svelte +80 -80
  49. package/dist/components/Switch/Switch.svelte +95 -96
  50. package/dist/components/Text/Text.svelte +27 -34
  51. package/dist/components/Textarea/Textarea.svelte +103 -104
  52. package/dist/components/ThemeSwitcher/ThemeSwitcher.svelte +30 -43
  53. package/dist/internal/Button.svelte +177 -177
  54. package/dist/internal/Child.svelte +21 -21
  55. package/dist/internal/Select.svelte +151 -158
  56. package/dist/internal/Text.svelte +42 -50
  57. package/dist/site/SiteFooter.svelte +60 -76
  58. package/dist/site/SiteFooterLink.svelte +14 -14
  59. package/dist/theme/default.css +40 -40
  60. package/dist/utilities/byte-units.js +2 -13
  61. package/package.json +77 -77
@@ -1,163 +1,156 @@
1
1
  <script lang="ts">
2
- import { getFieldContext } from '../common/context.svelte.js';
3
- import Field from '../components/Field/Field.svelte';
4
- import Icon from '../components/Icon/Icon.svelte';
5
- import IconButton from '../components/IconButton/IconButton.svelte';
6
- import Input from '../components/Input/Input.svelte';
7
- import Label from '../components/Label/Label.svelte';
8
- import type { SelectCommonProps, SelectItem } from '../types.js';
9
- import { cleanClass, generateId } from '../utils.js';
10
- import { mdiArrowDown, mdiArrowUp, mdiCheck, mdiUnfoldMoreHorizontal } from '@mdi/js';
11
- import { Select } from 'bits-ui';
12
-
13
- type T = SelectItem;
14
-
15
- type Props = {
16
- multiple?: boolean;
17
- values: T[];
18
- asLabel?: (items: T[]) => string;
19
- onChange?: (values: T[]) => void;
20
- } & SelectCommonProps<T>;
21
-
22
- let {
23
- data,
24
- shape,
25
- size = 'medium',
26
- multiple = false,
27
- values = $bindable([]),
28
- onChange,
29
- asLabel = (options: T[]) => options.map(({ label }) => label).join(', '),
30
- placeholder,
31
- class: className,
32
- }: Props = $props();
33
-
34
- const asOptions = (items: string[] | T[]) => {
35
- return items.map((item) => {
36
- if (typeof item === 'string') {
37
- return { value: item, label: item } as T;
38
- }
39
-
40
- const label = item.label ?? item.value;
41
- return { ...item, label };
42
- });
43
- };
44
-
45
- const options = $derived(asOptions(data));
46
-
47
- const { readOnly, required, invalid, disabled, label, ...labelProps } =
48
- $derived(getFieldContext());
49
-
50
- const id = generateId();
51
- const inputId = `input-${id}`;
52
- const labelId = `label-${id}`;
53
-
54
- const selectedLabel = $derived(asLabel(values));
55
-
56
- let inputRef = $state<HTMLElement | null>(null);
57
- let contentRef = $state<HTMLElement | null>(null);
58
- let ref = $state<HTMLElement | null>(null);
59
-
60
- $effect(() => {
61
- if (ref && contentRef) {
62
- contentRef.style.width = `${ref.clientWidth}px`;
63
- }
64
- });
65
-
66
- const findOption = (value: string) => options.find((option) => option.value === value);
67
-
68
- const onValueChange = (items: string[] | string) => {
69
- values = multiple
70
- ? ((items as string[]).map(findOption) as T[])
71
- : [findOption(items as string) as T].filter(Boolean);
72
-
73
- onChange?.(values);
74
- };
75
-
76
- let internalValue = $derived(
77
- multiple ? values.map(({ value }) => value) : (values[0]?.value ?? ''),
78
- );
2
+ import { getFieldContext } from '../common/context.svelte.js';
3
+ import Field from '../components/Field/Field.svelte';
4
+ import Icon from '../components/Icon/Icon.svelte';
5
+ import IconButton from '../components/IconButton/IconButton.svelte';
6
+ import Input from '../components/Input/Input.svelte';
7
+ import Label from '../components/Label/Label.svelte';
8
+ import type { SelectCommonProps, SelectItem } from '../types.js';
9
+ import { cleanClass, generateId } from '../utils.js';
10
+ import { mdiArrowDown, mdiArrowUp, mdiCheck, mdiUnfoldMoreHorizontal } from '@mdi/js';
11
+ import { Select } from 'bits-ui';
12
+
13
+ type T = SelectItem;
14
+
15
+ type Props = {
16
+ multiple?: boolean;
17
+ values: T[];
18
+ asLabel?: (items: T[]) => string;
19
+ onChange?: (values: T[]) => void;
20
+ } & SelectCommonProps<T>;
21
+
22
+ let {
23
+ data,
24
+ shape,
25
+ size = 'medium',
26
+ multiple = false,
27
+ values = $bindable([]),
28
+ onChange,
29
+ asLabel = (options: T[]) => options.map(({ label }) => label).join(', '),
30
+ placeholder,
31
+ class: className,
32
+ }: Props = $props();
33
+
34
+ const asOptions = (items: string[] | T[]) => {
35
+ return items.map((item) => {
36
+ if (typeof item === 'string') {
37
+ return { value: item, label: item } as T;
38
+ }
39
+
40
+ const label = item.label ?? item.value;
41
+ return { ...item, label };
42
+ });
43
+ };
44
+
45
+ const options = $derived(asOptions(data));
46
+
47
+ const { readOnly, required, invalid, disabled, label, ...labelProps } = $derived(getFieldContext());
48
+
49
+ const id = generateId();
50
+ const inputId = `input-${id}`;
51
+ const labelId = `label-${id}`;
52
+
53
+ const selectedLabel = $derived(asLabel(values));
54
+
55
+ let inputRef = $state<HTMLElement | null>(null);
56
+ let contentRef = $state<HTMLElement | null>(null);
57
+ let ref = $state<HTMLElement | null>(null);
58
+
59
+ $effect(() => {
60
+ if (ref && contentRef) {
61
+ contentRef.style.width = `${ref.clientWidth}px`;
62
+ }
63
+ });
64
+
65
+ const findOption = (value: string) => options.find((option) => option.value === value);
66
+
67
+ const onValueChange = (items: string[] | string) => {
68
+ values = multiple
69
+ ? ((items as string[]).map(findOption) as T[])
70
+ : [findOption(items as string) as T].filter(Boolean);
71
+
72
+ onChange?.(values);
73
+ };
74
+
75
+ let internalValue = $derived(multiple ? values.map(({ value }) => value) : (values[0]?.value ?? ''));
79
76
  </script>
80
77
 
81
78
  <div class={cleanClass('flex flex-col gap-1', className)} bind:this={ref}>
82
- {#if label}
83
- <Label id={labelId} for={inputId} {label} {...labelProps} />
84
- {/if}
85
-
86
- <Select.Root
87
- type={multiple ? 'multiple' : 'single'}
88
- bind:value={internalValue as never}
89
- {onValueChange}
90
- >
91
- <Select.Trigger
92
- {disabled}
93
- class="w-full items-center gap-1 rounded-lg focus-visible:outline-none"
94
- aria-label={placeholder}
95
- >
96
- <Field {readOnly} {required} {disabled} {invalid}>
97
- <Input
98
- bind:containerRef={inputRef}
99
- id={inputId}
100
- {size}
101
- {shape}
102
- {placeholder}
103
- value={selectedLabel}
104
- readonly
105
- aria-labelledby={labelId}
106
- aria-readonly
107
- >
108
- {#snippet trailingIcon()}
109
- <IconButton
110
- variant="ghost"
111
- shape="round"
112
- color="secondary"
113
- class="m-1"
114
- icon={mdiUnfoldMoreHorizontal}
115
- {disabled}
116
- aria-label="Expand"
117
- />
118
- {/snippet}
119
- </Input>
120
- </Field>
121
- </Select.Trigger>
122
- <Select.Portal>
123
- <Select.Content
124
- bind:ref={contentRef}
125
- class="bg-light text-dark max-h-96 rounded-xl border py-3 outline-none select-none"
126
- sideOffset={10}
127
- >
128
- <Select.ScrollUpButton class="flex w-full items-center justify-center">
129
- <Icon icon={mdiArrowUp} />
130
- </Select.ScrollUpButton>
131
- <Select.Viewport>
132
- {#each options as { value, label, disabled }, i (i + value)}
133
- <Select.Item
134
- class={cleanClass(
135
- 'hover:bg-subtle data-[selected]:bg-primary/10 flex h-10 w-full items-center px-5 py-3 text-sm duration-75 outline-none select-none data-disabled:opacity-50',
136
- disabled ? 'cursor-not-allowed' : 'cursor-pointer',
137
- )}
138
- {value}
139
- {label}
140
- {disabled}
141
- >
142
- {#snippet children({ selected })}
143
- <div
144
- class="flex cursor-pointer items-center justify-center gap-2 text-sm font-medium whitespace-nowrap transition-colors"
145
- >
146
- <span>{label}</span>
147
- </div>
148
- {#if selected}
149
- <div class="ms-auto">
150
- <Icon icon={mdiCheck} />
151
- </div>
152
- {/if}
153
- {/snippet}
154
- </Select.Item>
155
- {/each}
156
- </Select.Viewport>
157
- <Select.ScrollDownButton class="flex w-full items-center justify-center">
158
- <Icon icon={mdiArrowDown} />
159
- </Select.ScrollDownButton>
160
- </Select.Content>
161
- </Select.Portal>
162
- </Select.Root>
79
+ {#if label}
80
+ <Label id={labelId} for={inputId} {label} {...labelProps} />
81
+ {/if}
82
+
83
+ <Select.Root type={multiple ? 'multiple' : 'single'} bind:value={internalValue as never} {onValueChange}>
84
+ <Select.Trigger
85
+ {disabled}
86
+ class="w-full items-center gap-1 rounded-lg focus-visible:outline-none"
87
+ aria-label={placeholder}
88
+ >
89
+ <Field {readOnly} {required} {disabled} {invalid}>
90
+ <Input
91
+ bind:containerRef={inputRef}
92
+ id={inputId}
93
+ {size}
94
+ {shape}
95
+ {placeholder}
96
+ value={selectedLabel}
97
+ readonly
98
+ aria-labelledby={labelId}
99
+ aria-readonly
100
+ >
101
+ {#snippet trailingIcon()}
102
+ <IconButton
103
+ variant="ghost"
104
+ shape="round"
105
+ color="secondary"
106
+ class="m-1"
107
+ icon={mdiUnfoldMoreHorizontal}
108
+ {disabled}
109
+ aria-label="Expand"
110
+ />
111
+ {/snippet}
112
+ </Input>
113
+ </Field>
114
+ </Select.Trigger>
115
+ <Select.Portal>
116
+ <Select.Content
117
+ bind:ref={contentRef}
118
+ class="bg-light text-dark max-h-96 rounded-xl border py-3 outline-none select-none"
119
+ sideOffset={10}
120
+ >
121
+ <Select.ScrollUpButton class="flex w-full items-center justify-center">
122
+ <Icon icon={mdiArrowUp} />
123
+ </Select.ScrollUpButton>
124
+ <Select.Viewport>
125
+ {#each options as { value, label, disabled }, i (i + value)}
126
+ <Select.Item
127
+ class={cleanClass(
128
+ 'hover:bg-subtle data-[selected]:bg-primary/10 flex h-10 w-full items-center px-5 py-3 text-sm duration-75 outline-none select-none data-disabled:opacity-50',
129
+ disabled ? 'cursor-not-allowed' : 'cursor-pointer',
130
+ )}
131
+ {value}
132
+ {label}
133
+ {disabled}
134
+ >
135
+ {#snippet children({ selected })}
136
+ <div
137
+ class="flex cursor-pointer items-center justify-center gap-2 text-sm font-medium whitespace-nowrap transition-colors"
138
+ >
139
+ <span>{label}</span>
140
+ </div>
141
+ {#if selected}
142
+ <div class="ms-auto">
143
+ <Icon icon={mdiCheck} />
144
+ </div>
145
+ {/if}
146
+ {/snippet}
147
+ </Select.Item>
148
+ {/each}
149
+ </Select.Viewport>
150
+ <Select.ScrollDownButton class="flex w-full items-center justify-center">
151
+ <Icon icon={mdiArrowDown} />
152
+ </Select.ScrollDownButton>
153
+ </Select.Content>
154
+ </Select.Portal>
155
+ </Select.Root>
163
156
  </div>
@@ -1,60 +1,52 @@
1
1
  <script lang="ts">
2
- import type { FontWeight, HeadingColor, HeadingTag, TextVariant } from '../types.js';
3
- import { cleanClass } from '../utils.js';
4
- import type { Snippet } from 'svelte';
5
- import type { HTMLAttributes } from 'svelte/elements';
6
- import { tv } from 'tailwind-variants';
2
+ import type { FontWeight, HeadingColor, HeadingTag, TextVariant } from '../types.js';
3
+ import { cleanClass } from '../utils.js';
4
+ import type { Snippet } from 'svelte';
5
+ import type { HTMLAttributes } from 'svelte/elements';
6
+ import { tv } from 'tailwind-variants';
7
7
 
8
- type Props = {
9
- /**
10
- * The HTML element type.
11
- */
12
- tag?: HeadingTag;
13
- fontWeight?: FontWeight;
14
- variant?: TextVariant;
15
- color?: HeadingColor;
16
- class?: string;
8
+ type Props = {
9
+ /**
10
+ * The HTML element type.
11
+ */
12
+ tag?: HeadingTag;
13
+ fontWeight?: FontWeight;
14
+ variant?: TextVariant;
15
+ color?: HeadingColor;
16
+ class?: string;
17
17
 
18
- children: Snippet;
19
- } & HTMLAttributes<HTMLElement>;
18
+ children: Snippet;
19
+ } & HTMLAttributes<HTMLElement>;
20
20
 
21
- const {
22
- tag = 'p',
23
- color,
24
- fontWeight,
25
- variant,
26
- class: className,
27
- children,
28
- ...restProps
29
- }: Props = $props();
21
+ const { tag = 'p', color, fontWeight, variant, class: className, children, ...restProps }: Props = $props();
30
22
 
31
- const styles = tv({
32
- variants: {
33
- color: {
34
- muted: 'text-gray-600 dark:text-gray-400',
35
- primary: 'text-primary',
36
- secondary: 'text-dark',
37
- success: 'text-success',
38
- danger: 'text-danger',
39
- warning: 'text-warning',
40
- info: 'text-info',
41
- },
42
- fontWeight: {
43
- light: 'font-light',
44
- normal: 'font-normal',
45
- 'semi-bold': 'font-semibold',
46
- bold: 'font-bold',
47
- 'extra-bold': 'font-extrabold',
48
- },
49
- variant: {
50
- italic: 'italic',
51
- },
52
- },
53
- });
23
+ const styles = tv({
24
+ variants: {
25
+ color: {
26
+ muted: 'text-gray-600 dark:text-gray-400',
27
+ primary: 'text-primary',
28
+ secondary: 'text-dark',
29
+ success: 'text-success',
30
+ danger: 'text-danger',
31
+ warning: 'text-warning',
32
+ info: 'text-info',
33
+ },
34
+ fontWeight: {
35
+ light: 'font-light',
36
+ normal: 'font-normal',
37
+ 'semi-bold': 'font-semibold',
38
+ bold: 'font-bold',
39
+ 'extra-bold': 'font-extrabold',
40
+ },
41
+ variant: {
42
+ italic: 'italic',
43
+ },
44
+ },
45
+ });
54
46
 
55
- const classList = $derived(cleanClass(styles({ color, fontWeight, variant }), className));
47
+ const classList = $derived(cleanClass(styles({ color, fontWeight, variant }), className));
56
48
  </script>
57
49
 
58
50
  <svelte:element this={tag} class={classList} {...restProps}>
59
- {@render children()}
51
+ {@render children()}
60
52
  </svelte:element>
@@ -1,84 +1,68 @@
1
1
  <script lang="ts">
2
- import Heading from '../components/Heading/Heading.svelte';
3
- import Stack from '../components/Stack/Stack.svelte';
4
- import VStack from '../components/Stack/VStack.svelte';
5
- import Text from '../components/Text/Text.svelte';
6
- import ThemeSwitcher from '../components/ThemeSwitcher/ThemeSwitcher.svelte';
7
- import { Constants } from './constants.js';
8
- import SiteFooterLink from './SiteFooterLink.svelte';
9
- import {
10
- mdiBookshelf,
11
- mdiChartGantt,
12
- mdiCubeOutline,
13
- mdiHomeSearchOutline,
14
- mdiKey,
15
- mdiOfficeBuildingOutline,
16
- mdiScriptTextOutline,
17
- mdiServerOutline,
18
- mdiShoppingOutline,
19
- } from '@mdi/js';
20
- import { siAndroid, siApple, siDiscord, siGithub, siReddit } from 'simple-icons';
2
+ import Heading from '../components/Heading/Heading.svelte';
3
+ import Stack from '../components/Stack/Stack.svelte';
4
+ import VStack from '../components/Stack/VStack.svelte';
5
+ import Text from '../components/Text/Text.svelte';
6
+ import ThemeSwitcher from '../components/ThemeSwitcher/ThemeSwitcher.svelte';
7
+ import { Constants } from './constants.js';
8
+ import SiteFooterLink from './SiteFooterLink.svelte';
9
+ import {
10
+ mdiBookshelf,
11
+ mdiChartGantt,
12
+ mdiCubeOutline,
13
+ mdiHomeSearchOutline,
14
+ mdiKey,
15
+ mdiOfficeBuildingOutline,
16
+ mdiScriptTextOutline,
17
+ mdiServerOutline,
18
+ mdiShoppingOutline,
19
+ } from '@mdi/js';
20
+ import { siAndroid, siApple, siDiscord, siGithub, siReddit } from 'simple-icons';
21
21
  </script>
22
22
 
23
- <div class="bg-dark/10 mt-16 rounded-t-3xl p-8">
24
- <div class="mx-auto max-w-(--breakpoint-lg) xl:py-8">
25
- <Stack gap={8}>
26
- <div class="place-center grid grid-cols-2 gap-8 md:grid-cols-3 lg:grid-cols-5">
27
- <Stack>
28
- <Heading size="tiny">Social</Heading>
29
- <SiteFooterLink href={Constants.Socials.Github} icon={siGithub} text="GitHub" />
30
- <SiteFooterLink href={Constants.Socials.Discord} icon={siDiscord} text="Discord" />
31
- <SiteFooterLink href={Constants.Socials.Reddit} icon={siReddit} text="Reddit" />
32
- </Stack>
23
+ <div class="mt-16 rounded-t-3xl bg-gray-100 p-8 dark:bg-neutral-900">
24
+ <div class="mx-auto max-w-(--breakpoint-lg) xl:py-8">
25
+ <Stack gap={8}>
26
+ <div class="place-center grid grid-cols-2 gap-8 md:grid-cols-3 lg:grid-cols-5">
27
+ <Stack>
28
+ <Heading size="tiny">Social</Heading>
29
+ <SiteFooterLink href={Constants.Socials.Github} icon={siGithub} text="GitHub" />
30
+ <SiteFooterLink href={Constants.Socials.Discord} icon={siDiscord} text="Discord" />
31
+ <SiteFooterLink href={Constants.Socials.Reddit} icon={siReddit} text="Reddit" />
32
+ </Stack>
33
33
 
34
- <Stack>
35
- <Heading size="tiny">Download</Heading>
36
- <SiteFooterLink href={Constants.Get.Android} icon={siAndroid} text="Android" />
37
- <SiteFooterLink href={Constants.Get.iOS} icon={siApple} text="iOS" />
38
- <SiteFooterLink
39
- href={Constants.Get.DockerCompose}
40
- icon={mdiServerOutline}
41
- text="Server"
42
- />
43
- </Stack>
34
+ <Stack>
35
+ <Heading size="tiny">Download</Heading>
36
+ <SiteFooterLink href={Constants.Get.Android} icon={siAndroid} text="Android" />
37
+ <SiteFooterLink href={Constants.Get.iOS} icon={siApple} text="iOS" />
38
+ <SiteFooterLink href={Constants.Get.DockerCompose} icon={mdiServerOutline} text="Server" />
39
+ </Stack>
44
40
 
45
- <Stack>
46
- <Heading size="tiny">Company</Heading>
47
- <SiteFooterLink
48
- href={Constants.Socials.Futo}
49
- icon={mdiOfficeBuildingOutline}
50
- text="FUTO"
51
- />
52
- <SiteFooterLink href={Constants.Sites.Buy} icon={mdiKey} text="Purchase" />
53
- <SiteFooterLink href={Constants.Sites.Store} icon={mdiShoppingOutline} text="Merch" />
54
- </Stack>
41
+ <Stack>
42
+ <Heading size="tiny">Company</Heading>
43
+ <SiteFooterLink href={Constants.Socials.Futo} icon={mdiOfficeBuildingOutline} text="FUTO" />
44
+ <SiteFooterLink href={Constants.Sites.Buy} icon={mdiKey} text="Purchase" />
45
+ <SiteFooterLink href={Constants.Sites.Store} icon={mdiShoppingOutline} text="Merch" />
46
+ </Stack>
55
47
 
56
- <Stack>
57
- <Heading size="tiny">Sites</Heading>
58
- <SiteFooterLink
59
- href={Constants.Sites.Docs}
60
- icon={mdiScriptTextOutline}
61
- text="Documentation"
62
- />
63
- <SiteFooterLink href={Constants.Sites.My} icon={mdiHomeSearchOutline} text="My Immich" />
64
- <SiteFooterLink href={Constants.Sites.Api} icon={mdiCubeOutline} text="Immich API" />
65
- </Stack>
48
+ <Stack>
49
+ <Heading size="tiny">Sites</Heading>
50
+ <SiteFooterLink href={Constants.Sites.Docs} icon={mdiScriptTextOutline} text="Documentation" />
51
+ <SiteFooterLink href={Constants.Sites.My} icon={mdiHomeSearchOutline} text="My Immich" />
52
+ <SiteFooterLink href={Constants.Sites.Api} icon={mdiCubeOutline} text="Immich API" />
53
+ </Stack>
66
54
 
67
- <Stack>
68
- <Heading size="tiny">Miscellaneous</Heading>
69
- <SiteFooterLink href={Constants.Pages.Roadmap} icon={mdiChartGantt} text="Roadmap" />
70
- <SiteFooterLink
71
- href={Constants.Pages.CursedKnowledge}
72
- icon={mdiBookshelf}
73
- text="Cursed Knowledge"
74
- />
75
- </Stack>
76
- </div>
77
- <VStack class="text-center">
78
- <Text size="large">This project is available under GNU AGPL v3 license.</Text>
79
- <Text color="muted" variant="italic">Privacy should not be a luxury</Text>
80
- <ThemeSwitcher color="secondary" />
81
- </VStack>
82
- </Stack>
83
- </div>
55
+ <Stack>
56
+ <Heading size="tiny">Miscellaneous</Heading>
57
+ <SiteFooterLink href={Constants.Pages.Roadmap} icon={mdiChartGantt} text="Roadmap" />
58
+ <SiteFooterLink href={Constants.Pages.CursedKnowledge} icon={mdiBookshelf} text="Cursed Knowledge" />
59
+ </Stack>
60
+ </div>
61
+ <VStack class="text-center">
62
+ <Text size="large">This project is available under GNU AGPL v3 license.</Text>
63
+ <Text color="muted" variant="italic">Privacy should not be a luxury</Text>
64
+ <ThemeSwitcher color="secondary" />
65
+ </VStack>
66
+ </Stack>
67
+ </div>
84
68
  </div>
@@ -1,21 +1,21 @@
1
1
  <script lang="ts">
2
- import HStack from '../components/Stack/HStack.svelte';
3
- import Icon from '../components/Icon/Icon.svelte';
4
- import Link from '../components/Link/Link.svelte';
5
- import Text from '../components/Text/Text.svelte';
2
+ import HStack from '../components/Stack/HStack.svelte';
3
+ import Icon from '../components/Icon/Icon.svelte';
4
+ import Link from '../components/Link/Link.svelte';
5
+ import Text from '../components/Text/Text.svelte';
6
6
 
7
- type Props = {
8
- href: string;
9
- icon: string | { path: string };
10
- text: string;
11
- };
7
+ type Props = {
8
+ href: string;
9
+ icon: string | { path: string };
10
+ text: string;
11
+ };
12
12
 
13
- let { href, icon, text }: Props = $props();
13
+ let { href, icon, text }: Props = $props();
14
14
  </script>
15
15
 
16
16
  <Link {href} external>
17
- <HStack>
18
- <Icon {icon} size="1.5em" />
19
- <Text>{text}</Text>
20
- </HStack>
17
+ <HStack>
18
+ <Icon {icon} size="1.5em" />
19
+ <Text>{text}</Text>
20
+ </HStack>
21
21
  </Link>