feat: add some stuff

This commit is contained in:
2023-11-12 17:33:15 +01:00
parent 48b21a9b3a
commit 89a202b0a2
38 changed files with 1710 additions and 1380 deletions

View File

@ -0,0 +1,37 @@
<script lang="ts">
export let src: string;
export let alt = '';
const u = new URL(src);
const filename = u.pathname.split('/').pop() || '9a8sda';
const int = parseInt(filename?.slice(0, 8), 16) % 7;
</script>
<div class="frame" style="--frame: url(/frames/frame_0{int}.png)">
<img {src} {alt} />
</div>
<style>
.frame {
position: relative;
width: fit-content;
height: fit-content;
}
.frame:before {
content: '';
position: absolute;
height: 100%;
width: 100%;
background: var(--frame);
background-position: center center;
background-size: contain;
background-repeat: no-repeat;
filter: drop-shadow(0px 0px 20px black);
}
img {
z-index: -1;
max-width: 70%;
max-height: 70%;
margin-left: 15%;
margin-top: 12%;
}
</style>

View File

@ -0,0 +1,100 @@
<script lang="ts">
export let min = 0;
export let max = 100;
export let value = 50;
</script>
<input type="range" {min} {max} bind:value />
<style>
input[type='range'] {
height: 32px;
appearance: none;
margin: 10px 0;
width: 100%;
background: transparent;
}
input[type='range']:focus {
outline: none;
}
input[type='range']::-webkit-slider-runnable-track {
width: 100%;
height: 7px;
cursor: pointer;
animate: 0.2s;
box-shadow: 0px 0px 0px #000000;
background: #d9c556;
border-radius: 29px;
border: 0px solid #010101;
}
input[type='range']::-webkit-slider-thumb {
box-shadow: 0px 0px 0px #000031;
border: 0px solid #00001e;
height: 26px;
width: 26px;
border-radius: 26px;
background: #ffffff;
cursor: pointer;
-webkit-appearance: none;
margin-top: -9.5px;
}
input[type='range']:focus::-webkit-slider-runnable-track {
background: #d9c556;
}
input[type='range']::-moz-range-track {
width: 100%;
height: 7px;
cursor: pointer;
animate: 0.2s;
box-shadow: 0px 0px 0px #000000;
background: #d9c556;
border-radius: 29px;
border: 0px solid #010101;
}
input[type='range']::-moz-range-thumb {
box-shadow: 0px 0px 0px #000031;
border: 0px solid #00001e;
height: 26px;
width: 26px;
border-radius: 26px;
background: #ffffff;
cursor: pointer;
}
input[type='range']::-ms-track {
width: 100%;
height: 7px;
cursor: pointer;
animate: 0.2s;
background: transparent;
border-color: transparent;
color: transparent;
}
input[type='range']::-ms-fill-lower {
background: #d9c556;
border: 0px solid #010101;
border-radius: 58px;
box-shadow: 0px 0px 0px #000000;
}
input[type='range']::-ms-fill-upper {
background: #d9c556;
border: 0px solid #010101;
border-radius: 58px;
box-shadow: 0px 0px 0px #000000;
}
input[type='range']::-ms-thumb {
margin-top: 1px;
box-shadow: 0px 0px 0px #000031;
border: 0px solid #00001e;
height: 26px;
width: 26px;
border-radius: 26px;
background: #ffffff;
cursor: pointer;
}
input[type='range']:focus::-ms-fill-lower {
background: #d9c556;
}
input[type='range']:focus::-ms-fill-upper {
background: #d9c556;
}
</style>

View File

