web: partially pre-render web for page metadata

This commit is contained in:
wukko 2024-09-03 14:39:52 +06:00
commit cef90219e0
No known key found for this signature in database
GPG key ID: 3E30B3F26C7B4AA2
12 changed files with 142 additions and 68 deletions

View file

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="viewport-fit=cover, width=device-width, height=device-height, initial-scale=1, maximum-scale=1">
<title>cobalt</title>
%sveltekit.head%
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
@ -18,18 +18,45 @@
<link crossorigin="use-credentials" rel="manifest" href="%sveltekit.assets%/manifest.json">
%sveltekit.head%
</head>
<style>
body {
background-color: black;
}
@media (prefers-color-scheme: light) {
body {
<noscript>
<style>
#cobalt { opacity: 1 !important }
</style>
</noscript>
<style>
body, #body-dark {
background-color: black;
}
#body-light {
background-color: white;
}
}
</style>
@media (prefers-color-scheme: light) {
body {
background-color: white;
}
}
#cobalt {
opacity: 0;
}
#cobalt.loaded {
opacity: 1;
transition: opacity .2s ease-out;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
let settings = localStorage.getItem('settings'), theme;
if (settings && ({ theme } = JSON.parse(settings)?.appearance)) {
document.body.id = `body-${theme}`;
}
});
</script>
</head>
<body data-sveltekit-preload-data="hover" data-sveltekit-preload-code="eager">
<div style="display: contents">
%sveltekit.body%

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { page } from "$app/stores";
import { goto } from "$app/navigation";
import { browser } from "$app/environment";
import { SvelteComponent, tick } from "svelte";
import env from "$lib/env";
@ -43,7 +44,7 @@
};
$: linkFromHash = $page.url.hash.replace("#", "") || "";
$: linkFromQuery = $page.url.searchParams.get("u") || "";
$: linkFromQuery = (browser ? $page.url.searchParams.get("u") : 0) || "";
$: if (linkFromHash || linkFromQuery) {
if (validLink(linkFromHash)) {

View file

@ -1,45 +1,66 @@
const ua = navigator.userAgent.toLowerCase();
const iPhone = ua.includes("iphone os");
const iPad = !iPhone && ua.includes("mac os") && navigator.maxTouchPoints > 0;
const iOS = iPhone || iPad;
const android = ua.includes("android") || ua.includes("diordna");
const mobile = iOS || android;
const language = navigator.language.toLowerCase().slice(0, 2);
const installed = window.matchMedia('(display-mode: standalone)').matches;
const reducedMotion = window.matchMedia(`(prefers-reduced-motion: reduce)`).matches;
const reducedTransparency = window.matchMedia(`(prefers-reduced-transparency: reduce)`).matches;
import { browser } from "$app/environment";
const app = {
is: {
installed
installed: false,
}
}
const device = {
is: {
iPhone: false,
iPad: false,
iOS: false,
android: false,
mobile: false,
},
prefers: {
language: "en",
reducedMotion: false,
reducedTransparency: false,
},
supports: {
share: false,
directDownload: false,
},
userAgent: "sveltekit server",
}
if (browser) {
const ua = navigator.userAgent.toLowerCase();
const iPhone = ua.includes("iphone os");
const iPad = !iPhone && ua.includes("mac os") && navigator.maxTouchPoints > 0;
const iOS = iPhone || iPad;
const android = ua.includes("android") || ua.includes("diordna");
const installed = window.matchMedia('(display-mode: standalone)').matches;
app.is = {
installed,
};
device.is = {
iPhone,
iPad,
iOS,
android,
mobile,
},
prefers: {
language,
reducedMotion,
reducedTransparency,
},
supports: {
mobile: iOS || android,
};
device.prefers = {
language: navigator.language.toLowerCase().slice(0, 2) || "en",
reducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)').matches,
reducedTransparency: window.matchMedia('(prefers-reduced-transparency: reduce)').matches,
};
device.supports = {
share: navigator.share !== undefined,
directDownload: !(installed && iOS),
},
userAgent: navigator.userAgent,
};
device.userAgent = navigator.userAgent;
}
export { device, app };

View file

