diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e291365
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..1f704be
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,4 @@
+# 浏览器请求地址。开发环境通过 Vite 代理,生产环境需配置同源反向代理。
+VITE_API_BASE_URL=/api
+# 仅供 Vite 开发服务器使用,不会暴露后端密钥。
+API_PROXY_TARGET=http://localhost:3412
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..365ffaf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+node_modules
+dist
+coverage
+.env
+.env.*.local
+.eslintcache
+*.tsbuildinfo
+.DS_Store
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..1ab0520
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1 @@
+{ "recommendations": ["Vue.volar", "oxc.oxc-vscode", "dbaeumer.vscode-eslint", "bradlc.vscode-tailwindcss"] }
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..ce05750
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,6 @@
+{
+ "editor.defaultFormatter": "oxc.oxc-vscode",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" },
+ "vue.server.hybridMode": true
+}
diff --git a/README.md b/README.md
index f926be0..30f8907 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,150 @@
# Short Drama Agent Front
-短剧 Agent 前端工作台,采用 Vite、Vue、TypeScript、Tailwind CSS 与 Reka UI。
+短剧 Agent 的前端工作台。第一阶段支持 `create-drama` 与 `breakdown`,使用真实后端 API,不包含演示数据或浏览器端模型调用。
-初期开发在 `dev` 分支,功能范围为 `create-drama` 与 `breakdown`。
+## 启动
+
+需要 Node.js >= 22.18 和 pnpm 11。
+
+```bash
+pnpm install
+cp .env.example .env
+pnpm dev
+```
+
+浏览器访问 http://localhost:5173。先在后端仓库启动 `pnpm dev`,默认监听 **3412**。
+
+```dotenv
+VITE_API_BASE_URL=/api
+API_PROXY_TARGET=http://localhost:3412
+```
+
+更改环境变量后重启 Vite。后端若运行在其他机器或端口,请修改 `API_PROXY_TARGET`。不能把 API Key、数据库密码等秘密写入任何 `VITE_*` 变量。
+
+## 功能
+
+| 工作区 | 已实现 |
+| --- | --- |
+| 项目列表 | 真实项目读取、搜索、状态筛选、新建剧本 |
+| 剧本创作 | 主题/风格/集数配置、剧集目录与正文、角色、世界观、审核、执行记录、正文导出 |
+| 创作恢复 | 恢复剧集生成、恢复改写,操作前确认 |
+| 拆解配置 | 每组集数、人物/场景/道具模块选择、后端分组预览 |
+| 拆解结果 | 主体、别名、视觉形态、Episode → Beat → Shot、主体绑定、抽取任务状态、校验问题、JSON 导出 |
+| 拆解恢复 | 失败抽取重试、缺失剧集镜头补齐、分镜引用与 Form 绑定修复 |
+
+本阶段没有实现:手工编辑剧本(后端暂无对应写接口)、StoryboardDirection graph、图像或视频生成、用户登录。正文中的模型输出按纯文本显示,避免执行不可信 HTML。
+
+## 技术与规范
+
+- Vite 8、Vue 3、TypeScript、Vue Router
+- Tailwind CSS 4,通过 `@tailwindcss/vite` 集成
+- Reka UI:Dialog、Tabs、Checkbox、Progress 等无样式基础组件
+- Oxlint:脚本质量检查;ESLint:补充 Vue 模板语义
+- Oxfmt(不是 `oxformat`):沿用后端四空格、单引号、无分号、无尾逗号的风格
+- Vitest + Vue Test Utils + happy-dom:API 契约、组件交互和异步生命周期测试
+- Lefthook + Commitlint:提交前执行检查,提交信息使用 `feat:`、`fix:`、`refactor:` 等
+
+```bash
+pnpm check # lint + format:check + typecheck + test
+pnpm format # 格式化
+pnpm lint:fix # 修复可自动处理的问题
+pnpm build # Vue 类型检查 + 生产构建
+pnpm preview # 预览 dist,不含开发代理
+```
+
+Oxfmt 不负责代码质量,Oxlint 不负责 Vue 的完整类型推导;Vue SFC 的类型检查由 `vue-tsc` 承担。未照搬后端 `--type-aware` 来冒充完整 Vue 类型检查。
+
+## 目录
+
+| 目录 | 职责 |
+| --- | --- |
+| `src/features/projects` | 项目接口、类型、列表、新建弹窗、项目布局与共享上下文 |
+| `src/features/create-drama` | 剧本创作 graph 页面 |
+| `src/features/breakdown` | 拆解 graph 的 API、类型、配置页面、主体与分镜组件 |
+| `src/features/workflows` | checkpoint 选择、执行记录、长请求互斥与操作确认 |
+| `src/components/ui` | 跨业务使用的 Reka UI 封装与公共展示组件 |
+| `src/composables` | 具备取消和竞态保护的轮询 |
+| `src/lib` | HTTP、错误格式、日期和导出工具 |
+| `src/router` | 按 graph 拆分的懒加载路由 |
+
+每个功能模块通过 `index.ts` 暴露公共入口。业务类型和行为函数均添加中文职责注释。不要把下一条 graph 的服务继续堆进 `App.vue`。
+
+## 接口基线
+
+对齐后端 `qianlanse/short-drama-agent` 的 `dev`:
+`de47d42eddd5acdbd32a8b4ed3044f9155d18508`。
+
+| HTTP | 路径(以 /api 为前缀) | 用途 |
+| --- | --- | --- |
+| GET / POST | `/projects` | 项目列表 / 创建剧本 |
+| GET | `/projects/:id` | 正式数据库详情、剧集、角色、世界观与审核 |
+| GET | `/projects/:id/checkpoints` | 两条 graph 的 checkpoint;前端按 workflowName 过滤 |
+| POST | `/projects/:id/resume-generation` | 继续生成未完成剧集 |
+| POST | `/projects/:id/resume-rewrite` | 恢复审核与改写 |
+| GET | `/projects/:id/breakdown-preview?groupSize=3&modules=character,scene,prop` | 真实分组与任务预览 |
+| POST | `/projects/:id/breakdown/start` | 启动拆解 |
+| POST | `/projects/:id/breakdown/retry` | 仅重试失败的抽取 Task |
+| POST | `/projects/:id/breakdown/resume-shots` | 复用已成功剧集镜头,生成缺失剧集 |
+| POST | `/projects/:id/breakdown/resume-storyboard` | 修复已有镜头的 SubjectRef / Form,再持久化 |
+
+API 层也提供 `state` 与 `breakdown/latest` 方法。页面通过共享 checkpoint 查询取得阶段状态,避免重复拉取同样内容。
+
+### 异步与恢复约定
+
+1. `POST /projects` 返回顶层 `202 { projectId, status }`,其他接口通常返回 `{ data }`;HTTP 层分别兼容。
+2. Breakdown 和恢复接口目前是**长 HTTP 请求**。不设客户端短超时,不自动重试 POST。关闭页面只会丢失请求连接,**不会取消后端任务**。
+3. 项目列表每 12 秒、项目详情每 6 秒串行刷新。隐藏页面暂停自动查询。切换项目或离开布局时取消查询,旧响应不会覆盖新项目。
+4. 抽取任务进度不是全流程进度。抽取完成之后还需要主体合并、形态、节拍、镜头和持久化。
+5. 后端失败 checkpoint 可能只有错误。页面回看最近可用的完整快照以保留成果,同时使用最新记录的 execution 状态。显示的是**最近可用快照**,并不保证全部成果来自最后一次执行。
+6. 后端并不为每个阶段提供持久化的 running 状态。没有明确终态时,页面显示“阶段快照 · 执行状态待确认”,不会猜测成功或失败。
+7. 恢复操作要求确认后台已停止。当前只做浏览器会话内、项目级互斥,不能替代后端跨浏览器/跨用户的任务锁。断网后先检查后台和 checkpoint,不要立即重复点击。
+8. 修改分组或模块后,必须重新预览再启动;重新拆解会调用完整流程,可能替换旧的主体和分镜。
+
+### 后端限制
+
+目前 `checkpoints` 接口返回完整历史与 state,没有分页或轻量状态接口。长篇剧本会增加轮询流量。下一步建议后端增加:
+
+- 持久化 executionId、运行状态、跨请求互斥和幂等键;
+- 精简的进度接口及按 graph 分页的 checkpoint;
+- 将 Breakdown 改为 202 异步任务提交,再以轮询或 SSE 读取进度。
+
+前端没有假装这些能力已经存在,也没有为了显示进度去调用 `stream-test` 等测试接口。
+
+## 部署
+
+`pnpm build` 生成 `dist`,使用 Nginx 等静态服务器部署。SPA 深层路由需要回退到 `index.html`。
+开发代理只存在于 Vite dev server,`vite preview` 与生产环境需配置独立后端地址或反向代理。
+
+```nginx
+server {
+ listen 80;
+ root /var/www/short-drama-agent-front/dist;
+ index index.html;
+
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+
+ location /api/ {
+ # 保留 /api 前缀,不在 proxy_pass 后追加斜杠。
+ proxy_pass http://127.0.0.1:3412;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_read_timeout 3600s;
+ proxy_send_timeout 3600s;
+ proxy_buffering off;
+ }
+}
+```
+
+实际生成时间可能超过代理限制,仍建议将后端改为异步提交协议。部署前应给应用加访问控制;当前后端没有身份认证,不建议直接对公网开放。
+
+## 本地联调验收
+
+1. 后端启动成功,前端项目列表能显示数据库项目。
+2. 新建 3 集剧本,确认获取项目 ID 后进入创作页,完成后能读到正文。
+3. 查看角色、世界观与审核,再进入拆解页;预览分组与模块。
+4. 启动拆解,查看 checkpoint、主体、形态、每集 Beat / Shot 和绑定。
+5. 在可控测试项目中触发故障,确认 retry / resume-shots / resume-storyboard 各自对应正确失败阶段。
+
+自动测试使用模拟 API,只验证前端契约与交互,不等同于真实模型、MySQL 或浏览器视觉联调。请勿用正式项目随意制造失败以测试恢复。
diff --git a/commitlint.config.mjs b/commitlint.config.mjs
new file mode 100644
index 0000000..8f8a616
--- /dev/null
+++ b/commitlint.config.mjs
@@ -0,0 +1,2 @@
+/** 与后端一致,使用 Conventional Commits,例如 feat: 实现剧本工作台。 */
+export default { extends: ['@commitlint/config-conventional'] }
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..1d5e120
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,13 @@
+import pluginVue from 'eslint-plugin-vue'
+import tsParser from '@typescript-eslint/parser'
+
+/** Oxlint 检查脚本,ESLint 仅补充 Vue 模板语义检查,格式统一交给 Oxfmt。 */
+export default [
+ { ignores: ['dist/**', 'coverage/**', 'node_modules/**'] },
+ ...pluginVue.configs['flat/essential'],
+ {
+ files: ['**/*.vue'],
+ languageOptions: { parserOptions: { parser: tsParser } },
+ rules: { 'vue/multi-word-component-names': 'off' }
+ }
+]
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..e160c49
--- /dev/null
+++ b/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ 短剧工作台
+
+
+
+
+
+
diff --git a/lefthook.yml b/lefthook.yml
new file mode 100644
index 0000000..9f84e26
--- /dev/null
+++ b/lefthook.yml
@@ -0,0 +1,8 @@
+pre-commit:
+ commands:
+ check:
+ run: pnpm check
+commit-msg:
+ commands:
+ commitlint:
+ run: pnpm exec commitlint --edit {1}
diff --git a/oxfmt.config.ts b/oxfmt.config.ts
new file mode 100644
index 0000000..8d0fde4
--- /dev/null
+++ b/oxfmt.config.ts
@@ -0,0 +1,69 @@
+import { defineConfig } from 'oxfmt'
+
+/** 项目的 Oxfmt 格式化规则 */
+export default defineConfig({
+ // 单参数箭头函数省略括号
+ arrowParens: 'avoid',
+ // 多行标签的右尖括号单独换行
+ bracketSameLine: false,
+ // 对象大括号保留空格
+ bracketSpacing: true,
+ // 自动格式化嵌入代码
+ embeddedLanguageFormatting: 'auto',
+ // 统一使用 LF 换行
+ endOfLine: 'lf',
+ // 按 CSS 规则处理 HTML 空白
+ htmlWhitespaceSensitivity: 'css',
+ // 排除生成物、依赖和文档
+ ignorePatterns: [
+ 'dist',
+ 'coverage',
+ 'docs',
+ 'node_modules',
+ 'README*.md',
+
+ '.agent',
+ '.agents',
+ '.codex',
+ '.claude',
+ '.langgraph_api',
+ '.worktree',
+ '*.lock',
+ '*-lock.yaml'
+ ],
+ // 文件末尾保留换行
+ insertFinalNewline: true,
+ // JSON 系列文件单独配置
+ overrides: [
+ {
+ // 匹配 JSON、JSON5 和 JSONC
+ files: ['*.json', '*.json5', '*.jsonc', '**/*.json', '**/*.json5', '**/*.jsonc'],
+ options: {
+ // 保留原有属性引号
+ quoteProps: 'preserve',
+ // JSON 使用双引号
+ singleQuote: false,
+ // JSON 不添加尾逗号
+ trailingComma: 'none'
+ }
+ }
+ ],
+ // 单行最大 120 字符
+ printWidth: 120,
+ // 不自动折叠 Markdown 文本
+ proseWrap: 'never',
+ // 仅必要时给属性名加引号
+ quoteProps: 'as-needed',
+ // 语句末尾不加分号
+ semi: false,
+ // JavaScript 使用单引号
+ singleQuote: true,
+ // 不自动排序导入
+ sortImports: false,
+ // 不自动排序 package.json
+ sortPackageJson: false,
+ // 使用四空格缩进
+ tabWidth: 4,
+ // 多行结构不添加尾逗号
+ trailingComma: 'none'
+})
diff --git a/oxlint.config.ts b/oxlint.config.ts
new file mode 100644
index 0000000..9b07033
--- /dev/null
+++ b/oxlint.config.ts
@@ -0,0 +1,18 @@
+import { defineConfig } from 'oxlint'
+
+/** 延续后端质量规则,增加浏览器及 Vue 脚本环境。模板由 ESLint 补充。 */
+export default defineConfig({
+ categories: { correctness: 'error', suspicious: 'warn' },
+ env: { browser: true, node: true, es2021: true },
+ plugins: ['eslint', 'typescript', 'unicorn', 'vue', 'vitest'],
+ ignorePatterns: ['dist/**', 'coverage/**', 'node_modules/**'],
+ rules: {
+ eqeqeq: ['error', 'always'],
+ 'no-console': ['error', { allow: ['warn', 'error'] }],
+ 'no-debugger': 'error',
+ 'no-var': 'error',
+ 'prefer-const': 'error',
+ 'typescript/no-explicit-any': 'error',
+ 'vitest/no-focused-tests': 'error'
+ }
+})
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..122e590
--- /dev/null
+++ b/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "short-drama-agent-front",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vue-tsc --noEmit && vite build",
+ "preview": "vite preview",
+ "typecheck": "vue-tsc --noEmit",
+ "lint": "oxlint . && eslint . --cache",
+ "lint:fix": "oxlint --fix . && eslint . --cache --fix",
+ "format": "oxfmt .",
+ "format:check": "oxfmt --check .",
+ "test": "vitest run",
+ "check": "pnpm lint && pnpm format:check && pnpm typecheck && pnpm test",
+ "prepare": "lefthook install"
+ },
+ "dependencies": {
+ "vue": "3.5.42",
+ "vue-router": "^4.6.0",
+ "reka-ui": "2.10.4",
+ "@lucide/vue": "1.34.0"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^6.0.0",
+ "vite": "8.2.2",
+ "typescript": "^6.0.3",
+ "vue-tsc": "^3.2.0",
+ "tailwindcss": "^4.1.0",
+ "@tailwindcss/vite": "^4.1.0",
+ "@types/node": "^24.0.0",
+ "oxlint": "^1.78.0",
+ "oxfmt": "^0.60.0",
+ "eslint": "^10.0.0",
+ "eslint-plugin-vue": "^10.0.0",
+ "@typescript-eslint/parser": "^8.67.0",
+ "vitest": "^4.0.15",
+ "@vue/test-utils": "^2.4.6",
+ "happy-dom": "^20.0.0",
+ "lefthook": "^2.1.10",
+ "@commitlint/cli": "^21.2.2",
+ "@commitlint/config-conventional": "^21.2.2"
+ },
+ "engines": {
+ "node": ">=22.18.0",
+ "pnpm": ">=11.1.2"
+ },
+ "packageManager": "pnpm@11.19.0"
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..3b9c58d
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,3729 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@lucide/vue':
+ specifier: 1.34.0
+ version: 1.34.0(vue@3.5.42(typescript@6.0.3))
+ reka-ui:
+ specifier: 2.10.4
+ version: 2.10.4(vue@3.5.42(typescript@6.0.3))
+ vue:
+ specifier: 3.5.42
+ version: 3.5.42(typescript@6.0.3)
+ vue-router:
+ specifier: ^4.6.0
+ version: 4.6.4(vue@3.5.42(typescript@6.0.3))
+ devDependencies:
+ '@commitlint/cli':
+ specifier: ^21.2.2
+ version: 21.2.2(@types/node@24.13.3)(conventional-commits-parser@7.1.2)(typescript@6.0.3)
+ '@commitlint/config-conventional':
+ specifier: ^21.2.2
+ version: 21.2.2
+ '@tailwindcss/vite':
+ specifier: ^4.1.0
+ version: 4.3.3(vite@8.2.2(@types/node@24.13.3)(jiti@2.7.0))
+ '@types/node':
+ specifier: ^24.0.0
+ version: 24.13.3
+ '@typescript-eslint/parser':
+ specifier: ^8.67.0
+ version: 8.68.0(eslint@10.9.1(jiti@2.7.0))(typescript@6.0.3)
+ '@vitejs/plugin-vue':
+ specifier: ^6.0.0
+ version: 6.0.8(vite@8.2.2(@types/node@24.13.3)(jiti@2.7.0))(vue@3.5.42(typescript@6.0.3))
+ '@vue/test-utils':
+ specifier: ^2.4.6
+ version: 2.4.11(@vue/compiler-dom@3.5.42)(@vue/server-renderer@3.5.42)(vue@3.5.42(typescript@6.0.3))
+ eslint:
+ specifier: ^10.0.0
+ version: 10.9.1(jiti@2.7.0)
+ eslint-plugin-vue:
+ specifier: ^10.0.0
+ version: 10.10.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.9.1(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.9.1(jiti@2.7.0)))
+ happy-dom:
+ specifier: ^20.0.0
+ version: 20.11.6
+ lefthook:
+ specifier: ^2.1.10
+ version: 2.1.10
+ oxfmt:
+ specifier: ^0.60.0
+ version: 0.60.0
+ oxlint:
+ specifier: ^1.78.0
+ version: 1.80.0
+ tailwindcss:
+ specifier: ^4.1.0
+ version: 4.3.3
+ typescript:
+ specifier: ^6.0.3
+ version: 6.0.3
+ vite:
+ specifier: 8.2.2
+ version: 8.2.2(@types/node@24.13.3)(jiti@2.7.0)
+ vitest:
+ specifier: ^4.0.15
+ version: 4.1.11(@types/node@24.13.3)(happy-dom@20.11.6)(vite@8.2.2(@types/node@24.13.3)(jiti@2.7.0))
+ vue-tsc:
+ specifier: ^3.2.0
+ version: 3.3.11(typescript@6.0.3)
+
+packages:
+
+ '@babel/code-frame@7.29.7':
+ resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.29.7':
+ resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.29.7':
+ resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.8':
+ resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/types@7.29.8':
+ resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==}
+ engines: {node: '>=6.9.0'}
+
+ '@commitlint/cli@21.2.2':
+ resolution: {integrity: sha512-a+6hQxIxnpdvSvS2apvttPNbEliYsVC3PqFYDiiB2kjbwIsQsj1urvQ4Tkf70pKYozPalKAuRQmm/GHwndduqA==}
+ engines: {node: '>=22.12.0'}
+ hasBin: true
+
+ '@commitlint/config-conventional@21.2.2':
+ resolution: {integrity: sha512-NxA37SZviusFUEYOQZ5hNnZ1h7O/KiemPkxjOlpzKJNnWxThiwc6/SaZhaPa8fyLvfRBAywhQhJJk8XESHWlpQ==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/config-validator@21.2.0':
+ resolution: {integrity: sha512-t7AzNHAKeIdo/3NRGwzpufKHsKkPHmFs/56N2Fnsh0/r0rGtnQzTxk6vnFgjaGr4hdSQKNB50/KAhR9Yk4LJKA==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/ensure@21.2.0':
+ resolution: {integrity: sha512-76IF9vDNS13lAzEEik9eKwzt8f9hYhWiwVXZ2AnyLCz5/f511FsEQ3pw1X3/zSQpdRLQU7i5qDMVKyXi1GWjSg==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/execute-rule@21.0.1':
+ resolution: {integrity: sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/format@21.2.2':
+ resolution: {integrity: sha512-v6fvxZSc/AvVMROlr3H34+1766bZSYApRUSCAMjWamStPjKMvZ8GdvVA5YW/VQNgbFTmcMz6OYmSTJEvIjPrfA==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/is-ignored@21.2.2':
+ resolution: {integrity: sha512-9UoKNgfFE3LU7FrzierCvk3CdDfMDeVGC86qZiT/n0TIjfq/dmZ9MHuXd45OTNRa26ZanmJRxEtmiXk/lEJihg==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/lint@21.2.2':
+ resolution: {integrity: sha512-Fy8JxEBzdmsYWFude/61GxXu5O+wEymwiRK2z9GL9R8mCsXphCoGxAFc5iHn5mjlfcSrhiiONE+ksf4KOjnaPg==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/load@21.2.2':
+ resolution: {integrity: sha512-0Tt6wDPX167cjKC5D4zhm0+20wJJG+TN/TKovMOspfSe78rOnKX+MNzlVNiu6HyQPZChPJ8QBH31MVt6Bb8fCg==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/message@21.2.0':
+ resolution: {integrity: sha512-YxGoiXD/HXNXLJPrQwE5poXa+XH0CBEm+mdvbHQP0g6MV/dmJyUFCzPNzZbxL93GvZ70TmtTK0Z0/IBpAqHv8g==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/parse@21.2.2':
+ resolution: {integrity: sha512-MEkobPfvRp+z06Wro8HMG1BDGHzZmj82A1LH1nWeG3ipHpg/x4m6v3wEDvMBIKjRFUnfR3nBeFs3MVCr7UdAmg==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/read@21.2.1':
+ resolution: {integrity: sha512-hUW7EJQnNTL0vPOmVMNK4CrnrNBN0nN+JJHReFkdHO5y4iyHeEmTBwuC15OCqUTjxWo7idnH1LftfpWVIaPWIA==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/resolve-extends@21.2.2':
+ resolution: {integrity: sha512-RPkJ/IFi7sMUUVbZLqwWFtWw/zRDcfFsmrPSiTMrt5wb7AdxOr86EGQFvmGzef5QKV5IPBWWCujqVTw1RWX44A==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/rules@21.2.2':
+ resolution: {integrity: sha512-eplQzyYkBjYB1HyyRj8hkcK11Y9DU9nuBz7uOKEd6NpE9NGDytLFCAnlRE+OoiK/5sHEJsaz2RGhuWBvYzIbNA==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/to-lines@21.0.1':
+ resolution: {integrity: sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/top-level@21.2.0':
+ resolution: {integrity: sha512-Y5gmQ+KxzqCrBFJfLvFEPvvwD3LDiNZoTT2yeFBm96M8qhmqSzQc5DvX3rheAaAMjyIvMXOCLS/mWfdpONsjyQ==}
+ engines: {node: '>=22.12.0'}
+
+ '@commitlint/types@21.2.0':
+ resolution: {integrity: sha512-7zVFCDB2reMvJH5dmbKnOQPjZEvjdJTH8jc0U/PIPU1r3/+vf5pD1HlfitV2MWsWXrvu7u39iY1lyLUPOaN0Gw==}
+ engines: {node: '>=22.12.0'}
+
+ '@conventional-changelog/git-client@3.1.2':
+ resolution: {integrity: sha512-jZqwnJwf7nboIlAcw/mkOjVa6DexCcUOgT2oOQgkoi3z9vR8tGFkcMy2BFcYwjhL9sYcDDXkRQDayiDieCoW7A==}
+ engines: {node: '>=22'}
+ peerDependencies:
+ conventional-commits-filter: ^6.0.1
+ conventional-commits-parser: ^7.1.2
+ peerDependenciesMeta:
+ conventional-commits-filter:
+ optional: true
+ conventional-commits-parser:
+ optional: true
+
+ '@conventional-changelog/template@1.4.0':
+ resolution: {integrity: sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==}
+ engines: {node: '>=22'}
+
+ '@eslint-community/eslint-utils@4.10.1':
+ resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.2':
+ resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.23.5':
+ resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/config-helpers@0.7.0':
+ resolution: {integrity: sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/core@1.2.1':
+ resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/object-schema@3.0.5':
+ resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/plugin-kit@0.7.2':
+ resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@floating-ui/core@1.8.0':
+ resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==}
+
+ '@floating-ui/dom@1.8.0':
+ resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==}
+
+ '@floating-ui/utils@0.2.12':
+ resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==}
+
+ '@floating-ui/vue@1.1.11':
+ resolution: {integrity: sha512-HzHKCNVxnGS35r9fCHBc3+uCnjw9IWIlCPL683cGgM9Kgj2BiAl8x1mS7vtvP6F9S/e/q4O6MApwSHj8hNLGfw==}
+
+ '@humanfs/core@0.19.2':
+ resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.8':
+ resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/types@0.15.0':
+ resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
+ '@internationalized/date@3.12.3':
+ resolution: {integrity: sha512-fuLX+3ZKLsxI73y8b01EG/WjHb6gE6weCqlfawPO27kBWGMh9G1yH6Csv1uU7/cac9H2GHmOMt6CjmuQ1aia4Q==}
+
+ '@internationalized/number@3.6.7':
+ resolution: {integrity: sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg==}
+
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@lucide/vue@1.34.0':
+ resolution: {integrity: sha512-eB48kx1oe9O6mJAg0gFWgv66y4rhYJYuhfWF8wIz/YBFPDDHWHI7+kRJpkvz/8kePY6ddbwxXIGfP8E54wlGhA==}
+ peerDependencies:
+ vue: '>=3.0.1'
+
+ '@one-ini/wasm@0.1.1':
+ resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
+
+ '@oxc-project/types@0.146.0':
+ resolution: {integrity: sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==}
+
+ '@oxfmt/binding-android-arm-eabi@0.60.0':
+ resolution: {integrity: sha512-1q4q4Jc8FlOMVojEisyFAVyl8h1yawNv6phjgmhGVEDeyeOdsSnSr9x0+D4mOnEKvpO5L4mxKZ/DP9X6U3A/Mw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxfmt/binding-android-arm64@0.60.0':
+ resolution: {integrity: sha512-tD41I6nCt9k8SQXft0CSjjU9jg6SwG7uMu7PxodSEHXl+GDW0868oy6tTtoJkyUze8YKFgTpz/k5LuPUnFiGLw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxfmt/binding-darwin-arm64@0.60.0':
+ resolution: {integrity: sha512-TTpzPug96Zxdyb46KvTyIUQDdsqbumXh2TKG9C23PCT0kF7JkW56Z/quPuG9rqOFKQIi1gpRNZ7DX18LwxXPnw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxfmt/binding-darwin-x64@0.60.0':
+ resolution: {integrity: sha512-CnOoWgQ7L+JL/YQaRJ+NyATciSfcftncm7y3kqyte1cGtFEGnStaCd1TAyrinkfQ7nRBfHrTs1/vTwUJr3WF2Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxfmt/binding-freebsd-x64@0.60.0':
+ resolution: {integrity: sha512-ychJo7S3hZxdO6eDZ9zM6F2lM9fpJS3EKS5CAUSWyprdLYxTu4gbaUKV/VBPTcMJwQa2Bpo+643y3OJ537pihA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxfmt/binding-linux-arm-gnueabihf@0.60.0':
+ resolution: {integrity: sha512-36IH5o55T2Fx7E0feDttt+mifxN6yk9pWv4KfhAIsP0dFnUq27331OwbpOsZdoXF9soOLWm7mQUz5+UUmyec4g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxfmt/binding-linux-arm-musleabihf@0.60.0':
+ resolution: {integrity: sha512-G1Ve7lAa6sFBolVI2LWHfEAqy0YKh4vnioH8uYO9kAEdgM7mR40IksIx9/Zk4+vbYew/sGa4J9Q4tZ3n9gXDHA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxfmt/binding-linux-arm64-gnu@0.60.0':
+ resolution: {integrity: sha512-LTQdRBf6uzj/h7Xk6lKzbGD2hrF/fK4YI9LIN1c0509tPUn8wRa3mCmrFQpEWJPLYGFrLFFMTYW1Ljj6VqW2Hw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-arm64-musl@0.60.0':
+ resolution: {integrity: sha512-2JMo3XPxMPx3hiqddSZYyaH+fKJm6cz0u8n1naYjP/CdOQOZW34i8lKBUfmbWiuFvd6KoYXLmhAyBuvojsYS7Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxfmt/binding-linux-ppc64-gnu@0.60.0':
+ resolution: {integrity: sha512-L3C+nBD13lr306tr/PjM3RMll+BVqgFrIgUyoeHuai5oueJrRLgO3j+GO5/Cbhtkf5PSlHYTI1JY7iqBd1qa6A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-riscv64-gnu@0.60.0':
+ resolution: {integrity: sha512-M4MsmvqlxFiPtSRGyBYQSZxchEf463AOyd+Dh4/9xDpjWBsRtDUTDMFN5EdHinjVK1/eDJQ8MLpcYjpYayaCnA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-riscv64-musl@0.60.0':
+ resolution: {integrity: sha512-OH+9UskYuxRB+GxqdGkVN8f5UpwhqG8YscNo1wl8+KJ62cd7wZdGga6iGLJIf8kibF1WBwvlfDUx3cez/VXwFg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxfmt/binding-linux-s390x-gnu@0.60.0':
+ resolution: {integrity: sha512-y7AAFutt9wFWBFOAn6+BHaV39usZmcr3YYH2385f+NHgPNpIF9HpqKp0jgUxPaUOCyG3oaX5VhJduL1Nw164rw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-x64-gnu@0.60.0':
+ resolution: {integrity: sha512-yKZ9+CXAI+1RO5nH/4Z/9M6DAsfOzd5bw/gtWk81KB4mpalMaRRSXfouc5/tHxazDmBek55HNPepNYBgaCew0Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-x64-musl@0.60.0':
+ resolution: {integrity: sha512-bCUGaF6hJOYnQzLJdHLZbvGsOd5oSvGAyJhPAKum2uyLYUuXmP8vqg690DWi2hqcnIoYpqSqCrjzE5aiUAgwQg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxfmt/binding-openharmony-arm64@0.60.0':
+ resolution: {integrity: sha512-GrUeZOvzP30ExxfCuQiyofuUGI+OmvAgFwOO5w5p9mGPlxcyuqI+6Sy9fAKFFfLQrqKYWFgc5sYA2Unj/29nPg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxfmt/binding-win32-arm64-msvc@0.60.0':
+ resolution: {integrity: sha512-WD4Q954kUl2TDJV/6q7UnE2rlKk047kXLJsr4bJ2mXRaAqNXcmV3nwKUsGCc3mz/jYDBnXtJEaBErJEybK8iQQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxfmt/binding-win32-ia32-msvc@0.60.0':
+ resolution: {integrity: sha512-HqDekjr8JXzVDUP1YthDZ1Y3CBEcuZT4WX3B+1kaxj8CvZA8Y2YhcEsXqoSop3tVsgjACxjnFQFDkBo0r/jq1Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxfmt/binding-win32-x64-msvc@0.60.0':
+ resolution: {integrity: sha512-tz78yhmGPKboTMHCHSaUqXK8JrmoSejgDcWeqAtg2s07ZGKQ3rH5Jn8NuXPGNG33CDbY2e9NoQWXIVEmKO21Rw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@oxlint/binding-android-arm-eabi@1.80.0':
+ resolution: {integrity: sha512-RM3Plj+biQpxa5d1GOOX6ciDlcUROmm4OZ/pLTpitkQt2mJv4jhtY4cbgaetOm5UKWZe05/TGQ6o1Vl8EOHkrA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxlint/binding-android-arm64@1.80.0':
+ resolution: {integrity: sha512-YlO5JEf0Yr2bUUlu8O8daVcUxtcGGbcSmyV7E7nSbJbfAdxTE0PFPwgnIlw7wXJaTYjb+qs5hI5q3jxUkI7cAw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxlint/binding-darwin-arm64@1.80.0':
+ resolution: {integrity: sha512-BULDOyO3AhsmdWfQeIUCykDt3dd7XZBGLhp1eIh56skRv01O+cNjNPwXMIbeW1x4+pxcln5if72wcRgViVo7PA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxlint/binding-darwin-x64@1.80.0':
+ resolution: {integrity: sha512-YJ4JzLw7N5TDSQFlA0hAQGHvnDZgyypm1yunObVWcWiF9KM7eGCJKYKLgTC2Fi/57OdnBhbj4OkzPGdFQJ6HyA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxlint/binding-freebsd-x64@1.80.0':
+ resolution: {integrity: sha512-AYUIk5QnL0s8oWAYsREZwkRYy1SupJTXALo93J1TgzHywxQtdM99FecRMQ87MXEdPQ0j1TmEpeeq3fGNkpvMqg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.80.0':
+ resolution: {integrity: sha512-9hBZVANupQ89W9dXyE0n8doCyaW5pDyGn3y6XlIMPZ+rIKuyqkr3SNUXmVJIhuvUq0NBU3RBiSXXE69l4XI6KA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm-musleabihf@1.80.0':
+ resolution: {integrity: sha512-SvS2uKqzY+pbfuvAHzH4338R6Zwo805GAwrIMVvK1KxoOWCIjZUdfzTCvilD7z6JK91v011+zYMryabhDo2AsQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm64-gnu@1.80.0':
+ resolution: {integrity: sha512-tCLadyqRVL3pQTRPNg7cjXKvcvS4fbyXeQHhKk5BTJ1oftQln5/yIIWbu/Xom/DX41zv2P9QGt6+D/TtQVtY3A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-arm64-musl@1.80.0':
+ resolution: {integrity: sha512-XfpCNRlOPcLlJl4Bn/FUhjqlR6BVavEykERBf/MV7YA9VZDa5g5znVqYhyviMafcxS9Pe/i/kPvHNO0U6svEHQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-linux-ppc64-gnu@1.80.0':
+ resolution: {integrity: sha512-3I4yMwcFG9NeO8ioY6JBBuKsIm5GL/x7MATt1S4tVWaxPu5HcJ+XnLUbcVBTxG8q2Wu56HSj+NmXQiVYb1lp6A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-riscv64-gnu@1.80.0':
+ resolution: {integrity: sha512-E1wAKymkpe1/E8helzBKdm81OBOF+ezxRyXRMEuik3ZpWDER5CPOKZwF66RsdwW98uwZv8UTFremUQtC1CzdJA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-riscv64-musl@1.80.0':
+ resolution: {integrity: sha512-+gLRGD4sIo3+VA++iham5UxD9tKSoJ/VOrROCEXIcknrYtQg6iIQgvjN0cpiRF7N6UYC7pJbvHJlDnMge5LRpQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-linux-s390x-gnu@1.80.0':
+ resolution: {integrity: sha512-aR0PrzHj9leW3NmzBAAP4EzdoBNoJcs9sjnIQPIwyRnBGYrRbXUIpEB5Q39AqK3PLY5JK5uEhDQDiUa1QSAstw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-x64-gnu@1.80.0':
+ resolution: {integrity: sha512-vSVh5cSo3Xxs6ghBCcFJlpbkbENzDog1qXtoXLa/HC3aCrR4XO76GZbXmQoCPHnu99nQpdCeC3H9tdNICfDh7A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-x64-musl@1.80.0':
+ resolution: {integrity: sha512-FfzBXpNQ8u7/ZI/p8bl73MeZ508Ax3hxWp3SiJpEFiC+BB9XcXy5FAZHTLKDPSzrUpxQZSZJAVdDmuJp/+HDBQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-openharmony-arm64@1.80.0':
+ resolution: {integrity: sha512-zMzbkumtmprCgRwoYNzcB3iC39fXdJIMLMU33KdCjEGLlJGOEt1+LwQ4LF8ndLzAEKVz4BR0y3V6Xrkk3Nm3yA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxlint/binding-win32-arm64-msvc@1.80.0':
+ resolution: {integrity: sha512-ib6iRcrXsk4t1fm3iKcwksyWh1ZkZXC/2mEzakl0ai2+6HZunf1WWMZ/xP9EJAvw9g9K4UVTC3NF/+G2qLrbTQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxlint/binding-win32-ia32-msvc@1.80.0':
+ resolution: {integrity: sha512-xhRWBMpLxZvgKAH6+DJZmpP+W8Y8UdQOSU1JfxSWNXsaBaRGW77j+1hCuNHlzj7OH4SPN8fYd1q0o2qrDtoVyw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxlint/binding-win32-x64-msvc@1.80.0':
+ resolution: {integrity: sha512-yAnO7lwBYQnz2pcfBPIGQQZWIX5zd5R/1aAKIF3oE+TVj7IhoHcROjOkz3sRDngzqhfPKfFaXqug5j5rE5dn6Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@pkgjs/parseargs@0.11.0':
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+
+ '@rolldown/binding-android-arm-eabi@1.2.5':
+ resolution: {integrity: sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@rolldown/binding-android-arm64@1.2.5':
+ resolution: {integrity: sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@rolldown/binding-darwin-arm64@1.2.5':
+ resolution: {integrity: sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-x64@1.2.5':
+ resolution: {integrity: sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rolldown/binding-freebsd-x64@1.2.5':
+ resolution: {integrity: sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.2.5':
+ resolution: {integrity: sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-gnu@1.2.5':
+ resolution: {integrity: sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-arm64-musl@1.2.5':
+ resolution: {integrity: sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-linux-ppc64-gnu@1.2.5':
+ resolution: {integrity: sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-s390x-gnu@1.2.5':
+ resolution: {integrity: sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-gnu@1.2.5':
+ resolution: {integrity: sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-musl@1.2.5':
+ resolution: {integrity: sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-openharmony-arm64@1.2.5':
+ resolution: {integrity: sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rolldown/binding-win32-arm64-msvc@1.2.5':
+ resolution: {integrity: sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rolldown/binding-win32-x64-msvc@1.2.5':
+ resolution: {integrity: sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@rolldown/pluginutils@1.0.1':
+ resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
+
+ '@simple-libs/child-process-utils@2.0.0':
+ resolution: {integrity: sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==}
+ engines: {node: '>=22'}
+
+ '@simple-libs/stream-utils@2.0.0':
+ resolution: {integrity: sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==}
+ engines: {node: '>=22'}
+
+ '@standard-schema/spec@1.1.0':
+ resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+
+ '@swc/helpers@0.5.23':
+ resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==}
+
+ '@tailwindcss/node@4.3.3':
+ resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==}
+
+ '@tailwindcss/oxide-android-arm64@4.3.3':
+ resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.3':
+ resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.3.3':
+ resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.3':
+ resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3':
+ resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.3':
+ resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.3':
+ resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.3':
+ resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.3':
+ resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.3':
+ resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.3':
+ resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.3':
+ resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.3.3':
+ resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==}
+ engines: {node: '>= 20'}
+
+ '@tailwindcss/vite@4.3.3':
+ resolution: {integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7 || ^8
+
+ '@tanstack/virtual-core@3.17.8':
+ resolution: {integrity: sha512-BfEvehNpOT75r5Ksc5xW6NZuXujTfb7nlSEyVu4XHG3gdxNg1KqXruWbDewXOUaUYIo4oRbSfkjIajz4MAT8tA==}
+
+ '@tanstack/vue-virtual@3.13.36':
+ resolution: {integrity: sha512-gKpExv4RbB9luVG+SucTXoqPZv/gzu/Yvz6BNO+8kpNxJ2x+I/ulryzl5W9BRciahZGp5Tls3Dp5XP1ztVGbMw==}
+ peerDependencies:
+ vue: ^2.7.0 || ^3.0.0
+
+ '@types/chai@5.2.3':
+ resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
+
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
+ '@types/esrecurse@4.3.1':
+ resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==}
+
+ '@types/estree@1.0.9':
+ resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/node@24.13.3':
+ resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==}
+
+ '@types/web-bluetooth@0.0.21':
+ resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
+ '@types/whatwg-mimetype@3.0.2':
+ resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==}
+
+ '@types/ws@8.18.1':
+ resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
+
+ '@typescript-eslint/parser@8.68.0':
+ resolution: {integrity: sha512-fHq2VC1kpyYfvEcbiMjOpySY4WS7voEp89yAThrHRX5sm9j2lzYppCb2umFMEed4fWcyeLjHxrz0mpjNBaBxMQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/project-service@8.68.0':
+ resolution: {integrity: sha512-5GQtWZCXFcFYux955pvoS02WLc49pXNlvIxocKjS0clvwo3in1RdlzVKyiqQH9vE5AKWFLTaUgeQkOrTS+0Qxw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/scope-manager@8.68.0':
+ resolution: {integrity: sha512-T5eXpcaJNg8bhjHJ8Rjp68Vq/QBteYtTKY8TZqVNPaUbuz0f6jI9t6aDkylwvalpAB9XTTFeFOjrjXAZ3YvmVA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.68.0':
+ resolution: {integrity: sha512-F7zrGQfiJHojPwi8vhxZQC1tWtJzvL74cK/nqri2lk8YUXvYaYwl263xOJ69jDWPUk1hmcdoayFwk9lX09npVw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/types@8.68.0':
+ resolution: {integrity: sha512-9RnpsGJjrAllCMefGVVsImJM24YurhC0Q1h4UbvivtvOqXmR/vEJge2OoE++z9m6hyg8T1Q8t5SNT6tHSbrxcg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.68.0':
+ resolution: {integrity: sha512-OKKsD0tYmoNiU5PW2zehO1yO56jYOm1ShYlxon/Z0SJNidAkdVg86eg9ruRuoXf8xfnuWZGbwDsStkoXbZtIIA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/visitor-keys@8.68.0':
+ resolution: {integrity: sha512-YR65gGdGvTUAWLldC3xLOvOzamdGzB4A5/N8rehEaHs3Zvoe39BhgY+u0SPch1OvrVTfLcc55wsSgK2NcnTS/A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@vitejs/plugin-vue@6.0.8':
+ resolution: {integrity: sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==}
+ 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
+
+ '@vitest/expect@4.1.11':
+ resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==}
+
+ '@vitest/mocker@4.1.11':
+ resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^6.0.0 || ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@4.1.11':
+ resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==}
+
+ '@vitest/runner@4.1.11':
+ resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==}
+
+ '@vitest/snapshot@4.1.11':
+ resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==}
+
+ '@vitest/spy@4.1.11':
+ resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==}
+
+ '@vitest/utils@4.1.11':
+ resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==}
+
+ '@volar/language-core@2.4.28':
+ resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==}
+
+ '@volar/source-map@2.4.28':
+ resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==}
+
+ '@volar/typescript@2.4.28':
+ resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@vue/compiler-core@3.5.41':
+ resolution: {integrity: sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==}
+
+ '@vue/compiler-core@3.5.42':
+ resolution: {integrity: sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==}
+
+ '@vue/compiler-dom@3.5.41':
+ resolution: {integrity: sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==}
+
+ '@vue/compiler-dom@3.5.42':
+ resolution: {integrity: sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==}
+
+ '@vue/compiler-sfc@3.5.42':
+ resolution: {integrity: sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==}
+
+ '@vue/compiler-ssr@3.5.42':
+ resolution: {integrity: sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==}
+
+ '@vue/devtools-api@6.6.4':
+ resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+ '@vue/language-core@3.3.11':
+ resolution: {integrity: sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==}
+
+ '@vue/reactivity@3.5.42':
+ resolution: {integrity: sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==}
+
+ '@vue/runtime-core@3.5.42':
+ resolution: {integrity: sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==}
+
+ '@vue/runtime-dom@3.5.42':
+ resolution: {integrity: sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==}
+
+ '@vue/server-renderer@3.5.42':
+ resolution: {integrity: sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==}
+
+ '@vue/shared@3.5.41':
+ resolution: {integrity: sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==}
+
+ '@vue/shared@3.5.42':
+ resolution: {integrity: sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==}
+
+ '@vue/test-utils@2.4.11':
+ resolution: {integrity: sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==}
+ peerDependencies:
+ '@vue/compiler-dom': 3.x
+ '@vue/server-renderer': 3.x
+ vue: 3.x
+ peerDependenciesMeta:
+ '@vue/server-renderer':
+ optional: true
+
+ '@vueuse/core@14.4.0':
+ resolution: {integrity: sha512-X4WHz1HlCzCBoYXesUkifzzWBAcZgXG8Fi5iNPQg/epdzOB3gu8Fawj3hvuwYR1nGcXGnvxwYYcUC/71++svtQ==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ '@vueuse/metadata@14.4.0':
+ resolution: {integrity: sha512-swx/255R6JyHZFJhx845iz5CRWDZdCfvkZOpACWc5+c5WHcG24mv8gUT1WIdFQaHt6dq79rvILd9QnCWiyVm9g==}
+
+ '@vueuse/shared@14.4.0':
+ resolution: {integrity: sha512-JRgY90Sz8DDtPMsaDflvPMp9xYk69JZAmbuDvAquUVXKr2gEjqtzGNTTthLfckH0BzBqvnu31gb4a8TGLRe79g==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ abbrev@2.0.0:
+ resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.18.0:
+ resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv@6.15.0:
+ resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
+
+ ajv@8.20.0:
+ resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
+
+ alien-signals@3.2.1:
+ resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.3.0:
+ resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ argue-cli@3.1.0:
+ resolution: {integrity: sha512-DhBpBfXL4SS2uC0N922MMajKR3CdrTG0u2or1PNYgXMsrSzViJrbtvT0nCLlLGUI0plam/ZZCs7aAauHtW9thw==}
+ engines: {node: '>=22'}
+
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
+ engines: {node: '>=10'}
+
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ balanced-match@4.0.4:
+ resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+ engines: {node: 18 || 20 || >=22}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ brace-expansion@2.1.4:
+ resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
+
+ brace-expansion@5.0.9:
+ resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==}
+ engines: {node: 20 || >=22}
+
+ buffer-image-size@0.6.4:
+ resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==}
+ engines: {node: '>=4.0'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ chai@6.2.2:
+ resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
+ engines: {node: '>=18'}
+
+ cliui@9.0.1:
+ resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==}
+ engines: {node: '>=20'}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ commander@10.0.1:
+ resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
+ engines: {node: '>=14'}
+
+ config-chain@1.1.13:
+ resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+
+ conventional-changelog-angular@9.4.0:
+ resolution: {integrity: sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w==}
+ engines: {node: '>=22'}
+
+ conventional-changelog-conventionalcommits@10.4.0:
+ resolution: {integrity: sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==}
+ engines: {node: '>=22'}
+
+ conventional-commits-parser@7.1.2:
+ resolution: {integrity: sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==}
+ engines: {node: '>=22'}
+ hasBin: true
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cosmiconfig-typescript-loader@6.3.0:
+ resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==}
+ engines: {node: '>=v18'}
+ peerDependencies:
+ '@types/node': '*'
+ cosmiconfig: '>=9'
+ typescript: '>=5'
+
+ cosmiconfig@9.0.2:
+ resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ typescript: '>=4.9.5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ defu@6.1.7:
+ resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ editorconfig@1.0.7:
+ resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ emoji-regex@10.6.0:
+ resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ enhanced-resolve@5.24.5:
+ resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==}
+ engines: {node: '>=10.13.0'}
+
+ entities@7.0.1:
+ resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
+ engines: {node: '>=0.12'}
+
+ env-paths@2.2.1:
+ resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
+ engines: {node: '>=6'}
+
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
+ es-module-lexer@2.3.2:
+ resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==}
+
+ es-toolkit@1.51.0:
+ resolution: {integrity: sha512-zC2lQGkM7QX+Gm6iM3+WIdZJzthsEd14LvRNJneSO2hzyz/zNBENR8+YXWo1cKxgPBtV6ksPYHELbcwBRzmdCw==}
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-plugin-vue@10.10.0:
+ resolution: {integrity: sha512-dL9x9rBHqqNcByWiLOHK6L0SB97V82/NC0cZRn9cXPjM7pCuWlpQQP9bFH4vjBv80ej1ZpzAkuD8zWH1o9bZbA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0
+ '@typescript-eslint/parser': ^7.0.0 || ^8.0.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ vue-eslint-parser: ^10.3.0
+ peerDependenciesMeta:
+ '@stylistic/eslint-plugin':
+ optional: true
+ '@typescript-eslint/parser':
+ optional: true
+
+ eslint-scope@9.1.2:
+ resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@5.0.1:
+ resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ eslint@10.9.1:
+ resolution: {integrity: sha512-9VaAkDURekixUQJy0oJYl2DcN6oKMfxay7XzaGYAWQwsb6qfKf+x76R2k1L8kb1boc+FyCAaTA9GmiKaaiaF+A==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@11.2.0:
+ resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ esquery@1.7.0:
+ resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ expect-type@1.4.0:
+ resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==}
+ engines: {node: '>=12.0.0'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fast-uri@3.1.6:
+ resolution: {integrity: sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.4.4:
+ resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==}
+
+ foreground-child@3.3.1:
+ resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
+ engines: {node: '>=14'}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-east-asian-width@1.6.0:
+ resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==}
+ engines: {node: '>=18'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@10.5.0:
+ resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==}
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
+ hasBin: true
+
+ global-directory@5.0.0:
+ resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==}
+ engines: {node: '>=20'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ happy-dom@20.11.6:
+ resolution: {integrity: sha512-Hldbg8AdAa5a5oDcZpjqnGitp7JB0hqWmfv/8qr+kft4vzSD8BHsbdRfzYvL/0QcbKcURC/yyoygbeDQarPvYg==}
+ engines: {node: '>=20.0.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ ini@6.0.0:
+ resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+ hasBin: true
+
+ jiti@2.7.0:
+ resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
+ hasBin: true
+
+ js-beautify@1.15.4:
+ resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ js-cookie@3.0.8:
+ resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@4.3.1:
+ resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ lefthook-darwin-arm64@2.1.10:
+ resolution: {integrity: sha512-nw+X8wRNDoUUV6WSteyKBbcLySq+fsmZt5WV/s50ZJpysmsDKJOUMln6SllNfP+60dzUahAO7REco/2633BsLg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ lefthook-darwin-x64@2.1.10:
+ resolution: {integrity: sha512-KQ/bHmvpkFdHMn4pZnUdTf+GuSC+aBBgBTxZT4GW+6cSf+qbErKZBhK7cH6BmILsvx43+VzEArvHYY7YOfRFOQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ lefthook-freebsd-arm64@2.1.10:
+ resolution: {integrity: sha512-8su6DwydP7+pv7kG0zCtjphqsw4ouOnfexRUErapy5GTxYBoUOhYz3RSHTSWNRsK6W4jva7FPUh2Lp5/PSn30w==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ lefthook-freebsd-x64@2.1.10:
+ resolution: {integrity: sha512-GeAJEFxko3Lk+AsnS3NleAFrpyMLFUKOlgJvPKuU0xHwVEI/z+ZoCcmuO0BX+4CS0NLbZhC/YQAvBASqDvvVdQ==}
+ cpu: [x64]
+ os: [freebsd]
+
+ lefthook-linux-arm64@2.1.10:
+ resolution: {integrity: sha512-1sHTCmpTWjVMs+yKPBLRNT1kuuIr1yjietlk7rCB6wFPVOS6Ph3o2zPFH2AvW1UymHlqwyHXzBr9EtDpQ7j1mQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ lefthook-linux-x64@2.1.10:
+ resolution: {integrity: sha512-z/VlRB3bh6mBvW3r1rwnJ5vP8z+Krx5gJzkZ4veDXh+6FlRTx8wtd3g3fllOv/yZMxkgmL3fQoFXv05Esa7vBQ==}
+ cpu: [x64]
+ os: [linux]
+
+ lefthook-openbsd-arm64@2.1.10:
+ resolution: {integrity: sha512-430zL8sSIKw5P0YXGG6PB+eAhHa06n0PXuaERaAQE4Ss3odfqwnl5Mq9hQmkEnOS1EGiQEKkd0UHv/i4PtMNIQ==}
+ cpu: [arm64]
+ os: [openbsd]
+
+ lefthook-openbsd-x64@2.1.10:
+ resolution: {integrity: sha512-bgkO8PphGZVDhQgCJ524aYYPI5491pVmCiLPGjBIo1AvOSlIyw4N1Y+1C3QfqwEmechzw+Aq16SNc8pqv6UuXg==}
+ cpu: [x64]
+ os: [openbsd]
+
+ lefthook-windows-arm64@2.1.10:
+ resolution: {integrity: sha512-5Q6etF0Fla2DDA4ilDySrdNgiR5+W7cJZwnZ69Je3kvWCaWm4wnkuc8FEdjp3kiL2x3ZXipdI00f5vpO8aWmog==}
+ cpu: [arm64]
+ os: [win32]
+
+ lefthook-windows-x64@2.1.10:
+ resolution: {integrity: sha512-c/XH8YZtylG4XaxzqFfXluvq2LXq2W/p54Bnzn3+Z7E5X2Fk3JlFJAibulMbIt2+w8T7UI/r97ok5GqE4kGaeA==}
+ cpu: [x64]
+ os: [win32]
+
+ lefthook@2.1.10:
+ resolution: {integrity: sha512-K7mM4WoqMwqfXYK11EHy+lSH1uW8XHni3Yn/bSqyerPkUPygGdf3xn18JoV5HyA06xuQL3ofGAOjG01QX9oJ4w==}
+ hasBin: true
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lightningcss-android-arm64@1.32.0:
+ resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-android-arm64@1.33.0:
+ resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.32.0:
+ resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-arm64@1.33.0:
+ resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.32.0:
+ resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.33.0:
+ resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.32.0:
+ resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-freebsd-x64@1.33.0:
+ resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm-gnueabihf@1.33.0:
+ resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-gnu@1.33.0:
+ resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-arm64-musl@1.33.0:
+ resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-gnu@1.33.0:
+ resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.32.0:
+ resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-musl@1.33.0:
+ resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-arm64-msvc@1.33.0:
+ resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.33.0:
+ resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.32.0:
+ resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
+ engines: {node: '>= 12.0.0'}
+
+ lightningcss@1.33.0:
+ resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
+ engines: {node: '>= 12.0.0'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ minimatch@10.2.6:
+ resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
+ engines: {node: 18 || 20 || >=22}
+
+ minimatch@9.0.9:
+ resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass@7.1.3:
+ resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ muggle-string@0.4.1:
+ resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+ nanoid@3.3.18:
+ resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ nopt@7.2.1:
+ resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ hasBin: true
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ obug@2.1.4:
+ resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==}
+ engines: {node: '>=12.20.0'}
+
+ ohash@2.0.12:
+ resolution: {integrity: sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ oxfmt@0.60.0:
+ resolution: {integrity: sha512-fViX6i+gJuZWY+jI/fnR6WRbRj70GZ9RlCd30MygJrHTUNc4DxvKHWw8vBjMjffv3PgU5qWDR0AzmojQByqaZA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ svelte: ^5.0.0
+ vite-plus: '*'
+ peerDependenciesMeta:
+ svelte:
+ optional: true
+ vite-plus:
+ optional: true
+
+ oxlint@1.80.0:
+ resolution: {integrity: sha512-5nTiSps4qdbCWLbxzuO00alHkEO2exR9YMN/ig6QXWrLsYSG0KaObOAM+l6oU2LcKPWoSAGYbkZIGEu1ViiWKA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ oxlint-tsgolint: '>=7.0.2001'
+ vite-plus: '*'
+ peerDependenciesMeta:
+ oxlint-tsgolint:
+ optional: true
+ vite-plus:
+ optional: true
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.7:
+ resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==}
+ engines: {node: '>=12'}
+
+ postcss-selector-parser@7.1.5:
+ resolution: {integrity: sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==}
+ engines: {node: '>=4'}
+
+ postcss@8.5.26:
+ resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ proto-list@1.2.4:
+ resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ reka-ui@2.10.4:
+ resolution: {integrity: sha512-kbS5GAbkHkYj0EVKAg5ZPEPndhBjeUFSa1Aq5m5ftQwyHnB1v5tw8X6fcwfKH/ry3StPXblMWM1DW82MVS71cw==}
+ peerDependencies:
+ vue: '>= 3.4.0'
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
+ rolldown@1.2.5:
+ resolution: {integrity: sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
+ semver@7.8.5:
+ resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@4.2.0:
+ resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ string-width@7.2.0:
+ resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ engines: {node: '>=18'}
+
+ string-width@8.2.2:
+ resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==}
+ engines: {node: '>=20'}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.2.0:
+ resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
+ engines: {node: '>=12'}
+
+ tailwindcss@4.3.3:
+ resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==}
+
+ tapable@2.3.3:
+ resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
+ engines: {node: '>=6'}
+
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@1.3.0:
+ resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.17:
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+ engines: {node: '>=12.0.0'}
+
+ tinypool@2.1.0:
+ resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==}
+ engines: {node: ^20.0.0 || >=22.0.0}
+
+ tinyrainbow@3.1.1:
+ resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==}
+ engines: {node: '>=14.0.0'}
+
+ ts-api-utils@2.5.0:
+ resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typescript@6.0.3:
+ resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@7.18.2:
+ resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ vite@8.2.2:
+ resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ '@vitejs/devtools': ^0.4.0 || ^0.5.0
+ esbuild: ^0.27.0 || ^0.28.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ '@vitejs/devtools':
+ optional: true
+ esbuild:
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vitest@4.1.11:
+ resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==}
+ engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@opentelemetry/api': ^1.9.0
+ '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
+ '@vitest/browser-playwright': 4.1.11
+ '@vitest/browser-preview': 4.1.11
+ '@vitest/browser-webdriverio': 4.1.11
+ '@vitest/coverage-istanbul': 4.1.11
+ '@vitest/coverage-v8': 4.1.11
+ '@vitest/ui': 4.1.11
+ happy-dom: '*'
+ jsdom: '*'
+ vite: ^6.0.0 || ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@opentelemetry/api':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser-playwright':
+ optional: true
+ '@vitest/browser-preview':
+ optional: true
+ '@vitest/browser-webdriverio':
+ optional: true
+ '@vitest/coverage-istanbul':
+ optional: true
+ '@vitest/coverage-v8':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ vscode-uri@3.2.0:
+ resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==}
+
+ vue-component-type-helpers@3.3.11:
+ resolution: {integrity: sha512-LwcxzeliO9fkQcpJG0PoX8X5kmAhKmH9wkpDLxNabwzkQ9Zeib2YVHwFV4pcWmMLfXVfjr/dSV+DaJ3cIPgSNA==}
+
+ vue-demi@0.14.10:
+ resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
+ engines: {node: '>=12'}
+ hasBin: true
+ peerDependencies:
+ '@vue/composition-api': ^1.0.0-rc.1
+ vue: ^3.0.0-0 || ^2.6.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ vue-eslint-parser@10.4.1:
+ resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+
+ vue-router@4.6.4:
+ resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ vue-tsc@3.3.11:
+ resolution: {integrity: sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=5.0.0'
+
+ vue@3.5.42:
+ resolution: {integrity: sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ whatwg-mimetype@3.0.0:
+ resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
+ engines: {node: '>=12'}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
+ wrap-ansi@9.0.2:
+ resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
+ engines: {node: '>=18'}
+
+ ws@8.21.3:
+ resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yargs-parser@22.0.0:
+ resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=23}
+
+ yargs@18.1.0:
+ resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=23}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+snapshots:
+
+ '@babel/code-frame@7.29.7':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.29.7
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/helper-string-parser@7.29.7': {}
+
+ '@babel/helper-validator-identifier@7.29.7': {}
+
+ '@babel/parser@7.29.8':
+ dependencies:
+ '@babel/types': 7.29.8
+
+ '@babel/types@7.29.8':
+ dependencies:
+ '@babel/helper-string-parser': 7.29.7
+ '@babel/helper-validator-identifier': 7.29.7
+
+ '@commitlint/cli@21.2.2(@types/node@24.13.3)(conventional-commits-parser@7.1.2)(typescript@6.0.3)':
+ dependencies:
+ '@commitlint/config-conventional': 21.2.2
+ '@commitlint/format': 21.2.2
+ '@commitlint/lint': 21.2.2
+ '@commitlint/load': 21.2.2(@types/node@24.13.3)(typescript@6.0.3)
+ '@commitlint/read': 21.2.1(conventional-commits-parser@7.1.2)
+ '@commitlint/types': 21.2.0
+ tinyexec: 1.3.0
+ yargs: 18.1.0
+ transitivePeerDependencies:
+ - '@types/node'
+ - conventional-commits-filter
+ - conventional-commits-parser
+ - typescript
+
+ '@commitlint/config-conventional@21.2.2':
+ dependencies:
+ '@commitlint/types': 21.2.0
+ conventional-changelog-conventionalcommits: 10.4.0
+
+ '@commitlint/config-validator@21.2.0':
+ dependencies:
+ '@commitlint/types': 21.2.0
+ ajv: 8.20.0
+
+ '@commitlint/ensure@21.2.0':
+ dependencies:
+ '@commitlint/types': 21.2.0
+ es-toolkit: 1.51.0
+
+ '@commitlint/execute-rule@21.0.1': {}
+
+ '@commitlint/format@21.2.2':
+ dependencies:
+ '@commitlint/types': 21.2.0
+ picocolors: 1.1.1
+
+ '@commitlint/is-ignored@21.2.2':
+ dependencies:
+ '@commitlint/types': 21.2.0
+ semver: 7.8.5
+
+ '@commitlint/lint@21.2.2':
+ dependencies:
+ '@commitlint/is-ignored': 21.2.2
+ '@commitlint/parse': 21.2.2
+ '@commitlint/rules': 21.2.2
+ '@commitlint/types': 21.2.0
+
+ '@commitlint/load@21.2.2(@types/node@24.13.3)(typescript@6.0.3)':
+ dependencies:
+ '@commitlint/config-validator': 21.2.0
+ '@commitlint/execute-rule': 21.0.1
+ '@commitlint/resolve-extends': 21.2.2
+ '@commitlint/types': 21.2.0
+ cosmiconfig: 9.0.2(typescript@6.0.3)
+ cosmiconfig-typescript-loader: 6.3.0(@types/node@24.13.3)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3)
+ es-toolkit: 1.51.0
+ is-plain-obj: 4.1.0
+ picocolors: 1.1.1
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ '@commitlint/message@21.2.0': {}
+
+ '@commitlint/parse@21.2.2':
+ dependencies:
+ '@commitlint/types': 21.2.0
+ conventional-changelog-angular: 9.4.0
+ conventional-commits-parser: 7.1.2
+
+ '@commitlint/read@21.2.1(conventional-commits-parser@7.1.2)':
+ dependencies:
+ '@commitlint/top-level': 21.2.0
+ '@commitlint/types': 21.2.0
+ '@conventional-changelog/git-client': 3.1.2(conventional-commits-parser@7.1.2)
+ tinyexec: 1.3.0
+ transitivePeerDependencies:
+ - conventional-commits-filter
+ - conventional-commits-parser
+
+ '@commitlint/resolve-extends@21.2.2':
+ dependencies:
+ '@commitlint/config-validator': 21.2.0
+ '@commitlint/types': 21.2.0
+ es-toolkit: 1.51.0
+ global-directory: 5.0.0
+ resolve-from: 5.0.0
+
+ '@commitlint/rules@21.2.2':
+ dependencies:
+ '@commitlint/ensure': 21.2.0
+ '@commitlint/message': 21.2.0
+ '@commitlint/to-lines': 21.0.1
+ '@commitlint/types': 21.2.0
+
+ '@commitlint/to-lines@21.0.1': {}
+
+ '@commitlint/top-level@21.2.0':
+ dependencies:
+ escalade: 3.2.0
+
+ '@commitlint/types@21.2.0':
+ dependencies:
+ conventional-commits-parser: 7.1.2
+ picocolors: 1.1.1
+
+ '@conventional-changelog/git-client@3.1.2(conventional-commits-parser@7.1.2)':
+ dependencies:
+ '@simple-libs/child-process-utils': 2.0.0
+ '@simple-libs/stream-utils': 2.0.0
+ semver: 7.8.5
+ optionalDependencies:
+ conventional-commits-parser: 7.1.2
+
+ '@conventional-changelog/template@1.4.0': {}
+
+ '@eslint-community/eslint-utils@4.10.1(eslint@10.9.1(jiti@2.7.0))':
+ dependencies:
+ eslint: 10.9.1(jiti@2.7.0)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.2': {}
+
+ '@eslint/config-array@0.23.5':
+ dependencies:
+ '@eslint/object-schema': 3.0.5
+ debug: 4.4.3
+ minimatch: 10.2.6
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.7.0':
+ dependencies:
+ '@eslint/core': 1.2.1
+
+ '@eslint/core@1.2.1':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/object-schema@3.0.5': {}
+
+ '@eslint/plugin-kit@0.7.2':
+ dependencies:
+ '@eslint/core': 1.2.1
+ levn: 0.4.1
+
+ '@floating-ui/core@1.8.0':
+ dependencies:
+ '@floating-ui/utils': 0.2.12
+
+ '@floating-ui/dom@1.8.0':
+ dependencies:
+ '@floating-ui/core': 1.8.0
+ '@floating-ui/utils': 0.2.12
+
+ '@floating-ui/utils@0.2.12': {}
+
+ '@floating-ui/vue@1.1.11(vue@3.5.42(typescript@6.0.3))':
+ dependencies:
+ '@floating-ui/dom': 1.8.0
+ '@floating-ui/utils': 0.2.12
+ vue-demi: 0.14.10(vue@3.5.42(typescript@6.0.3))
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+
+ '@humanfs/core@0.19.2':
+ dependencies:
+ '@humanfs/types': 0.15.0
+
+ '@humanfs/node@0.16.8':
+ dependencies:
+ '@humanfs/core': 0.19.2
+ '@humanfs/types': 0.15.0
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanfs/types@0.15.0': {}
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@internationalized/date@3.12.3':
+ dependencies:
+ '@swc/helpers': 0.5.23
+
+ '@internationalized/number@3.6.7':
+ dependencies:
+ '@swc/helpers': 0.5.23
+
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.2.0
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@lucide/vue@1.34.0(vue@3.5.42(typescript@6.0.3))':
+ dependencies:
+ vue: 3.5.42(typescript@6.0.3)
+
+ '@one-ini/wasm@0.1.1': {}
+
+ '@oxc-project/types@0.146.0': {}
+
+ '@oxfmt/binding-android-arm-eabi@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-android-arm64@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-darwin-arm64@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-darwin-x64@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-freebsd-x64@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm-gnueabihf@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm-musleabihf@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm64-gnu@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm64-musl@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-ppc64-gnu@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-riscv64-gnu@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-riscv64-musl@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-s390x-gnu@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-x64-gnu@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-linux-x64-musl@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-openharmony-arm64@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-win32-arm64-msvc@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-win32-ia32-msvc@0.60.0':
+ optional: true
+
+ '@oxfmt/binding-win32-x64-msvc@0.60.0':
+ optional: true
+
+ '@oxlint/binding-android-arm-eabi@1.80.0':
+ optional: true
+
+ '@oxlint/binding-android-arm64@1.80.0':
+ optional: true
+
+ '@oxlint/binding-darwin-arm64@1.80.0':
+ optional: true
+
+ '@oxlint/binding-darwin-x64@1.80.0':
+ optional: true
+
+ '@oxlint/binding-freebsd-x64@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-musleabihf@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-gnu@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-musl@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-ppc64-gnu@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-gnu@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-musl@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-s390x-gnu@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-gnu@1.80.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-musl@1.80.0':
+ optional: true
+
+ '@oxlint/binding-openharmony-arm64@1.80.0':
+ optional: true
+
+ '@oxlint/binding-win32-arm64-msvc@1.80.0':
+ optional: true
+
+ '@oxlint/binding-win32-ia32-msvc@1.80.0':
+ optional: true
+
+ '@oxlint/binding-win32-x64-msvc@1.80.0':
+ optional: true
+
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
+
+ '@rolldown/binding-android-arm-eabi@1.2.5':
+ optional: true
+
+ '@rolldown/binding-android-arm64@1.2.5':
+ optional: true
+
+ '@rolldown/binding-darwin-arm64@1.2.5':
+ optional: true
+
+ '@rolldown/binding-darwin-x64@1.2.5':
+ optional: true
+
+ '@rolldown/binding-freebsd-x64@1.2.5':
+ optional: true
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.2.5':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-gnu@1.2.5':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.2.5':
+ optional: true
+
+ '@rolldown/binding-linux-ppc64-gnu@1.2.5':
+ optional: true
+
+ '@rolldown/binding-linux-s390x-gnu@1.2.5':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.2.5':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.2.5':
+ optional: true
+
+ '@rolldown/binding-openharmony-arm64@1.2.5':
+ optional: true
+
+ '@rolldown/binding-win32-arm64-msvc@1.2.5':
+ optional: true
+
+ '@rolldown/binding-win32-x64-msvc@1.2.5':
+ optional: true
+
+ '@rolldown/pluginutils@1.0.1': {}
+
+ '@simple-libs/child-process-utils@2.0.0':
+ dependencies:
+ '@simple-libs/stream-utils': 2.0.0
+
+ '@simple-libs/stream-utils@2.0.0': {}
+
+ '@standard-schema/spec@1.1.0': {}
+
+ '@swc/helpers@0.5.23':
+ dependencies:
+ tslib: 2.8.1
+
+ '@tailwindcss/node@4.3.3':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.24.5
+ jiti: 2.7.0
+ lightningcss: 1.32.0
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.3.3
+
+ '@tailwindcss/oxide-android-arm64@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.3':
+ optional: true
+
+ '@tailwindcss/oxide@4.3.3':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.3.3
+ '@tailwindcss/oxide-darwin-arm64': 4.3.3
+ '@tailwindcss/oxide-darwin-x64': 4.3.3
+ '@tailwindcss/oxide-freebsd-x64': 4.3.3
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3
+ '@tailwindcss/oxide-linux-arm64-musl': 4.3.3
+ '@tailwindcss/oxide-linux-x64-gnu': 4.3.3
+ '@tailwindcss/oxide-linux-x64-musl': 4.3.3
+ '@tailwindcss/oxide-wasm32-wasi': 4.3.3
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3
+ '@tailwindcss/oxide-win32-x64-msvc': 4.3.3
+
+ '@tailwindcss/vite@4.3.3(vite@8.2.2(@types/node@24.13.3)(jiti@2.7.0))':
+ dependencies:
+ '@tailwindcss/node': 4.3.3
+ '@tailwindcss/oxide': 4.3.3
+ tailwindcss: 4.3.3
+ vite: 8.2.2(@types/node@24.13.3)(jiti@2.7.0)
+
+ '@tanstack/virtual-core@3.17.8': {}
+
+ '@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3))':
+ dependencies:
+ '@tanstack/virtual-core': 3.17.8
+ vue: 3.5.42(typescript@6.0.3)
+
+ '@types/chai@5.2.3':
+ dependencies:
+ '@types/deep-eql': 4.0.2
+ assertion-error: 2.0.1
+
+ '@types/deep-eql@4.0.2': {}
+
+ '@types/esrecurse@4.3.1': {}
+
+ '@types/estree@1.0.9': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/node@24.13.3':
+ dependencies:
+ undici-types: 7.18.2
+
+ '@types/web-bluetooth@0.0.21': {}
+
+ '@types/whatwg-mimetype@3.0.2': {}
+
+ '@types/ws@8.18.1':
+ dependencies:
+ '@types/node': 24.13.3
+
+ '@typescript-eslint/parser@8.68.0(eslint@10.9.1(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.68.0
+ '@typescript-eslint/types': 8.68.0
+ '@typescript-eslint/typescript-estree': 8.68.0(typescript@6.0.3)
+ '@typescript-eslint/visitor-keys': 8.68.0
+ debug: 4.4.3
+ eslint: 10.9.1(jiti@2.7.0)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.68.0(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.68.0(typescript@6.0.3)
+ '@typescript-eslint/types': 8.68.0
+ debug: 4.4.3
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.68.0':
+ dependencies:
+ '@typescript-eslint/types': 8.68.0
+ '@typescript-eslint/visitor-keys': 8.68.0
+
+ '@typescript-eslint/tsconfig-utils@8.68.0(typescript@6.0.3)':
+ dependencies:
+ typescript: 6.0.3
+
+ '@typescript-eslint/types@8.68.0': {}
+
+ '@typescript-eslint/typescript-estree@8.68.0(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.68.0(typescript@6.0.3)
+ '@typescript-eslint/tsconfig-utils': 8.68.0(typescript@6.0.3)
+ '@typescript-eslint/types': 8.68.0
+ '@typescript-eslint/visitor-keys': 8.68.0
+ debug: 4.4.3
+ minimatch: 10.2.6
+ semver: 7.8.5
+ tinyglobby: 0.2.17
+ ts-api-utils: 2.5.0(typescript@6.0.3)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.68.0':
+ dependencies:
+ '@typescript-eslint/types': 8.68.0
+ eslint-visitor-keys: 5.0.1
+
+ '@vitejs/plugin-vue@6.0.8(vite@8.2.2(@types/node@24.13.3)(jiti@2.7.0))(vue@3.5.42(typescript@6.0.3))':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.1
+ vite: 8.2.2(@types/node@24.13.3)(jiti@2.7.0)
+ vue: 3.5.42(typescript@6.0.3)
+
+ '@vitest/expect@4.1.11':
+ dependencies:
+ '@standard-schema/spec': 1.1.0
+ '@types/chai': 5.2.3
+ '@vitest/spy': 4.1.11
+ '@vitest/utils': 4.1.11
+ chai: 6.2.2
+ tinyrainbow: 3.1.1
+
+ '@vitest/mocker@4.1.11(vite@8.2.2(@types/node@24.13.3)(jiti@2.7.0))':
+ dependencies:
+ '@vitest/spy': 4.1.11
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ vite: 8.2.2(@types/node@24.13.3)(jiti@2.7.0)
+
+ '@vitest/pretty-format@4.1.11':
+ dependencies:
+ tinyrainbow: 3.1.1
+
+ '@vitest/runner@4.1.11':
+ dependencies:
+ '@vitest/utils': 4.1.11
+ pathe: 2.0.3
+
+ '@vitest/snapshot@4.1.11':
+ dependencies:
+ '@vitest/pretty-format': 4.1.11
+ '@vitest/utils': 4.1.11
+ magic-string: 0.30.21
+ pathe: 2.0.3
+
+ '@vitest/spy@4.1.11': {}
+
+ '@vitest/utils@4.1.11':
+ dependencies:
+ '@vitest/pretty-format': 4.1.11
+ convert-source-map: 2.0.0
+ tinyrainbow: 3.1.1
+
+ '@volar/language-core@2.4.28':
+ dependencies:
+ '@volar/source-map': 2.4.28
+
+ '@volar/source-map@2.4.28': {}
+
+ '@volar/typescript@2.4.28(typescript@6.0.3)':
+ dependencies:
+ '@volar/language-core': 2.4.28
+ path-browserify: 1.0.1
+ vscode-uri: 3.2.0
+ optionalDependencies:
+ typescript: 6.0.3
+
+ '@vue/compiler-core@3.5.41':
+ dependencies:
+ '@babel/parser': 7.29.8
+ '@vue/shared': 3.5.41
+ entities: 7.0.1
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-core@3.5.42':
+ dependencies:
+ '@babel/parser': 7.29.8
+ '@vue/shared': 3.5.42
+ entities: 7.0.1
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.41':
+ dependencies:
+ '@vue/compiler-core': 3.5.41
+ '@vue/shared': 3.5.41
+
+ '@vue/compiler-dom@3.5.42':
+ dependencies:
+ '@vue/compiler-core': 3.5.42
+ '@vue/shared': 3.5.42
+
+ '@vue/compiler-sfc@3.5.42':
+ dependencies:
+ '@babel/parser': 7.29.8
+ '@vue/compiler-core': 3.5.42
+ '@vue/compiler-dom': 3.5.42
+ '@vue/compiler-ssr': 3.5.42
+ '@vue/shared': 3.5.42
+ estree-walker: 2.0.2
+ magic-string: 0.30.21
+ postcss: 8.5.26
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.42':
+ dependencies:
+ '@vue/compiler-dom': 3.5.42
+ '@vue/shared': 3.5.42
+
+ '@vue/devtools-api@6.6.4': {}
+
+ '@vue/language-core@3.3.11':
+ dependencies:
+ '@volar/language-core': 2.4.28
+ '@vue/compiler-dom': 3.5.41
+ '@vue/shared': 3.5.41
+ alien-signals: 3.2.1
+ muggle-string: 0.4.1
+ path-browserify: 1.0.1
+ picomatch: 4.0.7
+
+ '@vue/reactivity@3.5.42':
+ dependencies:
+ '@vue/shared': 3.5.42
+
+ '@vue/runtime-core@3.5.42':
+ dependencies:
+ '@vue/reactivity': 3.5.42
+ '@vue/shared': 3.5.42
+
+ '@vue/runtime-dom@3.5.42':
+ dependencies:
+ '@vue/reactivity': 3.5.42
+ '@vue/runtime-core': 3.5.42
+ '@vue/shared': 3.5.42
+ csstype: 3.2.3
+
+ '@vue/server-renderer@3.5.42':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.42
+ '@vue/runtime-dom': 3.5.42
+ '@vue/shared': 3.5.42
+
+ '@vue/shared@3.5.41': {}
+
+ '@vue/shared@3.5.42': {}
+
+ '@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.42)(@vue/server-renderer@3.5.42)(vue@3.5.42(typescript@6.0.3))':
+ dependencies:
+ '@vue/compiler-dom': 3.5.42
+ js-beautify: 1.15.4
+ vue: 3.5.42(typescript@6.0.3)
+ vue-component-type-helpers: 3.3.11
+ optionalDependencies:
+ '@vue/server-renderer': 3.5.42
+
+ '@vueuse/core@14.4.0(vue@3.5.42(typescript@6.0.3))':
+ dependencies:
+ '@types/web-bluetooth': 0.0.21
+ '@vueuse/metadata': 14.4.0
+ '@vueuse/shared': 14.4.0(vue@3.5.42(typescript@6.0.3))
+ vue: 3.5.42(typescript@6.0.3)
+
+ '@vueuse/metadata@14.4.0': {}
+
+ '@vueuse/shared@14.4.0(vue@3.5.42(typescript@6.0.3))':
+ dependencies:
+ vue: 3.5.42(typescript@6.0.3)
+
+ abbrev@2.0.0: {}
+
+ acorn-jsx@5.3.2(acorn@8.18.0):
+ dependencies:
+ acorn: 8.18.0
+
+ acorn@8.18.0: {}
+
+ ajv@6.15.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ajv@8.20.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.1.6
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+
+ alien-signals@3.2.1: {}
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.3.0: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@6.2.3: {}
+
+ argparse@2.0.1: {}
+
+ argue-cli@3.1.0: {}
+
+ aria-hidden@1.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ assertion-error@2.0.1: {}
+
+ balanced-match@1.0.2: {}
+
+ balanced-match@4.0.4: {}
+
+ boolbase@1.0.0: {}
+
+ brace-expansion@2.1.4:
+ dependencies:
+ balanced-match: 1.0.2
+
+ brace-expansion@5.0.9:
+ dependencies:
+ balanced-match: 4.0.4
+
+ buffer-image-size@0.6.4:
+ dependencies:
+ '@types/node': 24.13.3
+
+ callsites@3.1.0: {}
+
+ chai@6.2.2: {}
+
+ cliui@9.0.1:
+ dependencies:
+ string-width: 7.2.0
+ strip-ansi: 7.2.0
+ wrap-ansi: 9.0.2
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ commander@10.0.1: {}
+
+ config-chain@1.1.13:
+ dependencies:
+ ini: 1.3.8
+ proto-list: 1.2.4
+
+ conventional-changelog-angular@9.4.0:
+ dependencies:
+ '@conventional-changelog/template': 1.4.0
+
+ conventional-changelog-conventionalcommits@10.4.0:
+ dependencies:
+ '@conventional-changelog/template': 1.4.0
+
+ conventional-commits-parser@7.1.2:
+ dependencies:
+ '@simple-libs/stream-utils': 2.0.0
+ argue-cli: 3.1.0
+
+ convert-source-map@2.0.0: {}
+
+ cosmiconfig-typescript-loader@6.3.0(@types/node@24.13.3)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3):
+ dependencies:
+ '@types/node': 24.13.3
+ cosmiconfig: 9.0.2(typescript@6.0.3)
+ jiti: 2.6.1
+ typescript: 6.0.3
+
+ cosmiconfig@9.0.2(typescript@6.0.3):
+ dependencies:
+ env-paths: 2.2.1
+ import-fresh: 3.3.1
+ js-yaml: 4.3.1
+ parse-json: 5.2.0
+ optionalDependencies:
+ typescript: 6.0.3
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ cssesc@3.0.0: {}
+
+ csstype@3.2.3: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ deep-is@0.1.4: {}
+
+ defu@6.1.7: {}
+
+ detect-libc@2.1.2: {}
+
+ eastasianwidth@0.2.0: {}
+
+ editorconfig@1.0.7:
+ dependencies:
+ '@one-ini/wasm': 0.1.1
+ commander: 10.0.1
+ minimatch: 9.0.9
+ semver: 7.8.5
+
+ emoji-regex@10.6.0: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ enhanced-resolve@5.24.5:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.3
+
+ entities@7.0.1: {}
+
+ env-paths@2.2.1: {}
+
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
+ es-module-lexer@2.3.2: {}
+
+ es-toolkit@1.51.0: {}
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-plugin-vue@10.10.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.9.1(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@10.9.1(jiti@2.7.0))):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0))
+ eslint: 10.9.1(jiti@2.7.0)
+ natural-compare: 1.4.0
+ nth-check: 2.1.1
+ postcss-selector-parser: 7.1.5
+ semver: 7.8.5
+ vue-eslint-parser: 10.4.1(eslint@10.9.1(jiti@2.7.0))
+ xml-name-validator: 5.0.0
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.68.0(eslint@10.9.1(jiti@2.7.0))(typescript@6.0.3)
+
+ eslint-scope@9.1.2:
+ dependencies:
+ '@types/esrecurse': 4.3.1
+ '@types/estree': 1.0.9
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@5.0.1: {}
+
+ eslint@10.9.1(jiti@2.7.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(jiti@2.7.0))
+ '@eslint-community/regexpp': 4.12.2
+ '@eslint/config-array': 0.23.5
+ '@eslint/config-helpers': 0.7.0
+ '@eslint/core': 1.2.1
+ '@eslint/plugin-kit': 0.7.2
+ '@humanfs/node': 0.16.8
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.9
+ ajv: 6.15.0
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 9.1.2
+ eslint-visitor-keys: 5.0.1
+ espree: 11.2.0
+ esquery: 1.7.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ minimatch: 10.2.6
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.7.0
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@11.2.0:
+ dependencies:
+ acorn: 8.18.0
+ acorn-jsx: 5.3.2(acorn@8.18.0)
+ eslint-visitor-keys: 5.0.1
+
+ esquery@1.7.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-walker@2.0.2: {}
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.9
+
+ esutils@2.0.3: {}
+
+ expect-type@1.4.0: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fast-uri@3.1.6: {}
+
+ fdir@6.5.0(picomatch@4.0.7):
+ optionalDependencies:
+ picomatch: 4.0.7
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.4.4
+ keyv: 4.5.4
+
+ flatted@3.4.4: {}
+
+ foreground-child@3.3.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+
+ fsevents@2.3.3:
+ optional: true
+
+ get-caller-file@2.0.5: {}
+
+ get-east-asian-width@1.6.0: {}
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@10.5.0:
+ dependencies:
+ foreground-child: 3.3.1
+ jackspeak: 3.4.3
+ minimatch: 9.0.9
+ minipass: 7.1.3
+ package-json-from-dist: 1.0.1
+ path-scurry: 1.11.1
+
+ global-directory@5.0.0:
+ dependencies:
+ ini: 6.0.0
+
+ graceful-fs@4.2.11: {}
+
+ happy-dom@20.11.6:
+ dependencies:
+ '@types/node': 24.13.3
+ '@types/whatwg-mimetype': 3.0.2
+ '@types/ws': 8.18.1
+ buffer-image-size: 0.6.4
+ entities: 7.0.1
+ whatwg-mimetype: 3.0.0
+ ws: 8.21.3
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ ignore@5.3.2: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ ini@1.3.8: {}
+
+ ini@6.0.0: {}
+
+ is-arrayish@0.2.1: {}
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-plain-obj@4.1.0: {}
+
+ isexe@2.0.0: {}
+
+ jackspeak@3.4.3:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ jiti@2.6.1: {}
+
+ jiti@2.7.0: {}
+
+ js-beautify@1.15.4:
+ dependencies:
+ config-chain: 1.1.13
+ editorconfig: 1.0.7
+ glob: 10.5.0
+ js-cookie: 3.0.8
+ nopt: 7.2.1
+
+ js-cookie@3.0.8: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@4.3.1:
+ dependencies:
+ argparse: 2.0.1
+
+ json-buffer@3.0.1: {}
+
+ json-parse-even-better-errors@2.3.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-schema-traverse@1.0.0: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ lefthook-darwin-arm64@2.1.10:
+ optional: true
+
+ lefthook-darwin-x64@2.1.10:
+ optional: true
+
+ lefthook-freebsd-arm64@2.1.10:
+ optional: true
+
+ lefthook-freebsd-x64@2.1.10:
+ optional: true
+
+ lefthook-linux-arm64@2.1.10:
+ optional: true
+
+ lefthook-linux-x64@2.1.10:
+ optional: true
+
+ lefthook-openbsd-arm64@2.1.10:
+ optional: true
+
+ lefthook-openbsd-x64@2.1.10:
+ optional: true
+
+ lefthook-windows-arm64@2.1.10:
+ optional: true
+
+ lefthook-windows-x64@2.1.10:
+ optional: true
+
+ lefthook@2.1.10:
+ optionalDependencies:
+ lefthook-darwin-arm64: 2.1.10
+ lefthook-darwin-x64: 2.1.10
+ lefthook-freebsd-arm64: 2.1.10
+ lefthook-freebsd-x64: 2.1.10
+ lefthook-linux-arm64: 2.1.10
+ lefthook-linux-x64: 2.1.10
+ lefthook-openbsd-arm64: 2.1.10
+ lefthook-openbsd-x64: 2.1.10
+ lefthook-windows-arm64: 2.1.10
+ lefthook-windows-x64: 2.1.10
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lightningcss-android-arm64@1.32.0:
+ optional: true
+
+ lightningcss-android-arm64@1.33.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.33.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.33.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.32.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.33.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.33.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.33.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.33.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.33.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.33.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.33.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.33.0:
+ optional: true
+
+ lightningcss@1.32.0:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.32.0
+ lightningcss-darwin-arm64: 1.32.0
+ lightningcss-darwin-x64: 1.32.0
+ lightningcss-freebsd-x64: 1.32.0
+ lightningcss-linux-arm-gnueabihf: 1.32.0
+ lightningcss-linux-arm64-gnu: 1.32.0
+ lightningcss-linux-arm64-musl: 1.32.0
+ lightningcss-linux-x64-gnu: 1.32.0
+ lightningcss-linux-x64-musl: 1.32.0
+ lightningcss-win32-arm64-msvc: 1.32.0
+ lightningcss-win32-x64-msvc: 1.32.0
+
+ lightningcss@1.33.0:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.33.0
+ lightningcss-darwin-arm64: 1.33.0
+ lightningcss-darwin-x64: 1.33.0
+ lightningcss-freebsd-x64: 1.33.0
+ lightningcss-linux-arm-gnueabihf: 1.33.0
+ lightningcss-linux-arm64-gnu: 1.33.0
+ lightningcss-linux-arm64-musl: 1.33.0
+ lightningcss-linux-x64-gnu: 1.33.0
+ lightningcss-linux-x64-musl: 1.33.0
+ lightningcss-win32-arm64-msvc: 1.33.0
+ lightningcss-win32-x64-msvc: 1.33.0
+
+ lines-and-columns@1.2.4: {}
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lru-cache@10.4.3: {}
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ minimatch@10.2.6:
+ dependencies:
+ brace-expansion: 5.0.9
+
+ minimatch@9.0.9:
+ dependencies:
+ brace-expansion: 2.1.4
+
+ minipass@7.1.3: {}
+
+ ms@2.1.3: {}
+
+ muggle-string@0.4.1: {}
+
+ nanoid@3.3.18: {}
+
+ natural-compare@1.4.0: {}
+
+ nopt@7.2.1:
+ dependencies:
+ abbrev: 2.0.0
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ obug@2.1.4: {}
+
+ ohash@2.0.12: {}
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ oxfmt@0.60.0:
+ dependencies:
+ tinypool: 2.1.0
+ optionalDependencies:
+ '@oxfmt/binding-android-arm-eabi': 0.60.0
+ '@oxfmt/binding-android-arm64': 0.60.0
+ '@oxfmt/binding-darwin-arm64': 0.60.0
+ '@oxfmt/binding-darwin-x64': 0.60.0
+ '@oxfmt/binding-freebsd-x64': 0.60.0
+ '@oxfmt/binding-linux-arm-gnueabihf': 0.60.0
+ '@oxfmt/binding-linux-arm-musleabihf': 0.60.0
+ '@oxfmt/binding-linux-arm64-gnu': 0.60.0
+ '@oxfmt/binding-linux-arm64-musl': 0.60.0
+ '@oxfmt/binding-linux-ppc64-gnu': 0.60.0
+ '@oxfmt/binding-linux-riscv64-gnu': 0.60.0
+ '@oxfmt/binding-linux-riscv64-musl': 0.60.0
+ '@oxfmt/binding-linux-s390x-gnu': 0.60.0
+ '@oxfmt/binding-linux-x64-gnu': 0.60.0
+ '@oxfmt/binding-linux-x64-musl': 0.60.0
+ '@oxfmt/binding-openharmony-arm64': 0.60.0
+ '@oxfmt/binding-win32-arm64-msvc': 0.60.0
+ '@oxfmt/binding-win32-ia32-msvc': 0.60.0
+ '@oxfmt/binding-win32-x64-msvc': 0.60.0
+
+ oxlint@1.80.0:
+ optionalDependencies:
+ '@oxlint/binding-android-arm-eabi': 1.80.0
+ '@oxlint/binding-android-arm64': 1.80.0
+ '@oxlint/binding-darwin-arm64': 1.80.0
+ '@oxlint/binding-darwin-x64': 1.80.0
+ '@oxlint/binding-freebsd-x64': 1.80.0
+ '@oxlint/binding-linux-arm-gnueabihf': 1.80.0
+ '@oxlint/binding-linux-arm-musleabihf': 1.80.0
+ '@oxlint/binding-linux-arm64-gnu': 1.80.0
+ '@oxlint/binding-linux-arm64-musl': 1.80.0
+ '@oxlint/binding-linux-ppc64-gnu': 1.80.0
+ '@oxlint/binding-linux-riscv64-gnu': 1.80.0
+ '@oxlint/binding-linux-riscv64-musl': 1.80.0
+ '@oxlint/binding-linux-s390x-gnu': 1.80.0
+ '@oxlint/binding-linux-x64-gnu': 1.80.0
+ '@oxlint/binding-linux-x64-musl': 1.80.0
+ '@oxlint/binding-openharmony-arm64': 1.80.0
+ '@oxlint/binding-win32-arm64-msvc': 1.80.0
+ '@oxlint/binding-win32-ia32-msvc': 1.80.0
+ '@oxlint/binding-win32-x64-msvc': 1.80.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ package-json-from-dist@1.0.1: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
+ path-browserify@1.0.1: {}
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ path-scurry@1.11.1:
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.3
+
+ pathe@2.0.3: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.7: {}
+
+ postcss-selector-parser@7.1.5:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss@8.5.26:
+ dependencies:
+ nanoid: 3.3.18
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ proto-list@1.2.4: {}
+
+ punycode@2.3.1: {}
+
+ reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)):
+ dependencies:
+ '@floating-ui/dom': 1.8.0
+ '@floating-ui/vue': 1.1.11(vue@3.5.42(typescript@6.0.3))
+ '@internationalized/date': 3.12.3
+ '@internationalized/number': 3.6.7
+ '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3))
+ '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3))
+ '@vueuse/shared': 14.4.0(vue@3.5.42(typescript@6.0.3))
+ aria-hidden: 1.2.6
+ defu: 6.1.7
+ ohash: 2.0.12
+ vue: 3.5.42(typescript@6.0.3)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+
+ require-from-string@2.0.2: {}
+
+ resolve-from@4.0.0: {}
+
+ resolve-from@5.0.0: {}
+
+ rolldown@1.2.5:
+ dependencies:
+ '@oxc-project/types': 0.146.0
+ '@rolldown/pluginutils': 1.0.1
+ optionalDependencies:
+ '@rolldown/binding-android-arm-eabi': 1.2.5
+ '@rolldown/binding-android-arm64': 1.2.5
+ '@rolldown/binding-darwin-arm64': 1.2.5
+ '@rolldown/binding-darwin-x64': 1.2.5
+ '@rolldown/binding-freebsd-x64': 1.2.5
+ '@rolldown/binding-linux-arm-gnueabihf': 1.2.5
+ '@rolldown/binding-linux-arm64-gnu': 1.2.5
+ '@rolldown/binding-linux-arm64-musl': 1.2.5
+ '@rolldown/binding-linux-ppc64-gnu': 1.2.5
+ '@rolldown/binding-linux-s390x-gnu': 1.2.5
+ '@rolldown/binding-linux-x64-gnu': 1.2.5
+ '@rolldown/binding-linux-x64-musl': 1.2.5
+ '@rolldown/binding-openharmony-arm64': 1.2.5
+ '@rolldown/binding-win32-arm64-msvc': 1.2.5
+ '@rolldown/binding-win32-x64-msvc': 1.2.5
+
+ semver@7.8.5: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ siginfo@2.0.0: {}
+
+ signal-exit@4.1.0: {}
+
+ source-map-js@1.2.1: {}
+
+ stackback@0.0.2: {}
+
+ std-env@4.2.0: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.2.0
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.6.0
+ get-east-asian-width: 1.6.0
+ strip-ansi: 7.2.0
+
+ string-width@8.2.2:
+ dependencies:
+ get-east-asian-width: 1.6.0
+ strip-ansi: 7.2.0
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.2.0:
+ dependencies:
+ ansi-regex: 6.3.0
+
+ tailwindcss@4.3.3: {}
+
+ tapable@2.3.3: {}
+
+ tinybench@2.9.0: {}
+
+ tinyexec@1.3.0: {}
+
+ tinyglobby@0.2.17:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.7)
+ picomatch: 4.0.7
+
+ tinypool@2.1.0: {}
+
+ tinyrainbow@3.1.1: {}
+
+ ts-api-utils@2.5.0(typescript@6.0.3):
+ dependencies:
+ typescript: 6.0.3
+
+ tslib@2.8.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typescript@6.0.3: {}
+
+ undici-types@7.18.2: {}
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ util-deprecate@1.0.2: {}
+
+ vite@8.2.2(@types/node@24.13.3)(jiti@2.7.0):
+ dependencies:
+ lightningcss: 1.33.0
+ picomatch: 4.0.7
+ postcss: 8.5.26
+ rolldown: 1.2.5
+ tinyglobby: 0.2.17
+ optionalDependencies:
+ '@types/node': 24.13.3
+ fsevents: 2.3.3
+ jiti: 2.7.0
+
+ vitest@4.1.11(@types/node@24.13.3)(happy-dom@20.11.6)(vite@8.2.2(@types/node@24.13.3)(jiti@2.7.0)):
+ dependencies:
+ '@vitest/expect': 4.1.11
+ '@vitest/mocker': 4.1.11(vite@8.2.2(@types/node@24.13.3)(jiti@2.7.0))
+ '@vitest/pretty-format': 4.1.11
+ '@vitest/runner': 4.1.11
+ '@vitest/snapshot': 4.1.11
+ '@vitest/spy': 4.1.11
+ '@vitest/utils': 4.1.11
+ es-module-lexer: 2.3.2
+ expect-type: 1.4.0
+ magic-string: 0.30.21
+ obug: 2.1.4
+ pathe: 2.0.3
+ picomatch: 4.0.7
+ std-env: 4.2.0
+ tinybench: 2.9.0
+ tinyexec: 1.3.0
+ tinyglobby: 0.2.17
+ tinyrainbow: 3.1.1
+ vite: 8.2.2(@types/node@24.13.3)(jiti@2.7.0)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 24.13.3
+ happy-dom: 20.11.6
+ transitivePeerDependencies:
+ - msw
+
+ vscode-uri@3.2.0: {}
+
+ vue-component-type-helpers@3.3.11: {}
+
+ vue-demi@0.14.10(vue@3.5.42(typescript@6.0.3)):
+ dependencies:
+ vue: 3.5.42(typescript@6.0.3)
+
+ vue-eslint-parser@10.4.1(eslint@10.9.1(jiti@2.7.0)):
+ dependencies:
+ debug: 4.4.3
+ eslint: 10.9.1(jiti@2.7.0)
+ eslint-scope: 9.1.2
+ eslint-visitor-keys: 5.0.1
+ espree: 11.2.0
+ esquery: 1.7.0
+ semver: 7.8.5
+ transitivePeerDependencies:
+ - supports-color
+
+ vue-router@4.6.4(vue@3.5.42(typescript@6.0.3)):
+ dependencies:
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.42(typescript@6.0.3)
+
+ vue-tsc@3.3.11(typescript@6.0.3):
+ dependencies:
+ '@volar/typescript': 2.4.28(typescript@6.0.3)
+ '@vue/language-core': 3.3.11
+ typescript: 6.0.3
+
+ vue@3.5.42(typescript@6.0.3):
+ dependencies:
+ '@vue/compiler-dom': 3.5.42
+ '@vue/compiler-sfc': 3.5.42
+ '@vue/runtime-dom': 3.5.42
+ '@vue/server-renderer': 3.5.42
+ '@vue/shared': 3.5.42
+ optionalDependencies:
+ typescript: 6.0.3
+
+ whatwg-mimetype@3.0.0: {}
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 5.1.2
+ strip-ansi: 7.2.0
+
+ wrap-ansi@9.0.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 7.2.0
+ strip-ansi: 7.2.0
+
+ ws@8.21.3: {}
+
+ xml-name-validator@5.0.0: {}
+
+ y18n@5.0.8: {}
+
+ yargs-parser@22.0.0: {}
+
+ yargs@18.1.0:
+ dependencies:
+ cliui: 9.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ string-width: 8.2.2
+ y18n: 5.0.8
+ yargs-parser: 22.0.0
+
+ yocto-queue@0.1.0: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..f8ea611
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,15 @@
+allowBuilds:
+ esbuild: true
+ lefthook: true
+ vue-demi: true
+minimumReleaseAgeExclude:
+ - '@vue/compiler-core@3.5.42'
+ - '@vue/compiler-dom@3.5.42'
+ - '@vue/compiler-sfc@3.5.42'
+ - '@vue/compiler-ssr@3.5.42'
+ - '@vue/reactivity@3.5.42'
+ - '@vue/runtime-core@3.5.42'
+ - '@vue/runtime-dom@3.5.42'
+ - '@vue/server-renderer@3.5.42'
+ - '@vue/shared@3.5.42'
+ - vue@3.5.42
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..e2d3269
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
跳到主要内容
+
+
+
+ 短剧制作 / {{ route.meta.title || '工作空间' }}创作工作区 v0.1
+
+
+
+
+
diff --git a/src/components/NotFoundPage.vue b/src/components/NotFoundPage.vue
new file mode 100644
index 0000000..56f3ae8
--- /dev/null
+++ b/src/components/NotFoundPage.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/src/components/ui/AppDialog.vue b/src/components/ui/AppDialog.vue
new file mode 100644
index 0000000..d8e262b
--- /dev/null
+++ b/src/components/ui/AppDialog.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+ {{ title }}{{
+ description
+ }}
+
+
+
+
+
+
+
+
diff --git a/src/components/ui/EmptyState.vue b/src/components/ui/EmptyState.vue
new file mode 100644
index 0000000..1cbd107
--- /dev/null
+++ b/src/components/ui/EmptyState.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
{{ title }}
+
{{ description }}
+
+
+
diff --git a/src/components/ui/StatusBadge.vue b/src/components/ui/StatusBadge.vue
new file mode 100644
index 0000000..9fd0351
--- /dev/null
+++ b/src/components/ui/StatusBadge.vue
@@ -0,0 +1,14 @@
+
+
+
+
+ {{ label || statusLabels[status as ProjectStatus] || status }}
+
+
diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts
new file mode 100644
index 0000000..a25aa2e
--- /dev/null
+++ b/src/components/ui/index.ts
@@ -0,0 +1,4 @@
+/** 公共 UI 的统一导出入口。 */
+export { default as AppDialog } from './AppDialog.vue'
+export { default as EmptyState } from './EmptyState.vue'
+export { default as StatusBadge } from './StatusBadge.vue'
diff --git a/src/composables/usePolling.test.ts b/src/composables/usePolling.test.ts
new file mode 100644
index 0000000..a3a86a2
--- /dev/null
+++ b/src/composables/usePolling.test.ts
@@ -0,0 +1,45 @@
+import { effectScope, nextTick, ref } from 'vue'
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import { usePolling } from './usePolling'
+
+afterEach(() => vi.useRealTimers())
+
+describe('异步轮询生命周期', () => {
+ it('切换项目忽略旧响应,卸载时不再排队请求', async () => {
+ vi.useFakeTimers()
+ const key = ref('first')
+ const finish = new Map void>()
+ const loader = vi.fn<(id: string) => Promise>(
+ (id: string) =>
+ new Promise(resolve => {
+ finish.set(id, resolve)
+ })
+ )
+ const scope = effectScope()
+ const query = scope.run(() => usePolling(key, loader, 1000))!
+ key.value = 'second'
+ await nextTick()
+ finish.get('second')!('新项目')
+ await Promise.resolve()
+ finish.get('first')!('旧项目')
+ await Promise.resolve()
+ expect(query.data.value).toBe('新项目')
+ scope.stop()
+ await vi.advanceTimersByTimeAsync(10_000)
+ expect(loader).toHaveBeenCalledTimes(2)
+ })
+
+ it('失败时保留已有数据,并向页面显示错误', async () => {
+ const scope = effectScope()
+ const loader = vi
+ .fn<() => Promise>()
+ .mockResolvedValueOnce('上次成功数据')
+ .mockRejectedValueOnce(new Error('连接已断开'))
+ const query = scope.run(() => usePolling(ref('p'), loader))!
+ await Promise.resolve()
+ await query.refresh()
+ expect(query.data.value).toBe('上次成功数据')
+ expect(query.error.value).toBe('连接已断开')
+ scope.stop()
+ })
+})
diff --git a/src/composables/usePolling.ts b/src/composables/usePolling.ts
new file mode 100644
index 0000000..c42003f
--- /dev/null
+++ b/src/composables/usePolling.ts
@@ -0,0 +1,69 @@
+import { onScopeDispose, ref, watch, type Ref } from 'vue'
+import { errorMessage } from '../lib/http'
+
+/** 串行轮询:请求完成后才计时;切换项目立即取消,过期响应不会覆盖新项目。 */
+export function usePolling(
+ key: Ref,
+ loader: (key: string, signal: AbortSignal) => Promise,
+ interval = 6000
+) {
+ const data = ref(null) as Ref
+ const loading = ref(false)
+ const error = ref('')
+ const updatedAt = ref('')
+ let generation = 0
+ let controller: AbortController | undefined
+ let timer: ReturnType | undefined
+ let disposed = false
+
+ /** 隐藏页面只安排下一次检查,不继续拉取完整 checkpoint。 */
+ function schedule() {
+ timer = setTimeout(() => {
+ if (document.visibilityState === 'hidden') schedule()
+ else void refresh()
+ }, interval)
+ }
+
+ /** 手动刷新也取消上一次查询,避免同时存在多个轮询链。 */
+ async function refresh() {
+ if (disposed) return
+ const current = ++generation
+ clearTimeout(timer)
+ controller?.abort()
+ controller = new AbortController()
+ const signal = controller.signal
+ loading.value = true
+ try {
+ const result = await loader(key.value, signal)
+ if (current !== generation || disposed) return
+ data.value = result
+ error.value = ''
+ updatedAt.value = new Date().toISOString()
+ } catch (cause) {
+ if (current === generation && !signal.aborted && !disposed) error.value = errorMessage(cause)
+ } finally {
+ if (current === generation && !disposed) {
+ loading.value = false
+ schedule()
+ }
+ }
+ }
+
+ watch(
+ key,
+ () => {
+ data.value = null
+ error.value = ''
+ updatedAt.value = ''
+ void refresh()
+ },
+ { immediate: true }
+ )
+ onScopeDispose(() => {
+ disposed = true
+ generation++
+ clearTimeout(timer)
+ controller?.abort()
+ })
+ return { data, loading, error, updatedAt, refresh }
+}
diff --git a/src/features/breakdown/BreakdownPage.vue b/src/features/breakdown/BreakdownPage.vue
new file mode 100644
index 0000000..f13026a
--- /dev/null
+++ b/src/features/breakdown/BreakdownPage.vue
@@ -0,0 +1,401 @@
+
+
+
+
+
+
+
+
+
+
拆解设置
+
+ 读取已保存的
+ {{ project.episodes.length }} 集剧本。每个分组分别抽取所选模块,再生成主体与分镜。
+
+
+
来源:正式剧集
+
+
+
+
+
+
+
+ 每组集数必须是正整数,并至少选择一个抽取模块。
+
+ {{ previewError }}
+
+
+
+ {{ preview.groupCount }} 个分组 /
+ {{ preview.estimatedTaskCount }} 个抽取任务
+
+
+
+
+
+ {{ String(group.groupNo).padStart(2, '0') }}第 {{ group.startEpisodeNo }}–{{ group.endEpisodeNo }} 集
+
+
+
+
+ 剧本仍在生成,暂不允许启动拆解,避免读取到不完整的剧集。
+
+
+ 后端尚无明确的完成或失败记录。为避免重复执行,已禁用整条重新拆解;如进程已中断,请先核实后台,再选择对应恢复操作。
+
+
+
+
+
+ 最近可用拆解快照
+ {{
+ nodeLabel(records.at(-1)?.metadata?.nodeName)
+ }}
+
+
+
+
+ 抽取完成 {{ summary.completed }} / {{ summary.total }}失败 {{ summary.failed }}主体 {{ subjects.length }}分镜剧集 {{ shots.length }} / {{ plans.length }}
+
+
+
+ {{ execution.errorMessage }}
+
+
+
分镜校验未通过
+
+ -
+ 第 {{ issue.episodeNo }} 集 / Beat {{ issue.beatNo }} / Shot {{ issue.shotNo }}:{{ issue.message }}
+
+
+
+
+
+
从中断处继续
+
+ 抽取失败、镜头缺失、主体绑定异常分别处理。禁用表示近期 checkpoint 不具备所需数据。
+
+
+
+
+
+
+
+
+
+ {{ option.label
+ }}{{
+ subjects.filter(item => item.module === option.value).length
+ }}分镜任务明细
+
+
+
+
+
+
diff --git a/src/features/breakdown/api.ts b/src/features/breakdown/api.ts
new file mode 100644
index 0000000..40b4b8f
--- /dev/null
+++ b/src/features/breakdown/api.ts
@@ -0,0 +1,18 @@
+import { optionalResource, request } from '../../lib/http'
+import type { BreakdownAction, BreakdownInput, BreakdownPreview, BreakdownState } from './types'
+
+/** Breakdown API;预览为 GET,启动和恢复为等待完成的长 POST。 */
+export const breakdownApi = {
+ preview: (id: string, input: BreakdownInput, signal?: AbortSignal) => {
+ const query = new URLSearchParams({ groupSize: String(input.groupSize), modules: input.modules.join(',') })
+ return request(`/projects/${encodeURIComponent(id)}/breakdown-preview?${query}`, { signal })
+ },
+ latest: (id: string, signal?: AbortSignal) =>
+ optionalResource(request(`/projects/${encodeURIComponent(id)}/breakdown/latest`, { signal })),
+ run: (id: string, action: BreakdownAction, input?: BreakdownInput) =>
+ request(`/projects/${encodeURIComponent(id)}/breakdown/${action}`, {
+ method: 'POST',
+ body: input,
+ timeoutMs: 0
+ })
+}
diff --git a/src/features/breakdown/components/StoryboardList.vue b/src/features/breakdown/components/StoryboardList.vue
new file mode 100644
index 0000000..4d8975a
--- /dev/null
+++ b/src/features/breakdown/components/StoryboardList.vue
@@ -0,0 +1,94 @@
+
+
+
+
+
+ {{ plan.beats.length }} 个节拍 ·
+ {{ episode?.beatShots.reduce((sum, beat) => sum + beat.shots.length, 0) ?? 0 }} 个镜头
+
+
+
{{ plan.storyGoal }}
+
{{ plan.emotionalArc }}
+
+
+ 本集已完成节拍规划,镜头尚未生成。请等待进度更新,或在确认中断后使用“补齐缺失镜头”。
+
+
+
+ {{ String(beat.beatNo).padStart(2, '0') }}
+
{{ beat.title }}
+ {{ purposeLabels[beat.purpose] || beat.purpose }}
+
+ {{ beat.description }}
+
+
+
+ 镜头 {{ String(shot.shotNo).padStart(2, '0')
+ }}{{ shot.durationSeconds }}s
+
+
+
{{ shot.title }}
+
{{ shot.description }}
+
视觉重点 · {{ shot.visualFocus }}
+
+ {{ ref }}
+
+
+ {{
+ shot.subjectBindings
+ .map(binding => binding.subjectRef + ' · ' + binding.formName)
+ .join(' / ')
+ }}
+
+
+
+
+
+
+
+
diff --git a/src/features/breakdown/components/SubjectList.vue b/src/features/breakdown/components/SubjectList.vue
new file mode 100644
index 0000000..da2f4ba
--- /dev/null
+++ b/src/features/breakdown/components/SubjectList.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
{{ subjects.length }} 个主体
+
+
+
+
+
+
+
+
{{ subject.name }}
+ {{ subject.ref }}别名:{{ subject.aliases.join('、') }}
+
+ {{ subject.description }}
+
+
+ 外观描述与形态
+ {{
+ forms.filter(form => form.profileId === subject.profileId).length
+ }}
+
+
+ {{ subject.appearance_prompt || '暂无外观描述' }}
+
+
+
+ {{ form.formName || form.name }}默认
+
+
{{ form.description }}
+
{{ form.appearancePrompt }}
+
+
+
+
+
+
+
diff --git a/src/features/breakdown/index.ts b/src/features/breakdown/index.ts
new file mode 100644
index 0000000..cef7959
--- /dev/null
+++ b/src/features/breakdown/index.ts
@@ -0,0 +1,3 @@
+/** Breakdown 模块公共入口。 */
+export { breakdownApi } from './api'
+export type { BreakdownInput, BreakdownState, BreakdownModule } from './types'
diff --git a/src/features/breakdown/types.ts b/src/features/breakdown/types.ts
new file mode 100644
index 0000000..fbbbb1f
--- /dev/null
+++ b/src/features/breakdown/types.ts
@@ -0,0 +1,154 @@
+/** 三种可配置的抽取模块,分镜并不是第四个模块。 */
+export type BreakdownModule = 'character' | 'scene' | 'prop'
+
+/** 拆解分组,来自后端预览接口。 */
+export interface EpisodeGroup {
+ groupId: string
+ groupNo: number
+ startEpisodeNo: number
+ endEpisodeNo: number
+ episodes: { episodeId: string; episodeNo: number; title: string }[]
+}
+
+/** 抽取任务统计,不代表整个 Breakdown 工作流已完成。 */
+export interface TaskSummary {
+ total: number
+ pending: number
+ running: number
+ completed: number
+ failed: number
+ finished: number
+ allFinished: boolean
+ hasFailed: boolean
+}
+
+/** 单个模块 × 分组的抽取任务。 */
+export interface BreakdownTask {
+ taskId: string
+ module: BreakdownModule
+ group: EpisodeGroup
+ status: 'pending' | 'running' | 'completed' | 'failed'
+ attempt: number
+ errorMessage?: string
+}
+
+/** 传给预览与启动接口的相同配置。 */
+export interface BreakdownInput {
+ groupSize: number
+ modules: BreakdownModule[]
+}
+
+/** 拆解启动前的服务器预览结果。 */
+export interface BreakdownPreview {
+ episodeCount: number
+ groupCount: number
+ estimatedTaskCount: number
+ groups: EpisodeGroup[]
+ tasks: BreakdownTask[]
+ modules: BreakdownModule[]
+ groupSize: number
+}
+
+/** 归一化主体;appearance_prompt 保留后端字段名。 */
+export interface SubjectCandidate {
+ profileId: string
+ name: string
+ ref: string
+ description: string
+ module: BreakdownModule
+ appearance_prompt: string
+ aliases?: string[]
+}
+
+/** 同一主体的不同视觉形态。 */
+export interface SubjectForm {
+ formId: string
+ profileId: string
+ type: BreakdownModule
+ name: string
+ formName?: string
+ isDefault: boolean
+ description: string
+ appearancePrompt: string
+}
+
+/** 分镜规划中的叙事节拍。 */
+export interface StoryboardBeat {
+ beatNo: number
+ title: string
+ purpose: string
+ description: string
+ visualFocus: string
+ narrativeGoal: string
+ emotionalTone: string
+ estimatedDurationSeconds: number
+ subjectRefs: string[]
+ isKeyBeat: boolean
+}
+
+/** 单集分镜规划。 */
+export interface EpisodePlan {
+ episodeNo: number
+ episodeTitle: string
+ storyGoal: string
+ centralConflict: string
+ emotionalArc: string
+ pacing: string
+ endingHook: string
+ beats: StoryboardBeat[]
+}
+
+/** 最小连续镜头及其主体形态绑定。 */
+export interface StoryboardShot {
+ shotNo: number
+ title: string
+ description: string
+ visualFocus: string
+ subjectRefs: string[]
+ durationSeconds: number
+ subjectBindings?: { subjectRef: string; profileId: string; formId: string; formName: string }[]
+}
+
+/** 单集按 Beat 组织的镜头结果。 */
+export interface EpisodeShots {
+ episodeNo: number
+ episodePlan: EpisodePlan
+ beatShots: { beatNo: number; shots: StoryboardShot[] }[]
+}
+
+/** 分镜校验报告。 */
+export interface ShotValidation {
+ valid: boolean
+ issues: { episodeNo: number; beatNo?: number | null; shotNo?: number | null; message: string }[]
+}
+
+/** Breakdown 的最终成果及阶段 checkpoint 共有的可展示字段。 */
+export interface BreakdownResult {
+ subjectCandidates?: SubjectCandidate[]
+ subjectForms?: SubjectForm[]
+ storyboardPlans?: EpisodePlan[]
+ storyboardEpisodeShots?: EpisodeShots[]
+ storyboardShotValidation?: ShotValidation
+ storyboardNeedsManualReview?: boolean
+}
+
+/** 工作流执行结果与抽取任务状态独立。 */
+export interface WorkflowExecution {
+ executionId: string
+ status: 'running' | 'completed' | 'failed'
+ errorMessage?: string
+ startedAt: string
+ completedAt?: string
+}
+
+/** latest 和 checkpoint 的恢复状态;失败 checkpoint 可能只有 execution。 */
+export interface BreakdownState extends BreakdownResult {
+ workflowExecution?: WorkflowExecution
+ runConfig?: BreakdownInput & { episodeGroups: EpisodeGroup[] }
+ taskSummary?: TaskSummary
+ tasks?: BreakdownTask[]
+ breakdownResult?: BreakdownResult
+}
+
+/** 不同失败阶段的恢复端点,不能用 retry 替代所有恢复。 */
+export type BreakdownAction = 'start' | 'retry' | 'resume-shots' | 'resume-storyboard'
diff --git a/src/features/create-drama/CreateDramaPage.vue b/src/features/create-drama/CreateDramaPage.vue
new file mode 100644
index 0000000..d4bf811
--- /dev/null
+++ b/src/features/create-drama/CreateDramaPage.vue
@@ -0,0 +1,258 @@
+
+
+
+
+
+
+
+ 已写入 {{ episodes.length }} / {{ total }} 集
+ · 等待计划集数。{{
+ project?.status === 'generating' ? '生成状态来自后端,请等待下一次更新。' : '可按中断阶段继续处理。'
+ }}
+
+
+
+
+
+
+
+
+
剧集正文 {{ episodes.length }}角色设定世界观审核记录
+
+
+
+
+
+
+
+
+ 第 {{ String(episode.episode).padStart(2, '0') }} 集
+ {{ episode.title }}
+ {{ episode.summary }}
+ {{ episode.content }}
+
+ - 核心冲突
+ - {{ episode.conflict }}
- 结尾钩子
+ - {{ episode.hook }}
+
+
+
+
+
+
+
+
+
{{ character.name }}
+ {{ character.role || '角色' }}{{ character.occupation }}
+
+
+ - {{ field.label }}
+ - {{ character[field.key] || '未提供' }}
+
+
+
+
+
+
故事的发生之地
+
+ {{ project.world.era }} · {{ project.world.location }}
+
+
+ - {{ field.label }}
+ - {{ project.world[field.key] || '未提供' }}
+
+
+ 世界规则
+ {{ JSON.stringify(project.world.rules, null, 2) }}
+
+
+
+
+
+
+ {{ review.passed ? '审核通过' : '需要修改' }}
+
+
+ {{ review.message || '本次审核未附加说明。' }}
+
+
+
+
+
+ 查看改写建议原文
+ {{ JSON.stringify(state.rewriteSuggestion, null, 2) }}
+
+
+
+
+
+
+
diff --git a/src/features/create-drama/index.ts b/src/features/create-drama/index.ts
new file mode 100644
index 0000000..ca414b4
--- /dev/null
+++ b/src/features/create-drama/index.ts
@@ -0,0 +1,2 @@
+/** Create Drama 页面入口,路由通过动态导入进行分包。 */
+export { default as CreateDramaPage } from './CreateDramaPage.vue'
diff --git a/src/features/projects/ProjectLayout.vue b/src/features/projects/ProjectLayout.vue
new file mode 100644
index 0000000..dbd301d
--- /dev/null
+++ b/src/features/projects/ProjectLayout.vue
@@ -0,0 +1,64 @@
+
+
+
+
+ 全部剧本
+
+
+
项目工作台
+
+ {{ context.project.value?.title || context.project.value?.topic || '读取项目' }}
+
+
{{ context.project.value?.style || '剧本与拆解结果' }}
+
+
+
+
+
+
+
+ {{ context.error.value }} 当前保留上次成功读取的数据。
+
+
+ {{
+ operation.label
+ }}。可切换页面查看结果,请勿重复提交或关闭浏览器;关闭页面不会取消后端任务。
+
+ {{ operation.error }}
+ {{ operation.notice }}
+
+
+ 正在读取项目和工作流记录……
+
+
+
diff --git a/src/features/projects/ProjectsPage.vue b/src/features/projects/ProjectsPage.vue
new file mode 100644
index 0000000..0754225
--- /dev/null
+++ b/src/features/projects/ProjectsPage.vue
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
工作空间 / 项目
+
我的剧本
+
从故事到镜头,在这里继续你的创作。
+
+
+
+
+ 剧本创作 / 主体拆解
+ / 分镜规划两个工作流,一个项目
+
+
+
+ {{ query.error.value }}
+
+
+
+ 正在读取项目……
+
+
+
+
+
+
+ 项目数据来自后端数据库
+ · 更新于 {{ formatDate(query.updatedAt.value) }}
+
+
+
diff --git a/src/features/projects/api.ts b/src/features/projects/api.ts
new file mode 100644
index 0000000..47485a0
--- /dev/null
+++ b/src/features/projects/api.ts
@@ -0,0 +1,18 @@
+import { request, optionalResource } from '../../lib/http'
+import type { Checkpoint } from '../workflows/types'
+import type { CreateProjectInput, DramaState, Project, ProjectDetail } from './types'
+
+/** 项目与 Create Drama API;路径严格对应后端 dev。 */
+export const projectsApi = {
+ list: (signal?: AbortSignal) => request('/projects', { signal }),
+ detail: (id: string, signal?: AbortSignal) =>
+ request(`/projects/${encodeURIComponent(id)}`, { signal }),
+ create: (input: CreateProjectInput) =>
+ request<{ projectId: string; status: string }>('/projects', { method: 'POST', body: input }),
+ state: (id: string, signal?: AbortSignal) =>
+ optionalResource(request(`/projects/${encodeURIComponent(id)}/state`, { signal })),
+ checkpoints: (id: string, signal?: AbortSignal) =>
+ request(`/projects/${encodeURIComponent(id)}/checkpoints`, { signal }),
+ resume: (id: string, action: 'resume-generation' | 'resume-rewrite') =>
+ request(`/projects/${encodeURIComponent(id)}/${action}`, { method: 'POST', timeoutMs: 0 })
+}
diff --git a/src/features/projects/components/CreateProjectDialog.vue b/src/features/projects/components/CreateProjectDialog.vue
new file mode 100644
index 0000000..c0129f3
--- /dev/null
+++ b/src/features/projects/components/CreateProjectDialog.vue
@@ -0,0 +1,105 @@
+
+
+
+
+ 新建剧本
+
+
+
diff --git a/src/features/projects/context.ts b/src/features/projects/context.ts
new file mode 100644
index 0000000..a9f2c0f
--- /dev/null
+++ b/src/features/projects/context.ts
@@ -0,0 +1,30 @@
+import { computed, inject, type InjectionKey } from 'vue'
+import { usePolling } from '../../composables/usePolling'
+import { projectsApi } from './api'
+import type { ProjectDetail } from './types'
+import type { Checkpoint } from '../workflows/types'
+import type { Ref } from 'vue'
+
+/** 同一项目的两个 graph 共用一份查询,避免每个面板重复请求。 */
+export function useProjectData(id: Ref) {
+ const query = usePolling(id, async (projectId, signal) => {
+ const [project, checkpoints] = await Promise.all([
+ projectsApi.detail(projectId, signal),
+ projectsApi.checkpoints(projectId, signal)
+ ])
+ return { project, checkpoints }
+ })
+ const project = computed(() => query.data.value?.project ?? null)
+ const checkpoints = computed(() => query.data.value?.checkpoints ?? [])
+ return { ...query, project, checkpoints }
+}
+
+/** 项目布局向子页面提供的类型安全上下文。 */
+export const projectContextKey: InjectionKey> = Symbol('project-context')
+
+/** 读取项目上下文,错误布局在开发阶段立即暴露。 */
+export function useProjectContext() {
+ const context = inject(projectContextKey)
+ if (!context) throw new Error('项目页面必须位于 ProjectLayout 中')
+ return context
+}
diff --git a/src/features/projects/index.ts b/src/features/projects/index.ts
new file mode 100644
index 0000000..f75892c
--- /dev/null
+++ b/src/features/projects/index.ts
@@ -0,0 +1,3 @@
+/** 项目模块公共入口。 */
+export { projectsApi } from './api'
+export type { Project, ProjectDetail, CreateProjectInput } from './types'
diff --git a/src/features/projects/types.ts b/src/features/projects/types.ts
new file mode 100644
index 0000000..8106dfa
--- /dev/null
+++ b/src/features/projects/types.ts
@@ -0,0 +1,80 @@
+/** 后端 DramaProject.status 的原始取值。 */
+export type ProjectStatus = 'draft' | 'generating' | 'completed' | 'need_review' | 'failed'
+
+/** 创建请求不包含标题,标题由后端在工作流收尾时更新。 */
+export interface CreateProjectInput {
+ topic: string
+ style: string
+ episodeCount: number
+}
+
+/** 项目列表记录;列表接口不包含剧集数量,不能据此推断生成进度。 */
+export interface Project {
+ id: string
+ title: string | null
+ topic: string
+ style: string | null
+ status: ProjectStatus
+ createdAt: string
+ updatedAt: string
+}
+
+/** 正式数据库剧集;episode 是编号,与 Breakdown 的 episodeNo 区分。 */
+export interface Episode {
+ id?: string
+ episode: number
+ title: string
+ summary?: string | null
+ content: string
+ conflict?: string | null
+ hook?: string | null
+}
+
+/** 剧本阶段的角色设定,不等同于拆解后的主体资产。 */
+export interface Character {
+ id: string
+ name: string
+ role?: string | null
+ age?: number | null
+ occupation?: string | null
+ personality?: string | null
+ goal?: string | null
+ secret?: string | null
+}
+
+/** 正式数据库保存的世界观。 */
+export interface World {
+ background?: string | null
+ era?: string | null
+ location?: string | null
+ coreConflict?: string | null
+ tone?: string | null
+ rules?: unknown
+}
+
+/** 后端审核记录,列表按最新在前返回。 */
+export interface Review {
+ id: string
+ passed: boolean
+ message?: string | null
+ createdAt: string
+}
+
+/** 项目详情接口包含的关联数据。 */
+export interface ProjectDetail extends Project {
+ episodes: Episode[]
+ characters: Character[]
+ world: World | null
+ reviews: Review[]
+ tasks: { id: string; type: string; status: string; error?: string | null }[]
+}
+
+/** Create Drama checkpoint 中页面使用的状态切片。 */
+export interface DramaState {
+ episodeCount?: number
+ episodes?: Episode[]
+ retryCount?: number
+ reviewPassed?: boolean
+ rewriteSuggestion?: unknown
+ rewritePlan?: unknown
+}
diff --git a/src/features/workflows/ConfirmAction.vue b/src/features/workflows/ConfirmAction.vue
new file mode 100644
index 0000000..67fa28e
--- /dev/null
+++ b/src/features/workflows/ConfirmAction.vue
@@ -0,0 +1,58 @@
+
+
+
+
+ {{ label }}
+
+ 此操作会提交到真实后端,可能调用模型并产生费用。请勿在其他页面或终端同时启动相同工作流。
+
+
+
+
+
diff --git a/src/features/workflows/HistoryPanel.vue b/src/features/workflows/HistoryPanel.vue
new file mode 100644
index 0000000..eeb1804
--- /dev/null
+++ b/src/features/workflows/HistoryPanel.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
diff --git a/src/features/workflows/index.ts b/src/features/workflows/index.ts
new file mode 100644
index 0000000..4a12cd8
--- /dev/null
+++ b/src/features/workflows/index.ts
@@ -0,0 +1,4 @@
+/** 跨 graph 的观测与操作接口。 */
+export { getOperation, runOperation } from './operations'
+export { workflowCheckpoints, breakdownSnapshot, recoveryOptions } from './selectors'
+export type { Checkpoint } from './types'
diff --git a/src/features/workflows/operations.test.ts b/src/features/workflows/operations.test.ts
new file mode 100644
index 0000000..2bd0c58
--- /dev/null
+++ b/src/features/workflows/operations.test.ts
@@ -0,0 +1,20 @@
+import { describe, expect, it, vi } from 'vitest'
+import { getOperation, runOperation } from './operations'
+
+describe('项目级长请求互斥', () => {
+ it('同一项目不同时执行两个 graph,错误不伪装成功', async () => {
+ let fail!: (reason: Error) => void
+ const action = vi.fn<() => Promise>(
+ () =>
+ new Promise((_resolve, reject) => {
+ fail = reject
+ })
+ )
+ const first = runOperation('operation-test', '拆解', action)
+ expect(await runOperation('operation-test', '改写', action)).toBe(false)
+ expect(action).toHaveBeenCalledTimes(1)
+ fail(new Error('连接中断'))
+ expect(await first).toBe(false)
+ expect(getOperation('operation-test')).toMatchObject({ pending: false, error: '连接中断', notice: '' })
+ })
+})
diff --git a/src/features/workflows/operations.ts b/src/features/workflows/operations.ts
new file mode 100644
index 0000000..faa119b
--- /dev/null
+++ b/src/features/workflows/operations.ts
@@ -0,0 +1,35 @@
+import { reactive } from 'vue'
+import { errorMessage } from '../../lib/http'
+
+/** 当前浏览器会话中的操作状态;切换路由不会丢失长请求。 */
+export interface Operation {
+ pending: boolean
+ label: string
+ error: string
+ notice: string
+}
+
+/** 按项目而非 graph 上锁,防止改写剧本与拆解同时提交。 */
+const operations = reactive>({})
+
+/** 取得项目操作状态,不向浏览器持久化虚假的后台运行状态。 */
+export function getOperation(projectId: string): Operation {
+ return (operations[projectId] ??= { pending: false, label: '', error: '', notice: '' })
+}
+
+/** 只提交一次;断网不自动重试,后台是否已执行由刷新后的 checkpoint 确认。 */
+export async function runOperation(projectId: string, label: string, action: () => Promise) {
+ const operation = getOperation(projectId)
+ if (operation.pending) return false
+ Object.assign(operation, { pending: true, label, error: '', notice: '' })
+ try {
+ await action()
+ operation.notice = '请求已返回,正在重新读取后端状态;最终结果以下方记录为准。'
+ return true
+ } catch (error) {
+ operation.error = errorMessage(error)
+ return false
+ } finally {
+ operation.pending = false
+ }
+}
diff --git a/src/features/workflows/pages.test.ts b/src/features/workflows/pages.test.ts
new file mode 100644
index 0000000..c461ff1
--- /dev/null
+++ b/src/features/workflows/pages.test.ts
@@ -0,0 +1,142 @@
+import { computed, ref } from 'vue'
+import { createMemoryHistory, createRouter } from 'vue-router'
+import { flushPromises, mount, type VueWrapper } from '@vue/test-utils'
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import CreateProjectDialog from '../projects/components/CreateProjectDialog.vue'
+import BreakdownPage from '../breakdown/BreakdownPage.vue'
+import CreateDramaPage from '../create-drama/CreateDramaPage.vue'
+import ProjectsPage from '../projects/ProjectsPage.vue'
+import { projectContextKey, type useProjectData } from '../projects/context'
+import type { ProjectDetail } from '../projects/types'
+import type { Checkpoint } from './types'
+
+/** 模拟 API 响应仅用于测试,不会打包进生产页面。 */
+const fixture: ProjectDetail = {
+ id: 'page-test-project',
+ title: '雨夜来信',
+ topic: '一封信改变了两个人的命运',
+ style: '都市悬疑',
+ status: 'completed',
+ createdAt: '2026-08-27T00:00:00Z',
+ updatedAt: '2026-08-27T00:00:00Z',
+ episodes: [{ episode: 1, title: '来信', content: '\n第一场:旧书店。' }],
+ characters: [{ id: 'character', name: '林知夏' }],
+ world: { era: '当代' },
+ reviews: [],
+ tasks: []
+}
+let wrapper: VueWrapper | undefined
+
+/** 使用真实上下文形状,不绕过页面内的异步操作与按钮守卫。 */
+function context(): ReturnType {
+ const data = ref({ project: fixture, checkpoints: [] as Checkpoint[] })
+ return {
+ data,
+ project: computed(() => data.value.project),
+ checkpoints: computed(() => data.value.checkpoints),
+ loading: ref(false),
+ error: ref(''),
+ updatedAt: ref(''),
+ refresh: vi.fn<() => Promise>().mockResolvedValue()
+ }
+}
+
+/** 找到挂载到 body 的 Reka 弹窗按钮。 */
+function button(label: string): HTMLButtonElement {
+ const element = [...document.querySelectorAll('button')].find(item => item.textContent?.trim() === label)
+ if (!element) throw new Error('找不到按钮:' + label)
+ return element
+}
+
+afterEach(() => {
+ wrapper?.unmount()
+ wrapper = undefined
+ document.body.innerHTML = ''
+ vi.unstubAllGlobals()
+})
+
+describe('工作台页面交互', () => {
+ it('项目筛选无结果时可清除条件并返回真实列表', async () => {
+ vi.stubGlobal(
+ 'fetch',
+ vi.fn().mockImplementation(async () => new Response(JSON.stringify({ data: [fixture] })))
+ )
+ const router = createRouter({
+ history: createMemoryHistory(),
+ routes: [{ path: '/', component: ProjectsPage }]
+ })
+ await router.push('/')
+ wrapper = mount(ProjectsPage, { attachTo: document.body, global: { plugins: [router] } })
+ await flushPromises()
+ expect(wrapper.text()).toContain('雨夜来信')
+ await wrapper.get('input[aria-label="搜索项目"]').setValue('不存在的关键词')
+ expect(wrapper.text()).toContain('没有匹配的剧本')
+ button('清除筛选').click()
+ await flushPromises()
+ expect(wrapper.findAll('tbody tr')).toHaveLength(1)
+ })
+
+ it('新建弹窗提交真实参数并返回 202 的项目 ID', async () => {
+ const fetcher = vi
+ .fn()
+ .mockResolvedValue(new Response('{"projectId":"created","status":"generating"}', { status: 202 }))
+ vi.stubGlobal('fetch', fetcher)
+ wrapper = mount(CreateProjectDialog, { attachTo: document.body })
+ button('新建剧本').click()
+ await flushPromises()
+ const topic = document.querySelector('#topic')!
+ topic.value = ' 雨夜来信 '
+ topic.dispatchEvent(new Event('input', { bubbles: true }))
+ const count = document.querySelector('#episode-count')!
+ count.value = '6'
+ count.dispatchEvent(new Event('input', { bubbles: true }))
+ document.querySelector('form')!.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }))
+ await flushPromises()
+ expect(wrapper.emitted('created')).toEqual([['created']])
+ expect(JSON.parse(fetcher.mock.calls[0]![1]!.body as string)).toEqual({
+ topic: '雨夜来信',
+ style: '爽文反转',
+ episodeCount: 6
+ })
+ })
+
+ it('修改每组集数会废弃预览,重新预览并确认后才能启动', async () => {
+ const fetcher = vi.fn().mockImplementation(async url => {
+ if (String(url).includes('breakdown-preview'))
+ return new Response(
+ '{"data":{"episodeCount":1,"groupCount":1,"estimatedTaskCount":3,"groups":[],"tasks":[],"modules":["character","scene","prop"],"groupSize":2}}'
+ )
+ return new Response('{"data":{"workflowExecution":{"status":"completed"}}}')
+ })
+ vi.stubGlobal('fetch', fetcher)
+ const provided = context()
+ wrapper = mount(BreakdownPage, {
+ attachTo: document.body,
+ global: { provide: { [projectContextKey as symbol]: provided } }
+ })
+ button('预览分组').click()
+ await flushPromises()
+ expect(button('开始拆解').disabled).toBe(false)
+ await wrapper.get('#group-size').setValue('2')
+ expect(document.body.textContent).not.toContain('开始拆解')
+ button('预览分组').click()
+ await flushPromises()
+ button('开始拆解').click()
+ await flushPromises()
+ button('确认开始拆解').click()
+ await flushPromises()
+ const post = fetcher.mock.calls.find(call => call[1]?.method === 'POST')
+ expect(post?.[0]).toBe('/api/projects/page-test-project/breakdown/start')
+ expect(JSON.parse(post![1]!.body as string)).toEqual({ groupSize: 2, modules: ['character', 'scene', 'prop'] })
+ expect(provided.refresh).toHaveBeenCalledOnce()
+ })
+
+ it('剧本文本按纯文本显示,不执行模型输出的 HTML', () => {
+ wrapper = mount(CreateDramaPage, {
+ attachTo: document.body,
+ global: { provide: { [projectContextKey as symbol]: context() }, stubs: { RouterLink: true } }
+ })
+ expect(wrapper.find('.script-body').text()).toContain('')
+ expect(wrapper.find('.script-body script').exists()).toBe(false)
+ })
+})
diff --git a/src/features/workflows/selectors.test.ts b/src/features/workflows/selectors.test.ts
new file mode 100644
index 0000000..63e5642
--- /dev/null
+++ b/src/features/workflows/selectors.test.ts
@@ -0,0 +1,71 @@
+import { describe, expect, it } from 'vitest'
+import { breakdownSnapshot, recoveryOptions, workflowCheckpoints } from './selectors'
+import type { Checkpoint } from './types'
+import type { BreakdownState } from '../breakdown/types'
+
+/** 只构造测试所需的真实 checkpoint 字段,避免页面依赖虚构 DTO。 */
+function checkpoint(index: number, state: BreakdownState, workflowName = 'breakdown'): Checkpoint {
+ return { checkpointId: String(index), workflowName, createdAt: new Date(index * 1000).toISOString(), state }
+}
+
+describe('Checkpoint 选择与恢复', () => {
+ it('按 graph 隔离并保持输入不可变', () => {
+ const records = [checkpoint(2, {}), checkpoint(1, {}, 'create-drama')]
+ expect(workflowCheckpoints(records, 'breakdown').map(item => item.checkpointId)).toEqual(['2'])
+ expect(records[0]?.checkpointId).toBe('2')
+ })
+
+ it('只有错误的最新 checkpoint 仍能展示上一份成果,同时保留最新失败状态', () => {
+ const result = breakdownSnapshot([
+ checkpoint(1, {
+ breakdownResult: { subjectCandidates: [] },
+ runConfig: { groupSize: 3, modules: ['character'], episodeGroups: [] }
+ }),
+ checkpoint(2, {
+ workflowExecution: {
+ executionId: 'e',
+ status: 'failed',
+ startedAt: '',
+ errorMessage: '模型未返回 JSON'
+ }
+ })
+ ])
+ expect(result?.runConfig?.groupSize).toBe(3)
+ expect(result?.workflowExecution?.status).toBe('failed')
+ })
+
+ it('新阶段不能继承上一轮 completed 状态', () => {
+ const result = breakdownSnapshot([
+ checkpoint(1, {
+ workflowExecution: { executionId: 'e', status: 'completed', startedAt: '' },
+ breakdownResult: {}
+ }),
+ checkpoint(2, { runConfig: { groupSize: 2, modules: ['scene'], episodeGroups: [] } })
+ ])
+ expect(result?.workflowExecution).toBeUndefined()
+ expect(result?.runConfig?.groupSize).toBe(2)
+ })
+
+ it('恢复窗口和后端一致,过旧的失败任务不启用重试', () => {
+ const records = Array.from({ length: 11 }, (_, index) =>
+ checkpoint(
+ index,
+ index === 0
+ ? {
+ tasks: [
+ {
+ taskId: 't',
+ module: 'prop',
+ status: 'failed',
+ attempt: 1,
+ group: { groupId: 'g', groupNo: 1, startEpisodeNo: 1, endEpisodeNo: 1, episodes: [] }
+ }
+ ]
+ }
+ : {}
+ )
+ )
+ expect(recoveryOptions(records).retry).toBe(false)
+ expect(recoveryOptions(records.slice(0, 10)).retry).toBe(true)
+ })
+})
diff --git a/src/features/workflows/selectors.ts b/src/features/workflows/selectors.ts
new file mode 100644
index 0000000..88b7c33
--- /dev/null
+++ b/src/features/workflows/selectors.ts
@@ -0,0 +1,45 @@
+import type { BreakdownState } from '../breakdown/types'
+import type { Checkpoint } from './types'
+
+/** 按工作流隔离并排序,后端数组顺序变化不会影响恢复判断。 */
+export function workflowCheckpoints(checkpoints: Checkpoint[], name: string): Checkpoint[] {
+ return checkpoints
+ .filter(item => item.workflowName === name)
+ .toSorted((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt))
+}
+
+/** 失败快照可能只有错误;回看最近有效状态,同时只使用最新记录的执行状态。 */
+export function breakdownSnapshot(checkpoints: Checkpoint[]): BreakdownState | null {
+ const records = workflowCheckpoints(checkpoints, 'breakdown')
+ const latest = records.at(-1)
+ if (!latest) return null
+ const data =
+ records
+ .slice(-30)
+ .toReversed()
+ .find(item => item.state.runConfig || item.state.breakdownResult || item.state.storyboardPlans?.length)
+ ?.state ?? {}
+ return { ...data, workflowExecution: latest.state.workflowExecution }
+}
+
+/** 根据最近后端可恢复窗口推导操作;页面仍需先确认任务已经结束。 */
+export function recoveryOptions(checkpoints: Checkpoint[]) {
+ const records = workflowCheckpoints(checkpoints, 'breakdown').toReversed()
+ const retry = records.slice(0, 10).some(item => item.state.tasks?.some(task => task.status === 'failed'))
+ const shots = records
+ .slice(0, 30)
+ .some(({ state }) =>
+ Boolean(
+ state.storyboardPlans?.length &&
+ state.subjectCandidates?.length &&
+ state.subjectForms &&
+ (state.storyboardEpisodeShots?.length ?? 0) < state.storyboardPlans.length
+ )
+ )
+ const storyboard = records
+ .slice(0, 10)
+ .some(({ state }) =>
+ Boolean(state.storyboardEpisodeShots?.length && state.subjectCandidates?.length && state.subjectForms)
+ )
+ return { retry, shots, storyboard }
+}
diff --git a/src/features/workflows/types.ts b/src/features/workflows/types.ts
new file mode 100644
index 0000000..cfa1206
--- /dev/null
+++ b/src/features/workflows/types.ts
@@ -0,0 +1,11 @@
+import type { DramaState } from '../projects/types'
+import type { BreakdownState } from '../breakdown/types'
+
+/** 观测接口返回的 checkpoint,保留工作流归属以防混淆两条 graph。 */
+export interface Checkpoint {
+ checkpointId: string
+ workflowName: string
+ createdAt: string
+ metadata?: { nodeName?: string; nodeDurationMs?: number; [key: string]: unknown } | null
+ state: DramaState & BreakdownState
+}
diff --git a/src/lib/format.ts b/src/lib/format.ts
new file mode 100644
index 0000000..a5ec7a5
--- /dev/null
+++ b/src/lib/format.ts
@@ -0,0 +1,72 @@
+import type { ProjectStatus } from '../features/projects/types'
+
+/** 原始项目状态的中文名称。 */
+export const statusLabels: Record = {
+ draft: '草稿',
+ generating: '生成中',
+ completed: '已完成',
+ need_review: '待审核',
+ failed: '生成失败'
+}
+
+/** 日期统一使用用户设备所在时区。 */
+export function formatDate(value?: string): string {
+ if (!value) return '—'
+ const date = new Date(value)
+ return Number.isNaN(date.getTime())
+ ? '—'
+ : new Intl.DateTimeFormat('zh-CN', {
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: false
+ }).format(date)
+}
+
+/** 节点中文名称;未知新节点仍显示原名,方便后端扩展。 */
+export function nodeLabel(name?: string): string {
+ const labels: Record = {
+ createProject: '创建项目',
+ generateCharacter: '角色设定',
+ generateWorld: '世界观',
+ generateEpisode: '编写剧集',
+ reviewDrama: '审核剧本',
+ generateRewriteSuggestion: '改写建议',
+ rewrite: '改写剧本',
+ finishProject: '项目收尾',
+ resumeRewrite: '恢复改写',
+ prepare: '准备分组',
+ prepareRetry: '准备失败重试',
+ summarizeTasks: '汇总抽取任务',
+ extractTask: '执行抽取',
+ mergeMentions: '合并提及',
+ buildProfiles: '整合主体档案',
+ buildSubjects: '构建主体',
+ generateStoryboardPlan: '规划剧情节拍',
+ generateStoryboardShots: '生成分镜',
+ storyboard_shots_progress: '镜头批次已保存',
+ storyboard_ready: '分镜校验完成,准备入库',
+ extract: '模块抽取',
+ merge_mentions: '合并提及',
+ build_profiles: '整合主体档案',
+ build_subjects: '构建主体',
+ generate_storyboard_plan: '规划剧情节拍',
+ generate_storyboard_shots: '生成分镜',
+ completed: '工作流完成',
+ failed: '工作流失败',
+ partial_failed: '部分抽取失败',
+ finalize: '结果入库'
+ }
+ return name ? (labels[name] ?? name) : '状态记录'
+}
+
+/** 下载纯文本或 JSON,不将模型内容作为 HTML 渲染。 */
+export function downloadText(filename: string, content: string, type = 'text/plain;charset=utf-8') {
+ const url = URL.createObjectURL(new Blob([content], { type }))
+ const link = document.createElement('a')
+ link.href = url
+ link.download = filename.replace(/[<>:"/\\|?*]/g, '_')
+ link.click()
+ setTimeout(() => URL.revokeObjectURL(url), 1000)
+}
diff --git a/src/lib/http.test.ts b/src/lib/http.test.ts
new file mode 100644
index 0000000..49a1d66
--- /dev/null
+++ b/src/lib/http.test.ts
@@ -0,0 +1,86 @@
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import { ApiError, optionalResource, request } from './http'
+import { projectsApi } from '../features/projects/api'
+import { breakdownApi } from '../features/breakdown/api'
+
+afterEach(() => {
+ vi.unstubAllGlobals()
+ vi.useRealTimers()
+})
+
+describe('后端 API 契约', () => {
+ it('解包 data,同时保留创建项目的顶层 202 结构', async () => {
+ const fetcher = vi
+ .fn<(url: string, options: RequestInit) => Promise>()
+ .mockResolvedValueOnce(new Response(JSON.stringify({ data: [{ id: 'p1' }] })))
+ .mockResolvedValueOnce(
+ new Response(JSON.stringify({ projectId: 'p2', status: 'generating' }), { status: 202 })
+ )
+ vi.stubGlobal('fetch', fetcher)
+ expect(await projectsApi.list()).toEqual([{ id: 'p1' }])
+ expect(await projectsApi.create({ topic: '故事', style: '悬疑', episodeCount: 3 })).toEqual({
+ projectId: 'p2',
+ status: 'generating'
+ })
+ expect(JSON.parse(fetcher.mock.calls[1]![1].body as string)).toEqual({
+ topic: '故事',
+ style: '悬疑',
+ episodeCount: 3
+ })
+ })
+
+ it('只将缺少 checkpoint 的 404 视为可选结果,保留服务器错误', async () => {
+ expect(await optionalResource(Promise.reject(new ApiError('没有 checkpoint', 404)))).toBeNull()
+ await expect(optionalResource(Promise.reject(new ApiError('数据库异常', 500)))).rejects.toMatchObject({
+ status: 500
+ })
+ })
+
+ it('保留错误细节且拒绝 HTML 代理错误', async () => {
+ vi.stubGlobal(
+ 'fetch',
+ vi
+ .fn<(url: string, options: RequestInit) => Promise>()
+ .mockResolvedValueOnce(
+ new Response(JSON.stringify({ message: '校验失败', issues: ['缺少形态'] }), { status: 400 })
+ )
+ .mockResolvedValueOnce(new Response('Bad Gateway', { status: 502 }))
+ )
+ await expect(request('/projects')).rejects.toMatchObject({ status: 400, details: ['缺少形态'] })
+ await expect(request('/projects')).rejects.toThrow('接口未返回 JSON')
+ })
+
+ it('预览模块使用逗号参数,三种恢复不误发到 start', async () => {
+ const fetcher = vi
+ .fn<(url: string, options: RequestInit) => Promise>()
+ .mockImplementation(() => Promise.resolve(new Response('{"data":{}}')))
+ vi.stubGlobal('fetch', fetcher)
+ await breakdownApi.preview('a/b', { groupSize: 3, modules: ['character', 'scene'] })
+ for (const action of ['retry', 'resume-shots', 'resume-storyboard'] as const)
+ await breakdownApi.run('p', action)
+ expect(fetcher.mock.calls.map(call => call[0])).toEqual([
+ '/api/projects/a%2Fb/breakdown-preview?groupSize=3&modules=character%2Cscene',
+ '/api/projects/p/breakdown/retry',
+ '/api/projects/p/breakdown/resume-shots',
+ '/api/projects/p/breakdown/resume-storyboard'
+ ])
+ })
+
+ it('长工作流不使用普通查询的超时,不自动重试 POST', async () => {
+ vi.useFakeTimers()
+ let finish!: (value: Response) => void
+ const fetcher = vi.fn<(url: string, options: RequestInit) => Promise>().mockImplementation(
+ () =>
+ new Promise(resolve => {
+ finish = resolve
+ })
+ )
+ vi.stubGlobal('fetch', fetcher)
+ const running = breakdownApi.run('p', 'start', { groupSize: 3, modules: ['prop'] })
+ await vi.advanceTimersByTimeAsync(120_000)
+ expect(fetcher).toHaveBeenCalledTimes(1)
+ expect(fetcher.mock.calls[0]![1].signal?.aborted).toBe(false)
+ finish(new Response('{"data":{"workflowExecution":{"status":"failed"}}}'))
+ expect(await running).toMatchObject({ workflowExecution: { status: 'failed' } })
+ })
+})
diff --git a/src/lib/http.ts b/src/lib/http.ts
new file mode 100644
index 0000000..e4ee2b1
--- /dev/null
+++ b/src/lib/http.ts
@@ -0,0 +1,91 @@
+/** HTTP 错误携带状态码与后端细节,供页面区别空数据、校验错误与网络故障。 */
+export class ApiError extends Error {
+ constructor(
+ message: string,
+ public readonly status: number,
+ public readonly details?: unknown
+ ) {
+ super(message)
+ this.name = 'ApiError'
+ }
+}
+
+/** 请求选项;长工作流显式关闭超时,不自动重试任何 POST。 */
+export interface RequestOptions {
+ method?: 'GET' | 'POST'
+ body?: unknown
+ signal?: AbortSignal
+ timeoutMs?: number
+}
+
+/** 判断可安全访问键的 JSON 对象。 */
+function isRecord(value: unknown): value is Record {
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
+}
+
+/** 兼容普通 {data} 响应,以及 POST /projects 的顶层 202 响应。 */
+export async function request(path: string, options: RequestOptions = {}): Promise {
+ const base = (import.meta.env.VITE_API_BASE_URL || '/api').replace(/\/$/, '')
+ const controller = new AbortController()
+ const timeoutMs = options.timeoutMs ?? 30_000
+ const timer = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : undefined
+ const abort = () => controller.abort()
+ options.signal?.addEventListener('abort', abort, { once: true })
+ if (options.signal?.aborted) controller.abort()
+ try {
+ const response = await fetch(base + path, {
+ method: options.method ?? 'GET',
+ headers: {
+ Accept: 'application/json',
+ ...(options.body === undefined ? {} : { 'Content-Type': 'application/json' })
+ },
+ body: options.body === undefined ? undefined : JSON.stringify(options.body),
+ signal: controller.signal
+ })
+ const text = await response.text()
+ let data: unknown
+ try {
+ data = text ? JSON.parse(text) : undefined
+ } catch {
+ throw new ApiError('接口未返回 JSON,请检查 API 地址与代理配置。', response.status)
+ }
+ if (!response.ok) {
+ const message =
+ isRecord(data) && typeof data.message === 'string' ? data.message : `请求失败(${response.status})`
+ throw new ApiError(
+ message,
+ response.status,
+ isRecord(data) ? (data.details ?? data.issues ?? data.error) : data
+ )
+ }
+ return (isRecord(data) && 'data' in data ? data.data : data) as T
+ } catch (error) {
+ if (error instanceof ApiError) throw error
+ if (options.signal?.aborted) throw error
+ if (controller.signal.aborted)
+ throw new ApiError('请求等待超时。后台任务可能仍在运行,请先刷新状态,不要重复提交。', 0)
+ throw new ApiError('无法连接后端。请检查服务与代理;已提交的任务可能仍在运行。', 0)
+ } finally {
+ clearTimeout(timer)
+ options.signal?.removeEventListener('abort', abort)
+ }
+}
+
+/** 仅允许指定的“尚无 checkpoint”404 视为缺省,不吞掉 500 或网络错误。 */
+export async function optionalResource(promise: Promise): Promise {
+ try {
+ return await promise
+ } catch (error) {
+ if (error instanceof ApiError && error.status === 404) return null
+ throw error
+ }
+}
+
+/** 页面统一显示可操作的错误信息。 */
+export function errorMessage(error: unknown): string {
+ if (error instanceof ApiError && error.details) {
+ const details = typeof error.details === 'string' ? error.details : JSON.stringify(error.details)
+ return `${error.message} ${details}`
+ }
+ return error instanceof Error ? error.message : '操作失败,请刷新后重试。'
+}
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..cd6f6af
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,7 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+import { router } from './router'
+import './styles.css'
+
+/** 单页应用入口;不在浏览器保存模型密钥。 */
+createApp(App).use(router).mount('#app')
diff --git a/src/router/index.ts b/src/router/index.ts
new file mode 100644
index 0000000..a2a5163
--- /dev/null
+++ b/src/router/index.ts
@@ -0,0 +1,42 @@
+import { createRouter, createWebHistory } from 'vue-router'
+
+/** 按 graph 分路由,项目布局保留共享数据与长请求状态。 */
+export const router = createRouter({
+ history: createWebHistory(import.meta.env.BASE_URL),
+ routes: [
+ { path: '/', redirect: '/projects' },
+ {
+ path: '/projects',
+ component: () => import('../features/projects/ProjectsPage.vue'),
+ meta: { title: '我的剧本' }
+ },
+ {
+ path: '/projects/:projectId',
+ component: () => import('../features/projects/ProjectLayout.vue'),
+ redirect: to => `/projects/${String(to.params.projectId)}/create-drama`,
+ children: [
+ {
+ path: 'create-drama',
+ component: () => import('../features/create-drama/CreateDramaPage.vue'),
+ meta: { title: '剧本创作' }
+ },
+ {
+ path: 'breakdown',
+ component: () => import('../features/breakdown/BreakdownPage.vue'),
+ meta: { title: '剧本拆解' }
+ }
+ ]
+ },
+ {
+ path: '/:pathMatch(.*)*',
+ component: () => import('../components/NotFoundPage.vue'),
+ meta: { title: '页面不存在' }
+ }
+ ],
+ scrollBehavior: () => ({ top: 0 })
+})
+
+/** 路由切换同步浏览器标题,不依赖页面组件主动修改。 */
+router.afterEach(to => {
+ document.title = `${String(to.meta.title || '工作空间')} · 短剧工作台`
+})
diff --git a/src/styles.css b/src/styles.css
new file mode 100644
index 0000000..a091b5b
--- /dev/null
+++ b/src/styles.css
@@ -0,0 +1,978 @@
+@import 'tailwindcss';
+
+/* 工作台主题:纸白内容、炭灰导航、克制的朱砂色操作。 */
+@theme {
+ --color-ink: #292c28;
+ --color-muted: #797c74;
+ --color-faint: #acafa6;
+ --color-line: #e6e7e0;
+ --color-accent: #b45435;
+ --color-danger: #b14236;
+ --color-success: #4d7654;
+ --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
+}
+
+@layer base {
+ * {
+ box-sizing: border-box;
+ }
+ body {
+ margin: 0;
+ background: #f6f7f3;
+ color: var(--color-ink);
+ font-family: var(--font-sans);
+ -webkit-font-smoothing: antialiased;
+ }
+ button,
+ input,
+ textarea,
+ select {
+ font: inherit;
+ }
+ button,
+ a,
+ summary {
+ -webkit-tap-highlight-color: transparent;
+ }
+ button:not(:disabled),
+ summary,
+ select {
+ cursor: pointer;
+ }
+ button:disabled {
+ opacity: 0.45;
+ cursor: not-allowed;
+ }
+ a {
+ text-decoration: none;
+ color: inherit;
+ }
+ :focus-visible {
+ outline: 2px solid var(--color-accent);
+ outline-offset: 3px;
+ }
+ input:focus-visible,
+ textarea:focus-visible,
+ select:focus-visible {
+ outline-offset: 1px;
+ }
+ ::selection {
+ background: #ead7c8;
+ }
+}
+
+@layer components {
+ .app-shell {
+ display: grid;
+ grid-template-columns: 216px minmax(0, 1fr);
+ min-height: 100vh;
+ }
+ .sidebar {
+ position: sticky;
+ top: 0;
+ height: 100dvh;
+ display: flex;
+ flex-direction: column;
+ padding: 29px 16px 21px;
+ background: #242722;
+ color: #d2d5cb;
+ }
+ .brand {
+ display: flex;
+ align-items: center;
+ gap: 11px;
+ padding: 0 8px;
+ margin-bottom: 43px;
+ white-space: nowrap;
+ }
+ .brand-mark {
+ display: grid;
+ place-items: center;
+ width: 33px;
+ height: 36px;
+ border: 1px solid #737265;
+ border-radius: 5px;
+ color: #e9dbc5;
+ flex-shrink: 0;
+ }
+ .brand strong {
+ font-size: 15px;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ display: block;
+ color: #f2f2e8;
+ }
+ .brand small {
+ display: block;
+ font-size: 8px;
+ letter-spacing: 0.11em;
+ color: #8e9386;
+ margin-top: 5px;
+ }
+ .sidebar-section-label {
+ padding: 0 12px;
+ margin-bottom: 12px;
+ color: #7e8577;
+ font-size: 10px;
+ letter-spacing: 0.1em;
+ }
+ .side-link {
+ display: flex;
+ align-items: center;
+ gap: 11px;
+ padding: 11px 12px;
+ border-radius: 5px;
+ margin-bottom: 5px;
+ font-size: 12px;
+ color: #a9b0a0;
+ white-space: nowrap;
+ transition:
+ background 0.15s,
+ color 0.15s;
+ }
+ .side-link:hover {
+ background: #2e332b;
+ color: #edf0e6;
+ }
+ .side-link.selected {
+ color: #f3f0e4;
+ background: #393e32;
+ }
+ .side-link.selected svg {
+ color: #c0c9aa;
+ }
+ .sidebar-footer {
+ margin-top: auto;
+ padding-top: 30px;
+ }
+ .sidebar-divider {
+ height: 1px;
+ background: #393d34;
+ margin: 13px 9px;
+ }
+ .sidebar-collapsed {
+ grid-template-columns: 76px minmax(0, 1fr);
+ }
+ .sidebar-collapsed .sidebar-label {
+ display: none;
+ }
+ .sidebar-collapsed .sidebar {
+ padding-inline: 12px;
+ }
+ .sidebar-collapsed .brand {
+ padding: 0 9px;
+ }
+ .sidebar-collapsed .side-link {
+ justify-content: center;
+ padding-inline: 10px;
+ }
+ .app-main {
+ min-width: 0;
+ }
+ .topbar {
+ height: 66px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ padding-inline: 40px;
+ border-bottom: 1px solid var(--color-line);
+ background: #fbfcf8;
+ }
+ .topbar-note {
+ font-size: 11px;
+ color: var(--color-muted);
+ }
+ .page-container {
+ max-width: 1540px;
+ margin-inline: auto;
+ padding: 37px 40px 56px;
+ }
+ .page-heading {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 24px;
+ margin-bottom: 28px;
+ }
+ .page-heading h1 {
+ margin: 9px 0 0;
+ font-size: 27px;
+ font-weight: 600;
+ letter-spacing: -0.035em;
+ line-height: 1.4;
+ }
+ .eyebrow {
+ font-size: 10px;
+ letter-spacing: 0.11em;
+ color: var(--color-muted);
+ font-weight: 500;
+ }
+ .page-description {
+ margin-top: 9px;
+ color: var(--color-muted);
+ font-size: 12px;
+ line-height: 1.7;
+ }
+ .button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ padding: 9px 14px;
+ min-height: 36px;
+ border: 1px solid transparent;
+ border-radius: 5px;
+ font-size: 12px;
+ font-weight: 500;
+ white-space: nowrap;
+ transition:
+ background 0.15s,
+ border-color 0.15s;
+ }
+ .button-primary {
+ background: var(--color-accent);
+ color: white;
+ border-color: var(--color-accent);
+ }
+ .button-primary:hover:not(:disabled) {
+ background: #9e472c;
+ }
+ .button-secondary {
+ background: #fff;
+ border-color: #dcded5;
+ color: #4b5146;
+ }
+ .button-secondary:hover:not(:disabled) {
+ background: #f0f2ea;
+ border-color: #c7cbbf;
+ }
+ .icon-button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ height: 33px;
+ width: 33px;
+ border: 1px solid transparent;
+ border-radius: 5px;
+ color: var(--color-muted);
+ }
+ .icon-button:hover:not(:disabled) {
+ background: #eeefe9;
+ color: var(--color-ink);
+ }
+ .text-button {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 11px;
+ color: var(--color-muted);
+ padding: 6px 0;
+ }
+ .text-button:hover:not(:disabled) {
+ color: var(--color-accent);
+ }
+ .panel {
+ background: #fff;
+ border: 1px solid var(--color-line);
+ border-radius: 7px;
+ }
+ .workspace-note {
+ display: flex;
+ gap: 13px;
+ align-items: center;
+ padding: 17px 20px;
+ background: #edefe5;
+ border: 1px solid #e2e5d7;
+ border-radius: 5px;
+ color: #656e53;
+ font-size: 12px;
+ }
+ .toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ flex-wrap: wrap;
+ }
+ .filter-button {
+ padding: 7px 11px;
+ border-radius: 4px;
+ font-size: 11px;
+ color: var(--color-muted);
+ }
+ .filter-button:hover {
+ color: var(--color-ink);
+ }
+ .filter-button.active {
+ color: var(--color-ink);
+ background: #e9ece2;
+ }
+ .search-field {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ background: #fff;
+ border: 1px solid var(--color-line);
+ border-radius: 5px;
+ padding: 7px 10px;
+ color: var(--color-muted);
+ }
+ .search-field input {
+ background: transparent;
+ border: none;
+ outline: none;
+ font-size: 11px;
+ width: 145px;
+ color: var(--color-ink);
+ }
+ .search-field:focus-within {
+ border-color: var(--color-accent);
+ }
+ .table-scroll {
+ overflow-x: auto;
+ }
+ .project-table {
+ width: 100%;
+ border-collapse: collapse;
+ text-align: left;
+ font-size: 12px;
+ }
+ .project-table th {
+ padding: 13px 20px;
+ background: #fafbf7;
+ color: var(--color-muted);
+ font-size: 10px;
+ font-weight: 500;
+ border-bottom: 1px solid var(--color-line);
+ white-space: nowrap;
+ }
+ .project-table td {
+ padding: 21px 20px;
+ border-bottom: 1px solid #eeefe9;
+ }
+ .project-table tr:last-child td {
+ border-bottom: none;
+ }
+ .project-table tbody tr:hover {
+ background: #fcfcf9;
+ }
+ .project-name {
+ display: flex;
+ align-items: center;
+ gap: 13px;
+ min-width: 220px;
+ }
+ .project-monogram {
+ width: 39px;
+ height: 45px;
+ display: grid;
+ place-items: center;
+ background: #eeeee5;
+ border: 1px solid #e3e2d6;
+ border-radius: 3px 5px 5px 3px;
+ box-shadow: inset 3px 0 #dddfcf;
+ flex-shrink: 0;
+ font-family: 'Songti SC', serif;
+ font-size: 17px;
+ color: #6c715c;
+ }
+ .status-badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 10px;
+ white-space: nowrap;
+ color: var(--color-muted);
+ }
+ .status-dot {
+ width: 5px;
+ height: 5px;
+ border-radius: 50%;
+ background: currentColor;
+ }
+ .status-badge[data-status='completed'] {
+ color: #557b50;
+ }
+ .status-badge[data-status='generating'],
+ .status-badge[data-status='running'] {
+ color: #aa8137;
+ }
+ .status-badge[data-status='failed'] {
+ color: #b25040;
+ }
+ .status-badge[data-status='need_review'] {
+ color: #977536;
+ }
+ .empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 300px;
+ padding: 52px 24px;
+ text-align: center;
+ }
+ .dialog-overlay {
+ position: fixed;
+ inset: 0;
+ z-index: 50;
+ background: #1b201c70;
+ }
+ .dialog-content {
+ position: fixed;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 51;
+ width: min(560px, calc(100vw - 32px));
+ max-height: 90dvh;
+ overflow-y: auto;
+ background: #fff;
+ border: 1px solid var(--color-line);
+ border-radius: 9px;
+ padding: 28px;
+ box-shadow: 0 24px 90px #141c2329;
+ }
+ .dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ padding-top: 20px;
+ border-top: 1px solid var(--color-line);
+ }
+ .field-label {
+ display: block;
+ margin-bottom: 9px;
+ font-size: 12px;
+ font-weight: 500;
+ color: #4b5047;
+ }
+ .input {
+ width: 100%;
+ background: #fff;
+ color: var(--color-ink);
+ border: 1px solid #d9ddd2;
+ border-radius: 5px;
+ padding: 9px 11px;
+ font-size: 12px;
+ line-height: 1.7;
+ }
+ .input::placeholder {
+ color: #a3a79b;
+ }
+ .input:disabled {
+ background: #f5f6f2;
+ opacity: 0.6;
+ }
+ .alert {
+ padding: 12px 15px;
+ border: 1px solid #dfdfca;
+ border-radius: 5px;
+ background: #f4f4e9;
+ color: #6c6b45;
+ font-size: 12px;
+ line-height: 1.8;
+ overflow-wrap: anywhere;
+ }
+ .alert-error {
+ border-color: #ebd4cd;
+ background: #fcf4f0;
+ color: #9f4a38;
+ }
+ .back-link {
+ display: inline-flex;
+ gap: 7px;
+ align-items: center;
+ font-size: 11px;
+ color: var(--color-muted);
+ }
+ .back-link:hover {
+ color: var(--color-accent);
+ }
+ .workflow-nav {
+ display: flex;
+ gap: 28px;
+ border-bottom: 1px solid #dfe2d8;
+ }
+ .workflow-nav a {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ padding: 0 2px 16px;
+ border-bottom: 2px solid transparent;
+ margin-bottom: -1px;
+ color: var(--color-muted);
+ font-size: 12px;
+ }
+ .workflow-nav a.router-link-active {
+ color: #454f37;
+ border-color: #5d7050;
+ font-weight: 600;
+ }
+ .nav-code {
+ font:
+ 10px ui-monospace,
+ monospace;
+ opacity: 0.6;
+ margin-left: 5px;
+ }
+ .stage-strip {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 14px;
+ }
+ .stage-item {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ color: #969c8d;
+ font-size: 11px;
+ }
+ .stage-item.done {
+ color: #607550;
+ }
+ .stage-arrow {
+ margin-left: 12px;
+ color: #c1c6b8;
+ }
+ .progress-track {
+ width: 100%;
+ height: 3px;
+ overflow: hidden;
+ border-radius: 2px;
+ background: #e7eade;
+ }
+ .progress-fill {
+ height: 100%;
+ background: #7d9167;
+ transition: width 0.3s;
+ }
+ .tabs-list {
+ display: inline-flex;
+ gap: 3px;
+ padding: 3px;
+ background: #eceedf;
+ border-radius: 5px;
+ flex-wrap: wrap;
+ }
+ .tab-trigger {
+ padding: 7px 12px;
+ font-size: 11px;
+ border-radius: 3px;
+ color: #7f8574;
+ }
+ .tab-trigger[data-state='active'] {
+ background: #fff;
+ color: #343c2a;
+ box-shadow: 0 1px 3px #384e2110;
+ }
+ .tab-trigger span {
+ margin-left: 6px;
+ opacity: 0.65;
+ font-size: 10px;
+ }
+ .content-with-history {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 207px;
+ min-height: 480px;
+ overflow: hidden;
+ }
+ .history-panel {
+ padding: 23px 21px;
+ background: #fafbf7;
+ border-left: 1px solid var(--color-line);
+ }
+ .timeline {
+ margin: 0;
+ padding: 0 0 0 9px;
+ list-style: none;
+ }
+ .timeline li {
+ position: relative;
+ padding: 0 0 23px 17px;
+ border-left: 1px solid #dfe3d7;
+ }
+ .timeline li::before {
+ content: '';
+ position: absolute;
+ left: -3.5px;
+ top: 5px;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #c5ccbc;
+ }
+ .timeline li:last-child {
+ border-left-color: transparent;
+ padding-bottom: 0;
+ }
+ .timeline .timeline-latest::before {
+ background: #8a9b6f;
+ box-shadow: 0 0 0 3px #e9edde;
+ }
+ .script-workspace {
+ display: grid;
+ grid-template-columns: 166px minmax(0, 1fr);
+ height: 100%;
+ }
+ .episode-list {
+ border-right: 1px solid var(--color-line);
+ padding: 0 7px 20px;
+ background: #fdfefa;
+ max-height: 850px;
+ overflow-y: auto;
+ }
+ .episode-link {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ width: 100%;
+ text-align: left;
+ padding: 11px 9px;
+ border-radius: 4px;
+ font-size: 11px;
+ color: var(--color-muted);
+ }
+ .episode-link:hover {
+ background: #f2f4ec;
+ }
+ .episode-link.active {
+ color: #666b43;
+ background: #eeeee0;
+ font-weight: 500;
+ }
+ .episode-number {
+ font:
+ 10px ui-monospace,
+ monospace;
+ opacity: 0.7;
+ }
+ .script-page {
+ padding: 34px 34px 50px;
+ min-width: 0;
+ }
+ .script-summary {
+ font-size: 12px;
+ color: var(--color-muted);
+ line-height: 1.9;
+ padding: 17px 0 22px;
+ margin-bottom: 23px;
+ border-bottom: 1px solid var(--color-line);
+ }
+ .script-body {
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+ font-size: 14px;
+ line-height: 2.15;
+ color: #474b42;
+ }
+ .script-notes {
+ margin-top: 36px;
+ padding-top: 20px;
+ border-top: 1px solid var(--color-line);
+ font-size: 12px;
+ }
+ .script-notes dt {
+ color: #7f8267;
+ font-weight: 500;
+ margin-bottom: 6px;
+ }
+ .script-notes dd {
+ color: var(--color-muted);
+ line-height: 1.8;
+ margin-bottom: 15px;
+ }
+ .detail-grid {
+ display: grid;
+ grid-template-columns: 70px minmax(0, 1fr);
+ gap: 15px;
+ font-size: 12px;
+ line-height: 1.9;
+ }
+ .detail-grid dt {
+ color: var(--color-muted);
+ }
+ .detail-grid dd {
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+ }
+ .json-view {
+ max-height: 420px;
+ overflow: auto;
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+ padding: 16px;
+ background: #f6f7f1;
+ border-radius: 4px;
+ font:
+ 11px/1.9 ui-monospace,
+ monospace;
+ }
+ .tag {
+ display: inline-flex;
+ padding: 3px 7px;
+ border-radius: 3px;
+ background: #f1f3eb;
+ color: #7b826d;
+ font-size: 10px;
+ white-space: nowrap;
+ }
+ .breakdown-config {
+ display: grid;
+ grid-template-columns: 145px minmax(0, 1fr) auto;
+ align-items: start;
+ gap: 28px;
+ }
+ .checkbox {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+ border: 1px solid #c7cebb;
+ border-radius: 3px;
+ display: grid;
+ place-items: center;
+ background: white;
+ }
+ .checkbox[data-state='checked'] {
+ background: #788761;
+ border-color: #788761;
+ color: white;
+ }
+ .group-preview {
+ margin-top: 24px;
+ border-top: 1px solid var(--color-line);
+ padding-top: 20px;
+ }
+ .group-chip {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ border: 1px solid #e4e7db;
+ background: #fafbf6;
+ border-radius: 4px;
+ padding: 8px 11px;
+ font-size: 11px;
+ }
+ .recovery-strip {
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 15px;
+ padding: 17px;
+ border: 1px dashed #d8dccc;
+ border-radius: 5px;
+ }
+ .subject-ref {
+ display: inline-flex;
+ background: #f0f2e8;
+ padding: 3px 6px;
+ border-radius: 3px;
+ color: #727d58;
+ font-size: 10px;
+ }
+ .subject-details summary {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ color: #7f846f;
+ font-size: 11px;
+ list-style: none;
+ }
+ .subject-details summary::-webkit-details-marker {
+ display: none;
+ }
+ .subject-details[open] summary svg {
+ transform: rotate(180deg);
+ }
+ .beat-section {
+ margin-top: 25px;
+ padding-top: 23px;
+ border-top: 1px solid var(--color-line);
+ }
+ .beat-heading {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+ .beat-number {
+ color: #889174;
+ font:
+ 11px ui-monospace,
+ monospace;
+ }
+ .shot-row {
+ display: flex;
+ gap: 17px;
+ padding: 17px;
+ border: 1px solid #e7e9df;
+ border-radius: 5px;
+ background: #fdfefb;
+ }
+ .shot-label {
+ flex-shrink: 0;
+ width: 47px;
+ font-size: 10px;
+ color: #8a907d;
+ }
+ .skip-link {
+ position: fixed;
+ z-index: 100;
+ top: -80px;
+ left: 15px;
+ padding: 10px 15px;
+ background: white;
+ border: 1px solid var(--color-accent);
+ }
+ .skip-link:focus {
+ top: 10px;
+ }
+}
+
+/* 中等宽度保留正文空间,执行记录下移,移动端导航收为图标。 */
+@media (max-width: 1200px) {
+ .page-container {
+ padding-inline: 26px;
+ }
+ .topbar {
+ padding-inline: 26px;
+ }
+ .content-with-history {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ .history-panel {
+ border-left: 0;
+ border-top: 1px solid var(--color-line);
+ }
+ .timeline {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 15px;
+ }
+ .breakdown-config {
+ grid-template-columns: 120px minmax(0, 1fr);
+ gap: 20px;
+ }
+ .breakdown-config > button {
+ grid-column: 1 / -1;
+ justify-self: start;
+ }
+}
+@media (max-width: 760px) {
+ .app-shell {
+ grid-template-columns: 62px minmax(0, 1fr);
+ }
+ .sidebar {
+ padding: 23px 7px 15px;
+ }
+ .sidebar-label {
+ display: none;
+ }
+ .brand {
+ padding: 0 7px;
+ margin-bottom: 35px;
+ }
+ .side-link {
+ justify-content: center;
+ padding: 11px 10px;
+ }
+ .sidebar-collapsed {
+ grid-template-columns: 62px minmax(0, 1fr);
+ }
+ .sidebar-collapsed .sidebar {
+ padding-inline: 7px;
+ }
+ .sidebar-collapsed .brand {
+ padding-inline: 7px;
+ }
+ .page-container {
+ padding: 25px 18px 40px;
+ }
+ .topbar {
+ height: 56px;
+ padding-inline: 18px;
+ }
+ .topbar-note {
+ display: none;
+ }
+ .page-heading {
+ align-items: flex-start;
+ flex-direction: column;
+ gap: 16px;
+ }
+ .page-heading h1 {
+ font-size: 23px;
+ }
+ .nav-code {
+ display: none;
+ }
+ .workflow-nav {
+ gap: 24px;
+ }
+ .workspace-note {
+ padding: 14px;
+ font-size: 10px;
+ gap: 9px;
+ }
+ .workspace-note span.mx-3 {
+ margin-inline: 6px;
+ }
+ .script-workspace {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ .episode-list {
+ display: flex;
+ overflow-x: auto;
+ border-right: 0;
+ border-bottom: 1px solid var(--color-line);
+ padding: 8px;
+ gap: 4px;
+ }
+ .episode-list > p {
+ display: none;
+ }
+ .episode-link {
+ width: auto;
+ max-width: 170px;
+ flex-shrink: 0;
+ }
+ .script-page {
+ padding: 25px 20px 35px;
+ }
+ .timeline {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ .breakdown-config {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ .dialog-content {
+ padding: 22px;
+ }
+ .stage-strip {
+ gap: 9px;
+ }
+ .stage-arrow {
+ margin-left: 3px;
+ }
+ .tab-trigger {
+ padding: 7px 8px;
+ }
+ .project-table td,
+ .project-table th {
+ padding-inline: 14px;
+ }
+ .shot-row {
+ gap: 12px;
+ padding: 13px;
+ }
+}
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..0fe055b
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noUncheckedIndexedAccess": true,
+ "verbatimModuleSyntax": true,
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "noEmit": true,
+ "types": ["vite/client", "node"]
+ },
+ "include": ["src/**/*.ts", "src/**/*.vue", "*.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..3fe38ef
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,22 @@
+import { defineConfig, loadEnv } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import tailwindcss from '@tailwindcss/vite'
+
+/** 开发代理保留 /api 前缀;长工作流不使用代理层的短超时。 */
+export default defineConfig(({ mode }) => {
+ const env = loadEnv(mode, process.cwd(), '')
+ return {
+ plugins: [vue(), tailwindcss()],
+ server: {
+ port: 5173,
+ proxy: {
+ '/api': {
+ target: env.API_PROXY_TARGET || 'http://localhost:3412',
+ changeOrigin: true,
+ timeout: 0,
+ proxyTimeout: 0
+ }
+ }
+ }
+ }
+})
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 0000000..6f0ddc6
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,5 @@
+import { defineConfig } from 'vitest/config'
+import vue from '@vitejs/plugin-vue'
+
+/** 使用轻量 DOM 测试 API、异步状态和 Vue 页面行为。 */
+export default defineConfig({ plugins: [vue()], test: { environment: 'happy-dom', restoreMocks: true } })