@ -1,64 +1,318 @@
<script lang="ts">
import { persisted } from '$lib/helpers/localStore';
import { fade } from 'svelte/transition';
import { fade, slide } from 'svelte/transition';
import TextSplit from './TextSplit.svelte';
import ImageFrame from './ImageFrame.svelte';
import Button from './button.svelte';
import InputRange from './InputRange.svelte';
let tempName = '';
let data = persisted<{
name: string;
nameAccepted?: boolean;
let data = persisted('data', {
provideAdelsTitel?: boolean;
adelsTitel?: string;
adelsTitelAccepted?: boolean;
adelsTitelSuggestions?: string[];
providePortrait?: boolean;
portraitUrl?: string;
portraitHairType?: 'straight' | 'curly' | 'bald';
portraitHairColor?: 'red' | 'brown' | 'blonde' | 'black' | 'grey' | 'white';
portraitHairLength?: 'short' | 'medium' | 'long';
portraitAccepted?: boolean;
portraitPublic?: boolean;
confidence?: number;
confidenceAccepted?: boolean;
createPersonality?: boolean;
}>('data', {
name: '',
adelsTitel: ''
nameAccepted: false,
adelsTitel: undefined,
adelsTitelSuggestions: [],
provideAdelsTitel: undefined,
confidence: undefined,
confidenceAccepted: false,
createPersonality: undefined
});
let nameInputEl: HTMLInputElement;
let loadingAdelsTitel = false;
async function fetchAdelsTitel() {
if (loadingAdelsTitel) return;
loadingAdelsTitel = true;
const res = await fetch('https://adels-generator.herokuapp.com/');
const data = await res.json();
$data.adelsTitel = data.adelsTitel;
const res = await fetch('/api/ai/name/' + $data.name);
const json = await res.json();
$data.adelsTitelSuggestions = json;
loadingAdelsTitel = false;
}
let loadingPortrait = false;
async function fetchPortrait() {
if (loadingPortrait) return;
loadingPortrait = true;
const res = await fetch(`/api/ai/image/${$data.adelsTitel || $data.name}`, {
method: 'POST',
body: JSON.stringify({
hairType: $data.portraitHairType,
hairColor: $data.portraitHairColor,
hairLength: $data.portraitHairLength
})
});
const json = await res.json();
$data.portraitUrl = json.url;
loadingPortrait = false;
}
let isSubmitting = false;
async function submit() {
if (isSubmitting) return;
isSubmitting = true;
const res = await fetch(`/api/invites`, {
method: 'POST',
body: JSON.stringify($data)
});
}
</script>
<section in:fade={{ delay: 2000 }}>
{#if $data.name}
<h3>Name: {$data.name}</h3>
<button
on:click={() => {
$data.name = '';
}}>name ändern</button
>
{:else}
<TextSplit content="Fantastisch, wie lautet euer Name?" />
<input type="text" bind:value={tempName} />
{#if tempName}
<div class="wrapper">
<section in:slide>
<TextSplit content="Wie lautet euer Name?" />
<input bind:this={nameInputEl} placeholder="Name" type="text" bind:value={$data.name} />
{#if !$data.nameAccepted}
<button
on:click={() => {
$data.name = tempName;
tempName = '';
fetchAdelsTitel();
$data.nameAccepted = true;
}}>name speichern</button
>
{/if}
{/if}
</section>
</section>
{#if $data.name}
<section in:fade />
{/if}
{#if ($data.name && $data.nameAccepted) || $data.confidence !== undefined}
<section in:slide={{ delay: 0, duration: 500 }}>
<TextSplit content="Wie sicher können wir mit eurem Erscheinen rechnen?" delay={0} />
<div in:fade={{ delay: 1200 }}>
<InputRange bind:value={$data.confidence} />
</div>
{#if !$data.confidenceAccepted}
<button
on:click={() => {
$data.confidenceAccepted = true;
}}>ok</button
>
{/if}
</section>
{/if}
{#if $data.confidence !== undefined && $data.confidenceAccepted && $data.createPersonality === undefined}
<section in:slide out:slide>
<TextSplit content="Sollen wir eine alternative Persönlichkeit für euch finden?" />
<div>
<button
on:click={() => {
$data.createPersonality = true;
}}>Ja</button
>
<button
on:click={() => {
$data.createPersonality = false;
}}>Nein</button
>
</div>
{#if $data.adelsTitel}
{$data.adelsTitel}
{/if}
</section>
{/if}
{#if $data.createPersonality === true}
<section in:fade out:slide>
{#if $data.provideAdelsTitel === undefined}
<TextSplit content="Sollen wir einen Adelstitel vorschlagen?" />
<div>
<button
on:click={() => {
fetchAdelsTitel();
$data.provideAdelsTitel = true;
}}
>
ja</button
>
<button
on:click={() => {
$data.adelsTitel = '';
$data.provideAdelsTitel = false;
}}
>
nein</button
>
</div>
{/if}
{#if loadingAdelsTitel}
<p>Loading Adelstitel</p>
{:else if typeof $data.adelsTitel === 'string'}
<TextSplit content="Euer Adelstitel" />
<input placeholder="Name" type="text" bind:value={$data.adelsTitel} />
{#if !$data.adelsTitelAccepted}
<button
on:click={() => {
$data.adelsTitelAccepted = true;
}}>akzeptieren</button
>
{/if}
{:else if $data.adelsTitelSuggestions?.length}
<p>Adelstitel Vorschläge</p>
<hr />
{#each $data.adelsTitelSuggestions as suggestion}
<button
on:click={() => {
$data.adelsTitel = suggestion;
}}>{suggestion}</button
>
{/each}
<hr />
<button
on:click={() => {
fetchAdelsTitel();
}}>neue vorschläge</button
>
{/if}
</section>
{/if}
{#if $data.adelsTitel && $data.adelsTitelAccepted && $data.providePortrait !== false}
<section transition:slide>
{#if $data.providePortrait === undefined}
<TextSplit content="Sollen unsere Künstler ein Portrait eurer Figur anfertigen lassen?" />
<div>
<button
on:click={() => {
$data.providePortrait = true;
}}>ja</button
>
<button
on:click={() => {
$data.providePortrait = true;
}}>false</button
>
</div>
{/if}
{#if $data.providePortrait && !loadingPortrait && !$data.portraitUrl}
<p>
Wir werden “{$data.adelsTitel || $data.name}” mit
<select placeholder="Typ" bind:value={$data.portraitHairType}>
<option value="straight">glatten</option>
<option value="curly">lockigen</option>
<option value="wavy">welligen</option>
</select>
<select placeholder="Länge" bind:value={$data.portraitHairLength}>
<option value="long">langen</option>
<option value="medium">medium-langen</option>
<option value="short">kurzen</option>
</select>,
<select placeholder="Farbe" bind:value={$data.portraitHairColor}>
<option value="red">roten</option>
<option value="black">schwarzen</option>
<option value="blond">blonden</option>
<option value="braunem">braunen</option>
</select> Haaren zeichnen?
</p>
<button on:click={() => fetchPortrait()}> porträt malen (~30 Sekunden)</button>
{:else if loadingPortrait}
<p>Portrait wird gemalt</p>
{:else if $data.portraitUrl}
<div in:slide={{ duration: 2000 }} class="portrait">
<ImageFrame src={$data.portraitUrl} alt="portrait" />
{#if !$data.portraitAccepted}
<input bind:checked={$data.portraitPublic} type="checkbox" id="display-portrait" />
<label for="display-portrait">Porträt öffentlich anzeigen</label>
<br />
<button
on:click={() => {
$data.portraitAccepted = true;
}}>portrait akzeptieren</button
>
<button
on:click={() => {
$data.portraitUrl = undefined;
}}>neues portrait</button
>
{/if}
</div>
{/if}
</section>
{/if}
{#if $data.portraitAccepted}
<div class="button-wrapper">
<Button on:click={() => submit()}>Einladung abschicken</Button>
</div>
{/if}
</div>
<style>
.button-wrapper {
display: flex;
justify-content: center;
padding-block: 20px;
margin: 0 auto;
width: 100%;
max-width: 500px;
padding-block: 50px;
font-size: 2em;
margin-bottom: 200px;
}
.wrapper {
display: flex;
flex-direction: column;
gap: 50px;
padding-bottom: 200px;
}
.portrait {
width: 100%;
}
input[type='text'] {
font-family: 'Parisienne', cursive;
background: transparent;
border: none;
color: white;
font-size: 1em;
margin: 0 auto;
width: fit-content;
border-bottom: 1px solid white;
}
input[type='text']::after {
content: '';
background: white;
height: 1px;
width: 100%;
}
section {
color: white;
width: 100%;
font-family: 'Parisienne', cursive;
max-width: 500px;
margin: 0 auto;
font-size: 2em;
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
}
section {
border-width: 30px;
border-style: solid;
border-image: url(/border.svg);
border-image-repeat: stretch;
border-image-slice: 100%;
border-image-slice: 24% 23%;
}
</style>

View File

@ -6,21 +6,20 @@
let total = delay;
for (let i = 0; i < words.length; i++) {
const delay = total + words[i].length * 35;
delays.push(total);
total = delay;
delays.push(delay);
}
</script>
<div class="wrapper">
{#each words as word, i}
<span class="word" style="--delay:{2000 + delays[i]}ms">{word}</span>
<span class="word" style="--delay:{delays[i]}ms">{word}</span>
{/each}
</div>
<style>
.wrapper {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
width: 100%;
@ -31,7 +30,8 @@
margin: 0 0.1em;
animation: fadeIn 1s ease forwards;
animation-delay: var(--delay);
color: white;
font-size: 0.7em;
color: #fffa;
}
@keyframes fadeIn {
from {

View File

@ -0,0 +1,44 @@
<script lang="ts">
import { getSelectionContext } from './context';
const { selected: _selected } = getSelectionContext();
export let value = '';
export let selected: boolean = false;
$: if (selected) {
$_selected = value;
}
function updateSelected() {
if ($_selected === value) {
selected = true;
} else {
selected = false;
}
}
$: if ($_selected) updateSelected();
</script>
{#if $_selected !== value}
<div class="wrapper" class:selected={$_selected === value}>
<input type="checkbox" name="" id="" bind:checked={selected} />
<button
on:click={() => {
$_selected = value;
}}><slot /></button
>
</div>
{/if}
<style>
.wrapper {
display: flex;
color: black;
background: white;
}
.wrapper.selected {
position: absolute;
top: 0px;
}
</style>

View File

@ -0,0 +1,47 @@
<script lang="ts">
import { createSelectionContext } from './context';
const { selected } = createSelectionContext();
</script>
<div class="dropdown">
{#if !$selected}
<button class="dropbtn">Placeholder</button>
{:else}
<div class="spacer"></div>
{/if}
<div class="dropdown-content">
<slot />
</div>
</div>
<style>
.dropbtn {
color: white;
border: none;
cursor: pointer;
background: transparent;
text-decoration: underline;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
background: #fff;
position: absolute;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
max-height: 0px;
overflow: hidden;
}
.dropdown:hover .dropdown-content {
display: block;
max-height: 400px;
}
</style>

View File

@ -0,0 +1,18 @@
import { getContext, setContext } from "svelte";
import { writable } from "svelte/store";
export function getSelectionContext(): ReturnType<typeof createSelectionContext> {
return getContext('selection')
}
export function createSelectionContext() {
const ctx = {
selected: writable<string | undefined>()
}
setContext('selection', ctx)
return ctx;
}

View File

@ -0,0 +1,12 @@
import _Selection from './Selection.svelte';
import _Option from './Option.svelte';
type SelectionType = typeof _Selection & {
Option: typeof _Option
}
const Selection = _Selection as SelectionType;
Selection.Option = _Option;
export { Selection }

View File

@ -1,22 +0,0 @@
import OpenAI from 'openai';
import { OPENAI_API_KEY } from '$env/static/private';
const openai = new OpenAI({
apiKey: OPENAI_API_KEY,
});
export async function chat(prompt: string) {
const chatCompletion = await openai.chat.completions.create({
model: "gpt-4",
messages: [
{
"role": "system",
"content": prompt,
},
],
});
const res = chatCompletion.choices[0].message.content;
return res;
}

40
src/lib/helpers/openai.ts Normal file
View File

@ -0,0 +1,40 @@
import OpenAI from 'openai';
import { OPENAI_API_KEY } from '$env/static/private';
const openai = new OpenAI({
apiKey: OPENAI_API_KEY,
});
function processChatGptResult(resultString: string) {
// Split the string by newline
const lines: string[] = resultString.split('\n');
// Remove enumeration at the beginning of each line
const processedLines: string[] = lines.map(line => line.replace(/^\s*[\d.-]+\s*/, ''));
return processedLines.filter(line => line.length > 0);
}
export async function chat(prompt: string, { isList, temperature }: { isList?: boolean, temperature?: number } = {}) {
const chatCompletion = await openai.chat.completions.create({
model: "gpt-4",
temperature: temperature ?? 0.9,
messages: [
{
"role": "system",
"content": prompt,
},
],
});
const res = chatCompletion.choices[0].message.content;
if (res && isList) return processChatGptResult(res);
return res;
}
export function image(prompt: string) {
return openai.images.generate({ model: "dall-e-3", prompt });
}

17
src/lib/helpers/pb.ts Normal file
View File

@ -0,0 +1,17 @@
import Pocketbase from "pocketbase"
import { POCKETBASE_URL } from "$env/dynamic/private"
const pb = new Pocketbase(POCKETBASE_URL || "http://localhost:8090");
export function createPerson({ name, confidence, portrait, portrait_public, noble_name, hair_color, hair_type, hair_length }: { name: string, portrait: string, portrait_public: boolean, hair_type: string, hair_length: string, hair_color: string, confidence: number, noble_name: string }) {
return pb.collection("invites").create({
name,
confidence,
portrait,
portrait_public,
noble_name,
hair_type,
hair_length,
hair_color
})
}

114
src/lib/helpers/sheets.ts Normal file
View File

@ -0,0 +1,114 @@
import { env } from "$env/dynamic/private";
import { google } from 'googleapis';
const { GOOGLE_SHEET_ID, GOOGLE_APPLICATION_CREDENTIALS } = env;
const auth = GOOGLE_APPLICATION_CREDENTIALS && new google.auth.GoogleAuth({
keyFile: GOOGLE_APPLICATION_CREDENTIALS, //the key file
//url to spreadsheets API
scopes: 'https://www.googleapis.com/auth/spreadsheets'
});
const googleApi = (async () => {
if (!auth) return;
const authClientObject = await auth.getClient();
const googleSheetsInstance = google.sheets({ version: 'v4', auth: authClientObject });
return googleSheetsInstance;
})();
async function getSheet() {
return await (
await googleApi
).spreadsheets.values.get({
auth, //auth object
spreadsheetId: GOOGLE_SHEET_ID, // spreadsheet id
range: 'Gäste' //range of cells to read from.
});
}
function parseBoolean(o, key: string) {
if (key in o) {
o[key] = o[key].length ? !!parseInt(o[key]) : o[key];
}
}
function parseNumber(o, key: string) {
if (key in o) {
o[key] = o[key].length ? parseInt(o[key]) : null;
}
}
async function _getData(): Promise<{ Name: string; Alter: number }[]> {
const raw = await getSheet();
const _rows = raw.data.values;
if (!_rows) return [];
const [headers, ...rows] = _rows;
function parseRow(row: string[]) {
const o: {
schlafen?: boolean;
frauen?: boolean;
single?: boolean;
männer?: boolean;
veggies?: boolean;
Name: string;
Alter: number;
} = {
Name: '',
Alter: 0
};
row.forEach((v, i) => {
o[headers[i]] = v;
});
parseBoolean(o, 'Schlafen');
parseBoolean(o, 'Frauen');
parseBoolean(o, 'Single');
parseBoolean(o, 'Männer');
parseBoolean(o, 'Veggies');
parseNumber(o, 'Alter');
return o;
}
return rows.map((r: string[]) => parseRow(r));
}
let lastGetUpdate: number;
let cacheData: { Name: string; Alter: number }[];
export async function getData() {
if (!lastGetUpdate || Date.now() - 10000 > lastGetUpdate) {
cacheData = await _getData();
lastGetUpdate = Date.now();
}
return cacheData;
}
export async function addPerson({ name, confidence, noble_name }: { name: string, confidence: number, noble_name: string }): Promise<unknown> {
const api = await googleApi;
if (!api) return;
console.log({ name, confidence, noble_name });
return api.spreadsheets.values.append({
auth, //auth object
spreadsheetId: GOOGLE_SHEET_ID, // spreadsheet id
range: 'Gäste', //range of cells to read from.
valueInputOption: 'RAW',
resource: {
values: [[name, `${Number(confidence).toFixed(2).replace(".", ",")}%`, '', '', '', '', '', '', '', noble_name]]
}
});
}

View File

@ -17,13 +17,13 @@
let questionVisible = false;
onMount(() => {
// curtainsVisible = true;
curtainsVisible = false;
maskVisible = true;
maskSmall = true;
questionVisible = true;
curtainsVisible = true;
// curtainsVisible = false;
// maskVisible = true;
// maskSmall = true;
// questionVisible = true;
setTimeout(() => {
// buttonVisible = true;
buttonVisible = true;
}, 1500);
});
</script>

View File

@ -0,0 +1,25 @@
import { json } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";
import { putObject } from "$lib/helpers/minio";
import { generateImage } from "$lib/helpers/stability";
export const GET: RequestHandler = async () => {
const prompt = `golden brown rectangular picture frame, filled with pure red color, Charles Vess, opulence, mystery, elegance`;
const negativePrompt = "blurry, persons, figure"
const a = performance.now()
const image = await generateImage(prompt, negativePrompt);
const duration = performance.now() - a;
console.log({ duration })
const imageName = `${image.seed}-frame.png`
const res = await putObject(imageName, Buffer.from(image.base64, 'base64'), { "Content-Type": "image/png" });
return json({
...res,
url: `https://s3.app.max-richter.dev/silvester23/${imageName}`
})
}

View File

@ -3,25 +3,38 @@ import type { RequestHandler } from "./$types";
import { putObject } from "$lib/helpers/minio";
import { generateImage } from "$lib/helpers/stability";
export const GET: RequestHandler = async ({ params }) => {
export const POST: RequestHandler = async ({ params, request }) => {
const inputName = params.name;
if (!inputName) {
throw new Error("Missing name");
}
if (inputName.length > 50) {
throw new Error("Name too long");
}
const prompt = `realistic profile portrait oil painting of a masked ${inputName}, baroque, Charles Vess, masked ball attire, Charles Vess, opulence, mystery, elegance, medium-length blond hair, darker skin`;
const { hairType, hairColor, hairLength } = await request.json();
console.log(hairType, hairColor, hairLength)
if (!hairType || !hairColor || !hairLength) {
throw new Error("Missing hairType, hairColor or hairLength");
}
const prompt = `realistic portrait oil painting of a masked ${inputName}, baroque, in the style of Charles Vess, masked ball attire, opulence, mystery, elegance, ${hairLength} ${hairType} ${hairColor} hair, darker skin`;
const negativePrompt = "blurry, multiple persons, picture frame"
const a = performance.now()
// #const image = await openai.image(prompt);
const image = await generateImage(prompt, negativePrompt);
const duration = performance.now() - a;
console.log({ duration })
const imageName = `${image.seed}-${inputName.toLowerCase().split(" ").slice(0, 5).join("-").slice(0, 25)}.png`
const imageName = `${Math.random().toString(16).substring(3, 10)}-${inputName.toLowerCase().split(" ").slice(0, 5).join("-").slice(0, 25)}.png`
const res = await putObject(imageName, Buffer.from(image.base64, 'base64'), { "Content-Type": "image/png" });
await putObject(imageName, Buffer.from(image.base64, 'base64'), { "Content-Type": "image/png" });
return json({
...res,
url: `https://s3.app.max-richter.dev/silvester23/${imageName}`
duration,
url: `https://s3-api.app.max-richter.dev/silvester23/${imageName}`
})
}

View File

@ -1,14 +1,14 @@
import { type RequestHandler } from "@sveltejs/kit";
import { chat } from "$lib/helpers/chatgpt";
import { json, type RequestHandler } from "@sveltejs/kit";
import { chat } from "$lib/helpers/openai";
export const GET: RequestHandler = async function ({ params }) {
const inputName = params.name
const prompt = `Generate 10 variants of the name ${inputName}. The names should sound very much like the original but also like noble names from the 1900 century. Examples could be "lady rosalind of whitmore" "lord byron of castlemore" "Lord Max Richter". Only respond with 10 names seperated be newlines`;
const prompt = `Generate 10 variants of the name ${inputName}. The names should sound very much like the original but also like noble names from the 1900 century. Examples could be "lady rosalind of whitmore" "lord byron of castlemore" "Lord Max Richter". Choose english, german, french and italian sounding names. Only respond with 10 names seperated be newlines`;
const res = await chat(prompt);
const res = await chat(prompt, { isList: true, temperature: 1 });
return new Response(res);
return json(res);
}

View File

@ -0,0 +1,35 @@
import * as sheet from '$lib/helpers/sheets'
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import * as pb from "$lib/helpers/pb"
export const GET: RequestHandler = async () => {
const res = await sheet.getData();
return json(res);
}
export const POST: RequestHandler = async ({ request }) => {
const body = await request.json();
console.log(body)
try {
await sheet.addPerson({ name: body.name, confidence: body.confidence.toString(), noble_name: body.adelsTitel })
await pb.createPerson({
name: body.name,
confidence: body.confidence,
noble_name: body.adelsTitel,
portrait: body.portraitUrl,
hair_length: body.portraitHairLength,
hair_type: body.portraitHairType,
hair_color: body.portraitHairColor,
portrait_public: body.portraitPublic,
});
} catch (e) {
console.log(e)
}
return json(body);
}

View File

@ -0,0 +1,7 @@
<script lang="ts">
import ImageFrame from '$lib/components/ImageFrame.svelte';
</script>
<ImageFrame
src="https://s3-api.app.max-richter.dev/silvester23/13770f0-earl-maximus-of-richterla.png"
/>

View File

@ -0,0 +1,5 @@
<script lang="ts">
import Questions from '$lib/components/Questions.svelte';
</script>
<Questions />