sadly huge initial commit

This commit is contained in:
Jakub Kropáček 2024-12-21 23:35:45 +01:00
commit 8a37f14a48
92 changed files with 622 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

25
.gitignore vendored Normal file
View file

@ -0,0 +1,25 @@
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-*
.idea/

1
.npmrc Normal file
View file

@ -0,0 +1 @@
engine-strict=true

4
.prettierignore Normal file
View file

@ -0,0 +1,4 @@
# Package Managers
package-lock.json
pnpm-lock.yaml
yarn.lock

15
.prettierrc Normal file
View file

@ -0,0 +1,15 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}

38
README.md Normal file
View 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.

BIN
bun.lockb Executable file

Binary file not shown.

34
eslint.config.js Normal file
View file

@ -0,0 +1,34 @@
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';
import ts from 'typescript-eslint';
const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
export default ts.config(
includeIgnoreFile(gitignorePath),
js.configs.recommended,
...ts.configs.recommended,
...svelte.configs['flat/recommended'],
prettier,
...svelte.configs['flat/prettier'],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
},
{
files: ['**/*.svelte'],
languageOptions: {
parserOptions: {
parser: ts.parser
}
}
}
);

27
flake.lock Normal file
View file

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1734435836,
"narHash": "sha256-kMBQ5PRiFLagltK0sH+08aiNt3zGERC2297iB6vrvlU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4989a246d7a390a859852baddb1013f825435cee",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

21
flake.nix Normal file
View file

@ -0,0 +1,21 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
outputs = {nixpkgs, ...}: {
devShells.x86_64-linux =
let
pkgs = import nixpkgs { system = "x86_64-linux"; config.allowUnfree = true;};
in
{
default = pkgs.mkShell {
LD_LIBRARY_PATH = nixpkgs.lib.makeLibraryPath [ pkgs.stdenv.cc.cc ];
packages = with pkgs; [
bun
svelte-language-server
nodejs_23
];
};
};
};
}

35
package.json Normal file
View file

@ -0,0 +1,35 @@
{
"name": "maty-vanoce",
"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 ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"lint": "prettier --check . && eslint ."
},
"devDependencies": {
"@eslint/compat": "^1.2.3",
"@sveltejs/adapter-static": "^3.0.6",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"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",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0",
"vite": "^5.4.11"
},
"dependencies": {
"svelte-adapter-bun": "^0.5.2"
}
}

13
src/app.d.ts vendored Normal file
View 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
src/app.html Normal file
View 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>

87
src/lib/Modal.svelte Normal file
View file

@ -0,0 +1,87 @@
<script lang="ts">
const props = $props<{ onclose: () => void, videoUrl: string}>();
</script>
<div class="modal">
<div class="modal-content">
<button class="close" onclick={props.onclose}> X </button>
<video autoplay muted>
<source src={props.videoUrl} type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
</div>
<style>
/* Modal Background */
.modal {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7); /* Slightly darker background */
z-index: 1000; /* Ensure it's above other content */
}
/* Modal content box */
.modal-content {
position: relative;
max-width: 90%;
max-height: 90%;
padding: 10px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); /* Subtle shadow */
overflow: hidden;
}
/* Close button */
.close {
position: absolute;
top: 10px;
right: 10px;
border: none;
font-size: 1.5rem;
cursor: pointer;
width: 35px;
height: 35px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
background-color: #f2f2f2; /* Light gray background */
color: #333; /* Dark text for better visibility */
transition: background-color 0.3s ease;
}
.close:hover {
background-color: #e0e0e0; /* Slight color change on hover */
}
/* Video styling */
video {
width: 100%;
height: auto;
max-height: 80vh; /* Ensure it fits the screen */
border-radius: 5px; /* Rounded corners for the video */
}
/* Optional responsiveness for smaller screens */
@media (max-width: 768px) {
.modal-content {
max-width: 95%;
max-height: 95%;
}
.close {
top: 5px;
right: 5px;
width: 30px;
height: 30px;
}
}
</style>

1
src/lib/index.ts Normal file
View file

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

230
src/routes/+page.svelte Normal file
View file

