mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
fix: script in app.html
This commit is contained in:
parent
7e9fb5df5e
commit
7e433d6194
@ -66,37 +66,8 @@
|
|||||||
background-color: black;
|
background-color: black;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
|
||||||
/**
|
|
||||||
* Prevent FOUC on page load.
|
|
||||||
*/
|
|
||||||
const colorThemeKeyName = 'color-theme';
|
|
||||||
|
|
||||||
let theme = localStorage.getItem(colorThemeKeyName);
|
%app.fouc%
|
||||||
if (!theme) {
|
|
||||||
theme = { value: 'light', system: true };
|
|
||||||
} else if (theme === 'dark' || theme === 'light') {
|
|
||||||
theme = { value: theme, system: false };
|
|
||||||
localStorage.setItem(colorThemeKeyName, JSON.stringify(theme));
|
|
||||||
} else {
|
|
||||||
theme = JSON.parse(theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
let themeValue = theme.value;
|
|
||||||
if (theme.system) {
|
|
||||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
||||||
themeValue = 'dark';
|
|
||||||
} else {
|
|
||||||
themeValue = 'light';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (themeValue === 'light') {
|
|
||||||
document.documentElement.classList.remove('dark');
|
|
||||||
} else {
|
|
||||||
document.documentElement.classList.add('dark');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/custom.css" />
|
<link rel="stylesheet" href="/custom.css" />
|
||||||
</head>
|
</head>
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
import overpass from '$lib/assets/fonts/overpass/Overpass.ttf?url';
|
import overpass from '$lib/assets/fonts/overpass/Overpass.ttf?url';
|
||||||
import overpassMono from '$lib/assets/fonts/overpass/OverpassMono.ttf?url';
|
import overpassMono from '$lib/assets/fonts/overpass/OverpassMono.ttf?url';
|
||||||
|
import { transpileFile } from '$lib/utils';
|
||||||
|
import fouc from '$lib/utils/fouc?raw';
|
||||||
|
import theme from '$lib/utils/theme?raw';
|
||||||
import type { Handle } from '@sveltejs/kit';
|
import type { Handle } from '@sveltejs/kit';
|
||||||
|
|
||||||
// only used during the build to replace the variables from app.html
|
// only used during the build to replace the variables from app.html
|
||||||
export const handle = (async ({ event, resolve }) => {
|
export const handle = (async ({ event, resolve }) => {
|
||||||
return resolve(event, {
|
return resolve(event, {
|
||||||
transformPageChunk: ({ html }) => {
|
transformPageChunk: ({ html }) => {
|
||||||
return html.replace('%app.font%', overpass).replace('%app.monofont%', overpassMono);
|
const themePrepared = theme.replaceAll(/^export\s+/gm, '');
|
||||||
|
const foucPrepared = fouc.replaceAll(/^import.*$/gm, themePrepared);
|
||||||
|
const scriptFouc = `<script>${transpileFile(foucPrepared)}</script>`;
|
||||||
|
|
||||||
|
return html
|
||||||
|
.replace('%app.font%', overpass)
|
||||||
|
.replace('%app.monofont%', overpassMono)
|
||||||
|
.replace('%app.fouc%', scriptFouc);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}) satisfies Handle;
|
}) satisfies Handle;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import OnboardingCard from './onboarding-card.svelte';
|
import OnboardingCard from './onboarding-card.svelte';
|
||||||
import { colorTheme } from '$lib/stores/preferences.store';
|
import { colorTheme } from '$lib/stores/preferences.store';
|
||||||
import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths';
|
import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths';
|
||||||
import { Theme } from '$lib/constants';
|
import { Theme } from '$lib/utils/theme';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
export let onDone: () => void;
|
export let onDone: () => void;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import logoLightUrl from '$lib/assets/immich-logo-inline-light.svg';
|
import logoLightUrl from '$lib/assets/immich-logo-inline-light.svg';
|
||||||
import logoNoText from '$lib/assets/immich-logo.svg';
|
import logoNoText from '$lib/assets/immich-logo.svg';
|
||||||
import { content as alternativeLogo } from '$lib/assets/immich-logo.json';
|
import { content as alternativeLogo } from '$lib/assets/immich-logo.json';
|
||||||
import { Theme } from '$lib/constants';
|
import { Theme } from '$lib/utils/theme';
|
||||||
import { colorTheme } from '$lib/stores/preferences.store';
|
import { colorTheme } from '$lib/stores/preferences.store';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import type { HTMLImgAttributes } from 'svelte/elements';
|
import type { HTMLImgAttributes } from 'svelte/elements';
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from '$lib/components/elements/icon.svelte';
|
import Icon from '$lib/components/elements/icon.svelte';
|
||||||
import { Theme } from '$lib/constants';
|
|
||||||
import { colorTheme, mapSettings } from '$lib/stores/preferences.store';
|
import { colorTheme, mapSettings } from '$lib/stores/preferences.store';
|
||||||
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
|
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
|
||||||
|
import { Theme } from '$lib/utils/theme';
|
||||||
import { getServerConfig, type MapMarkerResponseDto } from '@immich/sdk';
|
import { getServerConfig, type MapMarkerResponseDto } from '@immich/sdk';
|
||||||
import mapboxRtlUrl from '@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js?url';
|
import mapboxRtlUrl from '@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.min.js?url';
|
||||||
import { mdiCog, mdiMap, mdiMapMarker } from '@mdi/js';
|
import { mdiCog, mdiMap, mdiMapMarker } from '@mdi/js';
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths';
|
import { moonPath, moonViewBox, sunPath, sunViewBox } from '$lib/assets/svg-paths';
|
||||||
import CircleIconButton, { type Padding } from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
import CircleIconButton, { type Padding } from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
import { Theme } from '$lib/constants';
|
|
||||||
import { colorTheme, handleToggleTheme } from '$lib/stores/preferences.store';
|
import { colorTheme, handleToggleTheme } from '$lib/stores/preferences.store';
|
||||||
|
import { Theme } from '$lib/utils/theme';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
$: icon = $colorTheme.value === Theme.LIGHT ? moonPath : sunPath;
|
$: icon = $colorTheme.value === Theme.LIGHT ? moonPath : sunPath;
|
||||||
|
@ -102,12 +102,6 @@ export const timeBeforeShowLoadingSpinner: number = 100;
|
|||||||
|
|
||||||
export const timeDebounceOnSearch: number = 300;
|
export const timeDebounceOnSearch: number = 300;
|
||||||
|
|
||||||
// should be the same values as the ones in the app.html
|
|
||||||
export enum Theme {
|
|
||||||
LIGHT = 'light',
|
|
||||||
DARK = 'dark',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fallbackLocale = {
|
export const fallbackLocale = {
|
||||||
code: 'en-US',
|
code: 'en-US',
|
||||||
name: 'English (US)',
|
name: 'English (US)',
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { Theme, defaultLang } from '$lib/constants';
|
import { defaultLang } from '$lib/constants';
|
||||||
import { getPreferredLocale } from '$lib/utils/i18n';
|
import { getPreferredLocale } from '$lib/utils/i18n';
|
||||||
|
import { colorThemeKeyName, Theme, type ThemeSetting } from '$lib/utils/theme';
|
||||||
import { persisted } from 'svelte-local-storage-store';
|
import { persisted } from 'svelte-local-storage-store';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
export interface ThemeSetting {
|
|
||||||
value: Theme;
|
|
||||||
system: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const handleToggleTheme = () => {
|
export const handleToggleTheme = () => {
|
||||||
const theme = get(colorTheme);
|
const theme = get(colorTheme);
|
||||||
theme.value = theme.value === Theme.DARK ? Theme.LIGHT : Theme.DARK;
|
theme.value = theme.value === Theme.DARK ? Theme.LIGHT : Theme.DARK;
|
||||||
@ -24,8 +20,7 @@ const initTheme = (): ThemeSetting => {
|
|||||||
|
|
||||||
const initialTheme = initTheme();
|
const initialTheme = initTheme();
|
||||||
|
|
||||||
// The 'color-theme' key is also used by app.html to prevent FOUC on page load.
|
export const colorTheme = persisted<ThemeSetting>(colorThemeKeyName, initialTheme, {
|
||||||
export const colorTheme = persisted<ThemeSetting>('color-theme', initialTheme, {
|
|
||||||
serializer: {
|
serializer: {
|
||||||
parse: (text: string): ThemeSetting => {
|
parse: (text: string): ThemeSetting => {
|
||||||
const parsedText: ThemeSetting = JSON.parse(text);
|
const parsedText: ThemeSetting = JSON.parse(text);
|
||||||
|
@ -25,6 +25,7 @@ import { mdiCogRefreshOutline, mdiDatabaseRefreshOutline, mdiHeadSyncOutline, md
|
|||||||
import { sortBy } from 'lodash-es';
|
import { sortBy } from 'lodash-es';
|
||||||
import { init, register, t } from 'svelte-i18n';
|
import { init, register, t } from 'svelte-i18n';
|
||||||
import { derived, get } from 'svelte/store';
|
import { derived, get } from 'svelte/store';
|
||||||
|
import { ModuleKind, transpileModule } from 'typescript';
|
||||||
|
|
||||||
interface DownloadRequestOptions<T = unknown> {
|
interface DownloadRequestOptions<T = unknown> {
|
||||||
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||||
@ -337,3 +338,10 @@ export const suggestDuplicateByFileSize = (assets: AssetResponseDto[]): AssetRes
|
|||||||
|
|
||||||
// eslint-disable-next-line unicorn/prefer-code-point
|
// eslint-disable-next-line unicorn/prefer-code-point
|
||||||
export const decodeBase64 = (data: string) => Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
|
export const decodeBase64 = (data: string) => Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
|
||||||
|
|
||||||
|
export const transpileFile = (content: string) => {
|
||||||
|
const result = transpileModule(content, {
|
||||||
|
compilerOptions: { module: ModuleKind.ES2020, removeComments: true },
|
||||||
|
});
|
||||||
|
return result.outputText;
|
||||||
|
};
|
||||||
|
12
web/src/lib/utils/fouc.ts
Normal file
12
web/src/lib/utils/fouc.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* we don't want to deal with imports so that import should be
|
||||||
|
* replaced by the actual content of the file before fouc.ts is transpiled
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import { colorThemeKeyName, Theme, type ThemeSetting } from '$lib/utils/theme';
|
||||||
|
|
||||||
|
const storedTheme = localStorage.getItem(colorThemeKeyName);
|
||||||
|
const theme: ThemeSetting = storedTheme ? JSON.parse(storedTheme) : { value: Theme.LIGHT, system: true };
|
||||||
|
const themeValue = theme.system && window.matchMedia('(prefers-color-scheme: dark)').matches ? Theme.DARK : theme.value;
|
||||||
|
|
||||||
|
document.documentElement.classList.toggle('dark', themeValue === Theme.DARK);
|
11
web/src/lib/utils/theme.ts
Normal file
11
web/src/lib/utils/theme.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export interface ThemeSetting {
|
||||||
|
value: Theme;
|
||||||
|
system: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Theme {
|
||||||
|
LIGHT = 'light',
|
||||||
|
DARK = 'dark',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const colorThemeKeyName = 'color-theme';
|
@ -8,11 +8,9 @@
|
|||||||
import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte';
|
import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte';
|
||||||
import UploadPanel from '$lib/components/shared-components/upload-panel.svelte';
|
import UploadPanel from '$lib/components/shared-components/upload-panel.svelte';
|
||||||
import VersionAnnouncementBox from '$lib/components/shared-components/version-announcement-box.svelte';
|
import VersionAnnouncementBox from '$lib/components/shared-components/version-announcement-box.svelte';
|
||||||
import { Theme } from '$lib/constants';
|
import { Theme, type ThemeSetting } from '$lib/utils/theme';
|
||||||
import { colorTheme, handleToggleTheme, type ThemeSetting } from '$lib/stores/preferences.store';
|
import { colorTheme, handleToggleTheme } from '$lib/stores/preferences.store';
|
||||||
|
|
||||||
import { serverConfig } from '$lib/stores/server-config.store';
|
import { serverConfig } from '$lib/stores/server-config.store';
|
||||||
|
|
||||||
import { user } from '$lib/stores/user.store';
|
import { user } from '$lib/stores/user.store';
|
||||||
import { closeWebsocketConnection, openWebsocketConnection } from '$lib/stores/websocket';
|
import { closeWebsocketConnection, openWebsocketConnection } from '$lib/stores/websocket';
|
||||||
import { copyToClipboard, setKey } from '$lib/utils';
|
import { copyToClipboard, setKey } from '$lib/utils';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user