Compare commits

...

21 Commits

Author SHA1 Message Date
BOT Alex c7f3fc625e sync 2024-10-01 08:47:27 +02:00
BOT Alex 376ff493b7 Changed background video to transparent 2024-09-11 15:11:02 +02:00
SveskeJuice 4a0e9509bc use node adapter 2024-08-28 15:16:54 +02:00
SveskeJuice 0cc874258d update deps 2024-08-28 15:08:47 +02:00
BOT Alex 76e855cf5f sync. idk 2024-08-21 20:50:54 +02:00
BOTAlex d2a418f4c4 progress sync 2024-08-20 22:06:08 +02:00
BOTAlex 0c5747e0bf Tons of changes 2024-08-08 02:55:25 +02:00
BOTAlex 46c06773cd Added speculative/preview to commands 2024-07-31 04:37:29 +02:00
BOTAlex 184f2617cd Anoter WASM version bump 2024-07-31 02:56:58 +02:00
BOTAlex 44e4559794 OpenBirch WASM version bump 2024-07-31 00:48:48 +02:00
BOTAlex 1718b16c4d Removed print statement 2024-07-29 18:20:44 +02:00
BOTAlex 0b6c3ba827 Nice! got DaisyUI themes to work! 2024-07-29 18:05:03 +02:00
BOTAlex b381426a05 Working console in svelte version!!! 2024-07-29 04:15:54 +02:00
BOTAlex 532a0503f4 console progress 2024-07-28 16:05:15 +02:00
BOTAlex dab7166472 sync before sleep 2024-07-27 06:32:45 +02:00
BOTAlex 036c5a4ebb Added background video 2024-07-27 06:06:38 +02:00
BOTAlex 4eff399404 Started progress on svelte OpenBirch 2024-07-24 02:55:52 +02:00
BOTAlex 7c5e96b921 didn't save iguess 2024-07-13 03:22:46 +02:00
BOTAlex fa9c60a6b7 A quick and dirty website is up and running. But blazor is needed here 2024-07-12 04:20:29 +02:00
BOTAlex 3ad51ebe28 Setup SvelteKit + Tailwind + DaisyUI + SCSS 2024-07-12 02:46:25 +02:00
BOTAlex f87bb3ef02 Clear worksspace/folder 2024-07-12 02:09:11 +02:00
50 changed files with 23603 additions and 1697 deletions

18
.gitignore vendored
View File

@ -1,10 +1,22 @@
.DS_Store
node_modules
/build
# Output
.output
.vercel
/.svelte-kit
/package
/build
# OS
.DS_Store
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
/.vs

View File

@ -0,0 +1,42 @@
# create-svelte
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app
npm create svelte@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.

View File

@ -1,18 +1,15 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
"target": "ES2020",
"jsx": "react",
"allowImportingTsExtensions": true,
"strictNullChecks": true,
"strictFunctionTypes": true
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}

4355
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,37 @@
{
"name": "deprived-main-website",
"name": "openbirchmockupsite",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch"
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@fontsource/fira-mono": "^4.5.10",
"@neoconfetti/svelte": "^1.0.0",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/kit": "^2.5.25",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tailwindcss/typography": "^0.5.13",
"autoprefixer": "^10.4.19",
"daisyui": "^4.12.10",
"postcss": "^8.4.39",
"sass": "^1.77.8",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"svelte-check": "^3.8.4",
"tailwindcss": "^3.4.4",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^5.0.3"
},
"type": "module"
"type": "module",
"dependencies": {
"@sveltejs/adapter-node": "latest",
"@sveltejs/kit": "latest",
"linq": "^4.0.3",
"svelte-parallax": "^0.6.0",
"theme-change": "^2.5.0"
}
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

101
src/app.css Normal file
View File