@ -0,0 +1,230 @@
<script lang="ts">
import Modal from '$lib/Modal.svelte';
const difficultyClass = 15;
const skin = 'winter';
let rollResult = $state<number | null>(null);
let guidanceResult = $state<number | null>(null);
let pressedContinue = $state<boolean>(false);
let rollModal = $state<boolean>(false);
let rollVideoUrl = $state<string | null>(null);
let rollClickedExit = $state<boolean>(false);
let guidanceModal = $state<boolean>(false);
let guidanceVideoUrl = $state<string | null>(null);
let guidanceClickedExit = $state<boolean>(false);
const getVideoUrl = async (skin: string, type: string, result: number) => {
return await fetch(`/video/${skin}/${type}/${result}`)
.then((res) => res.json())
.then((data) => `/videos/${data.videoFile}`);
};
const handleRoll = async () => {
rollResult = Math.floor(Math.random() * 20) + 1;
rollVideoUrl = await getVideoUrl(skin, 'd20', rollResult);
showModal('roll');
};
const handleGuidance = async () => {
guidanceResult = Math.floor(Math.random() * 4) + 1;
guidanceVideoUrl = await getVideoUrl(skin, 'd4', guidanceResult);
showModal('guidance');
};
const showModal = (modal: string) => {
if (modal === 'roll') {
rollModal = true;
} else if (modal === 'guidance') {
guidanceModal = true;
}
};
const resetPage = () => {
rollResult = null;
guidanceResult = null;
pressedContinue = false;
};
</script>
<main>
<section>
<h1>You have received a gift</h1>
</section>
<section>
<h2>Investigation</h2>
<p>Intelligence check</p>
</section>
<section>
<h2>Difficulty Class</h2>
<p>{difficultyClass}</p>
</section>
{#if !pressedContinue}
<section>
{#if !rollResult}
<button onclick={handleRoll}>Roll Ability</button>
{/if}
{#if rollResult && rollClickedExit}
<h2>Roll Result</h2>
<p>{rollResult}</p>
{/if}
</section>
{#if rollModal && rollVideoUrl}
<Modal onclose={() => {
rollModal = false;
rollClickedExit = true;
}} videoUrl={rollVideoUrl} />
{/if}
<section>
{#if !guidanceResult}
<button onclick={handleGuidance}>Roll Guidance</button>
{/if}
{#if guidanceResult && guidanceClickedExit}
<h2>Guidance Result</h2>
<p>{guidanceResult}</p>
{/if}
</section>
{#if guidanceModal && guidanceVideoUrl}
<Modal onclose={() => {
guidanceModal = false;
guidanceClickedExit = true;
}} videoUrl={guidanceVideoUrl} />
{/if}
{#if rollResult && guidanceResult}
<button class="continue" onclick={() => {pressedContinue = true}}>Continue</button>
{/if}
{/if}
<section>
{#if rollResult && guidanceResult && pressedContinue}
<h2>Final Result</h2>
<p>{rollResult + guidanceResult}</p>
{#if rollResult + guidanceResult >= difficultyClass}
<h2 class="success">Success</h2>
<a href="/present">Collect your gift!</a>
{:else}
<h2 class="failure">Failure</h2>
<button class="reset" onclick={resetPage}> Reset</button>
{/if}
{/if}
</section>
</main>
<style>
:global(body) {
margin: 0;
font-family: 'Arial', sans-serif;
background-color: #f4f4f4;
color: #1c1919;
}
main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
background-color: #a8571c;
color: white;
text-align: center;
overflow: hidden;
padding: 20px;
box-sizing: border-box;
}
h1, h2 {
font-size: 2rem;
font-weight: bold;
margin: 20px 0;
}
section {
margin: 15px 0;
width: 100%;
max-width: 400px;
}
p {
font-size: 1.2rem;
}
button {
background-color: #1c1919;
color: white;
padding: 12px 25px;
font-size: 1.1rem;
font-weight: bold;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 15px;
}
button:hover {
background-color: #333;
}
button:disabled {
background-color: #aaa;
cursor: not-allowed;
}
a {
color: #fff;
text-decoration: none;
font-weight: bold;
font-size: 1.2rem;
margin-top: 20px;
}
a:hover {
text-decoration: underline;
}
h2 {
color: #ffdf00;
}
h2.success {
color: #4CAF50;
}
h2.failure {
color: #FF6347;
}
button.reset {
background-color: #ff6347;
padding: 10px 20px;
font-size: 1.1rem;
}
button.reset:hover {
background-color: #e55347;
}
button.continue {
background-color: #4CAF50;
padding: 10px 20px;
font-size: 1.1rem;
}
button.continue:hover {
background-color: #45a049;
}
section > button {
margin-top: 20px;
}</style>

View file

@ -0,0 +1,8 @@
<script lang="ts">
// Show the gift key that was in ENV during build
const steamGiftKey = import.meta.env.VITE_STEAM_GIFT_KEY;
</script>
<h1>Congrats!</h1>
<h2>Here is your prize &lt;3</h2>
<p>{steamGiftKey}</p>

View file

@ -0,0 +1,27 @@
import { json } from '@sveltejs/kit';
/** @type {import('./$types').RequestHandler} */
export function GET({ params }) {
const { skin, dtype, result } = params;
const allVideoFiles = import.meta.glob('/static/videos/**/*');
// parse video files to get the video files for the skin, dtype, and result
let videoFiles = Object.keys(allVideoFiles).map((key) => {
const videoFile = key.replace('/static/videos/', '');
const [videoSkin, videoDtype, videoResult] = videoFile.split('/');
if (videoSkin !== skin || videoDtype !== dtype || videoResult !== result) {
return null;
}
return { videoFile, videoSkin, videoDtype, videoResult };
});
// filter out null values
videoFiles = videoFiles.filter((videoFile) => videoFile !== null);
// get a random one
const videoFile = videoFiles[Math.floor(Math.random() * videoFiles.length)];
// return the url pointing to the video file
return json(videoFile);
}

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

18
svelte.config.js Normal file
View file

@ -0,0 +1,18 @@
import adapter from 'svelte-adapter-bun'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
preprocess: vitePreprocess(),
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;

19
tsconfig.json Normal file
View 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
}

6
vite.config.ts Normal file
View file

@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});