commit ac3bdf0e0c81ef0418270f84f830f3d41c61fa14 Author: liRQ <813144884@qq.com> Date: Thu Jun 18 14:39:13 2026 +0800 初始提交:网盘管理跨端应用 diff --git a/.qoder/repowiki/knowledge/zh/_index.yaml b/.qoder/repowiki/knowledge/zh/_index.yaml new file mode 100644 index 0000000..b2cb594 --- /dev/null +++ b/.qoder/repowiki/knowledge/zh/_index.yaml @@ -0,0 +1,112 @@ +# 知识卡导出索引文件 +schema_version: 1 +locale: zh-CN +branch: Q0DeR-MaG1C-BrAnCh-Fo2-NoN-GiT-Or-No-C0MmIt +exported_at: "2026-06-12T08:09:05Z" +modules: + "": + dir_name: 网盘管理跨端应用 + title: 网盘管理跨端应用 + scope: + - vue\.gitignore + - vue\.vscode\extensions.json + - vue\APK_BUILD_OPTIONS.md + - vue\APP_GUIDE.md + - vue\PACKAGING_SUMMARY.md + - vue\README.md + - vue\android\.gitignore + - vue\android\app\.gitignore + - vue\android\app\build.gradle + - vue\android\app\capacitor.build.gradle + - vue\android\app\proguard-rules.pro + - vue\android\app\src\androidTest\java\com\getcapacitor\myapp\ExampleInstrumentedTest.java + - vue\android\app\src\main\AndroidManifest.xml + - vue\android\app\src\main\assets\capacitor.config.json + - vue\android\app\src\main\assets\capacitor.plugins.json + - vue\android\app\src\main\assets\public\assets\index-DrjxTYTT.css + - vue\android\app\src\main\assets\public\assets\index-xOoYtVTp.js + - vue\android\app\src\main\assets\public\cordova.js + - vue\android\app\src\main\assets\public\cordova_plugins.js + - vue\android\app\src\main\assets\public\index.html + - vue\android\app\src\main\assets\public\logo.png + - vue\android\app\src\main\assets\public\vite.svg + - vue\android\app\src\main\java\com\wangpan\app\MainActivity.java + - vue\android\app\src\main\res\drawable-land-hdpi\splash.png + - vue\android\app\src\main\res\drawable-land-mdpi\splash.png + - vue\android\app\src\main\res\drawable-land-xhdpi\splash.png + - vue\android\app\src\main\res\drawable-land-xxhdpi\splash.png + - vue\android\app\src\main\res\drawable-land-xxxhdpi\splash.png + - vue\android\app\src\main\res\drawable-port-hdpi\splash.png + - vue\android\app\src\main\res\drawable-port-mdpi\splash.png + - vue\android\app\src\main\res\drawable-port-xhdpi\splash.png + - vue\android\app\src\main\res\drawable-port-xxhdpi\splash.png + - vue\android\app\src\main\res\drawable-port-xxxhdpi\splash.png + - vue\android\app\src\main\res\drawable-v24\ic_launcher_foreground.xml + - vue\android\app\src\main\res\drawable\ic_launcher_background.xml + - vue\android\app\src\main\res\drawable\splash.png + - vue\android\app\src\main\res\layout\activity_main.xml + - vue\android\app\src\main\res\mipmap-anydpi-v26\ic_launcher.xml + - vue\android\app\src\main\res\mipmap-anydpi-v26\ic_launcher_round.xml + - vue\android\app\src\main\res\mipmap-hdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-hdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-hdpi\ic_launcher_round.png + - vue\android\app\src\main\res\mipmap-mdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-mdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-mdpi\ic_launcher_round.png + - vue\android\app\src\main\res\mipmap-xhdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-xhdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-xhdpi\ic_launcher_round.png + - vue\android\app\src\main\res\mipmap-xxhdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-xxhdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-xxhdpi\ic_launcher_round.png + - vue\android\app\src\main\res\mipmap-xxxhdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-xxxhdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-xxxhdpi\ic_launcher_round.png + - vue\android\app\src\main\res\values\ic_launcher_background.xml + - vue\android\app\src\main\res\values\strings.xml + - vue\android\app\src\main\res\values\styles.xml + - vue\android\app\src\main\res\xml\config.xml + - vue\android\app\src\main\res\xml\file_paths.xml + - vue\android\app\src\test\java\com\getcapacitor\myapp\ExampleUnitTest.java + - vue\android\build.gradle + - vue\android\capacitor-cordova-android-plugins\build.gradle + - vue\android\capacitor-cordova-android-plugins\cordova.variables.gradle + - vue\android\capacitor-cordova-android-plugins\src\main\AndroidManifest.xml + - vue\android\capacitor-cordova-android-plugins\src\main\java\.gitkeep + - vue\android\capacitor-cordova-android-plugins\src\main\res\.gitkeep + - vue\android\capacitor.settings.gradle + - vue\android\gradle.properties + - vue\android\gradle\wrapper\gradle-wrapper.jar + - vue\android\gradle\wrapper\gradle-wrapper.properties + - vue\android\gradlew + - vue\android\gradlew.bat + - vue\android\settings.gradle + - vue\android\variables.gradle + - vue\build-apk.bat + - vue\capacitor.config.json + - vue\index.html + - vue\package-lock.json + - vue\package.json + - vue\public\logo.png + - vue\public\vite.svg + - vue\src\App.vue + - vue\src\api\index.js + - vue\src\assets\vue.svg + - vue\src\components\DriveManageDialog.vue + - vue\src\components\FolderTree.vue + - vue\src\components\LoginView.vue + - vue\src\components\PosterCard.vue + - vue\src\components\RecordDialog.vue + - vue\src\components\SimpleDialog.vue + - vue\src\components\TreeNode.vue + - vue\src\components\UserManageDialog.vue + - vue\src\main.js + - vue\src\style.css + - vue\src\utils\captcha.js + - vue\src\utils\data.js + - vue\src\utils\storage.js + - vue\vite.config.js + source_files: [] + children: [] + depends_on: [] + related_to: [] diff --git a/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/_module.yaml b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/_module.yaml new file mode 100644 index 0000000..a6b797e --- /dev/null +++ b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/_module.yaml @@ -0,0 +1,105 @@ +schema_version: 1 +module_path: "" +title: 网盘管理跨端应用 +scope: + - vue\.gitignore + - vue\.vscode\extensions.json + - vue\APK_BUILD_OPTIONS.md + - vue\APP_GUIDE.md + - vue\PACKAGING_SUMMARY.md + - vue\README.md + - vue\android\.gitignore + - vue\android\app\.gitignore + - vue\android\app\build.gradle + - vue\android\app\capacitor.build.gradle + - vue\android\app\proguard-rules.pro + - vue\android\app\src\androidTest\java\com\getcapacitor\myapp\ExampleInstrumentedTest.java + - vue\android\app\src\main\AndroidManifest.xml + - vue\android\app\src\main\assets\capacitor.config.json + - vue\android\app\src\main\assets\capacitor.plugins.json + - vue\android\app\src\main\assets\public\assets\index-DrjxTYTT.css + - vue\android\app\src\main\assets\public\assets\index-xOoYtVTp.js + - vue\android\app\src\main\assets\public\cordova.js + - vue\android\app\src\main\assets\public\cordova_plugins.js + - vue\android\app\src\main\assets\public\index.html + - vue\android\app\src\main\assets\public\logo.png + - vue\android\app\src\main\assets\public\vite.svg + - vue\android\app\src\main\java\com\wangpan\app\MainActivity.java + - vue\android\app\src\main\res\drawable-land-hdpi\splash.png + - vue\android\app\src\main\res\drawable-land-mdpi\splash.png + - vue\android\app\src\main\res\drawable-land-xhdpi\splash.png + - vue\android\app\src\main\res\drawable-land-xxhdpi\splash.png + - vue\android\app\src\main\res\drawable-land-xxxhdpi\splash.png + - vue\android\app\src\main\res\drawable-port-hdpi\splash.png + - vue\android\app\src\main\res\drawable-port-mdpi\splash.png + - vue\android\app\src\main\res\drawable-port-xhdpi\splash.png + - vue\android\app\src\main\res\drawable-port-xxhdpi\splash.png + - vue\android\app\src\main\res\drawable-port-xxxhdpi\splash.png + - vue\android\app\src\main\res\drawable-v24\ic_launcher_foreground.xml + - vue\android\app\src\main\res\drawable\ic_launcher_background.xml + - vue\android\app\src\main\res\drawable\splash.png + - vue\android\app\src\main\res\layout\activity_main.xml + - vue\android\app\src\main\res\mipmap-anydpi-v26\ic_launcher.xml + - vue\android\app\src\main\res\mipmap-anydpi-v26\ic_launcher_round.xml + - vue\android\app\src\main\res\mipmap-hdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-hdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-hdpi\ic_launcher_round.png + - vue\android\app\src\main\res\mipmap-mdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-mdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-mdpi\ic_launcher_round.png + - vue\android\app\src\main\res\mipmap-xhdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-xhdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-xhdpi\ic_launcher_round.png + - vue\android\app\src\main\res\mipmap-xxhdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-xxhdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-xxhdpi\ic_launcher_round.png + - vue\android\app\src\main\res\mipmap-xxxhdpi\ic_launcher.png + - vue\android\app\src\main\res\mipmap-xxxhdpi\ic_launcher_foreground.png + - vue\android\app\src\main\res\mipmap-xxxhdpi\ic_launcher_round.png + - vue\android\app\src\main\res\values\ic_launcher_background.xml + - vue\android\app\src\main\res\values\strings.xml + - vue\android\app\src\main\res\values\styles.xml + - vue\android\app\src\main\res\xml\config.xml + - vue\android\app\src\main\res\xml\file_paths.xml + - vue\android\app\src\test\java\com\getcapacitor\myapp\ExampleUnitTest.java + - vue\android\build.gradle + - vue\android\capacitor-cordova-android-plugins\build.gradle + - vue\android\capacitor-cordova-android-plugins\cordova.variables.gradle + - vue\android\capacitor-cordova-android-plugins\src\main\AndroidManifest.xml + - vue\android\capacitor-cordova-android-plugins\src\main\java\.gitkeep + - vue\android\capacitor-cordova-android-plugins\src\main\res\.gitkeep + - vue\android\capacitor.settings.gradle + - vue\android\gradle.properties + - vue\android\gradle\wrapper\gradle-wrapper.jar + - vue\android\gradle\wrapper\gradle-wrapper.properties + - vue\android\gradlew + - vue\android\gradlew.bat + - vue\android\settings.gradle + - vue\android\variables.gradle + - vue\build-apk.bat + - vue\capacitor.config.json + - vue\index.html + - vue\package-lock.json + - vue\package.json + - vue\public\logo.png + - vue\public\vite.svg + - vue\src\App.vue + - vue\src\api\index.js + - vue\src\assets\vue.svg + - vue\src\components\DriveManageDialog.vue + - vue\src\components\FolderTree.vue + - vue\src\components\LoginView.vue + - vue\src\components\PosterCard.vue + - vue\src\components\RecordDialog.vue + - vue\src\components\SimpleDialog.vue + - vue\src\components\TreeNode.vue + - vue\src\components\UserManageDialog.vue + - vue\src\main.js + - vue\src\style.css + - vue\src\utils\captcha.js + - vue\src\utils\data.js + - vue\src\utils\storage.js + - vue\vite.config.js +source_files: [] +depends_on: [] +related_to: [] diff --git a/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/技术栈.md b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/技术栈.md new file mode 100644 index 0000000..caf28a1 --- /dev/null +++ b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/技术栈.md @@ -0,0 +1 @@ +Vue 3.5, Vite 7, Capacitor 8, Android Gradle Plugin \ No newline at end of file diff --git a/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/架构设计.md b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/架构设计.md new file mode 100644 index 0000000..0e08580 --- /dev/null +++ b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/架构设计.md @@ -0,0 +1,4 @@ +- **前端架构**:采用 Vite + Vue 3 (Composition API) 技术栈,通过 `src/api/index.js` 封装 RESTful 请求并处理 Token 鉴权。 +- **跨端桥接**:利用 Capacitor 8 将 Web 应用(`dist` 目录)嵌入原生 Android Shell (`BridgeActivity`),实现从 Web 到 Native 的无缝转换。 +- **配置管理**:通过 `capacitor.config.json` 定义应用 ID、启动屏及状态栏样式,并通过 `vite.config.js` 代理后端 API 请求。 +- **构建流程**:遵循“Vite 构建 Web -> Cap sync 同步资源 -> Gradle 编译 APK”的标准化流水线,由 `build-apk.bat` 脚本自动化执行。 \ No newline at end of file diff --git a/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/概述.md b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/概述.md new file mode 100644 index 0000000..a504770 --- /dev/null +++ b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/概述.md @@ -0,0 +1 @@ +基于 Vue 3 与 Capacitor 构建的网盘资源管理与海报展示跨端应用,支持 Android APK 打包。 \ No newline at end of file diff --git a/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/特殊配置与命令.md b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/特殊配置与命令.md new file mode 100644 index 0000000..bfc7aa9 --- /dev/null +++ b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/特殊配置与命令.md @@ -0,0 +1 @@ +- **APK 打包**:运行根目录下的 `build-apk.bat` 脚本,自动完成 Web 资源构建、Capacitor 同步并唤起 Android Studio 进行最终编译。 \ No newline at end of file diff --git a/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/编码规范.md b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/编码规范.md new file mode 100644 index 0000000..ab9fc8b --- /dev/null +++ b/.qoder/repowiki/knowledge/zh/网盘管理跨端应用/编码规范.md @@ -0,0 +1,2 @@ +- API 模块统一在 `src/api/index.js` 中导出,使用自定义 `request` 包装器处理全局认证头与 401/403 状态分发。 +- 组件采用 ` + + diff --git a/vue/package-lock.json b/vue/package-lock.json new file mode 100644 index 0000000..cc54867 --- /dev/null +++ b/vue/package-lock.json @@ -0,0 +1,2434 @@ +{ + "name": "vue", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vue", + "version": "0.0.0", + "dependencies": { + "@capacitor/android": "^8.4.0", + "@capacitor/app": "^8.1.0", + "@capacitor/cli": "^8.4.0", + "@capacitor/core": "^8.4.0", + "@capacitor/ios": "^8.4.0", + "@capacitor/splash-screen": "^8.0.1", + "@capacitor/status-bar": "^8.0.2", + "vue": "^3.5.17" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.0", + "vite": "^7.0.4" + } + }, + "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/@capacitor/android": { + "version": "8.4.0", + "resolved": "https://registry.npmmirror.com/@capacitor/android/-/android-8.4.0.tgz", + "integrity": "sha512-K1ZPkQzvRzPEALz9nBdLx5p5nAPzp5fsTYWk7LRiKZeH/NXqjDvqfTv7lrLgrziQNoDeaL6ijg64oBREzXiV+g==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^8.4.0" + } + }, + "node_modules/@capacitor/app": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/@capacitor/app/-/app-8.1.0.tgz", + "integrity": "sha512-MlmttTOWHDedr/G4SrhNRxsXMqY+R75S4MM4eIgzsgCzOYhb/MpCkA5Q3nuOCfL1oHm26xjUzqZ5aupbOwdfYg==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@capacitor/cli": { + "version": "8.4.0", + "resolved": "https://registry.npmmirror.com/@capacitor/cli/-/cli-8.4.0.tgz", + "integrity": "sha512-5Z9RKHxiqJYRTLrfMeZmzR4qrlg5B85MxsWZ5goyXsLkO3bgpW9a1qV/6fR1SX9s5gwLza5y7PZVwITl/hDJ7g==", + "license": "MIT", + "dependencies": { + "@ionic/cli-framework-output": "^2.2.8", + "@ionic/utils-subprocess": "^3.0.1", + "@ionic/utils-terminal": "^2.3.5", + "commander": "^12.1.0", + "debug": "^4.4.0", + "env-paths": "^2.2.0", + "fs-extra": "^11.2.0", + "kleur": "^4.1.5", + "native-run": "^2.0.3", + "open": "^8.4.0", + "plist": "^3.1.0", + "prompts": "^2.4.2", + "rimraf": "^6.0.1", + "semver": "^7.6.3", + "tar": "^7.5.3", + "tslib": "^2.8.1", + "xml2js": "^0.6.2" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@capacitor/core": { + "version": "8.4.0", + "resolved": "https://registry.npmmirror.com/@capacitor/core/-/core-8.4.0.tgz", + "integrity": "sha512-LrS1xPIrqLtJABBIPDGXxxKmI9OyesrzWw8DiHbxhSC9JoiLUleUAJlX1a0LWIVLRbuY4Szgf9huFeRqYH2SAQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/ios": { + "version": "8.4.0", + "resolved": "https://registry.npmmirror.com/@capacitor/ios/-/ios-8.4.0.tgz", + "integrity": "sha512-tnwstEdbTJ2nHAfoAwnurXgYRscWeLY+IIGdz69o24gN2Crfj9Xc0TWo8L5uFLF1LmpbUywH1IT0U1oHV8c+CA==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^8.4.0" + } + }, + "node_modules/@capacitor/splash-screen": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/@capacitor/splash-screen/-/splash-screen-8.0.1.tgz", + "integrity": "sha512-c/ew/Z3eA7z8l06WoRAtzVF16VwYYrExmHmfGq1Cg675pVzaC/yuucB8/1xG1vhEfnW4fZ1KhSf/kzR1RiVYgg==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@capacitor/status-bar": { + "version": "8.0.2", + "resolved": "https://registry.npmmirror.com/@capacitor/status-bar/-/status-bar-8.0.2.tgz", + "integrity": "sha512-WXs8YB8B9eEaPZz+bcdY6t2nForF1FLoj/JU0Dl9RRgQnddnS98FEEyDooQhaY7wivr000j4+SC1FyeJkrFO7A==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.8", + "resolved": "https://registry.npmmirror.com/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz", + "integrity": "sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==", + "license": "MIT", + "dependencies": { + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmmirror.com/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process": { + "version": "2.1.12", + "resolved": "https://registry.npmmirror.com/@ionic/utils-process/-/utils-process-2.1.12.tgz", + "integrity": "sha512-Jqkgyq7zBs/v/J3YvKtQQiIcxfJyplPgECMWgdO0E1fKrrH8EF0QGHNJ9mJCn6PYe2UtHNS8JJf5G21e09DfYg==", + "license": "MIT", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmmirror.com/@ionic/utils-stream/-/utils-stream-3.1.7.tgz", + "integrity": "sha512-eSELBE7NWNFIHTbTC2jiMvh1ABKGIpGdUIvARsNPMNQhxJB3wpwdiVnoBoTYp+5a6UUIww4Kpg7v6S7iTctH1w==", + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/@ionic/utils-subprocess/-/utils-subprocess-3.0.1.tgz", + "integrity": "sha512-cT4te3AQQPeIM9WCwIg8ohroJ8TjsYaMb2G4ZEgv9YzeDqHZ4JpeIKqG2SoaA3GmVQ3sOfhPM6Ox9sxphV/d1A==", + "license": "MIT", + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.12", + "@ionic/utils-stream": "3.1.7", + "@ionic/utils-terminal": "2.3.5", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@ionic/utils-terminal/-/utils-terminal-2.3.5.tgz", + "integrity": "sha512-3cKScz9Jx2/Pr9ijj1OzGlBDfcmx7OMVBt4+P1uRR0SSW4cm1/y3Mo4OY3lfkuaYifMNBW8Wz6lQHbs1bihr7A==", + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "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/@rollup/rollup-android-arm-eabi": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", + "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", + "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", + "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", + "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", + "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", + "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", + "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", + "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", + "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", + "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", + "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", + "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", + "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", + "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", + "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", + "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", + "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", + "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", + "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", + "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", + "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", + "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", + "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", + "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", + "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "25.9.2", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.9.2.tgz", + "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==", + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz", + "integrity": "sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==", + "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/@vue/compiler-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.35.tgz", + "integrity": "sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/shared": "3.5.35", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.35.tgz", + "integrity": "sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.35.tgz", + "integrity": "sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.35", + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.15", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.35.tgz", + "integrity": "sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.35.tgz", + "integrity": "sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.35.tgz", + "integrity": "sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.35.tgz", + "integrity": "sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/runtime-core": "3.5.35", + "@vue/shared": "3.5.35", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.35.tgz", + "integrity": "sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35" + }, + "peerDependencies": { + "vue": "3.5.35" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.35.tgz", + "integrity": "sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==", + "license": "MIT" + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.9.10.tgz", + "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmmirror.com/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "license": "Apache-2.0", + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "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/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "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/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.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/fs-extra": { + "version": "11.3.5", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.5.tgz", + "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "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/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmmirror.com/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "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/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "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/native-run": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/native-run/-/native-run-2.0.3.tgz", + "integrity": "sha512-U1PllBuzW5d1gfan+88L+Hky2eZx+9gv3Pf6rNBxKbORxi7boHzqiA6QFGSnqMem4j0A9tZ08NMIs5+0m/VS1Q==", + "license": "MIT", + "dependencies": { + "@ionic/utils-fs": "^3.1.7", + "@ionic/utils-terminal": "^2.3.4", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^4.1.1", + "plist": "^3.1.0", + "split2": "^4.2.0", + "through2": "^4.0.2", + "tslib": "^2.6.2", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "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.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/plist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/plist/-/plist-3.1.1.tgz", + "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "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.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", + "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.61.1", + "@rollup/rollup-android-arm64": "4.61.1", + "@rollup/rollup-darwin-arm64": "4.61.1", + "@rollup/rollup-darwin-x64": "4.61.1", + "@rollup/rollup-freebsd-arm64": "4.61.1", + "@rollup/rollup-freebsd-x64": "4.61.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", + "@rollup/rollup-linux-arm-musleabihf": "4.61.1", + "@rollup/rollup-linux-arm64-gnu": "4.61.1", + "@rollup/rollup-linux-arm64-musl": "4.61.1", + "@rollup/rollup-linux-loong64-gnu": "4.61.1", + "@rollup/rollup-linux-loong64-musl": "4.61.1", + "@rollup/rollup-linux-ppc64-gnu": "4.61.1", + "@rollup/rollup-linux-ppc64-musl": "4.61.1", + "@rollup/rollup-linux-riscv64-gnu": "4.61.1", + "@rollup/rollup-linux-riscv64-musl": "4.61.1", + "@rollup/rollup-linux-s390x-gnu": "4.61.1", + "@rollup/rollup-linux-x64-gnu": "4.61.1", + "@rollup/rollup-linux-x64-musl": "4.61.1", + "@rollup/rollup-openbsd-x64": "4.61.1", + "@rollup/rollup-openharmony-arm64": "4.61.1", + "@rollup/rollup-win32-arm64-msvc": "4.61.1", + "@rollup/rollup-win32-ia32-msvc": "4.61.1", + "@rollup/rollup-win32-x64-gnu": "4.61.1", + "@rollup/rollup-win32-x64-msvc": "4.61.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==", + "license": "ISC" + }, + "node_modules/semver": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.3.tgz", + "integrity": "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "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/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "7.5.16", + "resolved": "https://registry.npmmirror.com/tar/-/tar-7.5.16.tgz", + "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "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/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz", + "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.35.tgz", + "integrity": "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-sfc": "3.5.35", + "@vue/runtime-dom": "3.5.35", + "@vue/server-renderer": "3.5.35", + "@vue/shared": "3.5.35" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmmirror.com/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/vue/package.json b/vue/package.json new file mode 100644 index 0000000..3355124 --- /dev/null +++ b/vue/package.json @@ -0,0 +1,25 @@ +{ + "name": "vue", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@capacitor/android": "^8.4.0", + "@capacitor/app": "^8.1.0", + "@capacitor/cli": "^8.4.0", + "@capacitor/core": "^8.4.0", + "@capacitor/ios": "^8.4.0", + "@capacitor/splash-screen": "^8.0.1", + "@capacitor/status-bar": "^8.0.2", + "vue": "^3.5.17" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6.0.0", + "vite": "^7.0.4" + } +} diff --git a/vue/public/logo.png b/vue/public/logo.png new file mode 100644 index 0000000..f480038 Binary files /dev/null and b/vue/public/logo.png differ diff --git a/vue/public/vite.svg b/vue/public/vite.svg new file mode 100644 index 0000000..03a4521 --- /dev/null +++ b/vue/public/vite.svg @@ -0,0 +1,3220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vue/src/App.vue b/vue/src/App.vue new file mode 100644 index 0000000..92de3c8 --- /dev/null +++ b/vue/src/App.vue @@ -0,0 +1,1946 @@ + + + diff --git a/vue/src/api/index.js b/vue/src/api/index.js new file mode 100644 index 0000000..f4bc3ae --- /dev/null +++ b/vue/src/api/index.js @@ -0,0 +1,689 @@ +import { reactive } from 'vue' +import { AUTH_SESSION_STORAGE_KEY, readStorage, removeStorage } from '../utils/storage' +import { createLoginLog } from '../utils/data' + +const API_BASE_URL = '/api' +const TOKEN_EXPIRING_SOON_MS = 10 * 60 * 1000 + +/** + * 全局请求状态(响应式) + * pendingCount > 0 表示有请求正在进行 + */ +export const requestState = reactive({ pendingCount: 0 }) + +function getAuthSession() { + return readStorage(AUTH_SESSION_STORAGE_KEY, null) +} + +function isSessionExpired(session) { + if (!session?.token) return true + if (!session?.expiresAt) return false + return Date.now() >= session.expiresAt +} + +function clearExpiredSession() { + removeStorage(AUTH_SESSION_STORAGE_KEY) +} + +function dispatchUnauthorized(reason = '登录状态已失效,请重新登录') { + if (typeof window === 'undefined') return + window.dispatchEvent(new CustomEvent('wangpan:unauthorized', { detail: { reason } })) +} + +function dispatchForbidden(reason = '当前账号无权执行此操作') { + if (typeof window === 'undefined') return + window.dispatchEvent(new CustomEvent('wangpan:forbidden', { detail: { reason } })) +} + +function dispatchSessionExpiring(reason = '登录状态将在 10 分钟内过期,请注意及时重新登录') { + if (typeof window === 'undefined') return + window.dispatchEvent(new CustomEvent('wangpan:session-expiring', { detail: { reason } })) +} + +function notifySessionExpiring(session) { + if (!session?.expiresAt || session?.expiringSoonNotified) return + const remainingMs = session.expiresAt - Date.now() + if (remainingMs > 0 && remainingMs <= TOKEN_EXPIRING_SOON_MS) { + const nextSession = { + ...session, + expiringSoonNotified: true, + } + localStorage.setItem(AUTH_SESSION_STORAGE_KEY, JSON.stringify(nextSession)) + dispatchSessionExpiring() + } +} + +function buildAuthHeaders(extraHeaders = {}) { + const session = getAuthSession() + if (isSessionExpired(session)) { + clearExpiredSession() + return extraHeaders + } + + const token = session?.token + const tokenType = session?.tokenType || 'Bearer' + + if (!token) { + return extraHeaders + } + + return { + Authorization: `${tokenType} ${token}`, + ...extraHeaders, + } +} + +async function request(url, options = {}) { + // 延迟显示:150ms内完成的请求不显示加载动画(避免闪烁) + let showTimer = null + let incremented = false + if (!options.silent) { + showTimer = setTimeout(() => { requestState.pendingCount++; incremented = true }, 150) + } + + const session = getAuthSession() + const requiresAuth = options.requiresAuth !== false + + if (requiresAuth && isSessionExpired(session)) { + clearTimeout(showTimer) + clearExpiredSession() + dispatchUnauthorized('登录已过期,请重新登录') + const error = new Error('登录已过期,请重新登录') + error.status = 401 + error.code = 401 + throw error + } + + if (requiresAuth) { + notifySessionExpiring(session) + } + + try { + console.log(`[API Request] ${options.method || 'GET'} ${url}`) + + const isFormData = options.body instanceof FormData + const response = await fetch(url, { + headers: { + ...(isFormData ? {} : { 'Content-Type': 'application/json' }), + ...buildAuthHeaders(options.headers || {}), + }, + ...options, + }) + + const contentType = response.headers.get('content-type') || '' + const data = contentType.includes('application/json') ? await response.json() : null + + console.log(`[API Response] ${response.status}`, data) + + if (!response.ok) { + if (response.status === 401) { + clearExpiredSession() + dispatchUnauthorized(data?.message || '登录状态已失效,请重新登录') + } + + if (response.status === 403) { + dispatchForbidden(data?.message || '当前账号无权执行此操作') + } + + const error = new Error(data?.message || `请求失败:${response.status}`) + error.status = response.status + error.code = data?.code ?? response.status + error.payload = data + throw error + } + + return data + } catch (err) { + // 网络错误(如 Failed to fetch) + if (err.name === 'TypeError' && err.message === 'Failed to fetch') { + console.error('[Network Error] 无法连接到后端服务,请检查:') + console.error('1. 后端服务是否启动在 http://localhost:8888') + console.error('2. 是否有 CORS 跨域问题') + console.error('3. 网络连接是否正常') + + const networkError = new Error('无法连接到后端服务,请检查后端是否启动') + networkError.isNetworkError = true + throw networkError + } + + // 其他错误直接抛出 + throw err + } finally { + clearTimeout(showTimer) + if (incremented) { + requestState.pendingCount-- + } + } +} + +export function login(payload) { + return request(`${API_BASE_URL}/login`, { + method: 'POST', + requiresAuth: false, + body: JSON.stringify({ + username: payload.username, + password: payload.password, + }), + }) +} + +export function register(payload) { + const body = { username: payload.username, password: payload.password } + if (payload.email) body.email = payload.email + if (payload.phone) body.phone = payload.phone + return request(`${API_BASE_URL}/register`, { + method: 'POST', + requiresAuth: false, + body: JSON.stringify(body), + }) +} + +export function fetchDashboardData() { + return Promise.resolve({ + code: 200, + data: [], + }) +} + +export function saveRecord(payload) { + return Promise.resolve({ + code: 200, + data: payload, + }) +} + +export function deleteRecord(id) { + return Promise.resolve({ + code: 200, + data: { id }, + }) +} + +export function exportData() { + return Promise.resolve({ + code: 200, + data: {}, + }) +} + +export function resetData() { + return Promise.resolve({ + code: 200, + data: [], + }) +} + +export function fetchLoginLogs(params = {}) { + const searchParams = new URLSearchParams() + if (params.userId) searchParams.set('userId', String(params.userId)) + if (params.username) searchParams.set('username', params.username) + if (params.result) searchParams.set('result', params.result) + if (params.startDate) searchParams.set('startDate', params.startDate) + if (params.endDate) searchParams.set('endDate', params.endDate) + if (params.pageNo) searchParams.set('pageNo', String(params.pageNo)) + if (params.pageSize) searchParams.set('pageSize', String(params.pageSize)) + + const query = searchParams.toString() + return request(`${API_BASE_URL}/login-logs${query ? `?${query}` : ''}`, { + method: 'GET', + }).then((res) => ({ + ...res, + data: { + ...(res?.data || {}), + list: Array.isArray(res?.data?.list) + ? res.data.list.map((item) => + createLoginLog( + item?.userAgent || '未知设备', + item?.loginIp || '127.0.0.1', + { + id: item?.id, + time: item?.loginTime, + hashSeed: `${item?.userId || ''}-${item?.loginUsername || ''}-${item?.loginTime || ''}`, + username: item?.loginUsername, + result: item?.loginResult, + userId: item?.userId, + rawUserAgent: item?.userAgent, + failReason: item?.failReason, + }, + ), + ) + : [], + total: res?.data?.total || 0, // 返回总记录数 + }, + })) +} + +export function fetchTmdbConfig() { + return request(`${API_BASE_URL}/tmdb/key`, { + method: 'GET', + }) +} + +export function saveTmdbConfig(payload) { + return request(`${API_BASE_URL}/tmdb/key`, { + method: 'POST', + body: JSON.stringify({ + tmdbApiKey: payload.tmdbApiKey, + }), + }) +} + +// ==================== 网盘管理 API ==================== + +/** + * 创建网盘 + * @param {Object} payload - { name, description, logoData } + * @returns {Promise} + */ +export function createDrive(payload) { + return request(`${API_BASE_URL}/drive`, { + method: 'POST', + body: JSON.stringify({ + name: payload.name, + description: payload.description || '', + logoData: payload.logoData || '', + }), + }) +} + +/** + * 查询网盘列表 + * @param {Object} params - { pageNo, pageSize } + * @returns {Promise} + */ +export function fetchDriveList(params = {}) { + const searchParams = new URLSearchParams() + if (params.pageNo) searchParams.set('pageNo', String(params.pageNo)) + if (params.pageSize) searchParams.set('pageSize', String(params.pageSize)) + + const query = searchParams.toString() + return request(`${API_BASE_URL}/drive${query ? `?${query}` : ''}`, { + method: 'GET', + }) +} + +/** + * 删除网盘 + * @param {string} driveId - 网盘ID + * @returns {Promise} + */ +export function deleteDrive(driveId) { + return request(`${API_BASE_URL}/drive/${driveId}`, { + method: 'DELETE', + }) +} + +/** + * 更新网盘 + * @param {string} driveId - 网盘ID + * @param {Object} payload - { name, description, logoData } + * @returns {Promise} + */ +export function updateDrive(driveId, payload) { + return request(`${API_BASE_URL}/drive/${driveId}`, { + method: 'PUT', + body: JSON.stringify({ + name: payload.name, + description: payload.description, + logoData: payload.logoData, + }), + }) +} + +// ==================== 文件夹管理 API ==================== + +/** + * 查询文件夹列表 + * @param {string|number} driveId - 网盘ID + * @param {Object} params - { parentId } + * @returns {Promise} + */ +export function fetchFolders(driveId, params = {}) { + const searchParams = new URLSearchParams() + if (params.parentId !== undefined) searchParams.set('parentId', String(params.parentId)) + const query = searchParams.toString() + return request(`${API_BASE_URL}/drive/${driveId}/folders${query ? `?${query}` : ''}`, { + method: 'GET', + }) +} + +/** + * 查询文件夹详情 + * @param {string|number} driveId - 网盘ID + * @param {string|number} folderId - 文件夹ID + * @returns {Promise} + */ +export function fetchFolderDetail(driveId, folderId) { + return request(`${API_BASE_URL}/drive/${driveId}/folders/${folderId}`, { + method: 'GET', + }) +} + +/** + * 创建文件夹 + * @param {string|number} driveId - 网盘ID + * @param {Object} payload - { name, parentId, description, sortWeight } + * @returns {Promise} + */ +export function createFolderInDrive(driveId, payload) { + return request(`${API_BASE_URL}/drive/${driveId}/folders`, { + method: 'POST', + body: JSON.stringify({ + name: payload.name, + parentId: payload.parentId ?? 0, + description: payload.description || '', + sortWeight: payload.sortWeight ?? 0, + }), + }) +} + +/** + * 更新文件夹 + * @param {string|number} driveId - 网盘ID + * @param {string|number} folderId - 文件夹ID + * @param {Object} payload - { name, description, sortWeight } + * @returns {Promise} + */ +export function updateFolderInDrive(driveId, folderId, payload) { + const body = {} + if (payload.name !== undefined) body.name = payload.name + if (payload.description !== undefined) body.description = payload.description + if (payload.sortWeight !== undefined) body.sortWeight = payload.sortWeight + return request(`${API_BASE_URL}/drive/${driveId}/folders/${folderId}`, { + method: 'PUT', + body: JSON.stringify(body), + }) +} + +/** + * 删除文件夹 + * @param {string|number} driveId - 网盘ID + * @param {string|number} folderId - 文件夹ID + * @returns {Promise} + */ +export function deleteFolderInDrive(driveId, folderId) { + return request(`${API_BASE_URL}/drive/${driveId}/folders/${folderId}`, { + method: 'DELETE', + }) +} + +// ==================== 电影文件管理 API ==================== + +/** + * 查询指定文件夹下的电影列表 + * @param {string|number} driveId - 网盘ID + * @param {string|number} folderId - 文件夹ID,0表示根目录 + * @param {Object} params - { pageNo, pageSize } + * @returns {Promise} + */ +export function fetchFolderMovies(driveId, folderId, params = {}) { + const searchParams = new URLSearchParams() + if (params.pageNo) searchParams.set('pageNo', String(params.pageNo)) + if (params.pageSize) searchParams.set('pageSize', String(params.pageSize)) + const query = searchParams.toString() + return request(`${API_BASE_URL}/drive/${driveId}/folders/${folderId}/movies${query ? `?${query}` : ''}`, { + method: 'GET', + }) +} + +/** + * 查询电影列表 + * @param {string|number} driveId - 网盘ID + * @param {Object} params - { folderId, pageNo, pageSize } + * @returns {Promise} + */ +export function fetchMovieList(driveId, params = {}) { + const searchParams = new URLSearchParams() + if (params.folderId !== undefined) searchParams.set('folderId', String(params.folderId)) + if (params.pageNo) searchParams.set('pageNo', String(params.pageNo)) + if (params.pageSize) searchParams.set('pageSize', String(params.pageSize)) + const query = searchParams.toString() + return request(`${API_BASE_URL}/drive/${driveId}/movies${query ? `?${query}` : ''}`, { + method: 'GET', + }) +} + +/** + * 查询电影详情 + * @param {string|number} driveId - 网盘ID + * @param {string|number} movieId - 电影ID + * @returns {Promise} + */ +export function fetchMovieDetail(driveId, movieId) { + return request(`${API_BASE_URL}/drive/${driveId}/movies/${movieId}`, { + method: 'GET', + }) +} + +/** + * 创建电影(仅登记文件名) + * @param {string|number} driveId - 网盘ID + * @param {Object} payload - { title, folderId } + * @returns {Promise} + */ +export function createMovieInDrive(driveId, payload) { + return request(`${API_BASE_URL}/drive/${driveId}/movies`, { + method: 'POST', + body: JSON.stringify({ + title: payload.title, + folderId: payload.folderId ?? 0, + }), + }) +} + +/** + * 更新电影信息 + * @param {string|number} driveId - 网盘ID + * @param {string|number} movieId - 电影ID + * @param {Object} payload - 可选字段:title, originalName, subtitle, description, rating, tags, category + * @returns {Promise} + */ +export function updateMovieInDrive(driveId, movieId, payload) { + const body = {} + if (payload.title !== undefined) body.title = payload.title + if (payload.originalName !== undefined) body.originalName = payload.originalName + if (payload.subtitle !== undefined) body.subtitle = payload.subtitle + if (payload.description !== undefined) body.description = payload.description + if (payload.rating !== undefined) body.rating = payload.rating + if (payload.tags !== undefined) body.tags = payload.tags + if (payload.category !== undefined) body.category = payload.category + + return request(`${API_BASE_URL}/drive/${driveId}/movies/${movieId}`, { + method: 'PUT', + body: JSON.stringify(body), + }) +} + +/** + * 删除电影 + * @param {string|number} driveId - 网盘ID + * @param {string|number} movieId - 电影ID + * @returns {Promise} + */ +export function deleteMovieInDrive(driveId, movieId) { + return request(`${API_BASE_URL}/drive/${driveId}/movies/${movieId}`, { + method: 'DELETE', + }) +} + +/** + * 查询重复电影 + * @returns {Promise} + */ +export function fetchMovieDuplicates() { + return request(`${API_BASE_URL}/drive/movies/duplicates`, { + method: 'GET', + }) +} + +// ==================== 图标管理 API ==================== + +/** + * 查询图标列表 + * @returns {Promise} + */ +export function fetchIconList() { + return request(`${API_BASE_URL}/icon`, { method: 'GET' }) +} + +/** + * 查询单个图标详情 + * @param {string|number} id - 图标ID + * @returns {Promise} + */ +export function fetchIconDetail(id) { + return request(`${API_BASE_URL}/icon/${id}`, { method: 'GET' }) +} + +/** + * 创建图标 + * @param {Object} payload - { name, url?, internalUrl?, linkType?, description?, sortOrder? } + * @returns {Promise} + */ +export function createIcon(payload) { + return request(`${API_BASE_URL}/icon`, { + method: 'POST', + body: JSON.stringify({ + name: payload.name, + url: payload.url || '', + internalUrl: payload.internalUrl || '', + linkType: payload.linkType || 'external', + description: payload.description || '', + sortOrder: payload.sortOrder ?? 0, + }), + }) +} + +/** + * 更新图标 + * @param {string|number} id - 图标ID + * @param {Object} payload - { name?, url?, internalUrl?, linkType?, description?, sortOrder? } + * @returns {Promise} + */ +export function updateIcon(id, payload) { + const body = {} + if (payload.name !== undefined) body.name = payload.name + if (payload.url !== undefined) body.url = payload.url + if (payload.internalUrl !== undefined) body.internalUrl = payload.internalUrl + if (payload.linkType !== undefined) body.linkType = payload.linkType + if (payload.description !== undefined) body.description = payload.description + if (payload.sortOrder !== undefined) body.sortOrder = payload.sortOrder + return request(`${API_BASE_URL}/icon/${id}`, { + method: 'PUT', + body: JSON.stringify(body), + }) +} + +/** + * 删除图标 + * @param {string|number} id - 图标ID + * @returns {Promise} + */ +export function deleteIcon(id) { + return request(`${API_BASE_URL}/icon/${id}`, { method: 'DELETE' }) +} + +/** + * 上传图标 Logo 图片 + * @param {string|number} id - 图标ID + * @param {File} file - 图片文件(png/jpg/gif/webp/ico,最大 1MB) + * @returns {Promise} 返回 { code, data: { id, name, logoUrl, updateTime } } + */ +export function uploadIconLogo(id, file) { + const formData = new FormData() + formData.append('file', file) + return request(`${API_BASE_URL}/icon/${id}/logo`, { + method: 'POST', + body: formData, + }) +} + +// ==================== 用户管理 API(仅管理员) ==================== + +/** + * 查询用户列表 + * @param {Object} params - 查询参数 + * @param {string} [params.username] - 用户名模糊搜索 + * @param {number} [params.status] - 状态:0=禁用, 1=正常, 2=锁定 + * @param {number} [params.userType] - 角色:0=普通, 1=VIP, 2=管理员 + * @param {number} [params.pageNo] - 页码,默认1 + * @param {number} [params.pageSize] - 每页条数,默认20 + * @returns {Promise} 返回格式:{ code: 200, data: { total, list: [{ id, username, userType, status, ... }], pageNo, pageSize } } + */ +export function fetchUserList(params = {}) { + const searchParams = new URLSearchParams() + if (params.username) searchParams.set('username', params.username) + if (params.status !== undefined && params.status !== '') searchParams.set('status', String(params.status)) + if (params.userType !== undefined && params.userType !== '') searchParams.set('userType', String(params.userType)) + if (params.pageNo) searchParams.set('pageNo', String(params.pageNo)) + if (params.pageSize) searchParams.set('pageSize', String(params.pageSize)) + + const query = searchParams.toString() + return request(`${API_BASE_URL}/users${query ? `?${query}` : ''}`, { + method: 'GET', + }) +} + +/** + * 新增用户 + * @param {Object} payload - 用户信息 + * @param {string} payload.username - 用户名(必填) + * @param {string} payload.password - 密码(必填) + * @param {string} [payload.email] - 邮箱(可选) + * @param {string} [payload.phone] - 手机号(可选) + * @param {string} [payload.realName] - 真实姓名(可选) + * @param {number} [payload.status=1] - 状态:0=禁用, 1=正常 + * @param {number} [payload.userType=0] - 角色:0=普通, 1=VIP, 2=管理员 + * @returns {Promise} + */ +export function createUser(payload) { + return request(`${API_BASE_URL}/users`, { + method: 'POST', + body: JSON.stringify({ + username: payload.username, + password: payload.password, + email: payload.email || '', + phone: payload.phone || '', + realName: payload.realName || '', + status: payload.status !== undefined ? payload.status : 1, + userType: payload.userType !== undefined ? payload.userType : 0, + }), + }) +} + +/** + * 修改用户信息 + * @param {number} userId - 用户ID + * @param {Object} payload - 需要修改的字段(部分更新) + * @param {string} [payload.email] - 邮箱 + * @param {string} [payload.phone] - 手机号 + * @param {string} [payload.realName] - 真实姓名 + * @param {number} [payload.status] - 状态:0=禁用, 1=正常, 2=锁定 + * @param {number} [payload.userType] - 角色:0=普通, 1=VIP, 2=管理员 + * @returns {Promise} + */ +export function updateUser(userId, payload) { + // 构建请求体,只包含需要更新的字段 + const body = {} + if (payload.email !== undefined) body.email = payload.email + if (payload.phone !== undefined) body.phone = payload.phone + if (payload.realName !== undefined) body.realName = payload.realName + if (payload.status !== undefined) body.status = payload.status + if (payload.userType !== undefined) body.userType = payload.userType + + return request(`${API_BASE_URL}/users/${userId}`, { + method: 'PUT', + body: JSON.stringify(body), + }) +} + +/** + * 删除用户(软删除,将is_deleted设为1) + * @param {number} userId - 要删除的用户ID + * @returns {Promise} + */ +export function deleteUser(userId) { + return request(`${API_BASE_URL}/users/${userId}`, { + method: 'DELETE', + }) +} diff --git a/vue/src/assets/vue.svg b/vue/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/vue/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/vue/src/components/ConfirmDialog.vue b/vue/src/components/ConfirmDialog.vue new file mode 100644 index 0000000..2a3e474 --- /dev/null +++ b/vue/src/components/ConfirmDialog.vue @@ -0,0 +1,210 @@ + + + + + diff --git a/vue/src/components/DriveManageDialog.vue b/vue/src/components/DriveManageDialog.vue new file mode 100644 index 0000000..bf52a2e --- /dev/null +++ b/vue/src/components/DriveManageDialog.vue @@ -0,0 +1,1544 @@ + + + + + diff --git a/vue/src/components/FolderDialog.vue b/vue/src/components/FolderDialog.vue new file mode 100644 index 0000000..70d49a7 --- /dev/null +++ b/vue/src/components/FolderDialog.vue @@ -0,0 +1,101 @@ + + + diff --git a/vue/src/components/FolderTree.vue b/vue/src/components/FolderTree.vue new file mode 100644 index 0000000..529ffde --- /dev/null +++ b/vue/src/components/FolderTree.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/vue/src/components/IconManageDialog.vue b/vue/src/components/IconManageDialog.vue new file mode 100644 index 0000000..9d70723 --- /dev/null +++ b/vue/src/components/IconManageDialog.vue @@ -0,0 +1,780 @@ + + + + + diff --git a/vue/src/components/LoginView.vue b/vue/src/components/LoginView.vue new file mode 100644 index 0000000..9bcbeb0 --- /dev/null +++ b/vue/src/components/LoginView.vue @@ -0,0 +1,297 @@ + + + diff --git a/vue/src/components/PosterCard.vue b/vue/src/components/PosterCard.vue new file mode 100644 index 0000000..2df5255 --- /dev/null +++ b/vue/src/components/PosterCard.vue @@ -0,0 +1,54 @@ + + + diff --git a/vue/src/components/RecordDialog.vue b/vue/src/components/RecordDialog.vue new file mode 100644 index 0000000..c64c9ba --- /dev/null +++ b/vue/src/components/RecordDialog.vue @@ -0,0 +1,160 @@ + + + diff --git a/vue/src/components/SimpleDialog.vue b/vue/src/components/SimpleDialog.vue new file mode 100644 index 0000000..8582594 --- /dev/null +++ b/vue/src/components/SimpleDialog.vue @@ -0,0 +1,56 @@ + + + diff --git a/vue/src/components/TreeNode.vue b/vue/src/components/TreeNode.vue new file mode 100644 index 0000000..be67866 --- /dev/null +++ b/vue/src/components/TreeNode.vue @@ -0,0 +1,299 @@ + + + + + diff --git a/vue/src/components/UserManageDialog.vue b/vue/src/components/UserManageDialog.vue new file mode 100644 index 0000000..3443a78 --- /dev/null +++ b/vue/src/components/UserManageDialog.vue @@ -0,0 +1,1340 @@ + + + + + + diff --git a/vue/src/main.js b/vue/src/main.js new file mode 100644 index 0000000..2425c0f --- /dev/null +++ b/vue/src/main.js @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/vue/src/style.css b/vue/src/style.css new file mode 100644 index 0000000..27bbd4c --- /dev/null +++ b/vue/src/style.css @@ -0,0 +1,2857 @@ +:root { + color-scheme: light; + --bg: #f4f7fb; + --panel: #ffffff; + --ink: #172033; + --muted: #64748b; + --line: #d9e2ef; + --brand: #0f766e; + --brand-dark: #0b5d57; + --accent: #f59e0b; + --danger: #dc2626; + --shadow: 0 18px 50px rgba(19, 31, 51, 0.14); +} + +* { + box-sizing: border-box; + -webkit-tap-highlight-color: transparent; +} + +html { + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + touch-action: manipulation; +} + +html, +body { + min-width: 0; + min-height: 100%; + overflow: hidden auto; + -webkit-overflow-scrolling: touch; +} + +#app { + min-width: 0; + min-height: 100%; + min-height: -webkit-fill-available; +} + +button, +input, +textarea, +select { + font: inherit; +} + +button { + border: 0; + border-radius: 999px; + background: var(--brand); + color: #fff; + cursor: pointer; + min-height: 40px; + padding: 0 18px; + font-weight: 700; + transition: + transform 0.2s ease, + background 0.2s ease, + opacity 0.2s ease; +} + +button:hover { + background: var(--brand-dark); + transform: translateY(-1px); +} + +button:disabled { + opacity: 0.72; + cursor: not-allowed; + transform: none; +} + +button.secondary { + background: #e7eef7; + color: #243247; +} + +button.secondary:hover { + background: #d8e3f1; +} + +button.danger, +.icon-btn.danger { + background: #fee2e2; + color: var(--danger); +} + +button.danger:hover, +.icon-btn.danger:hover { + background: #fecaca; +} + +input, +textarea, +select { + width: 100%; + border: 1px solid var(--line); + border-radius: 14px; + background: #fff; + color: var(--ink); + outline: none; + padding: 12px 14px; +} + +textarea { + min-height: 110px; + resize: vertical; +} + +input:focus, +textarea:focus, +select:focus { + border-color: var(--brand); + box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.14); +} + +ul { + margin: 0; + padding-left: 18px; +} + +.app-wrapper { + min-height: 100vh; +} + +.login-shell { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 12px 16px; + overflow: hidden; + background: + radial-gradient(ellipse at 50% 80%, rgba(99, 31, 191, 0.8) 0%, rgba(3, 2, 12, 0.95) 60%), + linear-gradient(135deg, #060310, #0a0520); + isolation: isolate; +} + +.login-panel { + position: relative; + width: min(500px, 100%); + max-height: none; + overflow: hidden; + border: 1px solid rgba(197, 170, 255, 0.24); + border-radius: 24px; + background: rgba(12, 8, 32, 0.88); + box-shadow: 0 24px 80px rgba(0, 0, 0, 0.6); + padding: 20px 32px 24px; + text-align: center; + backdrop-filter: blur(16px); +} + +.login-panel::before { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 140px; + border-radius: 0 0 24px 24px; + background: radial-gradient(ellipse at 50% 100%, rgba(139, 67, 255, 0.36), transparent 72%); + pointer-events: none; + z-index: 0; +} + +.login-panel > * { + position: relative; + z-index: 1; +} + +.brand-mark { + display: block; + width: 64px; + height: 64px; + margin: 0 auto 10px; + border-radius: 16px; + object-fit: cover; +} + +.brand-mark--fallback { + display: grid; + place-items: center; + background: linear-gradient(135deg, #8d72ff, #5b38ff); + color: #fff; + font-size: 24px; + font-weight: 800; + letter-spacing: 1px; + box-shadow: 0 12px 24px rgba(91, 56, 255, 0.28); +} + +.login-panel h1 { + margin: 0 0 2px; + color: #f8f7ff; + font-size: 22px; +} + +.login-panel p { + max-width: 380px; + margin: 6px auto 0; + color: rgba(223, 215, 255, 0.72); + line-height: 1.4; + font-size: 13px; +} + +.auth-switch { + display: inline-grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 4px; + width: min(240px, 100%); + margin: 12px auto 0; + padding: 4px; + border: 1px solid rgba(183, 157, 255, 0.18); + border-radius: 999px; + background: rgba(255, 255, 255, 0.08); +} + +.switch-chip { + min-height: 30px; + border-radius: 999px; + background: transparent; + color: rgba(236, 232, 255, 0.84); + box-shadow: none; +} + +.switch-chip:hover { + background: rgba(255, 255, 255, 0.12); +} + +.switch-chip.active { + background: linear-gradient(90deg, rgba(255, 255, 255, 0.94), #8d72ff 48%, #5b38ff); + color: #fff; +} + +.login-form { + display: grid; + gap: 10px; + margin-top: 14px; +} + +.login-form label, +.tmdb-config label, +.field-grid label { + display: grid; + gap: 5px; + text-align: left; +} + +.login-form label span, +.tmdb-config label span, +.field-grid label span { + font-size: 13px; + font-weight: 700; +} + +.login-form label { + color: rgba(236, 232, 255, 0.82); +} + +.captcha-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 120px; + gap: 8px; +} + +canvas { + width: 120px; + height: 40px; + border: 1px solid rgba(183, 157, 255, 0.28); + border-radius: 8px; + background: rgba(15, 12, 35, 0.88); + cursor: pointer; +} + +.form-message { + min-height: 16px; + color: #fecaca; + font-size: 13px; +} + +.form-message.warning { + color: #fde68a; +} + +.form-message.success { + color: #bbf7d0; +} + +.login-form input { + border-color: rgba(183, 157, 255, 0.24); + background: rgba(8, 7, 25, 0.86); + color: #f8f7ff; + padding: 9px 12px; +} + +.login-form input::placeholder { + color: rgba(221, 215, 244, 0.52); +} + +.login-form button[type='submit'] { + min-height: 44px; + margin-top: 4px; + border-radius: 10px; + background: linear-gradient(90deg, rgba(255, 255, 255, 0.9), #8d72ff 42%, #5b38ff); + color: #fff; + box-shadow: 0 10px 28px rgba(99, 74, 255, 0.38); +} + +/* ==================== 快捷图标区域 ==================== */ +.quick-icons { + width: min(1500px, calc(100% - 48px)); + margin: 0 auto 20px; + padding: 20px 24px; + background: linear-gradient(135deg, rgba(255,255,255,0.85) 0%, rgba(248,250,252,0.9) 100%); + border-radius: 16px; + box-shadow: 0 2px 12px rgba(0,0,0,0.04); +} +.quick-icons__header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; +} +.quick-icons__header h3 { + font-size: 15px; + font-weight: 700; + color: #1e293b; + margin: 0; +} +.quick-icons__manage { + padding: 6px 14px; + border: none; + border-radius: 10px; + background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); + color: #fff; + cursor: pointer; + font-size: 14px; + font-weight: 600; + transition: all 0.25s; + box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3); + display: flex; + align-items: center; + gap: 4px; +} +.quick-icons__manage:hover { + transform: translateY(-1px); + box-shadow: 0 4px 14px rgba(99, 102, 241, 0.45); + background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); +} + +/* 快捷入口头部操作区(切换+管理) */ +.quick-icons__actions { + display: flex; + align-items: center; + gap: 12px; +} + +/* 内外网切换按钮组 */ +.network-toggle { + display: flex; + background: rgba(99, 102, 241, 0.08); + border-radius: 10px; + padding: 3px; + gap: 2px; +} + +.network-toggle__btn { + padding: 5px 14px; + border: none; + border-radius: 8px; + background: transparent; + color: #64748b; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; +} + +.network-toggle__btn:hover { + color: #6366f1; + background: rgba(255, 255, 255, 0.6); +} + +.network-toggle__btn--active { + background: #fff; + color: #6366f1; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); +} + +.network-toggle__btn--active:hover { + background: #fff; +} +.quick-icons__grid { + display: flex; + flex-wrap: wrap; + gap: 20px; +} +.quick-icons__item { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + text-decoration: none; + color: inherit; + padding: 12px 8px; + border-radius: 14px; + transition: all 0.25s ease; + min-width: 80px; + cursor: pointer; +} +.quick-icons__item:hover { + background: rgba(99, 102, 241, 0.08); + transform: translateY(-2px); + box-shadow: 0 4px 16px rgba(99, 102, 241, 0.12); +} +.quick-icons__thumb { + width: 52px; + height: 52px; + border-radius: 14px; + background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%); + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + box-shadow: 0 2px 8px rgba(99, 102, 241, 0.15); + transition: box-shadow 0.25s ease; +} +.quick-icons__item:hover .quick-icons__thumb { + box-shadow: 0 4px 16px rgba(99, 102, 241, 0.25); +} +.quick-icons__thumb img { + width: 32px; + height: 32px; + object-fit: contain; +} +.quick-icons__fallback { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + font-weight: 700; + color: #6366f1; +} +.quick-icons__label { + font-size: 12px; + font-weight: 600; + color: #334155; + text-align: center; + max-width: 80px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.quick-icons__empty { + padding: 20px; + text-align: center; + color: #94a3b8; + font-size: 13px; +} + +.topbar, +.stats-row, +.content-grid, +.poster-showcase, +.search-topbar, +.hero-carousel, +.manage-layout { + width: min(1500px, calc(100% - 48px)); + margin: 0 auto; +} + +.search-topbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + padding: 18px 0 10px; +} + +.search-topbar__input { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 12px; + flex: 1; +} + +.search-topbar__input input { + min-height: 52px; + border-radius: 999px; + padding: 0 20px; + transition: all 0.3s ease; + outline: none; +} + +.search-topbar__input input:focus { + box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.15); + border-color: #6366f1; + background: #fff; +} + +.search-topbar__actions { + justify-content: flex-end; +} + +/* 个人中心菜单 */ +.user-menu { + position: relative; +} + +.user-menu__trigger { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 16px; + background: #fff; + border: 2px solid var(--line); + border-radius: 999px; + cursor: pointer; + transition: all 0.2s; + min-height: 48px; +} + +.user-menu__trigger:hover { + border-color: var(--brand); + box-shadow: 0 4px 12px rgba(99, 102, 241, 0.15); +} + +.user-menu__avatar { + width: 36px; + height: 36px; + border-radius: 50%; + object-fit: cover; + border: 2px solid var(--line); +} + +.user-menu__avatar--fallback { + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #6366f1, #8b5cf6); + color: #fff; + font-weight: 700; + font-size: 16px; +} + +.user-menu__name { + font-weight: 600; + color: var(--ink); + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.user-menu__arrow { + font-size: 10px; + color: var(--muted); + transition: transform 0.2s; +} + +.user-menu__dropdown { + position: absolute; + top: calc(100% + 8px); + right: 0; + min-width: 280px; + background: #fff; + border-radius: 16px; + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); + padding: 16px; + z-index: 1000; + animation: dropdownFadeIn 0.2s ease; +} + +@keyframes dropdownFadeIn { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.user-menu__header { + display: flex; + align-items: center; + gap: 12px; + padding-bottom: 12px; + position: relative; +} + +.user-menu__avatar--lg { + width: 56px; + height: 56px; + font-size: 24px; +} + +.user-menu__info { + flex: 1; + min-width: 0; +} + +.user-menu__username { + font-weight: 700; + font-size: 16px; + color: var(--ink); + margin-bottom: 4px; +} + +.user-menu__email { + font-size: 13px; + color: var(--muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.user-menu__edit-avatar { + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + width: 32px; + height: 32px; + border-radius: 50%; + background: rgba(99, 102, 241, 0.1); + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + transition: all 0.2s; +} + +.user-menu__edit-avatar:hover { + background: rgba(99, 102, 241, 0.2); + transform: translateY(-50%) scale(1.1); +} + +.user-menu__divider { + height: 1px; + background: var(--line); + margin: 8px 0; +} + +.user-menu__item { + display: flex; + align-items: center; + gap: 12px; + width: 100%; + padding: 12px 16px; + background: transparent; + border: none; + border-radius: 10px; + cursor: pointer; + font-size: 14px; + font-weight: 500; + color: var(--ink); + transition: all 0.2s; + text-align: left; +} + +.user-menu__item:hover { + background: rgba(99, 102, 241, 0.08); +} + +.user-menu__icon { + font-size: 18px; + width: 24px; + text-align: center; +} + +.user-menu__item--danger { + color: var(--danger); +} + +.user-menu__item--danger:hover { + background: rgba(220, 38, 38, 0.08); +} + +.dashboard-topbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + padding: 10px 0 18px; +} + +.dashboard-brand { + display: grid; + gap: 6px; +} + +.dashboard-brand strong { + color: #10213a; + font-size: clamp(22px, 2.4vw, 28px); +} + +.dashboard-brand span { + color: var(--muted); + font-size: 14px; +} + +.view-switch { + display: inline-grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 4px; + padding: 5px; + min-width: 240px; + border: 1px solid rgba(15, 118, 110, 0.12); + border-radius: 999px; + background: linear-gradient(180deg, #f8fbff, #e7eef7); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.72); +} + +.view-switch__btn { + min-height: 40px; + padding: 0 20px; + background: transparent; + color: #35506b; + font-weight: 700; + box-shadow: none; +} + +.view-switch__btn:hover { + background: rgba(20, 184, 166, 0.08); +} + +.view-switch__btn.active { + background: linear-gradient(90deg, #14b8a6, #0f766e); + color: #fff; + box-shadow: 0 10px 24px rgba(15, 118, 110, 0.26); +} + +.top-actions, +.tools { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.icon-action { + min-height: 34px; + background: transparent; + color: var(--ink); + padding: 0 12px; +} + +.icon-action:hover { + background: rgba(15, 118, 110, 0.08); +} + +.btn-export { + background: #14b8a6; +} + +.hero-carousel { + padding-bottom: 6px; +} + +.hero-slide { + border-radius: 32px; +} + +.hero-slide--single { + min-height: 460px; + outline: none; +} + +.hero-fade-enter-active, +.hero-fade-leave-active { + transition: opacity 0.35s ease, transform 0.35s ease; +} + +.hero-fade-enter-from, +.hero-fade-leave-to { + opacity: 0; + transform: translateX(18px) scale(0.985); +} + +.hero-fade-enter-to, +.hero-fade-leave-from { + opacity: 1; + transform: translateX(0) scale(1); +} + +.hero-slide__topbar { + display: flex; + align-items: center; + gap: 16px; +} + +.hero-slide__content { + display: grid; + align-content: center; + gap: 16px; + max-width: 760px; +} + +.hero-slide__overlay { + display: grid; + align-content: center; + gap: 24px; + min-height: 460px; + padding: 32px; + color: #fff; +} + +.hero-slide__badge { + display: inline-flex; + width: fit-content; + padding: 9px 16px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.16); + font-size: 14px; + font-weight: 700; +} + +.hero-slide h2 { + margin: 0; + font-size: clamp(32px, 3.4vw, 44px); +} + +.hero-slide p { + margin: 0; + color: rgba(255, 255, 255, 0.88); + font-size: 16px; + line-height: 1.8; +} + +.hero-slide__meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px; +} + +.hero-slide__meta span { + padding: 6px 12px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.14); + font-size: 13px; + line-height: 1.2; +} + +.content-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + margin-bottom: 18px; +} + +.content-toolbar h2 { + margin: 0; + color: #10213a; +} + +.content-toolbar p { + margin: 6px 0 0; + color: var(--muted); +} + +.content-actions { + justify-content: flex-end; +} + +.stats-row { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 18px; + margin-top: 28px; +} + +.insight-row { + width: min(1500px, calc(100% - 48px)); + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 18px; + margin: 18px auto 0; +} + +.insight-card { + display: grid; + gap: 6px; + padding: 20px 24px; + border-radius: 20px; + background: rgba(255, 255, 255, 0.88); + box-shadow: var(--shadow); +} + +.insight-card strong { + font-size: 20px; + color: #032541; +} + +.insight-card span { + color: var(--muted); + font-size: 13px; +} + +.stats-row > div, +.duplicate-panel, +.poster-card, +.app-dialog::backdrop { + border-radius: 24px; +} + +.stats-row > div { + display: grid; + gap: 6px; + padding: 24px; + background: var(--panel); + box-shadow: var(--shadow); +} + +.stats-row span { + font-size: 32px; + font-weight: 800; +} + +.stats-row small, +.poster-time, +.poster-owner small, +.duplicate-item li, +.log-detail, +.tmdb-config p { + color: var(--muted); +} + +/* ==================== 全局加载条(顶部细条) ==================== */ +.global-loading-bar { + position: fixed; + top: 0; + left: 0; + right: 0; + height: 3px; + z-index: 99999; + background: rgba(99, 102, 241, 0.1); + overflow: hidden; +} + +.global-loading-bar__progress { + height: 100%; + width: 40%; + background: linear-gradient(90deg, #6366f1 0%, #a78bfa 50%, #6366f1 100%); + background-size: 200% 100%; + border-radius: 0 2px 2px 0; + animation: topbar-slide 1.2s ease-in-out infinite; +} + +@keyframes topbar-slide { + 0% { transform: translateX(-100%); background-position: 0% 0; } + 50% { transform: translateX(150%); background-position: 100% 0; } + 100% { transform: translateX(-100%); background-position: 0% 0; } +} + +/* 加载条淡入淡出过渡 */ +.topbar-fade-enter-active { transition: opacity 0.2s ease; } +.topbar-fade-leave-active { transition: opacity 0.4s ease 0.1s; } +.topbar-fade-enter-from, +.topbar-fade-leave-to { opacity: 0; } + +/* ==================== 加载动画通用旋转spinner ==================== */ +.loading-spinner, +.drive-spinner { + width: 32px; + height: 32px; + border-radius: 50%; + border: 3px solid rgba(99, 102, 241, 0.15); + border-top-color: #6366f1; + animation: spinner-rotate 0.7s linear infinite; +} + +@keyframes spinner-rotate { + to { transform: rotate(360deg); } +} + +.loading-panel { + width: min(1500px, calc(100% - 48px)); + margin: 28px auto 0; +} + +.global-notice { + position: fixed; + top: 80px; + right: 24px; + min-width: 320px; + max-width: 500px; + padding: 16px 20px; + border-radius: 12px; + font-weight: 600; + font-size: 14px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); + z-index: 10000; + backdrop-filter: blur(10px); +} + +/* Vue Transition 动画 */ +.notice-enter-active { + animation: slideInRight 0.3s ease-out; +} + +.notice-leave-active { + animation: slideOutRight 0.3s ease-in; +} + +@keyframes slideInRight { + from { + opacity: 0; + transform: translateX(100%); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes slideOutRight { + from { + opacity: 1; + transform: translateX(0); + } + to { + opacity: 0; + transform: translateX(100%); + } +} + +.global-notice::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: currentColor; + border-radius: 12px 12px 0 0; +} + +.global-notice--warning { + background: rgba(255, 255, 255, 0.95); + color: #b45309; + border-left: 4px solid #f59e0b; +} + +.global-notice--danger { + background: rgba(255, 255, 255, 0.95); + color: #b91c1c; + border-left: 4px solid #dc2626; +} + +.global-notice--error { + background: rgba(255, 255, 255, 0.95); + color: #dc2626; + border-left: 4px solid #ef4444; +} + +.global-notice--success { + background: rgba(255, 255, 255, 0.95); + color: #16a34a; + border-left: 4px solid #22c55e; +} + +.loading-card { + display: flex; + align-items: center; + gap: 12px; + padding: 18px 24px; + border-radius: 20px; + background: rgba(255, 255, 255, 0.82); + box-shadow: var(--shadow); + color: var(--muted); + font-weight: 600; + font-size: 14px; +} + +/* 搜索聚焦时的内容虚化效果 */ +.content-blur { + filter: blur(8px); + opacity: 0.5; + pointer-events: none; + transition: filter 0.3s ease, opacity 0.3s ease; +} + +/* 搜索结果为空的样式 */ +.empty-search-result { + display: flex; + align-items: center; + justify-content: center; + min-height: 400px; + padding: 40px 20px; +} + +.empty-search-content { + text-align: center; + max-width: 400px; +} + +.empty-search-icon { + font-size: 80px; + margin-bottom: 20px; + animation: float 3s ease-in-out infinite; +} + +@keyframes float { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-10px); + } +} + +.empty-search-content h3 { + font-size: 28px; + font-weight: 700; + color: var(--text); + margin-bottom: 12px; +} + +.empty-search-content p { + font-size: 16px; + color: var(--muted); + margin-bottom: 24px; + line-height: 1.6; +} + +/* 搜索聚焦时的提示样式 */ +.search-focus-hint { + display: flex; + align-items: center; + justify-content: center; + min-height: 400px; + padding: 40px 20px; +} + +.search-focus-content { + text-align: center; + max-width: 400px; +} + +.search-focus-icon { + font-size: 80px; + margin-bottom: 20px; + animation: pulse 2s ease-in-out infinite; +} + +@keyframes pulse { + 0%, 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.1); + opacity: 0.8; + } +} + +.search-focus-content h3 { + font-size: 28px; + font-weight: 700; + color: var(--text); + margin-bottom: 12px; +} + +.search-focus-content p { + font-size: 16px; + color: var(--muted); + line-height: 1.6; +} + +/* Vue Transition - Fade 动画 */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.3s ease; +} + +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} + +.content-grid { + display: grid; + grid-template-columns: minmax(0, 1fr) 340px; + gap: 24px; + margin-top: 28px; + padding-bottom: 28px; +} + +.poster-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + gap: 20px; +} + +.poster-list--manage { + display: flex; + flex-direction: column; + gap: 14px; +} + +.folder-tree-container { + background: rgba(255, 255, 255, 0.6); + border-radius: 16px; + padding: 20px; + min-height: 400px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); +} + +/* 网盘管理布局 */ +.manage-layout { + display: grid; + grid-template-columns: 1fr 380px; + gap: 24px; + margin-top: 28px; + padding-bottom: 28px; + position: relative; /* 为网盘加载遮罩层提供定位参考 */ +} + +.manage-main { + display: flex; + flex-direction: column; + gap: 0; +} + +.content-toolbar { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 20px; + margin-bottom: 16px; +} + +.content-toolbar h2 { + font-size: 22px; + font-weight: 700; + color: #032541; + margin: 0; +} + +.content-toolbar p { + margin: 6px 0 0; + color: var(--muted); + font-size: 14px; +} + +.top-actions { + display: flex; + gap: 10px; + flex-shrink: 0; +} + +.content-actions button { + padding: 10px 18px; + border: none; + border-radius: 12px; + background: #6366f1; + color: white; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.content-actions button:hover { + background: #4f46e5; + transform: translateY(-1px); +} + +.content-actions button.secondary { + background: rgba(99, 102, 241, 0.1); + color: #6366f1; +} + +.content-actions button.secondary:hover { + background: rgba(99, 102, 241, 0.2); +} + +/* 文件夹树操作按钮 */ +.folder-tree-actions { + margin-bottom: 16px; + padding-bottom: 16px; + border-bottom: 1px solid var(--line); +} + +.folder-tree-actions__btn { + padding: 10px 20px; + border: 2px dashed var(--line); + border-radius: 12px; + background: rgba(99, 102, 241, 0.05); + color: #6366f1; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + width: 100%; +} + +.folder-tree-actions__btn:hover { + background: rgba(99, 102, 241, 0.1); + border-color: #6366f1; +} + +/* 网盘标签页 */ +.drive-tabs { + display: flex; + gap: 8px; + margin: 16px 0; + padding: 12px; + background: rgba(99, 102, 241, 0.05); + border-radius: 12px; + overflow-x: auto; +} + +.drive-tab { + position: relative; + display: flex; + align-items: center; + gap: 8px; + padding: 10px 16px; + border-radius: 8px; + background: white; + cursor: pointer; + transition: all 0.2s; + border: 2px solid transparent; + font-weight: 500; + color: #4b5563; + white-space: nowrap; +} + +.drive-tab:hover { + background: rgba(99, 102, 241, 0.1); + border-color: rgba(99, 102, 241, 0.3); +} + +.drive-tab.active { + background: rgba(99, 102, 241, 0.15); + border-color: #6366f1; + color: #4f46e5; + font-weight: 600; +} + +.drive-tab__name { + flex: 1; +} + +/* 网盘标签页 Logo */ +.drive-tab__logo { + width: 20px; + height: 20px; + border-radius: 4px; + object-fit: cover; + flex-shrink: 0; +} +.drive-tab__logo--fallback { + display: flex; + align-items: center; + justify-content: center; + background: rgba(99, 102, 241, 0.15); + font-size: 12px; + line-height: 1; +} + + +/* 通用悬浮提示框(Teleport 到 body,position: fixed) */ +.app-tooltip { + position: fixed; + z-index: 99999; + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + border-radius: 12px; + padding: 0; + min-width: 240px; + max-width: 340px; + box-shadow: + 0 12px 40px rgba(0, 0, 0, 0.12), + 0 4px 12px rgba(0, 0, 0, 0.08), + 0 0 0 1px rgba(99, 102, 241, 0.08); + pointer-events: none; + overflow: hidden; +} +.app-tooltip__header { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + background: linear-gradient(90deg, rgba(99, 102, 241, 0.08) 0%, rgba(139, 92, 246, 0.06) 100%); + border-bottom: 1px solid rgba(99, 102, 241, 0.1); +} +.app-tooltip__icon { + font-size: 16px; + flex-shrink: 0; +} +.app-tooltip__title { + font-size: 13px; + font-weight: 700; + color: #1e293b; + letter-spacing: 0.01em; +} +.app-tooltip__body { + padding: 12px 16px; +} +.app-tooltip__desc { + font-size: 12px; + color: #64748b; + line-height: 1.7; + word-break: break-word; + margin: 0; +} +.app-tooltip__empty { + font-size: 12px; + color: #94a3b8; + font-style: italic; +} +.app-tooltip__meta { + margin-top: 8px; + padding-top: 8px; + border-top: 1px dashed #e2e8f0; + font-size: 11px; + color: #94a3b8; +} +.app-tooltip__arrow { + position: absolute; + top: -5px; + left: 50%; + transform: translateX(-50%) rotate(45deg); + width: 10px; + height: 10px; + background: #fff; + border-left: 1px solid rgba(99, 102, 241, 0.08); + border-top: 1px solid rgba(99, 102, 241, 0.08); +} + +/* 悬浮提示过渡动画(标签页用) */ +.tooltip-fade-enter-active { + transition: opacity 0.2s ease; +} +.tooltip-fade-leave-active { + transition: opacity 0.15s ease; +} +.tooltip-fade-enter-from, +.tooltip-fade-leave-to { + opacity: 0; +} + +.drive-tab__actions { + display: flex; + gap: 4px; + opacity: 0; + transition: opacity 0.2s; +} + +.drive-tab:hover .drive-tab__actions { + opacity: 1; +} + +.drive-tab__edit, +.drive-tab__delete { + padding: 4px; + border: none; + background: transparent; + cursor: pointer; + font-size: 14px; + transition: all 0.2s; + border-radius: 4px; +} + +.drive-tab__edit:hover { + background: rgba(99, 102, 241, 0.2); +} + +.drive-tab__delete:hover { + background: rgba(239, 68, 68, 0.2); +} + +.drive-tab--add { + background: transparent; + border: 2px dashed rgba(99, 102, 241, 0.3); + color: #6366f1; + font-weight: 600; +} + +.drive-tab--add:hover { + background: rgba(99, 102, 241, 0.1); + border-color: #6366f1; +} + +.poster-card-wrap { + display: grid; + gap: 10px; +} + +.poster-card-wrap--manage { + grid-template-columns: 160px minmax(0, 1fr); + align-items: center; + gap: 16px; +} + +.poster-card-wrap--manage .poster-card-actions { + justify-content: flex-start; +} + +.poster-card-wrap--manage .poster-card { + display: grid; + grid-template-columns: 168px minmax(0, 1fr); + align-items: stretch; + min-height: 168px; +} + +.poster-card-wrap--manage .poster-thumb { + aspect-ratio: auto; + height: 100%; +} + +.poster-card-wrap--manage .poster-body { + display: grid; + align-content: center; + padding: 20px 22px; +} + +.poster-card-actions { + display: flex; + justify-content: flex-end; +} + +.poster-card { + overflow: hidden; + background: #fff; + box-shadow: var(--shadow); + transition: all 0.2s; +} + +.poster-card:not(.is-readonly) { + cursor: pointer; +} + +.poster-card:not(.is-readonly):hover { + transform: translateY(-4px); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12); +} + +.poster-card.is-readonly { + cursor: default; +} + +.poster-thumb { + position: relative; + aspect-ratio: 0.74; + overflow: hidden; + background: #d9e2ef; +} + +.poster-thumb img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.score-badge { + position: absolute; + right: 14px; + bottom: 14px; + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 44px; + min-height: 44px; + padding: 0 8px; + border-radius: 999px; + background: rgba(3, 37, 65, 0.92); + color: #7ee8bc; + font-weight: 800; +} + +.poster-body { + display: grid; + gap: 12px; + padding: 18px; +} + +.poster-meta-row, +.poster-owner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; +} + +.poster-sheet { + display: inline-block; + padding: 4px 12px; + border-radius: 999px; + background: rgba(99, 102, 241, 0.12); + color: #6366f1; + font-weight: 600; + font-size: 13px; +} + +.poster-body h3 { + margin: 0; + font-size: 22px; +} + +.poster-body p { + margin: 0; + color: #41556e; +} + +.status-pill { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 30px; + padding: 0 12px; + border-radius: 999px; + font-size: 13px; + font-weight: 700; +} + +.status-pill.is-normal { + background: rgba(34, 197, 94, 0.12); + color: #15803d; +} + +.status-pill.is-warning { + background: rgba(245, 158, 11, 0.14); + color: #b45309; +} + +.status-pill.is-danger { + background: rgba(220, 38, 38, 0.12); + color: #b91c1c; +} + +.tag-list { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.tag-list span { + display: inline-flex; + align-items: center; + min-height: 28px; + padding: 0 12px; + border-radius: 999px; + background: #ecf4ff; + color: #245; + font-size: 13px; + font-weight: 700; +} + +.duplicate-section { + margin-top: 28px; + padding-bottom: 28px; +} + +.duplicate-panel { + display: grid; + align-content: start; + gap: 18px; + padding: 24px; + background: #fff; + box-shadow: var(--shadow); +} + +.duplicate-panel header { + display: flex; + align-items: center; + justify-content: space-between; +} + +.duplicate-panel header span { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 40px; + min-height: 40px; + border-radius: 999px; + background: #f3f7fb; + font-weight: 800; +} + +.duplicate-list, +.login-log-list, +.column-list { + display: grid; + gap: 12px; +} + +.log-loading, +.log-empty { + padding: 40px 20px; + text-align: center; + color: var(--muted); + font-size: 14px; +} + +.log-loading { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.log-loading::before { + content: ''; + width: 20px; + height: 20px; + border: 3px solid var(--line); + border-top-color: #6366f1; + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.empty-note { + padding: 14px; + border: 1px dashed var(--line); + border-radius: 16px; + color: var(--muted); + background: #fbfdff; +} + +.duplicate-item, +.log-entry, +.column-item { + padding: 14px; + border: 1px solid var(--line); + border-radius: 16px; + background: #fbfdff; +} + +.log-entry { + display: grid; + gap: 10px; +} + +.log-entry__header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; +} + +.log-account { + margin-top: 4px; + color: var(--muted); + font-size: 13px; +} + +.log-result { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 56px; + min-height: 28px; + padding: 0 10px; + border-radius: 999px; + font-size: 12px; + font-weight: 700; +} + +.log-result--success { + background: rgba(34, 197, 94, 0.12); + color: #15803d; +} + +.log-result--fail { + background: rgba(239, 68, 68, 0.12); + color: #b91c1c; +} + +.log-detail--ua { + word-break: break-all; +} + +.log-detail--fail { + color: #dc2626; + font-weight: 600; +} + +.column-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.add-column-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; +} + +.app-dialog { + width: min(760px, calc(100vw - 24px)); + padding: 0; + border: 0; + border-radius: 28px; + background: #fff; + box-shadow: 0 40px 100px rgba(15, 23, 42, 0.28); +} + +.app-dialog::backdrop { + background: rgba(2, 6, 23, 0.58); +} + +.dialog-form { + display: grid; + gap: 22px; + padding: 24px; +} + +.dialog-form header, +.dialog-form footer { + display: flex; + align-items: center; + gap: 14px; +} + +.dialog-form header { + justify-content: space-between; +} + +.dialog-form header h2 { + margin: 0; +} + +.dialog-form header button { + min-width: 42px; + min-height: 42px; + padding: 0; + border-radius: 50%; + background: #eff5fb; + color: #243247; +} + +.dialog-form footer { + justify-content: flex-end; +} + +.field-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; +} + +.field-span-2 { + grid-column: 1 / -1; +} + +.simple-dialog-body, +.tmdb-config, +.log-panel, +.column-manager { + display: grid; + gap: 16px; +} + +/* ===== 登录日志筛选区域 ===== */ +.log-filter { + display: grid; + gap: 12px; + padding: 16px; + background: rgba(99, 102, 241, 0.05); + border-radius: 12px; + border: 1px solid rgba(99, 102, 241, 0.1); +} + +.filter-row { + display: flex; + gap: 12px; + align-items: center; +} + +.filter-input, +.filter-select { + flex: 1; + padding: 10px 14px; + border: 1px solid var(--line); + border-radius: 8px; + font-size: 14px; + background: #fff; + transition: all 0.2s; +} + +.filter-input:focus, +.filter-select:focus { + outline: none; + border-color: #6366f1; + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); +} + +.filter-input::placeholder { + color: #9ca3af; +} + +.filter-reset-btn { + padding: 10px 20px; + background: linear-gradient(135deg, #a52a2a, #8b0000); /* 暗红色渐变 */ + color: #fff; + border: none; + border-radius: 8px; + font-weight: 600; + font-size: 14px; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; +} + +.filter-reset-btn:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(139, 0, 0, 0.3); /* 暗红色阴影 */ +} + +.filter-reset-btn:active { + transform: translateY(0); +} + +.filter-search-btn { + padding: 10px 24px; + background: linear-gradient(135deg, #6366f1, #8b5cf6); + color: #fff; + border: none; + border-radius: 8px; + font-weight: 600; + font-size: 14px; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; +} + +.filter-search-btn:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3); +} + +.filter-search-btn:active { + transform: translateY(0); +} + +/* ===== 登录日志分页区域 ===== */ +.log-pagination { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 16px; + margin-top: 8px; + background: rgba(99, 102, 241, 0.05); + border-radius: 12px; + border: 1px solid rgba(99, 102, 241, 0.1); +} + +.pagination-info { + font-size: 14px; + color: var(--muted); + font-weight: 500; +} + +.load-more-btn { + padding: 10px 24px; + background: linear-gradient(135deg, #6366f1, #8b5cf6); + color: #fff; + border: none; + border-radius: 8px; + font-weight: 600; + font-size: 14px; + cursor: pointer; + transition: all 0.2s; +} + +.load-more-btn:hover:not(:disabled) { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3); +} + +.load-more-btn:active:not(:disabled) { + transform: translateY(0); +} + +.load-more-btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.no-more { + font-size: 13px; + color: var(--muted); + text-align: center; + width: 100%; +} + +.tmdb-config__status { + padding: 14px 16px; + border-radius: 14px; + background: rgba(245, 158, 11, 0.12); + color: #b45309; + font-weight: 700; +} + +.tmdb-config__status.ready { + background: rgba(34, 197, 94, 0.12); + color: #15803d; +} + +.log-integrity { + padding: 14px 16px; + border-radius: 14px; + background: rgba(245, 158, 11, 0.12); + color: #b45309; + font-weight: 700; +} + +.log-integrity.ok { + background: rgba(34, 197, 94, 0.12); + color: #15803d; +} + +.icon-btn { + min-height: 34px; + padding: 0 14px; +} + +@media (max-width: 1100px) { + .content-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 900px) { + .dashboard-topbar, + .search-topbar, + .hero-carousel, + .content-grid, + .poster-showcase, + .insight-row, + .global-notice { + width: min(100%, calc(100% - 24px)); + } + + .dashboard-topbar, + .content-toolbar, + .search-topbar { + flex-direction: column; + align-items: stretch; + } + + .search-topbar__input, + .stats-row, + .insight-row, + .field-grid, + .add-column-row { + grid-template-columns: 1fr; + } + + /* 移动端用户菜单优化 */ + .user-menu__trigger { + padding: 6px 12px; + min-height: 44px; + flex-wrap: nowrap; + } + + .user-menu__avatar { + width: 32px; + height: 32px; + flex-shrink: 0; + } + + .user-menu__name { + max-width: 80px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; + flex-shrink: 1; + } + + .user-menu__arrow { + font-size: 10px; + flex-shrink: 0; + } + + .user-menu__dropdown { + right: 0; + min-width: 260px; + top: calc(100% + 8px); + } + + .poster-card-wrap--manage, + .poster-card-wrap--manage .poster-card { + grid-template-columns: 1fr; + } + + .poster-card-wrap--manage .poster-card-actions { + justify-content: flex-end; + } + + .poster-card-wrap--manage .poster-thumb { + aspect-ratio: 0.74; + } + + .hero-carousel__track { + grid-template-columns: repeat(3, minmax(260px, 1fr)); + } +} + +@media (max-width: 768px) { + /* ===== 全局优化 ===== */ + html { + font-size: 14px; + } + + body { + padding: 0; + } + + .app-wrapper { + padding: 0; + } + + /* ===== 顶部导航栏 ===== */ + .dashboard-topbar { + padding: 12px 16px; + gap: 12px; + } + + .dashboard-topbar__brand h1 { + font-size: 20px; + } + + /* ===== 搜索栏 ===== */ + .search-topbar { + padding: 0 16px 16px; + } + + .search-topbar__input { + grid-template-columns: 1fr auto; + } + + .search-topbar__input input { + min-height: 48px; + font-size: 15px; + } + + .search-topbar__input button { + min-height: 48px; + padding: 0 20px; + } + + /* ===== 视图切换按钮 ===== */ + .view-switch { + width: 100%; + } + + .view-switch__btn { + padding: 8px 12px; + font-size: 14px; + } + + /* ===== 每日推荐海报 ===== */ + .hero-carousel { + padding: 0 16px 16px; + } + + .hero-slide--single { + min-height: 280px; + border-radius: 16px; + } + + .hero-slide__content h2 { + font-size: 22px; + } + + .hero-slide__content p { + font-size: 14px; + } + + /* ===== 内容工具栏 ===== */ + .content-toolbar { + padding: 12px 16px; + flex-direction: column; + gap: 12px; + } + + .content-toolbar h2 { + font-size: 20px; + } + + .content-toolbar p { + font-size: 13px; + } + + .top-actions.content-actions { + width: 100%; + } + + /* ===== 网盘标签页 ===== */ + .drive-tabs { + padding: 0 16px; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + } + + .drive-tabs::-webkit-scrollbar { + display: none; + } + + .drive-tab { + min-width: 100px; + padding: 10px 16px; + font-size: 14px; + } + + .drive-tab--add { + min-width: 100px; + } + + /* ===== 文件夹树 ===== */ + .folder-tree-container { + padding: 0 16px; + } + + .folder-tree-actions { + padding: 12px 0; + } + + .folder-tree-actions__btn { + font-size: 14px; + padding: 10px 16px; + } + + .tree-item__header { + padding: 12px; + } + + .tree-item__icon { + font-size: 18px; + } + + .tree-item__title { + font-size: 15px; + } + + .tree-actions { + gap: 6px; + } + + .tree-actions button { + padding: 6px 10px; + font-size: 16px; + } + + /* ===== 重复内容面板 ===== */ + .duplicate-panel { + margin: 0 16px 16px; + border-radius: 16px; + } + + .duplicate-panel header { + padding: 16px; + } + + .duplicate-panel header h2 { + font-size: 18px; + } + + .duplicate-list { + padding: 0 16px 16px; + } + + /* ===== 海报展示 ===== */ + .poster-showcase { + padding: 0 16px 16px; + } + + .poster-list--showcase { + grid-template-columns: repeat(2, 1fr); + gap: 12px; + } + + .poster-card { + border-radius: 12px; + } + + .poster-thumb { + border-radius: 12px 12px 0 0; + } + + .poster-meta h3 { + font-size: 14px; + padding: 0 10px; + } + + .poster-meta p { + font-size: 12px; + padding: 0 10px; + } + + /* ===== 个人中心菜单 ===== */ + .user-menu__trigger { + padding: 6px 12px; + min-height: 44px; + } + + .user-menu__avatar { + width: 32px; + height: 32px; + } + + .user-menu__name { + max-width: 80px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; + } + + .user-menu__dropdown { + right: 0; + min-width: 260px; + top: calc(100% + 8px); + } + + /* ===== 通知提示 ===== */ + .global-notice { + top: 70px; + right: 12px; + left: 12px; + min-width: auto; + max-width: none; + font-size: 13px; + padding: 12px 16px; + } + + /* ===== 搜索聚焦提示 ===== */ + .search-focus-hint { + min-height: 300px; + padding: 30px 20px; + } + + .search-focus-icon { + font-size: 60px; + } + + .search-focus-content h3 { + font-size: 22px; + } + + .search-focus-content p { + font-size: 14px; + } + + /* ===== 空搜索结果 ===== */ + .empty-search-result { + min-height: 300px; + padding: 30px 20px; + } + + .empty-search-icon { + font-size: 60px; + } + + .empty-search-content h3 { + font-size: 22px; + } + + .empty-search-content p { + font-size: 14px; + } + + /* ===== 对话框优化 ===== */ + .simple-dialog { + width: calc(100% - 32px); + max-width: 500px; + border-radius: 16px; + } + + .simple-dialog__header { + padding: 16px; + } + + .simple-dialog__header h2 { + font-size: 18px; + } + + .simple-dialog__body { + padding: 16px; + max-height: 70vh; + } + + .simple-dialog__footer { + padding: 12px 16px; + } + + /* ===== 表单优化 ===== */ + .field-grid { + grid-template-columns: 1fr; + gap: 12px; + } + + .field-grid label { + font-size: 13px; + } + + .field-grid input, + .field-grid select, + .field-grid textarea { + font-size: 15px; + min-height: 44px; + } + + /* ===== 按钮优化 ===== */ + button, + .btn { + min-height: 44px; + padding: 10px 20px; + font-size: 15px; + } + + button.secondary, + .btn.secondary { + min-height: 40px; + padding: 8px 16px; + font-size: 14px; + } + + /* ===== 加载面板 ===== */ + .loading-panel { + padding: 40px 20px; + } + + .loading-card { + padding: 20px; + font-size: 15px; + } + + /* ===== 登录日志 ===== */ + .log-panel { + padding: 0; + } + + /* ===== 登录日志筛选区域 - 移动端适配 ===== */ + .log-filter { + padding: 12px; + } + + .filter-row { + flex-direction: column; + gap: 8px; + } + + .filter-input, + .filter-select { + width: 100%; + } + + .filter-reset-btn { + width: 100%; + padding: 12px; + } + + .filter-search-btn { + width: 100%; + padding: 12px; + } + + /* ===== 登录日志分页区域 - 移动端适配 ===== */ + .log-pagination { + flex-direction: column; + gap: 12px; + padding: 12px; + } + + .pagination-info { + font-size: 13px; + } + + .load-more-btn { + width: 100%; + padding: 12px; + } + + .log-integrity { + padding: 12px 16px; + font-size: 13px; + } + + .log-entry { + padding: 12px 16px; + } + + .log-time { + font-size: 13px; + } + + .log-account { + font-size: 13px; + } + + .log-detail { + font-size: 12px; + } + + /* ===== TMDB 配置 ===== */ + .tmdb-config { + padding: 0; + } + + .tmdb-config label span { + font-size: 14px; + } + + .tmdb-config input { + font-size: 15px; + min-height: 44px; + } + + .tmdb-config__status { + font-size: 13px; + } +} + +@media (max-width: 640px) { + .login-shell { + padding: 10px 12px; + } + + .login-panel { + width: 100%; + padding: 16px 14px 18px; + border-radius: 20px; + } + + .login-panel::before { + height: 88px; + border-radius: 0 0 20px 20px; + } + + .brand-mark { + width: 60px; + height: 60px; + border-radius: 14px; + margin-bottom: 8px; + } + + .login-panel h1 { + font-size: 20px; + } + + .login-panel p { + font-size: 12px; + line-height: 1.35; + } + + .auth-switch { + width: 100%; + max-width: 220px; + margin-top: 10px; + } + + .login-form { + margin-top: 12px; + gap: 8px; + } + + .login-form label { + gap: 4px; + } + + .login-form label span { + font-size: 12px; + } + + .login-form input { + padding: 8px 10px; + } + + /* 小屏幕用户菜单优化 */ + .user-menu__trigger { + padding: 6px 12px; + } + + .user-menu__avatar { + width: 32px; + height: 32px; + } + + .user-menu__dropdown { + right: -20px; + min-width: 240px; + } + + .user-menu__header { + flex-direction: column; + text-align: center; + } + + .user-menu__edit-avatar { + position: static; + transform: none; + margin-top: 8px; + } + + .user-menu__edit-avatar:hover { + transform: scale(1.1); + } + + .captcha-row { + grid-template-columns: 1fr; + } + + canvas { + width: 100%; + max-width: 120px; + height: auto; + aspect-ratio: 3 / 1; + justify-self: start; + } +} + +/* ===== 超小屏幕优化 (iPhone SE 等) ===== */ +@media (max-width: 375px) { + html { + font-size: 13px; + } + + .dashboard-topbar { + padding: 10px 12px; + } + + .dashboard-topbar__brand h1 { + font-size: 18px; + } + + .search-topbar { + padding: 0 12px 12px; + } + + .search-topbar__input input { + min-height: 44px; + font-size: 14px; + } + + .search-topbar__input button { + min-height: 44px; + padding: 0 16px; + font-size: 14px; + } + + .view-switch__btn { + padding: 6px 10px; + font-size: 13px; + } + + .poster-list--showcase { + grid-template-columns: 1fr; + } + + .hero-slide--single { + min-height: 240px; + } + + .hero-slide__content h2 { + font-size: 18px; + } + + .hero-slide__content p { + font-size: 13px; + } + + .drive-tab { + min-width: 90px; + padding: 8px 12px; + font-size: 13px; + } + + /* 超小屏幕用户菜单优化 */ + .user-menu__trigger { + padding: 4px 8px; + gap: 6px; + } + + .user-menu__avatar { + width: 28px; + height: 28px; + } + + .user-menu__name { + max-width: 60px; + font-size: 13px; + } + + .tree-item__header { + padding: 10px; + } + + .tree-item__title { + font-size: 14px; + } + + .content-toolbar { + padding: 10px 12px; + } + + .content-toolbar h2 { + font-size: 18px; + } + + button, + .btn { + min-height: 40px; + padding: 8px 16px; + font-size: 14px; + } + + .simple-dialog { + width: calc(100% - 24px); + } + + .global-notice { + top: 60px; + right: 8px; + left: 8px; + font-size: 12px; + padding: 10px 14px; + } + + .search-focus-hint, + .empty-search-result { + min-height: 260px; + padding: 24px 16px; + } + + .search-focus-icon, + .empty-search-icon { + font-size: 50px; + } + + .search-focus-content h3, + .empty-search-content h3 { + font-size: 20px; + } +} + +@media (max-height: 760px) { + .login-shell { + padding: 8px 12px; + } + + .login-panel { + padding: 14px 20px 16px; + } + + .brand-mark { + width: 56px; + height: 56px; + margin-bottom: 6px; + } + + .login-panel h1 { + font-size: 20px; + } + + .login-panel p { + margin-top: 4px; + font-size: 12px; + } + + .auth-switch, + .login-form { + margin-top: 10px; + } + + .login-form { + gap: 8px; + } + + .login-form label { + gap: 4px; + } + + .login-form label span, + .form-message { + font-size: 12px; + } + + .login-form input { + padding: 8px 10px; + } + + .login-form button[type='submit'] { + min-height: 40px; + } +} + +/* ====== 网盘操作加载遮罩层 ====== */ +/* 覆盖整个管理区域,显示旋转动画和提示文字 */ +.drive-loading-overlay { + position: absolute; /* 相对于 manage-layout 定位 */ + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(255, 255, 255, 0.88); /* 半透明白色背景 */ + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; /* 确保在所有内容之上 */ + border-radius: 8px; +} + +/* 加载动画容器:垂直居中显示spinner和文字 */ +.drive-loading-spinner { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} + +/* 网盘遮罩层spinner尺寸覆盖 */ +.drive-loading-spinner .drive-spinner { + width: 44px; + height: 44px; +} + +/* 加载提示文字样式 */ +.drive-loading-text { + color: #606266; + font-size: 14px; + font-weight: 500; + margin: 0; + letter-spacing: 0.5px; +} + +/* ====== 加载遮罩过渡动画(与用户管理组件保持一致) ====== */ +.loading-fade-enter-active { + transition: opacity 0.3s ease; +} +.loading-fade-leave-active { + transition: opacity 0.2s ease; +} +.loading-fade-enter-from, +.loading-fade-leave-to { + opacity: 0; +} diff --git a/vue/src/utils/captcha.js b/vue/src/utils/captcha.js new file mode 100644 index 0000000..39dbb68 --- /dev/null +++ b/vue/src/utils/captcha.js @@ -0,0 +1,47 @@ +export function generateCaptcha(length = 5) { + const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' + return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('') +} + +export function drawCaptcha(canvas, code) { + if (!canvas) return + const ctx = canvas.getContext('2d') + if (!ctx) return + + ctx.clearRect(0, 0, canvas.width, canvas.height) + ctx.fillStyle = '#f8fafc' + ctx.fillRect(0, 0, canvas.width, canvas.height) + + for (let i = 0; i < 36; i += 1) { + ctx.fillStyle = `rgba(${60 + Math.random() * 120}, ${80 + Math.random() * 120}, ${90 + Math.random() * 120}, .24)` + ctx.beginPath() + ctx.arc(Math.random() * canvas.width, Math.random() * canvas.height, Math.random() * 2.2 + 0.8, 0, Math.PI * 2) + ctx.fill() + } + + for (let i = 0; i < 4; i += 1) { + ctx.strokeStyle = `rgba(15, 118, 110, ${0.2 + Math.random() * 0.35})` + ctx.lineWidth = 1 + Math.random() * 1.5 + ctx.beginPath() + ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height) + ctx.bezierCurveTo( + Math.random() * canvas.width, + Math.random() * canvas.height, + Math.random() * canvas.width, + Math.random() * canvas.height, + Math.random() * canvas.width, + Math.random() * canvas.height, + ) + ctx.stroke() + } + + ;[...code].forEach((char, index) => { + ctx.save() + ctx.translate(20 + index * 23, 30 + (Math.random() - 0.5) * 8) + ctx.rotate((Math.random() - 0.5) * 0.45) + ctx.fillStyle = index % 2 ? '#0f766e' : '#172033' + ctx.font = `${22 + Math.random() * 5}px Arial` + ctx.fillText(char, 0, 0) + ctx.restore() + }) +} diff --git a/vue/src/utils/data.js b/vue/src/utils/data.js new file mode 100644 index 0000000..78f0517 --- /dev/null +++ b/vue/src/utils/data.js @@ -0,0 +1,224 @@ +export function normalizeText(value) { + return String(value ?? '').trim() +} + +export function filterRows(rows, keyword) { + const target = normalizeText(keyword).toLowerCase() + if (!target) return rows + return rows.filter((item) => { + const merged = [ + item.title, + item.subtitle, + item.accountName, + item.owner, + item.sheetName, + ...(item.tags || []), + ] + .join(' ') + .toLowerCase() + return merged.includes(target) + }) +} + +export function attachSheetName(sheets = []) { + return sheets.map((sheet) => ({ + ...sheet, + rows: (sheet.rows || []).map((row) => ({ ...row, sheetName: sheet.name })), + })) +} + +export function flattenRows(sheets = []) { + return attachSheetName(sheets).flatMap((sheet) => sheet.rows || []) +} + +export function buildDuplicates(rows) { + // 构建完整路径需要包含所有数据(文件和文件夹) + const allItems = rows + // 只检测文件类型,排除文件夹 + const fileRows = rows.filter(item => item.type !== 'folder') + + const map = new Map() + + fileRows.forEach((item) => { + // 使用更智能的标题标准化:去除空格、标点符号,统一小写 + const key = normalizeTitleForComparison(item.title) + if (!key) return + if (!map.has(key)) { + map.set(key, []) + } + map.get(key).push(item) + }) + + return [...map.entries()] + .filter(([, list]) => list.length > 1) + .map(([, list], index) => ({ + id: `duplicate-${index + 1}`, + title: list[0].title, + locations: list.map((item, idx) => { + // 构建完整路径:网盘名称 / 父文件夹路径 / 资源库 + // 注意:传入 allItems 而不是 fileRows,因为需要查找文件夹 + const parentPath = buildParentPath(item, allItems) + const location = `${item.sheetName}${parentPath} / ${item.accountName}` + + // 如果同一个位置有多个重复文件,添加序号区分 + const sameLocationCount = list.filter((other, otherIdx) => { + if (otherIdx === idx) return false + const otherParentPath = buildParentPath(other, allItems) + const otherLocation = `${other.sheetName}${otherParentPath} / ${other.accountName}` + return otherLocation === location + }).length + + return sameLocationCount > 0 ? `${location} (副本${sameLocationCount + 1})` : location + }), + })) +} + +// 标准化标题用于比较(去除空格、标点符号等) +function normalizeTitleForComparison(title) { + return normalizeText(title) + .toLowerCase() + .replace(/[\s\-_.]+/g, '') // 去除所有空格、连字符、下划线、点号 + .replace(/[\(\)\[\]【】《》]/g, '') // 去除各种括号 +} + +// 构建父文件夹路径 +function buildParentPath(item, allItems) { + if (!item.parentId) return '' + + const pathParts = [] + let currentId = item.parentId + const visited = new Set() // 防止循环引用 + + // 向上查找所有父文件夹 + while (currentId && !visited.has(currentId)) { + visited.add(currentId) + const parent = allItems.find(i => i.id === currentId && i.type === 'folder') + if (!parent) break + pathParts.unshift(parent.title) + currentId = parent.parentId + } + + return pathParts.length ? ' / ' + pathParts.join(' / ') : '' +} + +export function createLoginLog(device = '未知设备', ip = '127.0.0.1', options = {}) { + const hashSeed = String(options.hashSeed || `${Date.now()}-${Math.random()}`) + const hash = hashSeed + .split('') + .reduce((acc, char) => ((acc << 5) - acc + char.charCodeAt(0)) | 0, 0) + .toString(16) + .replace('-', '') + .slice(0, 8) + + return { + id: options.id ? `log-${options.id}` : `log-${Date.now()}`, + time: options.time || new Date().toLocaleString('zh-CN', { hour12: false }), + device, + ip, + hash, + username: normalizeText(options.username) || '未知账号', + result: normalizeText(options.result) || 'success', + userId: options.userId ?? null, + rawUserAgent: normalizeText(options.rawUserAgent) || device, + failReason: normalizeText(options.failReason) || '', + } +} + +export function createRecord(record = {}, fallbackSheetName = '') { + const normalizedTags = Array.isArray(record.tags) + ? record.tags.map((item) => normalizeText(item)).filter(Boolean) + : normalizeText(record.tags) + .split(/[,,]/) + .map((item) => item.trim()) + .filter(Boolean) + + return { + id: record.id || `record-${Date.now()}`, + title: normalizeText(record.title), + subtitle: normalizeText(record.subtitle), + accountName: normalizeText(record.accountName), + owner: normalizeText(record.owner), + updatedAt: normalizeText(record.updatedAt), + status: normalizeText(record.status) || '正常', + poster: normalizeText(record.poster), + tags: normalizedTags, + score: Number(record.score) || 0, + sheetName: normalizeText(record.sheetName) || fallbackSheetName, + parentId: record.parentId || null, // 父文件夹ID + type: record.type || 'file', // 'folder' 或 'file' + originalName: normalizeText(record.originalName), + description: normalizeText(record.description), + category: normalizeText(record.category), + driveId: record.driveId ?? null, + folderId: record.folderId ?? null, + minioUrl: normalizeText(record.minioUrl), + mimeType: normalizeText(record.mimeType), + extension: normalizeText(record.extension), + fileSize: Number(record.fileSize) || 0, + createdAt: normalizeText(record.createdAt), + } +} + +export function mapMovieToRecord(movie = {}, fallbackSheetName = '') { + return createRecord({ + id: movie.id, + title: movie.title, + subtitle: movie.subtitle || movie.description || '', + accountName: movie.originalName || movie.minioUrl || '', + owner: movie.category || '', + updatedAt: movie.updateTime || movie.createTime || '', + status: '正常', + poster: movie.posterUrl || '', + tags: movie.tags, + score: Number(movie.rating) || 0, + sheetName: fallbackSheetName, + parentId: movie.folderId || null, + type: 'file', + originalName: movie.originalName, + description: movie.description, + category: movie.category, + driveId: movie.driveId, + folderId: movie.folderId, + minioUrl: movie.minioUrl, + mimeType: movie.mimeType, + extension: movie.extension, + fileSize: movie.fileSize, + createdAt: movie.createTime, + }, fallbackSheetName) +} + +// 创建文件夹 +export function createFolder(folder = {}, fallbackSheetName = '') { + return { + id: folder.id || `folder-${Date.now()}`, + title: normalizeText(folder.title), + parentId: folder.parentId || null, + sheetName: normalizeText(folder.sheetName) || fallbackSheetName, + type: 'folder', + children: [], // 子项(文件夹或文件) + createdAt: folder.createdAt || new Date().toLocaleString('zh-CN', { hour12: false }), + } +} + +// 构建树形结构 +export function buildTree(items = []) { + const map = new Map() + const roots = [] + + // 创建映射 + items.forEach((item) => { + map.set(item.id, { ...item, children: [] }) + }) + + // 构建树 + items.forEach((item) => { + const node = map.get(item.id) + if (item.parentId && map.has(item.parentId)) { + map.get(item.parentId).children.push(node) + } else { + roots.push(node) + } + }) + + return roots +} diff --git a/vue/src/utils/scrollLock.js b/vue/src/utils/scrollLock.js new file mode 100644 index 0000000..5030bbc --- /dev/null +++ b/vue/src/utils/scrollLock.js @@ -0,0 +1,25 @@ +/** + * 全局滚动锁工具 + * 使用引用计数,支持多个弹窗同时打开而不冲突 + */ +let lockCount = 0 + +/** + * 锁定背景滚动 + */ +export function lockScroll() { + lockCount++ + if (lockCount === 1) { + document.body.style.overflow = 'hidden' + } +} + +/** + * 解锁背景滚动(仅当所有弹窗都关闭后才恢复) + */ +export function unlockScroll() { + lockCount = Math.max(0, lockCount - 1) + if (lockCount === 0) { + document.body.style.overflow = '' + } +} diff --git a/vue/src/utils/storage.js b/vue/src/utils/storage.js new file mode 100644 index 0000000..951b2f4 --- /dev/null +++ b/vue/src/utils/storage.js @@ -0,0 +1,21 @@ +export const AUTH_STORAGE_KEY = 'wangpan-auth-v1' +export const AUTH_SESSION_STORAGE_KEY = 'wangpan-auth-session-v1' +export const DASHBOARD_STORAGE_KEY = 'wangpan-dashboard-v1' +export const COLUMN_STORAGE_KEY = 'wangpan-columns-v1' + +export function readStorage(key, fallback) { + try { + const raw = localStorage.getItem(key) + return raw ? JSON.parse(raw) : fallback + } catch { + return fallback + } +} + +export function writeStorage(key, value) { + localStorage.setItem(key, JSON.stringify(value)) +} + +export function removeStorage(key) { + localStorage.removeItem(key) +} diff --git a/vue/vite.config.js b/vue/vite.config.js new file mode 100644 index 0000000..afa5c91 --- /dev/null +++ b/vue/vite.config.js @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue()], + server: { + host: '0.0.0.0', // 允许局域网访问 + port: 5173, + strictPort: true, // ✅ 强制使用指定端口,如果被占用则报错而不是自动切换 + proxy: { + '/api': { + target: 'http://192.168.1.18:8888', + changeOrigin: true, + secure: false, + }, + }, + }, +})