Add base vite stuff, replace svelte with gohtml
This commit is contained in:
parent
9c5f1f3954
commit
23b284f993
72 changed files with 368 additions and 17627 deletions
|
@ -1,5 +1,6 @@
|
|||
version = 1
|
||||
|
||||
dev = false
|
||||
app_url = "http://192.168.1.40:3030"
|
||||
|
||||
[server]
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,3 +12,4 @@ vite.config.js.timestamp-*
|
|||
vite.config.ts.timestamp-*
|
||||
.idea
|
||||
.vscode
|
||||
dist
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PortForwardingSettings">
|
||||
<ports>
|
||||
<entry key="5173">
|
||||
<ForwardedPortInfo>
|
||||
<option name="hostPort" value="5173" />
|
||||
<option name="readOnly" value="false" />
|
||||
</ForwardedPortInfo>
|
||||
</entry>
|
||||
</ports>
|
||||
</component>
|
||||
</project>
|
BIN
bun.lockb
Executable file
BIN
bun.lockb
Executable file
Binary file not shown.
39
cmd/make:route.go
Normal file
39
cmd/make:route.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// make:routeCmd represents the make:route command
|
||||
var makeRouteCmd = &cobra.Command{
|
||||
Use: "make:route",
|
||||
Short: "A brief description of your command",
|
||||
Long: `A longer description that spans multiple lines and likely contains examples
|
||||
and usage of using your command. For example:
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("make:route called")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(makeRouteCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// make:routeCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// make:routeCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
|
@ -70,7 +70,7 @@ func initConfig() {
|
|||
viper.AutomaticEnv() // read in environment variables that match
|
||||
|
||||
// If a config file is found, read it in.
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
}
|
||||
|
|
60
cmd/run.go
60
cmd/run.go
|
@ -1,15 +1,42 @@
|
|||
/*
|
||||
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.shadowhosting.xyz/shadow/utils"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/template/html/v2"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func viteAsset(src string) string {
|
||||
file, err := os.ReadFile("dist/.vite/manifest.json")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
var viteManifestData map[string]map[string]interface{}
|
||||
|
||||
if err := json.Unmarshal(file, &viteManifestData); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
src = strings.TrimPrefix(src, "/")
|
||||
|
||||
if _, ok := viteManifestData[src]; ok {
|
||||
return "/" + viteManifestData[src]["file"].(string)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// runCmd represents the run command
|
||||
var runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
|
@ -21,7 +48,34 @@ Cobra is a CLI library for Go that empowers applications.
|
|||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("run called")
|
||||
|
||||
if viper.GetBool("dev") {
|
||||
go utils.RunViteServer()
|
||||
}
|
||||
|
||||
engine := html.New("./dist", ".html")
|
||||
|
||||
if viper.GetBool("dev") {
|
||||
engine.Reload(true)
|
||||
}
|
||||
|
||||
engine.AddFunc("viteAsset", viteAsset)
|
||||
|
||||
app := fiber.New(fiber.Config{
|
||||
AppName: "shadow",
|
||||
EnableIPValidation: true,
|
||||
Views: engine,
|
||||
ViewsLayout: "index",
|
||||
})
|
||||
|
||||
app.Static("/assets", "./dist/assets")
|
||||
|
||||
app.Get("/", func(ctx *fiber.Ctx) error {
|
||||
return ctx.Render("views/index", fiber.Map{})
|
||||
})
|
||||
|
||||
log.Fatal(app.Listen(fmt.Sprintf("%s:%d", viper.GetString("server.host"), viper.GetInt32("server.port"))))
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
|
@ -1,30 +0,0 @@
|
|||
/** @type { import("eslint").Linter.Config } */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:svelte/recommended'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
parser: 'svelte-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
engine-strict=true
|
179
frontend/.svelte-kit/ambient.d.ts
vendored
179
frontend/.svelte-kit/ambient.d.ts
vendored
|
@ -1,179 +0,0 @@
|
|||
|
||||
// this file is generated — do not edit it
|
||||
|
||||
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
|
||||
/**
|
||||
* Environment variables [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env`. Like [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), this module cannot be imported into client-side code. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) _and do_ start with [`config.kit.env.privatePrefix`](https://kit.svelte.dev/docs/configuration#env) (if configured).
|
||||
*
|
||||
* _Unlike_ [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), the values exported from this module are statically injected into your bundle at build time, enabling optimisations like dead code elimination.
|
||||
*
|
||||
* ```ts
|
||||
* import { API_KEY } from '$env/static/private';
|
||||
* ```
|
||||
*
|
||||
* Note that all environment variables referenced in your code should be declared (for example in an `.env` file), even if they don't have a value until the app is deployed:
|
||||
*
|
||||
* ```
|
||||
* MY_FEATURE_FLAG=""
|
||||
* ```
|
||||
*
|
||||
* You can override `.env` values from the command line like so:
|
||||
*
|
||||
* ```bash
|
||||
* MY_FEATURE_FLAG="enabled" npm run dev
|
||||
* ```
|
||||
*/
|
||||
declare module '$env/static/private' {
|
||||
export const TERM_SESSION_ID: string;
|
||||
export const USER: string;
|
||||
export const npm_config_user_agent: string;
|
||||
export const SSH_CLIENT: string;
|
||||
export const XDG_SESSION_TYPE: string;
|
||||
export const BUN_INSTALL: string;
|
||||
export const npm_node_execpath: string;
|
||||
export const SHLVL: string;
|
||||
export const MOTD_SHOWN: string;
|
||||
export const HOME: string;
|
||||
export const OLDPWD: string;
|
||||
export const npm_package_json: string;
|
||||
export const SSH_TTY: string;
|
||||
export const TERMINAL_EMULATOR: string;
|
||||
export const _INTELLIJ_FORCE_SET_GOPATH: string;
|
||||
export const npm_config_local_prefix: string;
|
||||
export const GOROOT: string;
|
||||
export const DBUS_SESSION_BUS_ADDRESS: string;
|
||||
export const GOLAND_JDK: string;
|
||||
export const LOGNAME: string;
|
||||
export const GO111MODULE: string;
|
||||
export const _: string;
|
||||
export const XDG_SESSION_CLASS: string;
|
||||
export const REMOTE_DEV_LAUNCHER_NAME_FOR_USAGE: string;
|
||||
export const TERM: string;
|
||||
export const XDG_SESSION_ID: string;
|
||||
export const PATH: string;
|
||||
export const NODE: string;
|
||||
export const npm_package_name: string;
|
||||
export const XDG_RUNTIME_DIR: string;
|
||||
export const LANG: string;
|
||||
export const GOLAND_VM_OPTIONS: string;
|
||||
export const SSH_AUTH_SOCK: string;
|
||||
export const SHELL: string;
|
||||
export const npm_package_version: string;
|
||||
export const npm_lifecycle_event: string;
|
||||
export const GOPATH: string;
|
||||
export const NODE_PATH: string;
|
||||
export const REMOTE_DEV_NON_INTERACTIVE: string;
|
||||
export const GOLAND_PROPERTIES: string;
|
||||
export const _INTELLIJ_FORCE_SET_GOROOT: string;
|
||||
export const _INTELLIJ_FORCE_PREPEND_PATH: string;
|
||||
export const GPG_TTY: string;
|
||||
export const PWD: string;
|
||||
export const npm_execpath: string;
|
||||
export const IDEA_RESTART_VIA_EXIT_CODE: string;
|
||||
export const SSH_CONNECTION: string;
|
||||
export const _INTELLIJ_FORCE_SET_GO111MODULE: string;
|
||||
export const NODE_ENV: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to [`$env/static/private`](https://kit.svelte.dev/docs/modules#$env-static-private), except that it only includes environment variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code.
|
||||
*
|
||||
* Values are replaced statically at build time.
|
||||
*
|
||||
* ```ts
|
||||
* import { PUBLIC_BASE_URL } from '$env/static/public';
|
||||
* ```
|
||||
*/
|
||||
declare module '$env/static/public' {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This module provides access to runtime environment variables, as defined by the platform you're running on. For example if you're using [`adapter-node`](https://github.com/sveltejs/kit/tree/main/packages/adapter-node) (or running [`vite preview`](https://kit.svelte.dev/docs/cli)), this is equivalent to `process.env`. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) _and do_ start with [`config.kit.env.privatePrefix`](https://kit.svelte.dev/docs/configuration#env) (if configured).
|
||||
*
|
||||
* This module cannot be imported into client-side code.
|
||||
*
|
||||
* Dynamic environment variables cannot be used during prerendering.
|
||||
*
|
||||
* ```ts
|
||||
* import { env } from '$env/dynamic/private';
|
||||
* console.log(env.DEPLOYMENT_SPECIFIC_VARIABLE);
|
||||
* ```
|
||||
*
|
||||
* > In `dev`, `$env/dynamic` always includes environment variables from `.env`. In `prod`, this behavior will depend on your adapter.
|
||||
*/
|
||||
declare module '$env/dynamic/private' {
|
||||
export const env: {
|
||||
TERM_SESSION_ID: string;
|
||||
USER: string;
|
||||
npm_config_user_agent: string;
|
||||
SSH_CLIENT: string;
|
||||
XDG_SESSION_TYPE: string;
|
||||
BUN_INSTALL: string;
|
||||
npm_node_execpath: string;
|
||||
SHLVL: string;
|
||||
MOTD_SHOWN: string;
|
||||
HOME: string;
|
||||
OLDPWD: string;
|
||||
npm_package_json: string;
|
||||
SSH_TTY: string;
|
||||
TERMINAL_EMULATOR: string;
|
||||
_INTELLIJ_FORCE_SET_GOPATH: string;
|
||||
npm_config_local_prefix: string;
|
||||
GOROOT: string;
|
||||
DBUS_SESSION_BUS_ADDRESS: string;
|
||||
GOLAND_JDK: string;
|
||||
LOGNAME: string;
|
||||
GO111MODULE: string;
|
||||
_: string;
|
||||
XDG_SESSION_CLASS: string;
|
||||
REMOTE_DEV_LAUNCHER_NAME_FOR_USAGE: string;
|
||||
TERM: string;
|
||||
XDG_SESSION_ID: string;
|
||||
PATH: string;
|
||||
NODE: string;
|
||||
npm_package_name: string;
|
||||
XDG_RUNTIME_DIR: string;
|
||||
LANG: string;
|
||||
GOLAND_VM_OPTIONS: string;
|
||||
SSH_AUTH_SOCK: string;
|
||||
SHELL: string;
|
||||
npm_package_version: string;
|
||||
npm_lifecycle_event: string;
|
||||
GOPATH: string;
|
||||
NODE_PATH: string;
|
||||
REMOTE_DEV_NON_INTERACTIVE: string;
|
||||
GOLAND_PROPERTIES: string;
|
||||
_INTELLIJ_FORCE_SET_GOROOT: string;
|
||||
_INTELLIJ_FORCE_PREPEND_PATH: string;
|
||||
GPG_TTY: string;
|
||||
PWD: string;
|
||||
npm_execpath: string;
|
||||
IDEA_RESTART_VIA_EXIT_CODE: string;
|
||||
SSH_CONNECTION: string;
|
||||
_INTELLIJ_FORCE_SET_GO111MODULE: string;
|
||||
NODE_ENV: string;
|
||||
[key: `PUBLIC_${string}`]: undefined;
|
||||
[key: `${string}`]: string | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), but only includes variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code.
|
||||
*
|
||||
* Note that public dynamic environment variables must all be sent from the server to the client, causing larger network requests — when possible, use `$env/static/public` instead.
|
||||
*
|
||||
* Dynamic environment variables cannot be used during prerendering.
|
||||
*
|
||||
* ```ts
|
||||
* import { env } from '$env/dynamic/public';
|
||||
* console.log(env.PUBLIC_DEPLOYMENT_SPECIFIC_VARIABLE);
|
||||
* ```
|
||||
*/
|
||||
declare module '$env/dynamic/public' {
|
||||
export const env: {
|
||||
[key: `PUBLIC_${string}`]: string | undefined;
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
export { matchers } from './matchers.js';
|
||||
|
||||
export const nodes = [
|
||||
() => import('./nodes/0'),
|
||||
() => import('./nodes/1'),
|
||||
() => import('./nodes/2'),
|
||||
() => import('./nodes/3'),
|
||||
() => import('./nodes/4'),
|
||||
() => import('./nodes/5')
|
||||
];
|
||||
|
||||
export const server_loads = [];
|
||||
|
||||
export const dictionary = {
|
||||
"/": [2],
|
||||
"/about": [3],
|
||||
"/sverdle": [~4],
|
||||
"/sverdle/how-to-play": [5]
|
||||
};
|
||||
|
||||
export const hooks = {
|
||||
handleError: (({ error }) => { console.error(error) }),
|
||||
|
||||
reroute: (() => {})
|
||||
};
|
||||
|
||||
export { default as root } from '../root.svelte';
|
|
@ -1 +0,0 @@
|
|||
export const matchers = {};
|
|
@ -1 +0,0 @@
|
|||
export { default as component } from "../../../../src/routes/+layout.svelte";
|
|
@ -1 +0,0 @@
|
|||
export { default as component } from "../../../../node_modules/.pnpm/@sveltejs+kit@2.5.7_@sveltejs+vite-plugin-svelte@3.1.0_svelte@4.2.15_vite@5.2.10__svelte@4.2.15_vite@5.2.10/node_modules/@sveltejs/kit/src/runtime/components/error.svelte";
|
|
@ -1,3 +0,0 @@
|
|||
import * as universal from "../../../../src/routes/+page.ts";
|
||||
export { universal };
|
||||
export { default as component } from "../../../../src/routes/+page.svelte";
|
|
@ -1,3 +0,0 @@
|
|||
import * as universal from "../../../../src/routes/about/+page.ts";
|
||||
export { universal };
|
||||
export { default as component } from "../../../../src/routes/about/+page.svelte";
|
|
@ -1 +0,0 @@
|
|||
export { default as component } from "../../../../src/routes/sverdle/+page.svelte";
|
|
@ -1,3 +0,0 @@
|
|||
import * as universal from "../../../../src/routes/sverdle/how-to-play/+page.ts";
|
||||
export { universal };
|
||||
export { default as component } from "../../../../src/routes/sverdle/how-to-play/+page.svelte";
|
|
@ -1,57 +0,0 @@
|
|||
<!-- This file is generated by @sveltejs/kit — do not edit it! -->
|
||||
|
||||
<script>
|
||||
import { setContext, afterUpdate, onMount, tick } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
// stores
|
||||
export let stores;
|
||||
export let page;
|
||||
|
||||
export let constructors;
|
||||
export let components = [];
|
||||
export let form;
|
||||
export let data_0 = null;
|
||||
export let data_1 = null;
|
||||
|
||||
if (!browser) {
|
||||
setContext('__svelte__', stores);
|
||||
}
|
||||
|
||||
$: stores.page.set(page);
|
||||
afterUpdate(stores.page.notify);
|
||||
|
||||
let mounted = false;
|
||||
let navigated = false;
|
||||
let title = null;
|
||||
|
||||
onMount(() => {
|
||||
const unsubscribe = stores.page.subscribe(() => {
|
||||
if (mounted) {
|
||||
navigated = true;
|
||||
tick().then(() => {
|
||||
title = document.title || 'untitled page';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
mounted = true;
|
||||
return unsubscribe;
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if constructors[1]}
|
||||
<svelte:component this={constructors[0]} bind:this={components[0]} data={data_0}>
|
||||
<svelte:component this={constructors[1]} bind:this={components[1]} data={data_1} {form} />
|
||||
</svelte:component>
|
||||
{:else}
|
||||
<svelte:component this={constructors[0]} bind:this={components[0]} data={data_0} {form} />
|
||||
{/if}
|
||||
|
||||
{#if mounted}
|
||||
<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px">
|
||||
{#if navigated}
|
||||
{title}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
|
@ -1,34 +0,0 @@
|
|||
|
||||
import root from '../root.svelte';
|
||||
import { set_building, set_prerendering } from '__sveltekit/environment';
|
||||
import { set_assets } from '__sveltekit/paths';
|
||||
import { set_manifest, set_read_implementation } from '__sveltekit/server';
|
||||
import { set_private_env, set_public_env, set_safe_public_env } from '../../../node_modules/.pnpm/@sveltejs+kit@2.5.7_@sveltejs+vite-plugin-svelte@3.1.0_svelte@4.2.15_vite@5.2.10__svelte@4.2.15_vite@5.2.10/node_modules/@sveltejs/kit/src/runtime/shared-server.js';
|
||||
|
||||
export const options = {
|
||||
app_dir: "_app",
|
||||
app_template_contains_nonce: false,
|
||||
csp: {"mode":"auto","directives":{"upgrade-insecure-requests":false,"block-all-mixed-content":false},"reportOnly":{"upgrade-insecure-requests":false,"block-all-mixed-content":false}},
|
||||
csrf_check_origin: true,
|
||||
embedded: false,
|
||||
env_public_prefix: 'PUBLIC_',
|
||||
env_private_prefix: '',
|
||||
hooks: null, // added lazily, via `get_hooks`
|
||||
preload_strategy: "modulepreload",
|
||||
root,
|
||||
service_worker: false,
|
||||
templates: {
|
||||
app: ({ head, body, assets, nonce, env }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"" + assets + "/favicon.png\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t" + head + "\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">" + body + "</div>\n\t</body>\n</html>\n",
|
||||
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
|
||||
},
|
||||
version_hash: "1wjmj0y"
|
||||
};
|
||||
|
||||
export async function get_hooks() {
|
||||
return {
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
export { set_assets, set_building, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation, set_safe_public_env };
|
25
frontend/.svelte-kit/non-ambient.d.ts
vendored
25
frontend/.svelte-kit/non-ambient.d.ts
vendored
|
@ -1,25 +0,0 @@
|
|||
|
||||
// this file is generated — do not edit it
|
||||
|
||||
|
||||
declare module "svelte/elements" {
|
||||
export interface HTMLAttributes<T> {
|
||||
'data-sveltekit-keepfocus'?: true | '' | 'off' | undefined | null;
|
||||
'data-sveltekit-noscroll'?: true | '' | 'off' | undefined | null;
|
||||
'data-sveltekit-preload-code'?:
|
||||
| true
|
||||
| ''
|
||||
| 'eager'
|
||||
| 'viewport'
|
||||
| 'hover'
|
||||
| 'tap'
|
||||
| 'off'
|
||||
| undefined
|
||||
| null;
|
||||
'data-sveltekit-preload-data'?: true | '' | 'hover' | 'tap' | 'off' | undefined | null;
|
||||
'data-sveltekit-reload'?: true | '' | 'off' | undefined | null;
|
||||
'data-sveltekit-replacestate'?: true | '' | 'off' | undefined | null;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"$lib": [
|
||||
"../src/lib"
|
||||
],
|
||||
"$lib/*": [
|
||||
"../src/lib/*"
|
||||
]
|
||||
},
|
||||
"rootDirs": [
|
||||
"..",
|
||||
"./types"
|
||||
],
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"moduleResolution": "bundler",
|
||||
"module": "esnext",
|
||||
"noEmit": true,
|
||||
"target": "esnext"
|
||||
},
|
||||
"include": [
|
||||
"ambient.d.ts",
|
||||
"non-ambient.d.ts",
|
||||
"./types/**/$types.d.ts",
|
||||
"../vite.config.js",
|
||||
"../vite.config.ts",
|
||||
"../src/**/*.js",
|
||||
"../src/**/*.ts",
|
||||
"../src/**/*.svelte",
|
||||
"../tests/**/*.js",
|
||||
"../tests/**/*.ts",
|
||||
"../tests/**/*.svelte"
|
||||
],
|
||||
"exclude": [
|
||||
"../node_modules/**",
|
||||
"../src/service-worker.js",
|
||||
"../src/service-worker.ts",
|
||||
"../src/service-worker.d.ts"
|
||||
]
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"/": [
|
||||
"src/routes/+page.ts"
|
||||
],
|
||||
"/about": [
|
||||
"src/routes/about/+page.ts"
|
||||
],
|
||||
"/sverdle": [
|
||||
"src/routes/sverdle/+page.server.ts"
|
||||
],
|
||||
"/sverdle/how-to-play": [
|
||||
"src/routes/sverdle/how-to-play/+page.ts"
|
||||
]
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import type * as Kit from '@sveltejs/kit';
|
||||
|
||||
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
// @ts-ignore
|
||||
type MatcherParam<M> = M extends (param : string) => param is infer U ? U extends string ? U : string : string;
|
||||
type RouteParams = { };
|
||||
type RouteId = '/';
|
||||
type MaybeWithVoid<T> = {} extends T ? T | void : T;
|
||||
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
|
||||
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
|
||||
type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageParentData = EnsureDefined<LayoutData>;
|
||||
type LayoutRouteId = RouteId | "/" | "/about" | "/sverdle" | "/sverdle/how-to-play" | null
|
||||
type LayoutParams = RouteParams & { }
|
||||
type LayoutParentData = EnsureDefined<{}>;
|
||||
|
||||
export type PageServerData = null;
|
||||
export type PageLoad<OutputData extends OutputDataShape<PageParentData> = OutputDataShape<PageParentData>> = Kit.Load<RouteParams, PageServerData, PageParentData, OutputData, RouteId>;
|
||||
export type PageLoadEvent = Parameters<PageLoad>[0];
|
||||
export type PageData = Expand<Omit<PageParentData, keyof PageParentData & EnsureDefined<PageServerData>> & OptionalUnion<EnsureDefined<PageParentData & EnsureDefined<PageServerData>>>>;
|
||||
export type LayoutServerData = null;
|
||||
export type LayoutData = Expand<LayoutParentData>;
|
|
@ -1,19 +0,0 @@
|
|||
import type * as Kit from '@sveltejs/kit';
|
||||
|
||||
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
// @ts-ignore
|
||||
type MatcherParam<M> = M extends (param : string) => param is infer U ? U extends string ? U : string : string;
|
||||
type RouteParams = { };
|
||||
type RouteId = '/about';
|
||||
type MaybeWithVoid<T> = {} extends T ? T | void : T;
|
||||
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
|
||||
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
|
||||
type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageParentData = EnsureDefined<import('../$types.js').LayoutData>;
|
||||
|
||||
export type PageServerData = null;
|
||||
export type PageLoad<OutputData extends OutputDataShape<PageParentData> = OutputDataShape<PageParentData>> = Kit.Load<RouteParams, PageServerData, PageParentData, OutputData, RouteId>;
|
||||
export type PageLoadEvent = Parameters<PageLoad>[0];
|
||||
export type PageData = Expand<Omit<PageParentData, keyof PageParentData & EnsureDefined<PageServerData>> & OptionalUnion<EnsureDefined<PageParentData & EnsureDefined<PageServerData>>>>;
|
|
@ -1,30 +0,0 @@
|
|||
import type * as Kit from '@sveltejs/kit';
|
||||
|
||||
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
// @ts-ignore
|
||||
type MatcherParam<M> = M extends (param : string) => param is infer U ? U extends string ? U : string : string;
|
||||
type RouteParams = { };
|
||||
type RouteId = '/sverdle';
|
||||
type MaybeWithVoid<T> = {} extends T ? T | void : T;
|
||||
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
|
||||
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
|
||||
type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageServerParentData = EnsureDefined<import('../$types.js').LayoutServerData>;
|
||||
type PageParentData = EnsureDefined<import('../$types.js').LayoutData>;
|
||||
|
||||
export type PageServerLoad<OutputData extends OutputDataShape<PageServerParentData> = OutputDataShape<PageServerParentData>> = Kit.ServerLoad<RouteParams, PageServerParentData, OutputData, RouteId>;
|
||||
export type PageServerLoadEvent = Parameters<PageServerLoad>[0];
|
||||
type ExcludeActionFailure<T> = T extends Kit.ActionFailure<any> ? never : T extends void ? never : T;
|
||||
type ActionsSuccess<T extends Record<string, (...args: any) => any>> = { [Key in keyof T]: ExcludeActionFailure<Awaited<ReturnType<T[Key]>>>; }[keyof T];
|
||||
type ExtractActionFailure<T> = T extends Kit.ActionFailure<infer X> ? X extends void ? never : X : never;
|
||||
type ActionsFailure<T extends Record<string, (...args: any) => any>> = { [Key in keyof T]: Exclude<ExtractActionFailure<Awaited<ReturnType<T[Key]>>>, void>; }[keyof T];
|
||||
type ActionsExport = typeof import('../../../../../src/routes/sverdle/+page.server.js').actions
|
||||
export type SubmitFunction = Kit.SubmitFunction<Expand<ActionsSuccess<ActionsExport>>, Expand<ActionsFailure<ActionsExport>>>
|
||||
export type ActionData = Expand<Kit.AwaitedActions<ActionsExport>> | null;
|
||||
export type PageServerData = Expand<OptionalUnion<EnsureDefined<Kit.LoadProperties<Awaited<ReturnType<typeof import('../../../../../src/routes/sverdle/+page.server.js').load>>>>>>;
|
||||
export type PageData = Expand<Omit<PageParentData, keyof PageServerData> & EnsureDefined<PageServerData>>;
|
||||
export type Action<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Action<RouteParams, OutputData, RouteId>
|
||||
export type Actions<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Actions<RouteParams, OutputData, RouteId>
|
||||
export type RequestEvent = Kit.RequestEvent<RouteParams, RouteId>;
|
|
@ -1,19 +0,0 @@
|
|||
import type * as Kit from '@sveltejs/kit';
|
||||
|
||||
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
||||
// @ts-ignore
|
||||
type MatcherParam<M> = M extends (param : string) => param is infer U ? U extends string ? U : string : string;
|
||||
type RouteParams = { };
|
||||
type RouteId = '/sverdle/how-to-play';
|
||||
type MaybeWithVoid<T> = {} extends T ? T | void : T;
|
||||
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
|
||||
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
|
||||
type EnsureDefined<T> = T extends null | undefined ? {} : T;
|
||||
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
|
||||
export type Snapshot<T = any> = Kit.Snapshot<T>;
|
||||
type PageParentData = EnsureDefined<import('../../$types.js').LayoutData>;
|
||||
|
||||
export type PageServerData = null;
|
||||
export type PageLoad<OutputData extends OutputDataShape<PageParentData> = OutputDataShape<PageParentData>> = Kit.Load<RouteParams, PageServerData, PageParentData, OutputData, RouteId>;
|
||||
export type PageLoadEvent = Parameters<PageLoad>[0];
|
||||
export type PageData = Expand<Omit<PageParentData, keyof PageParentData & EnsureDefined<PageServerData>> & OptionalUnion<EnsureDefined<PageParentData & EnsureDefined<PageServerData>>>>;
|
Binary file not shown.
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"name": "shadow",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev:bun": "bun --bun run dev:node",
|
||||
"dev:node": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "npm run test:integration && npm run test:unit",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "eslint .",
|
||||
"test:integration": "playwright test",
|
||||
"test:unit": "vitest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fontsource/fira-mono": "^4.5.10",
|
||||
"@neoconfetti/svelte": "^1.0.0",
|
||||
"@playwright/test": "^1.28.1",
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@types/eslint": "^8.56.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"svelte": "^4.2.7",
|
||||
"svelte-check": "^3.6.0",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.3",
|
||||
"vitest": "^1.2.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
webServer: {
|
||||
command: 'npm run build && npm run preview',
|
||||
port: 4173
|
||||
},
|
||||
testDir: 'tests',
|
||||
testMatch: /(.+\.)?(test|spec)\.[jt]s/
|
||||
};
|
||||
|
||||
export default config;
|
File diff suppressed because it is too large
Load diff
13
frontend/src/app.d.ts
vendored
13
frontend/src/app.d.ts
vendored
|
@ -1,13 +0,0 @@
|
|||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
|
@ -1,12 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,16 +0,0 @@
|
|||
<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>
|
Before Width: | Height: | Size: 1.7 KiB |
|
@ -1 +0,0 @@
|
|||
<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>
|
Before Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 352 KiB |
Binary file not shown.
Before Width: | Height: | Size: 113 KiB |
|
@ -1,53 +0,0 @@
|
|||
<script>
|
||||
import Header from './Header.svelte';
|
||||
import './styles.css';
|
||||
</script>
|
||||
|
||||
<div class="app">
|
||||
<Header />
|
||||
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
max-width: 64rem;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
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>
|
|
@ -1,59 +0,0 @@
|
|||
<script>
|
||||
import Counter from './Counter.svelte';
|
||||
import welcome from '$lib/images/svelte-welcome.webp';
|
||||
import welcome_fallback from '$lib/images/svelte-welcome.png';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Home</title>
|
||||
<meta name="description" content="Svelte demo app" />
|
||||
</svelte:head>
|
||||
|
||||
<section>
|
||||
<h1>
|
||||
<span class="welcome">
|
||||
<picture>
|
||||
<source srcset={welcome} type="image/webp" />
|
||||
<img src={welcome_fallback} alt="Welcome" />
|
||||
</picture>
|
||||
</span>
|
||||
|
||||
to your new<br />SvelteKit app
|
||||
</h1>
|
||||
|
||||
<h2>
|
||||
try editing <strong>src/routes/+page.svelte</strong>
|
||||
</h2>
|
||||
|
||||
<Counter />
|
||||
</section>
|
||||
|
||||
<style>
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 0.6;
|
||||
}
|
||||
|
||||
h1 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding: 0 0 calc(100% * 495 / 2048) 0;
|
||||
}
|
||||
|
||||
.welcome img {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
|
@ -1,3 +0,0 @@
|
|||
// 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;
|
|
@ -1,102 +0,0 @@
|
|||
<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>
|
|
@ -1,129 +0,0 @@
|
|||
<script>
|
||||
import { page } from '$app/stores';
|
||||
import logo from '$lib/images/svelte-logo.svg';
|
||||
import github from '$lib/images/github.svg';
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<div class="corner">
|
||||
<a href="https://kit.svelte.dev">
|
||||
<img src={logo} alt="SvelteKit" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<svg viewBox="0 0 2 3" aria-hidden="true">
|
||||
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
|
||||
</svg>
|
||||
<ul>
|
||||
<li aria-current={$page.url.pathname === '/' ? 'page' : undefined}>
|
||||
<a href="/">Home</a>
|
||||
</li>
|
||||
<li aria-current={$page.url.pathname === '/about' ? 'page' : undefined}>
|
||||
<a href="/about">About</a>
|
||||
</li>
|
||||
<li aria-current={$page.url.pathname.startsWith('/sverdle') ? 'page' : undefined}>
|
||||
<a href="/sverdle">Sverdle</a>
|
||||
</li>
|
||||
</ul>
|
||||
<svg viewBox="0 0 2 3" aria-hidden="true">
|
||||
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
|
||||
</svg>
|
||||
</nav>
|
||||
|
||||
<div class="corner">
|
||||
<a href="https://github.com/sveltejs/kit">
|
||||
<img src={github} alt="GitHub" />
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.corner {
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
}
|
||||
|
||||
.corner a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.corner img {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
--background: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 2em;
|
||||
height: 3em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
path {
|
||||
fill: var(--background);
|
||||
}
|
||||
|
||||
ul {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 3em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
list-style: none;
|
||||
background: var(--background);
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
li[aria-current='page']::before {
|
||||
--size: 6px;
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: calc(50% - var(--size));
|
||||
border: var(--size) solid transparent;
|
||||
border-top: var(--size) solid var(--color-theme-1);
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
padding: 0 0.5rem;
|
||||
color: var(--color-text);
|
||||
font-weight: 700;
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s linear;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-theme-1);
|
||||
}
|
||||
</style>
|
|
@ -1,26 +0,0 @@
|
|||
<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>
|
|
@ -1,9 +0,0 @@
|
|||
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;
|
|
@ -1,107 +0,0 @@
|
|||
@import '@fontsource/fira-mono';
|
||||
|
||||
: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;
|
||||
background-attachment: fixed;
|
||||
background-color: var(--color-bg-1);
|
||||
background-size: 100vw 100vh;
|
||||
background-image: radial-gradient(
|
||||
50% 50% at 50% 50%,
|
||||
rgba(255, 255, 255, 0.75) 0%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
),
|
||||
linear-gradient(180deg, var(--color-bg-0) 0%, var(--color-bg-1) 15%, var(--color-bg-2) 50%);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
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;
|
|
@ -1,411 +0,0 @@
|
|||
<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']
|
||||
}}
|
||||
/>
|
||||
{/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>
|
|
@ -1,9 +0,0 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { Game } from './game';
|
||||
|
||||
describe('game test', () => {
|
||||
it('returns true when a valid word is entered', () => {
|
||||
const game = new Game();
|
||||
expect(game.enter('zorro'.split(''))).toBe(true);
|
||||
});
|
||||
});
|
|
@ -1,75 +0,0 @@
|
|||
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(' ')}`;
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<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><form></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>
|
|
@ -1,9 +0,0 @@
|
|||
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;
|
|
@ -1,23 +0,0 @@
|
|||
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
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
|
@ -1,3 +0,0 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
|
@ -1,18 +0,0 @@
|
|||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -1,6 +0,0 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
|
||||
test('about page has expected h1', async ({ page }) => {
|
||||
await page.goto('/about');
|
||||
await expect(page.getByRole('heading', { name: 'About this app' })).toBeVisible();
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"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
|
||||
// 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
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}']
|
||||
}
|
||||
});
|
5
go.mod
5
go.mod
|
@ -9,11 +9,15 @@ require (
|
|||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/goccy/go-yaml v1.11.3 // indirect
|
||||
github.com/gofiber/fiber/v2 v2.52.4 // indirect
|
||||
github.com/gofiber/template v1.8.3 // indirect
|
||||
github.com/gofiber/template/html/v2 v2.1.1 // indirect
|
||||
github.com/gofiber/utils v1.1.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
|
@ -25,6 +29,7 @@ require (
|
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
|
|
8
go.sum
8
go.sum
|
@ -1,5 +1,6 @@
|
|||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -13,6 +14,12 @@ github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
|
|||
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
|
||||
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
||||
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
|
||||
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
||||
github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8=
|
||||
github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0=
|
||||
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
||||
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
|
@ -40,6 +47,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
|
|
1
main.go
1
main.go
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
*/
|
||||
package main
|
||||
|
||||
|
|
22
package.json
Normal file
22
package.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "shadow",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun run vite build --watch --debug"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"glob": "^10.3.12",
|
||||
"postcss": "^8.4.38",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"ts-node": "^10.9.2",
|
||||
"vite": "^5.2.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"vitest": "^1.5.0"
|
||||
}
|
||||
}
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
}
|
||||
}
|
13
src/index.html
Normal file
13
src/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
{{ block "meta" . }}{{end}}
|
||||
<title>{{ template "title" . }}</title>
|
||||
{{ block "scripts" . }}{{end}}
|
||||
<link rel="stylesheet" href="{{ viteAsset `/styles/app.css` }}">
|
||||
</head>
|
||||
<body>
|
||||
{{ template "body" . }}
|
||||
</body>
|
||||
</html>
|
3
src/styles/app.css
Normal file
3
src/styles/app.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
10
src/views/index.html
Normal file
10
src/views/index.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
{{ define "title" }} Hello World! {{end}}
|
||||
|
||||
{{ define "body" }}
|
||||
|
||||
<h1 class="text-3xl underline py-2 bg-slate-500">
|
||||
Hello world!
|
||||
</h1>
|
||||
|
||||
|
||||
{{end}}
|
15
tailwind.config.ts
Normal file
15
tailwind.config.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import type { Config } from 'tailwindcss';
|
||||
|
||||
export default {
|
||||
darkMode: 'selector',
|
||||
content: [
|
||||
'./src/**/*.html',
|
||||
'./src/**/*.js',
|
||||
'./src/**/*.ts',
|
||||
'./src/index.html',
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
} satisfies Config;
|
25
tsconfig.json
Normal file
25
tsconfig.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": [
|
||||
"src/scripts"
|
||||
]
|
||||
}
|
143
utils/vite_dev.go
Normal file
143
utils/vite_dev.go
Normal file
|
@ -0,0 +1,143 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/spf13/viper"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//func DevWatcher(engine *html.Engine) {
|
||||
//
|
||||
// watcher, err := fsnotify.NewWatcher()
|
||||
// if err != nil {
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
// defer watcher.Close()
|
||||
//
|
||||
// err = watcher.Add("./src")
|
||||
// if err != nil {
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
//
|
||||
// if err := filepath.Walk("./src", func(path string, info fs.FileInfo, err error) error {
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// if info.IsDir() {
|
||||
// err = watcher.Add(path)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
// }); err != nil {
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
//
|
||||
// err = watcher.Add("./dist/.vite/manifest.json")
|
||||
// if err != nil {
|
||||
// log.Fatalln(err)
|
||||
// }
|
||||
//
|
||||
// for {
|
||||
// select {
|
||||
// case event, ok := <-watcher.Events:
|
||||
// if !ok {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// fmt.Println(event)
|
||||
//
|
||||
// if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
//
|
||||
// if filepath.Ext(event.Name) == ".gohtml" {
|
||||
//
|
||||
// fmt.Println("Ayo, I updated")
|
||||
// engine = html.New("./src", ".gohtml")
|
||||
//
|
||||
// } else if strings.Contains(event.Name, "manifest.json") {
|
||||
//
|
||||
// engine = html.New("./src", ".gohtml")
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// case err, ok := <-watcher.Errors:
|
||||
// if !ok {
|
||||
// return
|
||||
// }
|
||||
// log.Println("Error:", err)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
func RunViteServer() {
|
||||
if viper.GetBool("dev") {
|
||||
|
||||
go func() {
|
||||
|
||||
cmd := exec.Command("bun", "run", "dev")
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
defer stdout.Close()
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
defer stderr.Close()
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
io.Copy(os.Stdout, stderr)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
|
||||
for scanner.Scan() {
|
||||
|
||||
output := scanner.Text()
|
||||
|
||||
fmt.Println("VITE (Bun) | " + output)
|
||||
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
err = cmd.Wait()
|
||||
wg.Wait()
|
||||
|
||||
if cmd.ProcessState.ExitCode() != 0 {
|
||||
fmt.Println("Command failed with non-zero exit status")
|
||||
return
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
}
|
||||
}
|
19
vite.config.ts
Normal file
19
vite.config.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { defineConfig } from 'vitest/config';
|
||||
import path from "path";
|
||||
import { glob } from "glob";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [],
|
||||
root: "src",
|
||||
build: {
|
||||
manifest: true,
|
||||
outDir: path.join(__dirname, "dist"),
|
||||
rollupOptions: {
|
||||
input: glob.sync([path.resolve(__dirname, "src", "**/*.html"),path.resolve(__dirname, "src", "**/*.js"), path.resolve(__dirname, "src", "**/*.css")]),
|
||||
},
|
||||
emptyOutDir: true,
|
||||
},
|
||||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}']
|
||||
}
|
||||
});
|
Loading…
Reference in a new issue