mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Updating og image generation, updating Lucia Beta, and using Shadcn Form for the search form.
This commit is contained in:
parent
019798eb0b
commit
386d4e7e3a
22 changed files with 879 additions and 573 deletions
41
package.json
41
package.json
|
|
@ -28,16 +28,16 @@
|
|||
"devDependencies": {
|
||||
"@melt-ui/pp": "^0.3.0",
|
||||
"@melt-ui/svelte": "^0.70.0",
|
||||
"@playwright/test": "^1.41.0",
|
||||
"@resvg/resvg-js": "^2.4.1",
|
||||
"@sveltejs/adapter-auto": "^3.1.0",
|
||||
"@sveltejs/adapter-vercel": "^4.0.5",
|
||||
"@sveltejs/kit": "^2.3.5",
|
||||
"@playwright/test": "^1.41.1",
|
||||
"@resvg/resvg-js": "^2.6.0",
|
||||
"@sveltejs/adapter-auto": "^3.1.1",
|
||||
"@sveltejs/enhanced-img": "^0.1.8",
|
||||
"@sveltejs/kit": "^2.5.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@types/cookie": "^0.5.4",
|
||||
"@types/node": "^18.19.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
||||
"@typescript-eslint/parser": "^6.19.0",
|
||||
"@types/node": "^18.19.10",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
||||
"@typescript-eslint/parser": "^6.19.1",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
|
@ -59,14 +59,14 @@
|
|||
"svelte-meta-tags": "^3.1.0",
|
||||
"svelte-preprocess": "^5.1.3",
|
||||
"svelte-sequential-preprocessor": "^2.0.1",
|
||||
"sveltekit-flash-message": "^2.3.1",
|
||||
"sveltekit-superforms": "^1.13.3",
|
||||
"sveltekit-flash-message": "^2.4.1",
|
||||
"sveltekit-superforms": "^1.13.4",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tslib": "^2.6.1",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.11",
|
||||
"vitest": "^1.2.1",
|
||||
"vite": "^5.0.12",
|
||||
"vitest": "^1.2.2",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"type": "module",
|
||||
|
|
@ -78,32 +78,33 @@
|
|||
"@fontsource/fira-mono": "^4.5.10",
|
||||
"@iconify-icons/line-md": "^1.2.26",
|
||||
"@iconify-icons/mdi": "^1.2.47",
|
||||
"@lucia-auth/adapter-prisma": "4.0.0-beta.9",
|
||||
"@lucia-auth/adapter-prisma": "4.0.0-beta.10",
|
||||
"@lukeed/uuid": "^2.0.1",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@prisma/client": "^5.8.1",
|
||||
"@sentry/sveltekit": "^7.88.0",
|
||||
"@sveltejs/adapter-vercel": "^5.1.0",
|
||||
"@types/feather-icons": "^4.29.4",
|
||||
"@vercel/og": "^0.5.13",
|
||||
"bits-ui": "^0.11.8",
|
||||
"@vercel/og": "^0.5.20",
|
||||
"bits-ui": "^0.15.1",
|
||||
"boardgamegeekclient": "^1.9.1",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"cookie": "^0.5.0",
|
||||
"feather-icons": "^4.29.1",
|
||||
"formsnap": "^0.4.2",
|
||||
"formsnap": "^0.4.3",
|
||||
"html-entities": "^2.4.0",
|
||||
"iconify-icon": "^1.0.8",
|
||||
"just-kebab-case": "^4.2.0",
|
||||
"loader": "^2.1.1",
|
||||
"lucia": "3.0.0-beta.14",
|
||||
"lucia": "3.0.0-beta.15",
|
||||
"lucide-svelte": "^0.298.0",
|
||||
"open-props": "^1.6.16",
|
||||
"oslo": "^0.27.1",
|
||||
"open-props": "^1.6.17",
|
||||
"oslo": "^1.0.1",
|
||||
"radix-svelte": "^0.9.0",
|
||||
"svelte-french-toast": "^1.2.0",
|
||||
"svelte-lazy-loader": "^1.0.0",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"tailwind-merge": "^2.2.1",
|
||||
"tailwind-variants": "^0.1.19",
|
||||
"tailwindcss-animate": "^1.0.6",
|
||||
"zod-to-json-schema": "^3.22.3"
|
||||
|
|
|
|||
1044
pnpm-lock.yaml
1044
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
39
src/lib/components/search/GameSearchForm.svelte
Normal file
39
src/lib/components/search/GameSearchForm.svelte
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<script lang="ts">
|
||||
import type { SuperValidated } from 'sveltekit-superforms';
|
||||
import { search_schema, type SearchSchema } from '$lib/zodValidation';
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
|
||||
export let form: SuperValidated<SearchSchema>;
|
||||
</script>
|
||||
|
||||
<search>
|
||||
<Form.Root id="search-form" action="/search" method="GET" data-sveltekit-reload {form} schema={search_schema} let:config>
|
||||
<fieldset>
|
||||
<Form.Item>
|
||||
<Form.Field {config} name="q">
|
||||
<Form.Label for="label">Search</Form.Label>
|
||||
<Form.Input />
|
||||
<Form.Validation />
|
||||
</Form.Field>
|
||||
<Form.Field {config} name="skip">
|
||||
<Form.Input type="hidden" />
|
||||
</Form.Field>
|
||||
<Form.Field {config} name="limit">
|
||||
<Form.Input type="hidden" />
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<div class="flex items-center space-x-2">
|
||||
<Form.Field {config} name="exact">
|
||||
<Form.Label>Exact Search</Form.Label>
|
||||
<Form.Checkbox class="mt-0" />
|
||||
</Form.Field>
|
||||
</div>
|
||||
</fieldset>
|
||||
<Form.Button>Submit</Form.Button>
|
||||
</Form.Root>
|
||||
</search>
|
||||
|
||||
<style lang="postcss">
|
||||
</style>
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { superForm } from 'sveltekit-superforms/client';
|
||||
import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
|
||||
import type { SuperValidated } from 'sveltekit-superforms';
|
||||
import type { SearchSchema } from '$lib/zodValidation';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
|
||||
export let data;
|
||||
console.log("text search data", data);
|
||||
export let showButton: boolean = false;
|
||||
export let advancedSearch: boolean = false;
|
||||
|
||||
const { form, errors }: SuperValidated<SearchSchema> = superForm(data.form);
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
|
||||
// TODO: Keep all Pagination Values on back and forth browser
|
||||
// TODO: Add cache for certain number of pages so back and forth doesn't request data again
|
||||
</script>
|
||||
|
||||
{#if dev}
|
||||
<SuperDebug collapsible data={$form} />
|
||||
{/if}
|
||||
|
||||
<search>
|
||||
<form id="search-form" action="/search" method="GET">
|
||||
<div class="search">
|
||||
<fieldset class="text-search">
|
||||
<Label for="label">Search</Label>
|
||||
<Input type="text" id="q" class={$errors.q && "outline outline-destructive"} name="q" placeholder="Search board games" data-invalid={$errors.q} bind:value={$form.q} />
|
||||
{#if $errors.q}
|
||||
<p class="text-sm text-destructive">{$errors.q}</p>
|
||||
{/if}
|
||||
<input id="skip" type="hidden" name="skip" bind:value={$form.skip} />
|
||||
<input id="limit" type="hidden" name="limit" bind:value={$form.limit} />
|
||||
</fieldset>
|
||||
</div>
|
||||
{#if showButton}
|
||||
<Button type="submit">Submit</Button>
|
||||
{/if}
|
||||
</form>
|
||||
</search>
|
||||
|
||||
<style lang="postcss">
|
||||
:global(.disclosure-button) {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#search-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 1rem;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
gap: 1rem;
|
||||
place-content: start;
|
||||
place-items: center;
|
||||
|
||||
@media (max-width: 850px) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -7,12 +7,10 @@ const buttonVariants = tv({
|
|||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@
|
|||
export { className as class };
|
||||
</script>
|
||||
|
||||
<FormPrimitive.Description
|
||||
class={cn("text-sm text-muted-foreground", className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<FormPrimitive.Description class={cn("text-sm text-muted-foreground", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</FormPrimitive.Description>
|
||||
|
|
|
|||
|
|
@ -12,10 +12,6 @@
|
|||
const { errors, ids } = getFormField();
|
||||
</script>
|
||||
|
||||
<Label
|
||||
for={$ids.input}
|
||||
class={cn($errors && "text-destructive", className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<Label for={$ids.input} class={cn($errors && "text-destructive", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</Label>
|
||||
|
|
|
|||
|
|
@ -7,17 +7,12 @@
|
|||
placeholder?: string;
|
||||
};
|
||||
type $$Events = SelectPrimitive.TriggerEvents;
|
||||
const { attrStore } = getFormField();
|
||||
const { attrStore, value } = getFormField();
|
||||
export let placeholder = "";
|
||||
</script>
|
||||
|
||||
<Select.Trigger
|
||||
{...$$restProps}
|
||||
{...$attrStore}
|
||||
on:click
|
||||
on:keydown
|
||||
type="button"
|
||||
>
|
||||
<Select.Trigger {...$$restProps} {...$attrStore} on:click on:keydown type="button">
|
||||
<slot value={$value}>
|
||||
<Select.Value {placeholder} />
|
||||
<slot />
|
||||
</slot>
|
||||
</Select.Trigger>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@
|
|||
import { getFormField } from "formsnap";
|
||||
import type { HTMLTextareaAttributes } from "svelte/elements";
|
||||
import type { TextareaGetFormField } from ".";
|
||||
import {
|
||||
Textarea,
|
||||
type TextareaEvents
|
||||
} from "$lib/components/ui/textarea";
|
||||
import { Textarea, type TextareaEvents } from "$lib/components/ui/textarea";
|
||||
|
||||
type $$Props = HTMLTextareaAttributes;
|
||||
type $$Events = TextareaEvents;
|
||||
|
|
|
|||
|
|
@ -27,10 +27,7 @@ const SelectGroup = SelectComp.Group;
|
|||
const SelectItem = SelectComp.Item;
|
||||
const SelectSeparator = SelectComp.Separator;
|
||||
|
||||
export type TextareaGetFormField = Omit<
|
||||
ReturnType<typeof getFormField>,
|
||||
"value"
|
||||
> & {
|
||||
export type TextareaGetFormField = Omit<ReturnType<typeof getFormField>, "value"> & {
|
||||
value: Writable<string>;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ export type InputEvents = {
|
|||
change: FormInputEvent<Event>;
|
||||
click: FormInputEvent<MouseEvent>;
|
||||
focus: FormInputEvent<FocusEvent>;
|
||||
focusin: FormInputEvent<FocusEvent>;
|
||||
focusout: FormInputEvent<FocusEvent>;
|
||||
keydown: FormInputEvent<KeyboardEvent>;
|
||||
keypress: FormInputEvent<KeyboardEvent>;
|
||||
keyup: FormInputEvent<KeyboardEvent>;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
on:change
|
||||
on:click
|
||||
on:focus
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:keydown
|
||||
on:keypress
|
||||
on:keyup
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@
|
|||
export { className as class };
|
||||
</script>
|
||||
|
||||
<RadioGroupPrimitive.Root
|
||||
bind:value
|
||||
class={cn("grid gap-2", className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<RadioGroupPrimitive.Root bind:value class={cn("grid gap-2", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</RadioGroupPrimitive.Root>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,4 @@
|
|||
export { className as class };
|
||||
</script>
|
||||
|
||||
<SelectPrimitive.Separator
|
||||
class={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...$$restProps}
|
||||
/>
|
||||
<SelectPrimitive.Separator class={cn("-mx-1 my-1 h-px bg-muted", className)} {...$$restProps} />
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
<SelectPrimitive.Trigger
|
||||
class={cn(
|
||||
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 line-clamp-1 truncate",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
|
|
|
|||
45
src/lib/renderImage.ts
Normal file
45
src/lib/renderImage.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import satori from 'satori';
|
||||
import { Resvg } from '@resvg/resvg-js';
|
||||
import { html as toReactNode } from 'satori-html';
|
||||
import { dev } from '$app/environment';
|
||||
import { read } from '$app/server';
|
||||
|
||||
// we use a Vite plugin to turn this import into the result of fs.readFileSync during build
|
||||
import firaSansSemiBold from '$lib/fonts/FiraSans-SemiBold.ttf';
|
||||
|
||||
const fontData = read(firaSansSemiBold).arrayBuffer();
|
||||
|
||||
export async function componentToPng(component,
|
||||
props: Record<string, string | undefined>,
|
||||
height: number, width: number) {
|
||||
const result = component.render(props);
|
||||
const markup = toReactNode(`${result.html}<style lang="css">${result.css.code}</style>`);
|
||||
|
||||
const svg = await satori(markup, {
|
||||
fonts: [
|
||||
{
|
||||
name: 'Fira Sans',
|
||||
data: await fontData,
|
||||
style: 'normal'
|
||||
}
|
||||
],
|
||||
height: +height,
|
||||
width: +width
|
||||
});
|
||||
|
||||
const resvg = new Resvg(svg, {
|
||||
fitTo: {
|
||||
mode: 'width',
|
||||
value: +width
|
||||
}
|
||||
});
|
||||
|
||||
const image = resvg.render();
|
||||
|
||||
return new Response(image.asPng(), {
|
||||
headers: {
|
||||
'content-type': 'image/png',
|
||||
'cache-control': dev ? 'no-cache, no-store' : 'public, immutable, no-transform, max-age=86400'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ import { superValidate } from 'sveltekit-superforms/server';
|
|||
import { search_schema } from '$lib/zodValidation';
|
||||
import type { MetaTagsProps } from 'svelte-meta-tags';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import prisma from '$lib/prisma';
|
||||
import type { Game } from '@prisma/client';
|
||||
|
||||
export const load: PageServerLoad = async ({ fetch, url }) => {
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ async function searchForGames(
|
|||
};
|
||||
} catch (e) {
|
||||
console.log(`Error searching board games ${e}`);
|
||||
// throw error(500, { message: 'Something went wrong' });
|
||||
}
|
||||
return {
|
||||
totalCount: 0,
|
||||
|
|
@ -103,21 +102,27 @@ async function searchForGames(
|
|||
};
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ locals, fetch, url }) => {
|
||||
const defaults = {
|
||||
const defaults = {
|
||||
limit: 10,
|
||||
skip: 0,
|
||||
order: 'asc',
|
||||
sort: 'name'
|
||||
};
|
||||
order: 'name',
|
||||
sort: 'asc',
|
||||
q: '',
|
||||
exact: false,
|
||||
};
|
||||
|
||||
export const load: PageServerLoad = async ({ locals, fetch, url }) => {
|
||||
const searchParams = Object.fromEntries(url?.searchParams);
|
||||
console.log('searchParams', searchParams);
|
||||
searchParams.limit = searchParams.limit || `${defaults.limit}`;
|
||||
searchParams.skip = searchParams.skip || `${defaults.skip}`;
|
||||
searchParams.order = searchParams.order || defaults.order;
|
||||
searchParams.sort = searchParams.sort || defaults.sort;
|
||||
const form = await superValidate(searchParams, search_schema);
|
||||
// const modifyListForm = await superValidate(listGameSchema);
|
||||
searchParams.q = searchParams.q || defaults.q;
|
||||
const form = await superValidate({
|
||||
...searchParams,
|
||||
skip: Number(searchParams.skip || defaults.skip),
|
||||
limit: Number(searchParams.limit || defaults.limit),
|
||||
exact: searchParams.exact ? searchParams.exact === 'true' : defaults.exact
|
||||
}, search_schema);
|
||||
|
||||
const queryParams: SearchQuery = {
|
||||
limit: form.data?.limit,
|
||||
|
|
@ -125,7 +130,6 @@ export const load: PageServerLoad = async ({ locals, fetch, url }) => {
|
|||
q: form.data?.q
|
||||
};
|
||||
|
||||
// fields: ('id,name,min_age,min_players,max_players,thumb_url,min_playtime,max_playtime,min_age,description');
|
||||
try {
|
||||
if (form.data?.q === '') {
|
||||
return {
|
||||
|
|
@ -169,6 +173,8 @@ export const load: PageServerLoad = async ({ locals, fetch, url }) => {
|
|||
const urlQueryParams = new URLSearchParams(newQueryParams);
|
||||
const searchData = await searchForGames(locals, fetch, urlQueryParams);
|
||||
|
||||
console.log('search data', JSON.stringify(searchData, null, 2));
|
||||
|
||||
return {
|
||||
form,
|
||||
// modifyListForm,
|
||||
|
|
@ -178,6 +184,8 @@ export const load: PageServerLoad = async ({ locals, fetch, url }) => {
|
|||
} catch (e) {
|
||||
console.log(`Error searching board games ${e}`);
|
||||
}
|
||||
|
||||
console.log('returning default no data')
|
||||
return {
|
||||
form,
|
||||
searchData: {
|
||||
|
|
|
|||
|
|
@ -4,21 +4,26 @@
|
|||
import { superForm } from 'sveltekit-superforms/client';
|
||||
import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
|
||||
import { createPagination, createToolbar, melt } from '@melt-ui/svelte';
|
||||
import { ChevronLeft, ChevronRight, LayoutList, LayoutGrid } from 'lucide-svelte';
|
||||
import type { SearchSchema } from '$lib/zodValidation';
|
||||
import { ChevronLeft, ChevronRight, LayoutList, LayoutGrid, Check } from 'lucide-svelte';
|
||||
import { search_schema, type SearchSchema } from '$lib/zodValidation';
|
||||
import Game from '$components/Game.svelte';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Checkbox } from "$lib/components/ui/checkbox";
|
||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
import GameSearchForm from '$components/search/GameSearchForm.svelte';
|
||||
|
||||
export let data;
|
||||
|
||||
const { form, errors }: SuperValidated<SearchSchema> = superForm(data.form);
|
||||
const { games, totalCount } = data?.searchData;
|
||||
|
||||
console.log('data found', data);
|
||||
console.log('found games', games);
|
||||
console.log('found totalCount', totalCount);
|
||||
|
||||
let submitButton: HTMLElement;
|
||||
let pageSize = +form?.limit || 10;
|
||||
let pageSize: number = data.form.limit || 10;
|
||||
|
||||
$: showPagination = totalCount > pageSize;
|
||||
|
||||
|
|
@ -48,33 +53,10 @@
|
|||
|
||||
<div class="game-search">
|
||||
{#if dev}
|
||||
<SuperDebug collapsible data={$form} />
|
||||
<SuperDebug collapsible data={data.form} />
|
||||
{/if}
|
||||
|
||||
<search>
|
||||
<form id="search-form" action="/search" method="GET">
|
||||
<fieldset>
|
||||
<Label for="label">Search</Label>
|
||||
<Input type="text" id="q" class={$errors.q && "outline outline-destructive"} name="q" placeholder="Search board games" data-invalid={$errors.q} bind:value={$form.q} />
|
||||
{#if $errors.q}
|
||||
<p class="text-sm text-destructive">{$errors.q}</p>
|
||||
{/if}
|
||||
<input id="skip" type="hidden" name="skip" bind:value={$form.skip} />
|
||||
<input id="limit" type="hidden" name="limit" bind:value={$form.limit} />
|
||||
</fieldset>
|
||||
<fieldset class="flex items-center space-x-2">
|
||||
<Checkbox id="exact" bind:checked={$form.exact} aria-labelledby="exact-label" />
|
||||
<Label
|
||||
id="exact-label"
|
||||
for="exact"
|
||||
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Exact Search
|
||||
</Label>
|
||||
</fieldset>
|
||||
<Button type="submit">Submit</Button>
|
||||
</form>
|
||||
</search>
|
||||
<GameSearchForm form={data.form} />
|
||||
|
||||
<section class="games">
|
||||
<div>
|
||||
|
|
@ -139,15 +121,11 @@
|
|||
button {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 2px;
|
||||
background-color: rgb(var(--color-white) / 1);
|
||||
color: rgb(var(--color-magnum-700) / 1);
|
||||
box-shadow: 0px 1px 2px 0px rgb(var(--color-black) / 0.05);
|
||||
|
||||
font-size: 14px;
|
||||
|
||||
padding-inline: 0.75rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
|
|
|
|||
|
|
@ -1,56 +1,27 @@
|
|||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import satori from 'satori';
|
||||
import { Resvg } from '@resvg/resvg-js';
|
||||
import { html as toReactNode } from 'satori-html';
|
||||
import NotoSans from '$lib/fonts/NotoSans-Regular.ttf';
|
||||
import SocialImageCard from '$components/socialImageCard.svelte';
|
||||
import { componentToPng } from '$lib/renderImage.js';
|
||||
|
||||
const height = 630;
|
||||
const width = 1200;
|
||||
|
||||
export const GET: RequestHandler = async ({ url }) => {
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function GET({ url }) {
|
||||
try {
|
||||
const ogImage = `${new URL(url.origin).href}images/bored-game.png`;
|
||||
const faviconImageLocation = 'images/bored-game.png';
|
||||
const image = `${new URL(url.origin).href}${faviconImageLocation}`;
|
||||
const header = url.searchParams.get('header') ?? undefined;
|
||||
const page = url.searchParams.get('page') ?? undefined;
|
||||
const content = url.searchParams.get('content') ?? '';
|
||||
const result = SocialImageCard.render({
|
||||
|
||||
return componentToPng(SocialImageCard, {
|
||||
header,
|
||||
page,
|
||||
content,
|
||||
image: ogImage,
|
||||
width,
|
||||
height,
|
||||
image,
|
||||
width: `${width}`,
|
||||
height: `${height}`,
|
||||
url: new URL(url.origin).href
|
||||
});
|
||||
console.log('result', result);
|
||||
const element = toReactNode(`${result.html}<style>${result.css.code}</style>`);
|
||||
const svg = await satori(element, {
|
||||
fonts: [
|
||||
{
|
||||
name: 'Noto Sans',
|
||||
data: Buffer.from(NotoSans),
|
||||
style: 'normal'
|
||||
}
|
||||
],
|
||||
height,
|
||||
width
|
||||
});
|
||||
|
||||
const resvg = new Resvg(svg, {
|
||||
fitTo: {
|
||||
mode: 'width',
|
||||
value: width
|
||||
}
|
||||
});
|
||||
|
||||
const image = resvg.render();
|
||||
|
||||
return new Response(image.asPng(), {
|
||||
headers: {
|
||||
'content-type': 'image/png'
|
||||
}
|
||||
});
|
||||
}, height, width);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { sentrySvelteKit } from "@sentry/sveltekit";
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
// import fs from 'fs';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
|
|
@ -19,6 +18,9 @@ export default defineConfig({
|
|||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}']
|
||||
},
|
||||
define: {
|
||||
SUPERFORMS_LEGACY: true
|
||||
},
|
||||
css: {
|
||||
devSourcemap: true,
|
||||
preprocessorOptions: {
|
||||
|
|
@ -43,15 +45,3 @@ export default defineConfig({
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// function rawFonts(ext) {
|
||||
// return {
|
||||
// name: 'vite-plugin-raw-fonts',
|
||||
// transform(code, id) {
|
||||
// if (ext.some((e) => id.endsWith(e))) {
|
||||
// const buffer = fs.readFileSync(id);
|
||||
// return { code: `export default ${JSON.stringify(buffer)}`, map: null };
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
Loading…
Reference in a new issue