From e3033734418d6a732e328b2221b49689f6bb0a8d Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Sun, 15 Mar 2026 10:26:20 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8feature:=20Initial=20code=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + index.html | 12 + package-lock.json | 1848 +++++++++++++++++++++++++++++++++ package.json | 25 + src/App.tsx | 75 ++ src/data/sampleContentPack.ts | 164 +++ src/main.tsx | 11 + src/schemas/content.ts | 203 ++++ src/schemas/rules.ts | 71 ++ src/schemas/state.ts | 217 ++++ src/styles.css | 130 +++ src/types/content.ts | 215 ++++ src/types/rules.ts | 81 ++ src/types/state.ts | 218 ++++ tsconfig.app.json | 24 + tsconfig.json | 7 + tsconfig.node.json | 11 + vite.config.ts | 12 + 18 files changed, 3329 insertions(+) create mode 100644 .gitignore create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/App.tsx create mode 100644 src/data/sampleContentPack.ts create mode 100644 src/main.tsx create mode 100644 src/schemas/content.ts create mode 100644 src/schemas/rules.ts create mode 100644 src/schemas/state.ts create mode 100644 src/styles.css create mode 100644 src/types/content.ts create mode 100644 src/types/rules.ts create mode 100644 src/types/state.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..98de87d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +*.tsbuildinfo +vite.config.js +vite.config.d.ts diff --git a/index.html b/index.html new file mode 100644 index 0000000..0522b40 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + 2D6 Dungeon Web + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8d1fb29 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1848 @@ +{ + "name": "d2d6-dungeon-web", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "d2d6-dungeon-web", + "version": "0.1.0", + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0", + "zod": "^3.24.2" + }, + "devDependencies": { + "@types/node": "^22.13.10", + "@types/react": "^19.0.12", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.4.1", + "typescript": "^5.8.2", + "vite": "^6.2.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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/@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/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "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/@types/node": { + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.8", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", + "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001779", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz", + "integrity": "sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "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/electron-to-chromium": { + "version": "1.5.313", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", + "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "dev": true, + "license": "ISC" + }, + "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/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "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.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "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/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "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.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "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/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/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", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "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/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..771e202 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "d2d6-dungeon-web", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "npm run check && vite build", + "preview": "vite preview", + "check": "tsc --noEmit -p tsconfig.app.json && tsc --noEmit -p tsconfig.node.json" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0", + "zod": "^3.24.2" + }, + "devDependencies": { + "@types/node": "^22.13.10", + "@types/react": "^19.0.12", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.4.1", + "typescript": "^5.8.2", + "vite": "^6.2.0" + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..025e45a --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,75 @@ +import { sampleContentPack } from "@/data/sampleContentPack"; + +const planningDocs = [ + "Planning/PROJECT_PLAN.md", + "Planning/GAME_SPEC.md", + "Planning/content-checklist.json", + "Planning/DATA_MODEL.md", + "Planning/IMPLEMENTATION_NOTES.md", +]; + +const nextTargets = [ + "Encode Level 1 foundational tables into structured JSON.", + "Implement dice utilities for D3, D6, 2D6, and D66.", + "Create character creation state and validation.", + "Build deterministic room generation for the Level 1 loop.", +]; + +function App() { + return ( +
+
+

2D6 Dungeon Web

+

Project scaffold is live.

+

+ The app now has a Vite + React + TypeScript foundation, shared type + models, and Zod schemas that mirror the planning documents. +

+
+ +
+
+

Planning Set

+
    + {planningDocs.map((doc) => ( +
  • {doc}
  • + ))} +
+
+ +
+

Immediate Build Targets

+
    + {nextTargets.map((target) => ( +
  1. {target}
  2. + ))} +
+
+ +
+

Sample Content Pack

+
+
+
Tables
+
{sampleContentPack.tables.length}
+
+
+
Weapons
+
{sampleContentPack.weapons.length}
+
+
+
Manoeuvres
+
{sampleContentPack.manoeuvres.length}
+
+
+
Creatures
+
{sampleContentPack.creatures.length}
+
+
+
+
+
+ ); +} + +export default App; diff --git a/src/data/sampleContentPack.ts b/src/data/sampleContentPack.ts new file mode 100644 index 0000000..251ba26 --- /dev/null +++ b/src/data/sampleContentPack.ts @@ -0,0 +1,164 @@ +import type { ContentPack } from "@/types/content"; + +import { contentPackSchema } from "@/schemas/content"; + +const samplePack = { + version: "0.1.0", + sourceBooks: [ + "Source/2D6 Dungeon - Core Rules.pdf", + "Source/2D6 Dungeon - Tables Codex.pdf", + "Source/2D6 Dungeon - Play Sheet.pdf", + ], + tables: [ + { + id: "table.weapon-manoeuvres-1", + code: "WMT1", + name: "Weapon Manoeuvres Table 1", + category: "generic", + page: 13, + diceKind: "d6", + entries: [ + { + key: "1", + exact: 1, + label: "Exact Strike", + references: [{ type: "manoeuvre", id: "manoeuvre.exact-strike" }], + }, + ], + notes: ["Starter sample table used to validate content wiring."], + mvp: true, + }, + ], + weapons: [ + { + id: "weapon.short-sword", + name: "Short Sword", + category: "melee", + handedness: "one-handed", + baseDamage: 1, + allowedManoeuvreIds: ["manoeuvre.exact-strike", "manoeuvre.guard-break"], + tags: ["starter"], + startingOption: true, + }, + ], + manoeuvres: [ + { + id: "manoeuvre.exact-strike", + name: "Exact Strike", + weaponCategories: ["melee"], + precisionModifier: 1, + exactStrikeBonus: true, + effectText: "Improves accuracy and benefits from exact strike bonuses.", + mvp: true, + }, + { + id: "manoeuvre.guard-break", + name: "Guard Break", + weaponCategories: ["melee"], + shiftCost: 1, + damageModifier: 1, + effectText: "Trades shift for a stronger hit.", + mvp: true, + }, + ], + armour: [ + { + id: "armour.leather-vest", + name: "Leather Vest", + armourValue: 1, + startingOption: true, + valueGp: 6, + mvp: true, + }, + ], + items: [ + { + id: "item.ration", + name: "Ration", + itemType: "ration", + stackable: true, + consumable: true, + mvp: true, + }, + { + id: "item.lantern", + name: "Lantern", + itemType: "light-source", + stackable: false, + consumable: false, + mvp: true, + }, + ], + potions: [ + { + id: "potion.healing", + name: "Healing Potion", + tableSource: "Magic Potions Table 1", + useTiming: "any", + effects: [{ type: "heal", amount: 3, target: "self" }], + mvp: true, + }, + ], + scrolls: [ + { + id: "scroll.lesser-heal", + name: "Lesser Heal", + tableSource: "Starting Scroll Table 1", + castCheck: { + diceKind: "d6", + successMin: 3, + }, + onSuccess: [{ type: "heal", amount: 2, target: "self" }], + onFailureTableCode: "FTCCT1", + startingOption: true, + mvp: true, + }, + ], + creatures: [ + { + id: "creature.level1.giant-rat", + name: "Giant Rat", + level: 1, + category: "beast", + hp: 2, + attackProfile: { + discipline: 0, + precision: 0, + damage: 1, + numberAppearing: "1-2", + }, + xpReward: 1, + sourcePage: 102, + traits: ["level-1", "sample"], + mvp: true, + }, + ], + roomTemplates: [ + { + id: "room.level1.entry", + level: 1, + roomClass: "start", + tableCode: "L1R", + tableEntryKey: "entry", + title: "Entry Chamber", + text: "A quiet starting space for the first dungeon prototype.", + dimensions: { + width: 4, + height: 4, + }, + exits: [{ direction: "north", exitType: "door" }], + tags: ["starter", "entry"], + mvp: true, + }, + ], + townServices: [ + { + id: "service.market", + name: "Market", + serviceType: "market", + mvp: true, + }, + ], +} satisfies ContentPack; + +export const sampleContentPack = contentPackSchema.parse(samplePack); diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..c475b8a --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; + +import App from "./App"; +import "./styles.css"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/src/schemas/content.ts b/src/schemas/content.ts new file mode 100644 index 0000000..1ef5474 --- /dev/null +++ b/src/schemas/content.ts @@ -0,0 +1,203 @@ +import { z } from "zod"; + +import { + contentReferenceSchema, + diceKindSchema, + ruleEffectSchema, +} from "./rules"; + +export const tableEntrySchema = z.object({ + key: z.string().min(1), + min: z.number().int().optional(), + max: z.number().int().optional(), + exact: z.number().int().optional(), + d66: z.number().int().optional(), + label: z.string().min(1), + text: z.string().optional(), + effects: z.array(ruleEffectSchema).optional(), + references: z.array(contentReferenceSchema).optional(), +}); + +export const tableDefinitionSchema = z.object({ + id: z.string().min(1), + code: z.string().min(1), + name: z.string().min(1), + category: z.enum(["generic", "random-list", "loot", "level", "optional", "town", "room"]), + level: z.number().int().positive().optional(), + page: z.number().int().positive(), + diceKind: diceKindSchema, + usesModifiedRangesRule: z.boolean().optional(), + entries: z.array(tableEntrySchema), + notes: z.array(z.string()).optional(), + mvp: z.boolean(), +}); + +export const weaponDefinitionSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + category: z.enum(["melee", "ranged"]), + handedness: z.enum(["one-handed", "two-handed"]), + baseDamage: z.number().int(), + allowedManoeuvreIds: z.array(z.string()), + tags: z.array(z.string()), + startingOption: z.boolean(), +}); + +export const manoeuvreDefinitionSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + weaponCategories: z.array(z.enum(["melee", "ranged"])), + shiftCost: z.number().int().optional(), + disciplineModifier: z.number().int().optional(), + precisionModifier: z.number().int().optional(), + damageModifier: z.number().int().optional(), + exactStrikeBonus: z.boolean().optional(), + interruptRule: z.string().optional(), + effectText: z.string().optional(), + mvp: z.boolean(), +}); + +export const armourDefinitionSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + armourValue: z.number().int(), + penalties: z + .object({ + shift: z.number().int().optional(), + discipline: z.number().int().optional(), + precision: z.number().int().optional(), + }) + .optional(), + deflectionRule: z.string().optional(), + startingOption: z.boolean(), + valueGp: z.number().int().optional(), + mvp: z.boolean(), +}); + +export const itemDefinitionSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + itemType: z.enum([ + "gear", + "treasure", + "quest", + "herb", + "rune", + "misc", + "ration", + "light-source", + ]), + stackable: z.boolean(), + consumable: z.boolean(), + valueGp: z.number().int().optional(), + weight: z.number().optional(), + rulesText: z.string().optional(), + effects: z.array(ruleEffectSchema).optional(), + mvp: z.boolean(), +}); + +export const potionDefinitionSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + tableSource: z.string().min(1), + useTiming: z.enum(["combat", "exploration", "town", "any"]), + effects: z.array(ruleEffectSchema), + valueGp: z.number().int().optional(), + mvp: z.boolean(), +}); + +export const scrollDefinitionSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + tableSource: z.string().min(1), + castCheck: z + .object({ + diceKind: z.enum(["d6", "2d6"]), + successMin: z.number().int().optional(), + successMax: z.number().int().optional(), + }) + .optional(), + onSuccess: z.array(ruleEffectSchema), + onFailureTableCode: z.string().optional(), + valueGp: z.number().int().optional(), + startingOption: z.boolean(), + mvp: z.boolean(), +}); + +export const creatureDefinitionSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + level: z.number().int().positive(), + category: z.string().min(1), + hp: z.number().int().positive(), + attackProfile: z.object({ + discipline: z.number().int(), + precision: z.number().int(), + damage: z.number().int(), + numberAppearing: z.string().optional(), + }), + defenceProfile: z + .object({ + armour: z.number().int().optional(), + specialRules: z.array(z.string()).optional(), + }) + .optional(), + xpReward: z.number().int().optional(), + lootTableCodes: z.array(z.string()).optional(), + interruptRules: z.array(z.string()).optional(), + traits: z.array(z.string()).optional(), + sourcePage: z.number().int().positive(), + mvp: z.boolean(), +}); + +export const exitTemplateSchema = z.object({ + direction: z.enum(["north", "east", "south", "west"]).optional(), + exitType: z.enum(["open", "door", "locked", "secret", "shaft", "stairs"]), + destinationLevel: z.number().int().optional(), +}); + +export const roomTemplateSchema = z.object({ + id: z.string().min(1), + level: z.number().int().positive(), + roomClass: z.enum(["normal", "small", "large", "special", "start", "stairs"]), + tableCode: z.string().min(1), + tableEntryKey: z.string().min(1), + title: z.string().min(1), + text: z.string().optional(), + dimensions: z + .object({ + width: z.number().int().positive(), + height: z.number().int().positive(), + }) + .optional(), + exits: z.array(exitTemplateSchema).optional(), + encounterRefs: z.array(contentReferenceSchema).optional(), + objectRefs: z.array(contentReferenceSchema).optional(), + tags: z.array(z.string()), + mvp: z.boolean(), +}); + +export const townServiceDefinitionSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + serviceType: z.enum(["market", "temple", "tavern", "healer", "smith", "quest"]), + tableCodes: z.array(z.string()).optional(), + costRules: z.array(z.string()).optional(), + effects: z.array(ruleEffectSchema).optional(), + mvp: z.boolean(), +}); + +export const contentPackSchema = z.object({ + version: z.string().min(1), + sourceBooks: z.array(z.string()), + tables: z.array(tableDefinitionSchema), + weapons: z.array(weaponDefinitionSchema), + manoeuvres: z.array(manoeuvreDefinitionSchema), + armour: z.array(armourDefinitionSchema), + items: z.array(itemDefinitionSchema), + potions: z.array(potionDefinitionSchema), + scrolls: z.array(scrollDefinitionSchema), + creatures: z.array(creatureDefinitionSchema), + roomTemplates: z.array(roomTemplateSchema), + townServices: z.array(townServiceDefinitionSchema), +}); diff --git a/src/schemas/rules.ts b/src/schemas/rules.ts new file mode 100644 index 0000000..b9cabdc --- /dev/null +++ b/src/schemas/rules.ts @@ -0,0 +1,71 @@ +import { z } from "zod"; + +export const diceKindSchema = z.enum(["d3", "d6", "2d6", "d66"]); +export const contentReferenceTypeSchema = z.enum([ + "table", + "weapon", + "manoeuvre", + "armour", + "item", + "potion", + "scroll", + "creature", + "room", + "service", +]); + +export const contentReferenceSchema = z.object({ + type: contentReferenceTypeSchema, + id: z.string().min(1), +}); + +export const ruleEffectSchema = z.object({ + type: z.enum([ + "gain-xp", + "gain-gold", + "heal", + "take-damage", + "modify-shift", + "modify-discipline", + "modify-precision", + "apply-status", + "remove-status", + "add-item", + "remove-item", + "start-combat", + "reveal-exit", + "move-level", + "log-only", + ]), + amount: z.number().optional(), + statusId: z.string().optional(), + target: z.enum(["self", "enemy", "room", "campaign"]).optional(), + referenceId: z.string().optional(), + notes: z.string().optional(), +}); + +export const rollResultSchema = z.object({ + diceKind: diceKindSchema, + rolls: z.array(z.number().int()), + primary: z.number().int().optional(), + secondary: z.number().int().optional(), + total: z.number().int().optional(), + modifier: z.number().int().optional(), + modifiedTotal: z.number().int().optional(), + clamped: z.boolean().optional(), +}); + +export const logEntrySchema = z.object({ + id: z.string().min(1), + at: z.string().min(1), + type: z.enum(["system", "roll", "combat", "loot", "room", "town", "progression"]), + text: z.string().min(1), + relatedIds: z.array(z.string()).optional(), +}); + +export const actionResolutionSchema = z.object({ + success: z.boolean(), + effects: z.array(ruleEffectSchema), + logEntries: z.array(logEntrySchema), + warnings: z.array(z.string()).optional(), +}); diff --git a/src/schemas/state.ts b/src/schemas/state.ts new file mode 100644 index 0000000..6b351cb --- /dev/null +++ b/src/schemas/state.ts @@ -0,0 +1,217 @@ +import { z } from "zod"; + +import { logEntrySchema, rollResultSchema, ruleEffectSchema } from "./rules"; + +export const statusInstanceSchema = z.object({ + id: z.string().min(1), + source: z.string().optional(), + duration: z.enum(["round", "combat", "room", "run", "permanent"]).optional(), + value: z.number().optional(), + notes: z.string().optional(), +}); + +export const inventoryEntrySchema = z.object({ + definitionId: z.string().min(1), + quantity: z.number().int().nonnegative(), + identified: z.boolean().optional(), + charges: z.number().int().optional(), + notes: z.string().optional(), +}); + +export const inventoryStateSchema = z.object({ + carried: z.array(inventoryEntrySchema), + equipped: z.array(inventoryEntrySchema), + stored: z.array(inventoryEntrySchema), + currency: z.object({ + gold: z.number().int().nonnegative(), + }), + rationCount: z.number().int().nonnegative(), + lightSources: z.array(inventoryEntrySchema), +}); + +export const adventurerStateSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + level: z.number().int().positive(), + xp: z.number().int().nonnegative(), + hp: z.object({ + current: z.number().int(), + max: z.number().int().positive(), + base: z.number().int().positive(), + }), + stats: z.object({ + shift: z.number().int(), + discipline: z.number().int(), + precision: z.number().int(), + }), + weaponId: z.string().min(1), + manoeuvreIds: z.array(z.string()), + armourId: z.string().optional(), + favour: z.record(z.string(), z.number().int()), + statuses: z.array(statusInstanceSchema), + inventory: inventoryStateSchema, + progressionFlags: z.array(z.string()), +}); + +export const townStateSchema = z.object({ + visits: z.number().int().nonnegative(), + knownServices: z.array(z.string()), + stash: z.array(inventoryEntrySchema), + pendingSales: z.array(inventoryEntrySchema), + serviceFlags: z.array(z.string()), +}); + +export const questStateSchema = z.object({ + id: z.string().min(1), + title: z.string().min(1), + status: z.enum(["available", "active", "completed", "failed"]), + progressFlags: z.array(z.string()), + rewardText: z.string().optional(), +}); + +export const runSummarySchema = z.object({ + runId: z.string().min(1), + startedAt: z.string().min(1), + endedAt: z.string().optional(), + deepestLevel: z.number().int().positive(), + roomsVisited: z.number().int().nonnegative(), + creaturesDefeated: z.array(z.string()), + xpGained: z.number().int().nonnegative(), + treasureValue: z.number().int().nonnegative(), + outcome: z.enum(["escaped", "defeated", "saved-in-progress"]), +}); + +export const campaignStateSchema = z.object({ + id: z.string().min(1), + createdAt: z.string().min(1), + updatedAt: z.string().min(1), + rulesVersion: z.string().min(1), + contentVersion: z.string().min(1), + adventurer: adventurerStateSchema, + unlockedLevels: z.array(z.number().int().positive()), + completedLevels: z.array(z.number().int().positive()), + townState: townStateSchema, + questState: z.array(questStateSchema), + campaignFlags: z.array(z.string()), + runHistory: z.array(runSummarySchema), +}); + +export const roomExitStateSchema = z.object({ + id: z.string().min(1), + direction: z.enum(["north", "east", "south", "west"]), + exitType: z.enum(["open", "door", "locked", "secret", "shaft", "stairs"]), + discovered: z.boolean(), + traversable: z.boolean(), + leadsToRoomId: z.string().optional(), + destinationLevel: z.number().int().optional(), +}); + +export const encounterStateSchema = z.object({ + id: z.string().min(1), + sourceTableCode: z.string().optional(), + creatureIds: z.array(z.string()), + resolved: z.boolean(), + surprise: z.boolean().optional(), + rewardPending: z.boolean().optional(), +}); + +export const roomObjectStateSchema = z.object({ + id: z.string().min(1), + objectType: z.enum(["container", "altar", "corpse", "hazard", "feature", "quest"]), + sourceTableCode: z.string().optional(), + interacted: z.boolean(), + hidden: z.boolean().optional(), + effects: z.array(ruleEffectSchema).optional(), + notes: z.string().optional(), +}); + +export const roomStateSchema = z.object({ + id: z.string().min(1), + level: z.number().int().positive(), + templateId: z.string().optional(), + position: z.object({ + x: z.number().int(), + y: z.number().int(), + }), + dimensions: z.object({ + width: z.number().int().positive(), + height: z.number().int().positive(), + }), + roomClass: z.enum(["normal", "small", "large", "special", "start", "stairs"]), + exits: z.array(roomExitStateSchema), + discovery: z.object({ + generated: z.boolean(), + entered: z.boolean(), + cleared: z.boolean(), + searched: z.boolean(), + }), + encounter: encounterStateSchema.optional(), + objects: z.array(roomObjectStateSchema), + notes: z.array(z.string()), + flags: z.array(z.string()), +}); + +export const dungeonLevelStateSchema = z.object({ + level: z.number().int().positive(), + themeName: z.string().optional(), + rooms: z.record(z.string(), roomStateSchema), + discoveredRoomOrder: z.array(z.string()), + stairsUpRoomId: z.string().optional(), + stairsDownRoomId: z.string().optional(), + secretDoorUsed: z.boolean().optional(), + exhaustedExitSearch: z.boolean().optional(), +}); + +export const dungeonStateSchema = z.object({ + levels: z.record(z.string(), dungeonLevelStateSchema), + revealedPercentByLevel: z.record(z.string(), z.number()), + globalFlags: z.array(z.string()), +}); + +export const combatantStateSchema = z.object({ + id: z.string().min(1), + name: z.string().min(1), + sourceDefinitionId: z.string().optional(), + hpCurrent: z.number().int(), + hpMax: z.number().int().positive(), + shift: z.number().int(), + discipline: z.number().int(), + precision: z.number().int(), + armourValue: z.number().int().optional(), + statuses: z.array(statusInstanceSchema), + traits: z.array(z.string()), +}); + +export const interruptStateSchema = z.object({ + source: z.enum(["player", "enemy"]), + trigger: z.string().min(1), + effectText: z.string().min(1), + resolved: z.boolean(), +}); + +export const combatStateSchema = z.object({ + id: z.string().min(1), + round: z.number().int().positive(), + actingSide: z.enum(["player", "enemy"]), + fatigueDie: z.number().int().optional(), + player: combatantStateSchema, + enemies: z.array(combatantStateSchema), + selectedManoeuvreId: z.string().optional(), + lastRoll: rollResultSchema.optional(), + pendingInterrupt: interruptStateSchema.optional(), + combatLog: z.array(logEntrySchema), +}); + +export const runStateSchema = z.object({ + id: z.string().min(1), + campaignId: z.string().min(1), + status: z.enum(["active", "paused", "completed", "failed"]), + startedAt: z.string().min(1), + currentLevel: z.number().int().positive(), + currentRoomId: z.string().optional(), + dungeon: dungeonStateSchema, + adventurerSnapshot: adventurerStateSchema, + activeCombat: combatStateSchema.optional(), + log: z.array(logEntrySchema), + pendingEffects: z.array(ruleEffectSchema), +}); diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..aa16e3f --- /dev/null +++ b/src/styles.css @@ -0,0 +1,130 @@ +:root { + font-family: "Segoe UI", "Aptos", sans-serif; + color: #f3f1e8; + background: + radial-gradient(circle at top, rgba(179, 121, 59, 0.35), transparent 30%), + linear-gradient(180deg, #17130f 0%, #0d0b09 100%); + line-height: 1.5; + font-weight: 400; + color-scheme: dark; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; +} + +#root { + min-height: 100vh; +} + +.app-shell { + width: min(1100px, calc(100% - 2rem)); + margin: 0 auto; + padding: 3rem 0 4rem; +} + +.hero { + padding: 2rem; + border: 1px solid rgba(243, 241, 232, 0.16); + background: rgba(21, 18, 14, 0.72); + box-shadow: 0 30px 80px rgba(0, 0, 0, 0.35); + backdrop-filter: blur(12px); +} + +.eyebrow { + margin: 0 0 0.75rem; + text-transform: uppercase; + letter-spacing: 0.14em; + color: #d8b27a; + font-size: 0.8rem; +} + +.hero h1 { + margin: 0; + font-size: clamp(2.5rem, 7vw, 4.5rem); + line-height: 0.95; +} + +.lede { + width: min(55ch, 100%); + margin: 1.25rem 0 0; + color: rgba(243, 241, 232, 0.82); + font-size: 1.05rem; +} + +.panel-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 1rem; + margin-top: 1rem; +} + +.panel { + padding: 1.25rem; + border: 1px solid rgba(243, 241, 232, 0.14); + background: rgba(29, 24, 19, 0.82); +} + +.panel h2 { + margin-top: 0; + margin-bottom: 0.75rem; + font-size: 1rem; + color: #f6d49e; +} + +.panel ul, +.panel ol { + margin: 0; + padding-left: 1.1rem; +} + +.panel li + li { + margin-top: 0.45rem; +} + +.stats { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.75rem; + margin: 0; +} + +.stats div { + padding: 0.9rem; + background: rgba(243, 241, 232, 0.05); +} + +.stats dt { + color: rgba(243, 241, 232, 0.62); + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.stats dd { + margin: 0.3rem 0 0; + font-size: 1.7rem; + font-weight: 700; +} + +@media (max-width: 640px) { + .app-shell { + width: min(100% - 1rem, 1100px); + padding-top: 1rem; + } + + .hero, + .panel { + padding: 1rem; + } +} diff --git a/src/types/content.ts b/src/types/content.ts new file mode 100644 index 0000000..4ef1ecc --- /dev/null +++ b/src/types/content.ts @@ -0,0 +1,215 @@ +import type { ContentReference, DiceKind, RuleEffect } from "./rules"; + +export type TableCategory = + | "generic" + | "random-list" + | "loot" + | "level" + | "optional" + | "town" + | "room"; + +export type TableEntry = { + key: string; + min?: number; + max?: number; + exact?: number; + d66?: number; + label: string; + text?: string; + effects?: RuleEffect[]; + references?: ContentReference[]; +}; + +export type TableDefinition = { + id: string; + code: string; + name: string; + category: TableCategory; + level?: number; + page: number; + diceKind: DiceKind; + usesModifiedRangesRule?: boolean; + entries: TableEntry[]; + notes?: string[]; + mvp: boolean; +}; + +export type WeaponCategory = "melee" | "ranged"; + +export type WeaponDefinition = { + id: string; + name: string; + category: WeaponCategory; + handedness: "one-handed" | "two-handed"; + baseDamage: number; + allowedManoeuvreIds: string[]; + tags: string[]; + startingOption: boolean; +}; + +export type ManoeuvreDefinition = { + id: string; + name: string; + weaponCategories: WeaponCategory[]; + shiftCost?: number; + disciplineModifier?: number; + precisionModifier?: number; + damageModifier?: number; + exactStrikeBonus?: boolean; + interruptRule?: string; + effectText?: string; + mvp: boolean; +}; + +export type ArmourDefinition = { + id: string; + name: string; + armourValue: number; + penalties?: { + shift?: number; + discipline?: number; + precision?: number; + }; + deflectionRule?: string; + startingOption: boolean; + valueGp?: number; + mvp: boolean; +}; + +export type ItemType = + | "gear" + | "treasure" + | "quest" + | "herb" + | "rune" + | "misc" + | "ration" + | "light-source"; + +export type ItemDefinition = { + id: string; + name: string; + itemType: ItemType; + stackable: boolean; + consumable: boolean; + valueGp?: number; + weight?: number; + rulesText?: string; + effects?: RuleEffect[]; + mvp: boolean; +}; + +export type PotionUseTiming = "combat" | "exploration" | "town" | "any"; + +export type PotionDefinition = { + id: string; + name: string; + tableSource: string; + useTiming: PotionUseTiming; + effects: RuleEffect[]; + valueGp?: number; + mvp: boolean; +}; + +export type ScrollDefinition = { + id: string; + name: string; + tableSource: string; + castCheck?: { + diceKind: Extract; + successMin?: number; + successMax?: number; + }; + onSuccess: RuleEffect[]; + onFailureTableCode?: string; + valueGp?: number; + startingOption: boolean; + mvp: boolean; +}; + +export type CreatureDefinition = { + id: string; + name: string; + level: number; + category: string; + hp: number; + attackProfile: { + discipline: number; + precision: number; + damage: number; + numberAppearing?: string; + }; + defenceProfile?: { + armour?: number; + specialRules?: string[]; + }; + xpReward?: number; + lootTableCodes?: string[]; + interruptRules?: string[]; + traits?: string[]; + sourcePage: number; + mvp: boolean; +}; + +export type ExitType = "open" | "door" | "locked" | "secret" | "shaft" | "stairs"; + +export type ExitTemplate = { + direction?: "north" | "east" | "south" | "west"; + exitType: ExitType; + destinationLevel?: number; +}; + +export type RoomClass = "normal" | "small" | "large" | "special" | "start" | "stairs"; + +export type RoomTemplate = { + id: string; + level: number; + roomClass: RoomClass; + tableCode: string; + tableEntryKey: string; + title: string; + text?: string; + dimensions?: { + width: number; + height: number; + }; + exits?: ExitTemplate[]; + encounterRefs?: ContentReference[]; + objectRefs?: ContentReference[]; + tags: string[]; + mvp: boolean; +}; + +export type TownServiceType = + | "market" + | "temple" + | "tavern" + | "healer" + | "smith" + | "quest"; + +export type TownServiceDefinition = { + id: string; + name: string; + serviceType: TownServiceType; + tableCodes?: string[]; + costRules?: string[]; + effects?: RuleEffect[]; + mvp: boolean; +}; + +export type ContentPack = { + version: string; + sourceBooks: string[]; + tables: TableDefinition[]; + weapons: WeaponDefinition[]; + manoeuvres: ManoeuvreDefinition[]; + armour: ArmourDefinition[]; + items: ItemDefinition[]; + potions: PotionDefinition[]; + scrolls: ScrollDefinition[]; + creatures: CreatureDefinition[]; + roomTemplates: RoomTemplate[]; + townServices: TownServiceDefinition[]; +}; diff --git a/src/types/rules.ts b/src/types/rules.ts new file mode 100644 index 0000000..34d73c6 --- /dev/null +++ b/src/types/rules.ts @@ -0,0 +1,81 @@ +export type DiceKind = "d3" | "d6" | "2d6" | "d66"; + +export type ContentReferenceType = + | "table" + | "weapon" + | "manoeuvre" + | "armour" + | "item" + | "potion" + | "scroll" + | "creature" + | "room" + | "service"; + +export type ContentReference = { + type: ContentReferenceType; + id: string; +}; + +export type RuleEffectType = + | "gain-xp" + | "gain-gold" + | "heal" + | "take-damage" + | "modify-shift" + | "modify-discipline" + | "modify-precision" + | "apply-status" + | "remove-status" + | "add-item" + | "remove-item" + | "start-combat" + | "reveal-exit" + | "move-level" + | "log-only"; + +export type RuleEffectTarget = "self" | "enemy" | "room" | "campaign"; + +export type RuleEffect = { + type: RuleEffectType; + amount?: number; + statusId?: string; + target?: RuleEffectTarget; + referenceId?: string; + notes?: string; +}; + +export type RollResult = { + diceKind: DiceKind; + rolls: number[]; + primary?: number; + secondary?: number; + total?: number; + modifier?: number; + modifiedTotal?: number; + clamped?: boolean; +}; + +export type LogEntryType = + | "system" + | "roll" + | "combat" + | "loot" + | "room" + | "town" + | "progression"; + +export type LogEntry = { + id: string; + at: string; + type: LogEntryType; + text: string; + relatedIds?: string[]; +}; + +export type ActionResolution = { + success: boolean; + effects: RuleEffect[]; + logEntries: LogEntry[]; + warnings?: string[]; +}; diff --git a/src/types/state.ts b/src/types/state.ts new file mode 100644 index 0000000..d98200d --- /dev/null +++ b/src/types/state.ts @@ -0,0 +1,218 @@ +import type { ExitType, RoomClass } from "./content"; +import type { LogEntry, RollResult, RuleEffect } from "./rules"; + +export type StatusDuration = "round" | "combat" | "room" | "run" | "permanent"; + +export type StatusInstance = { + id: string; + source?: string; + duration?: StatusDuration; + value?: number; + notes?: string; +}; + +export type InventoryEntry = { + definitionId: string; + quantity: number; + identified?: boolean; + charges?: number; + notes?: string; +}; + +export type InventoryState = { + carried: InventoryEntry[]; + equipped: InventoryEntry[]; + stored: InventoryEntry[]; + currency: { + gold: number; + }; + rationCount: number; + lightSources: InventoryEntry[]; +}; + +export type AdventurerState = { + id: string; + name: string; + level: number; + xp: number; + hp: { + current: number; + max: number; + base: number; + }; + stats: { + shift: number; + discipline: number; + precision: number; + }; + weaponId: string; + manoeuvreIds: string[]; + armourId?: string; + favour: Record; + statuses: StatusInstance[]; + inventory: InventoryState; + progressionFlags: string[]; +}; + +export type TownState = { + visits: number; + knownServices: string[]; + stash: InventoryEntry[]; + pendingSales: InventoryEntry[]; + serviceFlags: string[]; +}; + +export type QuestState = { + id: string; + title: string; + status: "available" | "active" | "completed" | "failed"; + progressFlags: string[]; + rewardText?: string; +}; + +export type RunSummary = { + runId: string; + startedAt: string; + endedAt?: string; + deepestLevel: number; + roomsVisited: number; + creaturesDefeated: string[]; + xpGained: number; + treasureValue: number; + outcome: "escaped" | "defeated" | "saved-in-progress"; +}; + +export type CampaignState = { + id: string; + createdAt: string; + updatedAt: string; + rulesVersion: string; + contentVersion: string; + adventurer: AdventurerState; + unlockedLevels: number[]; + completedLevels: number[]; + townState: TownState; + questState: QuestState[]; + campaignFlags: string[]; + runHistory: RunSummary[]; +}; + +export type RoomExitState = { + id: string; + direction: "north" | "east" | "south" | "west"; + exitType: ExitType; + discovered: boolean; + traversable: boolean; + leadsToRoomId?: string; + destinationLevel?: number; +}; + +export type EncounterState = { + id: string; + sourceTableCode?: string; + creatureIds: string[]; + resolved: boolean; + surprise?: boolean; + rewardPending?: boolean; +}; + +export type RoomObjectState = { + id: string; + objectType: "container" | "altar" | "corpse" | "hazard" | "feature" | "quest"; + sourceTableCode?: string; + interacted: boolean; + hidden?: boolean; + effects?: RuleEffect[]; + notes?: string; +}; + +export type RoomState = { + id: string; + level: number; + templateId?: string; + position: { + x: number; + y: number; + }; + dimensions: { + width: number; + height: number; + }; + roomClass: RoomClass; + exits: RoomExitState[]; + discovery: { + generated: boolean; + entered: boolean; + cleared: boolean; + searched: boolean; + }; + encounter?: EncounterState; + objects: RoomObjectState[]; + notes: string[]; + flags: string[]; +}; + +export type DungeonLevelState = { + level: number; + themeName?: string; + rooms: Record; + discoveredRoomOrder: string[]; + stairsUpRoomId?: string; + stairsDownRoomId?: string; + secretDoorUsed?: boolean; + exhaustedExitSearch?: boolean; +}; + +export type DungeonState = { + levels: Record; + revealedPercentByLevel: Record; + globalFlags: string[]; +}; + +export type CombatantState = { + id: string; + name: string; + sourceDefinitionId?: string; + hpCurrent: number; + hpMax: number; + shift: number; + discipline: number; + precision: number; + armourValue?: number; + statuses: StatusInstance[]; + traits: string[]; +}; + +export type InterruptState = { + source: "player" | "enemy"; + trigger: string; + effectText: string; + resolved: boolean; +}; + +export type CombatState = { + id: string; + round: number; + actingSide: "player" | "enemy"; + fatigueDie?: number; + player: CombatantState; + enemies: CombatantState[]; + selectedManoeuvreId?: string; + lastRoll?: RollResult; + pendingInterrupt?: InterruptState; + combatLog: LogEntry[]; +}; + +export type RunState = { + id: string; + campaignId: string; + status: "active" | "paused" | "completed" | "failed"; + startedAt: string; + currentLevel: number; + currentRoomId?: string; + dungeon: DungeonState; + adventurerSnapshot: AdventurerState; + activeCombat?: CombatState; + log: LogEntry[]; + pendingEffects: RuleEffect[]; +}; diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..94dc86a --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "Bundler", + "allowImportingTsExtensions": false, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..a2d0e4c --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "allowSyntheticDefaultImports": true, + "types": ["node"] + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..ad58148 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import path from "node:path"; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, +});