@ -0,0 +1,101 @@
@import '@fontsource/fira-mono';
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--font-body: Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
--font-mono: 'Fira Mono', monospace;
--color-bg-0: rgb(202, 216, 228);
--color-bg-1: hsl(209, 36%, 86%);
--color-bg-2: hsl(224, 44%, 95%);
--color-theme-1: #ff3e00;
--color-theme-2: #4075a6;
--color-text: rgba(0, 0, 0, 0.7);
--column-width: 42rem;
--column-margin-top: 4rem;
font-family: var(--font-body);
color: var(--color-text);
}
body {
min-height: 100vh;
margin: 0;
}
h1,
h2,
p {
font-weight: 400;
}
p {
line-height: 1.5;
}
a {
color: var(--color-theme-1);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h1 {
font-size: 2rem;
text-align: center;
}
h2 {
font-size: 1rem;
}
pre {
font-size: 16px;
font-family: var(--font-mono);
background-color: rgba(255, 255, 255, 0.45);
border-radius: 3px;
box-shadow: 2px 2px 6px rgb(255 255 255 / 25%);
padding: 0.5em;
overflow-x: auto;
color: var(--color-text);
}
.text-column {
display: flex;
max-width: 48rem;
flex: 0.6;
flex-direction: column;
justify-content: center;
margin: 0 auto;
}
input,
button {
font-size: inherit;
font-family: inherit;
}
button:focus:not(:focus-visible) {
outline: none;
}
@media (min-width: 720px) {
h1 {
font-size: 2.4rem;
}
}
.visually-hidden {
border: 0;
clip: rect(0 0 0 0);
height: auto;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}

View File

@ -1,18 +1,20 @@
<!doctype html>
<html lang="en">
<html lang="en" data-theme="coffee">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Change theme for site here -->
<link rel="stylesheet" href="/stylesheets/main-theme.css" />
<link rel="stylesheet" href="/stylesheets/global.css" />
%sveltekit.head%
<!-- IMPORTANT: Remove before production!!! -->
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.10/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<script src="./OpenBirchWasm/OpenBirchWrapper.js"></script>
<script src="./OpenBirchWasm/OpenBirch.js"></script>
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
<body data-sveltekit-preload-data="hover" class="bg-base-200">
<div style="display: contents;">%sveltekit.body%</div>
</body>
</html>

View File

@ -0,0 +1,60 @@
import { writable } from 'svelte/store';
// Adds window.runEval into typescript env
declare function runEval(expression: string, stopAssignments?: boolean): any;
declare function resetOpenBirch(): any;
const OpenBirchHistory: OpenBirchConsoleEntry[] = [];
function createOpenBirchInstance(){
const {subscribe, set, update } = writable(OpenBirchHistory);
return {
subscribe,
sendCommand: (command: string) => {
OpenBirchHistory.push(new OpenBirchConsoleEntry(ConsoleSender.User, command));
set(OpenBirchHistory); // Publishes changes to subscribers
let result: string = runEval(command);
OpenBirchHistory.push(new OpenBirchConsoleEntry(ConsoleSender.System, result));
set(OpenBirchHistory); // Publishes changes to subscribers
//console.log(OpenBirchHistory);
},
speculateCommand: (command: string): string => {
try {
return runEval(command, true);
} catch (e) {
return '';
}
},
resetState: () => {
resetOpenBirch();
},
getHistory: (filter: ConsoleSender | undefined = undefined): OpenBirchConsoleEntry[] => {
if (filter == undefined)
return OpenBirchHistory;
return OpenBirchHistory.filter(x=>x.sender == filter);
}
}
}
export const OpenBirch = createOpenBirchInstance();
class OpenBirchConsoleEntry {
sender: ConsoleSender;
message: string;
constructor(sender: ConsoleSender, message: string){
this.sender = sender;
this.message = message;
}
}
export enum ConsoleSender {
User,
System
}

16
src/lib/images/github.svg Normal file
View File

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-3 -3 30 30">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5229 6.47715 22 12 22C17.5229 22 22 17.5229 22 12C22 6.47715 17.5229 2 12 2ZM0 12C0 5.3726 5.3726 0 12 0C18.6274 0 24 5.3726 24 12C24 18.6274 18.6274 24 12 24C5.3726 24 0 18.6274 0 12Z"
fill="rgba(0,0,0,0.7)"
stroke="none"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M9.59162 22.7357C9.49492 22.6109 9.49492 21.4986 9.59162 19.399C8.55572 19.4347 7.90122 19.3628 7.62812 19.1833C7.21852 18.9139 6.80842 18.0833 6.44457 17.4979C6.08072 16.9125 5.27312 16.8199 4.94702 16.6891C4.62091 16.5582 4.53905 16.0247 5.84562 16.4282C7.15222 16.8316 7.21592 17.9303 7.62812 18.1872C8.04032 18.4441 9.02572 18.3317 9.47242 18.1259C9.91907 17.9201 9.88622 17.1538 9.96587 16.8503C10.0666 16.5669 9.71162 16.5041 9.70382 16.5018C9.26777 16.5018 6.97697 16.0036 6.34772 13.7852C5.71852 11.5669 6.52907 10.117 6.96147 9.49369C7.24972 9.07814 7.22422 8.19254 6.88497 6.83679C8.11677 6.67939 9.06732 7.06709 9.73672 7.99999C9.73737 8.00534 10.6143 7.47854 12.0001 7.47854C13.386 7.47854 13.8777 7.90764 14.2571 7.99999C14.6365 8.09234 14.94 6.36699 17.2834 6.83679C16.7942 7.79839 16.3844 8.99999 16.6972 9.49369C17.0099 9.98739 18.2372 11.5573 17.4833 13.7852C16.9807 15.2706 15.9927 16.1761 14.5192 16.5018C14.3502 16.5557 14.2658 16.6427 14.2658 16.7627C14.2658 16.9427 14.4942 16.9624 14.8233 17.8058C15.0426 18.368 15.0585 19.9739 14.8708 22.6234C14.3953 22.7445 14.0254 22.8257 13.7611 22.8673C13.2924 22.9409 12.7835 22.9822 12.2834 22.9982C11.7834 23.0141 11.6098 23.0123 10.9185 22.948C10.4577 22.9051 10.0154 22.8343 9.59162 22.7357Z"
fill="rgba(0,0,0,0.7)"
stroke="none"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/lib/images/goku.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
src/lib/images/hoeless.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.1566,22.8189c-10.4-14.8851-30.94-19.2971-45.7914-9.8348L22.2825,29.6078A29.9234,29.9234,0,0,0,8.7639,49.6506a31.5136,31.5136,0,0,0,3.1076,20.2318A30.0061,30.0061,0,0,0,7.3953,81.0653a31.8886,31.8886,0,0,0,5.4473,24.1157c10.4022,14.8865,30.9423,19.2966,45.7914,9.8348L84.7167,98.3921A29.9177,29.9177,0,0,0,98.2353,78.3493,31.5263,31.5263,0,0,0,95.13,58.117a30,30,0,0,0,4.4743-11.1824,31.88,31.88,0,0,0-5.4473-24.1157" style="fill:#ff3e00"/><path d="M45.8171,106.5815A20.7182,20.7182,0,0,1,23.58,98.3389a19.1739,19.1739,0,0,1-3.2766-14.5025,18.1886,18.1886,0,0,1,.6233-2.4357l.4912-1.4978,1.3363.9815a33.6443,33.6443,0,0,0,10.203,5.0978l.9694.2941-.0893.9675a5.8474,5.8474,0,0,0,1.052,3.8781,6.2389,6.2389,0,0,0,6.6952,2.485,5.7449,5.7449,0,0,0,1.6021-.7041L69.27,76.281a5.4306,5.4306,0,0,0,2.4506-3.631,5.7948,5.7948,0,0,0-.9875-4.3712,6.2436,6.2436,0,0,0-6.6978-2.4864,5.7427,5.7427,0,0,0-1.6.7036l-9.9532,6.3449a19.0329,19.0329,0,0,1-5.2965,2.3259,20.7181,20.7181,0,0,1-22.2368-8.2427,19.1725,19.1725,0,0,1-3.2766-14.5024,17.9885,17.9885,0,0,1,8.13-12.0513L55.8833,23.7472a19.0038,19.0038,0,0,1,5.3-2.3287A20.7182,20.7182,0,0,1,83.42,29.6611a19.1739,19.1739,0,0,1,3.2766,14.5025,18.4,18.4,0,0,1-.6233,2.4357l-.4912,1.4978-1.3356-.98a33.6175,33.6175,0,0,0-10.2037-5.1l-.9694-.2942.0893-.9675a5.8588,5.8588,0,0,0-1.052-3.878,6.2389,6.2389,0,0,0-6.6952-2.485,5.7449,5.7449,0,0,0-1.6021.7041L37.73,51.719a5.4218,5.4218,0,0,0-2.4487,3.63,5.7862,5.7862,0,0,0,.9856,4.3717,6.2437,6.2437,0,0,0,6.6978,2.4864,5.7652,5.7652,0,0,0,1.602-.7041l9.9519-6.3425a18.978,18.978,0,0,1,5.2959-2.3278,20.7181,20.7181,0,0,1,22.2368,8.2427,19.1725,19.1725,0,0,1,3.2766,14.5024,17.9977,17.9977,0,0,1-8.13,12.0532L51.1167,104.2528a19.0038,19.0038,0,0,1-5.3,2.3287" style="fill:#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -1 +0,0 @@
// place files you want to import through the `$lib` alias in this folder.

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

View File

@ -1 +0,0 @@
export const prerender = true;

45
src/routes/+layout.svelte Normal file
View File

@ -0,0 +1,45 @@
<script>
import Header from './Header.svelte';
import '../app.css';
import Footer from './Footer.svelte';
</script>
<div class="app">
<Header />
<main>
<slot />
</main>
<Footer/>
</div>
<style>
.app {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
width: 100%;
}
footer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 12px;
}
footer a {
font-weight: bold;
}
@media (min-width: 480px) {
footer {
padding: 12px 0;
}
}
</style>

