void: Finish porting the main source
diff --git a/apps/void/src/app.d.ts b/apps/void/src/app.d.ts
new file mode 100644
index 0000000..f59b884
--- /dev/null
+++ b/apps/void/src/app.d.ts
@@ -0,0 +1,12 @@
+// See https://kit.svelte.dev/docs/types#app
+// for information about these interfaces
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/apps/void/src/app.html b/apps/void/src/app.html
new file mode 100644
index 0000000..effe0d0
--- /dev/null
+++ b/apps/void/src/app.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <link rel="icon" href="%sveltekit.assets%/favicon.png" />
+ <meta name="viewport" content="width=device-width" />
+ %sveltekit.head%
+ </head>
+ <body data-sveltekit-preload-data="hover">
+ <div style="display: contents">%sveltekit.body%</div>
+ </body>
+</html>
diff --git a/apps/void/src/app.postcss b/apps/void/src/app.postcss
new file mode 100644
index 0000000..76589de
--- /dev/null
+++ b/apps/void/src/app.postcss
@@ -0,0 +1,49 @@
+/* Write your global styles here, in PostCSS syntax */
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 224 71% 4%;
+ --foreground: 213 31% 91%;
+
+ --muted: 223 47% 11%;
+ --muted-foreground: 215.4 16.3% 56.9%;
+
+ --popover: 224 71% 4%;
+ --popover-foreground: 215 20.2% 65.1%;
+
+ --card: 224 71% 4%;
+ --card-foreground: 213 31% 91%;
+
+ --border: 216 34% 17%;
+ --input: 216 34% 17%;
+
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 1.2%;
+
+ --secondary: 222.2 47.4% 11.2%;
+ --secondary-foreground: 210 40% 98%;
+
+ --accent: 216 34% 17%;
+ --accent-foreground: 210 40% 98%;
+
+ --destructive: 359 51% 48%;
+ --destructive-foreground: 210 40% 98%;
+
+ --ring: 216 34% 17%;
+
+ --radius: 0.5rem;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ font-feature-settings: 'rlig' 1, 'calt' 1;
+ }
+}
diff --git a/apps/void/src/index.test.ts b/apps/void/src/index.test.ts
new file mode 100644
index 0000000..e07cbbd
--- /dev/null
+++ b/apps/void/src/index.test.ts
@@ -0,0 +1,7 @@
+import { describe, it, expect } from 'vitest';
+
+describe('sum test', () => {
+ it('adds 1 + 2 to equal 3', () => {
+ expect(1 + 2).toBe(3);
+ });
+});
diff --git a/apps/void/src/lib/utils.ts b/apps/void/src/lib/utils.ts
new file mode 100644
index 0000000..256f86f
--- /dev/null
+++ b/apps/void/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/apps/void/src/routes/+layout.svelte b/apps/void/src/routes/+layout.svelte
new file mode 100644
index 0000000..9b019ec
--- /dev/null
+++ b/apps/void/src/routes/+layout.svelte
@@ -0,0 +1,9 @@
+<script>
+ import { BackgroundStars } from '$components/decoration/BackgroundStars';
+ import '../app.postcss';
+ import '../app.postcss';
+</script>
+
+<slot />
+
+<BackgroundStars starCount={20} />
diff --git a/apps/void/src/routes/+page.server.ts b/apps/void/src/routes/+page.server.ts
new file mode 100644
index 0000000..19b40a3
--- /dev/null
+++ b/apps/void/src/routes/+page.server.ts
@@ -0,0 +1,14 @@
+export const actions = {
+ default: async ({ request }: { request: any }) => {
+ const data = await request.formData();
+ const message = data.get("message");
+ const pastMessages = data.get("pastMessages");
+ const reset = data.get("reset");
+
+ if (!reset) {
+ return { message, pastMessages, reset: false };
+ } else {
+ return { message: "", pastMessages: null, reset: true };
+ }
+ }
+}
diff --git a/apps/void/src/routes/+page.svelte b/apps/void/src/routes/+page.svelte
new file mode 100644
index 0000000..894b5c2
--- /dev/null
+++ b/apps/void/src/routes/+page.svelte
@@ -0,0 +1,100 @@
+<script lang="ts">
+ import Button from '$components/ui/button/Button.svelte';
+ import Input from '$components/ui/input/Input.svelte';
+ import { onMount } from 'svelte';
+ import { flip } from 'svelte/animate';
+ import { fade } from 'svelte/transition';
+
+ export let form;
+
+ let messageInput: HTMLInputElement;
+ function focusMessageInput() {
+ messageInput.focus();
+ }
+
+ let systemMessage =
+ 'Welcome to the void. Everything you put here is completely private. Messages will fade as they go up the page and disappear after a few minutes, or you can refresh the page to clear them too. Let out your frustrations and watch them slip away into the void...';
+ const resetMessage =
+ "Poof: it's gone; hopefully you feel a little better after letting your frustrations out. If there's something you still need to vent about feel free to continue. I'm still here to listen...";
+ let messages: string[] = [];
+ let currentlyTyped: string = '';
+
+ if (form?.pastMessages) {
+ messages = form?.pastMessages.toString().split('\n');
+ }
+
+ if (form?.message) {
+ messages.push(form?.message.toString());
+ }
+
+ if (form?.reset) {
+ systemMessage = resetMessage;
+ }
+
+ onMount(focusMessageInput);
+</script>
+
+<form
+ on:submit|preventDefault={() => {
+ messages.push(currentlyTyped);
+ messages = messages;
+ currentlyTyped = '';
+ focusMessageInput();
+ }}
+ method="post"
+ class="p-2 flex flex-col h-screen gap-2 justify-end pt-0"
+>
+ <div class="flex p-2 pt-0 flex-col justify-end overflow-hidden pastMessages">
+ {#each (messages.length === 0 ? [systemMessage] : ['']).concat(messages) as message, index (`${index} ${message}`)}
+ <p class:message="{index !== 0}" transition:fade={{ duration: 100 }} animate:flip={{ duration: 100 }}>
+ {message}
+ </p>
+ {/each}
+ <input type="hidden" value={messages.join('\n')} name="pastMessages" />
+ </div>
+
+ <div class="flex w-full gap-2">
+ <Input
+ bind:element={messageInput}
+ bind:value={currentlyTyped}
+ name="message"
+ placeholder="Write down your worries, and let them slip into the void"
+ />
+ <Button type="submit">Send</Button>
+ <Button
+ name="reset"
+ value="reset"
+ on:click={() => {
+ currentlyTyped = '';
+ systemMessage = resetMessage;
+ messages = [systemMessage];
+ }}>Clean</Button
+ >
+ </div>
+</form>
+
+<style lang="postcss">
+ div.pastMessages::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%);
+ }
+
+ @keyframes message-fade-out {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+ }
+
+ p.message {
+ animation: message-fade-out 150s ease-in-out forwards;
+ }
+</style>