commit facb36a07fbc59bb971404e2d6bebad4f459dbe6 Author: AlacrisDevs Date: Sun Dec 7 14:10:09 2025 +0200 Basic Jeopardy page, working on revamping designs. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cac8fab --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build +/dist + +# OS +.DS_Store +Thumbs.db +Desktop.ini +*.swp +*.swo +*~ + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +# Paraglide / Inlang +src/lib/paraglide +project.inlang/cache + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Cache +.cache +.parcel-cache +.turbo +*.tsbuildinfo + +# IDE +.idea +*.sublime-project +*.sublime-workspace +*.code-workspace + +# Testing +coverage +.nyc_output + +# Misc +*.local +.history diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bc31e15 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "*.css": "tailwindcss" + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..75842c4 --- /dev/null +++ b/README.md @@ -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! + +```sh +# 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: + +```sh +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: + +```sh +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. diff --git a/messages/en.json b/messages/en.json new file mode 100644 index 0000000..0b2727c --- /dev/null +++ b/messages/en.json @@ -0,0 +1,145 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "app_title": "Ultimate Gaming", + "app_version": "V0.01 Student Edition", + "coming_soon": "Coming Soon", + "game_kuldvillak": "Jeopardy", + "game_rooside_soda": "Family Feud", + "kv_title": "JEOPARDY", + "kv_new_game": "New Game", + "kv_load_game": "Load Game", + "kv_settings": "Settings", + "kv_exit": "Exit", + "kv_settings_title": "Settings", + "kv_settings_music": "Music", + "kv_settings_sfx": "Sound Effects", + "kv_settings_language": "Language", + "kv_settings_close": "Close", + "kv_error_404": "404", + "kv_error_not_found": "Not Found", + "kv_error_hint": "(pssst... make sure you're on the right page)", + "kv_edit_back": "Back", + "kv_edit_game_name": "Game name...", + "kv_edit_save": "Save", + "kv_edit_load": "Load", + "kv_edit_reset": "Reset", + "kv_edit_start": "Start", + "kv_edit_settings_teams": "Game Setup", + "kv_edit_rounds": "Rounds", + "kv_play_timer": "Time to Answer", + "kv_play_timer_reveal": "Answer Reveal", + "kv_play_seconds": "seconds", + "kv_edit_final_round_toggle": "Final Round", + "kv_edit_final_question": "Final Question", + "kv_edit_edit": "Edit", + "kv_edit_teams": "Points & Teams", + "kv_edit_points": "Points", + "kv_edit_preset_normal": "Normal", + "kv_edit_preset_double": "Double", + "kv_edit_x_base": "Multiplier", + "kv_edit_custom": "Custom", + "kv_edit_base": "Base", + "kv_edit_negative_scores": "Negative Scores", + "kv_edit_daily_doubles": "Daily Doubles", + "kv_edit_r1": "Jeopardy", + "kv_edit_r2": "Double Jeopardy", + "kv_edit_teams_label": "Players", + "kv_edit_round_1": "Jeopardy", + "kv_edit_round_2": "Double Jeopardy", + "kv_edit_dd_count": "Daily Double", + "kv_edit_category": "Category", + "kv_edit_dd": "★", + "kv_edit_no_category": "(No Category Yet)", + "kv_edit_question": "Question", + "kv_edit_answer": "Answer", + "kv_edit_daily_double": "Daily Double", + "kv_edit_starting_game": "Starting Game...", + "kv_edit_opening_projector": "Opening projector view", + "kv_error_min_players": "At least 2 players required", + "kv_error_no_questions": "Round {round} has no questions filled", + "kv_error_no_final": "Final round question is empty", + "kv_toast_game_saved": "Game saved!", + "kv_toast_game_loaded": "Game loaded!", + "kv_toast_invalid_file": "Invalid game file", + "kv_edit_reset_confirm": "Are you sure you want to reset all fields to default? This will clear all your work.", + "kv_edit_reset_success": "Game reset to defaults", + "kv_edit_categories_questions": "Categories & Questions", + "kv_edit_daily_doubles_count": "Daily Doubles", + "kv_edit_category_placeholder": "Category name", + "kv_edit_empty": "Empty", + "kv_edit_question_edit": "Edit Question", + "kv_edit_question_placeholder": "Enter the question...", + "kv_edit_answer_placeholder": "Enter the answer...", + "kv_edit_final_round": "Final Round", + "kv_edit_final_disabled": "Final Round is disabled in settings.", + "kv_edit_category_name_placeholder": "Enter category name...", + "kv_edit_final_question_placeholder": "Enter the final question...", + "kv_edit_game_settings": "Game Settings", + "kv_edit_num_rounds": "Number of Rounds", + "kv_edit_1_round": "1 Round", + "kv_edit_2_rounds": "2 Rounds", + "kv_edit_point_values": "Point Values", + "kv_edit_standard": "Standard (100-500)", + "kv_edit_double": "Double (200-1000)", + "kv_edit_multiplier": "Multiplier", + "kv_edit_base_value": "Base Value", + "kv_edit_default_timer": "Default Timer (seconds)", + "kv_edit_enable_final": "Enable Final Round", + "kv_edit_allow_negative": "Allow Negative Scores", + "kv_edit_teams_title": "Teams", + "kv_edit_team_name_placeholder": "Team name...", + "kv_edit_add_team": "Add Team", + "kv_edit_remove_team": "Remove", + "kv_play_loading": "Loading game...", + "kv_play_loading_hint": "If this takes too long, the game may not have been started.", + "kv_play_go_to_editor": "Go to Editor", + "kv_play_round": "Round", + "kv_play_phase": "Phase", + "kv_play_last_answer": "Last", + "kv_play_introduce_categories": "Introduce Categories", + "kv_play_skip_to_game": "Skip to Game", + "kv_play_introducing_categories": "Introducing Categories...", + "kv_play_start_game": "Start Game", + "kv_play_daily_double": "Daily Double", + "kv_play_wager": "Wager", + "kv_play_confirm": "Confirm", + "kv_play_question_number": "Question {current}/{total}", + "kv_play_showing_answer": "Showing Answer...", + "kv_play_question_short": "Q", + "kv_play_answer_short": "A", + "kv_play_answering": "Answering", + "kv_play_correct": "Correct", + "kv_play_wrong": "Wrong", + "kv_play_skip": "Skip / No Answer", + "kv_play_start": "Start", + "kv_play_stop": "Stop", + "kv_play_reset": "Reset", + "kv_play_final_round": "Final Round", + "kv_play_reveal_category": "Reveal Category", + "kv_play_reveal_answer": "Reveal Answer", + "kv_play_show_scores": "Show Final Scores", + "kv_play_scores": "Scores", + "kv_play_adjust_by": "Adjust by", + "kv_play_game_controls": "Game Controls", + "kv_play_next_round": "Next Round", + "kv_play_go_to_final": "Go to Final Round", + "kv_play_end_game": "End Game", + "kv_play_end_game_confirm": "End the game?", + "kv_play_open_projector": "Open Projector", + "kv_play_projector_url": "Projector", + "kv_play_game_over": "Game Over", + "kv_edit_values": "Values", + "kv_edit_disabled": "Disabled", + "kv_edit_rules": "Jeopardy Rules", + "kv_edit_how_to": "How to Play?", + "kv_settings_colors": "Colors", + "kv_settings_primary": "Primary", + "kv_settings_secondary": "Secondary", + "kv_settings_text_color": "Text", + "kv_settings_background": "Background", + "kv_settings_reset": "Reset Settings", + "kv_settings_save_exit": "Save and Exit", + "kv_edit_image_link": "Image Link", + "kv_edit_save_exit": "Save and Exit", + "kv_edit_final_enabled": "Final Round Enabled" +} \ No newline at end of file diff --git a/messages/et.json b/messages/et.json new file mode 100644 index 0000000..7fd2116 --- /dev/null +++ b/messages/et.json @@ -0,0 +1,145 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "app_title": "Sassi Mängukoobas", + "app_version": "V0.01 Tudengite Eri", + "coming_soon": "Tulekul", + "game_kuldvillak": "Kuldvillak", + "game_rooside_soda": "Rooside Sõda", + "kv_title": "KULDVILLAK", + "kv_new_game": "Uus mäng", + "kv_load_game": "Lae mäng", + "kv_settings": "Seaded", + "kv_exit": "Välju", + "kv_settings_title": "Seaded", + "kv_settings_music": "Muusika", + "kv_settings_sfx": "Heliefektid", + "kv_settings_language": "Keel", + "kv_settings_close": "Välju", + "kv_error_404": "404", + "kv_error_not_found": "Lehte ei leitud", + "kv_error_hint": "(pssst... vaata, et oled ikka õigel lehel)", + "kv_edit_back": "Tagasi", + "kv_edit_game_name": "Mängu nimi...", + "kv_edit_save": "Salvesta", + "kv_edit_load": "Lae", + "kv_edit_reset": "Lähtesta", + "kv_edit_start": "Alusta", + "kv_edit_settings_teams": "Mängu seadistus", + "kv_edit_rounds": "Voorude arv", + "kv_play_timer": "Vastamisaeg", + "kv_play_timer_reveal": "Vastuse näitamine", + "kv_play_seconds": "sekundit", + "kv_edit_final_round_toggle": "Finaalvoor", + "kv_edit_final_question": "Finaalküsimus", + "kv_edit_edit": "Muuda", + "kv_edit_teams": "Punktid & Tiimid", + "kv_edit_points": "Punktid", + "kv_edit_preset_normal": "Tavaline", + "kv_edit_preset_double": "Duubel", + "kv_edit_x_base": "Kordaja", + "kv_edit_custom": "Kohandatud", + "kv_edit_base": "Baas", + "kv_edit_negative_scores": "Negatiivsed punktid", + "kv_edit_daily_doubles": "Hõbevillak", + "kv_edit_r1": "Villak", + "kv_edit_r2": "Topeltvillak", + "kv_edit_teams_label": "Mängijad", + "kv_edit_round_1": "Villak", + "kv_edit_round_2": "Topeltvillak", + "kv_edit_dd_count": "Hõbevillak", + "kv_edit_category": "Kategooria", + "kv_edit_dd": "★", + "kv_edit_no_category": "(Kategooria puudub)", + "kv_edit_question": "Küsimus", + "kv_edit_answer": "Vastus", + "kv_edit_daily_double": "Hõbevillak", + "kv_edit_starting_game": "Alustan mängu...", + "kv_edit_opening_projector": "Avan projektori vaate", + "kv_error_min_players": "Vaja on vähemalt 2 mängijat", + "kv_error_no_questions": "Voorul {round} pole küsimusi", + "kv_error_no_final": "Finaalvooru küsimus on tühi", + "kv_toast_game_saved": "Mäng salvestatud!", + "kv_toast_game_loaded": "Mäng laetud!", + "kv_toast_invalid_file": "Vigane mängufail", + "kv_edit_reset_confirm": "Kas oled kindel, et soovid kõik väljad vaikeväärtustele lähtestada? See kustutab kogu sinu töö.", + "kv_edit_reset_success": "Mäng lähtestatud", + "kv_edit_categories_questions": "Kategooriad & Küsimused", + "kv_edit_daily_doubles_count": "Hõbevillakud", + "kv_edit_category_placeholder": "Kategooria nimi", + "kv_edit_empty": "Tühi", + "kv_edit_question_edit": "Muuda küsimust", + "kv_edit_question_placeholder": "Sisesta küsimus...", + "kv_edit_answer_placeholder": "Sisesta vastus...", + "kv_edit_final_round": "Finaalvoor", + "kv_edit_final_disabled": "Finaalvoor on seadetes keelatud.", + "kv_edit_category_name_placeholder": "Sisesta kategooria nimi...", + "kv_edit_final_question_placeholder": "Sisesta lõppküsimus...", + "kv_edit_game_settings": "Mängu seaded", + "kv_edit_num_rounds": "Voorude arv", + "kv_edit_1_round": "1 voor", + "kv_edit_2_rounds": "2 vooru", + "kv_edit_point_values": "Punktiväärtused", + "kv_edit_standard": "Tavaline (100-500)", + "kv_edit_double": "Topelt (200-1000)", + "kv_edit_multiplier": "Kordaja", + "kv_edit_base_value": "Baasväärtus", + "kv_edit_default_timer": "Vaikimisi taimer (sekundid)", + "kv_edit_enable_final": "Luba finaalvoor", + "kv_edit_allow_negative": "Luba negatiivsed punktid", + "kv_edit_teams_title": "Tiimid", + "kv_edit_team_name_placeholder": "Tiimi nimi...", + "kv_edit_add_team": "Lisa tiim", + "kv_edit_remove_team": "Eemalda", + "kv_play_loading": "Laen mängu...", + "kv_play_loading_hint": "Kui see võtab liiga kaua, siis mängu pole alustatud.", + "kv_play_go_to_editor": "Mine redaktorisse", + "kv_play_round": "Voor", + "kv_play_phase": "Hetkeseis", + "kv_play_last_answer": "Viimane", + "kv_play_introduce_categories": "Tutvusta kategooriaid", + "kv_play_skip_to_game": "Jäta vahele", + "kv_play_introducing_categories": "Tutvustan kategooriaid...", + "kv_play_start_game": "Alusta mängu", + "kv_play_daily_double": "Hõbevillak", + "kv_play_wager": "Panus", + "kv_play_confirm": "Kinnita", + "kv_play_question_number": "Küsimus {current}/{total}", + "kv_play_showing_answer": "Näitan vastust...", + "kv_play_question_short": "Küsimus", + "kv_play_answer_short": "Vastus", + "kv_play_answering": "Vastab", + "kv_play_correct": "Õige", + "kv_play_wrong": "Vale", + "kv_play_skip": "Jäta vahele", + "kv_play_start": "Alusta", + "kv_play_stop": "Lõpeta", + "kv_play_reset": "Lähtesta", + "kv_play_final_round": "Finaalvoor", + "kv_play_reveal_category": "Näita kategooriat", + "kv_play_reveal_answer": "Näita vastust", + "kv_play_show_scores": "Näita lõpptulemusi", + "kv_play_scores": "Tulemused", + "kv_play_adjust_by": "Muuda", + "kv_play_game_controls": "Mängu juhtimine", + "kv_play_next_round": "Järgmine voor", + "kv_play_go_to_final": "Finaalvooru", + "kv_play_end_game": "Lõpeta mäng", + "kv_play_end_game_confirm": "Lõpeta mäng?", + "kv_play_open_projector": "Ava projektor", + "kv_play_projector_url": "Projektor", + "kv_play_game_over": "Mäng läbi", + "kv_edit_values": "Väärtused", + "kv_edit_disabled": "Keelatud", + "kv_edit_rules": "Kuldvillaku reeglid", + "kv_edit_how_to": "Kuidas mängida?", + "kv_settings_colors": "Värvid", + "kv_settings_primary": "Primaarne", + "kv_settings_secondary": "Sekundaarne", + "kv_settings_text_color": "Tekst", + "kv_settings_background": "Taust", + "kv_settings_reset": "Lähtesta seaded", + "kv_settings_save_exit": "Salvesta ja välju", + "kv_edit_image_link": "Pildi link", + "kv_edit_save_exit": "Salvesta ja Välju", + "kv_edit_final_enabled": "Finaalvoor lubatud" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d1f0135 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2438 @@ +{ + "name": "myapp", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "myapp", + "version": "0.0.1", + "devDependencies": { + "@inlang/paraglide-js": "^2.5.0", + "@sveltejs/adapter-auto": "^7.0.0", + "@sveltejs/kit": "^2.48.5", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@tailwindcss/vite": "^4.1.17", + "svelte": "^5.43.8", + "svelte-check": "^4.3.4", + "tailwindcss": "^4.1.17", + "typescript": "^5.9.3", + "vite": "^7.2.2" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@inlang/paraglide-js": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inlang/paraglide-js/-/paraglide-js-2.5.0.tgz", + "integrity": "sha512-FnycOM6j0GYd/n97NCDyXJiHnyPYGPgufL640eZWs+rTIRrOgDVz/o77iWRYFZK84REOcmSDi0N6PbbY8NT8+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inlang/recommend-sherlock": "0.2.1", + "@inlang/sdk": "2.4.9", + "commander": "11.1.0", + "consola": "3.4.0", + "json5": "2.2.3", + "unplugin": "^2.1.2", + "urlpattern-polyfill": "^10.0.0" + }, + "bin": { + "paraglide-js": "bin/run.js" + } + }, + "node_modules/@inlang/recommend-sherlock": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@inlang/recommend-sherlock/-/recommend-sherlock-0.2.1.tgz", + "integrity": "sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "comment-json": "^4.2.3" + } + }, + "node_modules/@inlang/sdk": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@inlang/sdk/-/sdk-2.4.9.tgz", + "integrity": "sha512-cvz/C1rF5WBxzHbEoiBoI6Sz6q6M+TdxfWkEGBYTD77opY8i8WN01prUWXEM87GPF4SZcyIySez9U0Ccm12oFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lix-js/sdk": "0.4.7", + "@sinclair/typebox": "^0.31.17", + "kysely": "^0.27.4", + "sqlite-wasm-kysely": "0.3.0", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lix-js/sdk": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@lix-js/sdk/-/sdk-0.4.7.tgz", + "integrity": "sha512-pRbW+joG12L0ULfMiWYosIW0plmW4AsUdiPCp+Z8rAsElJ+wJ6in58zhD3UwUcd4BNcpldEGjg6PdA7e0RgsDQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@lix-js/server-protocol-schema": "0.1.1", + "dedent": "1.5.1", + "human-id": "^4.1.1", + "js-sha256": "^0.11.0", + "kysely": "^0.27.4", + "sqlite-wasm-kysely": "0.3.0", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@lix-js/server-protocol-schema": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@lix-js/server-protocol-schema/-/server-protocol-schema-0.1.1.tgz", + "integrity": "sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.31.28", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.28.tgz", + "integrity": "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sqlite.org/sqlite-wasm": { + "version": "3.48.0-build4", + "resolved": "https://registry.npmjs.org/@sqlite.org/sqlite-wasm/-/sqlite-wasm-3.48.0-build4.tgz", + "integrity": "sha512-hI6twvUkzOmyGZhQMza1gpfqErZxXRw6JEsiVjUbo7tFanVD+8Oil0Ih3l2nGzHdxPI41zFmfUQG7GHqhciKZQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "sqlite-wasm": "bin/index.js" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.7.tgz", + "integrity": "sha512-znp1A/Y1Jj4l/Zy7PX5DZKBE0ZNY+5QBngiE21NJkfSTyzzC5iKNWOtwFXKtIrn7MXEFBck4jD95iBNkGjK92Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-auto": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-7.0.0.tgz", + "integrity": "sha512-ImDWaErTOCkRS4Gt+5gZuymKFBobnhChXUZ9lhUZLahUgvA4OOvRzi3sahzYgbxGj5nkA6OV0GAW378+dl/gyw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.49.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.49.0.tgz", + "integrity": "sha512-oH8tXw7EZnie8FdOWYrF7Yn4IKrqTFHhXvl8YxXxbKwTMcD/5NNCryUSEXRk2ZR4ojnub0P8rNrsVGHXWqIDtA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", + "devalue": "^5.3.2", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "sade": "^1.8.1", + "set-cookie-parser": "^2.6.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", + "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", + "debug": "^4.4.1", + "deepmerge": "^4.3.1", + "magic-string": "^0.30.17", + "vitefu": "^1.1.1" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.1.tgz", + "integrity": "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.1" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.0.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.6.0", + "@emnapi/runtime": "^1.6.0", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.0.7", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.17.tgz", + "integrity": "sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.17", + "@tailwindcss/oxide": "4.1.17", + "tailwindcss": "4.1.17" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/comment-json": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", + "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/consola": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.0.tgz", + "integrity": "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.5.0.tgz", + "integrity": "sha512-69sM5yrHfFLJt0AZ9QqZXGCPfJ7fQjvpln3Rq5+PS03LD32Ost1Q9N+eEnaQwGRIriKkMImXD56ocjQmfjbV3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrap": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.0.tgz", + "integrity": "sha512-WBmtxe7R9C5mvL4n2le8nMUe4mD5V9oiK2vJpQ9I3y20ENPUomPcphBXE8D1x/Bm84oN1V+lOfgXxtqmxTp3Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/human-id": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", + "integrity": "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==", + "dev": true, + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-sha256": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.1.tgz", + "integrity": "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/kysely": { + "version": "0.27.6", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.6.tgz", + "integrity": "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sqlite-wasm-kysely": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/sqlite-wasm-kysely/-/sqlite-wasm-kysely-0.3.0.tgz", + "integrity": "sha512-TzjBNv7KwRw6E3pdKdlRyZiTmUIE0UttT/Sl56MVwVARl/u5gp978KepazCJZewFUnlWHz9i3NQd4kOtP/Afdg==", + "dev": true, + "dependencies": { + "@sqlite.org/sqlite-wasm": "^3.48.0-build2" + }, + "peerDependencies": { + "kysely": "*" + } + }, + "node_modules/svelte": { + "version": "5.45.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.45.2.tgz", + "integrity": "sha512-yyXdW2u3H0H/zxxWoGwJoQlRgaSJLp+Vhktv12iRw2WRDlKqUPT54Fi0K/PkXqrdkcQ98aBazpy0AH4BCBVfoA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.5.0", + "esm-env": "^1.2.1", + "esrap": "^2.2.0", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.4.tgz", + "integrity": "sha512-DVWvxhBrDsd+0hHWKfjP99lsSXASeOhHJYyuKOFYJcP7ThfSCKgjVarE8XfuMWpS5JV3AlDf+iK1YGGo2TACdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", + "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/urlpattern-polyfill": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz", + "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vite": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", + "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", + "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..829353f --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "myapp", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@inlang/paraglide-js": "^2.5.0", + "@sveltejs/adapter-auto": "^7.0.0", + "@sveltejs/kit": "^2.48.5", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@tailwindcss/vite": "^4.1.17", + "svelte": "^5.43.8", + "svelte-check": "^4.3.4", + "tailwindcss": "^4.1.17", + "typescript": "^5.9.3", + "vite": "^7.2.2" + } +} diff --git a/project.inlang/project_id b/project.inlang/project_id new file mode 100644 index 0000000..2d9f988 --- /dev/null +++ b/project.inlang/project_id @@ -0,0 +1 @@ +0vGuuiWc0Pt5x0peOL \ No newline at end of file diff --git a/project.inlang/settings.json b/project.inlang/settings.json new file mode 100644 index 0000000..f52f061 --- /dev/null +++ b/project.inlang/settings.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js" + ], + "plugin.inlang.messageFormat": { + "pathPattern": "./messages/{locale}.json" + }, + "baseLocale": "et", + "locales": [ + "et", + "en" + ] +} diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..da08e6d --- /dev/null +++ b/src/app.d.ts @@ -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 {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..50bd0b5 --- /dev/null +++ b/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..5182210 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,12 @@ +import type { Handle } from '@sveltejs/kit'; +import { paraglideMiddleware } from '$lib/paraglide/server'; + +const handleParaglide: Handle = ({ event, resolve }) => paraglideMiddleware(event.request, ({ request, locale }) => { + event.request = request; + + return resolve(event, { + transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale) + }); +}); + +export const handle: Handle = handleParaglide; diff --git a/src/hooks.ts b/src/hooks.ts new file mode 100644 index 0000000..e75600b --- /dev/null +++ b/src/hooks.ts @@ -0,0 +1,3 @@ +import { deLocalizeUrl } from '$lib/paraglide/runtime'; + +export const reroute = (request) => deLocalizeUrl(request.url).pathname; diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg new file mode 100644 index 0000000..8bc978c --- /dev/null +++ b/src/lib/assets/favicon.svg @@ -0,0 +1,46 @@ + + + + diff --git a/src/lib/components/LanguageSwitcher.svelte b/src/lib/components/LanguageSwitcher.svelte new file mode 100644 index 0000000..621aab2 --- /dev/null +++ b/src/lib/components/LanguageSwitcher.svelte @@ -0,0 +1,38 @@ + + + diff --git a/src/lib/components/Settings.svelte b/src/lib/components/Settings.svelte new file mode 100644 index 0000000..aaaed37 --- /dev/null +++ b/src/lib/components/Settings.svelte @@ -0,0 +1,240 @@ + + + + +{#if open} + +
e.key === "Enter" && handleCancel()} + >
+ + + +{/if} diff --git a/src/lib/components/Slider.svelte b/src/lib/components/Slider.svelte new file mode 100644 index 0000000..bfa9650 --- /dev/null +++ b/src/lib/components/Slider.svelte @@ -0,0 +1,94 @@ + + +
+ + {label} + + +
+ + {value}% + + +
+ +
+
+
+ + diff --git a/src/lib/components/Toast.svelte b/src/lib/components/Toast.svelte new file mode 100644 index 0000000..90c8a10 --- /dev/null +++ b/src/lib/components/Toast.svelte @@ -0,0 +1,40 @@ + + +{#if visible} + +{/if} diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts new file mode 100644 index 0000000..c36b6a5 --- /dev/null +++ b/src/lib/components/index.ts @@ -0,0 +1,8 @@ +// Shared Components +export { default as Slider } from './Slider.svelte'; +export { default as Settings } from './Settings.svelte'; +export { default as LanguageSwitcher } from './LanguageSwitcher.svelte'; +export { default as Toast } from './Toast.svelte'; + +// Kuldvillak Components +export * from './kuldvillak'; diff --git a/src/lib/components/kuldvillak/index.ts b/src/lib/components/kuldvillak/index.ts new file mode 100644 index 0000000..9c9e05e --- /dev/null +++ b/src/lib/components/kuldvillak/index.ts @@ -0,0 +1,2 @@ +// Kuldvillak UI Components +export * from './ui'; diff --git a/src/lib/components/kuldvillak/ui/KvButtonPrimary.svelte b/src/lib/components/kuldvillak/ui/KvButtonPrimary.svelte new file mode 100644 index 0000000..d235b81 --- /dev/null +++ b/src/lib/components/kuldvillak/ui/KvButtonPrimary.svelte @@ -0,0 +1,32 @@ + + +{#if href && !disabled} + + {@render children()} + +{:else} + +{/if} diff --git a/src/lib/components/kuldvillak/ui/KvButtonSecondary.svelte b/src/lib/components/kuldvillak/ui/KvButtonSecondary.svelte new file mode 100644 index 0000000..f7a28dd --- /dev/null +++ b/src/lib/components/kuldvillak/ui/KvButtonSecondary.svelte @@ -0,0 +1,32 @@ + + +{#if href && !disabled} + + {@render children()} + +{:else} + +{/if} diff --git a/src/lib/components/kuldvillak/ui/KvGameLogo.svelte b/src/lib/components/kuldvillak/ui/KvGameLogo.svelte new file mode 100644 index 0000000..5583bee --- /dev/null +++ b/src/lib/components/kuldvillak/ui/KvGameLogo.svelte @@ -0,0 +1,133 @@ + + +{#if variant === "kuldvillak"} + + + + + + + + + + + + + +{:else} + + + + + + + + + +{/if} diff --git a/src/lib/components/kuldvillak/ui/KvNumberInput.svelte b/src/lib/components/kuldvillak/ui/KvNumberInput.svelte new file mode 100644 index 0000000..0f854e8 --- /dev/null +++ b/src/lib/components/kuldvillak/ui/KvNumberInput.svelte @@ -0,0 +1,44 @@ + + +
+ +
diff --git a/src/lib/components/kuldvillak/ui/KvPlayerCard.svelte b/src/lib/components/kuldvillak/ui/KvPlayerCard.svelte new file mode 100644 index 0000000..ed1ec51 --- /dev/null +++ b/src/lib/components/kuldvillak/ui/KvPlayerCard.svelte @@ -0,0 +1,70 @@ + + +
e.key === "Enter" && onclick?.()} +> + + + + + {#if onadjust} +
+ + +
+ {/if} +
diff --git a/src/lib/components/kuldvillak/ui/KvProjectorCard.svelte b/src/lib/components/kuldvillak/ui/KvProjectorCard.svelte new file mode 100644 index 0000000..79ed3cf --- /dev/null +++ b/src/lib/components/kuldvillak/ui/KvProjectorCard.svelte @@ -0,0 +1,67 @@ + + +{#if variant === "category"} + +
+ + {text || "Kategooria"} + +
+{:else if variant === "price"} + +
+ {#if isRevealed} + {text} + {:else} + + {text} + + {/if} +
+{:else if variant === "player"} + +
+ + {text} + + + {score}€ + +
+{/if} diff --git a/src/lib/components/kuldvillak/ui/index.ts b/src/lib/components/kuldvillak/ui/index.ts new file mode 100644 index 0000000..1a90a4d --- /dev/null +++ b/src/lib/components/kuldvillak/ui/index.ts @@ -0,0 +1,16 @@ +// Kuldvillak UI Components + +// Buttons +export { default as KvButtonPrimary } from './KvButtonPrimary.svelte'; +export { default as KvButtonSecondary } from './KvButtonSecondary.svelte'; + +// Form Controls +export { default as KvNumberInput } from './KvNumberInput.svelte'; + +// Cards +export { default as KvProjectorCard } from './KvProjectorCard.svelte'; +export { default as KvPlayerCard } from './KvPlayerCard.svelte'; + +// Branding +export { default as KvLogo } from './KvGameLogo.svelte'; +export { default as KvGameLogo } from './KvGameLogo.svelte'; diff --git a/src/lib/example_testing_game.json b/src/lib/example_testing_game.json new file mode 100644 index 0000000..f09e194 --- /dev/null +++ b/src/lib/example_testing_game.json @@ -0,0 +1,328 @@ +{ + "name": "9. Klassi Viktoriini", + "settings": { + "numberOfRounds": 1, + "pointValuePreset": "round1", + "pointValues": [ + 10, + 20, + 30, + 40, + 50 + ], + "basePointValue": 10, + "categoriesPerRound": 6, + "questionsPerCategory": 5, + "dailyDoublesPerRound": [ + 1 + ], + "enableFinalRound": true, + "enableSoundEffects": true, + "allowNegativeScores": true, + "maxTeams": 6, + "defaultTimerSeconds": 15, + "answerRevealSeconds": 5 + }, + "teams": [ + { + "id": "r5f48jjat", + "name": "Tiim 1", + "score": 0 + }, + { + "id": "lnbeg51uo", + "name": "Tiim 2", + "score": 0 + } + ], + "rounds": [ + { + "id": "28mrdbeas", + "name": "Villak", + "categories": [ + { + "id": "lsc3pphby", + "name": "EESTI AJALUGU", + "questions": [ + { + "id": "a11mxf6ra", + "question": "Mis aastal kuulutati välja Eesti Vabariik?", + "answer": "1918", + "points": 10, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "j7ztvxf7l", + "question": "Kes oli Eesti Vabariigi esimene riigivanem?", + "answer": "Konstantin Päts", + "points": 20, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "cwcx4jf7m", + "question": "Mis aastal toimus Laulev revolutsioon?", + "answer": "1988", + "points": 30, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "7m6e1qdwx", + "question": "Mis oli Balti keti kuupäev 1989. aastal?", + "answer": "23. august", + "points": 40, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "mmudm4kjd", + "question": "Mis aastal toimus Jüriöö ülestõus?", + "answer": "1343", + "points": 50, + "isDailyDouble": false, + "isRevealed": false + } + ] + }, + { + "id": "sfkprzqyz", + "name": "MATEMAATIKA", + "questions": [ + { + "id": "zfg5pe8pl", + "question": "Mis on ruutjuur 144-st?", + "answer": "12", + "points": 10, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "ds2bmll5z", + "question": "Mis on Pythagorase teoreemi valem?", + "answer": "a² + b² = c²", + "points": 20, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "6jkk4qkwo", + "question": "Mis on arvu pi (π) väärtus kahe komakohani?", + "answer": "3,14", + "points": 30, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "h32bk4gll", + "question": "Kui kolmnurga alus on 8 cm ja kõrgus 6 cm, mis on pindala?", + "answer": "24 cm²", + "points": 40, + "isDailyDouble": true, + "isRevealed": false + }, + { + "id": "a2vjrjbuo", + "question": "Lahenda võrrand: 3x + 7 = 22", + "answer": "x = 5", + "points": 50, + "isDailyDouble": false, + "isRevealed": false + } + ] + }, + { + "id": "resy4vkyc", + "name": "LOODUS", + "questions": [ + { + "id": "a0tz6k2a5", + "question": "Mis on vee keemik valem?", + "answer": "H₂O", + "points": 10, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "1kjss32t3", + "question": "Mitu planeeti on meie päikesesüsteemis?", + "answer": "8", + "points": 20, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "4acp3zwgh", + "question": "Mis on fotosünteesi põhiprodukt?", + "answer": "Glükoos (suhkur) ja hapnik", + "points": 30, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "fh1jn2z6f", + "question": "Mis element on perioodilisustabelis tähisega Fe?", + "answer": "Raud", + "points": 40, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "wd96bi1d5", + "question": "Mis on DNA täisnimi?", + "answer": "Desoksüribonukleiinhape", + "points": 50, + "isDailyDouble": false, + "isRevealed": false + } + ] + }, + { + "id": "iy06ybvmy", + "name": "KIRJANDUS", + "questions": [ + { + "id": "he7in5nor", + "question": "Kes kirjutas eepose 'Kalevipoeg'?", + "answer": "Friedrich Reinhold Kreutzwald", + "points": 10, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "pafvsp6t9", + "question": "Mis on A. H. Tammsaare tuntuim romaan?", + "answer": "Tõde ja õigus", + "points": 20, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "rjjp0h8s4", + "question": "Kes kirjutas luuletuse 'Mu isamaa on minu arm'?", + "answer": "Lydia Koidula", + "points": 30, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "n7o3vkkcn", + "question": "Mis on soneti traditsiooniline värsside arv?", + "answer": "14 rida", + "points": 40, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "4hxuoa1fy", + "question": "Kes kirjutas romaani 'Kevade'?", + "answer": "Oskar Luts", + "points": 50, + "isDailyDouble": false, + "isRevealed": false + } + ] + }, + { + "id": "g9r2whn2u", + "name": "GEOGRAAFIA", + "questions": [ + { + "id": "4qkzwwe7r", + "question": "Mis on Eesti pealinn?", + "answer": "Tallinn", + "points": 10, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "7z3113es0", + "question": "Mis on Eesti kõrgeim mägi?", + "answer": "Suur Munamägi", + "points": 20, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "so7tqkk16", + "question": "Mis on maailma suurim ookean?", + "answer": "Vaikne ookean", + "points": 30, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "h3gyk1yi3", + "question": "Mis riik on pindalalt maailma suurim?", + "answer": "Venemaa", + "points": 40, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "385aprc7p", + "question": "Mis on Eesti suurim saar?", + "answer": "Saaremaa", + "points": 50, + "isDailyDouble": false, + "isRevealed": false + } + ] + }, + { + "id": "4y809i7nr", + "name": "ÜLDTEADMISED", + "questions": [ + { + "id": "jzcjmb4ef", + "question": "Mis värvid on Eesti lipul?", + "answer": "Sinine, must, valge", + "points": 10, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "n86mk1kg0", + "question": "Mis on Eesti rahvuslind?", + "answer": "Suitsupääsuke", + "points": 20, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "04e5zactm", + "question": "Mis aastal liitus Eesti Euroopa Liiduga?", + "answer": "2004", + "points": 30, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "xwhxh99j6", + "question": "Mis on Eesti rahvuslill?", + "answer": "Rukkilill", + "points": 40, + "isDailyDouble": false, + "isRevealed": false + }, + { + "id": "n54fmwmg8", + "question": "Mis aastal võttis Eesti kasutusele euro?", + "answer": "2011", + "points": 50, + "isDailyDouble": false, + "isRevealed": false + } + ] + } + ], + "pointMultiplier": 1 + } + ], + "finalRound": { + "category": "EESTI KULTUUR", + "question": "Mis aastal võitis Eesti esimest korda Eurovisiooni lauluvõistluse ja mis laul see oli?", + "answer": "2001. aastal lauluga 'Everybody' (Tanel Padar, Dave Benton ja 2XL)" + } +} \ No newline at end of file diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..0fb289a --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1,15 @@ +// ============================================ +// Ultimate Gaming - Library Exports +// ============================================ + +// Kuldvillak (Jeopardy) Types +export * from './types/kuldvillak'; + +// Kuldvillak Store +export { kuldvillakStore } from './stores/kuldvillak.svelte'; + +// Persistence (Save/Load) +export * from './stores/persistence'; + +// UI Components +export * from './components'; diff --git a/src/lib/stores/audio.svelte.ts b/src/lib/stores/audio.svelte.ts new file mode 100644 index 0000000..dae7f30 --- /dev/null +++ b/src/lib/stores/audio.svelte.ts @@ -0,0 +1,72 @@ +import { browser } from '$app/environment'; + +class AudioStore { + private audio: HTMLAudioElement | null = null; + private initialized = false; + + musicVolume = $state(50); + sfxVolume = $state(100); + + constructor() { + if (browser) { + // Load saved volumes + const savedMusic = localStorage.getItem('kv_music_volume'); + const savedSfx = localStorage.getItem('kv_sfx_volume'); + if (savedMusic) this.musicVolume = parseInt(savedMusic); + if (savedSfx) this.sfxVolume = parseInt(savedSfx); + } + } + + initMusic(src: string) { + if (!browser || this.initialized) return; + + this.audio = new Audio(src); + this.audio.loop = true; + this.audio.volume = this.musicVolume / 100; + this.initialized = true; + + // Try to play + this.audio.play().catch(() => { + const playOnInteraction = () => { + this.audio?.play(); + document.removeEventListener('click', playOnInteraction); + }; + document.addEventListener('click', playOnInteraction); + }); + } + + setMusicVolume(value: number) { + this.musicVolume = value; + if (this.audio) { + this.audio.volume = value / 100; + } + if (browser) { + localStorage.setItem('kv_music_volume', String(value)); + } + } + + setSfxVolume(value: number) { + this.sfxVolume = value; + if (browser) { + localStorage.setItem('kv_sfx_volume', String(value)); + } + } + + stopMusic() { + if (this.audio) { + this.audio.pause(); + this.audio.currentTime = 0; + } + } + + destroy() { + if (this.audio) { + this.audio.pause(); + this.audio.src = ''; + this.audio = null; + this.initialized = false; + } + } +} + +export const audioStore = new AudioStore(); diff --git a/src/lib/stores/gameSession.svelte.ts b/src/lib/stores/gameSession.svelte.ts new file mode 100644 index 0000000..7070365 --- /dev/null +++ b/src/lib/stores/gameSession.svelte.ts @@ -0,0 +1,623 @@ +import { browser } from "$app/environment"; +import type { Team, Round, FinalRound, GameSettings, GamePhase, QuestionResult } from "$lib/types/kuldvillak"; + +// Game session state that syncs across tabs +export interface GameSessionState { + // Game data + name: string; + settings: GameSettings; + teams: Team[]; + rounds: Round[]; + finalRound: FinalRound | null; + + // Current game state + phase: GamePhase; + currentRoundIndex: number; + activeTeamId: string | null; + + // Intro animation state + introCategoryIndex: number; // Which category is being shown during intro + categoriesIntroduced: boolean; // Have all categories been introduced for this round + boardRevealed: boolean; // Has the board been revealed (prices faded in) for this round + + // Question state + currentQuestion: { + roundIndex: number; + categoryIndex: number; + questionIndex: number; + } | null; + showAnswer: boolean; + wrongTeamIds: string[]; // Teams that answered wrong for current question + lastAnsweredTeamId: string | null; // Track who answered last + lastAnswerCorrect: boolean | null; // Was it correct or wrong + + // Daily Double + dailyDoubleWager: number | null; + + // Final Round + finalCategoryRevealed: boolean; // Has the final category been revealed + finalWagers: Record; + finalAnswers: Record; + finalRevealed: string[]; // Team IDs that have been revealed + + // Timer + timerRunning: boolean; + timerSeconds: number; + timerMax: number; + + // Question tracking + questionsAnswered: number; // How many questions have been answered + currentQuestionNumber: number; // Which question number is this (1-30) + questionResults: QuestionResult[]; // Results of answered questions +} + +const CHANNEL_NAME = "kuldvillak-game-session"; +const STORAGE_KEY = "kuldvillak-game-session"; + +class GameSessionStore { + private channel: BroadcastChannel | null = null; + private timerInterval: ReturnType | null = null; + private isTimerOwner = false; // Only one tab should own the timer + state = $state(null); + + constructor() { + if (browser) { + // Setup broadcast channel for cross-tab sync + this.channel = new BroadcastChannel(CHANNEL_NAME); + this.channel.onmessage = (event) => { + if (event.data.type === "STATE_UPDATE") { + this.state = event.data.state; + } else if (event.data.type === "REQUEST_STATE") { + // Another tab is requesting the current state + if (this.state) { + this.broadcast("STATE_UPDATE", this.state); + } + } else if (event.data.type === "TIMER_OWNER_CHECK") { + // Another tab is checking who owns the timer + if (this.isTimerOwner) { + this.channel?.postMessage({ type: "TIMER_OWNER_EXISTS" }); + } + } else if (event.data.type === "TIMER_OWNER_EXISTS") { + // Another tab owns the timer, don't start ours + this.isTimerOwner = false; + } + }; + + // Try to load from localStorage + const saved = localStorage.getItem(STORAGE_KEY); + if (saved) { + try { + this.state = JSON.parse(saved); + // Timer will be started by moderator view via enableTimerControl() + } catch { + // Invalid data + } + } + + // Request state from other tabs + this.channel.postMessage({ type: "REQUEST_STATE" }); + } + } + + private broadcast(type: string, state: GameSessionState) { + if (this.channel) { + // Use $state.snapshot to get plain object from Proxy + const plainState = $state.snapshot(state); + this.channel.postMessage({ type, state: plainState }); + } + } + + private persist() { + if (browser && this.state) { + // Use $state.snapshot to get plain object from Proxy + const plainState = $state.snapshot(this.state); + localStorage.setItem(STORAGE_KEY, JSON.stringify(plainState)); + this.broadcast("STATE_UPDATE", this.state); + } + } + + // Initialize a new game session + startGame(data: { + name: string; + settings: GameSettings; + teams: Team[]; + rounds: Round[]; + finalRound: FinalRound | null; + }) { + // Deep clone the data to remove any Proxy objects + const plainData = JSON.parse(JSON.stringify(data)); + + this.state = { + ...plainData, + phase: "intro" as const, + currentRoundIndex: 0, + activeTeamId: null, + introCategoryIndex: -1, + categoriesIntroduced: false, + boardRevealed: false, + currentQuestion: null, + showAnswer: false, + wrongTeamIds: [], + lastAnsweredTeamId: null, + lastAnswerCorrect: null, + dailyDoubleWager: null, + finalCategoryRevealed: false, + finalWagers: {}, + finalAnswers: {}, + finalRevealed: [], + timerRunning: false, + timerSeconds: 0, + timerMax: plainData.settings.defaultTimerSeconds ?? 10, + questionsAnswered: 0, + currentQuestionNumber: 0, + questionResults: [], + }; + // Timer will be started by moderator view via enableTimerControl() + this.persist(); + } + + // ============================================ + // Intro Phase Management + // ============================================ + + startCategoryIntro() { + if (!this.state) return; + this.state.phase = "intro-categories"; + this.state.introCategoryIndex = 0; + this.persist(); + } + + nextIntroCategory() { + if (!this.state) return; + const currentRound = this.state.rounds[this.state.currentRoundIndex]; + if (!currentRound) return; + + this.state.introCategoryIndex++; + + // Check if we've shown all categories - stay on villak screen + if (this.state.introCategoryIndex >= currentRound.categories.length) { + this.state.phase = "intro"; + this.state.introCategoryIndex = -1; + this.state.categoriesIntroduced = true; // Mark categories as introduced + } + this.persist(); + } + + startBoard() { + if (!this.state) return; + this.state.phase = "board"; + this.persist(); + } + + markBoardRevealed() { + if (!this.state) return; + this.state.boardRevealed = true; + this.persist(); + } + + // End the game session + endGame() { + this.stopInternalTimer(); + this.state = null; + if (browser) { + localStorage.removeItem(STORAGE_KEY); + this.broadcast("STATE_UPDATE", null as any); + } + } + + // ============================================ + // Question Management + // ============================================ + + selectQuestion(roundIndex: number, categoryIndex: number, questionIndex: number) { + if (!this.state) return; + + const question = this.state.rounds[roundIndex]?.categories[categoryIndex]?.questions[questionIndex]; + if (!question || question.isRevealed) return; + + this.state.currentQuestion = { roundIndex, categoryIndex, questionIndex }; + this.state.wrongTeamIds = []; + this.state.activeTeamId = null; + this.state.currentQuestionNumber = this.state.questionsAnswered + 1; + + if (question.isDailyDouble) { + this.state.phase = "daily-double"; + this.state.dailyDoubleWager = null; + } else { + this.state.phase = "question"; + } + + this.state.showAnswer = false; + + // Reset timer + this.state.timerSeconds = this.state.timerMax; + this.state.timerRunning = false; + + this.persist(); + } + + setDailyDoubleWager(teamId: string, wager: number) { + if (!this.state) return; + this.state.activeTeamId = teamId; + this.state.dailyDoubleWager = wager; + this.state.phase = "question"; + this.persist(); + } + + toggleAnswer() { + if (!this.state) return; + this.state.showAnswer = !this.state.showAnswer; + this.persist(); + } + + revealAnswer() { + if (!this.state) return; + this.state.showAnswer = true; + this.persist(); + } + + // Mark answer correct - awards points, shows answer, closes after delay + markCorrect(teamId: string) { + if (!this.state || !this.state.currentQuestion) return; + + const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion; + const question = this.state.rounds[roundIndex].categories[categoryIndex].questions[questionIndex]; + + // Award points + const team = this.state.teams.find(t => t.id === teamId); + if (team) { + const points = this.state.dailyDoubleWager ?? question.points; + team.score += points; + } + + // Track last answer + this.state.lastAnsweredTeamId = teamId; + this.state.lastAnswerCorrect = true; + + // Show answer and close after configured delay + this.state.showAnswer = true; + this.persist(); + + const revealMs = (this.state.settings.answerRevealSeconds ?? 5) * 1000; + setTimeout(() => this.finalizeQuestion(), revealMs); + } + + // Mark answer wrong - deducts points, adds to wrong list + markWrong(teamId: string) { + if (!this.state || !this.state.currentQuestion) return; + + const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion; + const question = this.state.rounds[roundIndex].categories[categoryIndex].questions[questionIndex]; + + // Deduct points if allowed + const team = this.state.teams.find(t => t.id === teamId); + if (team && this.state.settings.allowNegativeScores) { + const points = this.state.dailyDoubleWager ?? question.points; + team.score -= points; + } + + // Track last answer + this.state.lastAnsweredTeamId = teamId; + this.state.lastAnswerCorrect = false; + + // Add to wrong list + if (!this.state.wrongTeamIds.includes(teamId)) { + this.state.wrongTeamIds.push(teamId); + } + + // Clear active team for next selection + this.state.activeTeamId = null; + + // Check if all teams have answered wrong + const allTeamsWrong = this.state.teams.every(t => this.state!.wrongTeamIds.includes(t.id)); + if (allTeamsWrong) { + // Everyone wrong - show answer and close + this.state.showAnswer = true; + this.persist(); + const revealMs = (this.state.settings.answerRevealSeconds ?? 5) * 1000; + setTimeout(() => this.finalizeQuestion(), revealMs); + } else { + this.persist(); + } + } + + // Skip question - shows answer, closes after delay + skipQuestion() { + if (!this.state || !this.state.currentQuestion) return; + + this.state.showAnswer = true; + this.persist(); + const revealMs = (this.state.settings.answerRevealSeconds ?? 5) * 1000; + setTimeout(() => this.finalizeQuestion(), revealMs); + } + + // Actually close the question and return to board + private finalizeQuestion() { + if (!this.state || !this.state.currentQuestion) return; + + const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion; + const question = this.state.rounds[roundIndex].categories[categoryIndex].questions[questionIndex]; + + // Save question result before resetting state + const points = this.state.dailyDoubleWager ?? question.points; + let pointsChange = 0; + if (this.state.lastAnswerCorrect === true) { + pointsChange = points; + } else if (this.state.lastAnswerCorrect === false) { + pointsChange = -points; + } + + this.state.questionResults.push({ + categoryIndex, + questionIndex, + points: question.points, + teamId: this.state.lastAnsweredTeamId, + pointsChange, + isDailyDouble: question.isDailyDouble, + wager: this.state.dailyDoubleWager ?? undefined, + }); + + // Mark as revealed + question.isRevealed = true; + + // Increment questions answered counter + this.state.questionsAnswered++; + + // Reset state + this.state.currentQuestion = null; + this.state.showAnswer = false; + this.state.wrongTeamIds = []; + this.state.dailyDoubleWager = null; + this.state.activeTeamId = null; + this.state.phase = "board"; + + // Check if round is complete + this.checkRoundComplete(); + this.persist(); + } + + // Legacy method for compatibility + closeQuestion(correct: boolean | null, teamId?: string | null) { + if (correct === true && teamId) { + this.markCorrect(teamId); + } else if (correct === false && teamId) { + this.markWrong(teamId); + } else { + this.skipQuestion(); + } + } + + private checkRoundComplete() { + if (!this.state) return; + + const currentRound = this.state.rounds[this.state.currentRoundIndex]; + const allRevealed = currentRound.categories.every(cat => + cat.questions.every(q => q.isRevealed) + ); + + if (allRevealed) { + // Move to next round or final + if (this.state.currentRoundIndex < this.state.rounds.length - 1) { + this.state.currentRoundIndex++; + } else if (this.state.settings.enableFinalRound && this.state.finalRound) { + this.state.phase = "final-category"; + } else { + this.state.phase = "finished"; + } + } + } + + // ============================================ + // Team & Score Management + // ============================================ + + setActiveTeam(teamId: string | null) { + if (!this.state) return; + this.state.activeTeamId = teamId; + this.persist(); + } + + adjustScore(teamId: string, delta: number) { + if (!this.state) return; + const team = this.state.teams.find(t => t.id === teamId); + if (team) { + team.score += delta; + this.persist(); + } + } + + setScore(teamId: string, score: number) { + if (!this.state) return; + const team = this.state.teams.find(t => t.id === teamId); + if (team) { + team.score = score; + this.persist(); + } + } + + // ============================================ + // Round Management + // ============================================ + + nextRound() { + if (!this.state) return; + if (this.state.currentRoundIndex < this.state.rounds.length - 1) { + this.state.currentRoundIndex++; + this.state.phase = "intro"; + this.state.introCategoryIndex = -1; + this.state.categoriesIntroduced = false; // Reset for new round + this.state.boardRevealed = false; // Reset for new round + this.state.questionResults = []; + this.persist(); + } + } + + goToFinalRound() { + if (!this.state || !this.state.finalRound) return; + this.state.phase = "final-intro"; + this.persist(); + } + + startFinalCategoryReveal() { + if (!this.state) return; + this.state.phase = "final-category"; + this.persist(); + } + + finishFinalCategoryReveal() { + if (!this.state) return; + // Go back to Kuldvillak screen, waiting for moderator to start question + this.state.phase = "final-intro"; + this.state.finalCategoryRevealed = true; + this.persist(); + } + + // ============================================ + // Final Round + // ============================================ + + setFinalWager(teamId: string, wager: number) { + if (!this.state) return; + this.state.finalWagers[teamId] = wager; + this.persist(); + } + + showFinalQuestion() { + if (!this.state) return; + this.state.phase = "final-question"; + this.state.timerMax = 30; // Set 30 second timer for final round + this.state.timerSeconds = 30; + this.persist(); + } + + showFinalScores() { + if (!this.state) return; + this.state.phase = "final-scores"; + this.persist(); + } + + setFinalAnswer(teamId: string, answer: string) { + if (!this.state) return; + this.state.finalAnswers[teamId] = answer; + this.persist(); + } + + revealFinalAnswer(teamId: string, correct: boolean) { + if (!this.state) return; + + const team = this.state.teams.find(t => t.id === teamId); + const wager = this.state.finalWagers[teamId] ?? 0; + + if (team) { + if (correct) { + team.score += wager; + } else { + team.score -= wager; + } + } + + this.state.finalRevealed.push(teamId); + + // Check if all revealed + if (this.state.finalRevealed.length === this.state.teams.length) { + this.state.phase = "finished"; + } + + this.persist(); + } + + // ============================================ + // Timer + // ============================================ + + private startInternalTimer() { + // Only start if not already running + if (this.timerInterval) return; + + this.timerInterval = setInterval(() => { + if (this.state?.timerRunning) { + if (this.state.timerSeconds > 0) { + this.state.timerSeconds--; + this.persist(); + } else { + this.state.timerRunning = false; + this.persist(); + } + } + }, 1000); + } + + private stopInternalTimer() { + if (this.timerInterval) { + clearInterval(this.timerInterval); + this.timerInterval = null; + } + } + + setTimerMax(seconds: number) { + if (!this.state) return; + this.state.timerMax = seconds; + this.persist(); + } + + // Call this from moderator view only + enableTimerControl() { + this.startInternalTimer(); + } + + startTimer() { + if (!this.state) return; + this.state.timerRunning = true; + this.state.timerSeconds = this.state.timerMax; + this.persist(); + } + + stopTimer() { + if (!this.state) return; + this.state.timerRunning = false; + this.persist(); + } + + // Called externally - no longer needed but kept for compatibility + tickTimer() { + // Timer now runs internally, this is a no-op + } + + resetTimer() { + if (!this.state) return; + this.state.timerSeconds = this.state.timerMax; + this.state.timerRunning = false; + this.persist(); + } + + // ============================================ + // Helpers + // ============================================ + + get currentRound(): Round | null { + if (!this.state) return null; + return this.state.rounds[this.state.currentRoundIndex] ?? null; + } + + get currentQuestionData() { + if (!this.state?.currentQuestion) return null; + const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion; + const category = this.state.rounds[roundIndex]?.categories[categoryIndex]; + const question = category?.questions[questionIndex]; + return question ? { category, question } : null; + } + + get sortedTeams(): Team[] { + if (!this.state) return []; + return [...this.state.teams].sort((a, b) => b.score - a.score); + } + + getQuestionResult(categoryIndex: number, questionIndex: number): QuestionResult | null { + if (!this.state) return null; + return this.state.questionResults.find( + r => r.categoryIndex === categoryIndex && r.questionIndex === questionIndex + ) ?? null; + } +} + +export const gameSession = new GameSessionStore(); diff --git a/src/lib/stores/kuldvillak.svelte.ts b/src/lib/stores/kuldvillak.svelte.ts new file mode 100644 index 0000000..46fe143 --- /dev/null +++ b/src/lib/stores/kuldvillak.svelte.ts @@ -0,0 +1,363 @@ +// ============================================ +// Kuldvillak Game State Store (Svelte 5 Runes) +// ============================================ + +import { + type KuldvillakGame, + type Team, + type Question, + type GamePhase, + type Round, + type Category, + DEFAULT_SETTINGS, + DEFAULT_STATE +} from '$lib/types/kuldvillak'; + +// ============================================ +// Utility Functions +// ============================================ + +function generateId(): string { + return crypto.randomUUID(); +} + +function createEmptyGame(name: string = 'New Game'): KuldvillakGame { + const now = new Date().toISOString(); + return { + id: generateId(), + name, + createdAt: now, + updatedAt: now, + settings: { ...DEFAULT_SETTINGS }, + teams: [], + rounds: [], + finalRound: null, + state: { ...DEFAULT_STATE } + }; +} + +function createEmptyRound(name: string, multiplier: number, settings: typeof DEFAULT_SETTINGS): Round { + const categories: Category[] = []; + for (let i = 0; i < settings.categoriesPerRound; i++) { + const questions: Question[] = settings.pointValues.map((points) => ({ + id: generateId(), + question: '', + answer: '', + points: points * multiplier, + isDailyDouble: false, + isRevealed: false + })); + categories.push({ + id: generateId(), + name: `Category ${i + 1}`, + questions + }); + } + return { + id: generateId(), + name, + categories, + pointMultiplier: multiplier + }; +} + +// ============================================ +// Game Store Class +// ============================================ + +class KuldvillakStore { + // Reactive state using Svelte 5 runes + game = $state(null); + savedGames = $state([]); + + // Derived values + get currentRound(): Round | null { + if (!this.game) return null; + return this.game.rounds[this.game.state.currentRoundIndex] ?? null; + } + + get currentQuestion(): Question | null { + if (!this.game || !this.currentRound || !this.game.state.currentQuestionId) return null; + for (const category of this.currentRound.categories) { + const question = category.questions.find((q) => q.id === this.game!.state.currentQuestionId); + if (question) return question; + } + return null; + } + + get currentCategory(): Category | null { + if (!this.game || !this.currentRound || !this.game.state.currentCategoryId) return null; + return this.currentRound.categories.find((c) => c.id === this.game!.state.currentCategoryId) ?? null; + } + + get activeTeam(): Team | null { + if (!this.game || !this.game.state.activeTeamId) return null; + return this.game.teams.find((t) => t.id === this.game!.state.activeTeamId) ?? null; + } + + get isRoundComplete(): boolean { + if (!this.currentRound) return false; + return this.currentRound.categories.every((c) => c.questions.every((q) => q.isRevealed)); + } + + get isGameComplete(): boolean { + if (!this.game) return false; + const allRoundsComplete = this.game.rounds.every((round) => + round.categories.every((c) => c.questions.every((q) => q.isRevealed)) + ); + if (!this.game.settings.enableFinalRound) return allRoundsComplete; + return allRoundsComplete && this.game.state.phase === 'finished'; + } + + // ============================================ + // Game Lifecycle + // ============================================ + + newGame(name: string = 'New Game'): void { + this.game = createEmptyGame(name); + // Create default 2 rounds (Jeopardy + Double Jeopardy) + this.game.rounds = [ + createEmptyRound('Round 1', 1, this.game.settings), + createEmptyRound('Round 2', 2, this.game.settings) + ]; + } + + loadGame(gameData: KuldvillakGame): void { + this.game = gameData; + } + + resetGame(): void { + if (!this.game) return; + // Reset all questions to unrevealed + for (const round of this.game.rounds) { + for (const category of round.categories) { + for (const question of category.questions) { + question.isRevealed = false; + } + } + } + // Reset teams scores + for (const team of this.game.teams) { + team.score = 0; + } + // Reset state + this.game.state = { ...DEFAULT_STATE }; + } + + closeGame(): void { + this.game = null; + } + + // ============================================ + // Team Management + // ============================================ + + addTeam(name: string): void { + if (!this.game) return; + this.game.teams.push({ + id: generateId(), + name, + score: 0 + }); + } + + removeTeam(teamId: string): void { + if (!this.game) return; + this.game.teams = this.game.teams.filter((t) => t.id !== teamId); + } + + updateTeamName(teamId: string, name: string): void { + if (!this.game) return; + const team = this.game.teams.find((t) => t.id === teamId); + if (team) team.name = name; + } + + updateTeamScore(teamId: string, score: number): void { + if (!this.game) return; + const team = this.game.teams.find((t) => t.id === teamId); + if (team) team.score = score; + } + + adjustTeamScore(teamId: string, delta: number): void { + if (!this.game) return; + const team = this.game.teams.find((t) => t.id === teamId); + if (team) team.score += delta; + } + + setActiveTeam(teamId: string | null): void { + if (!this.game) return; + this.game.state.activeTeamId = teamId; + } + + // ============================================ + // Game Flow Control + // ============================================ + + setPhase(phase: GamePhase): void { + if (!this.game) return; + this.game.state.phase = phase; + } + + startGame(): void { + if (!this.game || this.game.teams.length === 0) return; + this.game.state.phase = 'board'; + this.game.state.currentRoundIndex = 0; + } + + selectQuestion(categoryId: string, questionId: string): void { + if (!this.game) return; + const question = this.findQuestion(questionId); + if (!question || question.isRevealed) return; + + this.game.state.currentCategoryId = categoryId; + this.game.state.currentQuestionId = questionId; + + if (question.isDailyDouble) { + this.game.state.phase = 'daily-double'; + } else { + this.game.state.phase = 'question'; + } + } + + setDailyDoubleWager(wager: number): void { + if (!this.game) return; + this.game.state.dailyDoubleWager = wager; + this.game.state.phase = 'question'; + } + + revealAnswer(): void { + if (!this.game) return; + this.game.state.phase = 'answer'; + } + + markCorrect(teamId: string): void { + if (!this.game || !this.currentQuestion) return; + const points = this.game.state.dailyDoubleWager ?? this.currentQuestion.points; + this.adjustTeamScore(teamId, points); + this.finishQuestion(); + } + + markIncorrect(teamId: string): void { + if (!this.game || !this.currentQuestion) return; + const points = this.game.state.dailyDoubleWager ?? this.currentQuestion.points; + this.adjustTeamScore(teamId, -points); + } + + finishQuestion(): void { + if (!this.game || !this.game.state.currentQuestionId) return; + const question = this.findQuestion(this.game.state.currentQuestionId); + if (question) question.isRevealed = true; + + this.game.state.currentQuestionId = null; + this.game.state.currentCategoryId = null; + this.game.state.dailyDoubleWager = null; + this.game.state.activeTeamId = null; + + // Check if round is complete + if (this.isRoundComplete) { + this.advanceRound(); + } else { + this.game.state.phase = 'board'; + } + } + + advanceRound(): void { + if (!this.game) return; + const nextIndex = this.game.state.currentRoundIndex + 1; + if (nextIndex < this.game.rounds.length) { + this.game.state.currentRoundIndex = nextIndex; + this.game.state.phase = 'board'; + } else if (this.game.settings.enableFinalRound && this.game.finalRound) { + this.game.state.phase = 'final-category'; + } else { + this.game.state.phase = 'finished'; + } + } + + // Final Round + startFinalRound(): void { + if (!this.game) return; + this.game.state.phase = 'final-question'; + } + + setFinalWager(teamId: string, wager: number): void { + if (!this.game) return; + this.game.state.finalWagers[teamId] = wager; + } + + setFinalAnswer(teamId: string, answer: string): void { + if (!this.game) return; + this.game.state.finalAnswers[teamId] = answer; + } + + scoreFinalAnswer(teamId: string, correct: boolean): void { + if (!this.game) return; + const wager = this.game.state.finalWagers[teamId] ?? 0; + this.adjustTeamScore(teamId, correct ? wager : -wager); + } + + finishGame(): void { + if (!this.game) return; + this.game.state.phase = 'finished'; + } + + // ============================================ + // Editor Functions + // ============================================ + + updateGameName(name: string): void { + if (!this.game) return; + this.game.name = name; + this.game.updatedAt = new Date().toISOString(); + } + + updateCategoryName(roundIndex: number, categoryId: string, name: string): void { + if (!this.game) return; + const category = this.game.rounds[roundIndex]?.categories.find((c) => c.id === categoryId); + if (category) category.name = name; + } + + updateQuestion(questionId: string, updates: Partial>): void { + if (!this.game) return; + const question = this.findQuestion(questionId); + if (question) { + if (updates.question !== undefined) question.question = updates.question; + if (updates.answer !== undefined) question.answer = updates.answer; + if (updates.isDailyDouble !== undefined) question.isDailyDouble = updates.isDailyDouble; + } + } + + updateFinalRound(category: string, question: string, answer: string): void { + if (!this.game) return; + this.game.finalRound = { category, question, answer }; + } + + addRound(): void { + if (!this.game) return; + const multiplier = this.game.rounds.length + 1; + this.game.rounds.push(createEmptyRound(`Round ${multiplier}`, multiplier, this.game.settings)); + } + + removeRound(roundIndex: number): void { + if (!this.game || this.game.rounds.length <= 1) return; + this.game.rounds.splice(roundIndex, 1); + } + + // ============================================ + // Helper Functions + // ============================================ + + private findQuestion(questionId: string): Question | null { + if (!this.game) return null; + for (const round of this.game.rounds) { + for (const category of round.categories) { + const question = category.questions.find((q) => q.id === questionId); + if (question) return question; + } + } + return null; + } +} + +// Export singleton instance +export const kuldvillakStore = new KuldvillakStore(); diff --git a/src/lib/stores/persistence.ts b/src/lib/stores/persistence.ts new file mode 100644 index 0000000..0f86e31 --- /dev/null +++ b/src/lib/stores/persistence.ts @@ -0,0 +1,259 @@ +// ============================================ +// LocalStorage Persistence for Game Data +// ============================================ + +import type { KuldvillakGame, GameMetadata } from '$lib/types/kuldvillak'; + +const STORAGE_PREFIX = 'ultimate_gaming'; +const KULDVILLAK_GAMES_KEY = `${STORAGE_PREFIX}_kuldvillak_games`; +const KULDVILLAK_ACTIVE_KEY = `${STORAGE_PREFIX}_kuldvillak_active`; + +// ============================================ +// Kuldvillak Save/Load Functions +// ============================================ + +/** + * Get all saved Kuldvillak games metadata (lightweight list) + */ +export function getKuldvillakGamesList(): GameMetadata[] { + if (typeof localStorage === 'undefined') return []; + + try { + const data = localStorage.getItem(KULDVILLAK_GAMES_KEY); + if (!data) return []; + + const games: KuldvillakGame[] = JSON.parse(data); + return games.map((game) => ({ + id: game.id, + name: game.name, + createdAt: game.createdAt, + updatedAt: game.updatedAt, + teamCount: game.teams.length, + roundCount: game.rounds.length + })); + } catch (e) { + console.error('Failed to load games list:', e); + return []; + } +} + +/** + * Get all saved Kuldvillak games (full data) + */ +export function getAllKuldvillakGames(): KuldvillakGame[] { + if (typeof localStorage === 'undefined') return []; + + try { + const data = localStorage.getItem(KULDVILLAK_GAMES_KEY); + return data ? JSON.parse(data) : []; + } catch (e) { + console.error('Failed to load games:', e); + return []; + } +} + +/** + * Load a specific Kuldvillak game by ID + */ +export function loadKuldvillakGame(gameId: string): KuldvillakGame | null { + const games = getAllKuldvillakGames(); + return games.find((g) => g.id === gameId) ?? null; +} + +/** + * Save a Kuldvillak game (creates new or updates existing) + */ +export function saveKuldvillakGame(game: KuldvillakGame): boolean { + if (typeof localStorage === 'undefined') return false; + + try { + const games = getAllKuldvillakGames(); + const existingIndex = games.findIndex((g) => g.id === game.id); + + // Update timestamp + game.updatedAt = new Date().toISOString(); + + if (existingIndex >= 0) { + games[existingIndex] = game; + } else { + games.push(game); + } + + localStorage.setItem(KULDVILLAK_GAMES_KEY, JSON.stringify(games)); + return true; + } catch (e) { + console.error('Failed to save game:', e); + return false; + } +} + +/** + * Delete a Kuldvillak game by ID + */ +export function deleteKuldvillakGame(gameId: string): boolean { + if (typeof localStorage === 'undefined') return false; + + try { + const games = getAllKuldvillakGames(); + const filtered = games.filter((g) => g.id !== gameId); + localStorage.setItem(KULDVILLAK_GAMES_KEY, JSON.stringify(filtered)); + return true; + } catch (e) { + console.error('Failed to delete game:', e); + return false; + } +} + +/** + * Duplicate a Kuldvillak game + */ +export function duplicateKuldvillakGame(gameId: string): KuldvillakGame | null { + const original = loadKuldvillakGame(gameId); + if (!original) return null; + + const now = new Date().toISOString(); + const duplicate: KuldvillakGame = { + ...JSON.parse(JSON.stringify(original)), // Deep clone + id: crypto.randomUUID(), + name: `${original.name} (Copy)`, + createdAt: now, + updatedAt: now + }; + + // Reset game state + duplicate.state = { + phase: 'lobby', + currentRoundIndex: 0, + currentQuestionId: null, + currentCategoryId: null, + activeTeamId: null, + dailyDoubleWager: null, + finalWagers: {}, + finalAnswers: {} + }; + + // Reset revealed questions and scores + for (const round of duplicate.rounds) { + for (const category of round.categories) { + for (const question of category.questions) { + question.isRevealed = false; + } + } + } + for (const team of duplicate.teams) { + team.score = 0; + } + + if (saveKuldvillakGame(duplicate)) { + return duplicate; + } + return null; +} + +// ============================================ +// Active Game Session (for resuming) +// ============================================ + +/** + * Save reference to currently active game + */ +export function setActiveKuldvillakGame(gameId: string | null): void { + if (typeof localStorage === 'undefined') return; + + if (gameId) { + localStorage.setItem(KULDVILLAK_ACTIVE_KEY, gameId); + } else { + localStorage.removeItem(KULDVILLAK_ACTIVE_KEY); + } +} + +/** + * Get the ID of the last active game + */ +export function getActiveKuldvillakGameId(): string | null { + if (typeof localStorage === 'undefined') return null; + return localStorage.getItem(KULDVILLAK_ACTIVE_KEY); +} + +/** + * Load the last active game + */ +export function loadActiveKuldvillakGame(): KuldvillakGame | null { + const activeId = getActiveKuldvillakGameId(); + if (!activeId) return null; + return loadKuldvillakGame(activeId); +} + +// ============================================ +// Export/Import Functions +// ============================================ + +/** + * Export a game to a JSON file (triggers download) + */ +export function exportKuldvillakGame(game: KuldvillakGame): void { + const dataStr = JSON.stringify(game, null, 2); + const blob = new Blob([dataStr], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const link = document.createElement('a'); + link.href = url; + link.download = `${game.name.replace(/[^a-z0-9]/gi, '_')}_kuldvillak.json`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); +} + +/** + * Import a game from a JSON file + */ +export async function importKuldvillakGame(file: File): Promise { + try { + const text = await file.text(); + const game: KuldvillakGame = JSON.parse(text); + + // Validate basic structure + if (!game.id || !game.name || !game.rounds || !game.settings) { + throw new Error('Invalid game file structure'); + } + + // Assign new ID to avoid conflicts + game.id = crypto.randomUUID(); + game.createdAt = new Date().toISOString(); + game.updatedAt = new Date().toISOString(); + + if (saveKuldvillakGame(game)) { + return game; + } + return null; + } catch (e) { + console.error('Failed to import game:', e); + return null; + } +} + +// ============================================ +// Storage Stats +// ============================================ + +/** + * Get approximate storage usage + */ +export function getStorageStats(): { used: number; available: number } { + if (typeof localStorage === 'undefined') { + return { used: 0, available: 0 }; + } + + let used = 0; + for (const key in localStorage) { + if (localStorage.hasOwnProperty(key)) { + used += localStorage.getItem(key)?.length ?? 0; + } + } + + // localStorage typically has ~5MB limit + const available = 5 * 1024 * 1024 - used; + + return { used, available }; +} diff --git a/src/lib/stores/theme.svelte.ts b/src/lib/stores/theme.svelte.ts new file mode 100644 index 0000000..a86515d --- /dev/null +++ b/src/lib/stores/theme.svelte.ts @@ -0,0 +1,112 @@ +import { browser } from "$app/environment"; + +const THEME_STORAGE_KEY = "kuldvillak-theme"; + +// Default theme colors +export const DEFAULT_THEME = { + primary: "#003B9B", + secondary: "#FFAB00", + text: "#FFFFFF", + background: "#000000", +}; + +// Load initial values from localStorage +function getInitialTheme() { + if (browser) { + const saved = localStorage.getItem(THEME_STORAGE_KEY); + if (saved) { + try { + const theme = JSON.parse(saved); + return { + primary: theme.primary ?? DEFAULT_THEME.primary, + secondary: theme.secondary ?? DEFAULT_THEME.secondary, + text: theme.text ?? DEFAULT_THEME.text, + background: theme.background ?? DEFAULT_THEME.background, + }; + } catch { + // Ignore parse errors + } + } + } + return { ...DEFAULT_THEME }; +} + +const initialTheme = getInitialTheme(); + +// Current applied values (what's visually shown) +let primary = $state(initialTheme.primary); +let secondary = $state(initialTheme.secondary); +let text = $state(initialTheme.text); +let background = $state(initialTheme.background); + +// Saved values (what's persisted to localStorage) +let savedPrimary = $state(initialTheme.primary); +let savedSecondary = $state(initialTheme.secondary); +let savedText = $state(initialTheme.text); +let savedBackground = $state(initialTheme.background); + +function applyTheme() { + if (browser) { + document.documentElement.style.setProperty("--kv-blue", primary); + document.documentElement.style.setProperty("--kv-yellow", secondary); + document.documentElement.style.setProperty("--kv-text", text); + document.documentElement.style.setProperty("--kv-background", background); + } +} + +// Save current values to localStorage +function save() { + if (browser) { + localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify({ + primary, + secondary, + text, + background + })); + // Update saved state + savedPrimary = primary; + savedSecondary = secondary; + savedText = text; + savedBackground = background; + } +} + +// Revert to last saved values (for cancel/close without saving) +function revert() { + primary = savedPrimary; + secondary = savedSecondary; + text = savedText; + background = savedBackground; + applyTheme(); +} + +// Reset to default values (applies immediately for preview) +function resetToDefaults() { + primary = DEFAULT_THEME.primary; + secondary = DEFAULT_THEME.secondary; + text = DEFAULT_THEME.text; + background = DEFAULT_THEME.background; + applyTheme(); +} + +// Reset and save (for full reset) +function reset() { + resetToDefaults(); + save(); +} + +export const themeStore = { + get primary() { return primary; }, + set primary(value: string) { primary = value; applyTheme(); }, + get secondary() { return secondary; }, + set secondary(value: string) { secondary = value; applyTheme(); }, + get text() { return text; }, + set text(value: string) { text = value; applyTheme(); }, + get background() { return background; }, + set background(value: string) { background = value; applyTheme(); }, + applyTheme, + save, + revert, + reset, + resetToDefaults, +}; diff --git a/src/lib/types/kuldvillak.ts b/src/lib/types/kuldvillak.ts new file mode 100644 index 0000000..2ca5bcb --- /dev/null +++ b/src/lib/types/kuldvillak.ts @@ -0,0 +1,167 @@ +// ============================================ +// Kuldvillak (Jeopardy) Type Definitions +// ============================================ + +/** A single question/answer pair on the board */ +export interface Question { + id: string; + question: string; + answer: string; + points: number; + isDailyDouble: boolean; + isRevealed: boolean; + imageUrl?: string; +} + +/** A category column containing questions */ +export interface Category { + id: string; + name: string; + questions: Question[]; +} + +/** A game round (e.g., Jeopardy, Double Jeopardy) */ +export interface Round { + id: string; + name: string; + categories: Category[]; + pointMultiplier: number; +} + +/** Final Jeopardy round structure */ +export interface FinalRound { + category: string; + question: string; + answer: string; +} + +/** A competing team */ +export interface Team { + id: string; + name: string; + score: number; +} + +/** Current game phase */ +export type GamePhase = + | 'intro' // Show Kuldvillak home screen (round start) + | 'intro-categories' // Animating category introductions + | 'lobby' + | 'board' + | 'question' + | 'answer' + | 'daily-double' + | 'final-intro' // Final round intro (Kuldvillak screen) + | 'final-category' // Reveal final round category + | 'final-question' + | 'final-reveal' + | 'final-scores' + | 'finished'; + +/** Result of a question for tracking */ +export interface QuestionResult { + categoryIndex: number; + questionIndex: number; + points: number; + teamId: string | null; // Who won/lost points (null if skipped) + pointsChange: number; // Positive for correct, negative for wrong, 0 for skip + isDailyDouble: boolean; + wager?: number; // DD wager if applicable +} + +/** Current state during gameplay */ +export interface GameState { + phase: GamePhase; + currentRoundIndex: number; + currentQuestionId: string | null; + currentCategoryId: string | null; + activeTeamId: string | null; + dailyDoubleWager: number | null; + finalWagers: Record; + finalAnswers: Record; +} + +/** Point value preset types */ +export type PointValuePreset = 'round1' | 'round2' | 'custom' | 'multiplier'; + +/** Configurable game settings */ +export interface GameSettings { + numberOfRounds: 1 | 2; + pointValuePreset: PointValuePreset; + pointValues: number[]; + basePointValue: number; // For multiplier preset (e.g., 100 → 100,200,300,400,500) + categoriesPerRound: number; + questionsPerCategory: number; + dailyDoublesPerRound: number[]; + enableFinalRound: boolean; + enableSoundEffects: boolean; + allowNegativeScores: boolean; + maxTeams: number; + defaultTimerSeconds: number; + answerRevealSeconds: number; +} + +/** Point value presets */ +export const POINT_PRESETS = { + round1: [100, 200, 300, 400, 500], + round2: [200, 400, 600, 800, 1000] +} as const; + +/** Complete game configuration (saveable/loadable) */ +export interface KuldvillakGame { + id: string; + name: string; + createdAt: string; + updatedAt: string; + settings: GameSettings; + teams: Team[]; + rounds: Round[]; + finalRound: FinalRound | null; + state: GameState; +} + +/** Default settings for new games */ +export const DEFAULT_SETTINGS: GameSettings = { + numberOfRounds: 2, + pointValuePreset: 'round1', + pointValues: [10, 20, 30, 40, 50], + basePointValue: 10, + categoriesPerRound: 6, + questionsPerCategory: 5, + dailyDoublesPerRound: [1, 2], + enableFinalRound: true, + enableSoundEffects: true, + allowNegativeScores: true, + maxTeams: 6, + defaultTimerSeconds: 5, + answerRevealSeconds: 5 +}; + +/** Default initial game state */ +export const DEFAULT_STATE: GameState = { + phase: 'lobby', + currentRoundIndex: 0, + currentQuestionId: null, + currentCategoryId: null, + activeTeamId: null, + dailyDoubleWager: null, + finalWagers: {}, + finalAnswers: {} +}; + +// ============================================ +// Helper Types for Editor/UI +// ============================================ + +/** Saved game metadata for game list */ +export interface GameMetadata { + id: string; + name: string; + createdAt: string; + updatedAt: string; + teamCount: number; + roundCount: number; +} + +/** View mode for dual-screen setup */ +export type ViewMode = 'projector' | 'moderator'; \ No newline at end of file diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte new file mode 100644 index 0000000..9fb5202 --- /dev/null +++ b/src/routes/+error.svelte @@ -0,0 +1,31 @@ + + +
+
+
+

+ {$page.status} +

+

+ {#if $page.status === 404} + {m.kv_error_not_found()} + {:else} + {$page.error?.message ?? "Error"} + {/if} +

+
+

+ {m.kv_error_hint()} +

+
+
+ + diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..df04f9d --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,19 @@ + + + + + + +{@render children()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..c2d3229 --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,80 @@ + + + + +
+
+ +
+

{m.app_title()}

+

{m.app_version()}

+
+ + +
+ {#each games as game} +
+ + {getGameName(game.nameKey)} + + {#if game.available} + + {getGameName(game.nameKey)} + + {:else} +
+ {getGameName(game.nameKey)} + + {m.coming_soon()} + +
+ {/if} +
+ {/each} +
+
+
diff --git a/src/routes/kuldvillak/+layout.svelte b/src/routes/kuldvillak/+layout.svelte new file mode 100644 index 0000000..1cde5b0 --- /dev/null +++ b/src/routes/kuldvillak/+layout.svelte @@ -0,0 +1,22 @@ + + +{@render children()} diff --git a/src/routes/kuldvillak/+page.svelte b/src/routes/kuldvillak/+page.svelte new file mode 100644 index 0000000..b155263 --- /dev/null +++ b/src/routes/kuldvillak/+page.svelte @@ -0,0 +1,106 @@ + + +
+ +
+ + +
+ + + + +
+ + {m.kv_new_game()} + + fileInput.click()} + class="w-full !text-lg md:!text-2xl !py-3 md:!py-4" + > + {m.kv_load_game()} + + + + {m.kv_settings()} + + + {m.kv_exit()} + +
+
+
+ + + + diff --git a/src/routes/kuldvillak/edit/+page.svelte b/src/routes/kuldvillak/edit/+page.svelte new file mode 100644 index 0000000..786554f --- /dev/null +++ b/src/routes/kuldvillak/edit/+page.svelte @@ -0,0 +1,1007 @@ + + + +
+ +
+
+ + + + + +
+ +
+
+ +
+ + + + +
+ + + + +
+ + +
+ +
+

+ {m.kv_edit_settings_teams()} +

+
+ + +
+
+ +
+ +
+ +
+
+ {m.kv_edit_rounds()} +
+
+ {m.kv_play_timer()} +
+
+ {m.kv_play_timer_reveal()} +
+
+ {m.kv_edit_final_round()} +
+
+ + +
+ +
+ + +
+ +
+
+ +
+ {m.kv_play_seconds()} +
+ +
+
+ +
+ {m.kv_play_seconds()} +
+ +
+ +
+
+
+ + +
+ +
+
+ {m.kv_edit_points()} +
+
+ {m.kv_edit_values()} +
+
+ {m.kv_edit_negative_scores()} +
+
+ {m.kv_edit_teams_label()} +
+
+ + +
+ +
+ + +
+ +
+ {#each settings.pointValues as val, i} +
+ {#if settings.pointValuePreset === "custom"} + + {:else} + {val} + {/if} +
+ {/each} +
+ +
+ +
+ +
+ {#each teams as team (team.id)} +
+ + +
+ {/each} + {#if teams.length < 6} + + {/if} +
+
+
+
+
+ + +
+ {#each rounds as round, ri} +
+ +
+

+ {ri === 0 ? "Villak" : "Topeltvillak"} + ({m.kv_edit_dd_count()} + {countDailyDoubles(ri)}/{settings + .dailyDoublesPerRound[ri] ?? 1}) +

+
+ + +
+ {#each round.categories as cat, ci} +
+ +
+ {/each} +
+ + +
+ {#each { length: settings.questionsPerCategory } as _, qi} + {#each round.categories as cat, ci} + {@const q = cat.questions[qi]} + + {/each} + {/each} +
+
+ {/each} +
+
+ + +{#if editingQuestion} + {@const q = + rounds[editingQuestion.roundIndex].categories[editingQuestion.catIndex] + .questions[editingQuestion.qIndex]} + {@const cat = + rounds[editingQuestion.roundIndex].categories[editingQuestion.catIndex]} + {@const maxDD = + settings.dailyDoublesPerRound[editingQuestion.roundIndex] ?? 1} + {@const currentDD = countDailyDoubles(editingQuestion.roundIndex)} +
e.target === e.currentTarget && saveQuestion()} + onkeydown={(e) => e.key === "Escape" && saveQuestion()} + role="dialog" + tabindex="-1" + > +
+ +
+

+ {cat.name || m.kv_edit_category()} - {q.points}€ +

+ +
+ + +
+
+ +
+
+ +
+
+ +
+ + +
+ + + {m.kv_edit_daily_double()} ({currentDD}/{maxDD}) + +
+
+ + + +
+
+{/if} + + +{#if editingFinalQuestion} +
+ e.target === e.currentTarget && (editingFinalQuestion = false)} + onkeydown={(e) => e.key === "Escape" && (editingFinalQuestion = false)} + role="dialog" + tabindex="-1" + > +
+
+

+ {m.kv_edit_final_round()} +

+ +
+ +
+
+ +
+
+ +
+
+ +
+ + +
+ + + {m.kv_edit_final_enabled()} + +
+
+ + +
+
+{/if} + + + + + + + + +{#if isStarting} +
+
+

+ {m.kv_edit_starting_game()} +

+
+{/if} diff --git a/src/routes/kuldvillak/play/+page.svelte b/src/routes/kuldvillak/play/+page.svelte new file mode 100644 index 0000000..c173926 --- /dev/null +++ b/src/routes/kuldvillak/play/+page.svelte @@ -0,0 +1,36 @@ + + +{#if !gameSession.state} +
+
+
+
🎮
+
+

+ {m.kv_play_loading()} +

+

+ {m.kv_play_loading_hint()} +

+ + {m.kv_play_go_to_editor()} + +
+
+{:else if view === "projector"} + +{:else} + +{/if} diff --git a/src/routes/kuldvillak/play/ModeratorView.svelte b/src/routes/kuldvillak/play/ModeratorView.svelte new file mode 100644 index 0000000..f18e450 --- /dev/null +++ b/src/routes/kuldvillak/play/ModeratorView.svelte @@ -0,0 +1,700 @@ + + +{#if session} +
+ +
+ +
+ +
+

+ {session.name} +

+
+ {currentRound?.name || m.kv_play_round()} - {m.kv_play_round()} + {session.currentRoundIndex + 1} +
+
+ + +
+
+ + {m.kv_play_open_projector()} + + {#if session.settings.enableFinalRound && session.finalRound && session.phase === "board"} + gameSession.goToFinalRound()} + class="!py-3 !px-6 !text-xl" + > + {m.kv_play_go_to_final()} + + {/if} + { + if (confirm(m.kv_play_end_game_confirm())) + gameSession.endGame(); + }} + class="!py-3 !px-6 !text-xl" + > + {m.kv_play_end_game()} + +
+ + +
+ {#if session.lastAnsweredTeamId} + {@const lastTeam = session.teams.find( + (t) => t.id === session.lastAnsweredTeamId, + )} + + {m.kv_play_last_answer()}: + + {lastTeam?.name} + {session.lastAnswerCorrect ? "✓" : "✗"} + + + {/if} +
+ {m.kv_play_adjust_by()}: + +
+
+
+
+ + +
+ {#each session.teams as team} + + gameSession.adjustScore(team.id, delta)} + onclick={() => + session.phase === "question" && + !session.showAnswer && + gameSession.setActiveTeam(team.id)} + disabled={isTeamWrong(team.id)} + /> + {/each} +
+
+ + {#if session.phase === "intro"} + +
+
+ KULDVILLAK +
+
+ {m.kv_play_round()} + {session.currentRoundIndex + 1} +
+
+ + +
+
+ {:else if session.phase === "intro-categories"} + +
+
+ {m.kv_play_introducing_categories()} +
+
+ {session.introCategoryIndex + 1} / {currentRound?.categories + .length ?? 0} +
+ {#if currentRound?.categories[session.introCategoryIndex]} +
+ {currentRound.categories[session.introCategoryIndex] + .name} +
+ + {:else} +
+ +
+ {/if} +
+ {:else if session.phase === "board"} + +
+ +
+ {#each currentRound?.categories ?? [] as cat} +
+ {cat.name || "???"} +
+ {/each} +
+ +
+ {#each currentRound?.categories ?? [] as cat, ci} +
+ {#each cat.questions as q, qi} + {@const result = gameSession.getQuestionResult( + ci, + qi, + )} + + {/each} +
+ {/each} +
+
+ {:else if session.phase === "daily-double"} + +
+

+ {m.kv_play_daily_double()}! +

+ +
+ {#each session.teams as team} + + {/each} +
+ + {#if session.activeTeamId} + {@const activeTeam = session.teams.find( + (t) => t.id === session.activeTeamId, + )} +
+ {m.kv_play_wager()}: + + +
+ {/if} +
+ {:else if session.phase === "question" && questionData} + +
+
+

+ {questionData.category.name} - {questionData.question + .points}€ + {#if session.dailyDoubleWager} + (DD: {session.dailyDoubleWager}€) + {/if} +

+
+ + {m.kv_play_question_number({ + current: session.currentQuestionNumber, + total: totalQuestions(), + })} + + {#if session.showAnswer} + {m.kv_play_showing_answer()} + {/if} +
+
+ + +
+ {#if session.wrongTeamIds.length > 0} + + ✗ {session.wrongTeamIds + .map( + (id) => + session.teams.find((t) => t.id === id) + ?.name, + ) + .join(", ")} + + {/if} + {#if session.activeTeamId && session.showAnswer} + {@const activeTeam = session.teams.find( + (t) => t.id === session.activeTeamId, + )} + {@const points = + session.dailyDoubleWager ?? + questionData.question.points} + {#if session.lastAnswerCorrect} + + ✓ {activeTeam?.name} +{points}€ + + {:else if session.lastAnswerCorrect === false} + + ✗ {activeTeam?.name} -{points}€ + + {/if} + {/if} +
+ +
+
+ {m.kv_play_question_short()}: + {questionData.question.question} +
+
+ {m.kv_play_answer_short()}: + {questionData.question.answer} +
+
+ + +
+ {m.kv_play_answering()}: + {#each session.teams as team} + {@const isWrong = isTeamWrong(team.id)} + + {/each} +
+ + +
+ + + +
+ + +
+ {m.kv_play_timer()} + {session.timerSeconds}s + + gameSession.setTimerMax(session?.timerMax ?? 10)} + /> + {m.kv_play_seconds()} + {#if session.timerRunning} + + {:else} + + {/if} + +
+
+ {:else if session.phase === "final-intro"} + +
+
+ {m.kv_play_final_round()} +
+ {#if session.finalCategoryRevealed} + +
+ {session.finalRound?.category} +
+
+ +
+ {:else} + +
+ +
+ {/if} +
+ {:else if session.phase === "final-category"} + +
+
+ {m.kv_play_introducing_categories()} +
+
+ {session.finalRound?.category} +
+
+ {:else if session.phase === "final-question"} + +
+

+ {m.kv_play_final_round()} +

+
+ {m.kv_edit_category()}: + {session.finalRound?.category} +
+
+ {m.kv_play_question_short()}: + {session.finalRound?.question} +
+
+ {m.kv_play_answer_short()}: + {session.finalRound?.answer} +
+ + +
+ {m.kv_play_timer()} + {session.timerSeconds}s + {#if session.timerRunning} + + {:else} + + {/if} + +
+ + + + + + +
+ {:else if session.phase === "final-scores"} + +
+

+ {m.kv_play_scores()} +

+ +
+ {:else if session.phase === "finished"} +
+

+ {m.kv_play_game_over()}! +

+ {m.kv_edit_back()} +
+ {/if} +
+{/if} diff --git a/src/routes/kuldvillak/play/ProjectorView.svelte b/src/routes/kuldvillak/play/ProjectorView.svelte new file mode 100644 index 0000000..04efc10 --- /dev/null +++ b/src/routes/kuldvillak/play/ProjectorView.svelte @@ -0,0 +1,715 @@ + + +{#if session} +
+ {#if session.phase === "intro"} + +
+ {#if session.categoriesIntroduced} + + {:else} + + {/if} +
+ {:else if session.phase === "intro-categories"} + + {@const currentCat = + currentRound?.categories[session.introCategoryIndex]} +
+ +
+ +
+ + + {#if currentCat} +
+
+
+ {currentCat.name} +
+
+
+ {/if} + + +
+ +
+
+ {:else if session.phase === "board" || session.phase === "question"} + +
+ +
+ {#each currentRound?.categories ?? [] as cat} + {@const roundName = + session.currentRoundIndex === 0 + ? "VILLAK" + : "TOPELTVILLAK"} +
+ {session.boardRevealed + ? cat.name || "???" + : roundName} +
+ {/each} +
+ + +
+ {#each currentRound?.categories ?? [] as cat, ci} + {#each cat.questions as q, qi} +
+ + {q.points} + +
+ {/each} + {/each} +
+
+ + + {#if session.phase === "question" && questionData && (animationPhase === "expanding" || animationPhase === "shown")} +
+ +
+ {#each session.teams as team} +
+
+ {team.name} + {team.score}€ +
+
+ {/each} +
+ + +
+ {#if questionData.question.imageUrl} + +
+ Question +
+ {:else} + +
+ {#if session.showAnswer} +
+ {questionData.question.answer} +
+ {:else} +
+ {questionData.question.question} +
+ {/if} +
+ {/if} +
+
+ {/if} + {:else if session.phase === "daily-double"} + +
+ + {#if session.dailyDoubleWager} +
+ {m.kv_play_wager()}: {session.dailyDoubleWager}€ +
+ {/if} +
+ {:else if session.phase === "final-intro"} + +
+ +
+ {:else if session.phase === "final-category"} + +
+ +
+ +
+ + +
+
+
+ {session.finalRound?.category || "???"} +
+
+
+ + +
+ +
+
+ {:else if session.phase === "final-question"} + +
+ +
+ {#each session.teams as team} +
+
+ {team.name} + {team.score}€ +
+
+ {/each} +
+ + +
+
+ {#if session.showAnswer} +
+ {session.finalRound?.answer} +
+ {:else} +
+ {session.finalRound?.question} +
+ {/if} +
+
+
+ {:else if session.phase === "final-scores"} + +
+
+
+ {m.kv_play_scores()} +
+
+ +
+ {#each gameSession.sortedTeams as team, i} +
+
+ #{i + 1} + {team.name} +
+
+ {team.score}€ +
+
+ {/each} +
+
+ {:else if session.phase === "finished"} + +
+
+
+ {m.kv_play_game_over()}! +
+ + {#if gameSession.sortedTeams[0]} +
+ 🏆 {gameSession.sortedTeams[0].name} 🏆 +
+ {/if} +
+ +
+ {#each gameSession.sortedTeams as team, i} +
+
+ #{i + 1} + {team.name} +
+
+ {team.score}€ +
+
+ {/each} +
+
+ {/if} +
+{/if} + + diff --git a/src/routes/layout.css b/src/routes/layout.css new file mode 100644 index 0000000..7937dea --- /dev/null +++ b/src/routes/layout.css @@ -0,0 +1,130 @@ +@import 'tailwindcss'; + +/* ============================================ + Tailwind Theme Extensions for Kuldvillak + ============================================ */ + +@theme { + /* Colors - Reference CSS variables for dynamic theming */ + --color-kv-blue: var(--kv-blue); + --color-kv-yellow: var(--kv-yellow); + --color-kv-green: #009900; + --color-kv-red: #990000; + --color-kv-black: var(--kv-background); + --color-kv-white: var(--kv-text); + /* Additional theme-aware colors */ + --color-kv-text: var(--kv-text); + --color-kv-background: var(--kv-background); + /* Fixed game board color - not affected by theme */ + --color-kv-board: #003B9B; + + /* Font Families */ + --font-kv-title: 'Swiss 921', sans-serif; + --font-kv-body: 'Swiss 921', sans-serif; + --font-kv-price: 'Bebas Neue', sans-serif; + --font-kv-question: 'ITC Korinna', serif; +} + +/* Text shadow utilities - Updated for new design */ +.kv-shadow-text { + text-shadow: 6px 6px 4px rgba(0, 0, 0, 0.5); +} +.kv-shadow-title { + text-shadow: 6px 6px 4px rgba(0, 0, 0, 0.5); +} +.kv-shadow-price { + text-shadow: 8px 8px 4px rgba(0, 0, 0, 0.5), 0 4px 4px rgba(0, 0, 0, 0.25); +} +.kv-shadow-question { + text-shadow: 6px 6px 4px rgba(0, 0, 0, 0.5); +} +.kv-shadow-button { + box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25), 8px 8px 4px rgba(0, 0, 0, 0.5); +} + +/* Icon styling - uses secondary color */ +.kv-icon { + color: var(--kv-yellow); +} + +/* ============================================ + Kuldvillak Custom Fonts + ============================================ */ + +/* Bebas Neue for prices - local font */ +@font-face { + font-family: 'Bebas Neue'; + src: url('/fonts/BebasNeue-Regular.ttf') format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Swiss 921'; + src: url('/fonts/Swiss 921 Regular.otf') format('opentype'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'ITC Korinna'; + src: url('/fonts/ITC Korinna Std Bold.otf') format('opentype'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +/* ============================================ + Kuldvillak Design Tokens (CSS Variables) + ============================================ */ + +:root { + /* Colors - New Figma Design (these are overridden by theme.svelte.ts) */ + --kv-blue: #003B9B; + --kv-yellow: #FFAB00; + --kv-text: #FFFFFF; + --kv-background: #000000; + --kv-green: #009900; + --kv-red: #990000; + --kv-black: #000000; + --kv-white: #FFFFFF; + + /* Legacy alias for compatibility */ + --kv-golden: var(--kv-yellow); + + /* Fonts */ + --kv-font-title: 'Swiss 921', sans-serif; + --kv-font-body: 'Swiss 921', sans-serif; + --kv-font-price: 'Bebas Neue', sans-serif; + --kv-font-question: 'ITC Korinna', serif; + /* Legacy alias */ + --kv-font-button: var(--kv-font-body); + --kv-font-category: var(--kv-font-body); + + /* Shadows - New Figma Design */ + --kv-shadow-text: 6px 6px 4px rgba(0, 0, 0, 0.5); + --kv-shadow-title: 6px 6px 4px rgba(0, 0, 0, 0.5); + --kv-shadow-price: 8px 8px 4px rgba(0, 0, 0, 0.5), 0 4px 4px rgba(0, 0, 0, 0.25); + --kv-shadow-button: 0 4px 4px rgba(0, 0, 0, 0.25), 8px 8px 4px rgba(0, 0, 0, 0.5); + --kv-shadow-question: 6px 6px 4px rgba(0, 0, 0, 0.5); + --kv-shadow-category: 6px 6px 4px rgba(0, 0, 0, 0.5); +} + +/* ============================================ + Global Styles + ============================================ */ + +html, +body { + height: 100%; + margin: 0; + padding: 0; + background-color: var(--kv-background); +} + +body { + font-family: var(--kv-font-button); + color: var(--kv-text); +} diff --git a/static/audio/kuldvillak_teema.mp3 b/static/audio/kuldvillak_teema.mp3 new file mode 100644 index 0000000..ce979c1 Binary files /dev/null and b/static/audio/kuldvillak_teema.mp3 differ diff --git a/static/favicon.svg b/static/favicon.svg new file mode 100644 index 0000000..8bc978c --- /dev/null +++ b/static/favicon.svg @@ -0,0 +1,46 @@ + + + + diff --git a/static/fonts/BebasNeue-Regular.ttf b/static/fonts/BebasNeue-Regular.ttf new file mode 100644 index 0000000..d2190b5 Binary files /dev/null and b/static/fonts/BebasNeue-Regular.ttf differ diff --git a/static/fonts/ITC Korinna Std Bold.otf b/static/fonts/ITC Korinna Std Bold.otf new file mode 100644 index 0000000..694437d Binary files /dev/null and b/static/fonts/ITC Korinna Std Bold.otf differ diff --git a/static/fonts/Swiss 921 Regular.otf b/static/fonts/Swiss 921 Regular.otf new file mode 100644 index 0000000..b548504 Binary files /dev/null and b/static/fonts/Swiss 921 Regular.otf differ diff --git a/static/icons/en.svg b/static/icons/en.svg new file mode 100644 index 0000000..f87fdb0 --- /dev/null +++ b/static/icons/en.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/static/icons/et.svg b/static/icons/et.svg new file mode 100644 index 0000000..39f1ca7 --- /dev/null +++ b/static/icons/et.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/static/images/kuldvillak-cover.jpg b/static/images/kuldvillak-cover.jpg new file mode 100644 index 0000000..33fbca8 Binary files /dev/null and b/static/images/kuldvillak-cover.jpg differ diff --git a/static/images/rooside-soda-cover.jpg b/static/images/rooside-soda-cover.jpg new file mode 100644 index 0000000..e54bc21 Binary files /dev/null and b/static/images/rooside-soda-cover.jpg differ diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 0000000..4cdf6f4 --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,15 @@ +import adapter from '@sveltejs/adapter-auto'; +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; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c2ed3c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "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 + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..2fa21a2 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,15 @@ +import { paraglideVitePlugin } from '@inlang/paraglide-js'; +import tailwindcss from '@tailwindcss/vite'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + tailwindcss(), + sveltekit(), + paraglideVitePlugin({ + project: './project.inlang', + outdir: './src/lib/paraglide' + }) + ] +});