View File

@ -1,17 +1,205 @@
<div class="main-title">
<h1>The Deprived Devs</h1>
<script lang="ts">
import Counter from "./Counter.svelte";
import welcome from "$lib/images/svelte-welcome.webp";
import welcome_fallback from "$lib/images/svelte-welcome.png";
import differencialBackgroundVideo from "$lib/videos/DifferentialEquationTransparent.gif";
import "tailwindcss/tailwind.css";
import ConsoleText from "./console/ConsoleText.svelte";
import { OpenBirch } from "$lib/OpenBirchService";
const sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
let isAutoTyping = true;
let textField: HTMLInputElement;
async function startTyping() {
for (var example of exampleInputs) {
for (let charecter of example) {
if (!isAutoTyping) break;
textField.value += charecter;
await sleep(200);
}
await sleep(500);
try {
OpenBirch.sendCommand(textField.value);
} catch (_) {}
resetFeild();
}
}
const exampleInputs: readonly string[] = [
"2+2",
"69*420+50",
"sin(2+4)",
"sqrt(9)",
"nroot(27, 3)",
"f(x):=2*x",
"f(5)",
"pi",
"e",
"myvar := 5",
"15 + myvar",
"myfunctionvar := f",
"myfunctionvar(10)",
"d/dx x",
"d/dx x^2",
"d/dx 69*x^2",
"d/dx sin(x)*x^2",
];
async function resetFeild() {
textField.value = "";
}
async function onInputClicked() {
OpenBirch.resetState();
window.location.href = "/console";
}
import { onMount } from "svelte";
import { Parallax, ParallaxLayer, StickyLayer } from "svelte-parallax";
onMount(() => {
resetFeild();
startTyping();
});
</script>
<svelte:head>
<title>Home</title>
<meta name="description" content="Svelte demo app" />
</svelte:head>
<div>
<Parallax sections={1.5} config={{ stiffness: 0.2, damping: 0.3 }}>
<ParallaxLayer offset={0} rate={0} span={0.6}>
<div class="relative h-full fill">
<img
class="backgroundVideoContainer"
src={differencialBackgroundVideo}
alt="Background"
/>
<div class="top-0 left-0 w-full h-full absolute fill place-content-center">
<div class="centerAndLimit double_centered_boxes">
<div>
<h1 class="text-neutral-content">
Welcome to<br />
<b class="text-primary">OpenBirch</b>
</h1>
</div>
<div></div>
<div class="join join-vertical pt-10">
<div class="join-item bg-primary-content">
<div
class="h-40 overflow-auto flex flex-col-reverse p-4"
>
<ConsoleText />
</div>
</div>
<div
class="input join-item input-bordered flex items-center grow gap-2"
style="cursor: text;"
>
<div class="w-full">
<div class="flex">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-8 w-8 opacity-70 block m-auto fill-primary"
>
<path
d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"
/>
</svg>
<input
autofocus
type="text"
class="text-primary grow"
placeholder=""
bind:this={textField}
on:click={onInputClicked}
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</ParallaxLayer>
<ParallaxLayer rate={0} offset={0.575} span={1} class="bg-base-100">
<div class="ma-10">
<div class="card shadow-xl lg:card-side bg-base-100">
<div class="card-body rounded-l-xl">
<h1 class="text-primary">What is it?</h1>
<p class="text-neutral-content">
I love the colour of yellow and I also love the
colour of yellow and I also love the colour of
yellow and I also love the colour of yellow and I
also love the colour of yellow and I also love the
colour of yellow and I also love the colour of
yellow and I also love the colour of yellow and I
also love the colour of yellow and I also love
</p>
</div>
<figure>
<img
src="https://img.daisyui.com/images/stock/photo-1494232410401-ad00d5433cfa.webp"
alt="Album"
/>
</figure>
</div>
<div class="card shadow-xl lg:card-side bg-base-100">
<div class="card-body rounded-l-xl">
<h1 class="text-primary">What is our mission?</h1>
<p class="text-neutral-content">
I love the colour of yellow and I also love the
colour of yellow and I also love the colour of
yellow and I also love the colour of yellow and I
also love
</p>
</div>
<figure>
<img
src="https://img.daisyui.com/images/stock/photo-1494232410401-ad00d5433cfa.webp"
alt="Album"
/>
</figure>
</div>
</div>
</ParallaxLayer>
</Parallax>
</div>
<style lang="scss">
.double_centered_boxes {
display: grid;
grid-template-columns: 1fr 1fr;
}
<style>
.main-title {
font-family: "CozetteVector";
margin: 0 auto;
width: 80%;
text-align: center;
}
.centerAndLimit {
max-width: 64rem;
min-width: 32rem;
width: 50%;
margin: 0 auto;
// border: red 1mm solid;
}
.main-title > h1 {
font-size: 4.5vw; /* Change if title changes */
}
.backgroundVideoContainer {
filter: blur(10px);
overflow: hidden;
}
.fill {
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.fill img {
flex-shrink: 0;
min-width: 100%;
min-height: 100%;
}
</style>

3
src/routes/+page.ts Normal file
View File

@ -0,0 +1,3 @@
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in production
export const prerender = true;

102
src/routes/Counter.svelte Normal file
View File

@ -0,0 +1,102 @@
<script lang="ts">
import { spring } from 'svelte/motion';
let count = 0;
const displayed_count = spring();
$: displayed_count.set(count);
$: offset = modulo($displayed_count, 1);
function modulo(n: number, m: number) {
// handle negative numbers
return ((n % m) + m) % m;
}
</script>
<div class="counter">
<button on:click={() => (count -= 1)} aria-label="Decrease the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5" />
</svg>
</button>
<div class="counter-viewport">
<div class="counter-digits" style="transform: translate(0, {100 * offset}%)">
<strong class="hidden" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
<strong>{Math.floor($displayed_count)}</strong>
</div>
</div>
<button on:click={() => (count += 1)} aria-label="Increase the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
</svg>
</button>
</div>
<style>
.counter {
display: flex;
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin: 1rem 0;
}
.counter button {
width: 2em;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border: 0;
background-color: transparent;
touch-action: manipulation;
font-size: 2rem;
}
.counter button:hover {
background-color: var(--color-bg-1);
}
svg {
width: 25%;
height: 25%;
}
path {
vector-effect: non-scaling-stroke;
stroke-width: 2px;
stroke: #444;
}
.counter-viewport {
width: 8em;
height: 4em;
overflow: hidden;
text-align: center;
position: relative;
}
.counter-viewport strong {
position: absolute;
display: flex;
width: 100%;
height: 100%;
font-weight: 400;
color: var(--color-theme-1);
font-size: 4rem;
align-items: center;
justify-content: center;
}
.counter-digits {
position: absolute;
width: 100%;
height: 100%;
}
.hidden {
top: -100%;
user-select: none;
}
</style>

83
src/routes/Header.svelte Normal file
View File

@ -0,0 +1,83 @@
<script>
import { page } from '$app/stores';
import goku from '$lib/images/goku.webp';
import "tailwindcss/tailwind.css";
import { onMount } from 'svelte'
import { themeChange } from 'theme-change'
// NOTE: the element that is using one of the theme attributes must be in the DOM on mount
onMount(() => {
themeChange(false)
// 👆 false parameter is required for svelte
})
let isDarkMode = true;
function toggleTheme(){
isDarkMode = !isDarkMode;
console.log("Darkmode: " + isDarkMode);
}
</script>
<header class="navbar bg-base-100">
<a tabindex="0" role="button" href="/" class="btn btn-ghost btn-circle avatar">
<div class="w-10 rounded-full">
<img
alt="Tailwind CSS Navbar component"
src={goku} />
</div>
</a>
<div class="flex-1"/>
<div class="flex-none">
<ul class="menu menu-horizontal px-1">
<li>
<!-- <button class="btn btn-outline btn-accent" on:click={toggleTheme}>Change theme test</button> -->
<select data-choose-theme>
<option value="light">Default</option>
<option value="dark">Dark</option>
<option value="cupcake">Cupcake</option>
<option value="bumblebee">Bumblebee</option>
<option value="emerald">Emerald</option>
<option value="corporate">Corporate</option>
<option value="synthwave">Synthwave</option>
<option value="retro">Retro</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="valentine">Valentine</option>
<option value="halloween">Halloween</option>
<option value="garden">Garden</option>
<option value="forest">Forest</option>
<option value="aqua">Aqua</option>
<option value="lofi">Lofi</option>
<option value="pastel">Pastel</option>
<option value="fantasy">Fantasy</option>
<option value="wireframe">Wireframe</option>
<option value="black">Black</option>
<option value="luxury">Luxury</option>
<option value="dracula">Dracula</option>
<option value="cmyk">CMYK</option>
<option value="autumn">Autumn</option>
<option value="business">Business</option>
<option value="acid">Acid</option>
<option value="lemonade">Lemonade</option>
<option value="night">Night</option>
<option value="coffee">Coffee</option>
<option value="winter">Winter</option>
<option value="dim">Dim</option>
<option value="nord">Nord</option>
<option value="sunset">Sunset</option>
</select>
</li>
<li>
<a href="/console">Console</a>
</li>
<li>
<a href="/pricing">Pricing</a>
</li>
</ul>
</div>
</header>
<style lang="scss">
</style>

View File

@ -1 +1,26 @@
<p>This is an informative about page :) </p>
<svelte:head>
<title>About</title>
<meta name="description" content="About this app" />
</svelte:head>
<div class="text-column">
<h1>About this app</h1>
<p>
This is a <a href="https://kit.svelte.dev">SvelteKit</a> app. You can make your own by typing the
following into your command line and following the prompts:
</p>
<pre>npm create svelte@latest</pre>
<p>
The page you're looking at is purely static HTML, with no client-side interactivity needed.
Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening
the devtools network panel and reloading.
</p>
<p>
The <a href="/sverdle">Sverdle</a> page illustrates SvelteKit's data loading and form handling. Try
using it with JavaScript disabled!
</p>
</div>

