Generated initial files for project
This commit is contained in:
		
							
								
								
									
										35
									
								
								.devcontainer/devcontainer.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								.devcontainer/devcontainer.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
 | 
			
		||||
// README at: https://github.com/devcontainers/templates/tree/main/src/python
 | 
			
		||||
{
 | 
			
		||||
	"name": "Python 3",
 | 
			
		||||
	// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
 | 
			
		||||
	"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
 | 
			
		||||
	"features": {
 | 
			
		||||
		"ghcr.io/devcontainers/features/node:1": {}
 | 
			
		||||
	},
 | 
			
		||||
	"customizations": {
 | 
			
		||||
		"vscode": {
 | 
			
		||||
			"extensions": [
 | 
			
		||||
				"ms-python.isort",
 | 
			
		||||
				"njpwerner.autodocstring",
 | 
			
		||||
				"ms-python.black-formatter",
 | 
			
		||||
				"ms-python.pylint"
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Features to add to the dev container. More info: https://containers.dev/features.
 | 
			
		||||
	// "features": {},
 | 
			
		||||
 | 
			
		||||
	// Use 'forwardPorts' to make a list of ports inside the container available locally.
 | 
			
		||||
	// "forwardPorts": [],
 | 
			
		||||
 | 
			
		||||
	// Use 'postCreateCommand' to run commands after the container is created.
 | 
			
		||||
	// "postCreateCommand": "pip3 install --user -r requirements.txt",
 | 
			
		||||
 | 
			
		||||
	// Configure tool-specific properties.
 | 
			
		||||
	// "customizations": {},
 | 
			
		||||
 | 
			
		||||
	// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
 | 
			
		||||
	// "remoteUser": "root"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										358
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,358 @@
 | 
			
		||||
# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
 | 
			
		||||
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,svelte,python,linux,node
 | 
			
		||||
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,svelte,python,linux,node
 | 
			
		||||
 | 
			
		||||
### Linux ###
 | 
			
		||||
*~
 | 
			
		||||
 | 
			
		||||
# temporary files which can be created if a process still has a handle open of a deleted file
 | 
			
		||||
.fuse_hidden*
 | 
			
		||||
 | 
			
		||||
# KDE directory preferences
 | 
			
		||||
.directory
 | 
			
		||||
 | 
			
		||||
# Linux trash folder which might appear on any partition or disk
 | 
			
		||||
.Trash-*
 | 
			
		||||
 | 
			
		||||
# .nfs files are created when an open file is removed but is still being accessed
 | 
			
		||||
.nfs*
 | 
			
		||||
 | 
			
		||||
### Node ###
 | 
			
		||||
# Logs
 | 
			
		||||
logs
 | 
			
		||||
*.log
 | 
			
		||||
npm-debug.log*
 | 
			
		||||
yarn-debug.log*
 | 
			
		||||
yarn-error.log*
 | 
			
		||||
lerna-debug.log*
 | 
			
		||||
.pnpm-debug.log*
 | 
			
		||||
 | 
			
		||||
# Diagnostic reports (https://nodejs.org/api/report.html)
 | 
			
		||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
 | 
			
		||||
 | 
			
		||||
# Runtime data
 | 
			
		||||
pids
 | 
			
		||||
*.pid
 | 
			
		||||
*.seed
 | 
			
		||||
*.pid.lock
 | 
			
		||||
 | 
			
		||||
# Directory for instrumented libs generated by jscoverage/JSCover
 | 
			
		||||
lib-cov
 | 
			
		||||
 | 
			
		||||
# Coverage directory used by tools like istanbul
 | 
			
		||||
coverage
 | 
			
		||||
*.lcov
 | 
			
		||||
 | 
			
		||||
# nyc test coverage
 | 
			
		||||
.nyc_output
 | 
			
		||||
 | 
			
		||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
 | 
			
		||||
.grunt
 | 
			
		||||
 | 
			
		||||
# Bower dependency directory (https://bower.io/)
 | 
			
		||||
bower_components
 | 
			
		||||
 | 
			
		||||
# node-waf configuration
 | 
			
		||||
.lock-wscript
 | 
			
		||||
 | 
			
		||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
 | 
			
		||||
build/Release
 | 
			
		||||
 | 
			
		||||
# Dependency directories
 | 
			
		||||
node_modules/
 | 
			
		||||
jspm_packages/
 | 
			
		||||
 | 
			
		||||
# Snowpack dependency directory (https://snowpack.dev/)
 | 
			
		||||
web_modules/
 | 
			
		||||
 | 
			
		||||
# TypeScript cache
 | 
			
		||||
*.tsbuildinfo
 | 
			
		||||
 | 
			
		||||
# Optional npm cache directory
 | 
			
		||||
.npm
 | 
			
		||||
 | 
			
		||||
# Optional eslint cache
 | 
			
		||||
.eslintcache
 | 
			
		||||
 | 
			
		||||
# Optional stylelint cache
 | 
			
		||||
.stylelintcache
 | 
			
		||||
 | 
			
		||||
# Microbundle cache
 | 
			
		||||
.rpt2_cache/
 | 
			
		||||
.rts2_cache_cjs/
 | 
			
		||||
.rts2_cache_es/
 | 
			
		||||
.rts2_cache_umd/
 | 
			
		||||
 | 
			
		||||
# Optional REPL history
 | 
			
		||||
.node_repl_history
 | 
			
		||||
 | 
			
		||||
# Output of 'npm pack'
 | 
			
		||||
*.tgz
 | 
			
		||||
 | 
			
		||||
# Yarn Integrity file
 | 
			
		||||
.yarn-integrity
 | 
			
		||||
 | 
			
		||||
# dotenv environment variable files
 | 
			
		||||
.env
 | 
			
		||||
.env.development.local
 | 
			
		||||
.env.test.local
 | 
			
		||||
.env.production.local
 | 
			
		||||
.env.local
 | 
			
		||||
 | 
			
		||||
# parcel-bundler cache (https://parceljs.org/)
 | 
			
		||||
.cache
 | 
			
		||||
.parcel-cache
 | 
			
		||||
 | 
			
		||||
# Next.js build output
 | 
			
		||||
.next
 | 
			
		||||
out
 | 
			
		||||
 | 
			
		||||
# Nuxt.js build / generate output
 | 
			
		||||
.nuxt
 | 
			
		||||
dist
 | 
			
		||||
 | 
			
		||||
# Gatsby files
 | 
			
		||||
.cache/
 | 
			
		||||
# Comment in the public line in if your project uses Gatsby and not Next.js
 | 
			
		||||
# https://nextjs.org/blog/next-9-1#public-directory-support
 | 
			
		||||
# public
 | 
			
		||||
 | 
			
		||||
# vuepress build output
 | 
			
		||||
.vuepress/dist
 | 
			
		||||
 | 
			
		||||
# vuepress v2.x temp and cache directory
 | 
			
		||||
.temp
 | 
			
		||||
 | 
			
		||||
# Docusaurus cache and generated files
 | 
			
		||||
.docusaurus
 | 
			
		||||
 | 
			
		||||
# Serverless directories
 | 
			
		||||
.serverless/
 | 
			
		||||
 | 
			
		||||
# FuseBox cache
 | 
			
		||||
.fusebox/
 | 
			
		||||
 | 
			
		||||
# DynamoDB Local files
 | 
			
		||||
.dynamodb/
 | 
			
		||||
 | 
			
		||||
# TernJS port file
 | 
			
		||||
.tern-port
 | 
			
		||||
 | 
			
		||||
# Stores VSCode versions used for testing VSCode extensions
 | 
			
		||||
.vscode-test
 | 
			
		||||
 | 
			
		||||
# yarn v2
 | 
			
		||||
.yarn/cache
 | 
			
		||||
.yarn/unplugged
 | 
			
		||||
.yarn/build-state.yml
 | 
			
		||||
.yarn/install-state.gz
 | 
			
		||||
.pnp.*
 | 
			
		||||
 | 
			
		||||
### Node Patch ###
 | 
			
		||||
# Serverless Webpack directories
 | 
			
		||||
.webpack/
 | 
			
		||||
 | 
			
		||||
# Optional stylelint cache
 | 
			
		||||
 | 
			
		||||
# SvelteKit build / generate output
 | 
			
		||||
.svelte-kit
 | 
			
		||||
 | 
			
		||||
### Python ###
 | 
			
		||||
# Byte-compiled / optimized / DLL files
 | 
			
		||||
__pycache__/
 | 
			
		||||
*.py[cod]
 | 
			
		||||
*$py.class
 | 
			
		||||
 | 
			
		||||
# C extensions
 | 
			
		||||
*.so
 | 
			
		||||
 | 
			
		||||
# Distribution / packaging
 | 
			
		||||
.Python
 | 
			
		||||
build/
 | 
			
		||||
develop-eggs/
 | 
			
		||||
dist/
 | 
			
		||||
downloads/
 | 
			
		||||
eggs/
 | 
			
		||||
.eggs/
 | 
			
		||||
lib/
 | 
			
		||||
lib64/
 | 
			
		||||
parts/
 | 
			
		||||
sdist/
 | 
			
		||||
var/
 | 
			
		||||
wheels/
 | 
			
		||||
share/python-wheels/
 | 
			
		||||
*.egg-info/
 | 
			
		||||
.installed.cfg
 | 
			
		||||
*.egg
 | 
			
		||||
MANIFEST
 | 
			
		||||
 | 
			
		||||
# PyInstaller
 | 
			
		||||
#  Usually these files are written by a python script from a template
 | 
			
		||||
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
 | 
			
		||||
*.manifest
 | 
			
		||||
*.spec
 | 
			
		||||
 | 
			
		||||
# Installer logs
 | 
			
		||||
pip-log.txt
 | 
			
		||||
pip-delete-this-directory.txt
 | 
			
		||||
 | 
			
		||||
# Unit test / coverage reports
 | 
			
		||||
htmlcov/
 | 
			
		||||
.tox/
 | 
			
		||||
.nox/
 | 
			
		||||
.coverage
 | 
			
		||||
.coverage.*
 | 
			
		||||
nosetests.xml
 | 
			
		||||
coverage.xml
 | 
			
		||||
*.cover
 | 
			
		||||
*.py,cover
 | 
			
		||||
.hypothesis/
 | 
			
		||||
.pytest_cache/
 | 
			
		||||
cover/
 | 
			
		||||
 | 
			
		||||
# Translations
 | 
			
		||||
*.mo
 | 
			
		||||
*.pot
 | 
			
		||||
 | 
			
		||||
# Django stuff:
 | 
			
		||||
local_settings.py
 | 
			
		||||
db.sqlite3
 | 
			
		||||
db.sqlite3-journal
 | 
			
		||||
 | 
			
		||||
# Flask stuff:
 | 
			
		||||
instance/
 | 
			
		||||
.webassets-cache
 | 
			
		||||
 | 
			
		||||
# Scrapy stuff:
 | 
			
		||||
.scrapy
 | 
			
		||||
 | 
			
		||||
# Sphinx documentation
 | 
			
		||||
docs/_build/
 | 
			
		||||
 | 
			
		||||
# PyBuilder
 | 
			
		||||
.pybuilder/
 | 
			
		||||
target/
 | 
			
		||||
 | 
			
		||||
# Jupyter Notebook
 | 
			
		||||
.ipynb_checkpoints
 | 
			
		||||
 | 
			
		||||
# IPython
 | 
			
		||||
profile_default/
 | 
			
		||||
ipython_config.py
 | 
			
		||||
 | 
			
		||||
# pyenv
 | 
			
		||||
#   For a library or package, you might want to ignore these files since the code is
 | 
			
		||||
#   intended to run in multiple environments; otherwise, check them in:
 | 
			
		||||
# .python-version
 | 
			
		||||
 | 
			
		||||
# pipenv
 | 
			
		||||
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 | 
			
		||||
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
 | 
			
		||||
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
 | 
			
		||||
#   install all needed dependencies.
 | 
			
		||||
#Pipfile.lock
 | 
			
		||||
 | 
			
		||||
# poetry
 | 
			
		||||
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
 | 
			
		||||
#   This is especially recommended for binary packages to ensure reproducibility, and is more
 | 
			
		||||
#   commonly ignored for libraries.
 | 
			
		||||
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
 | 
			
		||||
#poetry.lock
 | 
			
		||||
 | 
			
		||||
# pdm
 | 
			
		||||
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
 | 
			
		||||
#pdm.lock
 | 
			
		||||
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
 | 
			
		||||
#   in version control.
 | 
			
		||||
#   https://pdm.fming.dev/#use-with-ide
 | 
			
		||||
.pdm.toml
 | 
			
		||||
 | 
			
		||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
 | 
			
		||||
__pypackages__/
 | 
			
		||||
 | 
			
		||||
# Celery stuff
 | 
			
		||||
celerybeat-schedule
 | 
			
		||||
celerybeat.pid
 | 
			
		||||
 | 
			
		||||
# SageMath parsed files
 | 
			
		||||
*.sage.py
 | 
			
		||||
 | 
			
		||||
# Environments
 | 
			
		||||
.venv
 | 
			
		||||
env/
 | 
			
		||||
venv/
 | 
			
		||||
ENV/
 | 
			
		||||
env.bak/
 | 
			
		||||
venv.bak/
 | 
			
		||||
 | 
			
		||||
# Spyder project settings
 | 
			
		||||
.spyderproject
 | 
			
		||||
.spyproject
 | 
			
		||||
 | 
			
		||||
# Rope project settings
 | 
			
		||||
.ropeproject
 | 
			
		||||
 | 
			
		||||
# mkdocs documentation
 | 
			
		||||
/site
 | 
			
		||||
 | 
			
		||||
# mypy
 | 
			
		||||
.mypy_cache/
 | 
			
		||||
.dmypy.json
 | 
			
		||||
dmypy.json
 | 
			
		||||
 | 
			
		||||
# Pyre type checker
 | 
			
		||||
.pyre/
 | 
			
		||||
 | 
			
		||||
# pytype static type analyzer
 | 
			
		||||
.pytype/
 | 
			
		||||
 | 
			
		||||
# Cython debug symbols
 | 
			
		||||
cython_debug/
 | 
			
		||||
 | 
			
		||||
# PyCharm
 | 
			
		||||
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
 | 
			
		||||
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
 | 
			
		||||
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
 | 
			
		||||
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
 | 
			
		||||
#.idea/
 | 
			
		||||
 | 
			
		||||
### Python Patch ###
 | 
			
		||||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
 | 
			
		||||
poetry.toml
 | 
			
		||||
 | 
			
		||||
# ruff
 | 
			
		||||
.ruff_cache/
 | 
			
		||||
 | 
			
		||||
# LSP config files
 | 
			
		||||
pyrightconfig.json
 | 
			
		||||
 | 
			
		||||
### Svelte ###
 | 
			
		||||
# gitignore template for the SvelteKit, frontend web component framework
 | 
			
		||||
# website: https://kit.svelte.dev/
 | 
			
		||||
 | 
			
		||||
.svelte-kit/
 | 
			
		||||
package
 | 
			
		||||
 | 
			
		||||
### VisualStudioCode ###
 | 
			
		||||
.vscode/*
 | 
			
		||||
!.vscode/settings.json
 | 
			
		||||
!.vscode/tasks.json
 | 
			
		||||
!.vscode/launch.json
 | 
			
		||||
!.vscode/extensions.json
 | 
			
		||||
!.vscode/*.code-snippets
 | 
			
		||||
 | 
			
		||||
# Local History for Visual Studio Code
 | 
			
		||||
.history/
 | 
			
		||||
 | 
			
		||||
# Built Visual Studio Code Extensions
 | 
			
		||||
*.vsix
 | 
			
		||||
 | 
			
		||||
### VisualStudioCode Patch ###
 | 
			
		||||
# Ignore all local history of files
 | 
			
		||||
.history
 | 
			
		||||
.ionide
 | 
			
		||||
 | 
			
		||||
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,svelte,python,linux,node
 | 
			
		||||
 | 
			
		||||
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								mlstmyfasta.client/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								mlstmyfasta.client/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
test-results
 | 
			
		||||
node_modules
 | 
			
		||||
 | 
			
		||||
# Output
 | 
			
		||||
.output
 | 
			
		||||
.vercel
 | 
			
		||||
.netlify
 | 
			
		||||
.wrangler
 | 
			
		||||
/.svelte-kit
 | 
			
		||||
/build
 | 
			
		||||
 | 
			
		||||
# OS
 | 
			
		||||
.DS_Store
 | 
			
		||||
Thumbs.db
 | 
			
		||||
 | 
			
		||||
# Env
 | 
			
		||||
.env
 | 
			
		||||
.env.*
 | 
			
		||||
!.env.example
 | 
			
		||||
!.env.test
 | 
			
		||||
 | 
			
		||||
# Vite
 | 
			
		||||
vite.config.js.timestamp-*
 | 
			
		||||
vite.config.ts.timestamp-*
 | 
			
		||||
							
								
								
									
										1
									
								
								mlstmyfasta.client/.npmrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								mlstmyfasta.client/.npmrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
engine-strict=true
 | 
			
		||||
							
								
								
									
										4
									
								
								mlstmyfasta.client/.prettierignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								mlstmyfasta.client/.prettierignore
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
# Package Managers
 | 
			
		||||
package-lock.json
 | 
			
		||||
pnpm-lock.yaml
 | 
			
		||||
yarn.lock
 | 
			
		||||
							
								
								
									
										15
									
								
								mlstmyfasta.client/.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								mlstmyfasta.client/.prettierrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
{
 | 
			
		||||
	"useTabs": true,
 | 
			
		||||
	"singleQuote": true,
 | 
			
		||||
	"trailingComma": "none",
 | 
			
		||||
	"printWidth": 100,
 | 
			
		||||
	"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
 | 
			
		||||
	"overrides": [
 | 
			
		||||
		{
 | 
			
		||||
			"files": "*.svelte",
 | 
			
		||||
			"options": {
 | 
			
		||||
				"parser": "svelte"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								mlstmyfasta.client/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								mlstmyfasta.client/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
# sv
 | 
			
		||||
 | 
			
		||||
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
 | 
			
		||||
 | 
			
		||||
## Creating a project
 | 
			
		||||
 | 
			
		||||
If you're seeing this, you've probably already done this step. Congrats!
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# create a new project in the current directory
 | 
			
		||||
npx sv create
 | 
			
		||||
 | 
			
		||||
# create a new project in my-app
 | 
			
		||||
npx sv create my-app
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Developing
 | 
			
		||||
 | 
			
		||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm run dev
 | 
			
		||||
 | 
			
		||||
# or start the server and open the app in a new browser tab
 | 
			
		||||
npm run dev -- --open
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Building
 | 
			
		||||
 | 
			
		||||
To create a production version of your app:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm run build
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You can preview the production build with `npm run preview`.
 | 
			
		||||
 | 
			
		||||
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
 | 
			
		||||
							
								
								
									
										6
									
								
								mlstmyfasta.client/e2e/demo.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								mlstmyfasta.client/e2e/demo.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
import { expect, test } from '@playwright/test';
 | 
			
		||||
 | 
			
		||||
test('home page has expected h1', async ({ page }) => {
 | 
			
		||||
	await page.goto('/');
 | 
			
		||||
	await expect(page.locator('h1')).toBeVisible();
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										24
									
								
								mlstmyfasta.client/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								mlstmyfasta.client/eslint.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
import prettier from 'eslint-config-prettier';
 | 
			
		||||
import js from '@eslint/js';
 | 
			
		||||
import { includeIgnoreFile } from '@eslint/compat';
 | 
			
		||||
import svelte from 'eslint-plugin-svelte';
 | 
			
		||||
import globals from 'globals';
 | 
			
		||||
import { fileURLToPath } from 'node:url';
 | 
			
		||||
const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
 | 
			
		||||
 | 
			
		||||
/** @type {import('eslint').Linter.Config[]} */
 | 
			
		||||
export default [
 | 
			
		||||
	includeIgnoreFile(gitignorePath),
 | 
			
		||||
	js.configs.recommended,
 | 
			
		||||
	...svelte.configs['flat/recommended'],
 | 
			
		||||
	prettier,
 | 
			
		||||
	...svelte.configs['flat/prettier'],
 | 
			
		||||
	{
 | 
			
		||||
		languageOptions: {
 | 
			
		||||
			globals: {
 | 
			
		||||
				...globals.browser,
 | 
			
		||||
				...globals.node
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
];
 | 
			
		||||
							
								
								
									
										19
									
								
								mlstmyfasta.client/jsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								mlstmyfasta.client/jsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
{
 | 
			
		||||
	"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://svelte.dev/docs/kit/configuration#alias
 | 
			
		||||
	// except $lib which is handled by https://svelte.dev/docs/kit/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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4694
									
								
								mlstmyfasta.client/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4694
									
								
								mlstmyfasta.client/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										45
									
								
								mlstmyfasta.client/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								mlstmyfasta.client/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "mlstmyfasta.client",
 | 
			
		||||
	"private": true,
 | 
			
		||||
	"version": "0.0.1",
 | 
			
		||||
	"type": "module",
 | 
			
		||||
	"scripts": {
 | 
			
		||||
		"dev": "vite dev",
 | 
			
		||||
		"build": "vite build",
 | 
			
		||||
		"preview": "vite preview",
 | 
			
		||||
		"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
 | 
			
		||||
		"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
 | 
			
		||||
		"format": "prettier --write .",
 | 
			
		||||
		"lint": "prettier --check . && eslint .",
 | 
			
		||||
		"test:unit": "vitest",
 | 
			
		||||
		"test": "npm run test:unit -- --run && npm run test:e2e",
 | 
			
		||||
		"test:e2e": "playwright test"
 | 
			
		||||
	},
 | 
			
		||||
	"devDependencies": {
 | 
			
		||||
		"@eslint/compat": "^1.2.3",
 | 
			
		||||
		"@fontsource/fira-mono": "^5.0.0",
 | 
			
		||||
		"@neoconfetti/svelte": "^2.0.0",
 | 
			
		||||
		"@playwright/test": "^1.45.3",
 | 
			
		||||
		"@sveltejs/adapter-static": "^3.0.6",
 | 
			
		||||
		"@sveltejs/kit": "^2.0.0",
 | 
			
		||||
		"@sveltejs/vite-plugin-svelte": "^4.0.0",
 | 
			
		||||
		"autoprefixer": "^10.4.20",
 | 
			
		||||
		"eslint": "^9.7.0",
 | 
			
		||||
		"eslint-config-prettier": "^9.1.0",
 | 
			
		||||
		"eslint-plugin-svelte": "^2.36.0",
 | 
			
		||||
		"globals": "^15.0.0",
 | 
			
		||||
		"prettier": "^3.3.2",
 | 
			
		||||
		"prettier-plugin-svelte": "^3.2.6",
 | 
			
		||||
		"prettier-plugin-tailwindcss": "^0.6.5",
 | 
			
		||||
		"svelte": "^5.0.0",
 | 
			
		||||
		"svelte-check": "^4.0.0",
 | 
			
		||||
		"tailwindcss": "^3.4.9",
 | 
			
		||||
		"typescript": "^5.0.0",
 | 
			
		||||
		"vite": "^5.4.11",
 | 
			
		||||
		"vitest": "^2.0.4"
 | 
			
		||||
	},
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"@tailwindcss/forms": "^0.5.9",
 | 
			
		||||
		"@tailwindcss/typography": "^0.5.15"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								mlstmyfasta.client/playwright.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								mlstmyfasta.client/playwright.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
import { defineConfig } from '@playwright/test';
 | 
			
		||||
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
	webServer: {
 | 
			
		||||
		command: 'npm run build && npm run preview',
 | 
			
		||||
		port: 4173
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	testDir: 'e2e'
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										6
									
								
								mlstmyfasta.client/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								mlstmyfasta.client/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
export default {
 | 
			
		||||
	plugins: {
 | 
			
		||||
		tailwindcss: {},
 | 
			
		||||
		autoprefixer: {}
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										110
									
								
								mlstmyfasta.client/src/app.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								mlstmyfasta.client/src/app.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
@import 'tailwindcss/base';
 | 
			
		||||
@import 'tailwindcss/components';
 | 
			
		||||
@import 'tailwindcss/utilities';
 | 
			
		||||
@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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								mlstmyfasta.client/src/app.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								mlstmyfasta.client/src/app.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
// See https://svelte.dev/docs/kit/types#app.d.ts
 | 
			
		||||
// for information about these interfaces
 | 
			
		||||
declare global {
 | 
			
		||||
	namespace App {
 | 
			
		||||
		// interface Error {}
 | 
			
		||||
		// interface Locals {}
 | 
			
		||||
		// interface PageData {}
 | 
			
		||||
		// interface PageState {}
 | 
			
		||||
		// interface Platform {}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export {};
 | 
			
		||||
							
								
								
									
										12
									
								
								mlstmyfasta.client/src/app.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								mlstmyfasta.client/src/app.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
<!doctype html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
	<head>
 | 
			
		||||
		<meta charset="utf-8" />
 | 
			
		||||
		<link rel="icon" href="%sveltekit.assets%/favicon.png" />
 | 
			
		||||
		<meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
			
		||||
		%sveltekit.head%
 | 
			
		||||
	</head>
 | 
			
		||||
	<body data-sveltekit-preload-data="hover">
 | 
			
		||||
		<div style="display: contents">%sveltekit.body%</div>
 | 
			
		||||
	</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										7
									
								
								mlstmyfasta.client/src/demo.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								mlstmyfasta.client/src/demo.spec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
import { describe, it, expect } from 'vitest';
 | 
			
		||||
 | 
			
		||||
describe('sum test', () => {
 | 
			
		||||
	it('adds 1 + 2 to equal 3', () => {
 | 
			
		||||
		expect(1 + 2).toBe(3);
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										58
									
								
								mlstmyfasta.client/src/routes/+layout.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								mlstmyfasta.client/src/routes/+layout.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
<script>
 | 
			
		||||
	import Header from './Header.svelte';
 | 
			
		||||
	import '../app.css';
 | 
			
		||||
 | 
			
		||||
	/** @type {{children: import('svelte').Snippet}} */
 | 
			
		||||
	let { children } = $props();
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="app">
 | 
			
		||||
	<Header />
 | 
			
		||||
 | 
			
		||||
	<main>
 | 
			
		||||
		{@render children()}
 | 
			
		||||
	</main>
 | 
			
		||||
 | 
			
		||||
	<footer>
 | 
			
		||||
		<p>
 | 
			
		||||
			visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to learn about 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>
 | 
			
		||||
							
								
								
									
										3
									
								
								mlstmyfasta.client/src/routes/+page.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								mlstmyfasta.client/src/routes/+page.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
// since there's no dynamic data here, we can prerender
 | 
			
		||||
// it so that it gets served as a static asset in production
 | 
			
		||||
export const prerender = true;
 | 
			
		||||
							
								
								
									
										59
									
								
								mlstmyfasta.client/src/routes/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								mlstmyfasta.client/src/routes/+page.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
<script>
 | 
			
		||||
	import Counter from './Counter.svelte';
 | 
			
		||||
	import welcome from '$lib/images/svelte-welcome.webp';
 | 
			
		||||
	import welcomeFallback 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={welcomeFallback} 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>
 | 
			
		||||
							
								
								
									
										110
									
								
								mlstmyfasta.client/src/routes/Counter.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								mlstmyfasta.client/src/routes/Counter.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
<script>
 | 
			
		||||
	import { spring } from 'svelte/motion';
 | 
			
		||||
 | 
			
		||||
	let count = $state(0);
 | 
			
		||||
 | 
			
		||||
	// svelte-ignore state_referenced_locally
 | 
			
		||||
	const displayedCount = spring(count);
 | 
			
		||||
 | 
			
		||||
	$effect(() => {
 | 
			
		||||
		displayedCount.set(count);
 | 
			
		||||
	});
 | 
			
		||||
	let offset = $derived(modulo($displayedCount, 1));
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param {number} n
 | 
			
		||||
	 * @param {number} m
 | 
			
		||||
	 */
 | 
			
		||||
	function modulo(n, m) {
 | 
			
		||||
		// handle negative numbers
 | 
			
		||||
		return ((n % m) + m) % m;
 | 
			
		||||
	}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="counter">
 | 
			
		||||
	<button onclick={() => (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($displayedCount + 1)}</strong>
 | 
			
		||||
			<strong>{Math.floor($displayedCount)}</strong>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<button onclick={() => (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>
 | 
			
		||||
							
								
								
									
										129
									
								
								mlstmyfasta.client/src/routes/Header.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								mlstmyfasta.client/src/routes/Header.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,129 @@
 | 
			
		||||
<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://svelte.dev/docs/kit">
 | 
			
		||||
			<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>
 | 
			
		||||
							
								
								
									
										9
									
								
								mlstmyfasta.client/src/routes/about/+page.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								mlstmyfasta.client/src/routes/about/+page.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import { dev } from '$app/environment';
 | 
			
		||||
 | 
			
		||||
// we don't need any JS on this page, though we'll load
 | 
			
		||||
// it in dev so that we get hot module replacement
 | 
			
		||||
export const csr = dev;
 | 
			
		||||
 | 
			
		||||
// since there's no dynamic data here, we can prerender
 | 
			
		||||
// it so that it gets served as a static asset in production
 | 
			
		||||
export const prerender = true;
 | 
			
		||||
							
								
								
									
										26
									
								
								mlstmyfasta.client/src/routes/about/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								mlstmyfasta.client/src/routes/about/+page.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
<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://svelte.dev/docs/kit">SvelteKit</a> app. You can make your own by typing
 | 
			
		||||
		the following into your command line and following the prompts:
 | 
			
		||||
	</p>
 | 
			
		||||
 | 
			
		||||
	<pre>npx sv create</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>
 | 
			
		||||
							
								
								
									
										70
									
								
								mlstmyfasta.client/src/routes/sverdle/+page.server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								mlstmyfasta.client/src/routes/sverdle/+page.server.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
import { fail } from '@sveltejs/kit';
 | 
			
		||||
import { Game } from './game';
 | 
			
		||||
 | 
			
		||||
/** @satisfies {import('./$types').PageServerLoad} */
 | 
			
		||||
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 {import('./$types').Actions} */
 | 
			
		||||
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 = /** @type {string[]} */ (data.getAll('guess'));
 | 
			
		||||
 | 
			
		||||
		if (!game.enter(guess)) {
 | 
			
		||||
			return fail(400, { badGuess: true });
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cookies.set('sverdle', game.toString(), { path: '/' });
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	restart: async ({ cookies }) => {
 | 
			
		||||
		cookies.delete('sverdle', { path: '/' });
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										420
									
								
								mlstmyfasta.client/src/routes/sverdle/+page.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										420
									
								
								mlstmyfasta.client/src/routes/sverdle/+page.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,420 @@
 | 
			
		||||
<script>
 | 
			
		||||
	import { enhance } from '$app/forms';
 | 
			
		||||
	import { confetti } from '@neoconfetti/svelte';
 | 
			
		||||
 | 
			
		||||
	import { reducedMotion } from './reduced-motion';
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @typedef {Object} Props
 | 
			
		||||
	 * @property {import('./$types').PageData} data
 | 
			
		||||
	 * @property {import('./$types').ActionData} form
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @type {Props}
 | 
			
		||||
	 */
 | 
			
		||||
	let { data, form = $bindable() } = $props();
 | 
			
		||||
 | 
			
		||||
	/** Whether or not the user has won */
 | 
			
		||||
	let won = $derived(data.answers.at(-1) === 'xxxxx');
 | 
			
		||||
 | 
			
		||||
	/** The index of the current guess */
 | 
			
		||||
	let i = $derived(won ? -1 : data.answers.length);
 | 
			
		||||
 | 
			
		||||
	/** The current guess */
 | 
			
		||||
	// svelte-ignore state_referenced_locally
 | 
			
		||||
	let currentGuess = $state(data.guesses[i] || '');
 | 
			
		||||
 | 
			
		||||
	$effect(() => {
 | 
			
		||||
		currentGuess = data.guesses[i] || '';
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	/** Whether the current guess can be submitted */
 | 
			
		||||
	let submittable = $derived(currentGuess.length === 5);
 | 
			
		||||
 | 
			
		||||
	const { classnames, description } = $derived.by(() => {
 | 
			
		||||
		/**
 | 
			
		||||
		 * A map of classnames for all letters that have been guessed,
 | 
			
		||||
		 * used for styling the keyboard
 | 
			
		||||
		 * @type {Record<string, 'exact' | 'close' | 'missing'>}
 | 
			
		||||
		 */
 | 
			
		||||
		let classnames = {};
 | 
			
		||||
		/**
 | 
			
		||||
		 * A map of descriptions for all letters that have been guessed,
 | 
			
		||||
		 * used for adding text for assistive technology (e.g. screen readers)
 | 
			
		||||
		 * @type {Record<string, string>}
 | 
			
		||||
		 */
 | 
			
		||||
		let 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';
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		return { classnames, description };
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Modify the game state without making a trip to the server,
 | 
			
		||||
	 * if client-side JavaScript is enabled
 | 
			
		||||
	 * @param {MouseEvent} event
 | 
			
		||||
	 */
 | 
			
		||||
	function update(event) {
 | 
			
		||||
		event.preventDefault();
 | 
			
		||||
		const key = /** @type {HTMLButtonElement} */ (event.target).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
 | 
			
		||||
	 * @param {KeyboardEvent} event
 | 
			
		||||
	 */
 | 
			
		||||
	function keydown(event) {
 | 
			
		||||
		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 onkeydown={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
 | 
			
		||||
					onclick={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
 | 
			
		||||
								onclick={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: $reducedMotion ? 0 : undefined,
 | 
			
		||||
			force: 0.7,
 | 
			
		||||
			stageWidth: window.innerWidth,
 | 
			
		||||
			stageHeight: window.innerHeight,
 | 
			
		||||
			colors: ['#ff3e00', '#40b3ff', '#676778']
 | 
			
		||||
		}}
 | 
			
		||||
	></div>
 | 
			
		||||
{/if}
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
	form {
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		height: 100%;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		flex-direction: column;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		gap: 1rem;
 | 
			
		||||
		flex: 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.how-to-play {
 | 
			
		||||
		color: var(--color-text);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.how-to-play::before {
 | 
			
		||||
		content: 'i';
 | 
			
		||||
		display: inline-block;
 | 
			
		||||
		font-size: 0.8em;
 | 
			
		||||
		font-weight: 900;
 | 
			
		||||
		width: 1em;
 | 
			
		||||
		height: 1em;
 | 
			
		||||
		padding: 0.2em;
 | 
			
		||||
		line-height: 1;
 | 
			
		||||
		border: 1.5px solid var(--color-text);
 | 
			
		||||
		border-radius: 50%;
 | 
			
		||||
		text-align: center;
 | 
			
		||||
		margin: 0 0.5em 0 0;
 | 
			
		||||
		position: relative;
 | 
			
		||||
		top: -0.05em;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.grid {
 | 
			
		||||
		--width: min(100vw, 40vh, 380px);
 | 
			
		||||
		max-width: var(--width);
 | 
			
		||||
		align-self: center;
 | 
			
		||||
		justify-self: center;
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		height: 100%;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		flex-direction: column;
 | 
			
		||||
		justify-content: flex-start;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.grid .row {
 | 
			
		||||
		display: grid;
 | 
			
		||||
		grid-template-columns: repeat(5, 1fr);
 | 
			
		||||
		grid-gap: 0.2rem;
 | 
			
		||||
		margin: 0 0 0.2rem 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@media (prefers-reduced-motion: no-preference) {
 | 
			
		||||
		.grid.bad-guess .row.current {
 | 
			
		||||
			animation: wiggle 0.5s;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.grid.playing .row.current {
 | 
			
		||||
		filter: drop-shadow(3px 3px 10px var(--color-bg-0));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.letter {
 | 
			
		||||
		aspect-ratio: 1;
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		align-items: center;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		text-align: center;
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
		text-transform: lowercase;
 | 
			
		||||
		border: none;
 | 
			
		||||
		font-size: calc(0.08 * var(--width));
 | 
			
		||||
		border-radius: 2px;
 | 
			
		||||
		background: white;
 | 
			
		||||
		margin: 0;
 | 
			
		||||
		color: rgba(0, 0, 0, 0.7);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.letter.missing {
 | 
			
		||||
		background: rgba(255, 255, 255, 0.5);
 | 
			
		||||
		color: rgba(0, 0, 0, 0.5);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.letter.exact {
 | 
			
		||||
		background: var(--color-theme-2);
 | 
			
		||||
		color: white;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.letter.close {
 | 
			
		||||
		border: 2px solid var(--color-theme-2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.selected {
 | 
			
		||||
		outline: 2px solid var(--color-theme-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.controls {
 | 
			
		||||
		text-align: center;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		height: min(18vh, 10rem);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard {
 | 
			
		||||
		--gap: 0.2rem;
 | 
			
		||||
		position: relative;
 | 
			
		||||
		display: flex;
 | 
			
		||||
		flex-direction: column;
 | 
			
		||||
		gap: var(--gap);
 | 
			
		||||
		height: 100%;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard .row {
 | 
			
		||||
		display: flex;
 | 
			
		||||
		justify-content: center;
 | 
			
		||||
		gap: 0.2rem;
 | 
			
		||||
		flex: 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard button,
 | 
			
		||||
	.keyboard button:disabled {
 | 
			
		||||
		--size: min(8vw, 4vh, 40px);
 | 
			
		||||
		background-color: white;
 | 
			
		||||
		color: black;
 | 
			
		||||
		width: var(--size);
 | 
			
		||||
		border: none;
 | 
			
		||||
		border-radius: 2px;
 | 
			
		||||
		font-size: calc(var(--size) * 0.5);
 | 
			
		||||
		margin: 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard button.exact {
 | 
			
		||||
		background: var(--color-theme-2);
 | 
			
		||||
		color: white;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard button.missing {
 | 
			
		||||
		opacity: 0.5;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard button.close {
 | 
			
		||||
		border: 2px solid var(--color-theme-2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard button:focus {
 | 
			
		||||
		background: var(--color-theme-1);
 | 
			
		||||
		color: white;
 | 
			
		||||
		outline: none;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard button[data-key='enter'],
 | 
			
		||||
	.keyboard button[data-key='backspace'] {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		bottom: 0;
 | 
			
		||||
		width: calc(1.5 * var(--size));
 | 
			
		||||
		height: calc(1 / 3 * (100% - 2 * var(--gap)));
 | 
			
		||||
		text-transform: uppercase;
 | 
			
		||||
		font-size: calc(0.3 * var(--size));
 | 
			
		||||
		padding-top: calc(0.15 * var(--size));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard button[data-key='enter'] {
 | 
			
		||||
		right: calc(50% + 3.5 * var(--size) + 0.8rem);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard button[data-key='backspace'] {
 | 
			
		||||
		left: calc(50% + 3.5 * var(--size) + 0.8rem);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.keyboard button[data-key='enter']:disabled {
 | 
			
		||||
		opacity: 0.5;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.restart {
 | 
			
		||||
		width: 100%;
 | 
			
		||||
		padding: 1rem;
 | 
			
		||||
		background: rgba(255, 255, 255, 0.5);
 | 
			
		||||
		border-radius: 2px;
 | 
			
		||||
		border: none;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.restart:focus,
 | 
			
		||||
	.restart:hover {
 | 
			
		||||
		background: var(--color-theme-1);
 | 
			
		||||
		color: white;
 | 
			
		||||
		outline: none;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@keyframes wiggle {
 | 
			
		||||
		0% {
 | 
			
		||||
			transform: translateX(0);
 | 
			
		||||
		}
 | 
			
		||||
		10% {
 | 
			
		||||
			transform: translateX(-2px);
 | 
			
		||||
		}
 | 
			
		||||
		30% {
 | 
			
		||||
			transform: translateX(4px);
 | 
			
		||||
		}
 | 
			
		||||
		50% {
 | 
			
		||||
			transform: translateX(-6px);
 | 
			
		||||
		}
 | 
			
		||||
		70% {
 | 
			
		||||
			transform: translateX(+4px);
 | 
			
		||||
		}
 | 
			
		||||
		90% {
 | 
			
		||||
			transform: translateX(-2px);
 | 
			
		||||
		}
 | 
			
		||||
		100% {
 | 
			
		||||
			transform: translateX(0);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										72
									
								
								mlstmyfasta.client/src/routes/sverdle/game.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								mlstmyfasta.client/src/routes/sverdle/game.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
import { words, allowed } from './words.server';
 | 
			
		||||
 | 
			
		||||
export class Game {
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create a game object from the player's cookie, or initialise a new game
 | 
			
		||||
	 * @param {string | undefined} serialized
 | 
			
		||||
	 */
 | 
			
		||||
	constructor(serialized = 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 = /** @type {string[]} */ ([]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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
 | 
			
		||||
	 * @param {string[]} letters
 | 
			
		||||
	 */
 | 
			
		||||
	enter(letters) {
 | 
			
		||||
		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(' ')}`;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
import { dev } from '$app/environment';
 | 
			
		||||
 | 
			
		||||
// we don't need any JS on this page, though we'll load
 | 
			
		||||
// it in dev so that we get hot module replacement
 | 
			
		||||
export const csr = dev;
 | 
			
		||||
 | 
			
		||||
// since there's no dynamic data here, we can prerender
 | 
			
		||||
// it so that it gets served as a static asset in production
 | 
			
		||||
export const prerender = true;
 | 
			
		||||
@@ -0,0 +1,95 @@
 | 
			
		||||
<svelte:head>
 | 
			
		||||
	<title>How to play Sverdle</title>
 | 
			
		||||
	<meta name="description" content="How to play Sverdle" />
 | 
			
		||||
</svelte:head>
 | 
			
		||||
 | 
			
		||||
<div class="text-column">
 | 
			
		||||
	<h1>How to play Sverdle</h1>
 | 
			
		||||
 | 
			
		||||
	<p>
 | 
			
		||||
		Sverdle is a clone of <a href="https://www.nytimes.com/games/wordle/index.html">Wordle</a>, the
 | 
			
		||||
		word guessing game. To play, enter a five-letter English word. For example:
 | 
			
		||||
	</p>
 | 
			
		||||
 | 
			
		||||
	<div class="example">
 | 
			
		||||
		<span class="close">r</span>
 | 
			
		||||
		<span class="missing">i</span>
 | 
			
		||||
		<span class="close">t</span>
 | 
			
		||||
		<span class="missing">z</span>
 | 
			
		||||
		<span class="exact">y</span>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<p>
 | 
			
		||||
		The <span class="exact">y</span> is in the right place. <span class="close">r</span> and
 | 
			
		||||
		<span class="close">t</span>
 | 
			
		||||
		are the right letters, but in the wrong place. The other letters are wrong, and can be discarded.
 | 
			
		||||
		Let's make another guess:
 | 
			
		||||
	</p>
 | 
			
		||||
 | 
			
		||||
	<div class="example">
 | 
			
		||||
		<span class="exact">p</span>
 | 
			
		||||
		<span class="exact">a</span>
 | 
			
		||||
		<span class="exact">r</span>
 | 
			
		||||
		<span class="exact">t</span>
 | 
			
		||||
		<span class="exact">y</span>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<p>This time we guessed right! You have <strong>six</strong> guesses to get the word.</p>
 | 
			
		||||
 | 
			
		||||
	<p>
 | 
			
		||||
		Unlike the original Wordle, Sverdle runs on the server instead of in the browser, making it
 | 
			
		||||
		impossible to cheat. It uses <code><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>
 | 
			
		||||
							
								
								
									
										26
									
								
								mlstmyfasta.client/src/routes/sverdle/reduced-motion.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								mlstmyfasta.client/src/routes/sverdle/reduced-motion.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
import { readable } from 'svelte/store';
 | 
			
		||||
import { browser } from '$app/environment';
 | 
			
		||||
 | 
			
		||||
const reducedMotionQuery = '(prefers-reduced-motion: reduce)';
 | 
			
		||||
 | 
			
		||||
const getInitialMotionPreference = () => {
 | 
			
		||||
	if (!browser) return false;
 | 
			
		||||
	return window.matchMedia(reducedMotionQuery).matches;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const reducedMotion = readable(getInitialMotionPreference(), (set) => {
 | 
			
		||||
	if (browser) {
 | 
			
		||||
		/**
 | 
			
		||||
		 * @param {MediaQueryListEvent} event
 | 
			
		||||
		 */
 | 
			
		||||
		const setReducedMotion = (event) => {
 | 
			
		||||
			set(event.matches);
 | 
			
		||||
		};
 | 
			
		||||
		const mediaQueryList = window.matchMedia(reducedMotionQuery);
 | 
			
		||||
		mediaQueryList.addEventListener('change', setReducedMotion);
 | 
			
		||||
 | 
			
		||||
		return () => {
 | 
			
		||||
			mediaQueryList.removeEventListener('change', setReducedMotion);
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										12980
									
								
								mlstmyfasta.client/src/routes/sverdle/words.server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12980
									
								
								mlstmyfasta.client/src/routes/sverdle/words.server.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								mlstmyfasta.client/static/favicon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								mlstmyfasta.client/static/favicon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.5 KiB  | 
							
								
								
									
										3
									
								
								mlstmyfasta.client/static/robots.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								mlstmyfasta.client/static/robots.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
# https://www.robotstxt.org/robotstxt.html
 | 
			
		||||
User-agent: *
 | 
			
		||||
Disallow:
 | 
			
		||||
							
								
								
									
										13
									
								
								mlstmyfasta.client/svelte.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								mlstmyfasta.client/svelte.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import adapter from '@sveltejs/adapter-static';
 | 
			
		||||
 | 
			
		||||
/** @type {import('@sveltejs/kit').Config} */
 | 
			
		||||
const config = {
 | 
			
		||||
	kit: {
 | 
			
		||||
		// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
 | 
			
		||||
		// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
 | 
			
		||||
		// See https://svelte.dev/docs/kit/adapters for more information about adapters.
 | 
			
		||||
		adapter: adapter()
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default config;
 | 
			
		||||
							
								
								
									
										13
									
								
								mlstmyfasta.client/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								mlstmyfasta.client/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import forms from '@tailwindcss/forms';
 | 
			
		||||
import typography from '@tailwindcss/typography';
 | 
			
		||||
 | 
			
		||||
/** @type {import('tailwindcss').Config} */
 | 
			
		||||
export default {
 | 
			
		||||
	content: ['./src/**/*.{html,js,svelte,ts}'],
 | 
			
		||||
 | 
			
		||||
	theme: {
 | 
			
		||||
		extend: {}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	plugins: [typography, forms]
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										10
									
								
								mlstmyfasta.client/vite.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								mlstmyfasta.client/vite.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
import { defineConfig } from 'vitest/config';
 | 
			
		||||
import { sveltekit } from '@sveltejs/kit/vite';
 | 
			
		||||
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
	plugins: [sveltekit()],
 | 
			
		||||
 | 
			
		||||
	test: {
 | 
			
		||||
		include: ['src/**/*.{test,spec}.{js,ts}']
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										31
									
								
								mlstmyfasta.server/src/mlstmyfasta/setup.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								mlstmyfasta.server/src/mlstmyfasta/setup.cfg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
[metadata]
 | 
			
		||||
name = mlstmyfasta.server
 | 
			
		||||
 | 
			
		||||
[options]
 | 
			
		||||
package_dir =
 | 
			
		||||
    = src
 | 
			
		||||
install_requires =
 | 
			
		||||
    sqlalchemy       ==2.0
 | 
			
		||||
    flask-sqlalchemy ==3.0
 | 
			
		||||
    flask            ==2.3
 | 
			
		||||
    pyvcf3           ==1
 | 
			
		||||
    requests         ==2.29
 | 
			
		||||
    cachier          ==2.1
 | 
			
		||||
    biopython        ==1.81
 | 
			
		||||
    scipy            ==1.10
 | 
			
		||||
    spacy            ==3.4
 | 
			
		||||
    matplotlib       ==3.7
 | 
			
		||||
    cupy             ==12.1
 | 
			
		||||
    en-core-sci-lg   ==0.5.1
 | 
			
		||||
    scispacy         ==0.5.2
 | 
			
		||||
 | 
			
		||||
[tool:pytest]
 | 
			
		||||
pythonpath = src
 | 
			
		||||
testpaths = tests
 | 
			
		||||
log_cli = 1
 | 
			
		||||
 | 
			
		||||
[pylint]
 | 
			
		||||
max-line-length = 88
 | 
			
		||||
 | 
			
		||||
[isort]
 | 
			
		||||
profile = black
 | 
			
		||||
		Reference in New Issue
	
	Block a user