testing
Initial commit
Torsten Dittmann 3h ago 8176c2021494e25c6c275f76a140245263f0cbe4
root commit — shown against the empty tree
added
.gitignoreadded
.npmrcadded
.vscode/extensions.jsonadded
README.mdadded
bun.lockadded
package.jsonadded
src/app.d.tsadded
src/app.htmladded
src/lib/images/github.svgadded
src/lib/images/svelte-logo.svgadded
src/routes/+layout.svelteadded
src/routes/+page.svelteadded
src/routes/+page.tsadded
src/routes/Counter.svelteadded
src/routes/Header.svelteadded
src/routes/about/+page.svelteadded
src/routes/about/+page.tsadded
src/routes/layout.cssadded
src/routes/sverdle/+page.server.tsadded
src/routes/sverdle/+page.svelteadded
src/routes/sverdle/game.tsadded
src/routes/sverdle/how-to-play/+page.svelteadded
src/routes/sverdle/how-to-play/+page.tsadded
src/routes/sverdle/words.server.tsadded
static/favicon.svgadded
static/robots.txtadded
tsconfig.jsonadded
vite.config.tsadded
src/lib/images/svelte-welcome.pngadded
src/lib/images/svelte-welcome.webp 1 +
node_modules 2 +
3 +
# Output 4 +
.output 5 +
.vercel 6 +
.netlify 7 +
.wrangler 8 +
/.svelte-kit 9 +
/build 10 +
11 +
# OS 12 +
.DS_Store 13 +
Thumbs.db 14 +
15 +
# Env 16 +
.env 17 +
.env.* 18 +
!.env.example 19 +
!.env.test 20 +
21 +
# Vite 22 +
vite.config.js.timestamp-* 23 +
vite.config.ts.timestamp-* 1 +
engine-strict=true 1 +
{ 2 +
"recommendations": ["svelte.svelte-vscode"] 3 +
} 1 +
# sv 2 +
3 +
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). 4 +
5 +
## Creating a project 6 +
7 +
If you're seeing this, you've probably already done this step. Congrats! 8 +
9 +
```sh 10 +
# create a new project 11 +
npx sv create my-app 12 +
``` 13 +
14 +
To recreate this project with the same configuration: 15 +
16 +
```sh 17 +
# recreate this project 18 +
bun x sv@0.17.0 create --template demo --types ts --install bun better-hub-test-prods 19 +
``` 20 +
21 +
## Developing 22 +
23 +
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 24 +
25 +
```sh 26 +
npm run dev 27 +
28 +
# or start the server and open the app in a new browser tab 29 +
npm run dev -- --open 30 +
``` 31 +
32 +
## Building 33 +
34 +
To create a production version of your app: 35 +
36 +
```sh 37 +
npm run build 38 +
``` 39 +
40 +
You can preview the production build with `npm run preview`. 41 +
42 +
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. 1 +
{ 2 +
"lockfileVersion": 2, 3 +
"configVersion": 1, 4 +
"workspaces": { 5 +
"": { 6 +
"name": "better-hub-test-prods", 7 +
"devDependencies": { 8 +
"@fontsource/fira-mono": "^5.2.7", 9 +
"@neoconfetti/svelte": "^2.2.2", 10 +
"@sveltejs/adapter-auto": "^7.0.1", 11 +
"@sveltejs/kit": "^2.63.0", 12 +
"@sveltejs/vite-plugin-svelte": "^7.1.2", 13 +
"svelte": "^5.56.1", 14 +
"svelte-check": "^4.6.0", 15 +
"typescript": "^6.0.3", 16 +
"vite": "^8.0.16", 17 +
}, 18 +
}, 19 +
}, 20 +
"packages": { 21 +
"@fontsource/fira-mono": ["@fontsource/fira-mono@5.3.0", "", {}, "sha512-FZzRwKhpoXQU1uhEGKUg5AHUUdfiaTXxGCdBBeGHUmcZaqEDEmndSVvpXHkSViH25f/UiWfhcWTib+6A8o4qeQ=="], 22 +
23 +
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 24 +
25 +
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], 26 +
27 +
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 28 +
29 +
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 30 +
31 +
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 32 +
33 +
"@neoconfetti/svelte": ["@neoconfetti/svelte@2.2.2", "", { "peerDependencies": { "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, "sha512-E7xCFVEEm5Ctnj2udTJy1b9oaTvjz1zi1mYdEtE8rB5BVwq6kHisosDS+zdWN5PMfEMjtbsOV9Cl6tsNSAD1sA=="], 34 +
35 +
"@oxc-project/types": ["@oxc-project/types@0.146.0", "", {}, "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA=="], 36 +
37 +
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], 38 +
39 +
"@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.5", "", { "os": "android", "cpu": "arm" }, "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA=="], 40 +
41 +
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.5", "", { "os": "android", "cpu": "arm64" }, "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig=="], 42 +
43 +
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww=="], 44 +
45 +
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A=="], 46 +
47 +
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw=="], 48 +
49 +
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.5", "", { "os": "linux", "cpu": "arm" }, "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg=="], 50 +
51 +
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA=="], 52 +
53 +
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA=="], 54 +
55 +
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA=="], 56 +
57 +
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ=="], 58 +
59 +
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ=="], 60 +
61 +
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA=="], 62 +
63 +
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.5", "", { "os": "none", "cpu": "arm64" }, "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw=="], 64 +
65 +
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw=="], 66 +
67 +
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.5", "", { "os": "win32", "cpu": "x64" }, "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw=="], 68 +
69 +
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], 70 +
71 +
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], 72 +
73 +
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.13", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ=="], 74 +
75 +
"@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@7.0.1", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ=="], 76 +
77 +
"@sveltejs/kit": ["@sveltejs/kit@2.70.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3 || ^6.0.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg=="], 78 +
79 +
"@sveltejs/load-config": ["@sveltejs/load-config@0.2.3", "", {}, "sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ=="], 80 +
81 +
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@7.3.0", "", { "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^1.0.0", "obug": "^2.1.0", "vitefu": "^1.1.2" }, "peerDependencies": { "svelte": "^5.46.4", "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "sha512-QbRoJyD92e9R0ufeQIWRHrCC0ObcqSv/aBDdrQMoU+sypav3cDx5wytdQ6GLdXjEMO6xjrXGzfkUygng8JMv0A=="], 82 +
83 +
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], 84 +
85 +
"@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], 86 +
87 +
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], 88 +
89 +
"acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], 90 +
91 +
"aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="], 92 +
93 +
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], 94 +
95 +
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], 96 +
97 +
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 98 +
99 +
"cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], 100 +
101 +
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], 102 +
103 +
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 104 +
105 +
"devalue": ["devalue@5.9.1", "", {}, "sha512-+17vil3EVQRzvtDJSFuTWEb8XJRvXqAiV3qZyQWD398QeXUa6CxsUyMdD1fxzEhUrd4FojitFz7lhIHBTlV4fw=="], 106 +
107 +
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 108 +
109 +
"esrap": ["esrap@2.3.6", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, "peerDependencies": { "@typescript-eslint/types": "^8.2.0" }, "optionalPeers": ["@typescript-eslint/types"] }, "sha512-yc0OC12UjPqLoc+fe+v5GNs4TOjAigUw3sTikfC+xeBPGUw7gDRz3DtYaqEhxyMVJojcSWJw7jT0QWR+CbuE/A=="], 110 +
111 +
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 112 +
113 +
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 114 +
115 +
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], 116 +
117 +
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], 118 +
119 +
"lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], 120 +
121 +
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], 122 +
123 +
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="], 124 +
125 +
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="], 126 +
127 +
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="], 128 +
129 +
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="], 130 +
131 +
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="], 132 +
133 +
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="], 134 +
135 +
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="], 136 +
137 +
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="], 138 +
139 +
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="], 140 +
141 +
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="], 142 +
143 +
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], 144 +
145 +
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 146 +
147 +
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], 148 +
149 +
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], 150 +
151 +
"nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], 152 +
153 +
"obug": ["obug@2.1.4", "", {}, "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA=="], 154 +
155 +
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 156 +
157 +
"picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], 158 +
159 +
"postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="], 160 +
161 +
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 162 +
163 +
"rolldown": ["rolldown@1.2.5", "", { "dependencies": { "@oxc-project/types": "=0.146.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.5", "@rolldown/binding-android-arm64": "1.2.5", "@rolldown/binding-darwin-arm64": "1.2.5", "@rolldown/binding-darwin-x64": "1.2.5", "@rolldown/binding-freebsd-x64": "1.2.5", "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", "@rolldown/binding-linux-arm64-gnu": "1.2.5", "@rolldown/binding-linux-arm64-musl": "1.2.5", "@rolldown/binding-linux-ppc64-gnu": "1.2.5", "@rolldown/binding-linux-s390x-gnu": "1.2.5", "@rolldown/binding-linux-x64-gnu": "1.2.5", "@rolldown/binding-linux-x64-musl": "1.2.5", "@rolldown/binding-openharmony-arm64": "1.2.5", "@rolldown/binding-win32-arm64-msvc": "1.2.5", "@rolldown/binding-win32-x64-msvc": "1.2.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA=="], 164 +
165 +
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], 166 +
167 +
"set-cookie-parser": ["set-cookie-parser@3.1.2", "", {}, "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw=="], 168 +
169 +
"sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], 170 +
171 +
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 172 +
173 +
"svelte": ["svelte@5.56.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.10", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.8.1", "esm-env": "^1.2.1", "esrap": "^2.2.12", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-Lcxbj8I/KAbpY+VjtY4ENQBV0dDCipfGAhqb51XQZ67CIQqXgsv/8dPkbILaj4Fb6/b6JAEM/PIVbILXgDQy2g=="], 174 +
175 +
"svelte-check": ["svelte-check@4.7.6", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "@sveltejs/load-config": "^0.2.3", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.0.0 || ^6.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-t2scM//ZuVbSY/T2w6FSBw1v9s2NEmh/g+sy1lqtosW5ylBV5AF4wFb1Ts9Kf3MbfPDUDJDZ9L436YT0SPTdvw=="], 176 +
177 +
"tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], 178 +
179 +
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], 180 +
181 +
"typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], 182 +
183 +
"vite": ["vite@8.2.2", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.26", "rolldown": "~1.2.4", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0 || ^0.5.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q=="], 184 +
185 +
"vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], 186 +
187 +
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], 188 +
189 +
"@sveltejs/vite-plugin-svelte/magic-string": ["magic-string@1.2.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-veT/+7iXrXzT39XnEN4lOxtNl72dMgJ8Lp+5Bd6YcMSWpb0n0MjBM8Uuooi6jgJr8dhUW2swQgBmoZVMni5SVg=="], 190 +
} 191 +
} 1 +
{ 2 +
"name": "better-hub-test-prods", 3 +
"private": true, 4 +
"version": "0.0.1", 5 +
"type": "module", 6 +
"scripts": { 7 +
"dev": "vite dev", 8 +
"build": "vite build", 9 +
"preview": "vite preview", 10 +
"prepare": "svelte-kit sync || echo ''", 11 +
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 12 +
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" 13 +
}, 14 +
"devDependencies": { 15 +
"@fontsource/fira-mono": "^5.2.7", 16 +
"@neoconfetti/svelte": "^2.2.2", 17 +
"@sveltejs/adapter-auto": "^7.0.1", 18 +
"@sveltejs/kit": "^2.63.0", 19 +
"@sveltejs/vite-plugin-svelte": "^7.1.2", 20 +
"svelte": "^5.56.1", 21 +
"svelte-check": "^4.6.0", 22 +
"typescript": "^6.0.3", 23 +
"vite": "^8.0.16" 24 +
} 25 +
} 1 +
// See https://svelte.dev/docs/kit/types#app.d.ts 2 +
// for information about these interfaces 3 +
declare global { 4 +
namespace App { 5 +
// interface Error {} 6 +
// interface Locals {} 7 +
// interface PageData {} 8 +
// interface PageState {} 9 +
// interface Platform {} 10 +
} 11 +
} 12 +
13 +
export {}; 1 +
<!doctype html> 2 +
<html lang="en"> 3 +
<head> 4 +
<meta charset="utf-8" /> 5 +
<link rel="icon" href="%sveltekit.assets%/favicon.svg" /> 6 +
<meta name="viewport" content="width=device-width, initial-scale=1" /> 7 +
<meta name="text-scale" content="scale" /> 8 +
%sveltekit.head% 9 +
</head> 10 +
<body data-sveltekit-preload-data="hover"> 11 +
<div style="display: contents">%sveltekit.body%</div> 12 +
</body> 13 +
</html> 1 +
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg> 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> 1 +
<script lang="ts"> 2 +
import Header from './Header.svelte'; 3 +
import './layout.css'; 4 +
5 +
let { children } = $props(); 6 +
</script> 7 +
8 +
<div class="app"> 9 +
<Header /> 10 +
11 +
<main> 12 +
{@render children()} 13 +
</main> 14 +
15 +
<footer> 16 +
<p> 17 +
visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to learn about SvelteKit 18 +
</p> 19 +
</footer> 20 +
</div> 21 +
22 +
<style> 23 +
.app { 24 +
display: flex; 25 +
flex-direction: column; 26 +
min-height: 100vh; 27 +
} 28 +
29 +
main { 30 +
flex: 1; 31 +
display: flex; 32 +
flex-direction: column; 33 +
padding: 1rem; 34 +
width: 100%; 35 +
max-width: 64rem; 36 +
margin: 0 auto; 37 +
box-sizing: border-box; 38 +
} 39 +
40 +
footer { 41 +
display: flex; 42 +
flex-direction: column; 43 +
justify-content: center; 44 +
align-items: center; 45 +
padding: 12px; 46 +
} 47 +
48 +
footer a { 49 +
font-weight: bold; 50 +
} 51 +
52 +
@media (min-width: 480px) { 53 +
footer { 54 +
padding: 12px 0; 55 +
} 56 +
} 57 +
</style> 1 +
<script lang="ts"> 2 +
import welcomeFallback from '$lib/images/svelte-welcome.png'; 3 +
import welcome from '$lib/images/svelte-welcome.webp'; 4 +
5 +
import Counter from './Counter.svelte'; 6 +
</script> 7 +
8 +
<svelte:head> 9 +
<title>Home</title> 10 +
<meta name="description" content="Svelte demo app" /> 11 +
</svelte:head> 12 +
13 +
<section> 14 +
<h1> 15 +
<span class="welcome"> 16 +
<picture> 17 +
<source srcset={welcome} type="image/webp" /> 18 +
<img src={welcomeFallback} alt="Welcome" /> 19 +
</picture> 20 +
</span> 21 +
22 +
to your new<br />SvelteKit app 23 +
</h1> 24 +
25 +
<h2> 26 +
try editing <strong>src/routes/+page.svelte</strong> 27 +
</h2> 28 +
29 +
<Counter /> 30 +
</section> 31 +
32 +
<style> 33 +
section { 34 +
display: flex; 35 +
flex-direction: column; 36 +
justify-content: center; 37 +
align-items: center; 38 +
flex: 0.6; 39 +
} 40 +
41 +
h1 { 42 +
width: 100%; 43 +
} 44 +
45 +
.welcome { 46 +
display: block; 47 +
position: relative; 48 +
width: 100%; 49 +
height: 0; 50 +
padding: 0 0 calc(100% * 495 / 2048) 0; 51 +
} 52 +
53 +
.welcome img { 54 +
position: absolute; 55 +
width: 100%; 56 +
height: 100%; 57 +
top: 0; 58 +
display: block; 59 +
} 60 +
</style> 1 +
// since there's no dynamic data here, we can prerender 2 +
// it so that it gets served as a static asset in production 3 +
export const prerender = true; 1 +
<script lang="ts"> 2 +
import { Spring } from 'svelte/motion'; 3 +
4 +
const count = new Spring(0); 5 +
const offset = $derived(modulo(count.current, 1)); 6 +
7 +
function modulo(n: number, m: number) { 8 +
// handle negative numbers 9 +
return ((n % m) + m) % m; 10 +
} 11 +
</script> 12 +
13 +
<div class="counter"> 14 +
<button onclick={() => (count.target -= 1)} aria-label="Decrease the counter by one"> 15 +
<svg aria-hidden="true" viewBox="0 0 1 1"> 16 +
<path d="M0,0.5 L1,0.5" /> 17 +
</svg> 18 +
</button> 19 +
20 +
<div class="counter-viewport"> 21 +
<div class="counter-digits" style="transform: translate(0, {100 * offset}%)"> 22 +
<strong class="hidden" aria-hidden="true">{Math.floor(count.current + 1)}</strong> 23 +
<strong>{Math.floor(count.current)}</strong> 24 +
</div> 25 +
</div> 26 +
27 +
<button onclick={() => (count.target += 1)} aria-label="Increase the counter by one"> 28 +
<svg aria-hidden="true" viewBox="0 0 1 1"> 29 +
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" /> 30 +
</svg> 31 +
</button> 32 +
</div> 33 +
34 +
<style> 35 +
.counter { 36 +
display: flex; 37 +
border-top: 1px solid rgba(0, 0, 0, 0.1); 38 +
border-bottom: 1px solid rgba(0, 0, 0, 0.1); 39 +
margin: 1rem 0; 40 +
} 41 +
42 +
.counter button { 43 +
width: 2em; 44 +
padding: 0; 45 +
display: flex; 46 +
align-items: center; 47 +
justify-content: center; 48 +
border: 0; 49 +
background-color: transparent; 50 +
touch-action: manipulation; 51 +
font-size: 2rem; 52 +
} 53 +
54 +
.counter button:hover { 55 +
background-color: var(--color-bg-1); 56 +
} 57 +
58 +
svg { 59 +
width: 25%; 60 +
height: 25%; 61 +
} 62 +
63 +
path { 64 +
vector-effect: non-scaling-stroke; 65 +
stroke-width: 2px; 66 +
stroke: #444; 67 +
} 68 +
69 +
.counter-viewport { 70 +
width: 8em; 71 +
height: 4em; 72 +
overflow: hidden; 73 +
text-align: center; 74 +
position: relative; 75 +
} 76 +
77 +
.counter-viewport strong { 78 +
position: absolute; 79 +
display: flex; 80 +
width: 100%; 81 +
height: 100%; 82 +
font-weight: 400; 83 +
color: var(--color-theme-1); 84 +
font-size: 4rem; 85 +
align-items: center; 86 +
justify-content: center; 87 +
} 88 +
89 +
.counter-digits { 90 +
position: absolute; 91 +
width: 100%; 92 +
height: 100%; 93 +
} 94 +
95 +
.hidden { 96 +
top: -100%; 97 +
user-select: none; 98 +
} 99 +
</style> 1 +
<script lang="ts"> 2 +
import { resolve } from '$app/paths'; 3 +
import { page } from '$app/state'; 4 +
import github from '$lib/images/github.svg'; 5 +
import logo from '$lib/images/svelte-logo.svg'; 6 +
</script> 7 +
8 +
<header> 9 +
<div class="corner"> 10 +
<a href="https://svelte.dev/docs/kit"> 11 +
<img src={logo} alt="SvelteKit" /> 12 +
</a> 13 +
</div> 14 +
15 +
<nav> 16 +
<svg viewBox="0 0 2 3" aria-hidden="true"> 17 +
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" /> 18 +
</svg> 19 +
<ul> 20 +
<li aria-current={page.url.pathname === '/' ? 'page' : undefined}> 21 +
<a href={resolve('/')}>Home</a> 22 +
</li> 23 +
<li aria-current={page.url.pathname === '/about' ? 'page' : undefined}> 24 +
<a href={resolve('/about')}>About</a> 25 +
</li> 26 +
<li aria-current={page.url.pathname.startsWith('/sverdle') ? 'page' : undefined}> 27 +
<a href={resolve('/sverdle')}>Sverdle</a> 28 +
</li> 29 +
</ul> 30 +
<svg viewBox="0 0 2 3" aria-hidden="true"> 31 +
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" /> 32 +
</svg> 33 +
</nav> 34 +
35 +
<div class="corner"> 36 +
<a href="https://github.com/sveltejs/kit"> 37 +
<img src={github} alt="GitHub" /> 38 +
</a> 39 +
</div> 40 +
</header> 41 +
42 +
<style> 43 +
header { 44 +
display: flex; 45 +
justify-content: space-between; 46 +
} 47 +
48 +
.corner { 49 +
width: 3em; 50 +
height: 3em; 51 +
} 52 +
53 +
.corner a { 54 +
display: flex; 55 +
align-items: center; 56 +
justify-content: center; 57 +
width: 100%; 58 +
height: 100%; 59 +
} 60 +
61 +
.corner img { 62 +
width: 2em; 63 +
height: 2em; 64 +
object-fit: contain; 65 +
} 66 +
67 +
nav { 68 +
display: flex; 69 +
justify-content: center; 70 +
--background: rgba(255, 255, 255, 0.7); 71 +
} 72 +
73 +
svg { 74 +
width: 2em; 75 +
height: 3em; 76 +
display: block; 77 +
} 78 +
79 +
path { 80 +
fill: var(--background); 81 +
} 82 +
83 +
ul { 84 +
position: relative; 85 +
padding: 0; 86 +
margin: 0; 87 +
height: 3em; 88 +
display: flex; 89 +
justify-content: center; 90 +
align-items: center; 91 +
list-style: none; 92 +
background: var(--background); 93 +
background-size: contain; 94 +
} 95 +
96 +
li { 97 +
position: relative; 98 +
height: 100%; 99 +
} 100 +
101 +
li[aria-current='page']::before { 102 +
--size: 6px; 103 +
content: ''; 104 +
width: 0; 105 +
height: 0; 106 +
position: absolute; 107 +
top: 0; 108 +
left: calc(50% - var(--size)); 109 +
border: var(--size) solid transparent; 110 +
border-top: var(--size) solid var(--color-theme-1); 111 +
} 112 +
113 +
nav a { 114 +
display: flex; 115 +
height: 100%; 116 +
align-items: center; 117 +
padding: 0 0.5rem; 118 +
color: var(--color-text); 119 +
font-weight: 700; 120 +
font-size: 0.8rem; 121 +
text-transform: uppercase; 122 +
letter-spacing: 0.1em; 123 +
text-decoration: none; 124 +
transition: color 0.2s linear; 125 +
} 126 +
127 +
a:hover { 128 +
color: var(--color-theme-1); 129 +
} 130 +
</style> 1 +
<script lang="ts"> 2 +
import { resolve } from '$app/paths'; 3 +
</script> 4 +
5 +
<svelte:head> 6 +
<title>About</title> 7 +
<meta name="description" content="About this app" /> 8 +
</svelte:head> 9 +
10 +
<div class="text-column"> 11 +
<h1>About this app</h1> 12 +
13 +
<p> 14 +
This is a <a href="https://svelte.dev/docs/kit">SvelteKit</a> app. You can make your own by typing 15 +
the following into your command line and following the prompts: 16 +
</p> 17 +
18 +
<pre>npx sv create</pre> 19 +
20 +
<p> 21 +
The page you're looking at is purely static HTML, with no client-side interactivity needed. 22 +
Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening 23 +
the devtools network panel and reloading. 24 +
</p> 25 +
26 +
<p> 27 +
The <a href={resolve('/sverdle')}>Sverdle</a> page illustrates SvelteKit's data loading and form handling. 28 +
Try using it with JavaScript disabled! 29 +
</p> 30 +
</div> 1 +
import { dev } from '$app/environment'; 2 +
3 +
// we don't need any JS on this page, though we'll load 4 +
// it in dev so that we get hot module replacement 5 +
export const csr = dev; 6 +
7 +
// since there's no dynamic data here, we can prerender 8 +
// it so that it gets served as a static asset in production 9 +
export const prerender = true; 1 +
@import '@fontsource/fira-mono'; 2 +
3 +
:root { 4 +
--font-body: 5 +
Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 6 +
'Open Sans', 'Helvetica Neue', sans-serif; 7 +
--font-mono: 'Fira Mono', monospace; 8 +
--color-bg-0: rgb(202, 216, 228); 9 +
--color-bg-1: hsl(209, 36%, 86%); 10 +
--color-bg-2: hsl(224, 44%, 95%); 11 +
--color-theme-1: #ff3e00; 12 +
--color-theme-2: #4075a6; 13 +
--color-text: rgba(0, 0, 0, 0.7); 14 +
--column-width: 42rem; 15 +
--column-margin-top: 4rem; 16 +
font-family: var(--font-body); 17 +
color: var(--color-text); 18 +
} 19 +
20 +
body { 21 +
min-height: 100vh; 22 +
margin: 0; 23 +
background-attachment: fixed; 24 +
background-color: var(--color-bg-1); 25 +
background-size: 100vw 100vh; 26 +
background-image: 27 +
radial-gradient(50% 50% at 50% 50%, rgba(255, 255, 255, 0.75) 0%, rgba(255, 255, 255, 0) 100%), 28 +
linear-gradient(180deg, var(--color-bg-0) 0%, var(--color-bg-1) 15%, var(--color-bg-2) 50%); 29 +
} 30 +
31 +
h1, 32 +
h2, 33 +
p { 34 +
font-weight: 400; 35 +
} 36 +
37 +
p { 38 +
line-height: 1.5; 39 +
} 40 +
41 +
a { 42 +
color: var(--color-theme-1); 43 +
text-decoration: none; 44 +
} 45 +
46 +
a:hover { 47 +
text-decoration: underline; 48 +
} 49 +
50 +
h1 { 51 +
font-size: 2rem; 52 +
text-align: center; 53 +
} 54 +
55 +
h2 { 56 +
font-size: 1rem; 57 +
} 58 +
59 +
pre { 60 +
font-size: 16px; 61 +
font-family: var(--font-mono); 62 +
background-color: rgba(255, 255, 255, 0.45); 63 +
border-radius: 3px; 64 +
box-shadow: 2px 2px 6px rgb(255 255 255 / 25%); 65 +
padding: 0.5em; 66 +
overflow-x: auto; 67 +
color: var(--color-text); 68 +
} 69 +
70 +
.text-column { 71 +
display: flex; 72 +
max-width: 48rem; 73 +
flex: 0.6; 74 +
flex-direction: column; 75 +
justify-content: center; 76 +
margin: 0 auto; 77 +
} 78 +
79 +
input, 80 +
button { 81 +
font-size: inherit; 82 +
font-family: inherit; 83 +
} 84 +
85 +
button:focus:not(:focus-visible) { 86 +
outline: none; 87 +
} 88 +
89 +
@media (min-width: 720px) { 90 +
h1 { 91 +
font-size: 2.4rem; 92 +
} 93 +
} 94 +
95 +
.visually-hidden { 96 +
border: 0; 97 +
clip: rect(0 0 0 0); 98 +
height: auto; 99 +
margin: 0; 100 +
overflow: hidden; 101 +
padding: 0; 102 +
position: absolute; 103 +
width: 1px; 104 +
white-space: nowrap; 105 +
} 1 +
import { fail } from '@sveltejs/kit'; 2 +
import type { Actions, PageServerLoad } from './$types'; 3 +
import { Game } from './game.ts'; 4 +
5 +
export const load = (({ cookies }) => { 6 +
const game = new Game(cookies.get('sverdle')); 7 +
8 +
return { 9 +
/** 10 +
* The player's guessed words so far 11 +
*/ 12 +
guesses: game.guesses, 13 +
14 +
/** 15 +
* An array of strings like '__x_c' corresponding to the guesses, where 'x' means 16 +
* an exact match, and 'c' means a close match (right letter, wrong place) 17 +
*/ 18 +
answers: game.answers, 19 +
20 +
/** 21 +
* The correct answer, revealed if the game is over 22 +
*/ 23 +
answer: game.answers.length >= 6 ? game.answer : null 24 +
}; 25 +
}) satisfies PageServerLoad; 26 +
27 +
export const actions = { 28 +
/** 29 +
* Modify game state in reaction to a keypress. If client-side JavaScript 30 +
* is available, this will happen in the browser instead of here 31 +
*/ 32 +
update: async ({ request, cookies }) => { 33 +
const game = new Game(cookies.get('sverdle')); 34 +
35 +
const data = await request.formData(); 36 +
const key = data.get('key'); 37 +
38 +
const i = game.answers.length; 39 +
40 +
if (key === 'backspace') { 41 +
game.guesses[i] = game.guesses[i].slice(0, -1); 42 +
} else { 43 +
game.guesses[i] += key; 44 +
} 45 +
46 +
cookies.set('sverdle', game.toString(), { path: '/' }); 47 +
}, 48 +
49 +
/** 50 +
* Modify game state in reaction to a guessed word. This logic always runs on 51 +
* the server, so that people can't cheat by peeking at the JavaScript 52 +
*/ 53 +
enter: async ({ request, cookies }) => { 54 +
const game = new Game(cookies.get('sverdle')); 55 +
56 +
const data = await request.formData(); 57 +
const guess = data.getAll('guess') as string[]; 58 +
59 +
if (!game.enter(guess)) { 60 +
return fail(400, { badGuess: true }); 61 +
} 62 +
63 +
cookies.set('sverdle', game.toString(), { path: '/' }); 64 +
}, 65 +
66 +
restart: async ({ cookies }) => { 67 +
cookies.delete('sverdle', { path: '/' }); 68 +
} 69 +
} satisfies Actions; 1 +
<script lang="ts"> 2 +
import { enhance } from '$app/forms'; 3 +
import { resolve } from '$app/paths'; 4 +
import { confetti } from '@neoconfetti/svelte'; 5 +
import { MediaQuery } from 'svelte/reactivity'; 6 +
7 +
import type { PageProps } from './$types'; 8 +
9 +
let { data }: PageProps = $props(); 10 +
11 +
/** Whether the user prefers reduced motion */ 12 +
const reducedMotion = new MediaQuery('(prefers-reduced-motion: reduce)'); 13 +
14 +
let shake = $state(false); 15 +
16 +
/** Whether or not the user has won */ 17 +
let won = $derived(data.answers.at(-1) === 'xxxxx'); 18 +
19 +
/** The index of the current guess */ 20 +
let i = $derived(won ? -1 : data.answers.length); 21 +
22 +
/** The current guess */ 23 +
let currentGuess = $derived(data.guesses[i] || ''); 24 +
25 +
/** Whether the current guess can be submitted */ 26 +
let submittable = $derived(currentGuess.length === 5); 27 +
28 +
const { classnames, description } = $derived.by(() => { 29 +
/** 30 +
* A map of classnames for all letters that have been guessed, 31 +
* used for styling the keyboard 32 +
*/ 33 +
let classnames: Record<string, 'exact' | 'close' | 'missing'> = {}; 34 +
/** 35 +
* A map of descriptions for all letters that have been guessed, 36 +
* used for adding text for assistive technology (e.g. screen readers) 37 +
*/ 38 +
let description: Record<string, string> = {}; 39 +
data.answers.forEach((answer, i) => { 40 +
const guess = data.guesses[i]; 41 +
for (let i = 0; i < 5; i += 1) { 42 +
const letter = guess[i]; 43 +
if (answer[i] === 'x') { 44 +
classnames[letter] = 'exact'; 45 +
description[letter] = 'correct'; 46 +
} else if (!classnames[letter]) { 47 +
classnames[letter] = answer[i] === 'c' ? 'close' : 'missing'; 48 +
description[letter] = answer[i] === 'c' ? 'present' : 'absent'; 49 +
} 50 +
} 51 +
}); 52 +
return { classnames, description }; 53 +
}); 54 +
55 +
/** 56 +
* Modify the game state without making a trip to the server, 57 +
* if client-side JavaScript is enabled 58 +
*/ 59 +
function update(event: MouseEvent) { 60 +
event.preventDefault(); 61 +
const key = (event.target as HTMLButtonElement).getAttribute( 62 +
'data-key' 63 +
); 64 +
65 +
if (key === 'backspace') { 66 +
currentGuess = currentGuess.slice(0, -1); 67 +
shake = false; 68 +
} else if (currentGuess.length < 5) { 69 +
currentGuess += key; 70 +
} 71 +
} 72 +
73 +
/** 74 +
* Trigger form logic in response to a keydown event, so that 75 +
* desktop users can use the keyboard to play the game 76 +
*/ 77 +
function keydown(event: KeyboardEvent) { 78 +
if (event.metaKey) return; 79 +
80 +
if (event.key === 'Enter' && !submittable) return; 81 +
82 +
document 83 +
.querySelector(`[data-key="${event.key}" i]`) 84 +
?.dispatchEvent(new MouseEvent('click', { cancelable: true, bubbles: true })); 85 +
} 86 +
</script> 87 +
88 +
<svelte:window onkeydown={keydown} /> 89 +
90 +
<svelte:head> 91 +
<title>Sverdle</title> 92 +
<meta name="description" content="A Wordle clone written in SvelteKit" /> 93 +
</svelte:head> 94 +
95 +
<h1 class="visually-hidden">Sverdle</h1> 96 +
97 +
<form 98 +
method="post" 99 +
action="?/enter" 100 +
use:enhance={() => { 101 +
// prevent default callback from resetting the form 102 +
return ({ result, update }) => { 103 +
shake = result.type === 'failure'; 104 +
update({ reset: false }); 105 +
}; 106 +
}} 107 +
> 108 +
<a class="how-to-play" href={resolve('/sverdle/how-to-play')}>How to play</a> 109 +
110 +
<div class="grid" class:playing={!won} class:shake onanimationend={() => (shake = false)}> 111 +
{#each Array.from(Array(6).keys()) as row (row)} 112 +
{const current = $derived(row === i)} 113 +
114 +
<h2 class="visually-hidden">Row {row + 1}</h2> 115 +
<div class="row" class:current> 116 +
{#each Array.from(Array(5).keys()) as column (column)} 117 +
{const guess = $derived(current ? currentGuess : data.guesses[row])} 118 +
{const answer = $derived(data.answers[row]?.[column])} 119 +
{const value = $derived(guess?.[column] ?? '')} 120 +
{const selected = $derived(current && column === guess.length)} 121 +
{const exact = $derived(answer === 'x')} 122 +
{const close = $derived(answer === 'c')} 123 +
{const missing = $derived(answer === '_')} 124 +
125 +
<div class="letter" class:exact class:close class:missing class:selected> 126 +
{value} 127 +
<span class="visually-hidden"> 128 +
{#if exact} 129 +
(correct) 130 +
{:else if close} 131 +
(present) 132 +
{:else if missing} 133 +
(absent) 134 +
{:else} 135 +
empty 136 +
{/if} 137 +
</span> 138 +
<input name="guess" disabled={!current} type="hidden" {value} /> 139 +
</div> 140 +
{/each} 141 +
</div> 142 +
{/each} 143 +
</div> 144 +
145 +
<div class="controls"> 146 +
{#if won || data.answers.length >= 6} 147 +
{#if !won && data.answer} 148 +
<p>the answer was "{data.answer}"</p> 149 +
{/if} 150 +
<button data-key="enter" class="restart selected" formaction="?/restart"> 151 +
{won ? 'you won :)' : `game over :(`} play again? 152 +
</button> 153 +
{:else} 154 +
<div class="keyboard"> 155 +
<button data-key="enter" class:selected={submittable} disabled={!submittable}>enter</button> 156 +
157 +
<button 158 +
onclick={update} 159 +
data-key="backspace" 160 +
formaction="?/update" 161 +
name="key" 162 +
value="backspace" 163 +
> 164 +
back 165 +
</button> 166 +
167 +
{#each ['qwertyuiop', 'asdfghjkl', 'zxcvbnm'] as row (row)} 168 +
<div class="row"> 169 +
{#each row as letter, index (index)} 170 +
<button 171 +
onclick={update} 172 +
data-key={letter} 173 +
class={classnames[letter]} 174 +
disabled={submittable} 175 +
formaction="?/update" 176 +
name="key" 177 +
value={letter} 178 +
aria-label="{letter} {description[letter] || ''}" 179 +
> 180 +
{letter} 181 +
</button> 182 +
{/each} 183 +
</div> 184 +
{/each} 185 +
</div> 186 +
{/if} 187 +
</div> 188 +
</form> 189 +
190 +
{#if won} 191 +
<div 192 +
style="position: absolute; left: 50%; top: 30%" 193 +
use:confetti={{ 194 +
particleCount: reducedMotion.current ? 0 : undefined, 195 +
force: 0.7, 196 +
stageWidth: window.innerWidth, 197 +
stageHeight: window.innerHeight, 198 +
colors: ['#ff3e00', '#40b3ff', '#676778'] 199 +
}} 200 +
></div> 201 +
{/if} 202 +
203 +
<style> 204 +
form { 205 +
width: 100%; 206 +
height: 100%; 207 +
display: flex; 208 +
flex-direction: column; 209 +
align-items: center; 210 +
justify-content: center; 211 +
gap: 1rem; 212 +
flex: 1; 213 +
} 214 +
215 +
.how-to-play { 216 +
color: var(--color-text); 217 +
} 218 +
219 +
.how-to-play::before { 220 +
content: 'i'; 221 +
display: inline-block; 222 +
font-size: 0.8em; 223 +
font-weight: 900; 224 +
width: 1em; 225 +
height: 1em; 226 +
padding: 0.2em; 227 +
line-height: 1; 228 +
border: 1.5px solid var(--color-text); 229 +
border-radius: 50%; 230 +
text-align: center; 231 +
margin: 0 0.5em 0 0; 232 +
position: relative; 233 +
top: -0.05em; 234 +
} 235 +
236 +
.grid { 237 +
--width: min(100vw, 40vh, 380px); 238 +
max-width: var(--width); 239 +
align-self: center; 240 +
justify-self: center; 241 +
width: 100%; 242 +
height: 100%; 243 +
display: flex; 244 +
flex-direction: column; 245 +
justify-content: flex-start; 246 +
} 247 +
248 +
.grid .row { 249 +
display: grid; 250 +
grid-template-columns: repeat(5, 1fr); 251 +
grid-gap: 0.2rem; 252 +
margin: 0 0 0.2rem 0; 253 +
} 254 +
255 +
@media (prefers-reduced-motion: no-preference) { 256 +
.grid.shake .row.current { 257 +
animation: wiggle 0.5s; 258 +
} 259 +
} 260 +
261 +
.grid.playing .row.current { 262 +
filter: drop-shadow(3px 3px 10px var(--color-bg-0)); 263 +
} 264 +
265 +
.letter { 266 +
aspect-ratio: 1; 267 +
width: 100%; 268 +
display: flex; 269 +
align-items: center; 270 +
justify-content: center; 271 +
text-align: center; 272 +
box-sizing: border-box; 273 +
text-transform: lowercase; 274 +
border: none; 275 +
font-size: calc(0.08 * var(--width)); 276 +
border-radius: 2px; 277 +
background: white; 278 +
margin: 0; 279 +
color: rgba(0, 0, 0, 0.7); 280 +
} 281 +
282 +
.letter.missing { 283 +
background: rgba(255, 255, 255, 0.5); 284 +
color: rgba(0, 0, 0, 0.5); 285 +
} 286 +
287 +
.letter.exact { 288 +
background: var(--color-theme-2); 289 +
color: white; 290 +
} 291 +
292 +
.letter.close { 293 +
border: 2px solid var(--color-theme-2); 294 +
} 295 +
296 +
.selected { 297 +
outline: 2px solid var(--color-theme-1); 298 +
} 299 +
300 +
.controls { 301 +
text-align: center; 302 +
justify-content: center; 303 +
height: min(18vh, 10rem); 304 +
} 305 +
306 +
.keyboard { 307 +
--gap: 0.2rem; 308 +
position: relative; 309 +
display: flex; 310 +
flex-direction: column; 311 +
gap: var(--gap); 312 +
height: 100%; 313 +
} 314 +
315 +
.keyboard .row { 316 +
display: flex; 317 +
justify-content: center; 318 +
gap: 0.2rem; 319 +
flex: 1; 320 +
} 321 +
322 +
.keyboard button, 323 +
.keyboard button:disabled { 324 +
--size: min(8vw, 4vh, 40px); 325 +
background-color: white; 326 +
color: black; 327 +
width: var(--size); 328 +
border: none; 329 +
border-radius: 2px; 330 +
font-size: calc(var(--size) * 0.5); 331 +
margin: 0; 332 +
} 333 +
334 +
.keyboard button.exact { 335 +
background: var(--color-theme-2); 336 +
color: white; 337 +
} 338 +
339 +
.keyboard button.missing { 340 +
opacity: 0.5; 341 +
} 342 +
343 +
.keyboard button.close { 344 +
border: 2px solid var(--color-theme-2); 345 +
} 346 +
347 +
.keyboard button:focus { 348 +
background: var(--color-theme-1); 349 +
color: white; 350 +
outline: none; 351 +
} 352 +
353 +
.keyboard button[data-key='enter'], 354 +
.keyboard button[data-key='backspace'] { 355 +
position: absolute; 356 +
bottom: 0; 357 +
width: calc(1.5 * var(--size)); 358 +
height: calc(1 / 3 * (100% - 2 * var(--gap))); 359 +
text-transform: uppercase; 360 +
font-size: calc(0.3 * var(--size)); 361 +
padding-top: calc(0.15 * var(--size)); 362 +
} 363 +
364 +
.keyboard button[data-key='enter'] { 365 +
right: calc(50% + 3.5 * var(--size) + 0.8rem); 366 +
} 367 +
368 +
.keyboard button[data-key='backspace'] { 369 +
left: calc(50% + 3.5 * var(--size) + 0.8rem); 370 +
} 371 +
372 +
.keyboard button[data-key='enter']:disabled { 373 +
opacity: 0.5; 374 +
} 375 +
376 +
.restart { 377 +
width: 100%; 378 +
padding: 1rem; 379 +
background: rgba(255, 255, 255, 0.5); 380 +
border-radius: 2px; 381 +
border: none; 382 +
} 383 +
384 +
.restart:focus, 385 +
.restart:hover { 386 +
background: var(--color-theme-1); 387 +
color: white; 388 +
outline: none; 389 +
} 390 +
391 +
@keyframes wiggle { 392 +
0% { 393 +
transform: translateX(0); 394 +
} 395 +
10% { 396 +
transform: translateX(-2px); 397 +
} 398 +
30% { 399 +
transform: translateX(4px); 400 +
} 401 +
50% { 402 +
transform: translateX(-6px); 403 +
} 404 +
70% { 405 +
transform: translateX(+4px); 406 +
} 407 +
90% { 408 +
transform: translateX(-2px); 409 +
} 410 +
100% { 411 +
transform: translateX(0); 412 +
} 413 +
} 414 +
</style> 1 +
import { allowed, words } from './words.server.ts'; 2 +
3 +
export class Game { 4 +
index: number; 5 +
guesses: string[]; 6 +
answers: string[]; 7 +
answer: string; 8 +
9 +
/** 10 +
* Create a game object from the player's cookie, or initialise a new game 11 +
*/ 12 +
constructor(serialized: string | undefined = undefined) { 13 +
if (serialized) { 14 +
const [index, guesses, answers] = serialized.split('-'); 15 +
16 +
this.index = +index; 17 +
this.guesses = guesses ? guesses.split(' ') : []; 18 +
this.answers = answers ? answers.split(' ') : []; 19 +
} else { 20 +
this.index = Math.floor(Math.random() * words.length); 21 +
this.guesses = ['', '', '', '', '', '']; 22 +
this.answers = []; 23 +
} 24 +
25 +
this.answer = words[this.index]; 26 +
} 27 +
28 +
/** 29 +
* Update game state based on a guess of a five-letter word. Returns 30 +
* true if the guess was valid, false otherwise 31 +
*/ 32 +
enter(letters: string[]) { 33 +
const word = letters.join(''); 34 +
const valid = allowed.has(word); 35 +
36 +
if (!valid) return false; 37 +
38 +
this.guesses[this.answers.length] = word; 39 +
40 +
const available = Array.from(this.answer); 41 +
const answer = Array(5).fill('_'); 42 +
43 +
// first, find exact matches 44 +
for (let i = 0; i < 5; i += 1) { 45 +
if (letters[i] === available[i]) { 46 +
answer[i] = 'x'; 47 +
available[i] = ' '; 48 +
} 49 +
} 50 +
51 +
// then find close matches (this has to happen 52 +
// in a second step, otherwise an early close 53 +
// match can prevent a later exact match) 54 +
for (let i = 0; i < 5; i += 1) { 55 +
if (answer[i] === '_') { 56 +
const index = available.indexOf(letters[i]); 57 +
if (index !== -1) { 58 +
answer[i] = 'c'; 59 +
available[index] = ' '; 60 +
} 61 +
} 62 +
} 63 +
64 +
this.answers.push(answer.join('')); 65 +
66 +
return true; 67 +
} 68 +
69 +
/** 70 +
* Serialize game state so it can be set as a cookie 71 +
*/ 72 +
toString() { 73 +
return `${this.index}-${this.guesses.join(' ')}-${this.answers.join(' ')}`; 74 +
} 75 +
} 1 +
<svelte:head> 2 +
<title>How to play Sverdle</title> 3 +
<meta name="description" content="How to play Sverdle" /> 4 +
</svelte:head> 5 +
6 +
<div class="text-column"> 7 +
<h1>How to play Sverdle</h1> 8 +
9 +
<p> 10 +
Sverdle is a clone of <a href="https://www.nytimes.com/games/wordle/index.html">Wordle</a>, the 11 +
word guessing game. To play, enter a five-letter English word. For example: 12 +
</p> 13 +
14 +
<div class="example"> 15 +
<span class="close">r</span> 16 +
<span class="missing">i</span> 17 +
<span class="close">t</span> 18 +
<span class="missing">z</span> 19 +
<span class="exact">y</span> 20 +
</div> 21 +
22 +
<p> 23 +
The <span class="exact">y</span> is in the right place. <span class="close">r</span> and 24 +
<span class="close">t</span> 25 +
are the right letters, but in the wrong place. The other letters are wrong, and can be discarded. 26 +
Let's make another guess: 27 +
</p> 28 +
29 +
<div class="example"> 30 +
<span class="exact">p</span> 31 +
<span class="exact">a</span> 32 +
<span class="exact">r</span> 33 +
<span class="exact">t</span> 34 +
<span class="exact">y</span> 35 +
</div> 36 +
37 +
<p>This time we guessed right! You have <strong>six</strong> guesses to get the word.</p> 38 +
39 +
<p> 40 +
Unlike the original Wordle, Sverdle runs on the server instead of in the browser, making it 41 +
impossible to cheat. It uses <code><form></code> and cookies to submit data, meaning you can 42 +
even play with JavaScript disabled! 43 +
</p> 44 +
</div> 45 +
46 +
<style> 47 +
span { 48 +
display: inline-flex; 49 +
justify-content: center; 50 +
align-items: center; 51 +
font-size: 0.8em; 52 +
width: 2.4em; 53 +
height: 2.4em; 54 +
background-color: white; 55 +
box-sizing: border-box; 56 +
border-radius: 2px; 57 +
border-width: 2px; 58 +
color: rgba(0, 0, 0, 0.7); 59 +
} 60 +
61 +
.missing { 62 +
background: rgba(255, 255, 255, 0.5); 63 +
color: rgba(0, 0, 0, 0.5); 64 +
} 65 +
66 +
.close { 67 +
border-style: solid; 68 +
border-color: var(--color-theme-2); 69 +
} 70 +
71 +
.exact { 72 +
background: var(--color-theme-2); 73 +
color: white; 74 +
} 75 +
76 +
.example { 77 +
display: flex; 78 +
justify-content: flex-start; 79 +
margin: 1rem 0; 80 +
gap: 0.2rem; 81 +
} 82 +
83 +
.example span { 84 +
font-size: 1.4rem; 85 +
} 86 +
87 +
p span { 88 +
position: relative; 89 +
border-width: 1px; 90 +
border-radius: 1px; 91 +
font-size: 0.4em; 92 +
transform: scale(2) translate(0, -10%); 93 +
margin: 0 1em; 94 +
} 95 +
</style> 1 +
import { dev } from '$app/environment'; 2 +
3 +
// we don't need any JS on this page, though we'll load 4 +
// it in dev so that we get hot module replacement 5 +
export const csr = dev; 6 +
7 +
// since there's no dynamic data here, we can prerender 8 +
// it so that it gets served as a static asset in production 9 +
export const prerender = true; 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.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg> 1 +
# allow crawling everything by default 2 +
User-agent: * 3 +
Disallow: 1 +
{ 2 +
"extends": "./.svelte-kit/tsconfig.json", 3 +
"compilerOptions": { 4 +
"rewriteRelativeImportExtensions": true, 5 +
"allowJs": true, 6 +
"checkJs": true, 7 +
"esModuleInterop": true, 8 +
"forceConsistentCasingInFileNames": true, 9 +
"resolveJsonModule": true, 10 +
"skipLibCheck": true, 11 +
"sourceMap": true, 12 +
"strict": true, 13 +
"moduleResolution": "bundler" 14 +
} 15 +
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 16 +
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 17 +
// 18 +
// To make changes to top-level options such as include and exclude, we recommend extending 19 +
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript 20 +
} 1 +
import adapter from '@sveltejs/adapter-auto'; 2 +
import { sveltekit } from '@sveltejs/kit/vite'; 3 +
import { defineConfig } from 'vite'; 4 +
5 +
export default defineConfig({ 6 +
plugins: [ 7 +
sveltekit({ 8 +
compilerOptions: { 9 +
// Force runes mode for the project, except for libraries. Can be removed in svelte 6. 10 +
runes: ({ filename }) => 11 +
filename.split(/[/\\]/).includes('node_modules') ? undefined : true 12 +
}, 13 +
14 +
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. 15 +
// If your environment is not supported, or you settled on a specific environment, switch out the adapter. 16 +
// See https://svelte.dev/docs/kit/adapters for more information about adapters. 17 +
adapter: adapter() 18 +
}) 19 +
] 20 +
});src/lib/images/svelte-welcome.png binary file not shownsrc/lib/images/svelte-welcome.webp binary file not shown