View File

@ -0,0 +1,9 @@
import { dev } from '$app/environment';
// we don't need any JS on this page, though we'll load
// it in dev so that we get hot module replacement
export const csr = dev;
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in production
export const prerender = true;

View File

@ -0,0 +1,102 @@
<script lang="ts">
import { OpenBirch, ConsoleSender } from "$lib/OpenBirchService";
import ConsoleText from "./ConsoleText.svelte";
import Enumerable from 'linq'
let inputField: HTMLInputElement;
async function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Enter') {
pushCommand();
} else if (event.key === 'ArrowUp') {
goBackInCommandHistory();
} else if (event.key === 'ArrowDown'){
goForwardInCommandHistory();
}
if (inputField.value == '' && event.key !== 'Enter') return;
setTimeout(() => {
updatePreviewCommand();
}, 25);
}
// Locally start sending command
function pushCommand(){
OpenBirch.sendCommand(inputField.value)
currentBackSelect = 0;
inputField.value = '';
}
let previewText: HTMLElement;
function updatePreviewCommand(){
previewText.innerHTML = OpenBirch.speculateCommand(inputField.value);
}
let currentBackSelect = 0;
function goBackInCommandHistory(){
currentBackSelect++;
updateInput();
}
function goForwardInCommandHistory(){
if (currentBackSelect > 0)
currentBackSelect--;
updateInput();
}
function updateInput(){
let history = OpenBirch.getHistory(ConsoleSender.User);
// let distinctHistory = Array.from(new Map(history.map(entry => [entry.message, entry])).values()); // .distinctBy(x=>x.message)
let distinctHistory = Enumerable.from(history).distinct(x=>x.message).toArray();
currentBackSelect = Math.min(distinctHistory.length, currentBackSelect); // Clamp to num of distinct history
inputField.value = distinctHistory[distinctHistory.length - currentBackSelect].message;
}
</script>
<div class="consoleContainer p-4">
<div class="p-4 flex grow flex-col bg-primary-content rounded-xl overflow-auto">
<div class="flex flex-col-reverse">
<ConsoleText/>
</div>
</div>
<div class="pt-4 join">
<div class="input join-item input-bordered flex items-center grow gap-2 h-20" style="cursor: text;">
<div class="w-full">
<div class="flex">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-8 w-8 opacity-70 block m-auto fill-primary">
<path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/>
</svg>
<input autofocus type="text" class="text-primary grow" placeholder="" bind:this={inputField} on:keydown={handleKeydown} />
</div>
<div class="text-accent opacity-50 italic" bind:this={previewText} style="line-height: 1rem; height: 0;"></div>
</div>
</div>
<button class="btn join-item btn-secondary h-20" on:click={pushCommand}>Execute</button>
</div>
</div>
<style lang="scss">
.consoleContainer {
display: flex;
flex-direction: column;
flex-grow: 1;
position: absolute;
right: 0;
left: 0;
bottom: 0;
top: 75px;
&:nth-child(1) {
flex: 0 0 90%;
}
&:nth-child(2) {
flex: 0 0 10%;
}
}
</style>