@ -1,11 +1,20 @@
import { env } from "$env/dynamic/public";
import * as _env from "$env/static/public";
const getEnv = (_key: string) => {
const env = _env as Record<string, string | undefined>;
const key = `PUBLIC_${_key}`;
if (key in env) {
return env[key];
}
}
const variables = {
HOST: env.PUBLIC_HOST,
PLAUSIBLE_HOST: env.PUBLIC_PLAUSIBLE_HOST,
PLAUSIBLE_ENABLED: env.PUBLIC_HOST && env.PUBLIC_PLAUSIBLE_HOST,
DEFAULT_API: env.PUBLIC_DEFAULT_API,
TURNSTILE_KEY: env.PUBLIC_TURNSTILE_KEY,
HOST: getEnv('HOST'),
PLAUSIBLE_HOST: getEnv('PLAUSIBLE_HOST'),
PLAUSIBLE_ENABLED: getEnv('HOST') && getEnv('PLAUSIBLE_HOST'),
DEFAULT_API: getEnv('DEFAULT_API'),
TURNSTILE_KEY: getEnv('TURNSTILE_KEY'),
}
const contacts = {

View file

@ -2,6 +2,7 @@ import mime from "mime";
import LibAV, { type LibAV as LibAVInstance } from "@imput/libav.js-remux-cli";
import type { FFmpegProgressCallback, FFmpegProgressEvent, FFmpegProgressStatus, FileInfo, RenderParams } from "./types/libav";
import type { FfprobeData } from "fluent-ffmpeg";
import { browser } from "$app/environment";
export default class LibAVWrapper {
libav: Promise<LibAVInstance> | null;
@ -10,12 +11,12 @@ export default class LibAVWrapper {
constructor(onProgress?: FFmpegProgressCallback) {
this.libav = null;
this.concurrency = Math.min(4, navigator.hardwareConcurrency);
this.concurrency = Math.min(4, browser ? navigator.hardwareConcurrency : 0);
this.onProgress = onProgress;
}
async init() {
if (!this.libav) {
if (this.concurrency && !this.libav) {
this.libav = LibAV.LibAV({
yesthreads: true,
base: '/_libav'

View file

@ -1,4 +1,5 @@
import { derived, readable, type Updater } from 'svelte/store';
import { browser } from '$app/environment';
import { merge } from 'ts-deepmerge';
import type {
@ -43,6 +44,9 @@ const migrate = (settings: AllPartialSettingsWithSchema): PartialSettings => {
const loadFromStorage = () => {
if (!browser)
return {};
const settings = localStorage.getItem('settings');
if (!settings) {
const migrated = migrateOldSettings();

View file

@ -1,4 +1,5 @@
import { readable, derived, type Readable } from 'svelte/store';
import { browser } from '$app/environment';
import settings from '$lib/state/settings';
import { themeOptions } from '$lib/types/settings';
@ -7,19 +8,25 @@ type Theme = typeof themeOptions[number];
let set: (_: Theme) => void;
const browserPreference = () =>
window.matchMedia('(prefers-color-scheme: light)')
.matches ? 'light' : 'dark';
const browserPreference = () => {
if (!browser || window.matchMedia('(prefers-color-scheme: light)').matches) {
return 'light';
}
return 'dark'
}
const browserPreferenceReadable = readable(
browserPreference(),
_set => { set = _set }
)
const matchMedia = window.matchMedia('(prefers-color-scheme: dark)');
if (browser) {
const matchMedia = window.matchMedia('(prefers-color-scheme: dark)');
if (matchMedia.addEventListener) {
matchMedia.addEventListener('change', () => set(browserPreference()));
if (matchMedia.addEventListener) {
matchMedia.addEventListener('change', () => set(browserPreference()));
}
}
export default derived(

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { afterNavigate } from "$app/navigation";
import { updated } from "$app/stores";
import { browser } from "$app/environment";
import env from "$lib/env";
import settings from "$lib/state/settings";
@ -46,9 +47,10 @@
{/if}
</svelte:head>
<div style="display: contents" data-theme={$currentTheme} lang={$locale}>
<div style="display: contents" data-theme={browser ? $currentTheme : undefined} lang={$locale}>
<div
id="cobalt"
class:loaded={browser}
data-iphone={device.is.iPhone}
data-reduce-motion={reduceMotion}
data-reduce-transparency={reduceTransparency}
@ -222,10 +224,6 @@
overscroll-behavior-y: none;
}
:global(body) {
background-color: var(--secondary);
}
#cobalt {
position: fixed;
height: 100%;

View file

@ -1,5 +1,5 @@
export const prerender = true;
export const ssr = false;
export const ssr = true;
import { browser } from '$app/environment';

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { page } from "$app/stores";
import { browser } from "$app/environment";
import settings from "$lib/state/settings";
import { version } from "$lib/version";
@ -38,7 +39,7 @@
$: isMobile = screenWidth <= 750;
$: isHome = $page.url.pathname === "/settings";
$: {
if (!isMobile && isHome) {
if (browser && !isMobile && isHome) {
goto(defaultSettingsPage(), { replaceState: true });
}
}

View file

@ -1,16 +1,17 @@
<script lang="ts">
import { onMount } from "svelte";
import { goto } from "$app/navigation";
import { device, app } from "$lib/device";
import { version } from "$lib/version";
import settings, { storedSettings } from "$lib/state/settings";
import { goto } from "$app/navigation";
import { defaultSettingsPage } from "$lib/settings/defaults";
$: {
onMount(() => {
if (!$settings.advanced.debug) {
goto(defaultSettingsPage(), { replaceState: true });
}
}
});
</script>
{#if $settings.advanced.debug}

View file

@ -1,10 +1,11 @@
<script lang="ts">
import { t } from "$lib/i18n/translations";
import { page } from "$app/stores";
import { browser } from "$app/environment";
import { t } from "$lib/i18n/translations";
import { getAllChangelogs } from "$lib/changelogs";
import type { ChangelogImport } from "$lib/types/changelogs";
import type { Optional } from "$lib/types/generic";
import type { ChangelogImport } from "$lib/types/changelogs";
import ChangelogEntry from "$components/changelog/ChangelogEntry.svelte";
@ -36,7 +37,10 @@
page: changelogs[version]() as Promise<ChangelogImport>,
};
window.location.hash = version;
if (browser) {
window.location.hash = version;
}
await changelog.page;
};