feat: add some ai stuff
This commit is contained in:
parent
dfd5e79246
commit
acf086da13
@ -29,7 +29,10 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.441.0",
|
||||||
"@sveltejs/adapter-node": "^1.3.1",
|
"@sveltejs/adapter-node": "^1.3.1",
|
||||||
|
"minio": "^7.1.3",
|
||||||
|
"openai": "^4.15.4",
|
||||||
"svelte-particles": "^2.12.0",
|
"svelte-particles": "^2.12.0",
|
||||||
"tsparticles": "^2.12.0",
|
"tsparticles": "^2.12.0",
|
||||||
"tsparticles-confetti": "^2.12.0",
|
"tsparticles-confetti": "^2.12.0",
|
||||||
|
1508
pnpm-lock.yaml
1508
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
24
src/lib/components/FadeIn.svelte
Normal file
24
src/lib/components/FadeIn.svelte
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script>
|
||||||
|
export let delay = 0;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div style="--delay: {delay}ms">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 1s ease forwards;
|
||||||
|
animation-delay: var(--delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
64
src/lib/components/Questions.svelte
Normal file
64
src/lib/components/Questions.svelte
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { persisted } from '$lib/helpers/localStore';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
import TextSplit from './TextSplit.svelte';
|
||||||
|
|
||||||
|
let tempName = '';
|
||||||
|
|
||||||
|
let data = persisted('data', {
|
||||||
|
name: '',
|
||||||
|
adelsTitel: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
loadingAdelsTitel = false;
|
||||||
|
}
|
||||||
|
</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}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
$data.name = tempName;
|
||||||
|
tempName = '';
|
||||||
|
fetchAdelsTitel();
|
||||||
|
}}>name speichern</button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{#if $data.name}
|
||||||
|
<section in:fade />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
section {
|
||||||
|
color: white;
|
||||||
|
font-family: 'Parisienne', cursive;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-size: 2em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
44
src/lib/components/TextSplit.svelte
Normal file
44
src/lib/components/TextSplit.svelte
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let content: string;
|
||||||
|
const words = content.split(' ');
|
||||||
|
export let delay = 0;
|
||||||
|
const delays: number[] = [];
|
||||||
|
let total = delay;
|
||||||
|
for (let i = 0; i < words.length; i++) {
|
||||||
|
const delay = total + words[i].length * 35;
|
||||||
|
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>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.word {
|
||||||
|
opacity: 0;
|
||||||
|
margin: 0 0.1em;
|
||||||
|
animation: fadeIn 1s ease forwards;
|
||||||
|
animation-delay: var(--delay);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -13,7 +13,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-family: 'Meow Script', cursive;
|
font-family: 'Meow Script', cursive;
|
||||||
font-size: 2em;
|
font-size: var(--font-size, 1em);
|
||||||
color: #ceba51;
|
color: #ceba51;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-width: 30px;
|
border-width: 30px;
|
||||||
@ -33,16 +33,16 @@
|
|||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: rgba(0, 0, 0, 0.7);
|
background: rgba(0, 0, 0, 0.7);
|
||||||
box-shadow: 0 0 70px #ffb11b88;
|
box-shadow: 0 0 70px #ffb11b22;
|
||||||
top: -15px;
|
top: -15px;
|
||||||
left: -15px;
|
left: -15px;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
width: calc(100% + 30px);
|
width: calc(100% + 30px);
|
||||||
height: calc(100% + 30px);
|
height: calc(100% + 30px);
|
||||||
transition: box-shadow 0.3s ease;
|
transition: box-shadow 0.8s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover::after {
|
button:hover::after {
|
||||||
box-shadow: 0 0 30px #ffb11b88;
|
box-shadow: 0 0 40px #ffb11b55;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
let confettiCanvas: HTMLCanvasElement;
|
||||||
|
|
||||||
let f: number;
|
let f: number;
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
@ -17,11 +18,15 @@
|
|||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const confetti = (await import('https://esm.run/canvas-confetti')).default;
|
const confetti = (await import('https://esm.run/canvas-confetti')).default;
|
||||||
|
confettiCanvas.width = window.innerWidth / 2;
|
||||||
|
confettiCanvas.height = window.innerHeight / 2;
|
||||||
|
|
||||||
|
const update = confetti.create(confettiCanvas);
|
||||||
|
|
||||||
function frame() {
|
function frame() {
|
||||||
i = (i + 1) % 1000;
|
i = (i + 1) % 1000;
|
||||||
if (i % 20 === 0) {
|
if (i % 10 === 0) {
|
||||||
confetti({
|
update({
|
||||||
particleCount: 1,
|
particleCount: 1,
|
||||||
startVelocity: 0,
|
startVelocity: 0,
|
||||||
useWorker: true,
|
useWorker: true,
|
||||||
@ -33,7 +38,7 @@
|
|||||||
colors: ['#d9c556'],
|
colors: ['#d9c556'],
|
||||||
shapes: ['circle'],
|
shapes: ['circle'],
|
||||||
gravity: randomInRange(0.4, 0.6),
|
gravity: randomInRange(0.4, 0.6),
|
||||||
scalar: randomInRange(0.4, 1),
|
scalar: randomInRange(0.1, 0.5),
|
||||||
drift: randomInRange(-2, 2),
|
drift: randomInRange(-2, 2),
|
||||||
disableForReducedMotion: true
|
disableForReducedMotion: true
|
||||||
});
|
});
|
||||||
@ -44,3 +49,15 @@
|
|||||||
frame();
|
frame();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<canvas style="height: 100vh; width: 100vw" bind:this={confettiCanvas} />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
canvas {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -40,6 +40,8 @@
|
|||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drapery {
|
.drapery {
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
height: 50%;
|
||||||
|
max-height: 50vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper > div {
|
.wrapper > div {
|
||||||
@ -33,6 +35,8 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
will-change: contents;
|
will-change: contents;
|
||||||
transform: translateZ(1px);
|
transform: translateZ(1px);
|
||||||
|
height: 100%;
|
||||||
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper > div :global(path) {
|
.wrapper > div :global(path) {
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import maskenball from './maskenball.svg?raw';
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<p>Willkommen zum Maskenball</p>
|
||||||
{@html maskenball}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import './maskenball.css';
|
p {
|
||||||
|
font-family: Parisienne;
|
||||||
div {
|
color: #d9c556;
|
||||||
margin-top: 10px;
|
text-align: center;
|
||||||
position: relative;
|
font-size: 4em;
|
||||||
display: flex;
|
margin-top: 0em;
|
||||||
z-index: 5;
|
animation: fadeIn 5s ease forwards;
|
||||||
align-items: center;
|
animation-delay: 3s;
|
||||||
justify-content: center;
|
opacity: 0;
|
||||||
filter: drop-shadow(0px 0px 40px #be8630aa) drop-shadow(0px 0px 5px black)
|
transition: font-size 3s ease;
|
||||||
drop-shadow(0px 0px 5px black) drop-shadow(0px 0px 5px black);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div :global(path) {
|
@keyframes fadeIn {
|
||||||
animation-duration: 5s !important;
|
from {
|
||||||
animation-delay: 3s !important;
|
opacity: 0;
|
||||||
animation-fill-mode: forwards !important;
|
transform: translateY(-5%) scale(0.8);
|
||||||
stroke: #d9c556;
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0%) scale(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
22
src/lib/helpers/chatgpt.ts
Normal file
22
src/lib/helpers/chatgpt.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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;
|
||||||
|
}
|
83
src/lib/helpers/localStore.ts
Normal file
83
src/lib/helpers/localStore.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { writable as internal, type Writable } from 'svelte/store'
|
||||||
|
|
||||||
|
declare type Updater<T> = (value: T) => T;
|
||||||
|
declare type StoreDict<T> = { [key: string]: Writable<T> }
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
interface Stores {
|
||||||
|
local: StoreDict<any>,
|
||||||
|
session: StoreDict<any>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const stores: Stores = {
|
||||||
|
local: {},
|
||||||
|
session: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Serializer<T> {
|
||||||
|
parse(text: string): T
|
||||||
|
stringify(object: T): string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StorageType = 'local' | 'session'
|
||||||
|
|
||||||
|
export interface Options<T> {
|
||||||
|
serializer?: Serializer<T>
|
||||||
|
storage?: StorageType
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStorage(type: StorageType) {
|
||||||
|
return type === 'local' ? localStorage : sessionStorage
|
||||||
|
}
|
||||||
|
export function persisted<T>(key: string, initialValue: T, options?: Options<T>): Writable<T> {
|
||||||
|
const serializer = options?.serializer ?? JSON
|
||||||
|
const storageType = options?.storage ?? 'local'
|
||||||
|
const browser = typeof (window) !== 'undefined' && typeof (document) !== 'undefined'
|
||||||
|
const storage = browser ? getStorage(storageType) : null
|
||||||
|
|
||||||
|
function updateStorage(key: string, value: T) {
|
||||||
|
storage?.setItem(key, serializer.stringify(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stores[storageType][key]) {
|
||||||
|
const store = internal(initialValue, (set) => {
|
||||||
|
const json = storage?.getItem(key)
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
set(<T>serializer.parse(json))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser && storageType == 'local') {
|
||||||
|
const handleStorage = (event: StorageEvent) => {
|
||||||
|
if (event.key === key)
|
||||||
|
set(event.newValue ? serializer.parse(event.newValue) : null)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("storage", handleStorage)
|
||||||
|
|
||||||
|
return () => window.removeEventListener("storage", handleStorage)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { subscribe, set } = store
|
||||||
|
|
||||||
|
stores[storageType][key] = {
|
||||||
|
set(value: T) {
|
||||||
|
updateStorage(key, value)
|
||||||
|
set(value)
|
||||||
|
},
|
||||||
|
update(callback: Updater<T>) {
|
||||||
|
return store.update((last) => {
|
||||||
|
const value = callback(last)
|
||||||
|
|
||||||
|
updateStorage(key, value)
|
||||||
|
|
||||||
|
return value
|
||||||
|
})
|
||||||
|
},
|
||||||
|
subscribe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stores[storageType][key]
|
||||||
|
}
|
20
src/lib/helpers/s3.ts
Normal file
20
src/lib/helpers/s3.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import Minio from 'minio'
|
||||||
|
|
||||||
|
import { S3_ENDPOINT_URL, S3_SECRET_ACCESS_KEY, S3_BUCKET_NAME, S3_ACCESS_KEY } from "$env/static/private"
|
||||||
|
|
||||||
|
const minioClient = new Minio.Client({
|
||||||
|
endPoint: S3_ENDPOINT_URL,
|
||||||
|
port: 80,
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: S3_ACCESS_KEY,
|
||||||
|
secretKey: S3_SECRET_ACCESS_KEY,
|
||||||
|
})
|
||||||
|
|
||||||
|
export function putObject(fileName: string, content: Buffer, metadata: Minio.ItemBucketMetadata = {}) {
|
||||||
|
return minioClient.putObject(S3_BUCKET_NAME, fileName, content, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listBuckets() {
|
||||||
|
return minioClient.listBuckets();
|
||||||
|
}
|
||||||
|
|
48
src/lib/helpers/stability.ts
Normal file
48
src/lib/helpers/stability.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { DREAM_API_KEY } from "$env/static/private";
|
||||||
|
|
||||||
|
const path =
|
||||||
|
"https://api.stability.ai/v1/generation/stable-diffusion-xl-1024-v1-0/text-to-image";
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: DREAM_API_KEY,
|
||||||
|
};
|
||||||
|
export async function generateImage(prompt: string, negativePrompt: string) {
|
||||||
|
const body = {
|
||||||
|
steps: 10,
|
||||||
|
width: 832,
|
||||||
|
height: 1216,
|
||||||
|
seed: Math.floor(Math.random() * 100000),
|
||||||
|
cfg_scale: 5,
|
||||||
|
samples: 1,
|
||||||
|
style_preset: "fantasy-art",
|
||||||
|
text_prompts: [
|
||||||
|
{
|
||||||
|
"text": prompt,
|
||||||
|
"weight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": negativePrompt,
|
||||||
|
"weight": -1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
path,
|
||||||
|
{
|
||||||
|
headers,
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Non-200 response: ${await response.text()}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseJSON = await response.json();
|
||||||
|
|
||||||
|
return responseJSON.artifacts[0];
|
||||||
|
}
|
@ -1,16 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import './global.css';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
<style global>
|
|
||||||
:global(html, body) {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background-color: black;
|
|
||||||
/* background-image: url(/confetti.png); */
|
|
||||||
background-size: 80%;
|
|
||||||
backdrop-filter: brightness(0.5);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,25 +1,36 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import FadeIn from '$lib/components/FadeIn.svelte';
|
||||||
|
import Questions from '$lib/components/Questions.svelte';
|
||||||
|
import TextSplit from '$lib/components/TextSplit.svelte';
|
||||||
import Button from '$lib/components/button.svelte';
|
import Button from '$lib/components/button.svelte';
|
||||||
import Confetti from '$lib/components/confetti.svelte';
|
import Confetti from '$lib/components/confetti.svelte';
|
||||||
import Curtains from '$lib/components/curtains.svelte';
|
import Curtains from '$lib/components/curtains.svelte';
|
||||||
import Mask from '$lib/components/mask.svelte';
|
import Mask from '$lib/components/mask.svelte';
|
||||||
import Maskenball from '$lib/components/maskenball.svelte';
|
import Maskenball from '$lib/components/maskenball.svelte';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
let curtainsVisible = false;
|
let curtainsVisible = false;
|
||||||
let buttonVisible = false;
|
let buttonVisible = false;
|
||||||
let maskVisible = false;
|
let maskVisible = false;
|
||||||
|
let maskSmall = false;
|
||||||
|
let contentVisible = false;
|
||||||
|
let questionVisible = false;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
curtainsVisible = true;
|
// curtainsVisible = true;
|
||||||
|
curtainsVisible = false;
|
||||||
|
maskVisible = true;
|
||||||
|
maskSmall = true;
|
||||||
|
questionVisible = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
buttonVisible = true;
|
// buttonVisible = true;
|
||||||
}, 1500);
|
}, 1500);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Confetti />
|
<Confetti />
|
||||||
<Curtains visible={curtainsVisible} />
|
<Curtains visible={curtainsVisible} />
|
||||||
<div class="center">
|
<div class="center" class:maskSmall>
|
||||||
{#if maskVisible}
|
{#if maskVisible}
|
||||||
<Mask />
|
<Mask />
|
||||||
<Maskenball />
|
<Maskenball />
|
||||||
@ -28,28 +39,75 @@
|
|||||||
{#if buttonVisible}
|
{#if buttonVisible}
|
||||||
<span class="enter-button" class:visible={buttonVisible}>
|
<span class="enter-button" class:visible={buttonVisible}>
|
||||||
<Button
|
<Button
|
||||||
|
--font-size="2em"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
curtainsVisible = false;
|
curtainsVisible = false;
|
||||||
buttonVisible = false;
|
buttonVisible = false;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
maskVisible = true;
|
maskVisible = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
maskSmall = true;
|
||||||
|
contentVisible = true;
|
||||||
|
}, 7000);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}}>Enter the Dungeon</Button
|
}}>Enter the Dungeon</Button
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if contentVisible}
|
||||||
|
<div class="einladung" out:fade>
|
||||||
|
<TextSplit
|
||||||
|
content="Wir laden dich herzlich ein, an unserer exklusiven Silvesterparty teilzunehmen, die dieses Jahr im magischen Ambiente eines Maskenballs stattfindet. Tauche ein in eine Nacht voller Geheimnisse, Eleganz und festlichem Glanz."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span in:fade={{ delay: 8000, duration: 1000 }}>
|
||||||
|
<Button
|
||||||
|
on:click={() => {
|
||||||
|
contentVisible = false;
|
||||||
|
questionVisible = true;
|
||||||
|
}}>Einladung annehmen</Button
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if questionVisible}
|
||||||
|
<Questions />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.center {
|
.center {
|
||||||
display: grid;
|
display: flex;
|
||||||
position: absolute;
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
align-items: center;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
transform: translateY(-5%);
|
|
||||||
place-content: center;
|
place-content: center;
|
||||||
|
transition: height 3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center.maskSmall {
|
||||||
|
height: 30vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.center.maskSmall > p) {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.einladung {
|
||||||
|
font-family: Parisienne;
|
||||||
|
font-size: 2em;
|
||||||
|
max-width: 500px;
|
||||||
|
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.enter-button {
|
.enter-button {
|
||||||
@ -68,9 +126,4 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(canvas) {
|
|
||||||
position: relative !important;
|
|
||||||
z-index: -1 !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
27
src/routes/api/ai/image/[name]/+server.ts
Normal file
27
src/routes/api/ai/image/[name]/+server.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { json } from "@sveltejs/kit";
|
||||||
|
import type { RequestHandler } from "./$types";
|
||||||
|
import { putObject } from "$lib/helpers/s3";
|
||||||
|
import { generateImage } from "$lib/helpers/stability";
|
||||||
|
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ params }) => {
|
||||||
|
|
||||||
|
const inputName = params.name;
|
||||||
|
|
||||||
|
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 negativePrompt = "blurry, multiple persons, picture frame"
|
||||||
|
const a = performance.now()
|
||||||
|
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 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}`
|
||||||
|
})
|
||||||
|
}
|
25
src/routes/api/ai/image/[name]/GET.ts
Normal file
25
src/routes/api/ai/image/[name]/GET.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { json } from "@sveltejs/kit";
|
||||||
|
import type { RequestHandler } from "./$types";
|
||||||
|
import { putObject } from "$lib/helpers/s3";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ params }) => {
|
||||||
|
|
||||||
|
const inputName = params.name;
|
||||||
|
|
||||||
|
const prompt = `upper body realistic portrait oil painting of a masked ${inputName}, Baroque, Charles Vess, masked ball attire, Charles Vess, opulence, mystery, elegance, dark long hair`;
|
||||||
|
const negativePrompt = "blurry, multiple persons, picture frame";
|
||||||
|
|
||||||
|
const image = generateImage(prompt, negativePrompt);
|
||||||
|
|
||||||
|
const imageName = `txt2img_${image.seed}.png`;
|
||||||
|
|
||||||
|
const res = await putObject(imageName, Buffer.from(image.base64, 'base64'), { contentType: "image/png" });
|
||||||
|
|
||||||
|
return json({
|
||||||
|
...res,
|
||||||
|
url: `https://s3.app.max-richter.dev/silvester23/${imageName}`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
14
src/routes/api/ai/name/[name]/+server.ts
Normal file
14
src/routes/api/ai/name/[name]/+server.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { type RequestHandler } from "@sveltejs/kit";
|
||||||
|
import { chat } from "$lib/helpers/chatgpt";
|
||||||
|
|
||||||
|
|
||||||
|
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 res = await chat(prompt);
|
||||||
|
|
||||||
|
return new Response(res);
|
||||||
|
}
|
8
src/routes/api/s3/+server.ts
Normal file
8
src/routes/api/s3/+server.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { getBuckets } from "$lib/helpers/s3";
|
||||||
|
import { json } from "@sveltejs/kit";
|
||||||
|
import type { RequestHandler } from "./$types";
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async () => {
|
||||||
|
const buckets = await getBuckets()
|
||||||
|
return json(buckets)
|
||||||
|
}
|
19
src/routes/global.css
Normal file
19
src/routes/global.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
/* parisienne-regular - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: 'Parisienne';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('/parisienne-v13-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: black;
|
||||||
|
/* background-image: url(/confetti.png); */
|
||||||
|
background-size: 80%;
|
||||||
|
backdrop-filter: brightness(0.5);
|
||||||
|
}
|
BIN
static/out/txt2img_2317708937.png
Normal file
BIN
static/out/txt2img_2317708937.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
BIN
static/parisienne-v13-latin-regular.woff2
Executable file
BIN
static/parisienne-v13-latin-regular.woff2
Executable file
Binary file not shown.
Loading…
Reference in New Issue
Block a user