mirror of
https://github.com/BradNut/personal-website-sveltekit
synced 2025-09-08 23:20:18 +00:00
Merge branch 'master' of github.com:BradNut/personal-website-sveltekit into redis
This commit is contained in:
commit
e9ee47de56
22 changed files with 368 additions and 30 deletions
20
.env.example
20
.env.example
|
|
@ -1,13 +1,15 @@
|
|||
WALLABAG_MAX_ARTICLES=30
|
||||
WALLABAG_CLIENT_ID=
|
||||
WALLABAG_CLIENT_SECRET=
|
||||
WALLABAG_USERNAME=
|
||||
WALLABAG_PASSWORD=
|
||||
WALLABAG_URL="https://app.wallabag.it"
|
||||
PUBLIC_SITE_URL=
|
||||
WALLABAG_MAX_PAGES=5
|
||||
NODE_VERSION=18.12.1
|
||||
WALLABAG_CLIENT_ID=''
|
||||
WALLABAG_CLIENT_SECRET=''
|
||||
WALLABAG_USERNAME=''
|
||||
WALLABAG_PASSWORD=''
|
||||
WALLABAG_URL=""
|
||||
PUBLIC_SITE_URL=""
|
||||
PUBLIC_UMAMI_DO_NOT_TRACK=true
|
||||
PUBLIC_UMAMI_URL=
|
||||
PUBLIC_UMAMI_ID=
|
||||
PUBLIC_UMAMI_URL=""
|
||||
PUBLIC_UMAMI_ID=""
|
||||
PAGE_SIZE=6
|
||||
USE_REDIS_CACHE=true
|
||||
REDIS_URI=redis://{username}:{password}@{redisURL}:{redisPort}
|
||||
REDIS_URI=
|
||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"Bandcamp",
|
||||
"bradleyshellnut",
|
||||
"iconify",
|
||||
"Mullvad",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@iconify-icons/material-symbols": "^1.2.27",
|
||||
"@iconify-icons/mdi": "^1.2.41",
|
||||
"@iconify-icons/radix-icons": "^1.2.8",
|
||||
"@iconify-icons/simple-icons": "^1.2.42",
|
||||
"@leveluptuts/svelte-side-menu": "^1.0.5",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ lockfileVersion: 5.4
|
|||
|
||||
specifiers:
|
||||
'@iconify-icons/material-symbols': ^1.2.27
|
||||
'@iconify-icons/mdi': ^1.2.41
|
||||
'@iconify-icons/radix-icons': ^1.2.8
|
||||
'@iconify-icons/simple-icons': ^1.2.42
|
||||
'@leveluptuts/svelte-side-menu': ^1.0.5
|
||||
|
|
@ -46,6 +47,7 @@ dependencies:
|
|||
|
||||
devDependencies:
|
||||
'@iconify-icons/material-symbols': 1.2.27
|
||||
'@iconify-icons/mdi': 1.2.41
|
||||
'@iconify-icons/radix-icons': 1.2.8
|
||||
'@iconify-icons/simple-icons': 1.2.42
|
||||
'@leveluptuts/svelte-side-menu': 1.0.5
|
||||
|
|
@ -569,6 +571,12 @@ packages:
|
|||
'@iconify/types': 2.0.0
|
||||
dev: true
|
||||
|
||||
/@iconify-icons/mdi/1.2.41:
|
||||
resolution: {integrity: sha512-duqTSmY0H+e/LdSZD5B8PxnJfdfh6qdLVnrI6klHGSSykz23d1KdvoPpfFpgF8mWWDm4UlHIO+rrvsqMLEb3NQ==}
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
dev: true
|
||||
|
||||
/@iconify-icons/radix-icons/1.2.8:
|
||||
resolution: {integrity: sha512-bZqRIbeqe6yNSLPgcQOyOl86C2P/apaY9Dq/BddWxitN8olbTp2MLuDJenNF+wxbQGgKkQFfm3vb6Z+4Nbhk+g==}
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { PUBLIC_UMAMI_DO_NOT_TRACK, PUBLIC_UMAMI_URL, PUBLIC_UMAMI_ID } from '$env/static/public';
|
||||
const analyticsURL = `https://${PUBLIC_UMAMI_URL}/umami.js`;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
@ -9,6 +8,6 @@
|
|||
defer
|
||||
data-website-id={PUBLIC_UMAMI_ID}
|
||||
data-do-not-track={PUBLIC_UMAMI_DO_NOT_TRACK}
|
||||
src={analyticsURL}
|
||||
src={PUBLIC_UMAMI_URL}
|
||||
></script>
|
||||
</svelte:head>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
<script lang="ts">
|
||||
import OpenInNew from '@iconify-icons/mdi/open-in-new';
|
||||
import type { Article } from "$root/lib/types/article";
|
||||
|
||||
export let articles: Article[];
|
||||
export let totalArticles: number;
|
||||
export let compact: boolean = false;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h2>Favorite Articles</h2>
|
||||
<div style="display: grid;">
|
||||
{#each articles as article}
|
||||
<article class="articleStyles card">
|
||||
<section>
|
||||
<h3>
|
||||
<a
|
||||
target="_blank"
|
||||
aria-label={`Link to ${article.title}`}
|
||||
href={article.url.toString()}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{#if compact}
|
||||
{article.title.substring(0, 50).trim()}…
|
||||
{:else}
|
||||
{article.title}
|
||||
{/if}
|
||||
<iconify-icon icon={OpenInNew} width="24" height="24" role="img" title="Open Article Externally" />
|
||||
</a>{' '}
|
||||
</h3>
|
||||
</section>
|
||||
<section>
|
||||
<p>Reading time: {article.reading_time} minutes</p>
|
||||
<div class="tagsStyles">
|
||||
<p>Tags:</p>
|
||||
{#each article.tags as tag}
|
||||
<p>{tag}</p>
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="moreArticlesStyles">
|
||||
<a href="/articles">{`${totalArticles} more articles`}</a>
|
||||
<a href="/articles" aria-label={`${totalArticles} more articles`}>
|
||||
<iconify-icon icon="material-symbols:arrow-right-alt-rounded"></iconify-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="postcss">
|
||||
.articleStyles {
|
||||
display: grid;
|
||||
grid-template-rows: repeat(1fr, 3);
|
||||
gap: 0.5rem;
|
||||
margin: 1.5rem 0;
|
||||
|
||||
a {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.4rem 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tagsStyles {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
|
||||
p + p {
|
||||
background-color: var(--linkHover);
|
||||
color: var(--buttonTextColor);
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin: 0.5rem;
|
||||
border-radius: 2px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.moreArticlesStyles {
|
||||
margin: 1.7rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
a {
|
||||
font-size: 2rem;
|
||||
svg {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
90
src/lib/components/bandcamp/index.svelte
Normal file
90
src/lib/components/bandcamp/index.svelte
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<script lang="ts">
|
||||
import type { Album } from "$root/lib/types/album";
|
||||
|
||||
|
||||
export let albums: Album[];
|
||||
const displayAlbums =
|
||||
albums?.length > 6 ? albums.slice(0, 6) : albums;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h2>Currently listening to:</h2>
|
||||
<div class="albumsStyles">
|
||||
{#each displayAlbums as album}
|
||||
<div>
|
||||
<figure>
|
||||
<a
|
||||
title={`Link to ${album.title} by ${album.artist}`}
|
||||
target="_blank"
|
||||
href={album.url}
|
||||
rel="noreferrer"
|
||||
>
|
||||
<img
|
||||
src={`https://images.weserv.nl/?url=${encodeURIComponent(
|
||||
album.artwork
|
||||
)}&w=230&h=230`}
|
||||
alt={`Album art for ${album.title}`}
|
||||
/>
|
||||
</a>
|
||||
</figure>
|
||||
<a
|
||||
target="_blank"
|
||||
href={album.url}
|
||||
rel="noreferrer"
|
||||
>
|
||||
<h3>{album.title.length > 20 ? `${album.title.slice(0, 20)}...` : album.title}</h3>
|
||||
<h3>by {album.artist}</h3>
|
||||
</a>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.albumsStyles {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(auto, 1fr));
|
||||
gap: 1rem;
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
grid-template-columns: repeat(2, minmax(150px, 1fr));
|
||||
img {
|
||||
width: 230px;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
height: 500px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
}
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--lightGrey) var(--darkGrey);
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--darkGrey);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--lightGrey);
|
||||
border-radius: 6px;
|
||||
border: 3px solid var(--darkGrey);
|
||||
}
|
||||
grid-template-columns: minmax(230px, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.albumStyles {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: 550px) {
|
||||
grid-template-columns: 0.75fr 0.75fr;
|
||||
font-size: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
6
src/lib/types/album.ts
Normal file
6
src/lib/types/album.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export type Album = {
|
||||
url: string;
|
||||
artwork: string;
|
||||
title: string;
|
||||
artist: string;
|
||||
};
|
||||
40
src/lib/util/fetchBandcampAlbums.ts
Normal file
40
src/lib/util/fetchBandcampAlbums.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// import * as htmlparser2 from 'htmlparser2';
|
||||
import scrapeIt from 'scrape-it';
|
||||
import type { Album } from '../types/album';
|
||||
|
||||
export async function fetchBandcampAlbums() {
|
||||
try {
|
||||
const { data } = await scrapeIt('https://bandcamp.com/royvalentine', {
|
||||
collectionItems: {
|
||||
listItem: '.collection-item-container',
|
||||
data: {
|
||||
url: {
|
||||
selector: '.collection-title-details > a.item-link',
|
||||
attr: 'href'
|
||||
},
|
||||
artwork: {
|
||||
selector: 'div.collection-item-art-container a img',
|
||||
attr: 'src'
|
||||
},
|
||||
title: {
|
||||
selector: 'span.item-link-alt > div.collection-item-title'
|
||||
},
|
||||
artist: {
|
||||
selector: 'span.item-link-alt > div.collection-item-artist'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const albums: Album[] = data?.collectionItems || [];
|
||||
console.log(`Albums ${JSON.stringify(albums)}`);
|
||||
|
||||
if (albums && albums?.length > 0) {
|
||||
return albums;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
16
src/routes/+page.server.ts
Normal file
16
src/routes/+page.server.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import type { PageServerLoad } from './lib/$types';
|
||||
import { PAGE_SIZE } from '$env/static/private';
|
||||
import { fetchBandcampAlbums } from '$root/lib/util/fetchBandcampAlbums';
|
||||
|
||||
export const load: PageServerLoad = async ({ fetch, setHeaders }) => {
|
||||
const albums = async () => await fetchBandcampAlbums();
|
||||
const articles = async () => await fetch(`/api/articles?page=1&limit=3`);
|
||||
|
||||
setHeaders({
|
||||
'cache-control': 'max-age=43200'
|
||||
});
|
||||
return {
|
||||
albums: albums(),
|
||||
articlesData: (await articles()).json()
|
||||
};
|
||||
};
|
||||
|
|
@ -1,7 +1,21 @@
|
|||
<script lang="ts">
|
||||
import SEO from '$root/lib/components/SEO.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import Bandcamp from '$lib/components/bandcamp/index.svelte';
|
||||
import Articles from '$lib/components/articles/index.svelte';
|
||||
import SEO from '$lib/components/SEO.svelte';
|
||||
import type { Album } from '$lib/types/album';
|
||||
import type { Article } from '$lib/types/article';
|
||||
import type { ArticlePageLoad } from './articles/[page]/+page.server';
|
||||
|
||||
// export let data: PageData;
|
||||
export let data: PageData;
|
||||
let albums: Album[];
|
||||
let articlesData: ArticlePageLoad;
|
||||
let articles: Article[];
|
||||
let totalArticles: number;
|
||||
$: ({ albums, articlesData } = data);
|
||||
$: ({ articles, totalArticles } = articlesData);
|
||||
$: console.log(`All data: ${JSON.stringify(articlesData)}`);
|
||||
|
||||
const userNames = {
|
||||
github: 'BradNut',
|
||||
linkedIn: 'bradley-shellnut',
|
||||
|
|
@ -57,8 +71,8 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="social-info">
|
||||
<!-- <Bandcamp /> -->
|
||||
<!-- <Articles /> -->
|
||||
<Bandcamp {albums} />
|
||||
<Articles {articles} {totalArticles} compact={true} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { Image, Picture } from 'svelte-lazy-loader';
|
||||
import { Picture } from 'svelte-lazy-loader';
|
||||
import Graphql from '@iconify-icons/simple-icons/graphql';
|
||||
import Nextdotjs from '@iconify-icons/simple-icons/next-dot-js';
|
||||
import Prisma from '@iconify-icons/simple-icons/prisma';
|
||||
|
|
|
|||
1
src/routes/about/+page.ts
Normal file
1
src/routes/about/+page.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const prerender = true;
|
||||
|
|
@ -4,8 +4,9 @@ import {
|
|||
WALLABAG_USERNAME,
|
||||
WALLABAG_PASSWORD,
|
||||
WALLABAG_URL,
|
||||
WALLABAG_MAX_ARTICLES,
|
||||
WALLABAG_MAX_PAGES,
|
||||
PAGE_SIZE,
|
||||
WALLABAG_MAX_ARTICLES,
|
||||
USE_REDIS_CACHE
|
||||
} from '$env/static/private';
|
||||
import intersect from 'just-intersect';
|
||||
|
|
@ -61,6 +62,29 @@ export async function fetchArticlesApi(
|
|||
|
||||
const auth = await authResponse.json();
|
||||
|
||||
const pageQuery: PageQuery = {
|
||||
sort: 'updated',
|
||||
perPage: +queryParams?.limit || +PAGE_SIZE,
|
||||
since: 0,
|
||||
page: +queryParams?.page || 1,
|
||||
tags: 'programming',
|
||||
content: 'metadata'
|
||||
};
|
||||
const entriesQueryParams = new URLSearchParams({
|
||||
...pageQuery,
|
||||
perPage: `${pageQuery.perPage}`,
|
||||
since: `${pageQuery.since}`,
|
||||
page: `${pageQuery.page}`
|
||||
});
|
||||
console.log(`Entries params: ${entriesQueryParams}`);
|
||||
|
||||
if (lastFetched) {
|
||||
pageQuery.since = Math.round(lastFetched / 1000);
|
||||
}
|
||||
|
||||
lastFetched = new Date();
|
||||
|
||||
const nbEntries = 0;
|
||||
const pageResponse = await fetch(`${WALLABAG_URL}/api/entries.json?${entriesQueryParams}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
|
|
@ -92,7 +116,7 @@ export async function fetchArticlesApi(
|
|||
articles.push({
|
||||
tags,
|
||||
title: article.title,
|
||||
url: article.url,
|
||||
url: new URL(article.url),
|
||||
hashed_url: article.hashed_url,
|
||||
reading_time: article.reading_time,
|
||||
preview_picture: article.preview_picture,
|
||||
|
|
@ -103,12 +127,12 @@ export async function fetchArticlesApi(
|
|||
}
|
||||
});
|
||||
|
||||
const responseData = {
|
||||
return {
|
||||
articles,
|
||||
currentPage: page,
|
||||
totalPages: pages,
|
||||
totalPages: pages > +WALLABAG_MAX_PAGES ? +WALLABAG_MAX_PAGES : pages,
|
||||
limit,
|
||||
totalArticles: total,
|
||||
totalArticles: total > +WALLABAG_MAX_ARTICLES ? +WALLABAG_MAX_ARTICLES : total,
|
||||
cacheControl
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,37 @@
|
|||
import { json } from '@sveltejs/kit';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import { WALLABAG_MAX_PAGES } from '$env/static/private';
|
||||
import type { RequestHandler, RequestEvent } from './$types';
|
||||
import { fetchArticlesApi } from '$root/routes/api';
|
||||
|
||||
export const GET: RequestHandler = async ({ url }: RequestEvent) => {
|
||||
try {
|
||||
const page = url?.searchParams?.get('page') || '1';
|
||||
if (+page > +WALLABAG_MAX_PAGES) {
|
||||
throw new Error('Page does not exist');
|
||||
}
|
||||
const response = await fetchArticlesApi('get', `fetchArticles`, {
|
||||
page: url?.searchParams?.get('page') || '1'
|
||||
page,
|
||||
limit: url?.searchParams?.get('limit') || '6'
|
||||
});
|
||||
|
||||
if (response?.articles) {
|
||||
if (response?.cacheControl) {
|
||||
if (!response.cacheControl.includes('no-cache')) {
|
||||
setHeaders({
|
||||
'cache-control': response?.cacheControl
|
||||
});
|
||||
} else {
|
||||
setHeaders({
|
||||
'cache-control': 'max-age=43200'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`API response ${JSON.stringify(response)}`);
|
||||
return json(response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw error(404, 'Page does not exist');
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { error } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import type { Article } from '$root/lib/types/article';
|
||||
import { WALLABAG_MAX_PAGES } from '$env/static/private';
|
||||
import type { Article } from '$lib/types/article';
|
||||
|
||||
export type ArticlePageLoad = {
|
||||
articles: Article[];
|
||||
|
|
@ -12,6 +14,11 @@ export type ArticlePageLoad = {
|
|||
|
||||
export const load: PageServerLoad = async ({ fetch, params, setHeaders }) => {
|
||||
const { page } = params;
|
||||
if (+page > +WALLABAG_MAX_PAGES) {
|
||||
throw error(404, {
|
||||
message: 'Not found'
|
||||
});
|
||||
}
|
||||
const resp = await fetch(`/api/articles?page=${page}`);
|
||||
const { articles, currentPage, totalPages, limit, totalArticles, cacheControl }: ArticlePageLoad =
|
||||
await resp.json();
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
<a
|
||||
target="_blank"
|
||||
aria-label={`Link to ${article.title}`}
|
||||
href={article.url.href}
|
||||
href={article.url.toString()}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{article.title}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
TabPanels,
|
||||
} from "@rgossiaux/svelte-headlessui";
|
||||
import { Image, Picture } from "svelte-lazy-loader";
|
||||
import SEO from "$root/lib/components/SEO.svelte";
|
||||
import personalSite from "$lib/assets/images/Bradley_Shellnut_New_Site.png?format=webp;avif;png&metadata";
|
||||
import personalSiteBlurred from "$lib/assets/images/Bradley_Shellnut_New_Site.png?w=100&png&blur=10";
|
||||
import weddingWebsite from "$lib/assets/images/Wedding_Website.png?format=webp;avif;png&metadata";
|
||||
|
|
@ -18,7 +17,9 @@
|
|||
import shellnutArchitectWebsiteBlurred from "$lib/assets/images/Mark_Shellnut_Architect.png?w=100&png&blur=10";
|
||||
</script>
|
||||
|
||||
<SEO title="Portfolio" />
|
||||
<svelte:head>
|
||||
<title>Portfolio | Bradley Shellnut</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>Portfolio!</h1>
|
||||
<div class="portfolioTabStyles">
|
||||
|
|
|
|||
1
src/routes/portfolio/+page.ts
Normal file
1
src/routes/portfolio/+page.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const prerender = true;
|
||||
1
src/routes/privacy/+page.ts
Normal file
1
src/routes/privacy/+page.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const prerender = true;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { Image, Picture } from "svelte-lazy-loader";
|
||||
import { Picture } from "svelte-lazy-loader";
|
||||
import SEO from "$root/lib/components/SEO.svelte";
|
||||
import desktop from '$lib/assets/images/Desktop_so_clean.jpg?format=webp;avif;jpg&metadata';
|
||||
import desktopBlurred from '$lib/assets/images/Desktop_so_clean.jpg?w=100&jpg&blur=10';
|
||||
|
|
|
|||
1
src/routes/uses/+page.ts
Normal file
1
src/routes/uses/+page.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const prerender = true;
|
||||
Loading…
Reference in a new issue