View File

@ -0,0 +1,13 @@
<script>
import { OpenBirch } from "$lib/OpenBirchService";
</script>
<ul class="prose">
{#each $OpenBirch as consoleEntry}
{#if consoleEntry.sender == 0}
<li class=""><b>&gt;</b> {" " + consoleEntry.message}</li>
{:else}
<li class="text-accent opacity-75"><b><i>{consoleEntry.message}</i></b></li>
{/if}
{/each}
</ul>

View File

@ -1 +0,0 @@

View File

@ -0,0 +1,190 @@
<script>
// Script
</script>
<main>
<div class="topbar"></div>
<div class="ads"></div>
<div class="content flex justify-center space-x-4 ">
<div class="card-container fullcontainer">
<div class="card w-96 shadow-xl bg-primary ">
<figure class="px-10 pt-10 ">
<img
src="https://i.redd.it/cxr3m537py6d1.jpeg"
alt="Shoes"
class="rounded-xl photo-card" />
</figure>
<div class="card-body items-center textbox-container">
<h2 class="card-title">Enterprise</h2>
<h1> Contact Us </h1>
<div class="card-actions">
<button class="btn btn-lg btn-secondary">Get Enterprise Usage</button>
</div>
<div class="textbox-container-decription">
<p class="big">Access to all features</p>
<p class="big">Access to an self-aware AI Girlfriend</p>
<p class="big">Access to private calls us everyday!</p>
<p class="big">Access to 1 Novemdecillion GB's of data</p>
</div>
</div>
</div>
</div>
<div class="card-container fullcontainer">
<div class="card w-96 shadow-xl bg-primary">
<figure class="px-10 pt-10 ">
<img
src="https://preview.redd.it/oh-hell-nah-v0-uflg6j7q8p6d1.jpeg?width=614&format=pjpg&auto=webp&s=27d308a5b6e203b3c06bab79320b003c90bda466"
alt="Shoes"
class="rounded-xl photo-card" />
</figure>
<div class="card-body items-center textbox-container">
<h2 class="card-title">Profesional</h2>
<h1> 0.00$ /month </h1>
<div class="card-actions">
<button class="btn btn-lg btn-secondary">Get Profesional Usage</button>
</div>
<div class="textbox-container-decription">
<p class="big">Access to all features</p>
<p class="big">Access to an machine learning AI</p>
<p class="big">Access to all of the CIA secrets</p>
<p class="big">Access to 1 Septillion GB's of data</p>
</div>
</div>
</div>
</div>
<div class="card-container fullcontainer ">
<div class="card w-96 shadow-xl bg-primary ">
<figure class="px-10 pt-10 ">
<img
src="https://i.redd.it/fp2psppzqm6d1.jpeg"
alt="Shoes"
class="rounded-xl photo-card" />
</figure>
<div class="card-body items-center textbox-container">
<h2 class="card-title">Profeional lite</h2>
<h1> 0$ /month </h1>
<div class="card-actions">
<button class="btn btn-lg btn-secondary">Get Profeional Lite Usage</button>
</div>
<div class="textbox-container-decription">
<p class="big ">Access to all features</p>
<p class="big">Access to blink while using the app</p>
<p class="big">Access to not talk to girls </p>
<p class="big">Access to 1 Quadrillion GB's of data</p>
</div>
</div>
</div>
</div>
<div class="card-container fullcontainer">
<div class="card w-96 shadow-xl bg-primary ">
<figure class="px-10 pt-10 ">
<img
src="https://preview.redd.it/hell-ye-jigsaw-you-a-goat-v0-qrhlgbaanu5d1.jpeg?width=961&format=pjpg&auto=webp&s=5dfd92e329eaef4353398c16313c38f65c755983"
alt="Shoes"
class="rounded-xl photo-card" />
</figure>
<div class="card-body items-center textbox-container">
<h2 class="card-title">Personal usage</h2>
<h1> Free </h1>
<div class="card-actions">
<button class="btn btn-lg btn-secondary">Get Personal Usage</button>
</div>
<div class="textbox-container-decription ">
<p class="big">Access to all features</p>
<p class="big">Access to breath while using the app</p>
<p class="big">Access to eat while mathing</p>
<p class="big">Access to 1 billion GB's of data</p>
</div>
</div>
</div>
</div>
</div>
<div class="sidebar"></div>
<div class="footer"></div>
</main>
<style>
main{
display: grid;
grid-template-areas:
"topbar topbar topbar"
"ads content sidebar"
"footer footer footer";
grid-template-columns: 1fr 3fr 1fr;
grid-template-rows: 50px auto 70px;
align-items: center;
}
.topbar {
grid-area: topbar;
text-align: center;
padding: 10px;
}
.ads {
grid-area: ads;
text-align: center;
padding: 10px;
}
.content {
grid-area: content;
text-align: center;
padding: 10px;
}
.sidebar {
grid-area: sidebar;
text-align: center;
padding: 10px;
}
.footer {
grid-area: footer;
text-align: center;
padding: 10px;
}
.fullcontainer{
outline: 3px solid black;
border-radius: 15px;
}
.photo-card {
height: 200px;
width: 300px;
outline: 3px solid black;
border-radius: 15px;
}
.textbox-container {
height: 700px;
}
.textbox-container-decription {
text-align: justify;
}
p.big {
line-height: 4;
}
</style>

View File

@ -0,0 +1,69 @@
import { fail } from '@sveltejs/kit';
import { Game } from './game';
import type { PageServerLoad, Actions } from './$types';
export const load = (({ cookies }) => {
const game = new Game(cookies.get('sverdle'));
return {
/**
* The player's guessed words so far
*/
guesses: game.guesses,
/**
* An array of strings like '__x_c' corresponding to the guesses, where 'x' means
* an exact match, and 'c' means a close match (right letter, wrong place)
*/
answers: game.answers,
/**
* The correct answer, revealed if the game is over
*/
answer: game.answers.length >= 6 ? game.answer : null
};
}) satisfies PageServerLoad;
export const actions = {
/**
* Modify game state in reaction to a keypress. If client-side JavaScript
* is available, this will happen in the browser instead of here
*/
update: async ({ request, cookies }) => {
const game = new Game(cookies.get('sverdle'));
const data = await request.formData();
const key = data.get('key');
const i = game.answers.length;
if (key === 'backspace') {
game.guesses[i] = game.guesses[i].slice(0, -1);
} else {
game.guesses[i] += key;
}
cookies.set('sverdle', game.toString(), { path: '/' });
},
/**
* Modify game state in reaction to a guessed word. This logic always runs on
* the server, so that people can't cheat by peeking at the JavaScript
*/
enter: async ({ request, cookies }) => {
const game = new Game(cookies.get('sverdle'));
const data = await request.formData();
const guess = data.getAll('guess') as string[];
if (!game.enter(guess)) {
return fail(400, { badGuess: true });
}
cookies.set('sverdle', game.toString(), { path: '/' });
},
restart: async ({ cookies }) => {
cookies.delete('sverdle', { path: '/' });
}
} satisfies Actions;

View File

@ -0,0 +1,411 @@
<script lang="ts">
import { confetti } from '@neoconfetti/svelte';
import { enhance } from '$app/forms';
import type { PageData, ActionData } from './$types';
import { reduced_motion } from './reduced-motion';
export let data: PageData;
export let form: ActionData;
/** Whether or not the user has won */
$: won = data.answers.at(-1) === 'xxxxx';
/** The index of the current guess */
$: i = won ? -1 : data.answers.length;
/** The current guess */
$: currentGuess = data.guesses[i] || '';
/** Whether the current guess can be submitted */
$: submittable = currentGuess.length === 5;
/**
* A map of classnames for all letters that have been guessed,
* used for styling the keyboard
*/
let classnames: Record<string, 'exact' | 'close' | 'missing'>;
/**
* A map of descriptions for all letters that have been guessed,
* used for adding text for assistive technology (e.g. screen readers)
*/
let description: Record<string, string>;
$: {
classnames = {};
description = {};
data.answers.forEach((answer, i) => {
const guess = data.guesses[i];
for (let i = 0; i < 5; i += 1) {
const letter = guess[i];
if (answer[i] === 'x') {
classnames[letter] = 'exact';
description[letter] = 'correct';
} else if (!classnames[letter]) {
classnames[letter] = answer[i] === 'c' ? 'close' : 'missing';
description[letter] = answer[i] === 'c' ? 'present' : 'absent';
}
}
});
}
/**
* Modify the game state without making a trip to the server,
* if client-side JavaScript is enabled
*/
function update(event: MouseEvent) {
const key = (event.target as HTMLButtonElement).getAttribute(
'data-key'
);
if (key === 'backspace') {
currentGuess = currentGuess.slice(0, -1);
if (form?.badGuess) form.badGuess = false;
} else if (currentGuess.length < 5) {
currentGuess += key;
}
}
/**
* Trigger form logic in response to a keydown event, so that
* desktop users can use the keyboard to play the game
*/
function keydown(event: KeyboardEvent) {
if (event.metaKey) return;
if (event.key === 'Enter' && !submittable) return;
document
.querySelector(`[data-key="${event.key}" i]`)
?.dispatchEvent(new MouseEvent('click', { cancelable: true }));
}
</script>
<svelte:window on:keydown={keydown} />
<svelte:head>
<title>Sverdle</title>
<meta name="description" content="A Wordle clone written in SvelteKit" />
</svelte:head>
<h1 class="visually-hidden">Sverdle</h1>
<form
method="POST"
action="?/enter"
use:enhance={() => {
// prevent default callback from resetting the form
return ({ update }) => {
update({ reset: false });
};
}}
>
<a class="how-to-play" href="/sverdle/how-to-play">How to play</a>
<div class="grid" class:playing={!won} class:bad-guess={form?.badGuess}>
{#each Array.from(Array(6).keys()) as row (row)}
{@const current = row === i}
<h2 class="visually-hidden">Row {row + 1}</h2>
<div class="row" class:current>
{#each Array.from(Array(5).keys()) as column (column)}
{@const guess = current ? currentGuess : data.guesses[row]}
{@const answer = data.answers[row]?.[column]}
{@const value = guess?.[column] ?? ''}
{@const selected = current && column === guess.length}
{@const exact = answer === 'x'}
{@const close = answer === 'c'}
{@const missing = answer === '_'}
<div class="letter" class:exact class:close class:missing class:selected>
{value}
<span class="visually-hidden">
{#if exact}
(correct)
{:else if close}
(present)
{:else if missing}
(absent)
{:else}
empty
{/if}
</span>
<input name="guess" disabled={!current} type="hidden" {value} />
</div>
{/each}
</div>
{/each}
</div>
<div class="controls">
{#if won || data.answers.length >= 6}
{#if !won && data.answer}
<p>the answer was "{data.answer}"</p>
{/if}
<button data-key="enter" class="restart selected" formaction="?/restart">
{won ? 'you won :)' : `game over :(`} play again?
</button>
{:else}
<div class="keyboard">
<button data-key="enter" class:selected={submittable} disabled={!submittable}>enter</button>
<button
on:click|preventDefault={update}
data-key="backspace"
formaction="?/update"
name="key"
value="backspace"
>
back
</button>
{#each ['qwertyuiop', 'asdfghjkl', 'zxcvbnm'] as row}
<div class="row">
{#each row as letter}
<button
on:click|preventDefault={update}
data-key={letter}
class={classnames[letter]}
disabled={submittable}
formaction="?/update"
name="key"
value={letter}
aria-label="{letter} {description[letter] || ''}"
>
{letter}
</button>
{/each}
</div>
{/each}
</div>
{/if}
</div>
</form>
{#if won}
<div
style="position: absolute; left: 50%; top: 30%"
use:confetti={{
particleCount: $reduced_motion ? 0 : undefined,
force: 0.7,
stageWidth: window.innerWidth,
stageHeight: window.innerHeight,
colors: ['#ff3e00', '#40b3ff', '#676778']
}}
></div>
{/if}
<style>
form {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1rem;
flex: 1;
}
.how-to-play {
color: var(--color-text);
}
.how-to-play::before {
content: 'i';
display: inline-block;
font-size: 0.8em;
font-weight: 900;
width: 1em;
height: 1em;
padding: 0.2em;
line-height: 1;
border: 1.5px solid var(--color-text);
border-radius: 50%;
text-align: center;
margin: 0 0.5em 0 0;
position: relative;
top: -0.05em;
}
.grid {
--width: min(100vw, 40vh, 380px);
max-width: var(--width);
align-self: center;
justify-self: center;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.grid .row {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-gap: 0.2rem;
margin: 0 0 0.2rem 0;
}
@media (prefers-reduced-motion: no-preference) {
.grid.bad-guess .row.current {
animation: wiggle 0.5s;
}
}
.grid.playing .row.current {
filter: drop-shadow(3px 3px 10px var(--color-bg-0));
}
.letter {
aspect-ratio: 1;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
box-sizing: border-box;
text-transform: lowercase;
border: none;
font-size: calc(0.08 * var(--width));
border-radius: 2px;
background: white;
margin: 0;
color: rgba(0, 0, 0, 0.7);
}
.letter.missing {
background: rgba(255, 255, 255, 0.5);
color: rgba(0, 0, 0, 0.5);
}
.letter.exact {
background: var(--color-theme-2);
color: white;
}
.letter.close {
border: 2px solid var(--color-theme-2);
}
.selected {
outline: 2px solid var(--color-theme-1);
}
.controls {
text-align: center;
justify-content: center;
height: min(18vh, 10rem);
}
.keyboard {
--gap: 0.2rem;
position: relative;
display: flex;
flex-direction: column;
gap: var(--gap);
height: 100%;
}
.keyboard .row {
display: flex;
justify-content: center;
gap: 0.2rem;
flex: 1;
}
.keyboard button,
.keyboard button:disabled {
--size: min(8vw, 4vh, 40px);
background-color: white;
color: black;
width: var(--size);
border: none;
border-radius: 2px;
font-size: calc(var(--size) * 0.5);
margin: 0;
}
.keyboard button.exact {
background: var(--color-theme-2);
color: white;
}
.keyboard button.missing {
opacity: 0.5;
}
.keyboard button.close {
border: 2px solid var(--color-theme-2);
}
.keyboard button:focus {
background: var(--color-theme-1);
color: white;
outline: none;
}
.keyboard button[data-key='enter'],
.keyboard button[data-key='backspace'] {
position: absolute;
bottom: 0;
width: calc(1.5 * var(--size));
height: calc(1 / 3 * (100% - 2 * var(--gap)));
text-transform: uppercase;
font-size: calc(0.3 * var(--size));
padding-top: calc(0.15 * var(--size));
}
.keyboard button[data-key='enter'] {
right: calc(50% + 3.5 * var(--size) + 0.8rem);
}
.keyboard button[data-key='backspace'] {
left: calc(50% + 3.5 * var(--size) + 0.8rem);
}
.keyboard button[data-key='enter']:disabled {
opacity: 0.5;
}
.restart {
width: 100%;
padding: 1rem;
background: rgba(255, 255, 255, 0.5);
border-radius: 2px;
border: none;
}
.restart:focus,
.restart:hover {
background: var(--color-theme-1);
color: white;
outline: none;
}
@keyframes wiggle {
0% {
transform: translateX(0);
}
10% {
transform: translateX(-2px);
}
30% {
transform: translateX(4px);
}
50% {
transform: translateX(-6px);
}
70% {
transform: translateX(+4px);
}
90% {
transform: translateX(-2px);
}
100% {
transform: translateX(0);
}
}
</style>

View File

@ -0,0 +1,75 @@
import { words, allowed } from './words.server';
export class Game {
index: number;
guesses: string[];
answers: string[];
answer: string;
/**
* Create a game object from the player's cookie, or initialise a new game
*/
constructor(serialized: string | undefined = undefined) {
if (serialized) {
const [index, guesses, answers] = serialized.split('-');
this.index = +index;
this.guesses = guesses ? guesses.split(' ') : [];
this.answers = answers ? answers.split(' ') : [];
} else {
this.index = Math.floor(Math.random() * words.length);
this.guesses = ['', '', '', '', '', ''];
this.answers = [];
}
this.answer = words[this.index];
}
/**
* Update game state based on a guess of a five-letter word. Returns
* true if the guess was valid, false otherwise
*/
enter(letters: string[]) {
const word = letters.join('');
const valid = allowed.has(word);
if (!valid) return false;
this.guesses[this.answers.length] = word;
const available = Array.from(this.answer);
const answer = Array(5).fill('_');
// first, find exact matches
for (let i = 0; i < 5; i += 1) {
if (letters[i] === available[i]) {
answer[i] = 'x';
available[i] = ' ';
}
}
// then find close matches (this has to happen
// in a second step, otherwise an early close
// match can prevent a later exact match)
for (let i = 0; i < 5; i += 1) {
if (answer[i] === '_') {
const index = available.indexOf(letters[i]);
if (index !== -1) {
answer[i] = 'c';
available[index] = ' ';
}
}
}
this.answers.push(answer.join(''));
return true;
}
/**
* Serialize game state so it can be set as a cookie
*/
toString() {
return `${this.index}-${this.guesses.join(' ')}-${this.answers.join(' ')}`;
}
}

View File

@ -0,0 +1,95 @@
<svelte:head>
<title>How to play Sverdle</title>
<meta name="description" content="How to play Sverdle" />
</svelte:head>
<div class="text-column">
<h1>How to play Sverdle</h1>
<p>
Sverdle is a clone of <a href="https://www.nytimes.com/games/wordle/index.html">Wordle</a>, the
word guessing game. To play, enter a five-letter English word. For example:
</p>
<div class="example">
<span class="close">r</span>
<span class="missing">i</span>
<span class="close">t</span>
<span class="missing">z</span>
<span class="exact">y</span>
</div>
<p>
The <span class="exact">y</span> is in the right place. <span class="close">r</span> and
<span class="close">t</span>
are the right letters, but in the wrong place. The other letters are wrong, and can be discarded.
Let's make another guess:
</p>
<div class="example">
<span class="exact">p</span>
<span class="exact">a</span>
<span class="exact">r</span>
<span class="exact">t</span>
<span class="exact">y</span>
</div>
<p>This time we guessed right! You have <strong>six</strong> guesses to get the word.</p>
<p>
Unlike the original Wordle, Sverdle runs on the server instead of in the browser, making it
impossible to cheat. It uses <code>&lt;form&gt;</code> and cookies to submit data, meaning you can
even play with JavaScript disabled!
</p>
</div>
<style>
span {
display: inline-flex;
justify-content: center;
align-items: center;
font-size: 0.8em;
width: 2.4em;
height: 2.4em;
background-color: white;
box-sizing: border-box;
border-radius: 2px;
border-width: 2px;
color: rgba(0, 0, 0, 0.7);
}
.missing {
background: rgba(255, 255, 255, 0.5);
color: rgba(0, 0, 0, 0.5);
}
.close {
border-style: solid;
border-color: var(--color-theme-2);
}
.exact {
background: var(--color-theme-2);
color: white;
}
.example {
display: flex;
justify-content: flex-start;
margin: 1rem 0;
gap: 0.2rem;
}
.example span {
font-size: 1.4rem;
}
p span {
position: relative;
border-width: 1px;
border-radius: 1px;
font-size: 0.4em;
transform: scale(2) translate(0, -10%);
margin: 0 1em;
}
</style>

View File

@ -0,0 +1,9 @@
import { dev } from '$app/environment';
// we don't need any JS on this page, though we'll load
// it in dev so that we get hot module replacement
export const csr = dev;
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in production
export const prerender = true;

View File

@ -0,0 +1,23 @@
import { readable } from 'svelte/store';
import { browser } from '$app/environment';
const reduced_motion_query = '(prefers-reduced-motion: reduce)';
const get_initial_motion_preference = () => {
if (!browser) return false;
return window.matchMedia(reduced_motion_query).matches;
};
export const reduced_motion = readable(get_initial_motion_preference(), (set) => {
if (browser) {
const set_reduced_motion = (event: MediaQueryListEvent) => {
set(event.matches);
};
const media_query_list = window.matchMedia(reduced_motion_query);
media_query_list.addEventListener('change', set_reduced_motion);
return () => {
media_query_list.removeEventListener('change', set_reduced_motion);
};
}
});

File diff suppressed because it is too large Load Diff

6020
static/OpenBirchWasm/OpenBirch.js Executable file

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,38 @@
// This is old code but works, so i won't touch it ¯\_(ツ)_/¯
function str2C(s) {
var size = lengthBytesUTF8(s) + 1;
var ret = _malloc(size);
stringToUTF8Array(s, HEAP8, ret, size);
return ret;
}
window.runEval = function (inputText, stopAssignments = false) {
if (!stopAssignments) console.log("input: " + inputText);
var inputPtr = str2C(inputText);
var resultPointer = Module._eval(inputPtr, stopAssignments);
var resultString = "";
// Convert resultPointer (char*) to JavaScript string
// Here, we treat it as ASCII and convert byte by byte
if (resultPointer) {
var i = 0;
while (true) {
var charCode = Module.getValue(resultPointer + i, 'i8');
if (charCode === 0) break;
resultString += String.fromCharCode(charCode);
i++;
}
}
if (!stopAssignments) console.log("Result from eval:", resultString);
// Free allocated memory
_free(resultPointer);
return resultString;
}
window.resetOpenBirch = function () {
Module._resetState();
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

3
static/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -1,12 +0,0 @@
/* --- FONTS --- */
@font-face {
font-family: "CozetteVector";
src:
local("CozetteVector"),
url("/fonts/CozetteVector.ttf") format("truetype");
}
body {
color: var(--text);
background-color: var(--background);
}

View File

@ -1,7 +0,0 @@
:root {
--text: #ece4ee;
--background: #120c13;
--primary: #ff8552;
--secondary: #6c6b44;
--accent: #7da16a;
}

View File

@ -1,17 +1,15 @@
import adapter from '@sveltejs/adapter-static';
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
export default {
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
prerender: {
handleHttpError: 'fail'
},
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: undefined,
precompress: false,
strict: true
})
adapter: adapter()
}
};
export default config;

50
tailwind.config.js Normal file
View File

@ -0,0 +1,50 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,svelte,js,ts}'],
theme: {
extend: {},
},
plugins: [require("@tailwindcss/typography"), require('daisyui')],
daisyui: {
themes: [
"light",
"dark",
"cupcake",
"bumblebee",
"emerald",
"corporate",
"synthwave",
"retro",
"cyberpunk",
"valentine",
"halloween",
"garden",
"forest",
"aqua",
"lofi",
"pastel",
"fantasy",
"wireframe",
"black",
"luxury",
"dracula",
"cmyk",
"autumn",
"business",
"acid",
"lemonade",
"night",
"coffee",
"winter",
"dim",
"nord",
"sunset",], // false: only light + dark | true: all themes | array: specific themes like this ["light", "dark", "cupcake"]
base: true, // applies background color and foreground color for root element by default
styled: true, // include daisyUI colors and design decisions for all components
utils: true, // adds responsive and modifier utility classes
prefix: "", // prefix for daisyUI classnames (components, modifiers and responsive class names. Not colors)
logs: true, // Shows info about daisyUI version and used config in the console when building your CSS
themeRoot: ":root", // The element that receives theme color CSS variables
},
}

19
tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}