commit 78260e30534d8ccf557a8d474ee8cb0adc306dc5 Author: kimsongwu Date: Tue Jul 28 20:42:25 2026 +0800 [verified] feat: launch Star Seed Vue game diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..efa7db2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +node_modules/ +coverage/ +.vite/ +*.log +.DS_Store +.env +.env.* +!.env.example +# dist/ is intentionally committed: dist/index.html is the standalone release. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..24645b8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 kimsongwu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c8d78ac --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# 星种:长夜之前 + +一款基于 Vue 3 的单机文明演化增量游戏。从无名潮池中的复制分子出发,跨越陆冠、炉城与星种四个纪元,在暮阳风暴抵达前完成生命档案、磁场天幕与星种方舟。 + +![游戏画面](docs/star-seed-game.png) + +## 直接游玩 + +无需安装、无需后端、无需联网: + +1. 下载仓库; +2. 直接双击 `dist/index.html`; +3. 游戏会在浏览器中运行。 + +`dist/index.html` 是 Vite 编译后的单文件发行版,JavaScript 与 CSS 均已内联。它已通过 `file://` 方式实际加载和点击验证。 + +## 核心内容 + +- 四个线性纪元:潮池、陆冠、炉城、星种; +- 12 个科学与文明里程碑; +- 点击采集与自动增长组成的增量循环; +- 本地自动存档与最多四小时离线收益; +- 生态平衡、生命档案、磁场天幕三项结局指标; +- 唯一正式结局“ 两片海的黎明 ”与新世界播种循环; +- 桌面端与移动端自适应界面; +- 无图片、字体、接口等运行时网络依赖。 + +## Vue、Bit 与动画组件 + +项目参考 [Bit 的 Vue 组件开发说明](https://bit.dev/docs/vue-intro),将界面拆分为职责单一、输入输出明确的 Vue 单文件组件: + +- `EvolutionScene.vue`:行星、轨道、星空与主点击交互; +- `MilestoneRail.vue`:纪元里程碑、资源门槛与完成状态; +- `SignalLog.vue`:文明叙事信号流; +- `EndingOverlay.vue`:最终发射结局。 + +动画层使用 `motion-v`,为组件提供弹簧入场、手势按压、阶段切换和结局转场;背景呼吸、极光、扫描、城市灯光与发射轨迹使用本地 CSS 动画。组件边界及 Bit 跟踪建议见 `docs/bit-components.md`。 + +## 本地开发 + +要求 Node.js 20.19+ 或 22.12+。 + +```bash +npm install +npm run dev +``` + +质量检查与发行构建: + +```bash +npm test +npm run build +npm run verify:dist +npm audit +``` + +构建会重新生成可直接打开的 `dist/index.html`。 + +## 项目结构 + +```text +star-seed/ +├── dist/index.html # 已编译、自包含、可直接运行的游戏入口 +├── docs/ # 组件说明与游戏截图 +├── src/components/ # Bit 风格的独立 Vue 动画组件 +├── src/game/gameState.js # 纯函数游戏状态与规则 +├── src/App.vue # 应用编排、存档与游戏循环 +├── src/styles.css # 科幻视觉系统与响应式布局 +└── src/**/*.test.js # 状态与界面测试 +``` + +## 技术栈 + +- Vue 3.5 +- Vite 8 +- Motion for Vue (`motion-v`) +- Vitest + Vue Test Utils +- Vite SingleFile + +## 许可 + +MIT diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..e0b3150 --- /dev/null +++ b/dist/index.html @@ -0,0 +1,25 @@ + + + + + + + + + 星种:长夜之前 + + + + +
+ + diff --git a/docs/bit-components.md b/docs/bit-components.md new file mode 100644 index 0000000..cd3a9f7 --- /dev/null +++ b/docs/bit-components.md @@ -0,0 +1,38 @@ +# Bit-ready Vue 动画组件 + +本项目按照 Bit 文档所强调的“独立、可组合组件”方式组织视觉层,同时保留标准 Vite 构建,以确保下载后可以直接运行。 + +## 组件边界 + +### EvolutionScene + +- 输入:当前纪元、资源数、点击强度、自动产率、发射状态; +- 输出:`pulse` 事件; +- 动画:Motion 弹簧入场和按压反馈;CSS 行星呼吸、城市灯光、星种发射轨迹; +- 不读取存档,不直接修改游戏状态。 + +### MilestoneRail + +- 输入:纪元配置、资源数、已完成里程碑; +- 输出:`purchase(id)` 事件; +- 动画:Motion 交错入场和悬停位移; +- 所有购买合法性仍由 `gameState.js` 判定。 + +### SignalLog + +- 输入:叙事信号数组; +- 动画:新信号淡入; +- 无全局状态依赖。 + +### EndingOverlay + +- 输入:结局等级; +- 输出:`restart` 事件; +- 动画:全屏结局转场; +- 不负责创建下一轮状态。 + +## 为什么保持 Vite 主构建 + +Bit 可用于后续独立跟踪、预览和发布这些组件;游戏发行仍使用 Vite SingleFile,把全部组件、动画和样式内联到 `dist/index.html`。因此玩家不需要 Bit CLI、Node.js 或网络连接。 + +若团队后续采用 Bit,可在现有项目执行官方文档中的 `bit init`,再将 `src/components` 下的组件按各自目录边界跟踪。游戏规则层与发行构建无需改变。 diff --git a/docs/star-seed-game.png b/docs/star-seed-game.png new file mode 100644 index 0000000..65244d6 Binary files /dev/null and b/docs/star-seed-game.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..216d9a7 --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + + + 星种:长夜之前 + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..226336e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2220 @@ +{ + "name": "star-seed", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "star-seed", + "version": "1.0.0", + "dependencies": { + "motion-v": "^2.3.0", + "vue": "^3.5.40" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.8", + "@vue/test-utils": "^2.2.7", + "jsdom": "^30.0.0", + "vite": "^8.1.5", + "vite-plugin-singlefile": "^2.3.3", + "vitest": "^4.1.10" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-6.0.5.tgz", + "integrity": "sha512-mbhpPMmnw/kwW19aRNmSUl1QzLbdGo1SCuE49BT98MNwqF6zaHb3o2owssFc/PEO/4t2UjqtCNwocuDtJornzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.2.1", + "@csstools/css-color-parser": "^4.1.9", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.5.2" + }, + "engines": { + "node": "^22.13.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-8.3.0.tgz", + "integrity": "sha512-UJLfKXBhrc8i1vH2eJXuYQMwlsLKWFw3O+CPqXSuVEiikeAim3UgrfWX0k4tA/X8cRFM8iZ7OaqBokFGbYusdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.5.2" + }, + "engines": { + "node": "^22.13.0 || >=24.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz", + "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz", + "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.0", + "@csstools/css-calc": "^3.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", + "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "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==", + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT", + "peer": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", + "integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.40.tgz", + "integrity": "sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/shared": "3.5.40", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.40.tgz", + "integrity": "sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.40.tgz", + "integrity": "sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.5.40", + "@vue/compiler-dom": "3.5.40", + "@vue/compiler-ssr": "3.5.40", + "@vue/shared": "3.5.40", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.40.tgz", + "integrity": "sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.40.tgz", + "integrity": "sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.40.tgz", + "integrity": "sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.40.tgz", + "integrity": "sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.40", + "@vue/runtime-core": "3.5.40", + "@vue/shared": "3.5.40", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.40.tgz", + "integrity": "sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.40", + "@vue/runtime-dom": "3.5.40", + "@vue/shared": "3.5.40" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.40.tgz", + "integrity": "sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==", + "license": "MIT" + }, + "node_modules/@vue/test-utils": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.2.7.tgz", + "integrity": "sha512-BMuoruUFTEqhLoOgsMcgNVMiByYbfHCKGr2C4CPdGtz/affUtDVX5zr1RnPuq0tYSiaqq+Enw5voUpG6JY8Q7g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.1" + } + }, + "node_modules/@vueuse/core": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.3.0.tgz", + "integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "14.3.0", + "@vueuse/shared": "14.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.3.0.tgz", + "integrity": "sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.3.0.tgz", + "integrity": "sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "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/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "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/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "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/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/framer-motion": { + "version": "12.42.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.2.tgz", + "integrity": "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.42.2", + "motion-utils": "^12.39.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "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/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.0.0.tgz", + "integrity": "sha512-JQHfRGmmKmaZoUAvIgff5jjG/0SzTQlGz8c7t72KzBzo8ZULEjAjnYE0sNwBOUA4QtWwYE2xoYitg8NFsmiYxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^6.0.5", + "@asamuzakjp/dom-selector": "^8.2.5", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.6", + "@exodus/bytes": "^1.15.1", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.5.2", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.2", + "undici": "^8.7.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^17.1.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "canvas": "^3.2.3" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "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.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "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/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/motion-dom": { + "version": "12.42.2", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.2.tgz", + "integrity": "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.39.0" + } + }, + "node_modules/motion-utils": { + "version": "12.39.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz", + "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==", + "license": "MIT" + }, + "node_modules/motion-v": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/motion-v/-/motion-v-2.3.0.tgz", + "integrity": "sha512-J0CCfXtICCni9RjotDUBOs57xNpYI9yyBSohEOxaRHrmjwOtlw291fhRu/mdgEdSasys96R028YDDOAtWBbRaA==", + "license": "MIT", + "dependencies": { + "framer-motion": "^12.40.0", + "hey-listen": "^1.0.8", + "motion-dom": "^12.40.0", + "motion-utils": "^12.39.0" + }, + "peerDependencies": { + "@vueuse/core": ">=10.0.0", + "vue": ">=3.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "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/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "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==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "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.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.4.9", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.9.tgz", + "integrity": "sha512-3kZ8wQQ/k5DrChD4X4FVvr2D7E5uoRgAqkPyLpSCGUvqOvqu+JEdr3mwMUaVWb+vMHZaKhF5fp2PBigKsui7hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.9" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.9", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.9.tgz", + "integrity": "sha512-DxKfPBI52p2msTEu7MPhdpdDTBhhVQg1a/8PjQckeyAvO13eMYElX545grIp6nnTGIMZlRvFZPvFhvI/WIz2Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/undici": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.9.0.tgz", + "integrity": "sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "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", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.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 + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "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/vite-plugin-singlefile": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.3.tgz", + "integrity": "sha512-XVnGH0QzbOa8fxRSsHdCarVN1BSBXNi7uLMQYlrGRN5apdHkk62XQWRJhVever0lnfuyBkwn+kvVChdm/OoOUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">18.0.0" + }, + "peerDependencies": { + "rollup": "^4.59.0", + "vite": "^5.4.21 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vue": { + "version": "3.5.40", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.40.tgz", + "integrity": "sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.40", + "@vue/compiler-sfc": "3.5.40", + "@vue/runtime-dom": "3.5.40", + "@vue/server-renderer": "3.5.40", + "@vue/shared": "3.5.40" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.1.0.tgz", + "integrity": "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.15.1", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^22.14.0 || >=24.0.0" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f4b47b9 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "star-seed", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --host 0.0.0.0", + "build": "vite build", + "preview": "vite preview --host 0.0.0.0", + "test": "vitest run", + "test:watch": "vitest", + "verify:dist": "node tests/verify-dist.mjs" + }, + "dependencies": { + "motion-v": "^2.3.0", + "vue": "^3.5.40" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.8", + "@vue/test-utils": "^2.2.7", + "jsdom": "^30.0.0", + "vite": "^8.1.5", + "vite-plugin-singlefile": "^2.3.3", + "vitest": "^4.1.10" + } +} diff --git a/src/App.test.js b/src/App.test.js new file mode 100644 index 0000000..866ba43 --- /dev/null +++ b/src/App.test.js @@ -0,0 +1,26 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' +import App from './App.vue' + +describe('星种游戏界面', () => { + it('展示当前纪元,并允许玩家采集第一份生命质', async () => { + const wrapper = mount(App, { + global: { + stubs: { + AnimatePresence: { template: '
' }, + }, + }, + }) + + expect(wrapper.text()).toContain('星种:长夜之前') + expect(wrapper.text()).toContain('潮池纪元') + expect(wrapper.text()).toContain('消耗 12') + expect(wrapper.get('.epoch-button').text()).toContain('还需 3 项') + expect(wrapper.get('[data-test="essence"]').text()).toContain('0') + + await wrapper.get('[data-test="pulse"]').trigger('click') + + expect(wrapper.get('[data-test="essence"]').text()).toContain('1') + wrapper.unmount() + }) +}) diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..be71950 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,322 @@ + + + diff --git a/src/components/EndingOverlay.test.js b/src/components/EndingOverlay.test.js new file mode 100644 index 0000000..b8ee307 --- /dev/null +++ b/src/components/EndingOverlay.test.js @@ -0,0 +1,19 @@ +import { mount } from '@vue/test-utils' +import { nextTick } from 'vue' +import { describe, expect, it } from 'vitest' +import EndingOverlay from './EndingOverlay.vue' + +describe('结局对话框', () => { + it('出现时把键盘焦点移到唯一操作,并在 Tab 时保持焦点', async () => { + const wrapper = mount(EndingOverlay, { props: { grade: '双生花园' }, attachTo: document.body }) + await nextTick() + + const restart = wrapper.get('button').element + expect(document.activeElement).toBe(restart) + + document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', bubbles: true })) + expect(document.activeElement).toBe(restart) + + wrapper.unmount() + }) +}) diff --git a/src/components/EndingOverlay.vue b/src/components/EndingOverlay.vue new file mode 100644 index 0000000..a7c6167 --- /dev/null +++ b/src/components/EndingOverlay.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/components/EvolutionScene.vue b/src/components/EvolutionScene.vue new file mode 100644 index 0000000..70747e9 --- /dev/null +++ b/src/components/EvolutionScene.vue @@ -0,0 +1,82 @@ + + + diff --git a/src/components/MilestoneRail.vue b/src/components/MilestoneRail.vue new file mode 100644 index 0000000..5ef9c69 --- /dev/null +++ b/src/components/MilestoneRail.vue @@ -0,0 +1,55 @@ + + + diff --git a/src/components/SignalLog.vue b/src/components/SignalLog.vue new file mode 100644 index 0000000..51046f8 --- /dev/null +++ b/src/components/SignalLog.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/game/gameState.js b/src/game/gameState.js new file mode 100644 index 0000000..5c00519 --- /dev/null +++ b/src/game/gameState.js @@ -0,0 +1,245 @@ +const clamp = (value, min, max) => Math.min(max, Math.max(min, value)) + +export const PHASES = [ + { + id: 'tidepool', + name: '潮池纪元', + chapter: 'CHAPTER 01', + resourceName: '生命质', + glyph: '◌', + accent: '#58f3c4', + headline: '在无名之海,学会记住自己', + story: '雷暴越过汐原的海面。某些分子开始复制,而你在复制之间醒来。', + transition: '陆冠纪元开始:生命第一次离开海洋。', + milestones: [ + { id: 'membrane', name: '脂质膜', description: '为脆弱的化学反应围起第一道边界。', cost: 12, effect: { auto: 0.45, insight: 1 }, signal: '边界诞生,内与外第一次有了意义。' }, + { id: 'photosynthesis', name: '光合作用', description: '把暮阳的光转译成稳定能量。', cost: 38, effect: { click: 2, auto: 0.8, balance: 5, insight: 1 }, signal: '海水泛起绿色,天空开始改变颜色。' }, + { id: 'nucleus', name: '遗传核心', description: '保存更长的记忆,允许生命变得复杂。', cost: 90, effect: { auto: 2.2, insight: 2 }, signal: '无数偶然被写成可以继承的秩序。' }, + ], + }, + { + id: 'landcrown', + name: '陆冠纪元', + chapter: 'CHAPTER 02', + resourceName: '生境力', + glyph: '⌁', + accent: '#b8f36b', + headline: '让海的记忆覆盖大陆', + story: '风吹过尚未命名的大陆。生命带着海盐,缓慢爬向干燥的光。', + transition: '炉城纪元开始:智慧生命点燃了第一簇火。', + milestones: [ + { id: 'roots', name: '根系网络', description: '植物固定土壤,为整个陆地生态供能。', cost: 70, effect: { auto: 3.5, balance: 8, insight: 2 }, signal: '绿色沿河谷蔓延,大陆拥有了呼吸。' }, + { id: 'symmetry', name: '灵巧肢体', description: '感知、移动与协作带来新的生存方式。', cost: 180, effect: { click: 4, auto: 4.5, insight: 2 }, signal: '生命不再只等待环境,而开始寻找环境。' }, + { id: 'sentience', name: '群体心智', description: '记忆跨越个体,语言跨越世代。', cost: 420, effect: { auto: 7, balance: 4, insight: 4 }, signal: '它们第一次抬头观察星空,并提出了为什么。' }, + ], + }, + { + id: 'forgecity', + name: '炉城纪元', + chapter: 'CHAPTER 03', + resourceName: '文明火种', + glyph: '⌬', + accent: '#ffb45b', + headline: '火焰照亮城市,也照见代价', + story: '城市沿海岸升起。天文学家同时发现:暮阳正在变得不再稳定。', + transition: '星种纪元开始:整个文明把目光投向大气层之外。', + milestones: [ + { id: 'agriculture', name: '共生农业', description: '让聚落增长,而不是透支脚下的土壤。', cost: 280, effect: { auto: 15, balance: 12, insight: 4 }, signal: '文明学会收获,也学会留下下一季的种子。' }, + { id: 'observatory', name: '暮阳观测阵列', description: '确认超级耀斑将在八百年内剥离大气。', cost: 700, effect: { click: 10, auto: 20, insight: 6 }, signal: '长夜被算出了日期,世界第一次拥有共同的倒计时。' }, + { id: 'covenant', name: '行星公约', description: '守园者与远航者共同签署星种计划。', cost: 1500, effect: { auto: 35, balance: 10, insight: 8 }, signal: '延续生命与守护故乡,不再是两条相反的道路。' }, + ], + }, + { + id: 'starseed', + name: '星种纪元', + chapter: 'CHAPTER 04', + resourceName: '星航能量', + glyph: '✦', + accent: '#77b8ff', + headline: '在长夜之前,把潮声送往群星', + story: '轨道船坞进入最后施工期。暮阳的第一轮风暴已经抵达外层行星。', + transition: '', + milestones: [ + { id: 'archive', name: '生命档案', description: '保存微生物、种子、胚胎与文明记忆。', cost: 1100, effect: { auto: 55, archive: 45, insight: 8 }, signal: '一整颗行星的生命,被浓缩成可以远航的花园。' }, + { id: 'canopy', name: '磁场天幕', description: '为汐原抵挡恒星风,给故乡留下未来。', cost: 2500, effect: { auto: 90, shield: 65, balance: 15, insight: 10 }, signal: '人造极光覆盖天空,故乡在风暴中继续呼吸。' }, + { id: 'ark', name: '星种方舟', description: '完成驶向新潮的最后一艘生态方舟。', cost: 5200, effect: { auto: 140, archive: 55, shield: 35, insight: 14 }, signal: '倒计时开始。另一片海正在航线尽头等待。' }, + ], + }, +] + +const ALL_MILESTONE_IDS = new Set(PHASES.flatMap((phase) => phase.milestones.map((milestone) => milestone.id))) + +function safeNumber(value, fallback, min = 0, max = Number.MAX_SAFE_INTEGER, integer = false) { + if (typeof value !== 'number' || !Number.isFinite(value)) return fallback + const normalized = integer ? Math.floor(value) : value + return clamp(normalized, min, max) +} + +export function createInitialState(now = Date.now()) { + return { + version: 1, + phase: 0, + essence: 0, + clickPower: 1, + autoRate: 0, + insight: 0, + balance: 50, + shield: 0, + archive: 0, + completedMilestones: [], + storyLog: ['潮声第一次被听见。'], + launched: false, + seedCount: 0, + lastUpdated: now, + offlineSeconds: 0, + } +} + +export function collectPulse(state) { + if (state.launched) return state + return { + ...state, + essence: state.essence + state.clickPower, + } +} + +export function buyMilestone(state, milestoneId) { + const milestone = PHASES[state.phase]?.milestones.find((item) => item.id === milestoneId) + if (!milestone || state.completedMilestones.includes(milestoneId) || state.essence < milestone.cost) { + return state + } + + const effect = milestone.effect + return { + ...state, + essence: state.essence - milestone.cost, + clickPower: state.clickPower + (effect.click ?? 0), + autoRate: state.autoRate + (effect.auto ?? 0), + insight: state.insight + (effect.insight ?? 0), + balance: clamp(state.balance + (effect.balance ?? 0), 0, 100), + shield: clamp(state.shield + (effect.shield ?? 0), 0, 100), + archive: clamp(state.archive + (effect.archive ?? 0), 0, 100), + completedMilestones: [...state.completedMilestones, milestoneId], + storyLog: [...state.storyLog, milestone.signal].slice(-8), + } +} + +export function canAdvance(state) { + const phase = PHASES[state.phase] + return Boolean( + phase + && state.phase < PHASES.length - 1 + && phase.milestones.every((milestone) => state.completedMilestones.includes(milestone.id)), + ) +} + +export function advancePhase(state) { + if (!canAdvance(state)) return state + const current = PHASES[state.phase] + return { + ...state, + phase: state.phase + 1, + essence: 0, + storyLog: [...state.storyLog, current.transition].slice(-8), + } +} + +export function elapsedSeconds(from, to) { + if (!Number.isFinite(from) || !Number.isFinite(to)) return 0 + return clamp((to - from) / 1000, 0, 14_400) +} + +export function tick(state, seconds, now = Date.now()) { + const elapsed = Math.max(0, Number.isFinite(seconds) ? seconds : 0) + if (state.launched) return state + const essence = safeNumber(state.essence, 0) + const autoRate = safeNumber(state.autoRate, 0) + return { + ...state, + essence: essence + autoRate * elapsed, + lastUpdated: now, + } +} + +export function applyOfflineProgress(state, now = Date.now()) { + if (state.launched) return state + const elapsed = elapsedSeconds(state.lastUpdated, now) + return { + ...tick(state, elapsed, now), + offlineSeconds: elapsed, + } +} + +export function isReadyToLaunch(state) { + const finalPhase = PHASES.at(-1) + return Boolean( + state.phase === PHASES.length - 1 + && !state.launched + && finalPhase.milestones.every((milestone) => state.completedMilestones.includes(milestone.id)), + ) +} + +export function launchSeed(state) { + if (!isReadyToLaunch(state)) return state + const endingGrade = state.balance >= 80 && state.shield === 100 && state.archive === 100 + ? '双生花园' + : state.shield >= 80 + ? '故乡守望' + : '远航火种' + return { + ...state, + launched: true, + seedCount: state.seedCount + 1, + endingTitle: '两片海的黎明', + endingGrade, + storyLog: [...state.storyLog, '另一片没有名字的海里,有东西第一次记住了自己。'].slice(-8), + } +} + +export function serializeGame(state) { + return JSON.stringify({ ...state, lastUpdated: Date.now() }) +} + +export function restoreGame(serialized, now = Date.now()) { + try { + const parsed = JSON.parse(serialized) + if (!parsed || parsed.version !== 1 || !Number.isInteger(parsed.phase)) { + return createInitialState(now) + } + + const initial = createInitialState(now) + const completedMilestones = Array.isArray(parsed.completedMilestones) + ? [...new Set(parsed.completedMilestones.filter((id) => typeof id === 'string' && ALL_MILESTONE_IDS.has(id)))] + : [] + const storyLog = Array.isArray(parsed.storyLog) + ? parsed.storyLog.filter((entry) => typeof entry === 'string' && entry.trim()).slice(-8) + : [] + const phase = clamp(parsed.phase, 0, PHASES.length - 1) + const launched = parsed.launched === true + && phase === PHASES.length - 1 + && PHASES.at(-1).milestones.every((milestone) => completedMilestones.includes(milestone.id)) + + const normalized = { + ...initial, + phase, + essence: safeNumber(parsed.essence, initial.essence), + clickPower: safeNumber(parsed.clickPower, initial.clickPower, 1), + autoRate: safeNumber(parsed.autoRate, initial.autoRate), + insight: safeNumber(parsed.insight, initial.insight, 0, Number.MAX_SAFE_INTEGER, true), + balance: safeNumber(parsed.balance, initial.balance, 0, 100), + shield: safeNumber(parsed.shield, initial.shield, 0, 100), + archive: safeNumber(parsed.archive, initial.archive, 0, 100), + seedCount: safeNumber(parsed.seedCount, initial.seedCount, 0, Number.MAX_SAFE_INTEGER, true), + completedMilestones, + storyLog: storyLog.length ? storyLog : initial.storyLog, + launched, + endingTitle: launched && typeof parsed.endingTitle === 'string' ? parsed.endingTitle : undefined, + endingGrade: launched && typeof parsed.endingGrade === 'string' ? parsed.endingGrade : undefined, + lastUpdated: safeNumber(parsed.lastUpdated, now, 0, now), + offlineSeconds: 0, + } + return applyOfflineProgress(normalized, now) + } catch { + return createInitialState(now) + } +} diff --git a/src/game/gameState.test.js b/src/game/gameState.test.js new file mode 100644 index 0000000..80f3bdd --- /dev/null +++ b/src/game/gameState.test.js @@ -0,0 +1,135 @@ +import { describe, expect, it } from 'vitest' +import { + PHASES, + advancePhase, + applyOfflineProgress, + buyMilestone, + canAdvance, + collectPulse, + createInitialState, + elapsedSeconds, + isReadyToLaunch, + launchSeed, + restoreGame, + serializeGame, + tick, +} from './gameState.js' + +describe('生命脉冲', () => { + it('从潮池纪元开始,并按点击强度采集生命质', () => { + const initial = createInitialState(1000) + const next = collectPulse(initial) + + expect(initial.phase).toBe(0) + expect(initial.essence).toBe(0) + expect(next.essence).toBe(1) + expect(next).not.toBe(initial) + }) + + it('只有资源充足时才能购买里程碑,且同一里程碑不能重复购买', () => { + const initial = createInitialState(1000) + const milestone = PHASES[0].milestones[0] + + expect(buyMilestone(initial, milestone.id)).toBe(initial) + + const funded = { ...initial, essence: milestone.cost } + const purchased = buyMilestone(funded, milestone.id) + const duplicate = buyMilestone(purchased, milestone.id) + + expect(purchased.essence).toBe(0) + expect(purchased.completedMilestones).toContain(milestone.id) + expect(purchased.autoRate).toBeGreaterThan(0) + expect(duplicate).toBe(purchased) + }) + + it('完成当前纪元全部里程碑后才能进入下一纪元', () => { + let state = createInitialState(1000) + + expect(canAdvance(state)).toBe(false) + expect(advancePhase(state)).toBe(state) + + for (const milestone of PHASES[0].milestones) { + state = buyMilestone({ ...state, essence: milestone.cost }, milestone.id) + } + + expect(canAdvance(state)).toBe(true) + const advanced = advancePhase(state) + expect(advanced.phase).toBe(1) + expect(advanced.essence).toBe(0) + expect(advanced.storyLog.at(-1)).toContain('陆冠') + }) + + it('按秒产生资源,并把离线收益限制在四小时', () => { + const productive = { ...createInitialState(1000), autoRate: 2 } + const afterTenSeconds = tick(productive, 10, 11000) + const afterLongAbsence = applyOfflineProgress(productive, 20_001_000) + + expect(afterTenSeconds.essence).toBe(20) + expect(afterTenSeconds.lastUpdated).toBe(11000) + expect(afterLongAbsence.essence).toBe(28_800) + expect(afterLongAbsence.offlineSeconds).toBe(14_400) + }) + + it('标签页被节流后补足完整间隔,并仍遵守四小时上限', () => { + expect(elapsedSeconds(1_000, 61_000)).toBe(60) + expect(elapsedSeconds(1_000, 18_001_000)).toBe(14_400) + expect(elapsedSeconds(61_000, 1_000)).toBe(0) + }) + + it('完成星种纪元后才能发射,并生成唯一的正式结局', () => { + let state = { ...createInitialState(1000), phase: 3 } + + expect(isReadyToLaunch(state)).toBe(false) + expect(launchSeed(state)).toBe(state) + + for (const milestone of PHASES[3].milestones) { + state = buyMilestone({ ...state, essence: milestone.cost }, milestone.id) + } + + expect(isReadyToLaunch(state)).toBe(true) + const launched = launchSeed(state) + expect(launched.launched).toBe(true) + expect(launched.seedCount).toBe(1) + expect(launched.endingTitle).toBe('两片海的黎明') + expect(launchSeed(launched)).toBe(launched) + }) + + it('存档往返保留状态,损坏存档安全回到初始世界', () => { + const state = { ...createInitialState(1000), essence: 42, insight: 7 } + const restored = restoreGame(serializeGame(state), 1000) + const fallback = restoreGame('{bad json', 5000) + + expect(restored.essence).toBe(42) + expect(restored.insight).toBe(7) + expect(fallback).toEqual(createInitialState(5000)) + }) + + it('语义损坏的本地存档会被归一化,不会让界面崩溃', () => { + const corrupted = JSON.stringify({ + ...createInitialState(1000), + essence: -10, + clickPower: null, + autoRate: 'oops', + insight: -2, + balance: 999, + shield: -4, + archive: 'full', + seedCount: 1.7, + completedMilestones: ['membrane', 'unknown', 42], + storyLog: [{ unsafe: true }, '仍可读取的潮声'], + }) + + const restored = restoreGame(corrupted, 1000) + + expect(restored.essence).toBe(0) + expect(restored.clickPower).toBe(1) + expect(restored.autoRate).toBe(0) + expect(restored.insight).toBe(0) + expect(restored.balance).toBe(100) + expect(restored.shield).toBe(0) + expect(restored.archive).toBe(0) + expect(restored.seedCount).toBe(1) + expect(restored.completedMilestones).toEqual(['membrane']) + expect(restored.storyLog).toEqual(['仍可读取的潮声']) + }) +}) diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..fdbdce5 --- /dev/null +++ b/src/main.js @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import App from './App.vue' +import './styles.css' + +createApp(App).mount('#app') diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..c47e7e0 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,437 @@ +@layer reset, base, layout, components, responsive; + +@layer reset { + *, *::before, *::after { box-sizing: border-box; } + html { color-scheme: dark; background: #03070b; scroll-behavior: smooth; } + body { margin: 0; min-width: 320px; min-height: 100vh; overflow-x: hidden; } + button, input { font: inherit; } + button { color: inherit; } + button:focus-visible, a:focus-visible { outline: 2px solid var(--accent, #58f3c4); outline-offset: 4px; } + ::selection { color: #02100c; background: var(--accent, #58f3c4); } +} + +@layer base { + :root { + font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; + color: #eef8f7; + background: #03070b; + font-synthesis: none; + text-rendering: optimizeLegibility; + } + + .game-shell { + --accent: #58f3c4; + --line: rgba(216, 247, 240, 0.14); + --line-strong: rgba(216, 247, 240, 0.28); + --muted: #94a6a3; + --surface: rgba(5, 13, 17, 0.78); + position: relative; + isolation: isolate; + min-height: 100vh; + overflow: hidden; + background: + radial-gradient(circle at 38% 45%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 34%), + linear-gradient(135deg, #020509 0%, #061016 52%, #02070b 100%); + } + + .game-shell::before { + position: fixed; + inset: 0; + z-index: -3; + content: ""; + pointer-events: none; + background-image: + linear-gradient(rgba(255,255,255,.018) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,.018) 1px, transparent 1px); + background-size: 72px 72px; + mask-image: linear-gradient(to bottom, black, transparent 85%); + } + + .micro-label { + display: block; + color: var(--accent); + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 10px; + font-weight: 700; + letter-spacing: .18em; + line-height: 1.2; + text-transform: uppercase; + } + + .aurora { + position: fixed; + z-index: -2; + width: 42vw; + height: 42vw; + border-radius: 50%; + pointer-events: none; + opacity: .14; + filter: blur(95px); + background: var(--accent); + transition: background 1.2s ease; + } + .aurora--one { top: -24vw; left: 20vw; animation: drift-one 16s ease-in-out infinite alternate; } + .aurora--two { right: -30vw; bottom: -26vw; opacity: .08; animation: drift-two 20s ease-in-out infinite alternate; } + .noise { + position: fixed; + inset: 0; + z-index: 20; + pointer-events: none; + opacity: .02; + background-image: repeating-radial-gradient(circle at 30% 40%, #fff 0 0.45px, transparent .7px 3px); + background-size: 5px 5px; + mix-blend-mode: screen; + } + + @keyframes drift-one { to { transform: translate3d(12vw, 9vw, 0) scale(1.12); } } + @keyframes drift-two { to { transform: translate3d(-8vw, -7vw, 0) scale(.92); } } +} + +@layer layout { + .mission-header { + position: relative; + z-index: 10; + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + min-height: 82px; + padding: 0 34px; + border-bottom: 1px solid var(--line); + background: rgba(2, 7, 10, .72); + backdrop-filter: blur(18px); + } + + .wordmark { + display: inline-flex; + align-items: center; + width: max-content; + color: #f0f8f7; + text-decoration: none; + } + .wordmark__mark { + display: grid; + width: 39px; + height: 39px; + margin-right: 12px; + place-items: center; + border: 1px solid var(--accent); + color: var(--accent); + font-size: 17px; + transform: rotate(45deg); + transition: border-color .8s ease, color .8s ease; + } + .wordmark__mark::first-letter { transform: rotate(-45deg); } + .wordmark span:last-child { display: grid; gap: 2px; } + .wordmark strong { font-size: 15px; letter-spacing: .26em; } + .wordmark small { color: var(--muted); font: 8px/1 ui-monospace, monospace; letter-spacing: .18em; } + + .mission-title { text-align: center; } + .mission-title span { color: var(--accent); font: 9px/1 ui-monospace, monospace; letter-spacing: .2em; } + .mission-title h1 { margin: 6px 0 0; font-size: 14px; font-weight: 500; letter-spacing: .18em; } + + .mission-meta { display: flex; align-items: center; justify-content: flex-end; gap: 20px; } + .save-indicator { color: var(--muted); font: 10px/1 ui-monospace, monospace; letter-spacing: .08em; } + .save-indicator i { + display: inline-block; + width: 6px; + height: 6px; + margin-right: 8px; + border-radius: 50%; + background: var(--accent); + box-shadow: 0 0 12px var(--accent); + animation: blink 2s ease-in-out infinite; + } + .text-button { padding: 8px 0; border: 0; color: #dbe9e7; background: none; cursor: pointer; font-size: 10px; letter-spacing: .12em; } + .text-button:hover { color: var(--accent); } + + .mission-progress { position: relative; z-index: 11; height: 2px; background: rgba(255,255,255,.04); } + .mission-progress span { display: block; width: 0; height: 100%; background: var(--accent); box-shadow: 0 0 18px var(--accent); transition: width .7s ease, background .8s ease; } + + .game-grid { + position: relative; + z-index: 1; + display: grid; + grid-template-columns: minmax(0, 1.7fr) minmax(355px, .8fr); + min-height: calc(100vh - 119px); + } + + .game-grid__scene { position: relative; min-width: 0; padding: 48px 3vw 20px; overflow: hidden; } + .command-panel { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + padding: 28px 28px 22px; + border-left: 1px solid var(--line); + background: linear-gradient(180deg, rgba(6,14,18,.78), rgba(2,8,11,.94)); + backdrop-filter: blur(24px); + } + + .mission-footer { + position: relative; + z-index: 12; + display: flex; + justify-content: space-between; + padding: 13px 34px; + border-top: 1px solid var(--line); + color: #667876; + background: #02070a; + font: 8px/1 ui-monospace, monospace; + letter-spacing: .14em; + } +} + +@layer components { + .chapter-copy { position: relative; z-index: 4; max-width: 680px; } + .chapter-copy h2 { + max-width: 660px; + margin: 14px 0 12px; + font-size: clamp(30px, 3.4vw, 58px); + font-weight: 600; + letter-spacing: -.045em; + line-height: .98; + } + .chapter-copy p { max-width: 600px; margin: 0; color: #92a3a0; font-size: 13px; line-height: 1.7; } + + .evolution-scene { position: relative; height: min(650px, 68vh); min-height: 500px; margin-top: -8px; overflow: hidden; } + .star-field { position: absolute; inset: 0; } + .star-field i { position: absolute; border-radius: 50%; background: #dffefa; box-shadow: 0 0 6px #fff; animation: twinkle 4.5s ease-in-out infinite; } + @keyframes twinkle { 50% { opacity: .2; transform: scale(.5); } } + + .planet-stage { + position: absolute; + top: 44%; + left: 50%; + width: clamp(270px, 31vw, 500px); + aspect-ratio: 1; + translate: -50% -50%; + } + .planet-glow { + position: absolute; + inset: -16%; + border-radius: 50%; + background: radial-gradient(circle, color-mix(in srgb, var(--accent) 22%, transparent), transparent 68%); + filter: blur(18px); + animation: breathe 5s ease-in-out infinite; + } + @keyframes breathe { 50% { opacity: .55; transform: scale(1.08); } } + .planet { + position: absolute; + inset: 8%; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--accent) 48%, #fff 10%); + border-radius: 50%; + background: + radial-gradient(circle at 34% 29%, rgba(255,255,255,.24), transparent 5%), + radial-gradient(circle at 65% 72%, rgba(0,0,0,.72), transparent 47%), + linear-gradient(135deg, #0a5570, #07293c 44%, #05121c 84%); + box-shadow: + inset -45px -28px 65px rgba(0,0,0,.7), + inset 14px 10px 36px rgba(255,255,255,.08), + 0 0 0 13px rgba(255,255,255,.015), + 0 0 85px color-mix(in srgb, var(--accent) 18%, transparent); + transition: background 1s ease, border-color .8s ease; + } + .planet--tidepool { background: radial-gradient(circle at 34% 27%, #38b7c6, transparent 6%), radial-gradient(circle at 65% 72%, #020911, transparent 46%), repeating-radial-gradient(ellipse at 30% 20%, #0e6d82 0 10px, #084157 11px 26px, #061b2b 27px 45px); } + .planet--landcrown { background: radial-gradient(circle at 34% 27%, #b5e989, transparent 5%), radial-gradient(circle at 70% 75%, #020b08, transparent 48%), linear-gradient(135deg, #3a7c56, #0e4e45 38%, #08202d 74%); } + .planet--forgecity { background: radial-gradient(circle at 35% 26%, #f5c17d, transparent 5%), radial-gradient(circle at 69% 73%, #070400, transparent 48%), linear-gradient(135deg, #875730, #273e40 39%, #08121c 76%); } + .planet--starseed { background: radial-gradient(circle at 35% 26%, #b7dcff, transparent 5%), radial-gradient(circle at 69% 73%, #01040b, transparent 48%), linear-gradient(135deg, #355f91, #172f5d 40%, #070d20 78%); } + .planet-atmosphere { position: absolute; inset: -2%; border: 5px solid color-mix(in srgb, var(--accent) 24%, transparent); border-radius: 50%; filter: blur(7px); } + .planet-grid { position: absolute; inset: 0; opacity: .11; background-image: linear-gradient(90deg, transparent 49.5%, #fff 50%, transparent 50.5%), linear-gradient(transparent 49.5%, #fff 50%, transparent 50.5%); background-size: 31px 31px; transform: rotate(-18deg) scale(1.3); } + .planet-continent { position: absolute; border-radius: 48% 52% 36% 64% / 58% 31% 69% 42%; background: color-mix(in srgb, var(--accent) 48%, #0a2d24); opacity: .55; filter: blur(1px); box-shadow: inset -12px -8px 18px rgba(0,0,0,.35); } + .continent-a { top: 22%; left: 15%; width: 43%; height: 27%; transform: rotate(23deg); } + .continent-b { right: 12%; bottom: 18%; width: 39%; height: 32%; transform: rotate(-29deg); } + .city-lights { position: absolute; inset: 12%; border-radius: 50%; opacity: .8; background-image: radial-gradient(circle, #ffe29a 0 1px, transparent 1.7px); background-size: 23px 19px; mask-image: radial-gradient(ellipse at 45% 43%, black, transparent 62%); animation: city-flicker 3s steps(4) infinite; } + @keyframes city-flicker { 50% { opacity: .52; } } + .launch-trail { position: absolute; top: 16%; right: 17%; width: 2px; height: 32%; background: linear-gradient(#fff, var(--accent), transparent); box-shadow: 0 0 12px var(--accent); transform: rotate(38deg); animation: launch 3.8s ease-in-out infinite; } + @keyframes launch { 0%, 30% { opacity: 0; transform: translate(-25px, 35px) rotate(38deg) scaleY(.3); } 55% { opacity: 1; } 100% { opacity: 0; transform: translate(45px, -65px) rotate(38deg) scaleY(1.3); } } + + .orbit { position: absolute; top: 50%; left: 50%; border: 1px solid rgba(213,247,240,.13); border-radius: 50%; transform: translate(-50%, -50%) rotate(-13deg); } + .orbit--wide { width: 78%; height: 37%; } + .orbit--tight { width: 57%; height: 25%; transform: translate(-50%, -50%) rotate(64deg); } + .orbit span { position: absolute; top: 50%; left: -4px; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 16px var(--accent); animation: satellite 8s linear infinite; transform-origin: calc(50% + 4px) 0; } + .orbit--tight span { animation-duration: 5.5s; animation-direction: reverse; } + @keyframes satellite { to { offset-distance: 100%; } } + .scan-line { position: absolute; top: 50%; left: 50%; width: 54%; height: 54%; border-radius: 50%; background: conic-gradient(from 190deg, transparent 0 73%, color-mix(in srgb, var(--accent) 13%, transparent) 88%, transparent 96%); transform: translate(-50%, -50%); animation: scan 9s linear infinite; } + @keyframes scan { to { transform: translate(-50%, -50%) rotate(360deg); } } + + .pulse-core { + position: absolute; + bottom: 38px; + left: 50%; + z-index: 6; + display: grid; + width: 170px; + min-height: 112px; + place-content: center; + border: 1px solid color-mix(in srgb, var(--accent) 48%, transparent); + border-radius: 50%; + color: #effffc; + background: rgba(3, 12, 15, .78); + box-shadow: inset 0 0 35px color-mix(in srgb, var(--accent) 9%, transparent), 0 0 30px rgba(0,0,0,.45); + cursor: pointer; + translate: -50% 0; + backdrop-filter: blur(10px); + } + .pulse-core:disabled { cursor: default; opacity: .55; } + .pulse-core__halo { position: absolute; inset: -9px; border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent); border-radius: 50%; animation: pulse-ring 2.6s ease-out infinite; } + @keyframes pulse-ring { 70%,100% { opacity: 0; transform: scale(1.18); } } + .pulse-core__eyebrow { color: var(--accent); font: 9px/1 ui-monospace, monospace; letter-spacing: .16em; } + .pulse-core strong { margin: 7px 0; font-size: 25px; font-weight: 500; letter-spacing: -.02em; } + .pulse-core__action { color: #8fa5a1; font-size: 9px; letter-spacing: .08em; } + + .scene-label { position: absolute; top: 45%; display: grid; gap: 5px; color: #8b9d99; font: 9px/1 ui-monospace, monospace; letter-spacing: .12em; } + .scene-label strong { color: #e1efed; font-size: 10px; } + .scene-label--left { left: 2%; } + .scene-label--right { right: 2%; text-align: right; } + .scene-coordinate { position: absolute; right: 2%; bottom: 21px; color: #50635f; font: 8px/1 ui-monospace, monospace; letter-spacing: .08em; } + + .resource-strip { display: grid; grid-template-columns: 1.15fr .85fr .85fr; padding: 2px 0 24px; border-bottom: 1px solid var(--line); } + .resource-strip div { padding: 0 14px; border-left: 1px solid var(--line); } + .resource-strip div:first-child { padding-left: 0; border-left: 0; } + .resource-strip span { display: block; margin-bottom: 8px; color: #697b78; font-size: 9px; letter-spacing: .08em; } + .resource-strip strong { font: 500 17px/1 ui-monospace, monospace; } + .resource-strip div:first-child strong { color: var(--accent); font-size: 22px; text-shadow: 0 0 20px color-mix(in srgb, var(--accent) 40%, transparent); } + + .milestone-rail { flex: 1; padding-top: 25px; } + .rail-header { display: flex; align-items: flex-end; justify-content: space-between; } + .rail-header h2 { margin: 7px 0 0; font-size: 19px; font-weight: 500; letter-spacing: .02em; } + .rail-count { color: var(--muted); font: 10px/1 ui-monospace, monospace; letter-spacing: .12em; } + .rail-progress { height: 2px; margin: 18px 0 9px; background: rgba(255,255,255,.06); } + .rail-progress span { display: block; height: 100%; background: var(--accent); box-shadow: 0 0 12px var(--accent); transition: width .6s ease; } + .milestone-list { display: grid; } + .milestone { + display: grid; + grid-template-columns: 30px 1fr auto; + gap: 12px; + align-items: center; + width: 100%; + min-height: 87px; + padding: 13px 2px; + border: 0; + border-bottom: 1px solid var(--line); + text-align: left; + background: transparent; + cursor: pointer; + transition: color .25s ease, opacity .25s ease, border-color .25s ease; + } + .milestone:not(:disabled):hover { border-color: color-mix(in srgb, var(--accent) 55%, transparent); } + .milestone:disabled:not(.is-complete) { cursor: not-allowed; opacity: .38; } + .milestone.is-complete { opacity: .62; } + .milestone__index { color: var(--accent); font: 9px/1 ui-monospace, monospace; } + .milestone__copy { display: grid; gap: 6px; } + .milestone__copy strong { font-size: 13px; font-weight: 500; letter-spacing: .04em; } + .milestone__copy small { max-width: 285px; color: #92a3a0; font-size: 11px; line-height: 1.5; } + .milestone__cost { min-width: 74px; padding: 7px 9px; border: 1px solid var(--line); color: #bdcfcc; font: 9px/1 ui-monospace, monospace; text-align: center; } + .milestone:not(.is-complete) .milestone__cost::after { content: " ◇"; color: var(--accent); } + .milestone.is-complete .milestone__cost { border-color: color-mix(in srgb, var(--accent) 38%, transparent); color: var(--accent); } + + .final-meters { display: grid; gap: 10px; margin: 18px 0 0; } + .final-meters > div { display: grid; grid-template-columns: 70px 1fr 38px; gap: 10px; align-items: center; color: #81928f; font-size: 9px; } + .final-meters i { height: 3px; background: rgba(255,255,255,.07); } + .final-meters b { display: block; height: 100%; background: var(--accent); box-shadow: 0 0 9px var(--accent); transition: width .6s ease; } + .final-meters strong { color: #d9e9e7; font: 9px/1 ui-monospace, monospace; text-align: right; } + + .epoch-button { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + min-height: 52px; + margin-top: 18px; + padding: 0 18px; + border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent); + color: #07110f; + background: var(--accent); + cursor: pointer; + font-size: 11px; + font-weight: 700; + letter-spacing: .14em; + text-transform: uppercase; + box-shadow: 0 0 28px color-mix(in srgb, var(--accent) 12%, transparent); + } + .epoch-button strong { font-size: 18px; } + .epoch-button__copy { display: grid; gap: 4px; text-align: left; } + .epoch-button__copy b { font-size: 11px; letter-spacing: .14em; } + .epoch-button__copy small { color: #81918f; font-size: 8px; font-weight: 500; letter-spacing: .08em; text-transform: none; } + .epoch-button:disabled { border-color: var(--line); color: #62716f; background: rgba(255,255,255,.035); box-shadow: none; cursor: not-allowed; } + .epoch-button--launch:not(:disabled) { color: #edfaff; background: linear-gradient(90deg, #2868a3, var(--accent)); } + + .signal-log { margin-top: 18px; padding-top: 15px; border-top: 1px solid var(--line); } + .signal-log__head { display: flex; align-items: center; color: #6d807c; font: 8px/1 ui-monospace, monospace; letter-spacing: .13em; } + .signal-dot { width: 5px; height: 5px; margin-right: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 9px var(--accent); } + .signal-log__entries { min-height: 74px; margin-top: 10px; } + .signal-log p { margin: 5px 0; color: #9cadaa; font-size: 9px; line-height: 1.45; } + .signal-log p span { margin-right: 9px; color: var(--accent); font-family: ui-monospace, monospace; } + + .phase-transition { position: fixed; inset: 0; z-index: 80; display: grid; place-content: center; text-align: center; background: rgba(1, 5, 8, .94); backdrop-filter: blur(22px); } + .phase-transition > div { width: min(330px, 60vw); height: 1px; margin-bottom: 28px; background: var(--accent); box-shadow: 0 0 25px var(--accent); } + .phase-transition span { color: var(--accent); font: 10px/1 ui-monospace, monospace; letter-spacing: .4em; } + .phase-transition strong { margin-top: 14px; font-size: clamp(34px, 6vw, 72px); font-weight: 400; letter-spacing: .12em; } + + .ending { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; overflow: hidden; background: radial-gradient(circle at 50% 90%, #123e55, transparent 38%), #010408; } + .ending::before { position: absolute; bottom: -38vw; left: 50%; width: min(90vw, 1050px); aspect-ratio: 1; content: ""; border: 2px solid rgba(114,213,255,.55); border-radius: 50%; background: radial-gradient(circle at 48% 35%, #4e9caa, #0d354a 35%, #030a11 70%); box-shadow: 0 -20px 90px rgba(83,192,230,.24); transform: translateX(-50%); } + .ending__stars { position: absolute; inset: 0; opacity: .7; background-image: radial-gradient(circle, #fff 0 1px, transparent 1.5px); background-size: 79px 73px; } + .ending__content { position: relative; z-index: 2; width: min(720px, calc(100vw - 42px)); padding-bottom: 8vh; text-align: center; } + .ending h2 { margin: 20px 0 18px; font-size: clamp(46px, 8vw, 104px); font-weight: 300; letter-spacing: -.06em; line-height: .9; } + .ending p { margin: 0 auto; color: #b4c7c6; font-size: 14px; line-height: 1.8; } + .ending__epilogue { max-width: 580px; } + .ending__line { width: 90px; height: 1px; margin: 28px auto; background: var(--accent); box-shadow: 0 0 16px var(--accent); } + .ghost-button { margin-top: 32px; padding: 15px 24px; border: 1px solid rgba(240,250,250,.35); border-radius: 999px; color: #edf8f7; background: rgba(240,250,250,.08); cursor: pointer; font-size: 11px; letter-spacing: .12em; } + .ghost-button:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); } + + .offline-notice { position: fixed; right: 24px; bottom: 24px; z-index: 70; max-width: min(420px, calc(100vw - 48px)); padding: 14px 18px; border: 1px solid color-mix(in srgb, var(--accent) 42%, transparent); color: #cfe1de; background: rgba(4,14,17,.94); box-shadow: 0 12px 40px rgba(0,0,0,.45); font-size: 10px; line-height: 1.5; } + @keyframes blink { 50% { opacity: .25; } } +} + +@layer responsive { + @media (max-width: 1080px) { + .mission-header { grid-template-columns: 1fr auto; } + .mission-title { display: none; } + .game-grid { grid-template-columns: minmax(0, 1.35fr) minmax(340px, .85fr); } + .game-grid__scene { padding-left: 24px; padding-right: 24px; } + .evolution-scene { height: 610px; } + .scene-label { display: none; } + } + + @media (max-width: 820px) { + body { overflow: auto; } + .mission-header { position: sticky; top: 0; min-height: 68px; padding: 0 18px; } + .save-indicator { display: none; } + .game-grid { display: block; min-height: 0; } + .game-grid__scene { min-height: 680px; padding: 36px 18px 0; } + .chapter-copy h2 { font-size: clamp(34px, 10vw, 54px); } + .evolution-scene { height: 550px; min-height: 500px; } + .planet-stage { width: clamp(280px, 65vw, 430px); } + .command-panel { padding: 28px 20px 30px; border-top: 1px solid var(--line); border-left: 0; } + .mission-footer { padding: 11px 18px; } + .mission-footer span:first-child { display: none; } + } + + @media (max-width: 520px) { + .wordmark strong { font-size: 13px; } + .wordmark__mark { width: 33px; height: 33px; } + .text-button { font-size: 9px; } + .game-grid__scene { min-height: 620px; } + .chapter-copy p { font-size: 12px; } + .evolution-scene { height: 470px; min-height: 450px; margin-top: 0; } + .planet-stage { top: 46%; width: 300px; } + .orbit--wide { width: 110%; } + .orbit--tight { width: 84%; } + .pulse-core { bottom: 20px; width: 148px; min-height: 96px; } + .pulse-core strong { font-size: 21px; } + .scene-coordinate { display: none; } + .resource-strip { grid-template-columns: 1.15fr 1fr 1fr; } + .resource-strip div { padding: 0 8px; } + .resource-strip strong { font-size: 14px; } + .resource-strip div:first-child strong { font-size: 18px; } + .milestone { grid-template-columns: 24px 1fr auto; gap: 8px; } + .milestone__copy small { max-width: 190px; } + .mission-footer span:nth-child(2) { display: none; } + .ending::before { bottom: -30vw; width: 130vw; } + } + + @media (prefers-reduced-motion: reduce) { + *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } + } +} diff --git a/tests/verify-dist.mjs b/tests/verify-dist.mjs new file mode 100644 index 0000000..a6e7c43 --- /dev/null +++ b/tests/verify-dist.mjs @@ -0,0 +1,15 @@ +import assert from 'node:assert/strict' +import { readdir, readFile } from 'node:fs/promises' + +const files = await readdir(new URL('../dist/', import.meta.url)) +assert.deepEqual(files.sort(), ['index.html'], 'dist 必须只包含一个可直接运行的入口') + +const html = await readFile(new URL('../dist/index.html', import.meta.url), 'utf8') +assert.match(html, /^/i, '缺少 HTML 文档入口') +assert.match(html, /